【Kubernetes】DeploymentとServiceでカナリアデプロイやってみる
はじめに
Kubernetesのデプロイ戦略として、DeploymentとServiceだけでカナリアデプロイを動かしてみます。
Kubernetesでの代表的なデプロイ戦略については、下記でざっくりまとめています。
【Kubernetes】デプロイ戦略についてざっくり理解する
:::affiliate-message 本ページはAmazonアフィリエイトのリンクを含みます。
カナリアデプロイとは
カナリアデプロイは、少数の新しいバージョンをデプロイし、問題がなければ徐々に新しいバージョンを増やすデプロイ方法です。
メリット | デメリット |
---|---|
・ダウンタイムがゼロ ・すぐに旧バージョンに戻せる ・新しいバージョンを本番環境で試せる ・様子を見ながら新しいバージョンに移行できる | ・ロールアウトまで時間がかかる ・モニタリングの手間がかかる |
実際に試してみる
実際にカナリアデプロイをやってみます。
カナリアデプロイを実現する方法はいくつかあると思いますが、今回はDeploymentとServiceで実装します。
現行のバージョンと新しいバージョンの共有のラベルに対して、Serviceからトラフィックを流し、新しいバージョンのレプリカ数を増やしていくことで実装します。
作成するマニフェスト
今回、作成するマニフェストは下記の通りです。
1.
2├── configmap-v1.yml # v1のhtml用
3├── configmap-v2.yml # v2のhtml用
4├── deployment-v1.yml # v1のDeployment
5├── deployment-v2.yml # v2のDeployment
6└── service.yml # アプリのService
v1アプリのデプロイ
まずは、v1のアプリをデプロイします。
nginxで表示するHTMLファイルを、ConfigMapを使ってマウントします。
configmap-v1.yml
は下記の通りです。
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: index-html-v1
5data:
6 index.html: |-
7 <!DOCTYPE html>
8 <html lang="ja">
9 <head>
10 <meta charset="UTF-8">
11 </head>
12 <body style="background-color: blue">
13 <h2>
14 v1 app.
15 </h2>
16 </body>
17 </html>
v1アプリは、v1用のConfigMapをマウントしたものになります。
deployment-v1.yml
は、下記の通りです。
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: myapp-v1
5spec:
6 replicas: 3
7 selector:
8 matchLabels:
9 app: myapp
10 template:
11 metadata:
12 labels:
13 app: myapp # アプリ共通のラベル
14 version: v1 # バージョンを表すラベル
15 spec:
16 containers:
17 - name: web
18 image: nginx
19 ports:
20 - containerPort: 80
21 volumeMounts:
22 - name: html-volume
23 mountPath: /usr/share/nginx/html
24 volumes:
25 - name: html-volume
26 configMap:
27 name: index-html-v1
28
アプリのServiceは、共通のラベルをセレクタとして使用します。バージョンのラベルはセレクタとして使用しません。
service.yml
は下記の通りです。
1apiVersion: v1
2kind: Service
3metadata:
4 name: myapp-service
5spec:
6 ports:
7 - name: http
8 port: 80
9 selector:
10 app: myapp # アプリ共通のラベル
11 type: NodePort
ConfigMap、Deployment、Serviceそれぞれ作成します。
1kubectl apply -f configmap-v1.yml,deployment-v1.yml,service.yml
それぞれ作成されたのが確認できます。
1❯ kubectl get pod,svc,cm
2NAME READY STATUS RESTARTS AGE
3pod/myapp-v1-556b75687-65ckg 1/1 Running 0 11s
4pod/myapp-v1-556b75687-6mf28 1/1 Running 0 11s
5pod/myapp-v1-556b75687-h8s26 1/1 Running 0 11s
6
7NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
8service/myapp-service NodePort 10.100.240.251 <none> 80:32207/TCP 11s
9
10NAME DATA AGE
11configmap/index-html-v1 1 11s
curl
でNodePortを指定して確認してみると、v1のアプリが動作していることが確認できます。何度実行してもv1のアプリからしかレスポンスは返ってきません。
1❯ curl localhost:32207
2<!DOCTYPE html>
3<html lang="ja">
4<head>
5 <meta charset="UTF-8">
6</head>
7<body style="background-color: blue">
8 <h2>
9 v1 app.
10 </h2>
11</body>
12</html>
v2アプリのデプロイ
v2のアプリの準備をしていきます。
v2のConfigMapのマニフェストconfigmap-v1.yml
は下記の通りです。
1apiVersion: v1
2kind: ConfigMap
3metadata:
4 name: index-html-v2
5data:
6 index.html: |-
7 <!DOCTYPE html>
8 <html lang="ja">
9 <head>
10 <meta charset="UTF-8">
11 </head>
12 <body style="background-color: green">
13 <h2>
14 v2 app.
15 </h2>
16 </body>
17 </html>
v2のDeploymentのマニフェストdeployment-v2.yaml
は下記の通りです。
v2のアプリはv2のConfigMapをマウントしており、レプリカ数は少なく、v2を表すラベルが追加されています。
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: myapp-v2
5spec:
6 replicas: 1 # 最初はレプリカ数を少なく
7 selector:
8 matchLabels:
9 app: myapp
10 template:
11 metadata:
12 labels:
13 app: myapp # アプリ共通のラベル
14 version: v2 # バージョンを表すラベル
15 spec:
16 containers:
17 - name: web
18 image: nginx
19 ports:
20 - containerPort: 80
21 volumeMounts:
22 - name: html-volume
23 mountPath: /usr/share/nginx/html
24 volumes:
25 - name: html-volume
26 configMap:
27 name: index-html-v2
ConfigMapとDeploymentを作成します。
1kubectl apply -f configmap-v2.yml,deployment-v2.yml
現行のバージョン(v1)のPodが3つ、新しいバージョン(v2)のPodが1つで、同じServiceからトラフィックが流れてくるようになりました。
1❯ kubectl get pod,svc,cm
2NAME READY STATUS RESTARTS AGE
3pod/myapp-v1-556b75687-65ckg 1/1 Running 0 2m39s
4pod/myapp-v1-556b75687-6mf28 1/1 Running 0 2m39s
5pod/myapp-v1-556b75687-h8s26 1/1 Running 0 2m39s
6pod/myapp-v2-6d55d79f9f-2hg7q 1/1 Running 0 9s
7
8NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
9service/myapp-service NodePort 10.100.240.251 <none> 80:32207/TCP 2m39s
10
11NAME DATA AGE
12configmap/index-html-v1 1 2m39s
13configmap/index-html-v2 1 9s
動作確認
curl
で動作確認してみると、数回に一度ぐらいの頻度でv2アプリからのレスポンスが返ってきます。
1❯ curl localhost:32207
2<!DOCTYPE html>
3<html lang="ja">
4<head>
5 <meta charset="UTF-8">
6</head>
7<body style="background-color: blue">
8 <h2>
9 v1 app.
10 </h2>
11</body>
12</html>
13
14❯ curl localhost:32207
15<!DOCTYPE html>
16<html lang="ja">
17<head>
18 <meta charset="UTF-8">
19</head>
20<body style="background-color: blue">
21 <h2>
22 v1 app.
23 </h2>
24</body>
25</html>
26
27❯ curl localhost:32207
28<!DOCTYPE html>
29<html lang="ja">
30<head>
31 <meta charset="UTF-8">
32</head>
33<body style="background-color: blue">
34 <h2>
35 v1 app.
36 </h2>
37</body>
38</html>
39
40❯ curl localhost:32207
41<!DOCTYPE html>
42<html lang="ja">
43<head>
44 <meta charset="UTF-8">
45</head>
46<body style="background-color: green">
47 <h2>
48 v2 app.
49 </h2>
50</body>
51</html>
この状況で新しいバージョンの動作を確認します。
バージョンの入れ替え
少ないレプリカ数で新しいバージョンの動作に自信が持てたら、レプリカ数を増やして、新しいバージョンのみにしていきます。
徐々に増やしていくこともできますが、今回は一気にレプリカ数を逆転させていきます。
1kubectl scale --replicas=3 deploy myapp-v2
2kubectl scale --replicas=0 deploy myapp-v1
新しいバージョンのみになりました。
1❯ kubectl get deploy,pod
2NAME READY UP-TO-DATE AVAILABLE AGE
3deployment.apps/myapp-v1 0/0 0 0 9m49s
4deployment.apps/myapp-v2 3/3 3 3 7m19s
5
6NAME READY STATUS RESTARTS AGE
7pod/myapp-v2-6d55d79f9f-2hg7q 1/1 Running 0 7m19s
8pod/myapp-v2-6d55d79f9f-blrv5 1/1 Running 0 64s
9pod/myapp-v2-6d55d79f9f-xp544 1/1 Running 0 64s
curl
で動作確認しても、v1からのレスポンスは返ってこなくなっています。
1❯ curl localhost:32207
2<!DOCTYPE html>
3<html lang="ja">
4<head>
5 <meta charset="UTF-8">
6</head>
7<body style="background-color: green">
8 <h2>
9 v2 app.
10 </h2>
11</body>
12</html>
13
14❯ curl localhost:32207
15<!DOCTYPE html>
16<html lang="ja">
17<head>
18 <meta charset="UTF-8">
19</head>
20<body style="background-color: green">
21 <h2>
22 v2 app.
23 </h2>
24</body>
25</html>%
v1アプリの削除
最後に、古いバージョン(v1)のアプリはもう必要ないので、削除して完了です。
1kubectl delete deploy myapp-v1