概要
このページでは、Podのローリングアップデートの方法について説明する。
ダウンタイムについても解説する。
目次
ローリングアップデートの方法
Deploymentを利用している場合、PodのDockerイメージのversionを上げてapplyするだけで、ローリングアップデートが実行される。
ハンズオン
Deploymentを定義
以下のようにDeploymentのDockerイメージのversionを上げる。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-dep
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: hello-dep
# このPodTemlateの部分に変更があったときに、ローリングアップデートが実行される
template:
metadata:
labels:
app: hello-dep
spec:
containers:
- - image: gcr.io/google-samples/hello-app:1.0
+ - image: gcr.io/google-samples/hello-app:2.0
imagePullPolicy: Always
name: hello-dep
ports:
- containerPort: 8080
ローリングアップデート前のPodの状態を確認
以下2つが現在稼働しているPodである。
$ sudo kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-dep-5ddf884dd9-nt22p 1/1 Running 0 16s
hello-dep-5ddf884dd9-wm6c9 1/1 Running 0 16s
Deploymentをapply
Dockerイメージのversionを更新したdeployment.ymlをapplyする。
$ sudo kubectl apply -f deployment.yml
ローリングアップデート開始
applyしたことにより、ローリングアップデートか開始される。
以下コマンドでPodの状態がリアルタイムで確認できる。
$ sudo kubectl get pod -w
NAME READY STATUS RESTARTS AGE
# 旧Pod
hello-dep-5ddf884dd9-nt22p 1/1 Running 0 20s
hello-dep-5ddf884dd9-wm6c9 1/1 Running 0 20s
# 新Pod1を起動開始
hello-dep-6595cc66c4-kcd8r 0/1 Pending 0 0s
hello-dep-6595cc66c4-kcd8r 0/1 Pending 0 0s
hello-dep-6595cc66c4-kcd8r 0/1 ContainerCreating 0 0s
# 新Pod1がRunning状態になった
hello-dep-6595cc66c4-kcd8r 1/1 Running 0 2s
# 旧Pod1を破棄開始
hello-dep-5ddf884dd9-nt22p 1/1 Terminating 0 30s
# 新Pod2を起動開始
hello-dep-6595cc66c4-wcc7q 0/1 Pending 0 0s
hello-dep-6595cc66c4-wcc7q 0/1 Pending 0 0s
hello-dep-6595cc66c4-wcc7q 0/1 ContainerCreating 0 0s
# 旧Pod2を破棄開始
hello-dep-5ddf884dd9-nt22p 0/1 Terminating 0 31s
# 新Pod2がRunning状態になった
hello-dep-6595cc66c4-wcc7q 1/1 Running 0 2s
# 旧Pod1,2が破棄される
hello-dep-5ddf884dd9-wm6c9 1/1 Terminating 0 32s
hello-dep-5ddf884dd9-nt22p 0/1 Terminating 0 32s
hello-dep-5ddf884dd9-nt22p 0/1 Terminating 0 32s
hello-dep-5ddf884dd9-wm6c9 0/1 Terminating 0 33s
hello-dep-5ddf884dd9-wm6c9 0/1 Terminating 0 34s
hello-dep-5ddf884dd9-wm6c9 0/1 Terminating 0 34s
ローリングアップデート後のPodの状態を確認
Podが新しいものに置き換わっており(NAMEが、アップデート前のものとは違う)Runningになっている。
$ sudo kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-dep-6595cc66c4-kcd8r 1/1 Running 0 19s
hello-dep-6595cc66c4-wcc7q 1/1 Running 0 17s
ローリングアップデートの手順としては、以上となる。
しかし、この方法ではダウンタイムが発生する可能性がある。
ダウンタイムについて
K8sは、旧versionのPodを破棄し、新versionのPodを起動するという形でローリングアップデートを実現するが、この間に微妙なダウンタイムが発生する可能性がある。
何故このような現象が起きるかというと、k8sは新versionのPodが起動したことまでは検知するが、Running状態になったことを検知しない。
つまり、新Podが起動した時点で旧Podはすぐに破棄されてしまうため、新PodがRunning状態になるまでの間、ダウンタイムが発生する可能性がある。
と、以下に書いてあった。
Enable Rolling updates in Kubernetes with Zero downtime
Once you apply this or edit this, notice that there maybe a little downtime on your application because the old pods are getting terminated and the new ones are getting created.
This happens because kubernetes doesn’t know when your new pod is ready to start accepting requests, so as soon as your new pod gets created, the old pod is terminated without waiting...
対策
ヘルスチェックを入れることで、ダウンタイムが発生しないようにする。
readinessProbe
は、サービスがリクエストを受け付けられる状態かどうかを確認する。
以下の説明が詳しい。
https://www.ianlewis.org/jp/kubernetes-health-check
readinessProbeが失敗した場合、そのパッドがサービスのエンドポイントから外される。そうすると、Kubernetesのサービスディスカバリー機能でトラフィックを受けられないポッドにトラフィックを転送しない。例えば、ローリングアップデートの時や、スケールアップした時、新しいポッドが機能されていたんだけど、まだトラフィック受けられないタイミングでリクエストが来たら、困るので、readinessProbeでそういうことを防ぐ。
また合わせて、RollingUpdate時の設定も追加した。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-dep
namespace: default
spec:
replicas: 2
+ # RollingUpdate時の設定
+ strategy:
+ # Recreate or RollingUpdate
+ type: RollingUpdate
+ rollingUpdate:
+ # アップデート時に許容する最大pod数 = replicas+maxSurge
+ # 今回の場合はreplicas(2)+maxSurge(1)なので最大Pod数が3になる。
+ maxSurge: 1
+ # アップデート時に許容する一度にunavailableになるPodの数
+ maxUnavailable: 25%
selector:
matchLabels:
app: hello-dep
# このPodTemlateの部分に変更があったときに、ローリングアップデートが実行される
template:
metadata:
labels:
app: hello-dep
spec:
containers:
- image: gcr.io/google-samples/hello-app:2.0
imagePullPolicy: Always
name: hello-dep
ports:
- containerPort: 8080
+ # ヘルスチェックの設定
+ readinessProbe:
+ # ヘルスチェックでアクセスするhttpエンドポイントの情報を書く。
+ httpGet:
+ path: /
+ port: 8080
+ # コンテナが起動してからヘルスチェックを始めるまでの秒数。Default: 0
+ initialDelaySeconds: 5
+ # ヘルスチェックのタイムアウト秒数。最小で1秒。Default: 1
+ timeoutSeconds: 1
+ # ヘルスチェックの間隔。最小で1秒。Default: 10
+ periodSeconds: 5
+ # Failure状態の時、何回成功したらSuccessになるか。最小で1回。Default: 1
+ successThreshold: 1
ローリングアップデート前のPodの状態を確認
$ sudo kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-dep-6ff6ff4cbf-j9pw9 1/1 Running 0 23s
hello-dep-6ff6ff4cbf-zgh5t 1/1 Running 0 23s
Deploymentをapply
$ sudo kubectl apply -f deployment.yml
deployment.apps/hello-dep created
ローリングアップデート開始
$ sudo kubectl get pod -w
NAME READY STATUS RESTARTS AGE
# 旧Pod
hello-dep-6ff6ff4cbf-j9pw9 1/1 Running 0 38s
hello-dep-6ff6ff4cbf-zgh5t 1/1 Running 0 38s
# 新Pod1を起動開始
hello-dep-bb849d7c6-j9dqs 0/1 Pending 0 0s
hello-dep-bb849d7c6-j9dqs 0/1 Pending 0 0s
hello-dep-bb849d7c6-j9dqs 0/1 ContainerCreating 0 0s
hello-dep-bb849d7c6-j9dqs 0/1 Running 0 2s
# 新Pod1がRunning状態 & READY1/1になった
hello-dep-bb849d7c6-j9dqs 1/1 Running 0 10s
# 旧Pod1を破棄開始
hello-dep-6ff6ff4cbf-zgh5t 1/1 Terminating 0 64s
# 新Pod2を起動開始
hello-dep-bb849d7c6-wsnhf 0/1 Pending 0 0s
hello-dep-bb849d7c6-wsnhf 0/1 Pending 0 0s
hello-dep-bb849d7c6-wsnhf 0/1 ContainerCreating 0 0s
# 旧Pod2を破棄開始
hello-dep-6ff6ff4cbf-zgh5t 0/1 Terminating 0 64s
hello-dep-6ff6ff4cbf-zgh5t 0/1 Terminating 0 65s
hello-dep-6ff6ff4cbf-zgh5t 0/1 Terminating 0 65s
hello-dep-bb849d7c6-wsnhf 0/1 Running 0 1s
# 新Pod2がRunning状態 & READY1/1になった
hello-dep-bb849d7c6-wsnhf 1/1 Running 0 7s
# 旧Pod1,2を破棄
hello-dep-6ff6ff4cbf-j9pw9 1/1 Terminating 0 71s
hello-dep-6ff6ff4cbf-j9pw9 0/1 Terminating 0 72s
hello-dep-6ff6ff4cbf-j9pw9 0/1 Terminating 0 73s
hello-dep-6ff6ff4cbf-j9pw9 0/1 Terminating 0 73s
ローリングアップデート後のPodの状態を確認
Podが新しいものに置き換わっており(NAMEが、アップデート前のものとは違う)Runningになっている。
$ sudo kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-dep-bb849d7c6-j9dqs 1/1 Running 0 25s
hello-dep-bb849d7c6-wsnhf 1/1 Running 0 15s