일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Kopf
- keda
- mlops
- Argo
- Kubernetes 인증
- opentelemetry
- nginx ingress
- operator
- MLflow
- xdp
- CANARY
- 오퍼레이터
- blue/green
- opensearch
- 카오스 엔지니어링
- gitops
- Model Serving
- argocd
- Kubeflow
- tekton
- Kubernetes
- seldon core
- Pulumi
- CI/CD
- Litmus
- knative
- kubernetes operator
- eBPF
- serving
- Continuous Deployment
- Today
- Total
Kubernetes 이야기
Flagger를 사용한 CD ( Continuous Deployment ) 구축하기 본문
Flagger
Flagger는 Kubernetes에서 실행되는 애플리케이션의 릴리스 프로세스를 자동화하는 CD 도구이다. Flagger는 현재 CNCF Incubating 프로젝트로서 사용자 요청 결과 (성공/실패) 메트릭을 자동으로 측정하고 설정한 임계치 미만으로 안정된 트래픽이 유지되면 새 버전으로 점진적으로 이동하여 프로덕션에 적용할 수 있는 솔루션이다.
Flagger는 GitOps 도구중 하니인 Flux 제품의 일부분인 프로젝트이다.
Flagger 는 배포를 진행할때 Blue/Green 또는 Canary 배포를 진행함에 있어 라우팅을 담당하는 여러 공급자와 연계하여 진행하게 된다.
대표적으로 아래의 공급자와 연계 가능하다.
Service mesh
- Istio
- Linkerd
- AWS App Mesh
- Open Service Mesh
- Kuma
Ingress Controller
- Nginx Ingress
- Contour
- Gloo
- Skipper Ingress
- Traefik
그럼 가장 대표적으로 많이 사용되는 Nginx Ingress와 연동하여 테스트를 진행해 보자.
설치
Flagger 는 Kubernetes v1.19 이상, Nginx ingress v1.0.2 이상이 필요하다.
테스트환경
Kubernetes 1.21
Nginx ingress 1.1.1
1) Nginx Ingress Controller 설치
# helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# kubectl create ns ingress-nginx
# helm upgrade -i ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--set controller.hostNetwork=true \
--set controller.metrics.enabled=true \
--set controller.podAnnotations."prometheus\.io/scrape"=true \
--set controller.podAnnotations."prometheus\.io/port"=10254
2) Flagger 설치
# helm repo add flagger https://flagger.app
# helm upgrade -i flagger flagger/flagger \
--namespace ingress-nginx \
--set prometheus.install=true \
--set meshProvider=nginx
설치된 내용은 아래와 같다.
# kubectl get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/flagger-695957cd69-g7v2k 1/1 Running 0 97s
pod/flagger-prometheus-7c7c88d987-428bj 1/1 Running 0 97s
pod/ingress-nginx-controller-7fnwh 1/1 Running 0 23m
pod/ingress-nginx-controller-kcthm 1/1 Running 0 23m
pod/ingress-nginx-controller-qfbw7 1/1 Running 0 23m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/flagger-prometheus ClusterIP 10.110.163.32 <none> 9090/TCP 97s
service/ingress-nginx-controller ClusterIP 10.109.32.60 <none> 80/TCP,443/TCP 29m
service/ingress-nginx-controller-admission ClusterIP 10.100.31.13 <none> 443/TCP 29m
service/ingress-nginx-controller-metrics ClusterIP 10.108.187.140 <none> 10254/TCP 29m
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/ingress-nginx-controller 3 3 3 3 3 kubernetes.io/os=linux 23m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/flagger 1/1 1 1 97s
deployment.apps/flagger-prometheus 1/1 1 1 97s
NAME DESIRED CURRENT READY AGE
replicaset.apps/flagger-695957cd69 1 1 1 97s
replicaset.apps/flagger-prometheus-7c7c88d987 1 1 1 97s
세팅 후 prometheus를 확인 해 보면 아래와 같이 데이터가 쌓이는 것을 볼수 있다.
이제 앱을 배포해 보자.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-v1
spec:
replicas: 3
selector:
matchLabels:
app: hello-v1
template:
metadata:
labels:
app: hello-v1
spec:
containers:
- name: hello
image: ghcr.io/kmaster8/hello:v1
ports:
- containerPort: 5050
---
apiVersion: v1
kind: Service
metadata:
name: hello-v1
spec:
type: ClusterIP
selector:
app: hello-v1
ports:
- protocol: TCP
port: 5000
targetPort: 5000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: prod-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: test.prod.10.60.200.121.sslip.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-v1
port:
number: 5000
앱을 test namespace에 배포하자. ( 주의할 점은 deployment name과 service name은 반드시 동일해야 한다. )
그리고 blue/green 배포 script를 만들어 보자.
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: hello
namespace: test
spec:
provider: nginx
# deployment reference
targetRef:
apiVersion: apps/v1
kind: Deployment
name: hello-v1
# ingress reference
ingressRef:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: prod-ingress
# 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: 5000
# container port number or name
targetPort: 5000
analysis:
# schedule interval (default 60s)
interval: 10s
iterations: 3
# max number of failed metric checks before rollback
threshold: 2
# NGINX Prometheus checks
metrics:
- name: error-rate
templateRef:
name: error-rate
thresholdRange:
max: 0
interval: 5s
위의 설정은 잠시 살펴보자
1) hello-v1이라는 deployment를 배포
2) prod-ingress 라는 이름의 ingress 사용
3) service port는 5000번 사용
4) 10초 간격으로 3번 체크. 2번 메트릭 정보를 조회 실패하면 롤백처리
5) error-rate라는 template을 조회하여 임계치가 0일때만 blue/green 배포완료되도록 하고 , 에러율이 0이 아니면 롤백을 한다.
여기서 error-rate라는 template을 보자.
apiVersion: flagger.app/v1beta1
kind: MetricTemplate
metadata:
name: error-rate
namespace: test
spec:
provider:
address: http://flagger-prometheus.ingress-nginx:9090
type: prometheus
query: |
sum(rate(nginx_ingress_controller_requests{status=~"5.*|4.*",namespace="{{ namespace }}",service="{{ target }}"}[{{ interval }}])) /
sum(rate(nginx_ingress_controller_requests{namespace="{{ namespace }}",service="{{ target }}"}[{{ interval }}])) * 100
이제 blue/green 배포 script를 실행하면 아래와 같이 deployment와 service의 상태가 변경된다.
# kubectl get all -n test
NAME READY STATUS RESTARTS AGE
pod/hello-v1-55575b9897-42q4f 1/1 Running 0 28s
pod/hello-v1-55575b9897-7pp9x 1/1 Running 0 28s
pod/hello-v1-55575b9897-w42vh 1/1 Running 0 28s
pod/hello-v1-primary-7479df5558-49scr 1/1 Running 0 10s
pod/hello-v1-primary-7479df5558-blrkr 1/1 Running 0 10s
pod/hello-v1-primary-7479df5558-zm47x 1/1 Running 0 10s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-v1 ClusterIP 10.103.254.249 <none> 5000/TCP 94s
service/hello-v1-canary ClusterIP 10.107.16.18 <none> 5000/TCP 10s
service/hello-v1-primary ClusterIP 10.106.97.212 <none> 5000/TCP 10s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-v1 0/0 3 3 28s
deployment.apps/hello-v1-primary 3/3 3 3 10s
NAME DESIRED CURRENT READY AGE
replicaset.apps/hello-v1-55575b9897 0 0 0 28s
replicaset.apps/hello-v1-primary-7479df5558 3 3 3 10s
NAME STATUS WEIGHT LASTTRANSITIONTIME
canary.flagger.app/hello Initialized 0 2022-02-24T23:30:07Z
deployment hello-v1-primary 이 생성되고 hello-v1-canary, hello-v1-primary 가 자동 생성된다. 그리고 기존 hello-v1 deployment는 replicas=0 으로 변경된다.
여기서 hello-v1-primary 는 version1 (현재 운영중인 버전) 이 되고, hello-v1이 version2 ( 새로운 앱 )이라고 생각하면 된다.
브라우저에서 호출해 보면
아직 version1 이 호출된다. 이제 새로운 앱을 배포해 보자. 기존 hello-v1 deployment의 이미지를 변경해 보겠다.
kubectl -n test set image deployment/hello-v1 hello=ghcr.io/kmaster8/hello:v2
이미지 변경은 하면 아래와 같이 hello-v1 이 생성되는 것을 볼 수 있다. flagger의 상태도 Progressing으로 변경된다.
# kubectl get all -n test
NAME READY STATUS RESTARTS AGE
pod/hello-v1-b97758d4b-k2rtg 1/1 Running 0 7s
pod/hello-v1-primary-7479df5558-49scr 1/1 Running 0 3m57s
pod/hello-v1-primary-7479df5558-blrkr 1/1 Running 0 3m57s
pod/hello-v1-primary-7479df5558-zm47x 1/1 Running 0 3m57s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hello-v1 ClusterIP 10.103.254.249 <none> 5000/TCP 5m21s
service/hello-v1-canary ClusterIP 10.107.16.18 <none> 5000/TCP 3m57s
service/hello-v1-primary ClusterIP 10.106.97.212 <none> 5000/TCP 3m57s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello-v1 1/1 1 1 4m15s
deployment.apps/hello-v1-primary 3/3 3 3 3m57s
NAME DESIRED CURRENT READY AGE
replicaset.apps/hello-v1-55575b9897 0 0 0 4m15s
replicaset.apps/hello-v1-b97758d4b 1 1 1 8s
replicaset.apps/hello-v1-primary-7479df5558 3 3 3 3m57s
NAME STATUS WEIGHT LASTTRANSITIONTIME
canary.flagger.app/hello Progressing 0 2022-02-24T23:33:47Z
ingress를 보면
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
kustomize.toolkit.fluxcd.io/reconcile: disabled
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "0"
creationTimestamp: "2022-02-24T23:48:37Z"
generation: 1
name: prod-ingress-canary
namespace: test
ownerReferences:
- apiVersion: flagger.app/v1beta1
blockOwnerDeletion: true
controller: true
kind: Canary
name: hello
uid: 434d4569-1e6c-4034-a5f8-4d56485c4a9a
resourceVersion: "10329251"
selfLink: /apis/networking.k8s.io/v1/namespaces/test/ingresses/prod-ingress-canary
uid: 8494232d-b799-45b7-9abd-48fbe4b66e23
spec:
rules:
- host: test.prod.10.60.200.121.sslip.io
http:
paths:
- backend:
service:
name: hello-v1-canary
port:
number: 5000
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 10.109.32.60
위와 같이 nginx.ingress.kubernetes.io/canary-weight: "0" 설정이 들어가는 것을 볼 수 있다. 현재는 canary배포가 아닌 blue/green 배포라서 0이 세팅되고 canary 배포로 설정하면 비율값이 들어가도록 되어 있다.
# kubectl describe canary -n test hello
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Synced 5m18s flagger hello-v1-primary.test not ready: waiting for rollout to finish: observed deployment generation less than desired generation
Normal Synced 5m8s (x2 over 5m18s) flagger all the metrics providers are available!
Normal Synced 5m8s flagger Initialization done! hello.test
Normal Synced 69s (x2 over 5m49s) flagger New revision detected! Scaling up hello-v1.test
Normal Synced 49s flagger New revision detected! Restarting analysis for hello-v1.test
Normal Synced 39s (x3 over 5m39s) flagger Starting canary analysis for hello-v1.test
Normal Synced 39s (x3 over 5m39s) flagger Advance hello.test canary iteration 1/3
Normal Synced 29s flagger Advance hello.test canary iteration 2/3
Normal Synced 19s flagger Advance hello.test canary iteration 3/3
Normal Synced 9s flagger Routing all traffic to canary
---
현재 blue/green 검증을 위한 error-rate metric을 조회하여 에러율이 0이면 자동으로 신규앱으로 이동함을 확인할 수 있다.
결론
보통 Blue/Green, Canary, A/B 테스트 등은 모두 배포 후 별도 검증을 통해 이상이 없으면 운영자가 routing rule 을 변경하여 배포하는 방식이다.
여기서 별도 검증이라고 하면 에러율 ( 404, 500 등 ) 이 없는 조건이거나, 응답시간이 ~초 이하 등이 될 수 있을 것이다.
Flagger 을 사용하면 사용자의 요청에 대한 에러율, 응답시간 등 다양한 조건을 Prometheus query로 만들어서 운영자가 개입하지 않고서도 자동 배포를 만들 수 있는 유용한 도구이다.
'Kubernetes > devops' 카테고리의 다른 글
Docker 없이 Docker Image 만들기 ( Kaniko ) (2) | 2022.02.27 |
---|---|
Argo rollout을 통한 CD (Continuous Deployment) 구축하기 (1) - Canary 배포 (0) | 2022.02.25 |
Kubernetes의 다양한 배포방식 (3) Canary 배포 (0) | 2022.02.20 |
Kubernetes의 다양한 배포방식 (2) Blue/Green 배포 (0) | 2022.02.19 |
Kubernetes의 다양한 배포방식 (1) RollingUpdate (0) | 2022.02.19 |