StatefulSetの基本

このチュートリアルでは、StatefulSetを使用したアプリケーションを管理するための基本を説明します。StatefulSetのPodを作成、削除、スケール、そして更新する方法について紹介します。

始める前に

このチュートリアルを始める前に、以下のKubernetesの概念について理解しておく必要があります。

目標

StatefulSetはステートフルアプリケーションや分散システムで使用するために存在します。しかし、Kubernetes上のステートフルアプリケーションや分散システムは、広範で複雑なトピックです。StatefulSetの基本的な機能を示すという目的のため、また、ステートフルアプリケーションを分散システムと混同しないようにするために、ここでは、Statefulsetを使用する単純なウェブアプリケーションのデプロイを行います。

このチュートリアルを終えると、以下のことが理解できるようになります。

  • StatefulSetの作成方法
  • StatefulSetがどのようにPodを管理するのか
  • StatefulSetの削除方法
  • StatefulSetのスケール方法
  • StatefulSetが管理するPodの更新方法

StatefulSetを作成する

はじめに、以下の例を使ってStatefulSetを作成しましょう。これは、コンセプトのStatefulSetのページで使ったものと同じような例です。nginxというheadless Serviceを作成し、webというStatefulSet内のPodのIPアドレスを公開します。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

上の例をダウンロードして、web.yamlという名前で保存します。

ここでは、ターミナルウィンドウを2つ使う必要があります。1つ目のターミナルでは、kubectl getを使って、StatefulSetのPodの作成を監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルでは、kubectl applyを使って、web.yamlに定義されたheadless ServiceとStatefulSetを作成します。

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

上のコマンドを実行すると、2つのPodが作成され、それぞれのPodでNGINXウェブサーバーが実行されます。nginxServiceを取得してみましょう。

kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    12s

そして、webStatefulSetを取得して、2つのリソースの作成が成功したことも確認します。

kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         20s

順序付きPodの作成

n 個のレプリカを持つStatefulSetは、Podをデプロイするとき、1つずつ順番に作成し、 {0..n-1} という順序付けを行います。1つ目のターミナルでkubectl getコマンドの出力を確認しましょう。最終的に、以下の例のような出力が表示されるはずです。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         19s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

web-0Podが Running (Pod Phaseを参照)かつ Ready (Pod Conditionstypeを参照)の状態になるまでは、web-1Podが起動していないことに注目してください。

StatefulSet内のPod

StatefulSet内のPodは、ユニークな順序インデックスと安定したネットワーク識別子を持ちます。

Podの順序インデックスを確かめる

StatefulSetのPodを取得します。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

StatefulSetのコンセプトで説明したように、StatefulSet内のPodは安定したユニークな識別子を持ちます。この識別子は、StatefulSetコントローラーによって各Podに割り当てられる、ユニークな順序インデックスに基づいて付けられます。Podの名前は、<statefulsetの名前>-<順序インデックス>という形式です。webStatefulSetは2つのレプリカを持つため、web-0web-1という2つのPodを作成します。

安定したネットワーク識別子の使用

各Podは、順序インデックスに基づいた安定したホスト名を持ちます。kubectl execを使用して、各Pod内でhostnameコマンドを実行してみましょう。

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

kubectl runを使用して、dnsutilsパッケージのnslookupコマンドを提供するコンテナを実行します。Podのホスト名に対してnslookupを実行すると、クラスター内のDNSアドレスが確認できます。

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

これにより、新しいシェルが起動します。新しいシェルで、次のコマンドを実行します。

# このコマンドは、dns-testコンテナのシェルで実行してください
nslookup web-0.nginx

出力は次のようになります。

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.6

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.6

(コンテナのシェルを終了するために、exitコマンドを実行してください。)

headless serviceのCNAMEは、SRVレコードを指しています(1つのレコードがRunningかつReadyのPodに対応します)。SRVレコードは、PodのIPアドレスを含むAレコードを指します。

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pod -w -l app=nginx

2つ目のターミナルで、kubectl deleteを使用して、StatefulSetのすべてのPodを削除します。

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

StatefulSetがPodを再起動して、2つのPodがRunningかつReadyの状態に移行するのを待ちます。

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

