본문 바로가기
WIKI 기술 지식 베이스

동작 원리

원문 보기 위키 갱신

동작 원리 (How it works)

Flagger는 canary라는 이름의 커스텀 리소스를 사용해 Kubernetes 워크로드의 릴리스 프로세스를 자동화하도록 구성할 수 있습니다. 이 문서에서는 canary 리소스가 어떻게 동작하는지, Flagger가 생성하는 오브젝트와 분석 주기에 대해 설명해 드릴게요.

출처: 문서

본문

Flagger는 canary라는 커스텀 리소스를 사용해 Kubernetes 워크로드의 릴리스 프로세스를 자동화하도록 구성할 수 있습니다.

Canary 리소스

canary 커스텀 리소스는 Kubernetes에서 실행되는 애플리케이션의 릴리스 프로세스를 정의하며, 클러스터, 서비스 메시, 인그레스 프로바이더를 가로질러 이식(portable)할 수 있습니다.

podinfo라는 배포에 대해, 점진적 트래픽 전환을 사용하는 카나리아 릴리스는 다음과 같이 정의할 수 있습니다:

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  service:
    port: 9898
  analysis:
    interval: 1m
    threshold: 10
    maxWeight: 50
    stepWeight: 5
    metrics:
      - name: request-success-rate
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        thresholdRange:
          max: 500
        interval: 1m
    webhooks:
      - name: load-test
        url: http://flagger-loadtester.test/
        metadata:
          cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"

앱의 새 버전을 배포하면 Flagger는 점진적으로 canary로 트래픽을 옮기면서, 동시에 요청 성공률과 평균 응답 시간을 측정합니다. 커스텀 메트릭, 승인(acceptance) 테스트, 부하 테스트로 카나리아 분석을 확장해 앱 릴리스 프로세스의 검증을 더 강화할 수 있습니다.

같은 클러스터에서 여러 서비스 메시나 인그레스 컨트롤러를 실행 중이라면, spec.provider로 특정 canary에 대한 전역 프로바이더를 재정의(override)할 수 있습니다.

Canary 대상 (Canary target)

canary 리소스는 Kubernetes Deployment 또는 DaemonSet을 대상으로 삼을 수 있습니다.

Kubernetes Deployment 예시:

spec:
  progressDeadlineSeconds: 60
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  autoscalerRef:
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    name: podinfo
    primaryScalerReplicas:
      minReplicas: 2
      maxReplicas: 5

위 구성에 따라 Flagger는 다음 Kubernetes 오브젝트를 생성합니다:

  • deployment/<targetRef.name>-primary
  • hpa/<autoscalerRef.name>-primary

primary 배포는 앱의 안정적인(Stable) 릴리스로 간주되며, 기본적으로 모든 트래픽이 이 버전으로 라우팅되고 대상 배포는 0으로 스케일됩니다. Flagger는 대상 배포의 변경(시크릿과 configmap 포함)을 감지하고, 새 버전을 primary로 승격(promote)하기 전에 카나리아 분석을 수행합니다.

.spec.autoscalerRef.primaryScalerReplicas를 사용하면 생성되는 primary HorizontalPodAutoscaler의 복제본 스케일링 구성을 재정의할 수 있습니다. 이는 원래 워크로드의 HorizontalPodAutoscaler와 같은 값을 사용하는 대신, primary 워크로드에 다른 스케일링 구성을 사용하고 싶을 때 유용합니다.

참고 대상 배포는 app: <DEPLOYMENT-NAME> 형식의 단일 라벨 셀렉터를 가져야 합니다:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: podinfo
spec:
  selector:
    matchLabels:
      app: podinfo
  template:
    metadata:
      labels:
        app: podinfo

app 외에도 Flagger는 name과 app.kubernetes.io/name 셀렉터를 지원합니다. 다른 규칙을 사용한다면 Flagger 배포 매니페스트의 컨테이너 args에 -selector-labels=my-app-label 명령 플래그로 지정하거나, Helm으로 Flagger를 설치할 때 --set selectorLabels=my-app-label로 설정해 라벨을 지정할 수 있습니다.

