โš™๏ธ Devops/๐ŸŒˆ Jenkins

Jenkins, Docker๋ฅผ ์ด์šฉํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์‹œ์ž‘ํ•˜๊ธฐ

iseunghan 2023. 5. 12. 10:21
๋ฐ˜์‘ํ˜•

์‹œ์ž‘ํ•˜๊ธฐ

์˜ˆ์ „์—๋Š” ๋ฐฐํฌํ•˜๋Š” ๋‚ ์ด๋ฉด ์ ‘์†ํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ ์€ ์ƒˆ๋ฒฝ์‹œ๊ฐ„์— ํ–ˆ๋‹ค๊ณ  ๋ณธ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ๋ฅผ ํ•  ๋•Œ์—๋Š” ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ jar ํŒŒ์ผ์„ ๋ฐฐํฌํ•  ์„œ๋ฒ„๋กœ ๋ณต์‚ฌ์‹œํ‚ค๊ณ , ์ง์ ‘ SSH๋กœ ์ ‘์†ํ•˜์—ฌ jar๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด์˜€์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ฐฐํฌ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด, ๋ฐฐํฌ๋ฅผ ํ•  ๋•Œ๋งˆ๋‹ค ๊ฐœ๋ฐœ์ž์˜ ๋งŽ์€ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค๋Š” ๋‹จ์ ๊ณผ jar๋ฅผ ๋ฐ”๊ฟ”๋ผ๋Š” ๊ทธ ์ฐฐ๋‚˜์— ์„œ๋น„์Šค๊ฐ€ ๋Š๊ธด๋‹ค๋Š” ์น˜๋ช…์ ์ธ ๋‹จ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋ฐฐํฌ๋ฅผ ์ž๋™ํ™”ํ•˜๊ณ , ๋˜ ๊ธฐ์กด ์„œ๋น„์Šค๋ฅผ ์ •์ง€์‹œํ‚ค์ง€ ์•Š๊ณ  ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์œ„์—์„œ ๋งํ•œ ์ด ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ์—๋Š” ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” ํ•˜๋‚˜์˜ ์„œ๋ฒ„๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , ๋‚ด๋ถ€์— Nginx์™€ Docker๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

0. ์ „์ฒด ๊ตฌ์„ฑ๋„

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌ์„ฑ๋„

ํ”„๋กœ์„ธ์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. Gitlab์— ํŠน์ • ๋ธŒ๋žœ์น˜๋กœ ํ‘ธ์‹œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, Jenkins๋กœ Trigger๋ฅผ ๋‚ ๋ฆฌ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  2. ์  ํ‚จ์Šค์—์„œ๋Š” GitLab์—์„œ ์ตœ์‹ ์ฝ”๋“œ๋ฅผ ๋ฐ›์•„์™€์„œ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์—ฌ Docker ์ €์žฅ์†Œ๋กœ Push ํ•ฉ๋‹ˆ๋‹ค.
  3. ์  ํ‚จ์Šค์—์„œ ๋ฐฐํฌ ์„œ๋ฒ„๋กœ ์ปค๋งจ๋“œ๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ ์„œ๋ฒ„๋ฅผ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์ „ ์„ค์ •

  • Jenkins ์„ค์ •
  • GitLab ์„ค์ •

1. ํ”Œ๋Ÿฌ๊ทธ์ธ ๋‹ค์šด๋กœ๋“œ

๋จผ์ € ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ์•„๋ž˜ ํ”Œ๋Ÿฌ๊ทธ์ธ๋“ค์„ ๋‹ค์šด๋ฐ›์•„์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๊นƒ๋žฉ, ๋„์ปค, SSH ๋ฅผ ์œ„ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ๋“ค ์ž…๋‹ˆ๋‹ค.

 

docker-build-step

This plugin allows to add various docker commands to your job as build steps.

plugins.jenkins.io

 

SSH Agent

This plugin allows you to provide SSH credentials to builds via a ssh-agent in Jenkins.

plugins.jenkins.io

 

Generic Webhook Trigger