kubectl execkubectl runコマンドを使用して、Podのホスト名とクラスター内DNSエントリーを確認します。まず、Podのホスト名を見てみましょう。

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1

その後、次のコマンドを実行します。

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm /bin/sh

これにより、新しいシェルが起動します。新しいシェルで、次のコマンドを実行します。

# このコマンドは、dns-testコンテナのシェルで実行してください
nslookup web-0.nginx

出力は次のようになります。

Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.1.7

nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.2.8

(コンテナのシェルを終了するために、exitコマンドを実行してください。)

Podの順序インデックス、ホスト名、SRVレコード、そしてAレコード名は変化していませんが、Podに紐付けられたIPアドレスは変化する可能性があります。このチュートリアルで使用しているクラスターでは、IPアドレスは変わりました。このようなことがあるため、他のアプリケーションがStatefulSet内のPodに接続するときには、IPアドレスで指定しないことが重要です。

StatefulSetの有効なメンバーを探して接続する必要がある場合は、headless ServiceのCNAME(nginx.default.svc.cluster.local)をクエリしなければなりません。CNAMEに紐付けられたSRVレコードには、StatefulSet内のRunnningかつReadyなPodだけが含まれます。

アプリケーションがlivenessとreadinessをテストするコネクションのロジックをすでに実装している場合、PodのSRVレコード(web-0.nginx.default.svc.cluster.localweb-1.nginx.default.svc.cluster.local)をPodが安定しているものとして使用できます。PodがRunning and Readyな状態に移行すれば、アプリケーションはPodのアドレスを発見できるようになります。

安定したストレージへの書き込み

web-0およびweb-1のためのPersistentVolumeClaimを取得しましょう。

kubectl get pvc -l app=nginx

出力は次のようになります。

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

StatefulSetコントローラーは、2つのPersistentVolumeにバインドされた2つのPersistentVolumeClaimを作成しています。

このチュートリアルで使用しているクラスターでは、PersistentVolumeの動的なプロビジョニングが設定されているため、PersistentVolumeが自動的に作成されてバインドされています。

デフォルトでは、NGINXウェブサーバーは/usr/share/nginx/html/index.htmlに置かれたindexファイルを配信します。StatefulSetのspec内のvolumeMountsフィールドによって、/usr/share/nginx/htmlディレクトリがPersistentVolume上にあることが保証されます。

Podのホスト名をindex.htmlファイルに書き込むことで、NGINXウェブサーバーがホスト名を配信することを検証しましょう。

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)" > /usr/share/nginx/html/index.html'; done

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pod -w -l app=nginx

2つ目のターミナルで、StatefulSetのすべてのPodを削除します。

kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

1つ目のターミナルでkubectl getコマンドの出力を確認して、すべてのPodがRunningかつReadyの状態に変わるまで待ちます。

kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

ウェブサーバーがホスト名を配信し続けていることを確認します。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

もしweb-0およびweb-1が再スケジュールされたとしても、Podは同じホスト名を配信し続けます。これは、PodのPersistentVolumeClaimに紐付けられたPersistentVolumeが、PodのvolumeMountsに再マウントされるためです。web-0web-1がどんなノードにスケジュールされたとしても、PodのPersistentVolumeは適切なマウントポイントにマウントされます。

StatefulSetをスケールする

StatefulSetのスケールとは、レプリカ数を増減することを意味します。これは、replicasフィールドを更新することによって実現できます。StatefulSetのスケールには、kubectl scalekubectl patchのどちらも使用できます。

スケールアップ

1つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、kubectl scaleを使って、レプリカ数を5にスケールします。

kubectl scale sts web --replicas=5
statefulset.apps/web scaled

1つ目のターミナルのkubectl getコマンドの出力を確認して、3つの追加のPodがRunningかつReadyの状態に変わるまで待ちます。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2h
web-1     1/1       Running   0          2h
NAME      READY     STATUS    RESTARTS   AGE
web-2     0/1       Pending   0          0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       ContainerCreating   0         0s
web-3     1/1       Running   0         18s
web-4     0/1       Pending   0         0s
web-4     0/1       Pending   0         0s
web-4     0/1       ContainerCreating   0         0s
web-4     1/1       Running   0         19s