대상 배포가 시크릿이나 configmap을 사용한다면, Flagger는 각 오브젝트의 -primary 접미사가 붙은 복사본을 만들고 이 오브젝트들을 primary 배포에서 참조합니다. ConfigMap이나 Secret에 flagger.app/config-tracking: disabled 애노테이션을 달면, Flagger는 primary 복사본을 만들지 않고 같은 오브젝트를 primary 배포에 사용합니다. 시크릿/configmap 추적을 전역으로 끄려면 Flagger 배포 매니페스트의 컨테이너 args에 -enable-config-tracking=false 명령 플래그를 사용하거나, Helm으로 설치할 때 --set configTracking.enabled=false로 설정할 수 있습니다. 다만 Secret/ConfigMap별 애노테이션으로 config-tracking을 끄는 것이 사용 사례에 더 잘 맞을 수도 있습니다.

오토스케일러 참조는 선택 사항입니다. 지정하면 Flagger는 대상과 primary 배포가 스케일 업·다운되는 동안 트래픽 증가를 일시 중지합니다. HPA는 카나리아 분석 중 리소스 사용량을 줄이는 데 도움이 됩니다. 오토스케일러 참조가 지정되면, 오토스케일러에 대한 변경은 배포의 롤아웃이 시작되고 성공적으로 완료될 때만 primary 오토스케일러에서 활성화됩니다. 선택적으로 canary용과 primary용 HPA 두 개를 만들면 새 롤아웃을 하지 않고도 HPA를 업데이트할 수 있습니다. canary 배포는 0으로 스케일되므로 canary의 HPA는 비활성 상태가 됩니다.

참고 Flagger는 HPA에 autoscaling/v2 또는 autoscaling/v2beta2 API 버전을 요구합니다.

progress deadline은 canary 배포가 롤백되기 전에 진행(progress)을 만들 수 있는 최대 시간(초)을 나타내며, 기본값은 10분입니다.

Canary 서비스

canary 리소스는 대상 워크로드가 클러스터 내부에서 어떻게 노출되는지를 결정합니다. canary 대상은 Flagger가 ClusterIP Service를 만들 때 사용할 TCP 포트를 노출해야 합니다.

spec:
  service:
    name: podinfo
    port: 9898
    portName: http
    appProtocol: http
    targetPort: 9898
    portDiscovery: true
    headless: false
    trafficDistribution: PreferClose

대상 워크로드의 컨테이너 포트는 service.port 또는 service.targetPort와 일치해야 합니다. service.name은 선택 사항이며 spec.targetRef.name이 기본값입니다. service.targetPort는 컨테이너 포트 번호나 이름이 될 수 있습니다. service.portName은 선택 사항이며(기본값 http), 워크로드가 gRPC를 사용한다면 포트 이름을 grpc로 설정하세요. service.appProtocol은 선택 사항이며, 자세한 내용은 여기에서 확인할 수 있습니다. service.trafficDistribution은 선택 사항이며, 자세한 내용은 여기에서 확인할 수 있습니다.

포트 발견(port discovery)이 활성화되면 Flagger는 대상 워크로드를 스캔해 canary 서비스에 지정된 포트와 서비스 메시 사이드카 포트를 제외한 컨테이너 포트를 추출합니다. 이 포트들은 ClusterIP 서비스를 생성할 때 사용됩니다.

canary spec의 서비스를 기반으로 Flagger는 다음 Kubernetes ClusterIP 서비스를 만듭니다:

  • <service.name>.<namespace>.svc.cluster.local

    셀렉터 app=<name>-primary

  • <service.name>-primary.<namespace>.svc.cluster.local

    셀렉터 app=<name>-primary

  • <service.name>-canary.<namespace>.svc.cluster.local

    셀렉터 app=<name>

