APISIX Ingress Controller와 Flagger를 사용한 원활한 Canary Release
Hengliang Tan
January 19, 2023
작성자: 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를 사용한 자동 카나리 릴리스를 지원합니다.
이제 이 원활한 카나리 릴리스 프로세스를 함께 경험해 보겠습니다.
환경
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
이제 도메인 이름 app.example.com을 통해 애플리케이션에 접근할 수 있으며, 현재 애플리케이션 버전을 볼 수 있습니다. (예제에서 app.example.com
을 실제 도메인 이름으로 대체하세요)
카나리 릴리스 자동화
Flagger는 카나리 노드로 트래픽을 점진적으로 전환하면서 HTTP 요청 성공률, 평균 요청 지연 시간, pod 상태 등 주요 성능 지표를 측정하는 제어 루프를 구현합니다. 관련 지표 분석에 따라 릴리스 또는 카나리 배포를 중단하고, Slack, MS Teams 또는 Prometheus Alert Manager와 같은 관련 플랫폼에 분석 결과를 게시합니다.
컨테이너 이미지 버전을 업데이트하여 카나리 릴리스를 트리거합니다.
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
을 실제 도메인 이름으로 대체하세요)
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
최종 릴리스가 완료되면 최신 안정 버전을 볼 수 있습니다.
참고: 카나리 릴리스 중에 배포를 다시 변경하면 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과 같은 더 많은 릴리스 기능을 실현할 것입니다.