새소식

⇥ DevOps Tech 🙋🏻‍♀️/✏️ Jenkins

Bitbucket 과 AWS CodeCommit Jenkins를 이용한 Mirroring

  • -
반응형

bitbucket 이전 버전에서는 AWS CodeCommit 과의 Mirroring 을 Plugin 을 통해서 지원했었다.
기존에는 v6.3.4 를 사용했었고 repo 설정 > 갈고리 > Mirror Hook 으로 설정이 가능했다.

해당 설정에서 mirroring 할 URL, Credential 을 이용하면 bitbucket 과 AWS 의 CodeCommit 미러링이 손쉽게 가능했다.

https://marketplace.atlassian.com/apps/1211351/repository-mirror-plugin-for-bitbucket/version-history

 

Repository Mirror Plugin for Bitbucket - Version history

Browse the top apps, add-ons, plugins & integrations for Jira, Confluence, Bitbucket, Hipchat & other Atlassian products. Free 30-day trial for all apps.

marketplace.atlassian.com

하지만 이번에 bitbucket v8.9.1 로 최신 버전을 사용하다보니 Mirror Plugin 이 지원하지 않는 버전까지 upgrade 되면서 해당 플러그인을 사용하지 못하는 문제가 발생했다. 
마지막 버전이 20년 8월인 것으로 보아 추가 플러그인을 내놓을 생각은 없어보였다...


AWS 의 CI / CD 배포를 사용하려면 codecommit 이 출발지가 되어야 한다. (다른 방법도 있지만 같이 사용하는 것이 편함)
CodeCommit 의 특정 branch 가 업데이트 되면 cloudwatch 에서 이벤트를 모니터링 하다가 codebuild, codepipeline 등을 실행하도록 구성할 수 있기 때문에 mirroring 기능은 반드시 필요한 기능이었지만 plugin 을 사용할 수 없었기 때문에 임시 방편으로 jenkins 를 활용했다.

구현은 간단하다. 해당 plugin 의 git 에서 확인해보면 알 수 있다.
https://github.com/ef-labs/stash-hook-mirror

git url 을 설정하고, 입력된 credential 을 활용하여 prune, foce 옵션과 함께 push 하는 동작으로 되어 있다.
그래서 해당 동작을 jenkins pipeline 으로 구현하였다.

bitbucket 에서 webhook 을 받아서 자동으로 jenkins job 이 동작할 수 있도록 trigger 하였고, 그 결과 해당 플러그인과 동일한 동작을 할 수 있도록 구현할 수 있었다. 물론 정식 지원은 아니라 불안정한 면도 있다. 하지만 없는 것 보다는 나았다...

 

Pipeline Code

