본문 바로가기

CICD/EC2

EC2 CICD Automation - 7. Jenkins Pipeline

Jenkins 세팅 시 만들었던, Jenkins User IAM의 권한을 수정한 후 Jenkins Credentials에 추가시켜줍니다.

 

 

그리고 파이프라인에서 사용해야 하는 Plug in을 설치해주도록 하겠습니다.

 


자, 이제 Pipeline Job을 만들어주도록 하겠습니다.

 

 

만들어진 Job에 Git Credentials ID 값과, AWS Credentials ID 값을 넣어주도록 합니다.

이유는, Pipeline에서는 코드에서 명령어를 실행해야 하다보니,

코드를 보면 WithAWS()~~ 라는 코드에 Credentials 값을 가지고 명령어를 수행할 수 있도록 구성되어있습니다.

 

젠킨스 변수에서 사실 String 변수로 넣어도 상관없지만,

변경하면 귀찮아지기 때문에 아무도 건들지 않을 만한 것으로 설정해주면 좋습니다.

(소스코드에서 변수 선언을 해도 무관)

 


또한 Maven Build도 Jenkins서버의 sh 명령어로 수행할 것이므로,

 

sudo yum -y install maven

 

을 통해 메이븐을 설치해줍시다.

 


자, 이제 맨 밑에 스크립트에 아래의 코드를 넣어주고 실행하면 끝입니다.

 

