はじめに
Kubernetes 1.7でStatefulSetのRolling Updateがbetaとして機能が入りました。
PodやNodeのライフサイクルに影響されずにPodから出たファイルのログなどをFluentdなどで収集されるまで置いておきたい用途があり、PersistentVolumeを使うためStatefulSetでvolumeClaimTemplatesを利用しようとしていました。ただ、この問題のためか、kubectl apply
によるimage更新などがうまくいってませんでした。
今回手元のユースケースで、image更新によるStatefulsetの kubectl apply
が1.6までだと成功しなかった更新が成功することを確認しました。
準備
Kubernetes Cluster
なにはともあれ、kubernetes 1.7 の環境を準備します。今回は GKE で試しています。主題ではないので詳細は省略します。
StatefulSet
StatefulSetも続いて準備します。簡単にnginxのimageを動かすことにします。
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: nginx-stateful
spec:
replicas: 3
serviceName: nginx
selector:
matchLabels:
app: nginx
updateStrategy:
type: RollingUpdate
rollingUpdate:
podManagementPolicy: Parallel
template:
spec:
containers:
- name: nginx
image: nginx:1.12.1-alpine
imagePullPolicy: Always
ports:
- containerPort: 80
hostPort: 80
volumeMounts:
- mountPath: "/var/log/nginx"
name: log
lifecycle:
preStop:
exec:
command: ["kill", "-QUIT", "1"]
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
metadata:
name: nginx
labels:
app: nginx
volumeClaimTemplates:
- metadata:
name: log
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 100Gi
updateStrategy
の部分が1.7で追加された記述です。ここにtype: RollingUpdate
と書くことでRolling Updateができるようになります。また、partition
というパラメータも追加されました。指定することで一部Podから入れ替えたりでカナリアリリースなどで利用できるようです。詳細はこちらをご覧ください。
今回/var/log/nginx
が真っ新なDiskに付け変わるので、元のimageにある/dev/stdout
や/dev/stderr
へのsymlinkは無くなります。また、ログ出力時にディレクトリが存在しないと問題になる場合は、initContainers
の処理などでディレクトリを作成しておく必要があります。
PersistentVolumeは、Dynamic Provisioningに任せることにして今回明示的には作成してません。pd-standard
で作成されるため、pd-ssd
が必要であれば、StorageClassなどの設定が必要です。また、Dynamic Provisioningに頼らない場合にはPersistentDiskの作成も必要です。Dynamic Provisioningで作成されたPersistentDiskは、PersistentVolumeClaimが消えるとPersistentDiskも消されるので注意が必要です。手元だとStatefulSetを消しても、PersistentVolumeClaimは消えなかったので明示的に消さない限りは大丈夫かも知れません。このあたりは別途調べてみたいと思います。
実行
StatefulSetを作成してみましょう。kubectl apply
で作成します。
$ kubectl apply -f nginx-1.12.1-statefulset.yml
作成された内容も確認してみます。
$ kubectl get statefulset
NAME DESIRED CURRENT AGE
nginx-stateful 3 3 31s
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE
log-nginx-stateful-0 Bound pvc-9d21a174-700d-11e7-9d7f-42010a92004d 100Gi RWO standard 48s
log-nginx-stateful-1 Bound pvc-9d28003b-700d-11e7-9d7f-42010a92004d 100Gi RWO standard 48s
log-nginx-stateful-2 Bound pvc-9d2c1e8d-700d-11e7-9d7f-42010a92004d 100Gi RWO standard 48s
$ kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-9d21a174-700d-11e7-9d7f-42010a92004d 100Gi RWO Delete Bound default/log-nginx-stateful-0 standard 57s
pvc-9d28003b-700d-11e7-9d7f-42010a92004d 100Gi RWO Delete Bound default/log-nginx-stateful-1 standard 57s
pvc-9d2c1e8d-700d-11e7-9d7f-42010a92004d 100Gi RWO Delete Bound default/log-nginx-stateful-2 standard 57s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-stateful-0 1/1 Running 0 1m
nginx-stateful-1 1/1 Running 0 1m
nginx-stateful-2 1/1 Running 0 1m
アクセスログも確認しておきます
$ kubectl exec nginx-stateful-0 head /var/log/nginx/access.log
10.44.4.1 - - [24/Jul/2017:01:16:40 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:16:40 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:16:50 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:16:50 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:00 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:00 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:10 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:10 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:20 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:20 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
image更新
続いてimageの更新をします。nginxのimageを1.13.3-alpineにしてみます。
$ kubectl patch statefulset nginx-stateful -p'{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:1.13.3-alpine"}]}}}}'
statefulset "nginx-stateful" patched
もしくは、上記ymlのimageの部分を書き換えた下記ymlを流します。
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: nginx-stateful
spec:
replicas: 3
serviceName: nginx
selector:
matchLabels:
app: nginx
updateStrategy:
type: RollingUpdate
rollingUpdate:
podManagementPolicy: Parallel
template:
spec:
containers:
- name: nginx
image: nginx:1.13.3-alpine
imagePullPolicy: Always
ports:
- containerPort: 80
hostPort: 80
volumeMounts:
- mountPath: "/var/log/nginx"
name: log
lifecycle:
preStop:
exec:
command: ["kill", "-QUIT", "1"]
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
metadata:
name: nginx
labels:
app: nginx
volumeClaimTemplates:
- metadata:
name: log
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 100Gi
$ kubectl apply -f nginx-1.13.3-statefulset.yml
statefulset "nginx-stateful" configured
$ kubectl rollout status statefulset/nginx-stateful
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 1 pods at revision nginx-stateful-46265502...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
waiting for statefulset rolling update to complete 2 pods at revision nginx-stateful-46265502...
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
statefulset rolling update complete 3 pods at revision nginx-stateful-46265502...
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-stateful-0 1/1 Running 0 39s
nginx-stateful-1 1/1 Running 0 1m
nginx-stateful-2 1/1 Running 0 1m
1podずつ更新されていることが確認できました。
アクセスログも残っていることを確認してみます。
$ kubectl exec nginx-stateful-0 head /var/log/nginx/access.log
10.44.4.1 - - [24/Jul/2017:01:16:40 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:16:40 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:16:50 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:16:50 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:00 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:00 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:10 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:10 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:20 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
10.44.4.1 - - [24/Jul/2017:01:17:20 +0000] "GET / HTTP/1.1" 200 612 "-" "Go-http-client/1.1" "-"
Pod更新前のログが残っていますね。
まとめ
Kubetnetes 1.7でStatefulSetのRolling Updateができました。kubectl apply
によるStatefulSetの更新もできるようになり、CI/CDの中でも使いやすくなったのでないかと思います。