Argo CD In practice 6. Designing Argo CD Delivery Pipelines

     

    Motivation (동기 부여)

    k8s로 전환할 때 가장 큰 문제 중 하나는 표준 롤링 업데이트처럼 정교한 배포 전략을 어떻게 설정할 수 있는지다. k8s에서 제공하고 있는 표준 롤링 업데이트는 모든 요구사항을 만족할 수 없기 때문이다. 새 버전을 배포하려고 할 때를 예로 들어보자.

    1. 새 버전 서비스가 제대로 작동하는지 테스트 먼저 진행
    2. 1번 결과가 정상인 경우, 모든 서비스를 새 버전으로 전환  

    이런 배포 전략을 Blue/Green 배포라고 한다. 먼저 k8s 리소스만 이용해서 Blue/Green 배포를 수행해보고자 한다. 


    Simple Blue/Green with Only k8s

    • 책에서는 ELB(AWS Load Balancer)를 사용하였으나, 외부 IP 노출이 어려워 NodePort로 수정해서 작업 진행했음.
    $ git clone https://github.com/PacktPublishing/ArgoCD-in-Practice.git
    $ kubectl create ns ch06-blue-green
    $ kubectl apply -f blue.yaml -n ch06-blue-green
    • Version1 파드를 배포한다.
    $ kubectl apply -f service-v1.yaml
    $ curl 10.130.102.212:31089/version
    v1.0%
    • Version1 파드에 대한 Service를 배포한다.  이 서비스는 Version1 파드를 가리킨다. 
    $ kubectl apply -f green.yaml
    • Version2 파드를 배포한다.
    $ kubectl get pod 
    NAME                      READY   STATUS    RESTARTS   AGE
    app-f6c66b898-jzcn6       1/1     Running   0          7m32s
    app-f6c66b898-qcrnr       1/1     Running   0          7m32s
    app-v2-6c4788bf64-7n2vm   1/1     Running   0          33s
    app-v2-6c4788bf64-fptbr   1/1     Running   0          33s
    • 현재 v1, v2 파드가 같이 동작 중이다. 

    • 그리고 서비스는 현재 라벨 셀렉터를 이용해 v1을 가리키고 있음.  (좌측 그림)
    • 대부분의 엔지니어링 팀은 이 상태에서 V2가 정상적으로 동작하는지를 수동 / 자동으로 테스트를 한다. 그리고 확신을 가지면 서비스의 라벨 셀럭터를 V2로 바꾸어서 위 상태(우측 그림)로 바꾼다. 이미 V2는 떠있고 검증이 완료된 상태이기 때문에 바로 트래픽을 받을 수 있으며, 다운 타임은 0가 된다.
    $ kubectl patch service app -p '{"spec":{"selector":{"version":"2.0"}}}'\
    $ curl 10.130.102.212:31089/version
    >> 
    v2.0%

    위 명령어를 이용해서 트래픽을 바꾸어주었고, 버전2에 대한 값을 보여주는 것을 알 수 있다. 

     

    문제점

    Blue/Green을 수행할 수는 있었지만, 모든 작업들을 수동으로 진행했다.

    • CI/CD 파이프 라인을 이용해 자동화 할 수 있다 -> 그러나 유지보수하기 너무 어려워 짐. 
    • GitOps 원칙을 따르지도 않았다. -> 클라이언트에서 직접 명령을 해서 수정함. 

    ArgoCD 진영에서는 점진적 고급 배포 전략을 지원하기 위해 Argo RollOuts이라는 CRD를 제공한다. Argo RollOuts은 ArgoCD와 함께 사용할 수 있는 CRD다. 

     

    What is Argo Rollouts?

    Argo Roll Out은 Deployment 자원과 유사한 수준의 k8s 자원이며,  ArgoCD 팀에서 생성한 CRD이다. ArgoCD가 깔려있지 않아도 사용할 수 있다. Argo Rollouts는 확장된 기능을 가지고 있기 때문에 변화를 점진적으로 Deployment에 반영되는 기능을 제공해준다. ArgoRollouts를 이용해서 사용할 수 있는 기능은 다음과 같다.

    • Blue-Green deployments
    • Canary Deployments
    • Weighted Traffic shift
    • Automatic rollbacks and promotions
    • Metric analysis

     

     

    왜 Argo RollOuts를 사용해야할까? 

    k8s deployment는 배포하는 동안 롤링 업데이트의 안전장치로 readness Probe 기능만 지원한다. 이런 기능만 지원하기 때문에 Blue/Green 전략을 달성하기 위해 많은 수작업이 필요했다. 그 외에도 다음과 같은 단점이 존재한다. 

    • 롤아웃을 얼마나 빨리 또는 느리게 할 수 있는지 제어할 방법이 없음. (기껏해야 MaxSurge? 거기에 수동)
    • 카나리, Blue/Green 같은 경우 트래픽을 최신 버전으로 전환할 수 없음.
    • Rolling Update에 사용할 수 있는 메트릭별 입력이 없음.
    • 진행을 중단 / 롤백을 수동으로 해야 함.

    대규모 클러스터 환경이라면, 롤링 업데이트 전략은 Deployment의 폭발 반경을 제어할 수 없다. 수작업으로 하는 사이에 이미 모든 클러스터에 배포가 되어있을 수 있기 때문이다. Deployment 리소스의 이런 단점을 보완하기 위해 Argo RollOuts CRD가 지원된다.

     

     

    Argo Rollouts 아키텍쳐

    실제로 Argo Rollouts 컨트롤러는 일반적인 k8s deployments처럼 ReplicaSet의 라이프사이클을 관리한다. 예를 들어 이렇게 동작할 것이다.

    1. Argo RollOuts 매니페스트 업데이트 -> RollOut Controller가 ReplicaSet 업데이트
    2. ReplicaSet 업데이트 내용을 replication Controller가 인지 후, Pod 업데이트
    3. Pod 업데이트 확인 후, kubelet이 컨테이너 런타임을 이용해 배포

    여기서 Argo Rollouts Controller는 1번 과정에 주로 관여한다. 아래 그림이 RollOut Controller가 RollOut CRD를 대하는 방법이다.

    아키텍처에서 각 구성 요소의 목적을 살펴봄으로써 Argo 롤아웃이 어떻게 작동하는지 이해해 보겠습니다:

    • Argo Rollouts controller : 이 컨트롤러는 Rollouts CRD를 관리함. 
    • Rollouts resource (CRD): Deployment 리소스와 호환되지만 Blue/Green, Canary 등 배포 전략의 단계와 임계값을 제어할 수 있는 몇 가지 추가 필드가 포함되어 있음. 따라서 이런 전략을 사용하려면 Deployment를 모두 RollOuts 자원으로 Migration 해야함.
    • ReplicaSet : k8s의 ReplicaSet
    • Analysis
      • Rollout Controller는 AnalysisTemplate이 있는 경우, RollOut에 필요한지 확인하고 사용하기도 함. 
      • AnalysisTemplate은 Pod Template 같은 Template임.
      • AnalysisTemplate이 k8s에 배포되어 있고, RollOut에서 AnalysisTemplate을 이용한 검사 필요를 선언하는 경우, Roll Out Controller는 AnalysisRun을 수행하고, 그 결과를 피드백해서 추가 배포를 진행함. 
      • 이 때 AnalysisRun은 Metric Provider로부터 메트릭을 받아서 Go/Stop을 결정함. Metric Provider는 Prometheus, DataDog등 다양함

     


    Blue/Green 배포 전략

    Blue/Green 배포 전략은 다음과 같이 동작한다.

    1. 이전 버전 / 새 버전이 동시에 떠있음.
    2. 테스트 스위트가 새 버전에 수동 / 자동으로 실행 완료될 때까지 트래픽은 이전 버전으로 전달됨. 
    3. 테스트 스위트가 성공한 경우, 트래픽을 새 버전으로 옮김 

    이 작업은 Argo RollOut Controller를 사용하면 손쉽게 달성할 수 있다. 이를 위한 ArgoRollout은 다음과 같다. 

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: app
      namespace: team-demo
    spec:
      replicas: 2
      // 이 부분
      strategy:
        blueGreen:
          activeService: rollout-bluegreen-active
          previewService: rollout-bluegreen-preview
          autoPromotionEnabled: false
      revisionHistoryLimit: 2
      selector:
        matchLabels:
          app: app
      template:
        metadata:
          labels:
            app: app
        spec:
          containers:
            - name: app
              image: spirosoik/ch06:v2.5.11
              ports:
                - name: http
                  containerPort: 3000
                  protocol: TCP

    일반적인 k8s deployment와의 주요 차이점은 배포 유형(strategy)과 Blue/Green 버전에 사용되는 두 가지 k8s Service를 정의하는 필드다. 이 RollOut이 배포되면, Template에 설정된 이미지로 파드를 변경하도록 Argo RollOut Controller가 동시에 트리거 된다는 점이다.

    • activeService: 프로모션이 실행될 때까지 트래픽을 받는 이전 버전을 의미.
    • previewService: 프로모션 실행되고 트래픽을 받은 새 버전을 의미
    • autoPromotionEnabled: ReplicaSet이 완전히 준비되어 사용할 수 있게 된 직후에 트래픽을 바로 전환할지를 결정함.

     

     

    Canary 배포 전략

    • Canary 배포전략의 기본 개념은 최종 사용자의 일부에게만 새로운 버전에 대한 트래픽을 제공하고, 나머지 트래픽에 대해서는 이전 버전을 제공한다는 점이다.
    • 카나리로 배포하면 새 버전이 제대로 작동하는지 실제로 검증할(테스트 스위트가 아니라) 수 있으며, 최종 사용자로 가는 트래픽을 점진적으로 늘려 이전 버전을 완전히 대체할 수 있음. 

    Argo Rollout Controller를 이용해서 카나리 배포 전략을 Gitops로 달성할 수 있다. 

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: rollout-canary
    spec:
      replicas: 5
      revisionHistoryLimit: 2
      selector:
        matchLabels:
          app: rollout-canary
      template:
        metadata:
          labels:
            app: rollout-canary
        spec:
          containers:
          - name: rollouts-demo
            image: spirosoik/ch06:blue
            imagePullPolicy: Always
            ports:
            - containerPort: 8080
      strategy:
      	// 트래픽이 점진적으로 증가하는 상태. 
        canary:
          steps:
          - setWeight: 20
          - pause: {}
          - setWeight: 40
          - pause: {duration: 40s}
          - setWeight: 60
          - pause: {duration: 20s}
          - setWeight: 80
          - pause: {duration: 20s}

    k8s deployment와 다른 내용은 strategy.canary 부분이다.

    • 처음 : 초기 카나리 트래픽이 20%만 전달됨. 이 때 pause {}로 설정되어 있어서, 무한히 대기한다. 이 때는 명시적으로 롤아웃의 재개가 필요함.
    • 그 다음 : 트래픽 40%만 전달됨. 이 때, 40초 대기함.
    명시적으로 롤아웃을 재개하는 방법은 다음과 같다.
    $ kubectl argo rollouts promote rollout-canary

    이 명령을 실행하면 롤아웃이 100%에 도달할 때 까지 점진적으로 20%씩 트래픽 증가를 수행하기 시작한다. 

     

    Argo RollOut 설치

    ArgoCD를 설치해도 Argo RollOut은 같이 설치되지 않는다. 따라서 아래 명령어를 이용해서 Argo RollOut Controller 및 CRD를 설치해야한다. 

    // argo rollout 설치
    $ kubectl create namespace argo-rollouts
    $ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
    
    // argo Rollout을 kubectl에 설치
    $ brew install argoproj/tap/kubectl-argo-rollouts
    
    // version 살펴보기
    $ kubectl argo rollouts version

     

    자동화 된 Canary 배포 with Rollouts -> 수동 Promoted

    $ kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
    $ kubectl apply -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/service.yaml

    먼저 위 리소스를 k8s 클러스터에 배포한다. 배포 결과로 Rollouts 1개, Service 1개가 배포된다. 

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: rollouts-demo
    spec:
      replicas: 5
      strategy:
        canary:
          steps:
            - setWeight: 20
            - pause: {}
            - setWeight: 40
            - pause: {duration: 10}
            - setWeight: 60
            - pause: {duration: 10}
            - setWeight: 80
            - pause: {duration: 10}
      ...
      template:
        metadata:
          labels:
            app: rollouts-demo
        spec:
          containers:
            - name: rollouts-demo
              image: argoproj/rollouts-demo:blue
    ...
    • 배포되는 RollOut은 위와 같은데 k8s의 deployment Object를 내포하는데, strategy에 Canary가 있다. Canary의 배포에는 steps로 나누어져 있어서 백엔드쪽 트래픽 분리를 유도할 수 있다.
    • pause : {}로 되어있는 것은 무한히 대기하는 것을 의미. 이 때는 수동 Promote가 필요함.
    • pause : {duration 10}으로 되어있는 것은 10초 대기 후 다음 스텝으로 넘어가는 것을 의미함. 
    $ kubectl argo rollouts get rollout rollouts-demo --watch

    다음 명령어를 이용하면 현재 Argo Rollouts의 상태를 알 수 있음.

    현재 배포된 상태의 Argo Rollout은 다음과 같다.

    • 5개의 replicas는 Replicaset revision 1에 되어있음.
    • replicaset revision1은 stable함.  (나머지 Pod들이 모두 Ready이기 때문)
    $ kubectl argo rollouts set image rollouts-demo rollouts-demo=argoproj/rollouts-demo:yellow

    이 때, Argo rollouts의 이미지를 바꾸게 되면 deployment의 이미지가 바뀌었던 것처럼 Template의 Hash가 재계산되어 새로운 Replicaset이 생성된다. 그리고 deployment가 그랬듯이 Update를 진행한다.

    • Argo rollouts 명령어를 실행하면, Update Strategy가 Canary이고 트래픽이 20%(1/5)이기 때문에 1대의 Pod가 새로운 Replicaset에 배정된다. 예전 버전의 Replicaset은 4대의 파드가 남게 된다.
    • 앞서 이야기 한 것처럼 pause : {}로 되어있었기 때문에 다음 스텝으로 넘어가려면, 수동 Promoted가 필요하다. 
    $ kubectl argo rollouts promote rollouts-demo

    위 명령어를 사용하면, Argo RollOut의 Canary Replicaset이 Promoted된다. 이 명령어를 사용하는 것은 카나리 파드가 정상적인 상태라는 것을 어떠한 방식으로든 검증한 다음이 될 것이다. 아무튼 이 명령어를 실행하면 이후 스텝으로 넘어간다.

    

    위 이미지에서 볼 수 있듯이, Strategy의 STEP에 정의해둔 것처럼 pause 타임 + 트래픽을 잘 지켜서 Canary 트래픽이 완전히 전환된다.

    요약

    • Argo Rollouts를 이용하면 k8s의 Deployment의 배포 전략보다 더욱 진보된 방식으로 Canary 배포를 할 수 있다. (속도 조절, 트리거 가능)
    • k8s만 이용했을 때는 손으로 해야하던 GitOps 형식으로 처리할 수 있음.
    • 모든 부분이 GitOps는 아님. (Integtation Test + Promoted는 수동으로 해야함.)

     


    자동화 된 Blue/Green 배포 with rollOut -> 수동 Promoted

    Argo Rollout을 이용한 Blue/Green 배포를 하는 방법은 다음과 같다. 

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: rollouts-blue-green
    spec:
      replicas: 5
      strategy:
        blueGreen:
          activeService: rollouts-green-svc
          previewService: rollouts-blue-svc
          autoPromotionEnabled: false
      revisionHistoryLimit: 2
      selector:
        matchLabels:
          app: rollouts-blue-green
      template:
        metadata:
          labels:
            app: rollouts-blue-green
        spec:
          containers:
            - name: rollouts-blue-green
              image: argoproj/rollouts-demo:blue
              ports:
                - name: http
                  containerPort: 8080
                  protocol: TCP
    • Argo Rollout을 선언함.
    • 주요한 부분은 strategy.blueGreen의 activeService / previewService임
      • Active Service는 이전 버전의 ReplicaSet를 가리키는 서비스를 의미함.
      • Preview Service는 새 버전의 ReplicaSet을 가리키는 서비스를 의미함.
    apiVersion: v1
    kind: Service
    metadata:
      name: rollouts-blue-svc
    spec:
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app: rollouts-blue-green
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: rollouts-green-svc
    spec:
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app: rollouts-blue-green

    블루/그린에서 사용할 수 있는 서비스를 생성한다. Argo Rollout과 Blue/Green에 사용할 서비스를 배포했으므로, 스크립트를 실행해서 실제로 Blue/Green으로 업데이트 되는지는 살펴보고자 한다.

    $ kubectl apply -f .

    먼저 위 명령어를 이용해서 Argo Rollout, Blue/Green을 이용한 Service를 배포한다.

    배포한 후 내용을 살펴보면 다음과 같이 Green만 존재하는 것을 확인할 수 있다. 

    $ kubectl argo rollouts set image rollouts-blue-green rollouts-blue-green=argoproj/rollouts-demo:yellow

    위 명령어를 이용해서 Rollout에 새로운 이미지를 사용하도록 수정한다.

    수정하면 다음과 같이 Blue와 관련된 새로운 ReplicaSet 58cf889bd5가 생성된다. 그리고 Replicas가 5에 대응되는 Pod들이 모두 생성되었다. 

    $ kubectl get svc -o yaml rollouts-blue-svc
    >>>
      selector:
        app: rollouts-blue-green
        rollouts-pod-template-hash: 58cf899bd5

    이 때 위 명령어를 이용해서 blue-svc의 Selector를 살펴보면 58cf889bd5가 붙은 것을 확인할 수 있다. 

    $ kubectl argo rollouts promote rollouts-blue-green

    그리고 위 명령어를 이용해서 Blue를 Green으로 승급시킨다. 마찬가지로 승급을 시키기 전에는 필요한 테스트를 완료한 후, 새로운 버전이 정상이라는 확신을 가진 후에 해야만 한다.

    승급시키면 이전 Replicaset은 delay:20s에 기록된 시간만큼 대기하다가 Scale Down 된다.

    $ kubectl get endpoints | grep rollouts-
    rollouts-blue-svc       192.168.1.29:8080,192.168.2.47:8080,192.168.3.51:8080 + 2 more...   7m9s
    rollouts-green-svc      192.168.1.29:8080,192.168.2.47:8080,192.168.3.51:8080 + 2 more...   7m9s

    이 때 rollouts에 사용된 blue / green 서비스의 endpoints를 살펴보면 모두 58cf899bd5를 가리키는 것을 볼 수 있다. 즉, 두 k8s 서비스 객체는 Selector를 이용해 58cf899bd5를 가리키고 있다. 

    모든 이전 버전이 사라진 것을 볼 수 있다. 

    요약

    • Argo Rollouts를 이용하면 k8s의 Deployment의 배포 전략보다 더욱 진보된 방식으로 Blue/Green 배포할 수 있음. 
      • 이전에는 많은 부분을 명령어 기반으로 처리해야했으나, Rollouts를 이용하면 명령어 1개로 충분함.
    • k8s만 이용했을 때는 손으로 해야하던 것들을 GitOps 형식으로 처리할 수 있음.
    • 모든 부분이 GitOps는 아님. (Integtation Test + Promoted는 수동으로 해야함.)

     

     


    SyncWave /  Sync Hook을 이용한 Promoted

    이 방법은 실제로는 완전히 자동으로 동작하지 않는다. 이유는 다음과 같다. 

    • Argo RollOuts은 Blue - Green으로 배포된 경우를 가정해보자.
      • 이미지를 수정한다 → 새로운 ReplicaSet이 생성된다 → 새로운 Pod가 생성된다. 즉, Sync가 일어난다.
      • 그러나 이 때 Argo RollOuts는 Paused 상태로 멈춘다. ArgoCD는 Paused 상태인 RollOuts를 Healthy하다고 보지 않는다. 따라서 Sync가 완료되지 않은 것으로 본다. 
      • 이런 이유 때문에 Pre-Sync Hook → Sync Hook까지는 수행되지만, Post-Sync Hook은 수행되지 않음.
      • Sync-Hook인 상황에 Promote Job을 같이 올리는 것은 문제가 됨. Sync + Sync-Hook은 동시에 수행되기 때문에 새로운 Blue 버전이 정상인지 담보할 수 없기 때문임. 
      • 만약 추가 진행하기 위해서는 Resume 버튼을 눌러줘야함. 단순히 Pause된 상태만 바뀌고, 실제 Promote Hook은 수행되지 않음.

    따라서 책에서 선언된 방법은 더 이상 동작하지 않는 것 같다. 아래는 사용했던 k8s 매니페스트 파일. 

    apiVersion: batch/v1
    kind: Job
    metadata:
      generateName: rollout-promoted
      annotations:
        argocd.argoproj.io/hook: PostSync
        argocd.argoproj.io/hook-delete-policy: HookSucceeded
    spec:
      template:
        spec:
          containers:
            - name: promote-green
              image:  quay.io/argoproj/kubectl-argo-rollouts:v1.1.1
              command: ["/bin/sh", "-c"]
              args:
                - kubectl-argo-rollouts promote rollouts-blue-green -n default;
          restartPolicy: Never
      backoffLimit: 2
    ---
    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: rollouts-blue-green
    spec:
      replicas: 5
      strategy:
        blueGreen:
          activeService: rollouts-green-svc
          previewService: rollouts-blue-svc
          autoPromotionEnabled: false
      revisionHistoryLimit: 2
      selector:
        matchLabels:
          app: rollouts-blue-green
      template:
        metadata:
          labels:
            app: rollouts-blue-green
        spec:
          containers:
            - name: rollouts-blue-green
              image: argoproj/rollouts-demo:blue
              ports:
                - name: http
                  containerPort: 8080
                  protocol: TCP
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: rollouts-blue-svc
    spec:
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app: rollouts-blue-green
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: rollouts-green-svc
    spec:
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app: rollouts-blue-green
    ---
    apiVersion: batch/v1
    kind: Job
    metadata:
      generateName: integration-tests
      annotations:
        argocd.argoproj.io/hook: Sync
        argocd.argoproj.io/hook-delete-policy: HookSucceeded
    spec:
    
      template:
        spec:
          containers:
            - name: run-tests
              image: curlimages/curl
              command: ["echo"]
          restartPolicy: Never
      backoffLimit: 2

     


    AnalysisTemplate + AnalysisRun + Rollout을 이용한 Canary의 완전 GitOps

    • Docs : https://argo-rollouts.readthedocs.io/en/stable/features/analysis/
    • AnalysisTemplate이 k8s에 배포되고, Argo RollOut에 AnalaysisTemplate과 평가 Step이 명시되어 있는 경우 Argo RollOut Controller가 Argo RollOut이 트리거 되었을 때, AnalysisTemplate으로 AnalysisRun을 수행함.
    • AnalysisRun을 만족하면 다음 스텝으로 Roll Out이 가능하지만, AnalysisRun이 실패하는 경우 LimitFailure까지만 시도한다.
    • LimitFailure 이상 실패하면, Canary는 Scale Down됨. 즉 RollOut됨. 

    위는 간략히 정리한 내용이다. 

    위 이미지에서 Canary 배포 후, Roll Out할 때 AnalysRun이 포함되는 것을 확인할 수 있다. 이 때 빨간색 세모표시 옆에 2가 있는데, 이것은 AnalysisRun이 2회 실패했음을 의미한다. 

    AnalysisRun이 완전히 실패한 경우 (5회), 6cf78c66c5 ReplicaSet에 남은 파드는 아무것도 없다. 즉, 배포된 카나리 파드들은 모두 롤백되었다. 

     

    위는 정상적으로 AnalysisRun이 통과되어서 카나리로 트래픽이 전환되고 있는 것을 보여준다. 2번째 스텝 이후 AnalysisRun이 평가되기 시작하며, 초록색 체크 1에서 볼 수 있듯이 성공했다. 

    Analysis가 성공하면 카나리 다음 단계를 계속 진행한다. 이것은 2번째 단계에서만 AnalysisRun이 평가되는 것이 아니라 트래픽이 100% 전환될 때 까지 AnalysisRun이 평가되는 것을 의미한다. 그리고 위의 이미지는 카나리로 모든 트래픽이 전환되고, 이전 버전 파드가 하나도 남아있지 않은 상태를 보여준다. 

    사용한 매니페스트 파일은 다음과 같다. 

    apiVersion: argoproj.io/v1alpha1
    kind: AnalysisTemplate
    metadata:
      name: success-rate
    spec:
      metrics:
        - name: success-rate
          interval: 5m
          # NOTE: prometheus queries return results in the form of a vector.
          # So it is common to access the index 0 of the returned array to obtain the value
          successCondition: result[0] > 0
          failureLimit: 3
          provider:
            prometheus:
              address: http://10.233.14.47:9090 # this is service IP.
              query: |
                count(kube_deployment_labels{namespace="default", deployment="metric-dummy-server"})
    ---
    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: rollouts-canary-analysis
    spec:
      replicas: 5
      strategy:
        canary:
          analysis:
            templates:
              - templateName: success-rate
            startingStep: 2
          steps:
            - setWeight: 20
            - pause: {duration: 10}
            - setWeight: 40
            - pause: {duration: 10}
            - setWeight: 60
            - pause: {duration: 10}
            - setWeight: 80
            - pause: {duration: 10}
      revisionHistoryLimit: 2
      selector:
        matchLabels:
          app: rollouts-canary-analysis
      template:
        metadata:
          labels:
            app: rollouts-canary-analysis
        spec:
          containers:
            - name: rollouts-canary-analysis
              image: argoproj/rollouts-demo:blue
              ports:
                - name: http
                  containerPort: 8080
                  protocol: TCP
              resources:
                requests:
                  memory: 32Mi
                  cpu: 5m
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: rollouts-canary-analysis
    spec:
      ports:
        - port: 80
          targetPort: http
          protocol: TCP
          name: http
      selector:
        app: rollouts-canary-analysis

     


    AnalysisTemplate + AnalysisRun + Rollout을 이용한 Blue/Green의 완전 GitOps

    RollOut의 Blue/Green 전략은 Promote가 핵심적이다. Promotion은 새로운 버전이 메이저 버전으로 바뀌어 '트래픽'을 받을 수 있는 상태로 바뀌는 것을 의미한다. 내부적으로는 서비스 스위치를 조작해서 RollOut과 관련된 모든 서비스가 새로운 버전을 가리키도록 바꾼다. 

    Blue/Green 전략에서 Promotion과 Analysis는 두 가지 조합으로 나누어진다

    • prePromotionAnalysis : AnalaysisRun에 통과하면 Promotion 해줌.
    • postPromotionAnalysis : Promotion 한 후 AnalysisRun을 평가함. 실패하면 RollBack함. 

    위는 prePromotionAnlaysis로 Blue/Green을 평가하고 있는 상황이다. 위에서 AnalysisRun이 임계치까지 실패하게 되면 생성된 Blue Pod들은 모두 롤백된다. 

    apiVersion: argoproj.io/v1alpha1
    kind: AnalysisTemplate
    metadata:
      name: success-rate
    spec:
      metrics:
        - name: success-rate10
          interval: 3s
          count: 2
          # NOTE: prometheus queries return results in the form of a vector.
          # So it is common to access the index 0 of the returned array to obtain the value
          successCondition: result[0] = 0
          failureLimit: 3
          provider:
            prometheus:
              address: http://10.233.14.47:9090 # this is service IP.
              query: |
                count(kube_deployment_labels{namespace="default", deployment="metric-dummy-server"})
    • AnalysisTemplate은 Canary에서 사용한 것과 다르게 interval + Count를 모두 선언해야한다. 그렇지 않으면, Validation에 실패해 AnalysisRun이 실행조차 되지 않는다. 
    • Canary는 각 스텝마다 점진적으로 AnalysisRun이 평가되지만, Blue/Green은 일괄적으로 프로모션을 진행해야하기 때문에 '평가 종료 결과'를 명확하기 위해 Count + Interval을 반드시 평가해야한다.
      • Count가 5, FailureLimit가 3인 경우, 프로모션 가능한지를 평가하기 위해서는 다음과 같이 동작해야 함.
      • 총 5번 요청을 보내야 함. 그리고 성공 횟수가 3이상인 경우, 정상으로 판단하고 프로모션됨. 
    • 반면 Canary는 Count를 표기 하지 않아도 되기 때문에 단 한번만 성공해도 AnalaysisRun이 성공으로 판단됨.

    지금까지는 AnalysisTemplate을 표현하는 것을 위주로 보았다. 이제는 RollOut도 살펴봐야 한다.

    // postPromotion 경우
    strategy:
      blueGreen:
        postPromotionAnalysis:
          templates:
          - templateName: success-rate
        activeService: rollouts-green-svc-analysis
        previewService: rollouts-blue-svc-analysis
        autoPromotionEnabled: true

    postPromotion의 경우, autoPromotionEnable를 반드시 True로 해야한다. 왜냐하면 AnalysisRun은 프로모션 된 다음에 평가되기 때문에 그 누구도 자동으로 프로모션 해주지 않기 때문이다.

    // prePromotion
    strategy:
      blueGreen:
        prePromotionAnalysis:
          templates:
          - templateName: success-rate
        activeService: rollouts-green-svc-analysis
        previewService: rollouts-blue-svc-analysis
        autoPromotionEnabled: false

    prePromotion의 경우 autoPromotionEnable는 true / false도 상관없다. AnalysisRun 결과가 정상적으로 완료되어야 프로모션이 이루어지기 때문이다.

     


    matrix-generator를 이용한 Mono-Repository의 MSA 배포 처리

    ArgoCD에서는 ApplicationSet CRD에 matrix-Generator라는 기능을 제공해준다. 이 기능을 이용하면 Mono-Repository에서 종속성을 가지는 MicroService의 배포를 순차적으로 처리할 수 있다.  (https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Generators-Matrix/)

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
      name: cluster-git
    spec:
      generators:
        # matrix 'parent' generator
        - matrix:
            generators:
              # git generator, 'child' #1
              - git:
                  repoURL: https://github.com/argoproj/argo-cd.git
                  revision: HEAD
                  directories:
                    - path: applicationset/examples/matrix/cluster-addons/*
              # cluster generator, 'child' #2
              - clusters:
                  selector:
                    matchLabels:
                      argocd.argoproj.io/secret-type: cluster
      template:
        metadata:
          name: '{{path.basename}}-{{name}}'
        spec:
          project: '{{metadata.labels.environment}}'
          source:
            repoURL: https://github.com/argoproj/argo-cd.git
            targetRevision: HEAD
            path: '{{path}}'
          destination:
            server: '{{server}}'
            namespace: '{{path.basename}}'

    먼저 이런 ApplicationSet이 지정되어 있다고 가정하자. 위 매니페스트를 살펴보면 다음이 있음을 알 수 있다.

    • ArgoCD는 이것을 보고 generators.git / gernerator.clusters 두 가지를 조합한 모든 경우의 수에 대한 Argo Application을 생성하고 배포한다. 
    - path: /examples/git-generator-directory/cluster-addons/argo-workflows
      path.basename: argo-workflows
    
    - path: /examples/git-generator-directory/cluster-addons/prometheus-operator
      path.basename: prometheus-operator
      
    - name: staging
      server: https://1.2.3.4
    
    - name: production
      server: https://2.4.6.8

    예를 들어 Git, cluster가 각각 2개가 있다고 가정해보자.

    - name: staging
      server: https://1.2.3.4
      path: /examples/git-generator-directory/cluster-addons/argo-workflows
      path.basename: argo-workflows
    
    - name: staging
      server: https://1.2.3.4
      path: /examples/git-generator-directory/cluster-addons/prometheus-operator
      path.basename: prometheus-operator
    
    - name: production
      server: https://2.4.6.8
      path: /examples/git-generator-directory/cluster-addons/argo-workflows
      path.basename: argo-workflows
    
    - name: production
      server: https://2.4.6.8
      path: /examples/git-generator-directory/cluster-addons/prometheus-operator
      path.basename: prometheus-operator

    그리고 이 조합의 결과로 Matrix Generator는 다음 만들어낸다. 그리고 server / Path는 각각 Argo Application의 Source / Destination에 대응되도록 구성되었기 때문에 2개의 클러스터에 각각 2개의 프로젝트가 배포되게 되는 셈이다. 여기까지 ApplicationSet을 이용하면 다양한 어플리케이션을 다양한 클러스터에 편리하게 배포할 수 있다는 것을 알게 되었다.

    그렇다면 어떻게 Matrix Generator를 이용해서 MSA의 종속성까지 해결할 수 있는 것일까? 정답은 Sync-Wave에 있다.

    • ArgoCD에 어떠한 어노테이션도 없이 생성되는 Application들은 sync-wave 기본값이 0이다.
    • sync-wave의 값이 작은 값일수록 먼저 시작되고, 큰 값일수록 나중에 시작된다.

    이 두 가지 특성을 이용하면 위의 그림처럼 wave-1 / wave+1을 이용해서 MSA 간의 종속성을 고려한 자동 배포도 가능한 것이다. ArgoCD에서 생성된 어플리케이션은 Sync를 맞출 때, 결과적으로는 원본 매니페스트를 읽게 된다. 예를 들면 deployment.yaml 파일에 어노테이션으로 종속성을 표시하면 된다.

    그런데 실제로 ArgoCD의 ApplicationSet을 이용해서 Argo Application을 만들었을 때, Argo Application 간의 순차 실행 순서는 보장할 수 없었다. 이유는 Argo Application Controller가 sync-wave 어노테이션을 읽어서, etcd에 업데이트 순차적으로 업데이트 할 것이기 때문이다. 즉 Application에 정의된 매니페스트 간에 sync-wave를 처리할 수는 있으나, Application 사이는 불가능하다. 

    // MySQL Deployment.yaml
    ...
    metadata:
      annotations:
        argocd.argoproj.io/sync-wave: "-1"
        
        
    // Spring Deployment.yaml
    metadata:
      annotations:
        argocd.argoproj.io/sync-wave: "0"

    위와 같이 sync-wave를 설정한다면, 생성된 Argo Application은 MySQL Application이 먼저 배포된 이후, Spring 서버를 배포하도록 할 수 있다. 뿐만 아니라 주문 / 정산이 각각의 의존성을 가진다고 하면 다음과 같이 배포할 수도 있다.

    • 이렇게 배포하는 것은 Application 내의 메니페스트에서만 가능함.
      • 주문 DB(-1)
      • 주문 서버(0)
      • 정산 DB(1)
      • 정산 서버(2)

    또한 각 Argo Application이 배포될 때 Sync-Phase를 추가해서 각 Application의 sync-wave 내의 Sync 기간동안의 Pre-Sync / Sync / Post-Sync Hook을 처리해서 좀 더 고도화 할 수 있다.

     

    살펴보기

    • Argo ApplicationSet argo-test-1에서 List Generator를 이용해서 Argo Application app1 / app2가 생성된다.
      • 이 때 app1의 sync-wave는 1, app2의 sync-wave는2임.
      • 그런데 실제로는 app1, app2가 동시에 sync되고 있음. 
    • 그러나 Application 내의 Manifest는 sync-wave 설정대로 순차적으로 실행된다.
      • sync1-1(sync-wave 1) → sync 1-2 (sync-wave 2)
      • sync2-1(sync-wave 3) → sync 2-2 (sync-wave 4)
      • 이렇게 설정했지만, sync1-1, sync2-1은 별개로 진행된다. 왜냐하면 서로 다른 Argo Application에서 생성되기 때문이다. 

    실제 생성된 결과를 봐도 알 수 있다. 

     

    참고

    • Argo Rollouts는 ArgoCD와 독립적으로 동작하는 k8s 워크로드 배포 도구임. 
    • Argo Rollouts는 kubernetes의 기본 Deployment 리소스를 확장하여 chunked 배포, Blue/Green 배포/ canary 배포 등의 고급 전략을 제공함. 

     

    댓글

    Designed by JB FACTORY