StatefulSetコントローラーはレプリカ数をスケールします。 StatefulSetを作成するで説明したように、StatefulSetコントローラーは各Podを順序インデックスに従って1つずつ作成し、次のPodを起動する前に、1つ前のPodがRunningかつReadyの状態になるまで待ちます。

スケールダウン

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、kubectl patchコマンドを使用して、StatefulSetを3つのレプリカにスケールダウンします。

kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

web-4およびweb-3がTerminatingの状態になるまで待ちます。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3h
web-1     1/1       Running             0          3h
web-2     1/1       Running             0          55s
web-3     1/1       Running             0          36s
web-4     0/1       ContainerCreating   0          18s
NAME      READY     STATUS    RESTARTS   AGE
web-4     1/1       Running   0          19s
web-4     1/1       Terminating   0         24s
web-4     1/1       Terminating   0         24s
web-3     1/1       Terminating   0         42s
web-3     1/1       Terminating   0         42s

順序付きPodを削除する

コントローラーは、順序インデックスの逆順に1度に1つのPodを削除し、次のPodを削除する前には、各Podが完全にシャットダウンするまで待機しています。

StatefulSetのPersistentVolumeClaimを取得しましょう。

kubectl get pvc -l app=nginx
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-2   Bound     pvc-e1125b27-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-3   Bound     pvc-e1176df6-b508-11e6-932f-42010a800002   1Gi        RWO           13h
www-web-4   Bound     pvc-e11bb5f8-b508-11e6-932f-42010a800002   1Gi        RWO           13h

まだ、5つのPersistentVolumeClaimと5つのPersistentVolumeが残っています。安定したストレージへの書き込みを読むと、StatefulSetのPodが削除されても、StatefulSetのPodにマウントされたPersistentVolumeは削除されないと書かれています。このことは、StatefulSetのスケールダウンによってPodが削除された場合にも当てはまります。

StatefulSetsを更新する

Kubernetes 1.7以降では、StatefulSetコントローラーは自動アップデートをサポートしています。使われる戦略は、StatefulSet APIオブジェクトのspec.updateStrategyフィールドによって決まります。この機能はコンテナイメージのアップグレード、リソースのrequestsやlimits、ラベル、StatefulSet内のPodのアノテーションの更新時に利用できます。有効なアップデートの戦略は、RollingUpdateOnDeleteの2種類です。

RollingUpdateは、StatefulSetのデフォルトのアップデート戦略です。

RollingUpdate

RollingUpdateアップデート戦略は、StatefulSetの保証を尊重しながら、順序インデックスの逆順にStatefulSet内のすべてのPodをアップデートします。

webStatefulSetにpatchを当てて、RollingUpdateアップデート戦略を適用しましょう。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
statefulset.apps/web patched

1つ目のターミナルで、webStatefulSetに再度patchを当てて、コンテナイメージを変更します。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.8"}]'
statefulset.apps/web patched

2つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pod -l app=nginx -w

出力は次のようになります。

NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          7m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          8m
web-2     1/1       Terminating   0         8m
web-2     1/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Terminating   0         8m
web-2     0/1       Pending   0         0s
web-2     0/1       Pending   0         0s
web-2     0/1       ContainerCreating   0         0s
web-2     1/1       Running   0         19s
web-1     1/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Terminating   0         8m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         6s
web-0     1/1       Terminating   0         7m
web-0     1/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Terminating   0         7m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s

StatefulSet内のPodは、順序インデックスの逆順に更新されました。StatefulSetコントローラーは各Podを終了させ、次のPodを更新する前に、新しいPodがRunningかつReadyの状態に変わるまで待機します。ここで、StatefulSetコントローラーは順序インデックスの前のPodがRunningかつReadyの状態になるまで次のPodの更新を始めず、現在の状態へのアップデートに失敗したPodがあった場合、そのPodをリストアすることに注意してください。

すでにアップデートを受け取ったPodは、アップデートされたバージョンにリストアされます。まだアップデートを受け取っていないPodは、前のバージョンにリストアされます。このような方法により、もし途中で失敗が起こっても、コントローラはアプリケーションが健全な状態を保ち続けられるようにし、更新が一貫したものになるようにします。