이렇게 하면 podinfo.test:9898로 가는 트래픽이 앱의 최신 안정 릴리스로 라우팅됩니다. podinfo-canary.test:9898 주소는 카나리아 분석 중에만 사용할 수 있으며, 합성 테스트나 부하 테스트에 사용할 수 있습니다.

다음을 사용해 생성되는 서비스에 애노테이션과 라벨을 설정하도록 Flagger를 구성할 수 있습니다:

spec:
  service:
    port: 9898
    apex:
      annotations:
        test: "test"
      labels:
        test: "test"
    canary:
      annotations:
        test: "test"
      labels:
        test: "test"
    primary:
      annotations:
        test: "test"
      labels:
        test: "test"

apex 애노테이션은 생성되는 Kubernetes Service와 서비스 메시/인그레스 오브젝트 양쪽 모두에 추가된다는 점에 유의하세요. 이는 Istio VirtualServices와 TraefikServices에서 external-dns를 사용할 수 있게 해 줍니다. 구성 충돌에 주의하세요 여기.

여기에 지정되지 않은 애노테이션이나 라벨을 추가하면 Flagger는 재조정(reconciliation) 중에 이를 제거한다는 점에 유의하세요. Flagger가 무시해야 할 메타데이터를 지정하려면 unmanagedMetadata를 구성하세요.

생성되는 Kubernetes ClusterIP 서비스가 헤드리스(headless)여야 한다면 service.headless를 true로 설정하세요.

포트 매핑과 메타데이터 외에도, 서비스 스펙에는 URI 매치·리라이트 규칙, 타임아웃, 재시도 정책이 포함될 수 있습니다:

spec:
  service:
    port: 9898
    match:
      - uri:
          prefix: /
    rewrite:
      uri: /
    retries:
      attempts: 3
      perTryTimeout: 1s
    timeout: 5s

메시 프로바이더로 Istio를 사용할 때는 HTTP 헤더 연산, CORS, 트래픽 정책, Istio 게이트웨이와 호스트도 지정할 수 있습니다. Istio 라우팅 구성은 여기에서 확인할 수 있습니다.

Canary 상태

kubectl을 사용해 클러스터 전역의 canary 배포 현재 상태를 확인할 수 있습니다:

kubectl get canaries --all-namespaces

NAMESPACE   NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
test        podinfo   Progressing   15       2019-06-30T14:05:07Z
prod        frontend  Succeeded     0        2019-06-30T16:15:07Z
prod        backend   Failed        0        2019-06-30T17:05:07Z

상태 조건은 카나리아 분석의 마지막으로 알려진 상태를 반영합니다:

kubectl -n test get canary/podinfo -oyaml | awk '/status/,0'

성공적인 롤아웃 상태:

status:
  canaryWeight: 0
  failedChecks: 0
  iterations: 0
  lastAppliedSpec: "14788816656920327485"
  lastPromotedSpec: "14788816656920327485"
  conditions:
  - lastTransitionTime: "2019-07-10T08:23:18Z"
    lastUpdateTime: "2019-07-10T08:23:18Z"
    message: Canary analysis completed successfully, promotion finished.
    reason: Succeeded
    status: "True"
    type: Promoted

Promoted 상태 조건은 다음 이유 중 하나를 가질 수 있습니다: Initialized, Waiting, Progressing, WaitingPromotion, Promoting, Finalising, Succeeded 또는 Failed. 실패한 canary는 promoted 상태가 false로, 이유가 failed로 설정되며, 마지막 적용 스펙이 마지막 승격 스펙과 달라집니다.

성공적인 롤아웃을 기다립니다:

kubectl wait canary/podinfo --for=condition=promoted

CI 예시:

# update the container image
kubectl set image deployment/podinfo podinfod=stefanprodan/podinfo:3.0.1

