FrameWorks/Kubernetes

[Kubernetes] Jenkins Pipeline - 기초부터 Blue, Green 까지

ABCD 2025. 3. 12.

의미 없는 서두...

이번 편의 공부는 삽질을 엄청 많이 했습니다.
그렇게 해서 배운 것도 많긴하지만 서도... 강의는 40분 안쪽 강의인데 1시간 30분? 2시간?? 정도를 삽질한거 같네요...

 

이유는 개인 GitHub에 강의용 Repository가 추가되는게 싫어서 기존에 사용하던 Study관련 레파지토리에 일프로님이 올려주신 소스코드, 배포파일 등을 받아서 사용하다보니 path관련과 Jenkins에서 script 실행시 시작 path때문에 골머리를 엄청 썩였습니다...

 

만약, 이런 고생을 하고 싶지 않으시다면 강의를 들으실때는 그냥 하라는 대로 하는게 나을수도...

 

하지만 삽질한 만큼 얻어가는건 많네요!!

 

그래서 이번편 정리는 그냥 일프로님이 하라는대로 하는 그런 방식으로 정리하겠습니다!!

참고... 실패의 흔적...

이미지

그림으로 이해하는 CI/CD 배포 시작전에 알아야 할 것들

이미지

CI/CD 파이프라인을 구성할 때 고려해야 하는 요소

이미지

배포 전략을 세울 때 고려해야 하는 요소

이미지

단계별 파이프라인 국축 방법

이미지


기초부터 Blud/Green까지 배포 구축 단계

이미지

Jenkins로 기본 구성 만들기

New view만들기

이전 작업 만들어서 정리해 놓기

이번 수업을 위한 만들기

Jenkins Pipeline 기본구성 만들기(Step1)

221 View 선택 후 클릭

item name 입력 및 Pipeline 선택

name 부분에는 '2211-jenkins_pipeline-step1'를 작성해 줍니다.

Script 복사

  • Pipeline script
    • DOCKERHUB_USERNAME 및 GITHUB_URL 주소 (본인의 Username으로 변경)

참고로 step을 구성할 때에 입력하는 방식을 모르겠다면 아래 Pipeline Syntax를 클릭해서 원하는 script를 UI로 만들어 code를 생성할 수 있습니다.

pipeline {
    agent any

    tools {
        gradle 'gradle-7.6.1'
        jdk 'jdk-17'
    }

    environment {
        // 본인의 username으로 하실 분은 수정해주세요.
        DOCKERHUB_USERNAME = '1pro'
        GITHUB_URL = 'https://github.com/k8s-1pro/kubernetes-anotherclass-sprint2.git'
        // deployment.yaml -> image: 1pro/api-tester:v1.0.0        

        // 실습 넘버링 - (수정x)
        CLASS_NUM = '2211'
    }

    stages {
        stage('Source Build') {
            steps {
                // 소스파일 체크아웃
                git branch: 'main', url: 'https://github.com/k8s-1pro/kubernetes-anotherclass-api-tester.git'

                // 소스 빌드
                // 755권한 필요 (윈도우에서 Git으로 소스 업로드시 권한은 644)
                sh "chmod +x ./gradlew"
                sh "gradle clean build"
            }
        }

        stage('Container Build') {
            steps {
                // 릴리즈파일 체크아웃
                checkout scmGit(branches: [[name: '*/main']],
                extensions: [[$class: 'SparseCheckoutPaths',
                        sparseCheckoutPaths: [[path: "/${CLASS_NUM}"]]]],
                userRemoteConfigs: [[url: "${GITHUB_URL}"]])

                // jar 파일 복사
                sh "cp ./build/libs/app-0.0.1-SNAPSHOT.jar ./${CLASS_NUM}/build/docker/app-0.0.1-SNAPSHOT.jar"

                // 컨테이너 빌드 및 업로드
                sh "docker build -t ${DOCKERHUB_USERNAME}/api-tester:v1.0.0 ./${CLASS_NUM}/build/docker"
                script{
                    if (DOCKERHUB_USERNAME == "1pro") {
                        echo "docker push ${DOCKERHUB_USERNAME}/api-tester:v1.0.0"
                    } else {
                        sh "docker push ${DOCKERHUB_USERNAME}/api-tester:v1.0.0"
                    }
                }
            }
        }

        stage('K8S Deploy') {
            steps {
                // 쿠버네티스 배포 
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/namespace.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/configmap.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/secret.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/service.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/deployment.yaml"
            }
        }
    }
}

