Infra/Kubernetes

[쿠버네티스 어나더 클래스 - Sprint2] #4. Jenkins Pipeline (기초부터 Blue/Green 까지)

@sseyeon_ 2026. 9. 21. 18:20
반응형
[참고]
본 글은 인프런의
<쿠버네티스 어나더 클래스 - Sprint 1, 2 (#실무기초 #설치 #배포 #Jenkins #Helm #ArgoCD)>
강의 내용과 실습을 바탕으로 정리한 기록입니다.

 

 

실습에서 다룬 Job (Pipeline)

  • 2211-jenkins_pipeline-step1: 분리되어 있던 세 개의 Job을 하나의 Pipeline으로 통합
  • 2212-jenkins_pipeline-step2: GitHub 연동 및 Pipeline script from SCM 방식으로 전환
  • 2213-jenkins_pipeline-step3: input 스텝을 활용한 수동 Blue/Green 배포
  • 2214: 배포 완료 여부를 폴링하는 자동 Blue/Green 배포

 

 

1. 분리된 Job을 하나의 Pipeline으로

 
Sprint 1에서는 2121-source-build, 2121-container-build, 2121-deploy 세 개의 Job을 각각의 stage로 나눠 관리했습니다.
 

 
이번 단계에서는 이 Job들을 하나의 Pipeline으로 합치는 작업을 수행했습니다. 실무에서 매일 하던 구성이라 이 부분은 빠르게 넘어갔습니다.

 

 

 

2. Pipeline script from SCM으로 전환

Jenkins UI에 스크립트를 직접 박아 넣는 대신, GitHub 저장소의 Jenkinsfile을 읽어오도록 변경했습니다.
설정 포인트 3가지

✅ Sparse Checkout을 쓰는 이유: 하나의 저장소에 여러 실습 번호(2211, 2212, 2213…)가 함께 들어 있을 때, 전체를 내려받지 않고 해당 Job이 필요로 하는 디렉토리만 체크아웃할 수 있습니다. 저장소가 커질수록 체크아웃 시간과 워크스페이스 용량에서 차이가 납니다.

 

 

 

 

 

3. Blue/Green 배포 실습 (수동)

input 스텝으로 각 단계마다 확인을 받으면서 트래픽이 어떻게 넘어가는지 직접 관찰했습니다.

 

3-1. Blue 배포 — Green Pod 생성

첫 번째 Yes를 누르면 기존 Blue Pod 2개는 그대로 둔 채, Green Pod 2개가 추가로 생성됩니다. 이 시점에는 Pod가 총 4개입니다.

# 운영 트래픽 — 계속 v1.0.0 응답
while true; do curl http://192.168.56.30:32213/version; sleep 1; echo ''; done;
# [App Version] : Api Tester v1.0.0
# [App Version] : Api Tester v1.0.0

# QA 담당자 전용 — 검증용 Service로 v2.0.0 직접 호출
curl http://192.168.56.30:32223/version
# [App Version] : Api Tester v2.0.0

 
 
✅ 핵심: 사용자는 여전히 v1을 보고 있는 상태에서, QA는 별도 포트(32223)로 v2를 미리 검증할 수 있습니다.
 
 
 
 

3-2. Green 전환

두 번째 Yes를 누르면 Service의 selector가 바뀌면서, 돌고 있던 32213 호출 결과가 v1에서 v2로 전환됩니다.

 

3-3. Blue 삭제

마지막으로 Done을 누르면 Blue Pod가 삭제되고 Green만 남습니다.

 

 

 

 

4. Blue/Green 배포 시 고려해야 할 요소

 
Sprint 1에서 라벨(Label)과 셀렉터(Selector) 매칭 구조를 정리했는데, Blue/Green은 그 설계가 실제로 왜 중요한지 바로 드러나는 지점이었습니다.
 
 
 
1) Blue-Green 배포를 고려한 Deployment 네이밍
Deployment 이름 뒤에 구분자를 붙입니다. api-tester-2213-1(Blue), api-tester-2213-2(Green) 형태로, 두 벌의 Deployment가 동시에 존재할 수 있어야 합니다.
 