Podを取得して、コンテナイメージを確認してみましょう。

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
k8s.gcr.io/nginx-slim:0.8
k8s.gcr.io/nginx-slim:0.8
k8s.gcr.io/nginx-slim:0.8

現在、StatefulSet内のすべてのPodは、前のコンテナイメージを実行しています。

ステージングアップデート

RollingUpdateアップデート戦略にpartitionパラメーターを使用すると、StatefulSetへのアップデートをステージングすることができます。ステージングアップデートを利用すれば、StatefulSet内のすべてのPodを現在のバージョンにしたまま、StatefulSetの.spec.templateを変更することが可能になります。

webStatefulSetにpatchを当てて、updateStrategyフィールドにpartitionを追加しましょう。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
statefulset.apps/web patched

StatefulSetに再度patchを当てて、コンテナイメージを変更します。

kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"k8s.gcr.io/nginx-slim:0.7"}]'
statefulset.apps/web patched

StatefulSet内のPodを削除します。

kubectl delete pod web-2
pod "web-2" deleted

PodがRunningかつReadyになるまで待ちます。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

Podのコンテナイメージを取得します。

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
k8s.gcr.io/nginx-slim:0.8

アップデート戦略がRollingUpdateであっても、StatefulSetが元のコンテナを持つPodをリストアしたことがわかります。これは、Podの順序インデックスがupdateStrategyで指定したpartitionより小さいためです。

カナリア版をロールアウトする

ステージングアップデートのときに指定したpartitionを小さくすることで、変更をテストするためのカナリア版をロールアウトできます。

StatefulSetにpatchを当てて、partitionを小さくします。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
statefulset.apps/web patched

web-2がRunningかつReadyの状態になるまで待ちます。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          4m
web-1     1/1       Running             0          4m
web-2     0/1       ContainerCreating   0          11s
web-2     1/1       Running   0         18s

Podのコンテナを取得します。

kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
k8s.gcr.io/nginx-slim:0.7

partitionを変更すると、StatefulSetコントローラーはPodを自動的に更新します。Podの順序インデックスがpartition以上の値であるためです。

web-1Podを削除します。

kubectl delete pod web-1
pod "web-1" deleted

web-1PodがRunningかつReadyになるまで待ちます。

kubectl get pod -l app=nginx -w

出力は次のようになります。

NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Running       0          6m
web-1     0/1       Terminating   0          6m
web-2     1/1       Running       0          2m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Terminating   0         6m
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         18s

web-1Podのコンテナイメージを取得します。

kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
k8s.gcr.io/nginx-slim:0.8

Podの順序インデックスがpartitionよりも小さいため、web-1は元の設定のコンテナイメージにリストアされました。partitionを指定すると、StatefulSetの.spec.templateが更新されたときに、順序インデックスがそれ以上の値を持つすべてのPodがアップデートされます。partitionよりも小さな順序インデックスを持つPodが削除されたり終了されたりすると、元の設定のPodにリストアされます。

フェーズロールアウト

カナリア版をロールアウトするのと同じような方法でパーティションされたローリングアップデートを使用すると、フェーズロールアウト(例: 線形、幾何級数的、指数関数的ロールアウト)を実行できます。フェーズロールアウトを実行するには、コントローラーがアップデートを途中で止めてほしい順序インデックスをpartitionに設定します。

現在、partitionは2に設定されています。partitionを0に設定します。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
statefulset.apps/web patched

StatefulSet内のすべてのPodがRunningかつReadyの状態になるまで待ちます。

kubectl get pod -l app=nginx -w

出力は次のようになります。

NAME      READY     STATUS              RESTARTS   AGE
web-0     1/1       Running             0          3m
web-1     0/1       ContainerCreating   0          11s
web-2     1/1       Running             0          2m
web-1     1/1       Running   0         18s
web-0     1/1       Terminating   0         3m
web-0     1/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Terminating   0         3m
web-0     0/1       Pending   0         0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         3s

StatefulSet内のPodのコンテナイメージの詳細を取得します。