지금빌드 샐행 후 Stage View 결과 확인

이렇게 파이프라인으로 생성을 하면 new Item으로 하나씩 구성해서 만든것에 비해 각 Stage마다 얼마나 시간이 걸렸고 어느 곳에서 가장 오래 걸리는지 등을 파악할 수 있습니다. 이점이 용이하지요??

Jenkins Pipeline 구조 이해

Agent

Jenkins Docs 에서 좀더 자세히 알아 볼 수 있습니다.

agent any

사용 가능한 에이전트에서 파이프라인 Stage를 실행, Master나 Salve 아무곳에서나 Stage가 실행됨

agent label(node)

지정된 레이블(노드)에서 Stage가 실행

agent docker

Docker 빌드를 제공해주는 Agent 사용

agent dockerfile

Dockerfile 을 직접 쓸 수 있는 Agent 사용

Github 연결 및 파이프라인 세분화 (Step 2)

item name 입력 및 Pipeline 선택

name에는 '2212-jenkins_pipeline-step2'라고 입력해줍니다.

Configure > General > GitHub project에 Project url 입력

프로젝트 url은 아래와 같이 입력하면 됩니다.
Github URL을 본인꺼를 사용해도 되지만, 이 경우에는 일프로님 깃허브를 사용해도 무방합니다.

# 일프로님 깃허브
Project url : https://github.com/k8s-1pro/kubernetes-anotherclass-sprint2/

# 개인 깃허브
Project url : https://github.com/${username}/kubernetes-anotherclass-sprint2/

Configure > Advanced Project Options > Pipeline 구성 (1/2)

Repository URL : https://github.com/k8s-1pro/kubernetes-anotherclass-sprint2.git

Configure > Advanced Project Options > Pipeline (2/2)

Branch Specifier : */main
Path : 2212
Script Path : 2212/Jenkinsfile

저장 후 지금 빌드 실행

설정 항목을 저장하고 item화면으로 이동하면 지금 빌드를 실행해 빌드를 시작합니다.

Stage View 결과 확인

젠킨스 스크립트 확인

Jenkinsfile << 클릭하시면 소스코드를 확인할 수 있습니다.

실습 후 정리

실습 후 다음 실습에 방해가 되지 않도록 다음 명령어를 통해 방금 만든 Namespace를 삭제해 주세요.

kubectl delete ns anotherclass-221

Blue/Green 배포 만들기 및 특징 실습 (Step 3)

item name 입력 및 Pipeline 선택

Enter an item name : 2213-jenkins_pipeline-step3
Copy from : 2212-jenkins_pipeline-step2

Configure > Additional Behaviours 및 Script Path 수정 후 저장

Path : 2213
Script Path : 2213/Jenkinsfile

Master Node에서 version 조회 시작

while true; do curl http://${Master Node IP}:32213/version; sleep 1; echo ''; done;

이후 '지금 빌드' 실행합니다. 그리고 '수동배포 시작' yes 클릭하게 되면 Green이 배포됩니다.

그렇게 되면 QA 담당자는 V2 Service 호출 가능해 집니다. 한번 정상적으로 불러오는지 확인해볼까요?? 아래 커멘드를 입력 후 다음 응답이 나온다면 정상 동작하는 것입니다.

curl http://${Master Node IP}:32223/version
Api Tester v2.0.0

그러면 이번엔 Blue와서 완전히 Green으로 전환을 해보겠습니다.
아래 이미와 같이 yes 클릭하게 되면 v2로 버전이 완전히 변경된 것을 확인 할 수 있습니다.