Can receive any HTTP request, extract any values from JSON or XML and trigger a job with those values available as variables. Works with GitHub, GitLab, Bitbucket, Jira and many more.

plugins.jenkins.io

 

GitLab

This plugin allows <a href="http://gitlab.com/" target="_blank" rel="nofollow noopener noreferrer">GitLab</a> to trigger Jenkins builds and display their results in the GitLab UI.

plugins.jenkins.io

ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์ •

[Jenkins Gitlab ์—ฐ๋™ - ์ถ”ํ›„ ํฌ์ŠคํŒ…]

[Jenkins SSH ์„ค์ •ํ•˜๊ธฐ- ์ถ”ํ›„ ํฌ์ŠคํŒ…]

[JDK ์„ค์ •ํ•˜๊ธฐ- ์ถ”ํ›„ ํฌ์ŠคํŒ…]

[Docker ์„ค์ •ํ•˜๊ธฐ- ์ถ”ํ›„ ํฌ์ŠคํŒ…]

Global Properties ์„ค์ •

Dashboard → Jenkins ๊ด€๋ฆฌ → Configure System → Global properties → Environment variables ์ถ”๊ฐ€

  • ๋ฐฐํฌ ์„œ๋ฒ„ URL
  • ๋„์ปค ์ €์žฅ์†Œ URL

Credentials ์ถ”๊ฐ€ํ•˜๊ธฐ

Dashboard → Jenkins ๊ด€๋ฆฌ → Credentials → System → Global credentials (unrestricted) → +Add Credentials

ID๋Š” Jenkins์—์„œ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.

  • ๊นƒ๋žฉ id/pw
  • docker id/pw
  • ์ถ”๊ฐ€

2. Pipeline ์ƒ์„ฑ

New Item → Pipeline ์ƒ์„ฑ

  • Build Triggers ์„ค์ •ํ•˜๊ธฐ (์ถ”ํ›„ ํฌ์ŠคํŒ… ์˜ˆ์ •)

Pipeline ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ

ํŒŒ์ดํ”„๋ผ์ธ์—๋Š” ์—ฌ๋Ÿฌ ์Šคํ…Œ์ด์ง€๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์Šคํ…Œ์ด์ง€๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  • GitLab ์ตœ์‹  ์ฝ”๋“œ Clone
  • Gradle ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ (ํ…Œ์ŠคํŠธ ๋ฐ JAR ์ƒ์„ฑ)
  • Docker Image Build
  • Docker Image Push
  • ๋ฐฐํฌ ์„œ๋ฒ„๋กœ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋ฒ„์ „ ๋ฐฐํฌ

GitLab ์ตœ์‹  ์ฝ”๋“œ Clone

stage ('git clone') {
    steps {
        git branch: 'main', credentialsId: 'gitlab-credentials', url: 'https://git.example.com/myapp.git'
    }
    post {
        failure {
          echo 'Repository clone failure !'
        }
        success {
          echo 'Repository clone success !'
        }
    }
}

์ด๋ฒˆ Stage์—์„œ๋Š” ์ด์ „์— ์„ค์ •ํ•ด๋‘” gitlab credentials ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  URL๋กœ Clone์„ ๋ฐ›๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. Cloneํ•  Branch๋ช…๊ณผ crendentials, URL์ด ์ž˜ ์„ค์ •๋˜์–ด์žˆ๋Š”์ง€ ํ™•์ธํ•˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

Gradle ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ (ํ…Œ์ŠคํŠธ ๋ฐ JAR ์ƒ์„ฑ)

stage('gradle build') {
    steps {
        sh 'chmod +x gradlew'
        sh './gradlew build'
    }
    post {
        failure {
            echo 'Gradle build failure!'
        }
        success {
            echo 'Gradle build success!'
        }
    }
}

์ด๋ฒˆ Stage์—์„œ๋Š” ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— ์žˆ๋Š” gradlew ํŒŒ์ผ์— ๋Œ€ํ•œ ์‹คํ–‰ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๊ณ , ๋นŒ๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•œ๋‹ค๋ฉด ์ดํ›„ Stage๋“ค์€ ๋ชจ๋‘ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Docker Image Build

