Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

Gitlab CI์™€ ์ˆ˜๋™ GitOps๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Kubernetes์—์„œ Canary ๋ฐฐํฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

์ด ์‹œ๋ฆฌ์ฆˆ์˜ ๊ธฐ์‚ฌ:

GitOps๋ฅผ ํ†ตํ•ด Canary ๋ฐฐํฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ธฐ๋ณธ Kubernetes ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑ/์ˆ˜์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ์‚ฌ๋Š” ์ฃผ๋กœ ์†Œ๊ฐœ์šฉ์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๊ธฐ์‚ฌ์—์„œ ๊ณ ๋ คํ•  ๋ณด๋‹ค ํšจ๊ณผ์ ์ธ ์ž๋™ํ™” ๋ฐฉ๋ฒ•์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Kubernetes Canary์—์„œ ๋ฐฐํฌ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.


Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

https://www.norberteder.com/canary-deployment/

์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ

์นด๋‚˜๋ฆฌ์•„ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๋ฉด ๋จผ์ € ์ผ๋ถ€ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋‹ˆํ„ฐ๋ง, ๋กœ๊ทธ ๋ฐ์ดํ„ฐ, ์ˆ˜๋™ ํ…Œ์ŠคํŠธ ๋˜๋Š” ๊ธฐํƒ€ ํ”ผ๋“œ๋ฐฑ ์ฑ„๋„์„ ํ†ตํ•ด ๋ฆด๋ฆฌ์Šค๋Š” ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฆด๋ฆฌ์Šค๋˜๊ธฐ ์ „์— ํ…Œ์ŠคํŠธ๋ฉ๋‹ˆ๋‹ค.

Kubernetes ๋ฐฐํฌ(๋กค๋ง ์—…๋ฐ์ดํŠธ)

Kubernetes ๋ฐฐํฌ์˜ ๊ธฐ๋ณธ ์ „๋žต์€ ๋กค๋ง ์—…๋ฐ์ดํŠธ์ด๋ฉฐ, ์—ฌ๊ธฐ์„œ ํŠน์ • ์ˆ˜์˜ Pod๊ฐ€ ์ƒˆ ๋ฒ„์ „์˜ ์ด๋ฏธ์ง€๋กœ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ ์—†์ด ์ƒ์„ฑ๋œ ๊ฒฝ์šฐ ์ด์ „ ๋ฒ„์ „์˜ ์ด๋ฏธ์ง€๊ฐ€ ํฌํ•จ๋œ ํฌ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ  ์ƒˆ ํฌ๋“œ๊ฐ€ ๋ณ‘๋ ฌ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

GitOps

