Skipper 카나리아 배포
Skipper 카나리아 배포 (Skipper Canary Deployments)
Skipper ingress controller와 Flagger를 함께 사용해서 canary 배포를 자동화하는 방법을 이 문서에서 알려드릴게요. Skipper 뒤에 앱 배포를 두고 Flagger가 트래픽을 점진적으로 옮겨가며 검증하도록 구성해 봅시다.
출처: 문서
본문
사전 준비 (Prerequisites)
Flagger는 Kubernetes 클러스터 v1.19 이상과 Skipper ingress v0.13 이상이 필요합니다.
Skipper ingress-controller는 upstream 정의를 사용해 설치할 수 있습니다.
다음 인자들이 관련이 있습니다:
- -enable-connection-metrics
- -histogram-metric-buckets=.01,1,10,100
- -kubernetes
- -kubernetes-in-cluster
- -kubernetes-path-mode=path-prefix
- -metrics-exp-decay-sample
- -metrics-flavour=prometheus
- -route-backend-metrics
- -route-backend-error-counters
- -route-response-metrics
- -serve-host-metrics
- -serve-route-metrics
- -whitelisted-healthcheck-cidr=0.0.0.0/0 # permit Kind source health checks
kustomize로 Flagger를 설치합니다:
kustomize build https://github.com/fluxcd/flagger/kustomize/kubernetes | kubectl apply -f -
부트스트랩 (Bootstrap)
Flagger는 Kubernetes deployment와 선택적으로 horizontal pod autoscaler(HPA)를 받아서 일련의 오브젝트(Kubernetes deployments, ClusterIP services, canary ingress)를 생성합니다. 이 오브젝트들은 앱을 클러스터 외부에 노출하며 canary 분석과 승격(promotion)을 진행시킵니다.
테스트 네임스페이스를 생성합니다:
kubectl create ns test
deployment와 horizontal pod autoscaler를 생성합니다:
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main
canary 분석 중 트래픽을 발생시킬 부하 테스트 서비스를 배포합니다:
helm upgrade -i flagger-loadtester flagger/loadtester \
--namespace=test
ingress 정의를 생성합니다(app.example.com을 자신의 도메인으로 바꾸세요):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: podinfo
namespace: test
labels:
app: podinfo
annotations:
kubernetes.io/ingress.class: "skipper"
spec:
rules:
- host: "app.example.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: podinfo
port:
number: 80
위 리소스를 podinfo-ingress.yaml로 저장한 뒤 적용합니다:
kubectl apply -f ./podinfo-ingress.yaml
canary 커스텀 리소스를 생성합니다(app.example.com을 자신의 도메인으로 바꾸세요):
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: podinfo
namespace: test
spec:
provider: skipper
# deployment reference
targetRef:
apiVersion: apps/v1
kind: Deployment
name: podinfo
# ingress reference
ingressRef:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: podinfo
# HPA reference (optional)
autoscalerRef:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
name: podinfo
# the maximum time in seconds for the canary deployment
# to make progress before it is rollback (default 600s)
progressDeadlineSeconds: 60
service:
# ClusterIP port number
port: 80
# container port number or name
targetPort: 9898
analysis:
# schedule interval (default 60s)
interval: 10s
# max number of failed metric checks before rollback
threshold: 10
# max traffic percentage routed to canary
# percentage (0-100)
maxWeight: 50
# canary increment step
# percentage (0-100)
stepWeight: 5
# Skipper Prometheus checks
metrics:
- name: request-success-rate
interval: 1m
# minimum req success rate (non 5xx responses)
# percentage (0-100)
thresholdRange:
min: 99
- name: request-duration
interval: 1m
# maximum req duration P99
# milliseconds
thresholdRange:
max: 500
webhooks:
- name: gate
type: confirm-rollout
url: http://flagger-loadtester.test/gate/approve
- name: acceptance-test
type: pre-rollout
url: http://flagger-loadtester.test/
timeout: 10s
metadata:
type: bash
cmd: "curl -sd 'test' http://podinfo-canary/token | grep token"
- name: load-test
type: rollout
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
type: cmd
cmd: "hey -z 10m -q 10 -c 2 -host app.example.com http://skipper-ingress.kube-system"
logCmdOutput: "true"
위 리소스를 podinfo-canary.yaml로 저장한 뒤 적용합니다:
kubectl apply -f ./podinfo-canary.yaml
몇 초 후 Flagger가 canary 오브젝트들을 생성합니다:
# applied
deployment.apps/podinfo
horizontalpodautoscaler.autoscaling/podinfo
ingress.networking.k8s.io/podinfo-ingress
canary.flagger.app/podinfo
# generated
deployment.apps/podinfo-primary
horizontalpodautoscaler.autoscaling/podinfo-primary
service/podinfo
service/podinfo-canary
service/podinfo-primary
ingress.networking.k8s.io/podinfo-canary
자동화된 canary 승격 (Automated canary promotion)
Flagger는 HTTP 요청 성공률, 요청 평균 지속 시간, pod 상태 같은 주요 성능 지표(KPI)를 측정하면서 canary로 트래픽을 점진적으로 이동시키는 컨트롤 루프를 구현합니다. KPI 분석 결과에 따라 canary는 승격되거나 중단(abort)되며, 분석 결과는 Slack이나 MS Teams로 게시됩니다.
canary 배포를 트리거하려면 컨테이너 이미지를 업데이트합니다:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6
Flagger는 deployment 리비전이 바뀐 것을 감지하고 새로운 롤아웃을 시작합니다:
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
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
Routing all traffic to primary
Promotion completed! Scaling down podinfo.test
canary 분석 중 deployment에 새 변경사항을 적용하면 Flagger가 분석을 다시 시작한다는 점을 참고하세요.
모든 canary를 다음 명령으로 모니터링할 수 있습니다:
watch kubectl get canaries --all-namespaces
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
test podinfo-2 Progressing 30 2020-08-14T12:32:12Z
test podinfo Succeeded 0 2020-08-14T11:23:88Z
자동화된 롤백 (Automated rollback)
canary 분석 중 HTTP 500 오류를 발생시켜 Flagger가 결함 버전을 일시 중지하고 롤백하는지 테스트할 수 있습니다.
또 다른 canary 배포를 트리거합니다:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6
부하 테스터 pod에 접속합니다:
kubectl -n test exec -it deploy/flagger-loadtester bash
HTTP 500 오류를 발생시킵니다:
hey -z 1m -c 5 -q 5 http://app.example.com/status/500
지연을 발생시킵니다:
watch -n 1 curl http://app.example.com/delay/1
실패한 검사 횟수가 canary 분석 임계값에 도달하면 트래픽이 primary로 되돌아가고, canary는 0으로 스케일 다운되며 롤아웃은 실패로 표시됩니다.
kubectl -n flagger-system 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 updated replicas are available
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
Advance podinfo.test canary weight 20
Halt podinfo.test advancement success rate 53.42% < 99%
Halt podinfo.test advancement success rate 53.19% < 99%
Halt podinfo.test advancement success rate 48.05% < 99%
Rolling back podinfo.test failed checks threshold reached 3
Canary failed! Scaling down podinfo.test
커스텀 메트릭 (Custom metrics)
canary 분석은 Prometheus 쿼리로 확장할 수 있습니다.
메트릭 템플릿을 생성하고 클러스터에 적용합니다:
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: latency
namespace: test
spec:
provider:
type: prometheus
address: http://flagger-prometheus.flagger-system:9090
query: |
histogram_quantile(0.99,
sum(
rate(
skipper_serve_route_duration_seconds_bucket{
route=~"{{ printf "kube(ew)?_%s__%s_canary__.*__%s_canary(_[0-9]+)?" namespace ingress service }}",
le="+Inf"
}[1m]
)
) by (le)
)
canary 분석을 수정하고 latency 검사를 추가합니다:
analysis:
metrics:
- name: "latency"
templateRef:
name: latency
thresholdRange:
max: 0.5
interval: 1m
임계값이 500ms로 설정되어 있어, 지난 1분간 평균 요청 지속 시간이 0.5초를 넘으면 분석이 실패하고 canary는 승격되지 않습니다.
컨테이너 이미지를 업데이트해 canary 배포를 트리거합니다:
kubectl -n test set image deployment/podinfo \
podinfod=stefanprodan/podinfo:4.0.6
높은 응답 지연을 발생시킵니다:
watch curl http://app.example.com/delay/2
Flagger 로그를 확인합니다:
kubectl -n flagger-system logs deployment/flagger -f | jq .msg
Starting canary deployment for podinfo.test
Advance podinfo.test canary weight 5
Advance podinfo.test canary weight 10
Advance podinfo.test canary weight 15
Halt podinfo.test advancement latency 1.20 > 0.5
Halt podinfo.test advancement latency 1.45 > 0.5
Halt podinfo.test advancement latency 1.60 > 0.5
Halt podinfo.test advancement latency 1.69 > 0.5
Halt podinfo.test advancement latency 1.70 > 0.5
Rolling back podinfo.test failed checks threshold reached 5
Canary failed! Scaling down podinfo.test
알림을 구성했다면 Flagger는 canary가 실패한 이유와 함께 알림을 보냅니다.