environment {
    dockerHubRegistry = 'https://example.dockerhub.com'
    dockerRepository = 'example.dockerhub.com/myapp'
}

stage('Docker Image Build') {
    steps {
        sh "docker build -t ${dockerRepository}:${currentBuild.number} ."
        sh "docker build -t ${dockerRepository}:latest ."
        sh "docker rmi ${dockerRepository}:${currentBuild.number}"
    }
    post {
            failure {
              echo 'Docker image build failure!'
            }
            success {
              echo 'Docker image build success!'
            }
    }
}

์ด๋ฒˆ์—๋Š” environment๋ฅผ ์ด์šฉํ•ด dockerHubRegistry์™€ dockerRepository๋ฅผ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๋“ฑ๋กํ•˜์—ฌ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋„์ปค ๋นŒ๋“œ ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

Docker Image Build

stage('Docker Image Push') {
    steps {
        withCredentials([[$class: 'UsernamePasswordMultiBinding',
        credentialsId: 'dockerhub-credentials',
        usernameVariable: 'DOCKER_USER_ID',
        passwordVariable: 'DOCKER_USER_PASSWORD'
        ]]){
            sh 'echo ${DOCKER_USER_PASSWORD} | docker login -u ${DOCKER_USER_ID} --password-stdin ${dockerHubRegistry}'
            sh "docker push ${dockerRepository}:latest"
        }
    }
}

์ด๋ฒˆ Stage์—์„œ๋Š” ์  ํ‚จ์Šค ๋‚ด๋ถ€์— ๋‚ด์žฅ๋œ UsernamePasswordMultiBinding์„ ์ด์šฉํ•˜์—ฌ ์ด์ „์— ๋“ฑ๋กํ•ด๋‘์—ˆ๋˜ Jenkins ๊ด€๋ จ ๋ณ€์ˆ˜๋“ค์„ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋„์ปค ๋กœ๊ทธ์ธ์„ ํ•ด์ฃผ๊ณ , ์ƒ์„ฑํ–ˆ๋˜ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅ์†Œ๋กœ ํ‘ธ์‹œํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐํฌ ๋‹จ๊ณ„

stage('Server Docker Run') {
    steps {
        sshagent (credentials: ['jenkins-rsa']) {
            sh '''
                #!/bin/bash

                if curl -s "${blue_url}" > /dev/null
                then
                    deployment_container_name=green
                    deployment_target_ip=$green_url
                    deployment_port=$green_port
                    old_container_name=blue
                else
                    deployment_container_name=blue
                    deployment_target_ip=$blue_url
                    deployment_port=$blue_port
                    old_container_name=green
                fi

                ssh root@${deploy_ip} "nohup docker run --name ${deployment_container_name} -p ${deployment_port}:8080 ${dockerRepository}:latest > /dev/null &" &

                for retry_count in \$(seq 10)
                do
                    if curl -s "${deployment_target_ip}" > /dev/null
                    then
                        echo "์„œ๋ฒ„ Health Check์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."
                        break
                    fi

                    if [ $retry_count -eq 10 ]
                    then
                        echo "์„œ๋ฒ„ Health Check์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
                        exit 1
                    fi

                    echo "์„œ๋ฒ„ Health Check๋ฅผ 10์ดˆ ์ดํ›„์— ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค..."
                    sleep 10
                done

                ssh root@${deploy_ip} "echo 'set \\\$service_url ${deployment_target_ip};' > /root/docker-compose/nginx/nginx/conf.d/service-url.inc"
                ssh root@${deploy_ip} "docker exec -i nginx service nginx reload"
                echo "Nginx Reverse Proxy ๋ณ€๊ฒฝ: ${deployment_target_ip}"

                echo "๊ธฐ์กด ๋„์ปค ์„œ๋ฒ„ ์ข…๋ฃŒ"
                ssh root@${deploy_ip} "docker rm -f ${old_container_name}"
            '''
        }
    }
}