์ด ์˜ˆ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ GitOps๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • Git์„ ๋‹จ์ผ ์ •๋ณด ์†Œ์Šค๋กœ ์‚ฌ์šฉ
  • ๋นŒ๋“œ ๋ฐ ๋ฐฐํฌ์— Git Operations๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค(git tag/merge ์ด์™ธ์˜ ๋ช…๋ น์€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

์˜ˆ

๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์šฉ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํ•˜๋‚˜์™€ ์ธํ”„๋ผ์šฉ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํ•˜๋‚˜๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ €์žฅ์†Œ

์ด๋Š” ์‘๋‹ต์„ JSON์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•œ Python+Flask API์ž…๋‹ˆ๋‹ค. GitlabCI๋ฅผ ํ†ตํ•ด ํŒจํ‚ค์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ Gitlab ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ํ‘ธ์‹œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—๋Š” ๋‘ ๊ฐ€์ง€ ๋ฆด๋ฆฌ์Šค ๋ฒ„์ „์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • wuestkamp/k8s-deployment-example-app:v1
  • wuestkamp/k8s-deployment-example-app:v2

์ด๋“ค ์‚ฌ์ด์˜ ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ๋ฐ˜ํ™˜๋œ JSON ํŒŒ์ผ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์–ด๋–ค ๋ฒ„์ „๊ณผ ํ†ต์‹ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ๊ฐ€๋Šฅํ•œ ํ•œ ์‰ฝ๊ฒŒ ์‹œ๊ฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ธํ”„๋ผ ์ €์žฅ์†Œ

์ด ์ˆœ๋ฌด์—์„œ๋Š” GitlabCI๋ฅผ ํ†ตํ•ด Kubernetes์— ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. .gitlab-ci.yml ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค :

image: traherom/kustomize-docker

before_script:
   - printenv
   - kubectl version

stages:
 - deploy

deploy test:
   stage: deploy
   before_script:
     - echo $KUBECONFIG
   script:
     - kubectl get all
     - kubectl apply -f i/k8s

   only:
     - master

์ง์ ‘ ์‹คํ–‰ํ•˜๋ ค๋ฉด ํด๋Ÿฌ์Šคํ„ฐ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ Gcloud๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

gcloud container clusters create canary --num-nodes 3 --zone europe-west3-b

gcloud compute firewall-rules create incoming-80 --allow tcp:80

ํฌํฌํ•ด์•ผ ํ•ด https://gitlab.com/wuestkamp/k8s-deployment-example-canary-infrastructure ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ  KUBECONFIG ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•œ ๊ตฌ์„ฑ์ด ํฌํ•จ๋œ GitlabCI์—์„œ kubectl ํด๋Ÿฌ์Šคํ„ฐ์—.

ํด๋Ÿฌ์Šคํ„ฐ(Gcloud)์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์„ ์ฝ์–ด๋ณด์„ธ์š”. ์—ฌ๊ธฐ์—.

์ธํ”„๋ผ Yaml

์ธํ”„๋ผ ์ €์žฅ์†Œ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์„œ๋น„์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

apiVersion: v1
kind: Service
metadata:
 labels:
   id: app
 name: app
spec:
 ports:
 - port: 80
   protocol: TCP
   targetPort: 5000
 selector:
   id: app
 type: LoadBalancer

๊ทธ๋ฆฌ๊ณ  ๋ฐฐํฌ deploy.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: app
spec:
 replicas: 10
 selector:
   matchLabels:
     id: app
     type: main
 template:
   metadata:
     labels:
       id: app
       type: main
   spec:
     containers:
     - image: registry.gitlab.com/wuestkamp/k8s-deployment-example-app:v1
       name: app
       resources:
         limits:
           cpu: 100m
           memory: 100Mi

๊ทธ๋ฆฌ๊ณ  ๋˜ ๋‹ค๋ฅธ ๋ฐฐํฌ deploy-canary.yaml:

kind: Deployment
metadata:
 name: app-canary
spec:
 replicas: 0
 selector:
   matchLabels:
     id: app
     type: canary
 template:
   metadata:
     labels:
       id: app
       type: canary
   spec:
     containers:
     - image: registry.gitlab.com/wuestkamp/k8s-deployment-example-app:v2
       name: app
       resources:
         limits:
           cpu: 100m
           memory: 100Mi

app-deploy์—๋Š” ์•„์ง ๋ณต์ œ๋ณธ์ด ์ •์˜๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐ ๋ฐฐํฌ ์ˆ˜ํ–‰

์ดˆ๊ธฐ ๋ฐฐํฌ๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ๋งˆ์Šคํ„ฐ ๋ถ„๊ธฐ์—์„œ GitlabCI ํŒŒ์ดํ”„๋ผ์ธ์„ ์ˆ˜๋™์œผ๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ kubectl ๋‹ค์Œ์„ ์ถœ๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

์šฐ๋ฆฌ๊ฐ€ ๋ณธ๋‹ค. app 10๊ฐœ์˜ ๋ณต์ œ๋ณธ์œผ๋กœ ๋ฐฐํฌํ•˜๊ณ  0์œผ๋กœ app-canary๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์„ ํ†ตํ•ด ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” LoadBalancer๋„ ์žˆ์Šต๋‹ˆ๋‹ค. curl ์™ธ๋ถ€ IP๋ฅผ ํ†ตํ•ด:

while true; do curl -s 35.198.149.232 | grep label; sleep 0.1; done

Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

ํ…Œ์ŠคํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด "v1"๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ ์‹คํ–‰

1๋‹จ๊ณ„: ์ผ๋ถ€ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ์ƒˆ ๋ฒ„์ „ ์ถœ์‹œ

๋ฐฐํฌ-canary.yaml ํŒŒ์ผ๊ณผ ์ƒˆ ๋ฒ„์ „ ์ด๋ฏธ์ง€์—์„œ ๋ณต์ œ๋ณธ ์ˆ˜๋ฅผ 1๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

kind: Deployment
metadata:
 name: app-canary
spec:
 replicas: 1
 selector:
   matchLabels:
     id: app
     type: canary
 template:
   metadata:
     labels:
       id: app
       type: canary
   spec:
     containers:
     - image: registry.gitlab.com/wuestkamp/k8s-deployment-example-app:v2
       name: app
       resources:
         limits:
           cpu: 100m
           memory: 100Mi

ํŒŒ์ผ์—์„œ deploy.yaml ๋ณต์ œ๋ณธ ์ˆ˜๋ฅผ 9๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

kind: Deployment
metadata:
 name: app
spec:
 replicas: 9
 selector:
   matchLabels:
     id: app
...

GitlabCI๋ฅผ ํ†ตํ•ด ๋ฐฐํฌ๊ฐ€ ์‹œ์ž‘๋  ์ €์žฅ์†Œ์— ์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ‘ธ์‹œํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

๋‘ ๋ฐฐํฌ ๋ชจ๋‘์— ์•ฑ ์„ ํƒ๊ธฐ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์šฐ๋ฆฌ ์„œ๋น„์Šค๋Š” ๋‘ ๋ฐฐํฌ๋ฅผ ๋ชจ๋‘ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. Kubernetes์˜ ๊ธฐ๋ณธ ๋ฌด์ž‘์œ„ํ™”๋กœ ์ธํ•ด ์š”์ฒญ์˜ ~10%์— ๋Œ€ํ•ด ์„œ๋กœ ๋‹ค๋ฅธ ์‘๋‹ต์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(Git์—์„œ Single Source Of Truth๋กœ ๊ฐ€์ ธ์˜จ GitOps)์˜ ํ˜„์žฌ ์ƒํƒœ๋Š” ๊ฐ ๋ฒ„์ „๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ํ™œ์„ฑ ๋ณต์ œ๋ณธ์ด ์žˆ๋Š” ๋‘ ๊ฐœ์˜ ๋ฐฐํฌ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•ฝ 10%์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ๋ฒ„์ „์— ์ต์ˆ™ํ•ด์ง€๊ณ  ์˜๋„์น˜ ์•Š๊ฒŒ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ๋ฌธ์ œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ๋กœ๊ทธ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ์ดํ„ฐ์˜ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•ด์•ผ ํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.

2๋‹จ๊ณ„: ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ƒˆ ๋ฒ„์ „ ์ถœ์‹œ

์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ง„ํ–‰๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์œผ๋ฉฐ ์ด์ œ ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ƒˆ ๋ฒ„์ „์„ ์ถœ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๊ฐ„๋‹จํžˆ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. deploy.yaml ์ƒˆ ๋ฒ„์ „์˜ ์ด๋ฏธ์ง€๋ฅผ ์„ค์น˜ํ•˜๊ณ  ๋ณต์ œ๋ณธ ์ˆ˜๋Š” 10์ž…๋‹ˆ๋‹ค. deploy-canary.yaml ๋ณต์ œ๋ณธ ์ˆ˜๋ฅผ ๋‹ค์‹œ 0์œผ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐฐํฌ ํ›„ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Kubernetes #1์˜ ์นด๋‚˜๋ฆฌ์•„ ๋ฐฐํฌ: Gitlab CI

์ตœ๋Œ€ ํ•ฉ๊ณ„

๋‚˜์—๊ฒŒ๋Š” ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ๋ฐฐํฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด k8s๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–ผ๋งˆ๋‚˜ ์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. Kubernetes๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด API๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ๊ฒƒ์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ด๋Ÿฌํ•œ ๋‹จ๊ณ„๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ์ž๋™ํ™”๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ํ•ด์•ผ ํ•  ๋˜ ๋‹ค๋ฅธ ์‚ฌํ•ญ์€ ์ƒˆ ๋ฒ„์ „์—๋งŒ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์Šคํ„ฐ ์ง„์ž…์ (LoadBalancer ๋˜๋Š” Ingress๋ฅผ ํ†ตํ•ด)์ž…๋‹ˆ๋‹ค. ์ˆ˜๋™ ๊ฒ€์ƒ‰์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ–ฅํ›„ ๊ธฐ์‚ฌ์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ์ž‘์—…์„ ๊ตฌํ˜„ํ•˜๋Š” ๋‹ค๋ฅธ ์ž๋™ํ™” ์†”๋ฃจ์…˜์„ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ์—์„œ ๋‹ค๋ฅธ ๊ธฐ์‚ฌ๋„ ์ฝ์–ด๋ณด์„ธ์š”.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€