# wait for Flagger to detect the change
ok=false
until ${ok}; do
    kubectl get canary/podinfo | grep 'Progressing' && ok=true || ok=false
    sleep 5
done

# wait for the canary analysis to finish
kubectl wait canary/podinfo --for=condition=promoted --timeout=5m

# check if the deployment was successful 
kubectl get canary/podinfo | grep Succeeded

Canary 파이널라이저

canary 삭제 시 Flagger의 기본 동작은 컨트롤러가 소유하지 않은 리소스를 현재 상태 그대로 두는 것입니다. 이는 삭제 동작을 단순화하고 리소스 파이널라이제이션 중 교착 상태(deadlock)를 피합니다. canary가 기존 리소스(예: service, virtual service 등)와 함께 도입된 경우, 이 리소스들은 초기화 단계에서 변경되어 더 이상 초기 상태를 반영하지 않습니다. 삭제 시 리소스를 초기 상태로 되돌리려면 revertOnDeletion 속성을 활성화할 수 있습니다.

spec:
  revertOnDeletion: true

클러스터에 삭제 동작이 제출되면 Flagger는 다음 리소스를 되돌리려 시도합니다:

  • Canary 대상 복제본이 primary 복제본 수로 업데이트됩니다
  • Canary 서비스 셀렉터가 되돌려집니다
  • 대상으로 라우팅된 Mesh/Ingress 트래픽

카나리아 분석을 비활성화하는 권장 방법은 리소스 재조정의 필요성을 줄여 주는 skipAnalysis 속성을 사용하는 것입니다. revertOnDeletion 속성은 더 이상 Flagger를 배포 관리에 의존하지 않을 계획일 때 활성화해야 합니다.

참고 이 기능이 활성화되면 재조정으로 인해 삭제 동작에 지연이 있을 수 있습니다.

Canary 분석

카나리아 분석은 다음을 정의합니다:

스펙:

  analysis:
    # schedule interval (default 60s)
    interval:
    # max number of failed metric checks before rollback
    threshold:
    # max traffic percentage routed to canary
    # percentage (0-100)
    maxWeight:
    # canary increment step
    # percentage (0-100)
    stepWeight:
    # promotion increment step
    # percentage (0-100)
    stepWeightPromotion:
    # total number of iterations
    # used for A/B Testing and Blue/Green
    iterations:
    # threshold of primary pods that need to be available to consider it ready
    # before starting rollout. this is optional and the default is 100
    # percentage (0-100)
    primaryReadyThreshold: 100
    # threshold of canary pods that need to be available to consider it ready
    # before starting rollout. this is optional and the default is 100
    # percentage (0-100)
    canaryReadyThreshold: 100
    # canary match conditions
    # used for A/B Testing
    match:
      - # HTTP header
    # key performance indicators
    metrics:
      - # metric check
    # alerting
    alerts:
      - # alert provider
    # external checks
    webhooks:
      - # hook

카나리아 분석은 최대 트래픽 가중치 또는 반복 횟수에 도달할 때까지 주기적으로 실행됩니다. 각 실행에서 Flagger는 웹훅을 호출하고 메트릭을 확인하며, 실패한 검사 임계값에 도달하면 분석을 중지하고 canary를 롤백합니다. 알림이 구성되어 있다면 Flagger는 알림 프로바이더를 사용해 분석 결과를 게시합니다.

Canary 일시 중지

suspend 필드를 true로 설정하면 Canary를 일시 중지할 수 있습니다. Canary가 일시 중지되면 재조정이 완전히 중지됩니다. 즉, 대상 워크로드, 추적되는 ConfigMap과 Secret의 변경이 Canary 실행을 트리거하지 않고, Flagger가 생성한 리소스에 대한 변경도 수정되지 않습니다. 활성 Canary 실행 중에 일시 중지되면 워크로드나 트래픽 가중치를 건드리지 않고 실행이 중지됩니다.

더 알아보기 (Learn more)