์ด์ „์— ์„ค์น˜ํ–ˆ๋˜ SSH Agent๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐฐํฌ ์„œ๋ฒ„์— ๋“ฑ๋ก์‹œํ‚จ RSA ๊ฐœ์ธํ‚ค๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ๋ฐฐํฌ ์„œ๋ฒ„๋กœ ๋ช…๋ น์–ด๋ฅผ ์ „์†ก์‹œํ‚ต๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ์ž์„ธํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. (์Šคํฌ๋ฆฝํŠธ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋œ ๋ณ€์ˆ˜๋“ค์€ Jenkins Global Variable๋กœ ์ €์žฅ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.)

if curl -s "${blue_url}" > /dev/null
then
    deployment_container_name=green
    deployment_target_ip=$green_url
    deployment_port=$green_port
    old_container_name=blue
else
    deployment_container_name=blue
    deployment_target_ip=$blue_url
    deployment_port=$blue_port
    old_container_name=green
fi

๋จผ์ € ๋ฐฐํฌ ์„œ๋ฒ„์— Blue IP์— ์š”์ฒญ์„ ๋‚ ๋ ค๋ณด๊ณ  ํ•ด๋‹น ์„œ๋ฒ„์— ์‘๋‹ต์ด ์žˆ์œผ๋ฉด Blue ์„œ๋ฒ„๊ฐ€ Old๊ฐ€ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์„œ๋ฒ„์— ์‘๋‹ต์ด ์—†๋‹ค๋ฉด ํ•ด๋‹น Blue๊ฐ€ New๊ฐ€ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ssh ${deploy_username}@${deploy_ip} "nohup docker run --name ${deployment_container_name} -p ${deployment_port}:8080 ${dockerRepository}:latest > /dev/null &" &

์ด์ œ ๋ฐฐํฌ ์„œ๋ฒ„๋กœ ์ €ํฌ๊ฐ€ ๋งŒ๋“  ์ตœ์‹  ๋ฒ„์ „์˜ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์— > /dev/null & ๋Š” ์ถœ๋ ฅ์„ ๋ฒ„๋ฆฌ๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

์ด์ œ ์ƒˆ๋กœ ๋ฐฐํฌํ•œ ์„œ๋ฒ„๊ฐ€ ์‹ค์ œ๋กœ ์ž˜ ๋–ด๋Š”์ง€ ํ™•์ธํ•ด๋ด์•ผ๊ฒ ์ฃ ?

for retry_count in \$(seq 10)
do
    if curl -s "${deployment_target_ip}" > /dev/null
    then
        echo "์„œ๋ฒ„ Health Check์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."
        break
    fi

    if [ $retry_count -eq 10 ]
    then
        echo "์„œ๋ฒ„ Health Check์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
        exit 1
    fi

    echo "์„œ๋ฒ„ Health Check๋ฅผ 10์ดˆ ์ดํ›„์— ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค..."
    sleep 10
done

 

๋ฐฐํฌํ•œ ์„œ๋ฒ„์— Curl ์š”์ฒญ์„ ๋‚ ๋ ค ์ž˜ ์‹คํ–‰๋˜์—ˆ๋Š”์ง€ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ 10๋ฒˆ๋™์•ˆ Health Check์— ์‹คํŒจํ•˜๋ฉด ํ•ด๋‹น Stage๋Š” ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ssh ${deploy_username}@${deploy_ip} "echo 'set \\\$service_url ${deployment_target_ip};' > /etc/nginx/conf.d/service-url.inc"
ssh ${deploy_username}@${deploy_ip} "docker exec -i nginx service nginx reload"
echo "Nginx Reverse Proxy ๋ณ€๊ฒฝ: ${deployment_target_ip}"

echo "๊ธฐ์กด ๋„์ปค ์„œ๋ฒ„ ์ข…๋ฃŒ"
ssh ${deploy_username}@${deploy_ip} "docker rm -f ${old_container_name}"

 