그전 다음 명령어를 실행해봅시다.

while true; do curl http://${Master Node IP}:32213/version; sleep 1; echo ''; done;

v1에서 v2로 버전이 변경된 것을 확인하셨나요??
while문으로 반복작업을 했기 때문에 다음과 같이 나와야 정상입니다.

롤백 여부 선택

  • done : Blue 배포 삭제됨
  • rollback : Blue로 다시 전환됨

Blue/Green시 필요한 요소

네이밍

Blue-Green 배포를 고려한 Deployment 네이밍 필요합니다.
아래 이미지에서 보시면 Deployment를 잘 보시면 각각

  • api-tester-2213-1
  • api-tester-2213-2

이 두개가 보이실 겁니다. 이 뒤에 붙어 있는 -1-2가 네이밍이라고 생각하시면 됩니다.

레이블 및 셀렉터

Blue-Green 배포를 위한 추가 레이블 및 셀렉터 필요가 필요합니다.
아래 Pod를 보시면 labels에 새로 추가된 속성이 보이실 겁니다.

  • blue-green-no: 1
  • blue-green-no: 2

이 두개가 보이실 건데요. 이렇게 각가 두어서 두개를 동시에 올릴 수 있습니다.

리소스 변경 사항 리뷰

  1. Green Deployment 생성 (네이밍, version, blue-green-no 고려), 테스트를 위한 Service 생성, QA 담당자 테스트 실시
  2. Service의 Selector (blue-green-no)를 2로 변경하여 트래픽 Green으로 전환
  3. Blue Deployment, Green Service 삭제 및 관련 모든 리소스의 레이블 정보 변경 (version) or Rollback

젠킨스 스크립트 확인

Jenkinsfile << 를 클릭하면 페이지가 열립니다.

create를 사용하지 않고 apply를 사용하는 이유

create를 사용하면 Pod을 새로 만들도록 동작을 하게 됩니다.
만약 기존에 Pod이 있다면 충돌이나거나 오류가 발생하게 되는데요.
이를 방지하기 위해 apply를 사용한 것입니다.

 

우리는 통상 수정한것을 새로 수정하거나 할 경우 기존에 있던 것을 삭제하고 적용시키지 않죠??

 

apply는 이런상황에서 우리가 원하는대로 동작하기 때문에 충돌이나 오류가 발생하지 않고 변경사항을 적용시켜주게 됩니다.

input

단순히 동작을 중간에 일시정지하기 위해 사용했고, 잘 보시면 데이터를 담아오는 부분도 있습니다.
데이터를 담아오는 부분은 48번째 줄을 참고하시면 되는데 다양하게 사용할 수 있으니 사용을 원할 때, 찾아보고 사용해보시면 되겠습니다.