2) Blue-Green 배포를 위한 추가 레이블 및 셀렉터
기존 5개 라벨(part-of, component, name, instance, version)에 blue-green-no 라벨을 하나 더 추가합니다.
 

구분
Deployment
Pod label
Service selector
Blue
api-tester-2213-1
blue-green-no: 1, version: 1.0.0
blue-green-no: "1"
Green
api-tester-2213-2
blue-green-no: 2, version: 2.0.0
blue-green-no: "2"

✅ 여기서 중요한 점: Service는 part-of, component, name, instance까지는 Blue/Green이 완전히 동일하고, 오직 blue-green-no 값 하나만 다릅니다. 그래서 이 라벨 값 하나만 1 → 2로 바꾸면 트래픽 전체가 넘어갑니다.

 

 

 

 

5. 리소스 변경 사항 정리

수동 배포 전체 흐름을 리소스 관점에서 3단계로 정리하면 다음과 같습니다.

  1. 생성: Green Deployment 생성 + 검증 전용 Service(api-tester-2) 생성
  2. 전환: 운영 Service의 selector blue-green-no를 "1" → "2"로 변경
  3. 정리: Blue Deployment 삭제, Green 전용 Service 삭제, 그리고 남은 리소스들의 version 라벨을 1.0.0 → 2.0.0으로 갱신

3번에서 문제가 드러납니다. Deployment만 정리하면 끝이 아니라 Service, ConfigMap, Secret에 붙은 version 라벨까지 전부 따라가야 합니다. 리소스가 늘어날수록 하나씩 누락될 위험이 커지고, 이걸 사람 손으로 챙기는 건 현실적이지 않습니다. Helm을 쓰는 이유가 여기서 나옵니다.
참고로 instance 라벨은 Blue/Green 간에 다르게 주는 경우가 많지는 않습니다.

 

 

 

 

6. 자동 배포 만들어보기

수동 단계의 input을 걷어내고, Green Pod가 전부 Ready가 될 때까지 폴링하도록 만들었습니다.

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

stage('Green 배포 확인중') {
    steps {
        script {
            def returnValue
            // Pod 2개가 모두 Ready("true true")가 될 때까지 5초 간격으로 확인
            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"
        // 남은 리소스들의 version 라벨 갱신
        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\"}}}'"
    }
}

✅ 코드에서 읽어야 할 포인트

  • while 문이 핵심: jsonpath로 Pod의 containerStatuses[*].ready를 뽑아 "true true"가 될 때까지 대기합니다. Pod가 2개니까 true도 2개여야 합니다.
  • 전환은 kubectl patch 한 줄: Service의 selector.blue-green-no만 바꾸면 끝입니다. 4번 항목의 라벨 설계가 그대로 코드에 반영된 모습입니다.
  • 뒷정리 patch가 3줄: Service, ConfigMap, Secret의 version 라벨을 각각 수동으로 갱신하고 있습니다. 5번에서 말한 누락 위험이 코드에서 그대로 보입니다.

다만 이건 실무용이 아닙니다. while로 무한 대기하는 구조라 타임아웃도 없고, 실패 시 롤백 처리도 없습니다. Blue/Green이 원리적으로 이렇게 돌아간다는 것을 보여주는 코드에 가깝습니다.
그럼 실무에서는? → ArgoCD 같은 GitOps 툴을 활용합니다.

 

 

 

 

마무리

Sprint 1에서 라벨과 셀렉터 매칭 구조를 다이어그램으로 그려봤을 때는 “규칙을 잘 지키면 관리가 편해지겠구나” 정도의 감이었는데, Blue/Green을 직접 만들어보니 그 효용이 훨씬 구체적으로 와닿았습니다. 라벨 설계가 잘 되어 있으면 무중단 전환이 patch 한 줄로 끝나고, 안 되어 있으면 배포 때마다 리소스를 손으로 뒤져야 합니다.
동시에 왜 Helm과 ArgoCD가 필요한지도 자연스럽게 이해됐습니다. Jenkinsfile에 kubectl patch를 세 줄 쌓고 있는 순간, 이건 리소스가 늘어나면 관리가 안 되는 방식이라는 게 코드 자체로 드러났기 때문입니다.

반응형