APISIX Ingress Controller와 Flagger를 사용한 원활한 Canary Release

Hengliang Tan

January 19, 2023

Ecosystem

작성자: XPENG의 엔지니어링 매니저, Hengliang Tan

프로젝트 개발 과정에서 서비스 업데이트는 종종 도전 과제입니다. 최고의 사용자 경험을 제공하기 위해 가능한 한 서비스 중단 위험을 피해야 합니다. 이에 따라 지속적 배포(Continuous Delivery)가 탄생했으며, 기업 소프트웨어 실천으로 받아들여졌고, 잘 정립된 지속적 통합(Continuous Integration) 원칙의 자연스러운 진화로 여겨집니다. 그러나 관리의 복잡성과 배포 실패가 시스템 가용성에 영향을 미칠 수 있다는 두려움 때문에 지속적 배포는 여전히 매우 드뭅니다. 카나리 릴리스(Canary Release)는 지속적 배포 시스템에서 가장 클래식한 시나리오일 것입니다. 이를 기반으로 비정상적이고 문제가 있는 서비스를 빠르게 발견하고 이전 버전으로 쉽게 롤백할 수 있습니다.

카나리 릴리스

카나리 릴리스는 그레이스케일 릴리스(Grayscale Release)라고도 합니다. 일반적으로 애플리케이션의 새 버전을 "카나리"로 배포하여 성능을 테스트합니다. 이전 버전은 동일한 단계에서 정상 운영을 위해 유지됩니다. 업그레이드 중 일부 사용자는 새 버전을 사용하도록 안내되고, 다른 사용자는 이전 버전을 계속 사용합니다. 전체 시스템의 안정성을 보장하는 전제 하에 버그를 조기에 발견하고 적시에 조정할 수 있습니다.

카나리 릴리스는 업데이트를 직접 배포하지 않습니다. 소수의 사용자에게 일정 비율의 트래픽을 천천히 유도합니다. 오류가 감지되지 않으면 모든 사용자에게 배포되고 이전 버전은 단계적으로 제거됩니다. 이 방법은 프로덕션 환경에 새로운 기능을 도입할 때의 위험을 줄입니다.

이 글에서는 Apache APISIX Ingress와 Flagger를 통해 원활한 카나리 릴리스를 달성하고, 릴리스 효율성을 높이고 릴리스 위험을 줄이는 방법을 소개합니다.

Apache APISIX Ingress 소개

Apache APISIX Ingress는 Apache APISIX를 데이터 플레인 프록시로 사용하는 Kubernetes Ingress Controller로 구현됩니다. 로드 밸런싱, 동적 업스트림, 카나리 릴리스, 세분화된 라우팅, 속도 제한, 서비스 저하, 서비스 차단, 인증 및 관찰 가능성 등 수백 가지 기능을 제공합니다. Zoom, Tencent Cloud, Jiakaobaodian, Horizon Robotics, European Copernicus Reference System 등 국내외 기업과 조직에서 채택되었습니다.

Flagger 소개

Flagger는 CNCF(Cloud Native Computing Foundation) 프로젝트이며 Flux 패밀리의 GitOps 도구 중 하나입니다. 최근 CNCF는 Flux의 공식 졸업을 발표했는데, 이는 클라우드 네이티브 기술의 성공과 밝은 미래를 보여주는 좋은 지표입니다. 점진적 배포 도구로서 Flagger는 Kubernetes에서 실행되는 애플리케이션의 릴리스 프로세스를 자동화합니다. 분석 지표를 측정하고 적합성 테스트를 실행하면서 새 버전으로 트래픽을 점진적으로 전환함으로써 프로덕션 환경에 새 소프트웨어 버전을 도입할 때의 위험을 줄입니다.

Apache APISIX와 Flux 커뮤니티의 지속적인 노력 끝에 Flagger는 최근 v1.27.0을 릴리스했으며, Apache APISIX Ingress와 Flagger를 사용한 자동 카나리 릴리스를 지원합니다.

featured-<Flagger and Apache APISIX Ingress>.jpg

이제 이 원활한 카나리 릴리스 프로세스를 함께 경험해 보겠습니다.

환경

v1.19 이상의 Kubernetes 클러스터가 필요하며, kind를 통해 설치할 수 있습니다.

컴포넌트 설치

Helm V3를 사용하여 Apache APISIX와 Apache APISIX Ingress Controller를 설치합니다.

helm repo add apisix https://charts.apiseven.com
kubectl create ns apisix