pipeline {
    agent any

    environment {
        // 본인의 username으로 하실 분은 수정해주세요.
        GITHUB_URL = 'https://github.com/k8s-1pro/kubernetes-anotherclass-sprint2.git'

        // 실습 넘버링
        CLASS_NUM = '2213'
    }

    stages {
        stage('릴리즈파일 체크아웃') {
            steps {
                checkout scmGit(branches: [[name: '*/main']],
                        extensions: [[$class: 'SparseCheckoutPaths',
                                      sparseCheckoutPaths: [[path: "/${CLASS_NUM}"]]]],
                        userRemoteConfigs: [[url: "${GITHUB_URL}"]])
            }
        }

        stage('쿠버네티스 Blue배포') {
            steps {
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/namespace.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/configmap.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/secret.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/service.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/deployment.yaml"
            }
        }

        stage('배포 시작') {
            steps {
                input message: '수동배포 시작', ok: "Yes"
            }
        }

        stage('쿠버네티스 Green배포') {
            steps {
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/green/deployment.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/green/service.yaml"
            }
        }

        stage('전환여부 확인') {
            steps {
                script {
                    returnValue = input message: 'Green 전환?', ok: "Yes", parameters: [booleanParam(defaultValue: true, name: 'IS_SWITCHED')]
                    if (returnValue) {
                        sh "kubectl patch -n anotherclass-221 svc api-tester -p '{\"spec\": {\"selector\": {\"blue-green-no\": \"2\"}}}'"
                    }
                }
            }
        }

        stage('롤백 확인') {
            steps {
                script {
                    returnValue = input message: 'Blue 롤백?', parameters: [choice(choices: ['done', 'rollback'], name: 'IS_ROLLBACk')]
                    if (returnValue == "done") {
                        sh "kubectl delete -f ./${CLASS_NUM}/deploy/k8s/blue/deployment.yaml"
                        sh "kubectl delete -f ./${CLASS_NUM}/deploy/k8s/green/service.yaml"
                        sh "kubectl patch -n anotherclass-221 svc api-tester -p '{\"metadata\": {\"labels\": {\"version\": \"2.0.0\"}}}'"
                        sh "kubectl patch -n anotherclass-221 cm api-tester-properties -p '{\"metadata\": {\"labels\": {\"version\": \"2.0.0\"}}}'"
                        sh "kubectl patch -n anotherclass-221 secret api-tester-postgresql -p '{\"metadata\": {\"labels\": {\"version\": \"2.0.0\"}}}'"
                    }
                    if (returnValue == "rollback") {
                        sh "kubectl patch -n anotherclass-221 svc api-tester -p '{\"spec\": {\"selector\": {\"blue-green-no\": \"1\"}}}'"
                    }
                }
            }
        }
    }

}

다음 시습을 위해 데이터 초기화

다음 실습에 방해가 되지 않도록 다음 명령어를 통해 데이터를 초기화 주세요.

kubectl delete ns anotherclass-221

Blue/Green 자동 배포 Script 만들기 (Step 4)

자동 배포의 경우 새로하는 배포하는 앱에 오류가 없음을 가정하고 작업을 합니다.

item name 입력 및 Pipeline 선택

Enter an item name : 2214-jenkins_pipeline-step4
Copy from : 2213-jenkins_pipeline-step3

Configure > Additional Behaviours 및 Script Path 수정 후 저장

Path : 2214
Script Path : 2214/Jenkinsfile

Master Node에서 version 조회 시작

while true; do curl http://${Master Node IP}:32214/version; sleep 1; echo ''; done;

조회를 시작헀으면 지금 빌드를 실행하여 동작을 모니터링 해봅시다.
그리고 자동배포 시작에서 yes 클릭하여 자동 배포를 진행해보도록 하겠습니다.

한번 진행 과정을 확인해 볼까요??

Green 배포 확인중에 v2.0.0으로 변경이 되는 것을 확인 할 수 있는데요.
다음과 같이 나온다면 성공한 것입니다.

리소스 변경 사항 리뷰

  1. Green Deployment 생성 (네이밍, version, blue-green-no 고려)
  2. Green Pod에서 Ready 상태 확인
  3. Service의 Selector (blue-green-no)를 2로 변경하여 트래픽 Green으로 전환
  4. Blue Deployment 삭제 및 관련 모든 리소스의 레이블 정보 변경 (version)

젠킨스 스크립트 확인

Jenkinsfile << 클릭하면 소스를 볼 수 있습니다.

Green배포 확인을 하는 스크립트를 확인해보겠습니다.
이 스크립트에서 사용하는 옵션에 대해 먼저 설명하겠습니다.

returnValue

while문에 사용할 boolean값을 담기위한 변수

sh

쉘 스크립트 명령어를 사용하기 위한 함수

  • returnStdout : 결과값을 return할 건지에 대한 여부
  • encoding : 인코딩 타입
  • script : 쉘 스크립트 명령어

이젠 쉘 스크립트 명령어를 한번 알아볼까요? 대충 눈치가 있으신분들은 보이실테니 해당 옵션의 대한 값이 무엇을 나타내는지만 설명하겠습니다.

-n

네임스페이스

pods

현재 Pod을 조회하겠다는 옵션

-l

라벨의 내용을 매칭시키는 역할이며 콤마(,)를 기준으로 나열해서 사용 할 수 있습니다.