๋ฐฐํฌ๋œ ์„œ๋ฒ„์˜ Health Check์— ์„ฑ๊ณตํ•˜์˜€๋‹ค๋ฉด, ์ด์ œ Nginx์˜ ํ”„๋ก์‹œ ๋ฐฉํ–ฅ์„ ์ƒˆ๋กœ์šด ์„œ๋ฒ„๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐํฌํ•œ ์„œ๋ฒ„ IP ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  Nginx๋ฅผ reload ์‹œํ‚ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด์— ์‚ด์•„์žˆ๋˜ ๊ตฌ๋ฒ„์ „ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ข…๋ฃŒ ๋ฐ ์‚ญ์ œ ์‹œํ‚ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ์Šคํฌ๋ฆฝํŠธ

pipeline {
    agent any
    
    tools {
        jdk "openjdk-11"
    }
    environment {
        dockerHubRegistry = 'https://example.dockerhub.com'
				dockerRepository = 'example.dockerhub.com/myapp'
    }
    
    stages {
        stage ('git clone') {
            steps {
                git branch: 'main', credentialsId: 'gitlab-credentials', url: 'https://git.example.com/myapp.git'
            }
            post {
                failure {
                  echo 'Repository clone failure !'
                }
                success {
                  echo 'Repository clone success !'
                }
            }
        }
        
        stage('gradle build') {
            steps {
                sh 'chmod +x gradlew'
                sh './gradlew build'
            }
            post {
                failure {
                    echo 'Gradle build failure!'
                }
                success {
                    echo 'Gradle build success!'
                }
            }
        }
        
        stage('Docker Image Build') {
            steps {
                sh "docker build -t ${dockerRepository}:${currentBuild.number} ."
                sh "docker build -t ${dockerRepository}:latest ."
                sh "docker rmi ${dockerRepository}:${currentBuild.number}"
            }
            post {
                    failure {
                      echo 'Docker image build failure!'
                    }
                    success {
                      echo 'Docker image build success!'
                    }
            }
        }
        
        stage('Docker Image Push') {
            steps {
                withCredentials([[$class: 'UsernamePasswordMultiBinding',
                credentialsId: 'dockerhub-credentials',
                usernameVariable: 'DOCKER_USER_ID',
                passwordVariable: 'DOCKER_USER_PASSWORD'
                ]]){
                    sh 'echo ${DOCKER_USER_PASSWORD} | docker login -u ${DOCKER_USER_ID} --password-stdin ${dockerHubRegistry}'
                    sh "docker push ${dockerRepository}:latest"
                }
            }
        }
        
        stage('Server Docker Run') {
            steps {
                sshagent (credentials: ['jenkins-rsa']) {
                    sh '''
                        #!/bin/bash
                        
                        if curl -s "${blue_url}" > /dev/null
                        then
                            deployment_container_name=green
                            deployment_target_ip=$green_url
                            deployment_port=$green_port
                            old_container_name=blue
                        else
                            deployment_container_name=blue
                            deployment_target_ip=$blue_url
                            deployment_port=$blue_port
                            old_container_name=green
                        fi
						                        
                        ssh ${deploy_username}@${deploy_ip} "nohup docker run --name ${deployment_container_name} -p ${deployment_port}:8080 ${dockerRepository}:latest > /dev/null &" &
						                        
                        for retry_count in \$(seq 10)
                        do
                            if curl -s "${deployment_target_ip}" > /dev/null
                            then
                                echo "์„œ๋ฒ„ Health Check์— ์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค."
                                break
                            fi

                            if [ $retry_count -eq 10 ]
                            then
                                echo "์„œ๋ฒ„ Health Check์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
                                exit 1
                            fi

                            echo "์„œ๋ฒ„ Health Check๋ฅผ 10์ดˆ ์ดํ›„์— ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค..."
                            sleep 10
                        done

                        ssh ${deploy_username}@${deploy_ip} "echo 'set \\\$service_url ${deployment_target_ip};' > /root/docker-compose/nginx/nginx/conf.d/service-url.inc"
                        ssh ${deploy_username}@${deploy_ip} "docker exec -i nginx service nginx reload"
                        echo "Nginx Reverse Proxy ๋ณ€๊ฒฝ: ${deployment_target_ip}"
						
                        echo "๊ธฐ์กด ๋„์ปค ์„œ๋ฒ„ ์ข…๋ฃŒ"
                        ssh root@${deploy_ip} "docker rm -f ${old_container_name}"
                    '''
                }
            }
        }
    }
}

