이 가이드에서는 Linkerd와 Flagger를 활용하여 카나리 배포와 A/B 테스트를 자동화하는 방법을 설명합니다.
사전 요구사항
Flagger는 Kubernetes 클러스터 v1.16 이상과 Linkerd 2.10 이상을 필요로 합니다.
Linkerd와 Prometheus(Linkerd Viz 구성요소)를 설치합니다:
linkerd install | kubectl apply -f -
linkerd viz install | kubectl apply -f -
linkerd 네임스페이스에 Flagger를 설치합니다:
kubectl apply -k github.com/fluxcd/flagger//kustomize/linkerd
초기 설정
Flagger는 Kubernetes 디플로이먼트와 선택적 Horizontal Pod Autoscaler(HPA)를 기반으로 하여 일련의 객체(Kubernetes 디플로이먼트, ClusterIP 서비스, SMI 트래픽 스플릿)를 생성합니다. 이러한 객체들은 애플리케이션을 메시 네트워크 내부에 노출시키고 카나리 분석 및 프로모션을 관리합니다.
test 네임스페이스를 생성하고 Linkerd 프록시 주입을 활성화합니다:
kubectl create ns test
kubectl annotate namespace test linkerd.io/inject=enabled
카나리 분석 중 트래픽을 생성하기 위한 로드 테스트 서비스를 설치합니다:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=main
디플로이먼트와 Horizontal Pod Autoscaler를 생성합니다:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main
podinfo 디플로이먼트에 대한 Canary 커스텀 리소스를 생성합니다:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
# 디플로이먼트 참조
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
# HPA 참조 (선택사항)
autoscalerRef:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
name: podinfo
# 카나리 디플로이먼트가 롤백되기 전에 진행해야 하는 최대 시간(초)
# 기본값: 600초
progressDeadlineSeconds: 60
service:
# ClusterIP 포트 번호
port: 9898
# 컨테이너 포트 번호 또는 이름 (선택사항)
targetPort: 9898
analysis:
# 스케줄 간격 (기본값: 60초)
interval: 30s
# 롤백 전 최대 실패 메트릭 체크 횟수
threshold: 5
# 카나리로 라우팅되는 최대 트래픽 백분율 (0-100)
maxWeight: 50
# 카나리 증가 단계 백분율 (0-100)
stepWeight: 5
# Linkerd Prometheus 체크
metrics:
- name: request-success-rate
# 최소 요청 성공률 (5xx가 아닌 응답)
# 백분율 (0-100)
thresholdRange:
min: 99
interval: 1m
- name: request-duration
# 최대 요청 지연 시간 P99 (밀리초)
thresholdRange:
max: 500
interval: 30s
# 테스트 (선택사항)
webhooks:
- name: acceptance-test
type: pre-rollout
url: http://flagger-loadtester.test/
timeout: 30s
metadata:
type: bash
cmd: "curl -sd 'test' http://podinfo-canary.test:9898/token | grep token"
- name: load-test
type: rollout
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 2m -q 10 -c 2 http://podinfo-canary.test:9898/"
위를 podinfo-canary.yaml로 저장한 후 적용합니다:
kubectl apply -f ./podinfo-canary.yaml
카나리 분석이 시작되면 Flagger는 트래픽을 카나리로 라우팅하기 전에 pre-rollout 웹후크를 호출합니다. 카나리 분석은 5분간 실행되며, 매 30초마다 HTTP 메트릭과 롤아웃 훅을 검증합니다.
몇 초 후 Flagger가 카나리 객체를 생성합니다:
# 적용됨
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
ingresses.extensions/podinfo
canary.flagger.app/podinfo
# 생성됨
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
trafficsplits.split.smi-spec.io/podinfo
초기화 후, podinfo 디플로이먼트는 0으로 스케일링되고, podinfo.test로의 트래픽은 기본 Pod로 라우팅됩니다. 카나리 분석 기간 중에는 podinfo-canary.test 주소를 사용하여 카나리 Pod에 직접 접근할 수 있습니다.
자동 카나리 프로모션
Flagger는 HTTP 요청成功率, 요청 평균 지속 시간, Pod 건강 상태 등 핵심 성능 지표를 측정하면서 점진적으로 트래픽을 카나리로 이전하는 제어 루프를 구현합니다. KPI 분석에 따라 카나리를 승격하거나 중단하고, 분석 결과를 Slack에 게시합니다.
카나리 디플로이먼트를 트리거하려면 컨테이너 이미지를 업데이트합니다:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:3.1.1
Flagger가 디플로이먼트 리비전 변경을 감지하고 새로운 배포를 시작합니다:
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 0
Phase: Succeeded
Events:
New revision detected! Scaling up podinfo.test
Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available
Pre-rollout check acceptance-test passed
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Advance podinfo.test canary weight 20
Advance podinfo.test canary weight 25
Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available
Advance podinfo.test canary weight 30
Advance podinfo.test canary weight 35
Advance podinfo.test canary weight 40
Advance podinfo.test canary weight 45
Advance podinfo.test canary weight 50
Copying podinfo.test template spec to podinfo-primary.test
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
Promotion completed! Scaling down podinfo.test
카나리 분석 기간 중 디플로이먼트에 새 변경을 적용하면 Flagger가 분석을 다시 시작합니다.
카나리 배포는 다음 객체의 변경으로 트리거됩니다:
- 디플로이먼트 PodSpec (컨테이너 이미지, 커맨드, 포트, 환경변수, 리소스 등)
- 볼륨으로 마운트되거나 환경변수로 매핑된 ConfigMaps
- 볼륨으로 마운트되거나 환경변수로 매핑된 Secrets
모든 카나리를 모니터링하려면:
watch 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
자동 롤백
카나리 분석 기간中にHTTP 500 오류와 높은 지연 시간을 발생시켜 Flagger가 장애 버전을 일시 중지하고 롤백하는지 테스트할 수 있습니다.
別のカ.na리 デプロイメントをトリガーします:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:3.1.2
로드테스터 Pod에서 다음 명령어를 실행합니다:
kubectl -n test exec -it flagger-loadtester-xx-xx sh
HTTP 500 오류를 생성합니다:
watch -n 1 curl http://podinfo-canary.test:9898/status/500
지연 시간을 생성합니다:
watch -n 1 curl http://podinfo-canary.test:9898/delay/1
실패한 체크 횟수가 카나리 분석 임계값에 도달하면 트래픽은 기본 서버로 라우팅되고, 카나리는 0으로 스케일링되며, 롤아웃은 실패로 표시됩니다.
kubectl -n test describe canary/podinfo
Status:
Canary Weight: 0
Failed Checks: 10
Phase: Failed
Events:
Starting canary analysis for podinfo.test
Pre-rollout check acceptance-test passed
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement success rate 69.17% < 99%
Halt podinfo.test advancement success rate 61.39% < 99%
Halt podinfo.test advancement success rate 55.06% < 99%
Halt podinfo.test advancement request duration 1.20s > 0.5s
Halt podinfo.test advancement request duration 1.45s > 0.5s
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.test
사용자 정의 메트릭
카나리 분석은 Prometheus 쿼리로 확장할 수 있습니다.
404 에러 검사를 정의해 보겠습니다. 카나리 분석을 편집하여 다음 메트릭을 추가합니다:
analysis:
metrics:
- name: "404s percentage"
threshold: 3
query: |
100 - sum(
rate(
response_total{
namespace="test",
deployment="podinfo",
status_code!="404",
direction="inbound"
}[1m]
)
)
/
sum(
rate(
response_total{
namespace="test",
deployment="podinfo",
direction="inbound"
}[1m]
)
)
* 100
위 구성은 HTTP 404 요청/초 비율이 총 트래픽의 3% 미만인지 확인하여 카나리 버전을 검증합니다. 404s율이 3% 임계값에 도달하면 분석이 중단되고 카나리는 실패로 표시됩니다.
카나리 디플로이먼트를 트리거하려면 컨테이너 이미지를 업데이트합니다:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:3.1.3
404를 생성합니다:
watch -n 1 curl http://podinfo-canary:9898/status/404
Flagger 로그를 모니터링합니다:
kubectl -n linkerd logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Pre-rollout check acceptance-test passed
Advance podinfo.test canary weight 5
Halt podinfo.test advancement 404s percentage 6.20 > 3
Halt podinfo.test advancement 404s percentage 6.45 > 3
Halt podinfo.test advancement 404s percentage 7.22 > 3
Halt podinfo.test advancement 404s percentage 6.50 > 3
Halt podinfo.test advancement 404s percentage 6.34 > 3
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.test
Slack이 구성되어 있으면 Flagger가 카나리 실패 이유를 설명하는 알림을 보냅니다.
Linkerd 인그레스
Flagger와 Linkerd 호환 가능한 인그레스 컨트롤러는 NGINX와 Gloo 두 가지가 있습니다.
NGINX를 설치합니다:
helm upgrade -i nginx-ingress stable/nginx-ingress \
--namespace ingress-nginx
podinfo에 대한 인그레스를 생성하고, 수신 헤더를 내부 서비스 이름으로 재작성합니다(Linkerd 필요):
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: podinfo
namespace: test
labels:
app: podinfo
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:9898;
proxy_hide_header l5d-remote-ip;
proxy_hide_header l5d-server-id;
spec:
rules:
- host: app.example.com
http:
paths:
- backend:
serviceName: podinfo
servicePort: 9898
인그레스 컨트롤러를 사용할 때, NGINX가 메시 외부에서 실행되므로 Linkerd 트래픽 스플릿이 수신 트래픽에 적용되지 않습니다. 프론트엔드 애플리케이션에 대한 카나리 분석을 실행하기 위해 Flagger는 shadow 인gress를 생성하고 NGINX 전용 어노테이션을 설정합니다.
A/B 테스트
가중 라우팅 외에도 Flagger는 HTTP 매칭 조건에 따라 트래픽을 카나리로 라우팅하도록 구성할 수 있습니다. A/B 테스트 시나리오에서는 HTTP 헤더 또는 쿠키를 사용하여 특정 사용자 그룹을 타겟팅합니다. 이는 세션 affinity가 필요한 프론트엔드 애플리케이션에 특히 유용합니다.
podinfo 카나리 분석을 편집하여 공급자를 nginx로 설정하고, 인그레스 참조를 추가하고, max/step 가중치를 제거하고 매칭 조건과 반복 횟수를 추가합니다:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
# 인그레스 참조
provider: nginx
ingressRef:
apiVersion: extensions/v1beta1
kind: Ingress
name: podinfo
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
autoscalerRef:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
name: podinfo
service:
# 컨테이너 포트
port: 9898
analysis:
interval: 1m
threshold: 10
iterations: 10
match:
# curl -H 'X-Canary: always' http://app.example.com
- headers:
x-canary:
exact: "always"
# curl -b 'canary=always' http://app.example.com
- headers:
cookie:
exact: "canary"
# Linkerd Prometheus 체크
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 30s
webhooks:
- name: acceptance-test
type: pre-rollout
url: http://flagger-loadtester.test/
timeout: 30s
metadata:
type: bash
cmd: "curl -sd 'test' http://podinfo-canary:9898/token | grep token"
- name: load-test
type: rollout
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 2m -q 10 -c 2 -H 'Cookie: canary=always' http://app.example.com"
위 구성은 10분간 분석을 실행하며, 대상 사용자는 canary 쿠키가 always로 설정되거나 X-Canary: always 헤더로 서비스를 호출하는 사용자입니다.
로드 테스트가 이제 외부 주소를 대상으로 하고 canary 쿠키를 사용하는 것을 주의하세요.
카나리 디플로이먼트를 트리거하려면 컨테이너 이미지를 업데이트합니다:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:3.1.4
Flagger가 디플로이먼트 리비전 변경을 감지하고 A/B 테스트를 시작합니다:
kubectl -n test describe canary/podinfo
Events:
Starting canary deployment for podinfo.test
Pre-rollout check acceptance-test passed
Advance podinfo.test canary iteration 1/10
Advance podinfo.test canary iteration 2/10
Advance podinfo.test canary iteration 3/10
Advance podinfo.test canary iteration 4/10
Advance podinfo.test canary iteration 5/10
Advance podinfo.test canary iteration 6/10
Advance podinfo.test canary iteration 7/10
Advance podinfo.test canary iteration 8/10
Advance podinfo.test canary iteration 9/10
Advance podinfo.test canary iteration 10/10
Copying podinfo.test template spec to podinfo-primary.test
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
Promotion completed! Scaling down podinfo.test