helm upgrade -i apisix apisix/apisix --version=0.11.3 \
--namespace apisix \
--set apisix.podAnnotations."prometheus\.io/scrape"=true \
--set apisix.podAnnotations."prometheus\.io/port"=9091 \
--set apisix.podAnnotations."prometheus\.io/path"=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.export_addr.ip=0.0.0.0 \
--set pluginAttrs.prometheus.export_addr.port=9091 \
--set pluginAttrs.prometheus.export_uri=/apisix/prometheus/metrics \
--set pluginAttrs.prometheus.metric_prefix=apisix_ \
--set ingress-controller.enabled=true \
--set ingress-controller.config.apisix.serviceNamespace=apisix

apisix 네임스페이스에 Flagger와 Prometheus 컴포넌트를 설치합니다.

helm repo add flagger https://flagger.app


helm upgrade -i flagger flagger/flagger \
--namespace apisix \
--set prometheus.install=true \
--set meshProvider=apisix

참고: Prometheus 또는 Prometheus Operator를 사용자 정의해야 하는 경우 관련 문서를 검색하여 수정할 수 있습니다.

애플리케이션 초기화

Flagger는 Kubernetes 배포 및 기타 워크로드에 적용할 수 있으며, HPA와도 결합할 수 있습니다. 이는 일련의 객체를 생성합니다: Kubernetes 배포, ClusterIP 서비스, ApisixRoute. 이러한 객체는 클러스터 외부에 애플리케이션을 노출시켜 서비스를 제공하고, 카나리 릴리스 프로세스의 분석에 사용됩니다.

새로운 테스트 네임스페이스를 생성합니다:

kubectl create ns test

새로운 배포와 HPA를 생성합니다. 여기서는 Flagger의 공식 코드 샘플을 추출했습니다.

kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main

Flagger의 부하 테스트 서비스를 배포하여 카나리 릴리스 중 트래픽을 생성하고 분석합니다.

helm upgrade -i flagger-loadtester flagger/loadtester \ --namespace=test

Apache APISIX의 ApisixRoute를 생성한 후, Flagger는 생성된 리소스를 참조하여 카나리 버전의 Apache APISIX Ingress의 ApisixRoute를 생성합니다. (아래 예제에서 app.example.com을 실제 도메인 이름으로 대체하세요)

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: podinfo
  namespace: test