for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
k8s.gcr.io/nginx-slim:0.7
k8s.gcr.io/nginx-slim:0.7
k8s.gcr.io/nginx-slim:0.7

partition0に移動することで、StatefulSetがアップデート処理を続けられるようにできます。

OnDelete

OnDeleteアップデート戦略は、(1.6以前の)レガシーな動作を実装しています。このアップデート戦略を選択すると、StatefulSetの.spec.templateフィールドへ変更を加えても、StatefulSetコントローラーが自動的にPodを更新しなくなります。この戦略を選択するには、.spec.template.updateStrategy.typeOnDeleteを設定します。

StatefulSetを削除する

StatefulSetは、非カスケードな削除とカスケードな削除の両方をサポートしています。非カスケードな削除では、StatefulSetが削除されても、StatefulSet内のPodは削除されません。カスケードな削除では、StatefulSetとPodが一緒に削除されます。

非カスケードな削除

1つ目のターミナルで、StatefulSet内のPodを監視します

kubectl get pods -w -l app=nginx

kubectl deleteを使用して、StatefulSetを削除します。このとき、--cascade=orphanパラメーターをコマンドに与えてください。このパラメーターは、Kubernetesに対して、StatefulSetだけを削除して配下のPodは削除しないように指示します。

kubectl delete statefulset web --cascade=orphan
statefulset.apps "web" deleted

Podを取得して、ステータスを確認します。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          6m
web-1     1/1       Running   0          7m
web-2     1/1       Running   0          5m

webが削除されても、すべてのPodはまだRunningかつReadyの状態のままです。web-0を削除します。

kubectl delete pod web-0
pod "web-0" deleted

StatefulSetのPodを取得します。

kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          10m
web-2     1/1       Running   0          7m

webStatefulSetはすでに削除されているため、web-0は再起動しません。

1つ目のターミナルで、StatefulSetのPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、StatefulSetを再作成します。もしnginxServiceを削除しなかった場合(この場合は削除するべきではありませんでした)、Serviceがすでに存在することを示すエラーが表示されます。

kubectl apply -f web.yaml
statefulset.apps/web created
service/nginx unchanged

このエラーは無視してください。このメッセージは、すでに存在する nginx というheadless Serviceを作成しようと試みたということを示しているだけです。

1つ目のターミナルで、kubectl getコマンドの出力を確認します。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-1     1/1       Running   0          16m
web-2     1/1       Running   0          2m
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         18s
web-2     1/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m
web-2     0/1       Terminating   0         3m

webStatefulSetが再作成されると、最初にweb-0を再実行します。web-1はすでにRunningかつReadyの状態であるため、web-0がRunningかつReadyの状態に移行すると、StatefulSetは単純にこのPodを選びます。StatefulSetをreplicasを2にして再作成したため、一度web-0が再作成されて、web-1がすでにRunningかつReadyの状態であることが判明したら、web-2は停止されます。

Podのウェブサーバーが配信しているindex.htmlファイルのコンテンツをもう一度見てみましょう。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

たとえStatefulSetとweb-0Podの両方が削除されても、Podは最初にindex.htmlファイルに書き込んだホスト名をまだ配信しています。これは、StatefulSetがPodに紐付けられたPersistentVolumeを削除しないためです。StatefulSetを再作成してweb-0を再実行すると、元のPersistentVolumeが再マウントされます。

カスケードな削除

1つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pods -w -l app=nginx

2つ目のターミナルで、StatefulSetをもう一度削除します。今回は、--cascade=orphanパラメーターを省略します。

kubectl delete statefulset web
statefulset.apps "web" deleted

1つ目のターミナルで実行しているkubectl getコマンドの出力を確認し、すべてのPodがTerminatingの状態に変わるまで待ちます。

kubectl get pods -w -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          11m
web-1     1/1       Running   0          27m
NAME      READY     STATUS        RESTARTS   AGE
web-0     1/1       Terminating   0          12m
web-1     1/1       Terminating   0         29m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-0     0/1       Terminating   0         12m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m
web-1     0/1       Terminating   0         29m