์ด๋ ‡๊ฒŒ GitLab, Jenkins, Docker, Nginx๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. Gitlab์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ CI/CD์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ด์ฃผ๊ธฐ๋„ ํ•˜์ง€๋งŒ Jenkins๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ณ  ์‹ถ์–ด์„œ ์‹ค์Šต ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. 

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Trouble Shooting

  • the input device is not a TTYํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
  • ๊ฐ„ํ˜น๊ฐ€๋‹ค bash shell script๋‚˜ ssh, docker์—์„œ -t ์˜ต์…˜์„ ์ค€ ๋‹ค์Œ <<<์„ ํ†ตํ•ด input๊ฐ’์„ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒฝ์šฐ pseudo-TTY์—๋Ÿฌ๊ฐ€ ์ž์ฃผ ๋œจ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” -i์˜ต์…˜์œผ๋กœ input์„ -t์˜ต์…˜์œผ๋กœ ์ธํ•ด interface driver๊ฐ€ tty(stdin/stdout์˜ ์ƒ์œ„)๋กœ ์‹คํ–‰๋˜์–ด์•ผํ•˜๋Š”๋ฐ input pipe๊ฐ€ ๋“ค์–ด์˜ค๋‹ˆ, ์ด๋Ÿด๋•Œ๋Š” t์˜ต์…˜์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด ๋œ๋‹ค.
  • $ ssh root@192.168.0.100 "docker exec -it nginx service nginx reload" the input device is not a TTY
  • linux redirection > permission denied
    • ssh๋กœ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ๋•Œ, ๋ช…๋ น์–ด๋ฅผ ๊ผญ “”๋กœ ๊ฐ์‹ธ์ค˜์•ผ ํ•œ๋‹ค. ์•ˆ๊ทธ๋Ÿฌ๋ฉด ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›ํ•˜๋Š” ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ์˜ˆ์ƒํ•  ์ˆ˜ ์—†๋‹ค.
  • ERROR: JAVA HOME is set to an invalid directory

REFERENCES

 

[Jenkins] # ์„ ์–ธ์ (Declarative) ํŒŒ์ดํ”„๋ผ์ธ

๋“ค์–ด๊ฐ€๊ธฐ ์•ž์„œ ๋‹ค์‹œํ•œ๋ฒˆ ์„ ์–ธ์  ๋ฌธ๋ฒ• ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ค‘์š”ํ•œ ํŠน์ง•์„ ๋ณต๊ธฐํ•ด๋ณด์ž.๊ทธ๋ฃจ๋น„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ‰๋‚ด๋‚ผ ํ•„์š”๊ฐ€ ์—†๋‹ค.๋” ์ ํ•ฉํ•œ ๊ฒ€์ฆ๊ณผ ์—๋Ÿฌํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.์„ ์–ธ์  ํŒŒ์ดํ”„๋ผ์ธ์€ ํฐ ํ•˜๋‚˜์˜

velog.io

 

 

Jenkins ๋กœ ๋„์ปค ์ด๋ฏธ์ง€ Build & Push ์ž๋™ํ™”ํ•˜๊ธฐ

Kubeflow์—๋Š” kfp๋ผ๋Š” Python SDK๊ฐ€ ์กด์žฌํ•˜๋ฉฐ ์ด๋ฅผ ํ†ตํ•ด ๋„์ปค ์ด๋ฏธ์ง€ ์—†์ด ML pipeline์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋”ฅ๋Ÿฌ๋‹์„ ์ˆ˜ํ–‰ํ•  ๊ฒฝ์šฐ ์ฝ”๋“œ ๊ธธ์ด๊ฐ€ ๊ธธ์–ด์ ธ kfp๋กœ ์ฝ”๋“œ ์ž‘์„ฑ ๋ฐ ์ˆ˜์ •์ด ์–ด๋ ค์›Œ์ง€๊ณ  ํฐ ๋ฐ์ด

blog.kubwa.co.kr

๋ฐ˜์‘ํ˜•