pipeline {
    agent any
    
    environment {
        aws = credentials('aws')

    }
    stages {

        stage('git clone') {
            steps {
                script {
                    try {
                        sh "echo ${project}"
                        dir(repo_name)
                        {
                            dir(branch){
                                git branch: '${branch}', credentialsId: '***', url: 'https://bitbucketurl/scm/${project}/${repo_name}.git' 
                            }
                        }
                    } catch (MissingPropertyException e) {
                        repo_name = merge_slug 
                        branch = merge_branch
                        project = merge_project
                        
                        dir(repo_name)
                        {
                            dir(branch){
                                git branch: '${merge_branch}', credentialsId: '***', url: 'https://bitbucketurl/scm/${merge_project}/${merge_slug}.git'      
                            }
                        }
                    }
                }
            }
        }
        
        stage('git init') {
            steps {
                dir(repo_name)
                {
                    dir(branch){
                        sh "git init"
                        sh "git prune"
                    }
                }
            }
        }

        stage('Add remote') {
            steps {
                script {
                        dir(repo_name)
                        {
                            dir(branch)
                                {
                                    try {
                                        sh "echo ${project}"
                                    } catch (MissingPropertyException e) {
                                        repo_name = merge_slug 
                                        branch = merge_branch
                                        project = merge_project
                                    }
                                    
                                    withCredentials([usernamePassword(credentialsId: 'aws', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
                                        sh "git remote remove origin"
                                        sh "git remote add origin https://${USERNAME}:${PASSWORD}@git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/${repo_name}-mirror"
                                    }
                                    def scriptOutput = sh(returnStatus: true, script: 'git remote update')
                                }
                            }
                        }
                    }
                }

        stage('push') {
            steps {
                dir(repo_name)
                {
                    dir(branch){
                        sh "git remote -v"
                        sh "git checkout ${branch}"
                        sh "git push origin ${branch} --force"     
                    }
                }         
            }
        }
    }
}

총 5개의 stage 로 구현되어 있다.

1. git clone
bitbucket 에서 source 를 clone 하는 단계이다. 이때 jenkins 에 bitbucket 의 credential 을 가지고 있어야 한다.
또한 hook 으로 발생시켰기 때문에 branch, project key, repo name 등의 정보를 response에서 참고하도록 jenkins 에서 설정했다.
만약 push 로 했다면 정상 동작하지만, merge 동작일 수 도 있다.
push 로 webhook이 발생했을 때와 merge 로 webhook 이 발생했을 때의 event payload 값이 다르기 때문에 각각 설정이 필요하다.
Generic Webhook Trigger Plugin 을 사용하였으며, 아래의 값을 받도록 하였다.

branch $.changes[0].ref.displayId
repo_name $.repository.slug
project_key $.repository.project.key 
merge_branch $.pullRequest.toRef.displayId
merge_slug $.pullRequest.toRef.repository.slug
merge_project $.pullRequest.toRef.repository.project.key

event payload 는 아래 주소를 참고하면 된다.
https://support.atlassian.com/bitbucket-cloud/docs/event-payloads/#Push 

 

Event payloads | Bitbucket Cloud | Atlassian Support

When you have a webhook with an event, Bitbucket Cloud sends the event request to the server URL for the webhook whenever that event occurs

support.atlassian.com

 

2. git init
해당 stage 에서는 단순히 clone 받은 repo 에서 prune 동작을 수행하는 정리 단계이다.

 

3. Add remote
bitbucket 에서 clone 을 완료하였으니 이제 AWS 의 CodeCommit 으로 원격 저장소를 변경해줘야 할 차례다.
local 에서 repository를 받으면 원격 저장소의 주소를 가지고 있고 git remote origin 명령어로 조회가 가능하다.
git remote remove 명령어로 원격 저장소의 연결을 끊어준다. 그리고 AWS CodeCommit 의 주소를 원격 저장소로 설정하는 stage이다. 젠킨스에 저장되어 있는 CodeCommit 의 Credential 을 가져와서 주소에 포함시켜서 원격 저장소를 설정하였다.
한가지 주의 사항이 있는데, 모든 CodeCommit 에 동일한 Jenkins Job 을 설정하도록 하여서, 한가지 규칙이 필요했다.
bitbucket 의 repo 이름 뒤에 -mirror 를 붙여서 CodeCommit 의 저장소 이름을 설정하여야 하는 제한 사항이 있다.

4. Push
마지막 stage 는 간단하다. 원격 저장소의 주소를 바꿨으니 force 옵션을 통하여 push 해주면 된다.


이렇게 하면 CodeCommit 미러링 동작이 구현되어 bitbucket에서 push 나 merge 동작이 수행되면 jenkins webhook 으로 trigger 되고 pipeline 이 수행된다. 해당 pipeline 코드가 수행되면 Aws CodeCommit 에 변경사항이 push 되고 변경사항이 발생하여 AWS 의 CodeBuild, CodePipeline 이 동작할 수 있게 된다.
임시 방편이기 때문에 가끔 webhook 이 발생하지 않을 때도 있고, 안정적이라고 말할 수는 없다.
하지만 더이상 지원하지 않는 플러그인을 대체하여 사용하기 위해서 어쩔 수 없이 작업했던 내용..

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.