スケールダウンのセクションで見たように、順序インデックスの逆順に従って、Podは一度に1つずつ終了します。StatefulSetコントローラーは、次のPodを終了する前に、前のPodが完全に終了するまで待ちます。

kubectl delete service nginx
service "nginx" deleted

さらにもう一度、StatefulSetとheadless Serviceを再作成します。

kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created

StatefulSet上のすべてのPodがRunningかつReadyの状態に変わったら、Pod上のindex.htmlファイルのコンテンツを取得します。

for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

StatefulSetを完全に削除して、すべてのPodが削除されたとしても、PersistentVolumeがマウントされたPodが再生成されて、web-0web-1はホスト名の配信を続けます。

最後に、webStatefulSetを削除します。

kubectl delete service nginx
service "nginx" deleted

そして、nginxServiceも削除します。

kubectl delete statefulset web
statefulset "web" deleted

Pod管理ポリシー

分散システムによっては、StatefulSetの順序の保証が不必要であったり望ましくない場合もあります。こうしたシステムでは、一意性と同一性だけが求められます。この問題に対処するために、Kubernetes 1.7でStatefulSet APIオブジェクトに.spec.podManagementPolicyが導入されました。

OrderedReadyのPod管理

OrderedReadyのPod管理はStatefulSetのデフォルトの設定です。StatefulSetコントローラーに対して、これまでに紹介したような順序の保証を尊重するように指示します。

ParallelのPod管理

ParallelのPod管理では、StatefulSetコントローラーに対して、PodがRunningかつReadyの状態や完全に停止するまで待たないように指示し、すべてのPodを並列に起動または停止させるようにします。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

上の例をダウンロードして、web-parallel.yamlという名前でファイルに保存してください。

このマニフェストは、.spec.podManagementPolicyParallelに設定されている以外は、前にダウンロードしたwebStatefulSetと同一です。

1つ目のターミナルで、StatefulSet内のPodを監視します。

kubectl get pod -l app=nginx -w

2つ目のターミナルで、マニフェスト内のStatefulSetとServiceを作成します。

kubectl apply -f web-parallel.yaml
service/nginx created
statefulset.apps/web created

1つ目のターミナルで実行したkubectl getコマンドの出力を確認します。

kubectl get pod -l app=nginx -w
NAME      READY     STATUS    RESTARTS   AGE
web-0     0/1       Pending   0          0s
web-0     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-0     0/1       ContainerCreating   0         0s
web-1     0/1       ContainerCreating   0         0s
web-0     1/1       Running   0         10s
web-1     1/1       Running   0         10s

StatefulSetコントローラーはweb-0web-1を同時に起動しています。

2つ目のターミナルで、StatefulSetをスケールしてみます。

kubectl scale statefulset/web --replicas=4
statefulset.apps/web scaled

kubectl getコマンドを実行しているターミナルの出力を確認します。

web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         0s
web-3     0/1       Pending   0         7s
web-3     0/1       ContainerCreating   0         7s
web-2     1/1       Running   0         10s
web-3     1/1       Running   0         26s

StatefulSetが2つのPodを実行し、1つ目のPodがRunningかつReadyの状態になるのを待たずに2つ目のPodを実行しているのがわかります。

クリーンアップ

2つのターミナルが開かれているはずなので、クリーンアップの一部としてkubectlコマンドを実行する準備ができています。

kubectl delete sts web
# stsは、statefulsetの略です。

kubectl getを監視すると、Podが削除されていく様子を確認できます。

kubectl get pod -l app=nginx -w
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-3     1/1       Terminating   0         9m
web-2     1/1       Terminating   0         9m
web-1     1/1       Terminating   0         44m
web-0     1/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-2     0/1       Terminating   0         9m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-1     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-0     0/1       Terminating   0         44m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m
web-3     0/1       Terminating   0         9m

削除の間、StatefulSetはすべてのPodを並列に削除し、順序インデックスが1つ前のPodが停止するのを待つことはありません。

kubectl getコマンドを実行しているターミナルを閉じて、nginxServiceを削除します。

kubectl delete svc nginx
最終更新 August 31, 2021 at 12:15 AM PST : [ja] Updating --cascade=false to --cascade=orphan (3d3db5a49)