pipeline {
    agent any
    stages {
        stage('Git Clone') {
            steps {
                script {
                    try {
                        git url: "https://git-codecommit.ap-northeast-2.amazonaws.com/v1/repos/cb-test-api", branch: "master", credentialsId: "$GIT_CREDENTIALS_ID"
                        sh "sudo rm -rf ./.git"
                        env.cloneResult=true
                    } catch (error) {
                        print(error)
                        env.cloneResult=false
                        currentBuild.result = 'FAILURE'
                    }
                }
            }
        }
        stage('Build JAR') {
            when {
                expression {
                    return env.cloneResult ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/
                }                
            }
            steps {
                script{
                    try {
                        sh """
                        rm -rf deploy
                        mkdir deploy
                        """ 
                        sh "sudo sed -i \"s/module_name=.*/module_name=${env.JOB_NAME}\\:${env.BUILD_NUMBER}/g\" /var/lib/jenkins/workspace/${env.JOB_NAME}/src/main/resources/application.properties"
                        sh "cat /var/lib/jenkins/workspace/${env.JOB_NAME}/src/main/resources/application.properties"
                        sh 'mvn -Dmaven.test.failure.ignore=true clean install'
                        sh """
                        cd deploy
                        cp /var/lib/jenkins/workspace/${env.JOB_NAME}/target/*.jar ./${env.JOB_NAME}.jar
                        """
                        env.mavenBuildResult=true
                    } catch (error) {
                        print(error)
                        echo 'Remove Deploy Files'
                        sh "sudo rm -rf /var/lib/jenkins/workspace/${env.JOB_NAME}/*"
                        env.mavenBuildResult=false
                        currentBuild.result = 'FAILURE'
                    }
                }
            }
            post {
                success {
                    slackSend channel: '#pipeline-deploy', color: 'good', message: "The pipeline ${currentBuild.fullDisplayName} stage Build JAR successfully."
                }
                failure {
                    slackSend channel: '#pipeline-deploy', color: 'danger', message: "The pipeline ${currentBuild.fullDisplayName} stage Build JAR failed."
                }
            }
        }
        stage('S3 Copy & Upload') {
            when {
                expression {
                    return env.mavenBuildResult ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/
                }                
            }
            steps{
                script{
                    try {                       
                        sh """
                        cd deploy
                        cat > deploy.sh <<-EOF
#!/bin/bash
kill -9 dollar(ps -ef | grep ${env.JOB_NAME}.jar | grep -v grep | awk '{print dollar2}')
nohup java -jar /home/jenkins/deploy/${env.JOB_NAME}.jar 1> /dev/null 2>&1 &
EOF"""

                        sh"""
                        cd deploy
                        sed -i "s/dollar/\$/g" ./deploy.sh
                        """
                        
                        sh """
                        cd deploy
                        cat>appspec.yml<<-EOF
version: 0.0
os: linux
files:
  - source:  /
    destination: /home/jenkins/deploy

permissions:
  - object: /
    pattern: "**"
    owner: jenkins
    group: jenkins

hooks:
  ApplicationStart:
    - location: deploy.sh
      timeout: 60
      runas: root
EOF"""
                        
                        sh """
                        cd deploy
                        chown jenkins:jenkins appspec.yml
                        chown jenkins:jenkins deploy.sh
                        chmod 700 appspec.yml
                        zip -r deploy *
                        """
                        withAWS(credentials:"$AWS_CREDENTIALS") {
                            s3Upload(path:"${env.JOB_NAME}/${env.BUILD_NUMBER}/${env.JOB_NAME}.zip", file:"/var/lib/jenkins/workspace/${env.JOB_NAME}/deploy/deploy.zip",bucket:'cb-test-deploy')
                            env.copyResult=true
                        }
                        echo 'Remove Deploy Files'
                        sh "sudo rm -rf /var/lib/jenkins/workspace/${env.JOB_NAME}/*"
                    } catch (error) {
                        print(error)
                        echo 'Remove Deploy Files'
                        sh "sudo rm -rf /var/lib/jenkins/workspace/${env.JOB_NAME}/*"
                        env.copyResult = false
                        currentBuild.result = 'FAILURE'                    
                    }
                }
            }
            post {
                success {
                    slackSend channel: '#pipeline-deploy', color: 'good', message: "The pipeline ${currentBuild.fullDisplayName} stage S3 CP & Upload successfully."
                }
                failure {
                    slackSend channel: '#pipeline-deploy', color: 'danger', message: "The pipeline ${currentBuild.fullDisplayName} stage S3 CP & Upload failed."
                }
            }
        }
        stage('Deploy'){
            when {
                expression {
                    return env.copyResult ==~ /(?i)(Y|YES|T|TRUE|ON|RUN)/
                }
            }
            steps {
                script{
                    try {
                        withAWS(credentials:"$AWS_CREDENTIALS") {
                            sh"""
                                aws deploy create-deployment \
                                --application-name CB-TEST-API \
                                --deployment-group-name TEST-BlueGreen \
                                --region ap-northeast-2 \
                                --s3-location bucket=cb-test-deploy,bundleType=zip,key=${env.JOB_NAME}/${env.BUILD_NUMBER}/${env.JOB_NAME}.zip \
                                --file-exists-behavior OVERWRITE \
                                --output json > DEPLOYMENT_ID.json
                                cat DEPLOYMENT_ID.json
                            """
                        }
                        def DEPLOYMENT_ID = readJSON file: './DEPLOYMENT_ID.json'
                        echo "${DEPLOYMENT_ID.deploymentId}"
                        sh "rm -rf ./DEPLOYMENT_ID.json"
                        def DEPLOYMENT_RESULT = ""
                        while("$DEPLOYMENT_RESULT" != "\"Succeeded\"") {
                            DEPLOYMENT_RESULT = withAWS(credentials:"$AWS_CREDENTIALS") {
                                sh(
                                    script:"aws deploy get-deployment \
                                    --query \"deploymentInfo.status\" \
                                    --region ap-northeast-2 \
                                    --deployment-id ${DEPLOYMENT_ID.deploymentId}",
                                    returnStdout: true
                                ).trim()
                            }
                            echo "$DEPLOYMENT_RESULT"
                            if ("$DEPLOYMENT_RESULT" == "\"Failed\"") {
                                throw new Exception("CodeDeploy Failed")
                                break
                            }
                            sleep(30)
                        }
                        currentBuild.result = 'SUCCESS'
                    } catch (error) {
                        print(error)
                        echo 'Remove Deploy Files'
                        sh "sudo rm -rf /var/lib/jenkins/workspace/${env.JOB_NAME}/*"
                        currentBuild.result = 'FAILURE'
                    }
                }
            }
            post {
                success {
                    slackSend channel: '#pipeline-deploy', color: 'good', message: "The pipeline ${currentBuild.fullDisplayName} stage Deploy successfully."
                }
                failure {
                    slackSend channel: '#pipeline-deploy', color: 'danger', message: "The pipeline ${currentBuild.fullDisplayName} stage Deploy failed."
                }
            }
        }
    }
}

수행이 제대로 되는지 보도록 하겠습니다.

유저 권한과 사소한 변수 에러로 12번 만에 성공하는 모습입니다..

 

 

CodeDeploy 명령어를 Jenkins에서 수행한 후 잘 진행되고 있는 모습입니다.

 

 

 

이제 새로 프로비저닝 된 오토스케일링 그룹의 서버들로 트래픽이 라우팅되고, 기존 서버가 죽는 것을 볼 수 있습니다.

 

 

배포가 성공적으로 완료되었고 젠킨스만 보더라도 확인이 가능합니다.

 

 

 

몇번 두번 더 테스트 해보고 domain으로 호출해 본 배포된 페이지 모습입니다.

 

 

이제 기본적인 배포 구성은 모두 끝났습니다.

다음 포스트는 DevOps 카테고리에서 Slack과의 연동 (알림 및 승인체계)에 대해서 알아보겠습니다.