3.2 DevOps 서비스의 Blue/Green 배포 전략으로 배포하기
DevOps 서비스를 통한 CI/CD 배포 자동화 하기
사전 준비 사항
- 4.1.1 NGINX Ingress Controller 설치하기을 참조하여 Nginx Ingress Controller를 미리 설치합니다.
DevOps 프로젝트 만들기
이전에 생성한 프로젝트를 그대로 사용하거나, 새롭게 프로젝트를 만듭니다.
애플리케이션 배포 자동화 하기
Code Repository를 사용하여 애플리케이션 코드 관리하기
샘플로 사전에 만든 Nginx 기반 웹앱을 사용하여 만들어 테스트하겠습니다.
코드 저장소 생성을 위해 왼쪽 메뉴에서 Code Repositories를 클릭합니다.
Create repository를 클릭하여 저장소를 만듭니다.
- Repository name: 예) oci-devops-oke-webpage
실제 개발 작업은 git 명령을 통해 개발 PC에서 진행하면 됩니다. 저장소 상세정보 위에 있는 Clone 버튼을 하면 Clone 명령어가 아래 그림처럼 뜨게 됩니다. 여기서는 Clone with HTTPS를 사용하겠습니다.
개발 PC에 복사한 주소를 사용해 git clone 명령어를 통해 복제합니다.
git clone <YourClonewithHTTPS URL>
이때 사용자 인증이 필요합니다. HTTPS기반 사용자 인증시 아래 유저명 형식과 AuthToken을 사용합니다.
이미 개발된 소스를 가져와 Clone한 저장소로 옮깁니다.
wget https://github.com/TheKoguryo/oci-devops-oke-webpage/archive/refs/tags/v2022.04.tar.gz tar -xvzf v2022.04.tar.gz --strip-components=1 -C oci-devops-oke-webpage/
코드를 Code Repository에 Push 합니다.
cd oci-devops-oke-webpage git add . git commit -m "init" git push
코드 작성 및 반영 완료
- Dockerfile, default.conf.template, html 폴더: nginx 기반 웹 앱의 이미지를 빌드 소스 파일입니다.
- build_spec.yaml: DevOps 서비의 빌드 스테이지에서 사용하는 빌드 정의서입니다.
- oci-oke-deployment.yaml: OKE에 배포하기 위해 정의된 Kubernetes Manifest 파일입니다. 이후 다시 설명하겠습니다.
Build Pipeline 만들기
CI/CD 중에 코드를 빌드하여 배포 산출물을 만드는 CI 과정에 해당되는 부분을 Build Pipeline을 통해 구성이 가능합니다.
프로젝트 페이지로 이동하여 왼쪽 메뉴의 Build Pipelines로 이동합니다.
Create build pipeline을 클릭하여 파이프라인을 생성합니다.
- Name: 예) webpage-build-pipeline
생성된 파이프라인을 클릭합니다.
그림과 같이 Stage를 추가하여 파이프라인 흐름을 구성할 수 있습니다. Add Stage를 클릭합니다.
Build Stage 만들기
빌드를 위해 먼저 Managed Build Stage를 추가합니다.
Managed Build Stage 설정
Stage name: 예) build-stage
Build Spec File Path: 빌드 스크립트 경로를 지정합니다. 기본적으로 소스 루트에 있는 build_spec.yaml을 파일을 사용합니다.
Primary Code Repository: 빌드할 메인 소스가 있는 코드 저장소를 지정합니다.
대상 소스 코드가 있는 저장소를 지정합니다.
설정된 Stage를 Add를 클릭하여 추가합니다.
테스트처럼 소스 코드상의 Build Spec의 정의가 필요합니다.
개발한 oci-devops-oke-webpage 소스 코드의 root 경로에 build_spec.yaml을 이미 정의해 두었습니다. 정의한 내용을 확인합니다.
build_spec.yaml
- outputArtifacts.output-image: 빌드 산출물인 컨테이너 이미지를 지칭합니다.
- outputArtifacts.output-oci-oke-deployment: 빌드 산출물로 소스코드상에 있는 OKE 배포용 Manifest 파일입니다.
- exportedVariables
- IMAGE_PATH: 빌드되는 환경정보와, APP_NAME을 기반으로 OCIR 리파지토리를 계산합니다.
- TAG: 편의를 위해 코드 Commit ID 또는 BUILD_ID 해쉬값으로 지정합니다.
version: 0.1 component: build timeoutInSeconds: 6000 runAs: root shell: bash env: # these are local variables to the build config variables: defaultAppName: "webpage" defaultContextPath: "/webpage" # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service # you can then access the value of that secret in your build_spec.yaml commands vaultVariables: # exportedVariables are made available to use as parameters in sucessor Build Pipeline stages # For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set exportedVariables: - BUILDRUN_HASH - APP_NAME - IMAGE_PATH - TAG - CONTEXT_PATH # Its a native way to fetch artifacts from external or artifact repo or a file path to use before a stage. # More about buildspec formats - https://docs.oracle.com/en-us/iaas/Content/devops/using/build_specs.htm inputArtifacts: steps: - type: Command name: "Init exportedVariables" timeoutInSeconds: 30 command: | APP_NAME=$defaultAppName CONTEXT_PATH=$defaultContextPath echo $APP_NAME echo $CONTEXT_PATH - type: Command name: "Build Source" timeoutInSeconds: 4000 command: | echo no action - type: Command name: "Define Image Tag - Commit ID" timeoutInSeconds: 30 command: | COMMIT_ID=`echo ${OCI_TRIGGER_COMMIT_HASH} | cut -c 1-7` BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7` [ -z "$COMMIT_ID" ] && TAG=$BUILDRUN_HASH || TAG=$COMMIT_ID - type: Command name: "Define Image Path" timeoutInSeconds: 30 command: | TENANCY_NAMESPACE=`oci os ns get --query data --raw-output` REPO_NAME=$APP_NAME IMAGE_PATH=$OCI_RESOURCE_PRINCIPAL_REGION.ocir.io/$TENANCY_NAMESPACE/$REPO_NAME - type: Command timeoutInSeconds: 1200 name: "Build Container Image" command: | cd ${OCI_PRIMARY_SOURCE_DIR} docker build --pull --rm -t new-generated-image . --build-arg CONTEXT_PATH=$CONTEXT_PATH - type: Command name: "Check exportedVariables" timeoutInSeconds: 30 command: | [ -z "$APP_NAME" ] && APP_NAME=unknown [ -z "$IMAGE_PATH" ] && IMAGE_PATH=unknown [ -z "$TAG" ] && TAG=unknown echo "APP_NAME: " $APP_NAME echo "IMAGE_PATH: " $IMAGE_PATH echo "TAG: " $TAG outputArtifacts: - name: output-image type: DOCKER_IMAGE # this location tag doesn't effect the tag used to deliver the container image # to the Container Registry location: new-generated-image - name: output-oci-oke-deployment type: BINARY location: ${OCI_PRIMARY_SOURCE_DIR}/oci-oke-deployment.yaml
빌드 스테이지가 완성되었습니다.
빌드 산출물을 저장할 Artifact 저장소 만들기
- 좌측 상단 햄버거 메뉴에서 Developer Services > Containers & Artifacts > Artifact Registry로 이동합니다.
- Create repository를 클릭하여 저장소를 추가합니다.
- Name: 예) oci-hol-artifact-repository
- Immutable artifacts: 테스트용으로 체크 해제
빌드 산출물을 OCIR 및 Artifact 저장소에 저장하기
Build Pipeline 탭으로 이동합니다.
플러스 버튼을 클릭하여 build-stage 다음에 stage를 추가합니다.
Delivery Artifact Stage를 선택합니다.
stage 이름을 입력하고 Create Artifact를 선택합니다.
Container image 유형으로 Artifact 추가합니다.
이미지 경로: docker tag를 달때 사용하는 이미지 경로입니다. 직접 입력해도 되지만 여기서는 build-stage에서 넘어온 exportedVariable을 사용하여 아래와 같이 입력합니다.
- Name: generated_image_with_tag
- Image Path: ${IMAGE_PATH}:${TAG}
같은 방식으로 하나 더 추가 합니다.
- Name: generated_image_with_latest
- Image Path: ${IMAGE_PATH}:latest
Kubernetes Manifest 파일을 앞서 생성한 Artifact Repository에 추가합니다.
Name: 예) oci-oke-deployment.yaml
Artifact path: oci-devops-oke-webpage/oci-oke-deployment.yaml
Version: ${BUILDRUN_HASH}
Artifact 매핑
Associate Artifact에서 방금 추가한 3개의 Artifact에 실제 컨테이너 이미지 파일을 매핑해 줍니다. 앞서 build-stage에서 build_spec.yaml에서 정의한 outputArtifacts의 이름을 입력합니다.
outputArtifacts: - name: output-image type: DOCKER_IMAGE # this location tag doesn't effect the tag used to deliver the container image # to the Container Registry location: new-generated-image - name: output-oci-oke-deployment type: BINARY location: ${OCI_PRIMARY_SOURCE_DIR}/oci-oke-deployment.yaml
이제 delivery stage까지 추가하였습니다.
Blue/Green Deployment Pipeline 만들기
CI/CD 중에 빌드된 산출물을 가지고 실제 서버에 배포하는 CD 과정에 해당되는 부분을 Deployment Pipeline을 통해 구성이 가능합니다.
Blue/Green 전략으로 쿠버네티스에 배포하기 위해서는 배포 환경, 배포할 Kubernetes Manifest 파일, 배포에 사용될 Kubernetes Namespace 두 개가 필요합니다.
Kubernetes Environment 등록하기
- 앞서 등록한 OKE 환경이 없는 경우, 프로젝트 페이지로 이동하여 왼쪽 메뉴의 Environments로 이동하여 배포할 OKE 환경을 등록합니다.
Kubernetes에 배포할 manifest 파일 준비
Kubernetes에 배포할 Stage 유형을 사용하기 위해서는 사전에 배포할 manifest yaml 파일을 준비해야 합니다.
앞서 실습과 같이 inline 형식으로 하는 방법도 있고, 이번에서 앞서 가져온 소스 프로젝트 파일에 Kubernetes Manifest 파일을 관리하고 사용할 수도 있습니다.
사용할 샘플 Manifest 파일
- Deployment, Service: 빌드된 컨테이너 앱을 배포할 Deployment와 Cluster IP기반 Service
- Ingress: Blue/Green 또는 Canary 배포를 위해 필요합니다. 둘다 Nginx Ingress Controller를 기반으로 라우팅 규칙을 조절하여 해당 기능을 제공하고 있습니다.
apiVersion: apps/v1 kind: Deployment metadata: name: ${APP_NAME}-deployment spec: selector: matchLabels: app: ${APP_NAME} replicas: 3 template: metadata: labels: app: ${APP_NAME} spec: containers: - name: ${APP_NAME} # enter the path to your image, be sure to include the correct region prefix image: ${IMAGE_PATH}:${TAG} imagePullPolicy: Always ports: - containerPort: 80 protocol: TCP readinessProbe: exec: command: - cat # For demo, see the start status easily about rolling updates. initialDelaySeconds: 5 periodSeconds: 5 livenessProbe: exec: command: - cat initialDelaySeconds: 5 periodSeconds: 5 env: - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace --- apiVersion: v1 kind: Service metadata: name: ${APP_NAME}-service spec: type: ClusterIP ports: - port: 8080 protocol: TCP targetPort: 80 selector: app: ${APP_NAME} --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ${APP_NAME}-ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: /$2 spec: rules: - http: paths: - path: ${CONTEXT_PATH}(/|$)(.*) pathType: Prefix backend: service: name: ${APP_NAME}-service port: number: 8080
Kubernetes에 배포할 네임스페이스 만들기
블루 앱이 배포될 네임스페이스, 그린 앱이 배포될 네임스페이스 필요합니다. 각 네임스페이스에 동일한 앱이 현재 버전, 신규 버전이 각각 배포되게 되고, 라우팅 설정을 통해 트래픽을 조절합니다.
Cloud Shell을 통해 kubectl 명령이 실행 가능한 환경으로 접속합니다.
두 개의 네임스페이스를 생성합니다.
예) ns-blue, ns-green
kubectl create ns ns-blue kubectl create ns ns-green
OCIR에서 이미지 가져오기
OCIR 리파지토지(예시, webpage)가 사전에 없는 경우 Root Compartment에 Private 형태로 만들어집니다. OKE에서 가져오기 위해서는 각각 네임스페이스에 imagepullsecret을 사전에 생성하거나, 리파지토리를 미리 Public으로 생성합니다.
Blue/Green 전략으로 Kubernetes 배포 Stage 만들기
프로젝트 페이지로 이동하여 왼쪽 메뉴의 Deployment Pipelines로 이동합니다.
Create pipeline을 클릭하여 파이프라인을 생성합니다.
- Name: 예) webpage-bluegreen-deployment-pipeline
생성된 파이프라인을 클릭하여 Blue/Green Strategy Stage를 추가합니다.
배포 유형을 OKE로 선택하고, 배포환경 및 manifest 파일을 선택합니다.
Blue/Green 배포를 위한 추가 설정을 합니다.
Namespace set: 앞서 만들 배포용 네임스페이스 2개를 입력합니다. 이전 버전이 있는 곳에 배포하고, 현재 버전으로 향한 트래픽을 새 버전이 있는 곳으로 변경하는 방식으로 2개의 순서는 크게 중요하지는 않습니다.
- 예) ns-blue, ns-green
NGINX ingress namespace: 배포 manifest 파일에서 정의한 배포 앱의 ingress 자원 이름을 입력합니다.
옵션 설정: Approval controls을 활성화합니다. 배포까지만 자동으로 진행하고, 승인후 트래픽을 전환하기 위해 추가합니다. 트래픽 전환 스위치 정도라고 생각하면 될 것 같습니다.
Approval controls:
- Name: 예) approval-stage
파이프라인 완성
Build Pipeline에서 Deployment Pipeline 호출하기
앞서 만든 Build Pipeline이 끝나고, 배포가 될수 있도록 Deployment Pipeline 호출을 추가합니다.
앞서 만든 Build Pipelines으로 이동합니다.
파이프라인 마지막에 Stage를 추가합니다.
Trigger Deployment 유형을 선택합니다.
앞서 만든 Blue/Green 배포용 Deployment Pipeline을 지정합니다.
빌드후 배포하는 전체 흐름이 완료되었습니다.
Trigger 설정하기
개발자가 코드를 코드 저장소에 반영이 될 때 자동으로 빌드, 배포 파이프라인이 동작할 필요가 있습니다. Trigger는 코드 저장소에 발생한 이벤트를 통해 빌드 파이프라인을 시작하게 하는 역할을 하게 됩니다.
프로젝트 페이지로 이동하여 왼쪽 메뉴의 Trigger로 이동합니다.
Create trigger을 클릭합니다.
Trigger를 설정합니다.
- Name: 예) webpage-build-pipeline-trigger
- Source Code Repository: 앞서 만든 OCI Code Repository상의 oci-devops-oke-webpage를 선택합니다.
- Actions: 트리거링 되었을 때 호출하는 액션으로 작성한 빌드 파이프라인인 webpage-pipeline을 선택합니다.
설정이 완료되었습니다.
테스트 - Blue 배포
Trigger에서 지정한 oci-devops-oke-webpage 소스 코드에 임의의 변경사항을 발생시키고 Code Repository에 반영합니다.
작업중인 코드가 있는 Cloud Shell로 이동합니다.
html/index.html을 변경해도 되지만, 편의상 환경변수로 해놓은 Dockerfile을 변경합니다.
현재 설정 기준으로 변경사항을 발생시키고 코드를 Commit 후 Push 합니다.
FROM nginx:alpine COPY default.conf.template /etc/nginx/templates/ COPY html/ /usr/share/nginx/html/ ENV POD_NAMESPACE="default" ENV VERSION="1.0" ENV MESSAGE="Hello OCI DevOps" ENV BACKGROUND="blue" ARG CONTEXT_PATH="/webpage" ENV CONTEXT_PATH=$CONTEXT_PATH
Trigger가 되고 빌드가 시작됩니다.
빌드가 성공하고, 배포 파이프라인이 실행하다, approval-stage에서 대기합니다.
쿠버네티스 배포 상태를 확인합니다. ns-blue에만 배포된 것을 알 수 있습니다.
[opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-blue NAME READY STATUS RESTARTS AGE pod/webpage-deployment-6fbfd769c7-gqprs 1/1 Running 0 9m28s pod/webpage-deployment-6fbfd769c7-m4lk4 1/1 Running 0 9m28s pod/webpage-deployment-6fbfd769c7-z924h 1/1 Running 0 9m28s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/webpage-service ClusterIP 10.96.166.161 <none> 8080/TCP 9m28s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/webpage-deployment 3/3 3 3 9m28s NAME DESIRED CURRENT READY AGE replicaset.apps/webpage-deployment-6fbfd769c7 3 3 3 9m28s NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/webpage-ingress <none> * 146.56.118.147 80 9m28s [opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-green No resources found in ns-green namespace.
실행중인 배포 파이프라인으로 돌아가 approval-stage에서 승인합니다.
승인이 되면 Traffic Shift도 진행되어 ns-green에도 ingress가 배포된 것을 볼수 있습니다. 그러나 지금은 ns-blue가 Active이므로 ns-green에 있는 webpage-ingress의 annotations에서 보면 canary-weight: “0“이 되어는 실제는 ns-green으로는 요청이 가지 않습니다.
[opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-green NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/webpage-ingress <none> * 146.56.118.147 80 4m [opc@jumpbox-945115 ~]$ kubectl get ingress webpage-ingress -n ns-green -o yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: redirect-to-namespaceB nginx.ingress.kubernetes.io/canary-weight: "0" nginx.ingress.kubernetes.io/rewrite-target: /$2 ...
Ingress 주소를 통해 웹페이지에 접속하면, 현재 Blue 앱이 잘 동작하는 것을 볼수 있습니다.
- 주소 예) http://146.56.118.147/webpage
테스트 - Green 배포
소스로 돌아가서 코드를 변경(예시, Dockerfile의 환경변수 변경)하고 Commit & Push 하여 코드를 반영합니다
FROM nginx:alpine COPY default.conf.template /etc/nginx/templates/ COPY html/ /usr/share/nginx/html/ ENV POD_NAMESPACE="default" ENV VERSION="1.1" ENV MESSAGE="Hello OCI DevOps" ENV BACKGROUND="green" ARG CONTEXT_PATH="/webpage" ENV CONTEXT_PATH=$CONTEXT_PATH
다시 빌드, 배포 파이프라인이 실행되고 approval-stage에서 멈출때까지 기다립니다.
쿠버네티스 배포 상태를 확인합니다. ns-green에 앱이 배포된 것을 볼 수 있습니다.
[opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-blue NAME READY STATUS RESTARTS AGE pod/webpage-deployment-6fbfd769c7-gqprs 1/1 Running 0 34m pod/webpage-deployment-6fbfd769c7-m4lk4 1/1 Running 0 34m pod/webpage-deployment-6fbfd769c7-z924h 1/1 Running 0 34m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/webpage-service ClusterIP 10.96.166.161 <none> 8080/TCP 34m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/webpage-deployment 3/3 3 3 34m NAME DESIRED CURRENT READY AGE replicaset.apps/webpage-deployment-6fbfd769c7 3 3 3 34m NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/webpage-ingress <none> * 146.56.118.147 80 34m [opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-green NAME READY STATUS RESTARTS AGE pod/webpage-deployment-75c8bb6ffc-5b497 1/1 Running 0 98s pod/webpage-deployment-75c8bb6ffc-m8dl4 1/1 Running 0 98s pod/webpage-deployment-75c8bb6ffc-xctr4 1/1 Running 0 98s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/webpage-service ClusterIP 10.96.153.34 <none> 8080/TCP 98s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/webpage-deployment 3/3 3 3 98s NAME DESIRED CURRENT READY AGE replicaset.apps/webpage-deployment-75c8bb6ffc 3 3 3 98s NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/webpage-ingress <none> * 146.56.118.147 80 18m
웹페이지에 접속하면, 여전히 Blue 앱으로 접속됩니다.
실행중인 배포 파이프라인으로 돌아가 approval-stage에서 승인합니다.
승인이 되면, Traffic Shift가 됩니다. 이제 ns-green이 Active이므로 ns-green내 webpage-ingress의 annotations에서 보면 canary-weight: “100“으로 변경된 걸 볼 수 있습니다. 이제 모든 요청은 ns-green으로만 전달됩니다.
[opc@jumpbox-945115 ~]$ kubectl get ingress webpage-ingress -n ns-green -o yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: redirect-to-namespaceB nginx.ingress.kubernetes.io/canary-weight: "100" nginx.ingress.kubernetes.io/rewrite-target: /$2 ...
웹페이지에 접속하면, Green 앱으로 접속됩니다. 여러번 반복해서 요청해도 Green 앱으로 접속됩니다.
테스트 - 추가적인 변경 사항 배포 - Red 배포
소스로 돌아가서 코드를 변경(예시, Dockerfile의 환경변수 변경)하고 Commit & Push 하여 코드를 반영합니다
... ENV VERSION="1.2" ENV MESSAGE="Hello OCI DevOps" ENV BACKGROUND="red" ...
다시 빌드, 배포 파이프라인이 실행되고 approval-stage에서 멈출때까지 기다립니다.
쿠버네티스 배포 상태를 확인합니다. POD의 AGE를 보면 신규 배포가 ns-blue로 된 것을 알 수 있습니다.
[opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-blue NAME READY STATUS RESTARTS AGE pod/webpage-deployment-7958d6b7b7-pkzmm 1/1 Running 0 115s pod/webpage-deployment-7958d6b7b7-vwx22 1/1 Running 0 2m4s pod/webpage-deployment-7958d6b7b7-wkbjd 1/1 Running 0 107s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/webpage-service ClusterIP 10.96.166.161 <none> 8080/TCP 53m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/webpage-deployment 3/3 3 3 53m NAME DESIRED CURRENT READY AGE replicaset.apps/webpage-deployment-6fbfd769c7 0 0 0 53m replicaset.apps/webpage-deployment-7958d6b7b7 3 3 3 2m4s NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/webpage-ingress <none> * 146.56.118.147 80 53m [opc@jumpbox-945115 ~]$ kubectl get all,ingress -n ns-green NAME READY STATUS RESTARTS AGE pod/webpage-deployment-75c8bb6ffc-5b497 1/1 Running 0 20m pod/webpage-deployment-75c8bb6ffc-m8dl4 1/1 Running 0 20m pod/webpage-deployment-75c8bb6ffc-xctr4 1/1 Running 0 20m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/webpage-service ClusterIP 10.96.153.34 <none> 8080/TCP 20m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/webpage-deployment 3/3 3 3 21m NAME DESIRED CURRENT READY AGE replicaset.apps/webpage-deployment-75c8bb6ffc 3 3 3 21m NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/webpage-ingress <none> * 146.56.118.147 80 37m
웹페이지에 접속하면, 여전히 Green 앱으로 접속됩니다.
실행중인 배포 파이프라인으로 돌아가 approval-stage에서 승인합니다.
승인이 되면, Traffic Shift가 됩니다. 이제 ns-blue가 Active입니다. ns-green내 webpage-ingress의 annotations에서 보면 canary-weight: “0“로 다시 변경된 걸 볼 수 있습니다. 이제 모든 요청은 ns-blue으로만 전달됩니다.
[opc@jumpbox-945115 ~]$ kubectl get ingress webpage-ingress -n ns-green -o yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: redirect-to-namespaceB nginx.ingress.kubernetes.io/canary-weight: "0" ...
웹페이지에 접속하면, ns-blue 네임스페이스에 새롭게 배포한 Red 앱으로 접속됩니다. 여러번 반복해서 요청해도 Red 앱으로 접속됩니다.
이렇게 Blue/Green 전략으로 앱을 배포하면, 지정한 2개의 네임스페이스를 이용하여, 현재 서비스 되지 않는 네임스페이스로 신규 버전을 배포하고, 승인이 되면, 신규 버전으로 트래픽을 변경하는 방법으로 지속적인 신규 버전에 대한 배포를 지원합니다.
테스트 - 롤백
신규 버전으로 서비스를 하다, 문제가 발생하면 이전 버전으로 롤백이 기능합니다.
배포 히스토리 중에서 현재 버전을 배포한 배포 파이프라인으로 이동합니다.
Traffic Shift 메뉴 중에서 Revert traffic shift를 클릭합니다.
변경내용 확인 후 다시 Revert traffic shift를 클릭합니다.
수동 롤백 작업이 완료되었습니다.
배포 변경은 없고 Traffic Shift만 변경된 것으로, 쿠버네티스 배포 상태를 확인해 보면, ns-green내 webpage-ingress의 annotations에서 보면 canary-weight: “100“로 다시 변경된 걸 볼 수 있습니다.
[opc@jumpbox-945115 ~]$ kubectl get ingress webpage-ingress -n ns-green -o yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: redirect-to-namespaceB nginx.ingress.kubernetes.io/canary-weight: "100" nginx.ingress.kubernetes.io/rewrite-target: /$2
웹페이지에 접속하면, 마지막 배포앱인 Red에서 ns-blue 네임스페이스에 있는 기존 앱인 Green 앱으로 복구된 것을 알 수 있습니다.
이렇게 DevOps 서비스에서 제공하는 Blue/Green 전략으로 앱을 배포하면, 2개의 네임스페이스, Ingress Controller의 라우팅 규칙 변경을 통해서 Blue/Green 배포를 할 수 있습니다.
이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.