-o

Pod의 내부 값을 추출하기 위한 옵션
jsonpath='{.items[*].status.containerStatuses[*].ready}'다음과 같이 입력하면 PodStatusReady상태추출 하겠다는 것입니다.

 

그럼 하나 궁금증이 생기는데요.

 

왜?? returnValue가 true true인 것이 참이라고 만들었을까요??

 

네!! 맞습니다.

 

바로 우리가 기본적으로 Green배포시에 replica를 2개로 설정해서 해당 라벨의 정보에 포함되는 Pod가 2개이기 때문에 true true가 리턴값으로 나오는 겁니다.

stage('Green 배포 확인중') {
    steps {
        script {
            def returnValue
            while (returnValue != "true true"){
                returnValue = sh(returnStdout: true, encoding: 'UTF-8', script: "kubectl get -n anotherclass-221 pods -l instance='api-tester-2214',blue-green-no='2' -o jsonpath='{.items[*].status.containerStatuses[*].ready}'")
                echo "${returnValue}"
                sleep 5
            }
        }
    }
}
pipeline {
    agent any

    environment {
        // 본인의 username으로 하실 분은 수정해주세요.
        GITHUB_URL = 'https://github.com/k8s-1pro/kubernetes-anotherclass-sprint2.git'

        // 실습 넘버링
        CLASS_NUM = '2214'
    }

    stages {
        stage('릴리즈파일 체크아웃') {
            steps {
                checkout scmGit(branches: [[name: '*/main']],
                        extensions: [[$class: 'SparseCheckoutPaths',
                                      sparseCheckoutPaths: [[path: "/${CLASS_NUM}"]]]],
                        userRemoteConfigs: [[url: "${GITHUB_URL}"]])
            }
        }

        stage('쿠버네티스 Blue배포') {
            steps {
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/namespace.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/configmap.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/secret.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/service.yaml"
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/blue/deployment.yaml"
            }
        }

        stage('자동배포 시작') {
            steps {
                input message: '자동배포 시작', ok: "Yes"
            }
        }

        stage('쿠버네티스 Green배포') {
            steps {
                sh "kubectl apply -f ./${CLASS_NUM}/deploy/k8s/green/deployment.yaml"
            }
        }

        stage('Green 배포 확인중') {
            steps {
                script {
                    def returnValue
                    while (returnValue != "true true"){
                        returnValue = sh(returnStdout: true, encoding: 'UTF-8', script: "kubectl get -n anotherclass-221 pods -l instance='api-tester-2214',blue-green-no='2' -o jsonpath='{.items[*].status.containerStatuses[*].ready}'")
                        echo "${returnValue}"
                        sleep 5
                    }
                }
            }
        }

        stage('Green 전환 완료') {
            steps {
                sh "kubectl patch -n anotherclass-221 svc api-tester-2214 -p '{\"spec\": {\"selector\": {\"blue-green-no\": \"2\"}}}'"
            }
        }

        stage('Blue 삭제') {
            steps {
                sh "kubectl delete -f ./${CLASS_NUM}/deploy/k8s/blue/deployment.yaml"
                sh "kubectl patch -n anotherclass-221 svc api-tester-2214 -p '{\"metadata\": {\"labels\": {\"version\": \"2.0.0\"}}}'"
                sh "kubectl patch -n anotherclass-221 cm api-tester-2214-properties -p '{\"metadata\": {\"labels\": {\"version\": \"2.0.0\"}}}'"
                sh "kubectl patch -n anotherclass-221 secret api-tester-2214-postgresql -p '{\"metadata\": {\"labels\": {\"version\": \"2.0.0\"}}}'"
            }
        }
    }
}

데이터 초기화

다음 실습을 위해 다음 명령어를 통해 데이터를 초기화 해주시면 됩니다.

kubectl delete ns anotherclass-221

 

이 글은 인프런의 일프로님 강의 내용을 재정리한 내용입니다.
728x90
반응형

댓글

💲 추천 글