spec:
  http:
    - backends:
        - serviceName: podinfo
          servicePort: 80
      match:
        hosts:
          - app.example.com
        methods:
          - GET
        paths:
          - /*
      name: method
      plugins:
        - name: prometheus
          enable: true
          config:
            disable: false
            prefer_name: true

이를 podinfo-apisixroute.yaml로 저장하고 클러스터에 제출합니다:

kubectl apply -f ./podinfo-apisixroute.yaml

Flagger 사용자 정의 리소스 Canary를 생성합니다. (예제에서 app.example.com을 실제 도메인 이름으로 대체하세요)

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: podinfo
  namespace: test
spec:
  provider: apisix
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: podinfo
  # apisix route 참조
  routeRef:
    apiVersion: apisix.apache.org/v2
    kind: ApisixRoute
    name: podinfo
  progressDeadlineSeconds: 60
  service:
    port: 80
    targetPort: 9898
  analysis:
    interval: 10s
    # 롤백을 위한 최대 실패 횟수
    threshold: 10
    # 카나리 버전으로의 최대 트래픽 비율
    # (0-100)
    maxWeight: 50
    # 카나리 분석의 단계 크기
    # (0-100)
    stepWeight: 10
    # Prometheus를 사용하여 APISIX의 트래픽 정보 확인
    metrics:
      - name: request-success-rate
        # 최소 성공률 (5xx 응답 없음)
        # (0-100)
        thresholdRange:
          min: 99
        interval: 1m
      - name: request-duration
        # P99는 최대 요청 지연(ms)
        thresholdRange:
          max: 500
        interval: 30s
    webhooks:
        # 카나리 분석을 위한 자동화된 트래픽, 실제 시나리오에 따라 수정
      - name: load-test
        url: http://flagger-loadtester.test/
        timeout: 5s
        type: rollout
        metadata:
          cmd: |-
            hey -z 1m -q 10 -c 2 -h2 -host app.example.com http://apisix-gateway.apisix/api/info

이를 podinfo-canary.yaml로 저장하고 클러스터에 제출합니다:

kubectl apply -f ./podinfo-canary.yaml

Flagger는 자동으로 관련 리소스를 생성합니다:

# 제출됨 deployment.apps/podinfo horizontalpodautoscaler.autoscaling/podinfo apisixroute/podinfo canary.flagger.app/podinfo

# 자동 생성됨 deployment.apps/podinfo-primary horizontalpodautoscaler.autoscaling/podinfo-primary service/podinfo service/podinfo-canary service/podinfo-primary apisixroute/podinfo-podinfo-canary

featured-<version 1>.jpg

이제 도메인 이름 app.example.com을 통해 애플리케이션에 접근할 수 있으며, 현재 애플리케이션 버전을 볼 수 있습니다. (예제에서 app.example.com을 실제 도메인 이름으로 대체하세요)

카나리 릴리스 자동화

Flagger는 카나리 노드로 트래픽을 점진적으로 전환하면서 HTTP 요청 성공률, 평균 요청 지연 시간, pod 상태 등 주요 성능 지표를 측정하는 제어 루프를 구현합니다. 관련 지표 분석에 따라 릴리스 또는 카나리 배포를 중단하고, Slack, MS Teams 또는 Prometheus Alert Manager와 같은 관련 플랫폼에 분석 결과를 게시합니다.

Flagger Control Loop

컨테이너 이미지 버전을 업데이트하여 카나리 릴리스를 트리거합니다.

kubectl -n test set image deployment/podinfo \ podinfod=stefanprodan/podinfo:6.0.1

Flagger는 배포의 새 버전이 있음을 감지하고 카나리 분석 릴리스의 시험 실행을 시작합니다.

kubectl -n test describe canary/podinfo

Status:
  Canary Weight:  0
  Conditions:
    Message:               Canary analysis completed successfully, promotion finished.
    Reason:                Succeeded
    Status:                True
    Type:                  Promoted
  Failed Checks:           1
  Iterations:              0
  Phase:                   Succeeded

Events:
  Type     Reason  Age                    From     Message
  ----     ------  ----                   ----     -------
  Warning  Synced  2m59s                  flagger  podinfo-primary.test not ready: waiting for rollout to finish: observed deployment generation less than desired generation
  Warning  Synced  2m50s                  flagger  podinfo-primary.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available
  Normal   Synced  2m40s (x3 over 2m59s)  flagger  all the metrics providers are available!
  Normal   Synced  2m39s                  flagger  Initialization done! podinfo.test
  Normal   Synced  2m20s                  flagger  New revision detected! Scaling up podinfo.test
  Warning  Synced  2m (x2 over 2m10s)     flagger  canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available
  Normal   Synced  110s                   flagger  Starting canary analysis for podinfo.test
  Normal   Synced  109s                   flagger  Advance podinfo.test canary weight 10
  Warning  Synced  100s                   flagger  Halt advancement no values found for apisix metric request-success-rate probably podinfo.test is not receiving traffic: running query failed: no values found
  Normal   Synced  90s                    flagger  Advance podinfo.test canary weight 20
  Normal   Synced  80s                    flagger  Advance podinfo.test canary weight 30
  Normal   Synced  69s                    flagger  Advance podinfo.test canary weight 40
  Normal   Synced  59s                    flagger  Advance podinfo.test canary weight 50
  Warning  Synced  30s (x2 over 40s)      flagger  podinfo-primary.test not ready: waiting for rollout to finish: 1 old replicas are pending termination
  Normal   Synced  9s (x3 over 50s)       flagger  (combined from similar events): Promotion completed! Scaling down podinfo.test

카나리 릴리스 과정에서 도메인 이름 app.example.com을 통해 애플리케이션에 접근하면 다른 응답을 받게 됩니다. (예제에서 app.example.com을 실제 도메인 이름으로 대체하세요)

featured-<version 2>.jpg

Flagger가 자동으로 생성한 Apache APISIX의 ApisixRoute 리소스 podinfo-podinfo-canary를 확인하면, 서비스 podinfo-primary와 서비스 podinfo-canary의 가중치가 릴리스 과정에 따라 변경되는 것을 볼 수 있습니다.

spec:
  http:
    - backends:
        - serviceName: podinfo-primary
          servicePort: 80
          # Flagger에 의해 자동 조정됨
          weight: 80
        - serviceName: podinfo-canary
          servicePort: 80
          # Flagger에 의해 자동 조정됨
          weight: 20

최종 릴리스가 완료되면 최신 안정 버전을 볼 수 있습니다.

featured-<version 3>.jpg

참고: 카나리 릴리스 중에 배포를 다시 변경하면 Flagger가 카나리 분석을 다시 실행합니다.

다음 명령어로 모든 카나리 릴리스를 관찰할 수 있습니다:

watch kubectl get canaries --all-namespaces

NAMESPACE   NAME      STATUS      WEIGHT   LASTTRANSITIONTIME
test        podinfo-2   Progressing   10       2022-11-23T05:00:54Z
test        podinfo     Succeeded     0        2022-11-23T06:00:54Z

롤백

카나리 릴리스 분석 중에 Flagger를 테스트하여 카나리 릴리스를 중단하고 이전 버전으로 롤백할 수 있습니다. HTTP 500 Bad Request를 생성하여 이를 수행합니다.

다른 카나리 릴리스를 트리거합니다:

kubectl -n test set image deployment/podinfo \ podinfod=stefanprodan/podinfo:6.0.2

부하 테스터 컨테이너에 접속합니다:

kubectl -n test exec -it deploy/flagger-loadtester bash

HTTP 500 오류를 생성합니다:

hey -z 1m -c 5 -q 5 -host app.example.com http://apisix-gateway.apisix/status/500

서버 지연을 시뮬레이션합니다:

watch -n 1 curl -H \"host: app.example.com\" http://apisix-gateway.apisix/delay/1

감지된 실패 횟수가 카나리 분석 임계값에 도달하면 트래픽이 자동으로 마스터 노드로 다시 라우팅되고, 카나리 노드는 0으로 축소되며, 카나리 릴리스 프로세스는 실패로 표시됩니다.

kubectl -n apisix logs deploy/flagger -f | jq .msg

"New revision detected! Scaling up podinfo.test"
"canary deployment podinfo.test not ready: waiting for rollout to finish: 0 of 1 (readyThreshold 100%) updated replicas are available"
"Starting canary analysis for podinfo.test"
"Advance podinfo.test canary weight 10"
"Halt podinfo.test advancement success rate 0.00% < 99%"
"Halt podinfo.test advancement success rate 26.76% < 99%"
"Halt podinfo.test advancement success rate 34.19% < 99%"
"Halt podinfo.test advancement success rate 37.32% < 99%"
"Halt podinfo.test advancement success rate 39.04% < 99%"
"Halt podinfo.test advancement success rate 40.13% < 99%"
"Halt podinfo.test advancement success rate 48.28% < 99%"
"Halt podinfo.test advancement success rate 50.35% < 99%"
"Halt podinfo.test advancement success rate 56.92% < 99%"
"Halt podinfo.test advancement success rate 67.70% < 99%"
"Rolling back podinfo.test failed checks threshold reached 10"
"Canary failed! Scaling down podinfo.test"

카나리 분석을 위한 사용자 정의 지표

카나리 분석은 Prometheus 지표를 쿼리하여 확장할 수 있습니다. 실제 비즈니스 시나리오에 따라 사용자 정의합니다. 지표 템플릿을 생성하고 클러스터에 제출합니다.

apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
  name: not-found-percentage
  namespace: test
spec:
  provider:
    type: prometheus
    address: http://flagger-prometheus.apisix:9090
  query: |
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+",
          code!~"4.."
        }[{{ interval }}]
      )
    )
    /
    sum(
      rate(
        apisix_http_status{
          route=~"{{ namespace }}_{{ route }}-{{ target }}-canary_.+"
        }[{{ interval }}]
      )
    ) * 100
# 카나리 릴리스의 분석을 수정하고 위에서 생성한 지표 템플릿을 추가합니다.
  analysis:
    metrics:
      - name: "404s percentage"
        templateRef:
          name: not-found-percentage
        thresholdRange:
          max: 5
        interval: 1m

위 구성은 HTTP 404 요청의 QPS(초당 쿼리 수)가 총 트래픽의 5%를 초과하는지 확인하여 카나리를 검증합니다. HTTP 404 요청이 5% 임계값을 초과하면 카나리 롤아웃이 실패합니다.

요약

위 프로세스는 더 많은 사용자 정의 지표 검사, Webhook, 수동 승인 및 Slack 또는 MS Teams 알림으로 확장할 수 있습니다.

Apache APISIX와 Flagger의 통합을 통해 매우 원활한 카나리 릴리스를 달성하여 릴리스 효율성을 높이고 릴리스 위험을 줄였습니다. 앞으로 두 커뮤니티는 더 긴밀히 협력하여 Blue/Green Mirroring 및 A/B Testing과 같은 더 많은 릴리스 기능을 실현할 것입니다.

Tags: