これは、このセクションの複数ページの印刷可能なビューです。 印刷するには、ここをクリックしてください.
ステートフルアプリケーション
1 - StatefulSetの基本
このチュートリアルでは、StatefulSetを使用したアプリケーションを管理するための基本を説明します。StatefulSetのPodを作成、削除、スケール、そして更新する方法について紹介します。
始める前に
このチュートリアルを始める前に、以下のKubernetesの概念について理解しておく必要があります。
備考: このチュートリアルでは、クラスターがPersistentVolumeの動的なプロビジョニングが行われるように設定されていることを前提としています。クラスターがそのように設定されていない場合、チュートリアルを始める前に1GiBのボリュームを2つ手動でプロビジョニングする必要があります。
目標
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ウェブサーバーが実行されます。nginx
Serviceを取得してみましょう。
kubectl get service nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 12s
そして、web
StatefulSetを取得して、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-0
Podが Running (Pod Phaseを参照)かつ Ready (Pod Conditionsのtype
を参照)の状態になるまでは、web-1
Podが起動していないことに注目してください。
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の名前>-<順序インデックス>
という形式です。web
StatefulSetは2つのレプリカを持つため、web-0
とweb-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 exec
とkubectl 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.local
、web-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
備考:上記のcurlコマンドに対して代わりに403 Forbiddenというレスポンスが返ってくる場合、
volumeMounts
でマウントしたディレクトリのパーミッションを修正する必要があります(これは、hostPathボリュームを使用したときに起こるバグが原因です)。この問題に対処するには、上のcurl
コマンドを再実行する前に、次のコマンドを実行します。
for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
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-0
とweb-1
がどんなノードにスケジュールされたとしても、PodのPersistentVolumeは適切なマウントポイントにマウントされます。
StatefulSetをスケールする
StatefulSetのスケールとは、レプリカ数を増減することを意味します。これは、replicas
フィールドを更新することによって実現できます。StatefulSetのスケールには、kubectl scale
と
kubectl 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のアノテーションの更新時に利用できます。有効なアップデートの戦略は、RollingUpdate
とOnDelete
の2種類です。
RollingUpdate
は、StatefulSetのデフォルトのアップデート戦略です。
RollingUpdate
RollingUpdate
アップデート戦略は、StatefulSetの保証を尊重しながら、順序インデックスの逆順にStatefulSet内のすべてのPodをアップデートします。
web
StatefulSetにpatchを当てて、RollingUpdate
アップデート戦略を適用しましょう。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
statefulset.apps/web patched
1つ目のターミナルで、web
StatefulSetに再度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は、前のコンテナイメージを実行しています。
備考:kubectl rollout status sts/<name>
を使って、StatefulSetへのローリングアップデートの状態を確認することもできます。
ステージングアップデート
RollingUpdate
アップデート戦略にpartition
パラメーターを使用すると、StatefulSetへのアップデートをステージングすることができます。ステージングアップデートを利用すれば、StatefulSet内のすべてのPodを現在のバージョンにしたまま、StatefulSetの.spec.template
を変更することが可能になります。
web
StatefulSetに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-1
Podを削除します。
kubectl delete pod web-1
pod "web-1" deleted
web-1
Podが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-1
Podのコンテナイメージを取得します。
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
partition
を0
に移動することで、StatefulSetがアップデート処理を続けられるようにできます。
OnDelete
OnDelete
アップデート戦略は、(1.6以前の)レガシーな動作を実装しています。このアップデート戦略を選択すると、StatefulSetの.spec.template
フィールドへ変更を加えても、StatefulSetコントローラーが自動的にPodを更新しなくなります。この戦略を選択するには、.spec.template.updateStrategy.type
にOnDelete
を設定します。
StatefulSetを削除する
StatefulSetは、非カスケードな削除とカスケードな削除の両方をサポートしています。非カスケードな削除では、StatefulSetが削除されても、StatefulSet内のPodは削除されません。カスケードな削除では、StatefulSetとPodが一緒に削除されます。
非カスケードな削除
1つ目のターミナルで、StatefulSet内のPodを監視します
kubectl get pods -w -l app=nginx
kubectl delete
を使用して、StatefulSetを削除します。このとき、--cascade=false
パラメーターをコマンドに与えてください。このパラメーターは、Kubernetesに対して、StatefulSetだけを削除して配下のPodは削除しないように指示します。
kubectl delete statefulset web --cascade=false
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
web
StatefulSetはすでに削除されているため、web-0
は再起動しません。
1つ目のターミナルで、StatefulSetのPodを監視します。
kubectl get pods -w -l app=nginx
2つ目のターミナルで、StatefulSetを再作成します。もしnginx
Serviceを削除しなかった場合(この場合は削除するべきではありませんでした)、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
web
StatefulSetが再作成されると、最初に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-0
Podの両方が削除されても、Podは最初にindex.html
ファイルに書き込んだホスト名をまだ配信しています。これは、StatefulSetがPodに紐付けられたPersistentVolumeを削除しないためです。StatefulSetを再作成してweb-0
を再実行すると、元のPersistentVolumeが再マウントされます。
カスケードな削除
1つ目のターミナルで、StatefulSet内のPodを監視します。
kubectl get pods -w -l app=nginx
2つ目のターミナルで、StatefulSetをもう一度削除します。今回は、--cascade=false
パラメーターを省略します。
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が完全に終了するまで待ちます。
備考: カスケードな削除ではStatefulSetがPodとともに削除されますが、StatefulSetと紐付けられたheadless Serviceは削除されません。そのため、nginx
Serviceは手動で削除する必要があります。
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-0
とweb-1
はホスト名の配信を続けます。
最後に、web
StatefulSetを削除します。
kubectl delete service nginx
service "nginx" deleted
そして、nginx
Serviceも削除します。
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.podManagementPolicy
がParallel
に設定されている以外は、前にダウンロードしたweb
StatefulSetと同一です。
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-0
とweb-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
コマンドを実行しているターミナルを閉じて、nginx
Serviceを削除します。
kubectl delete svc nginx
備考:このチュートリアルで使用したPersistentVolumeのための永続ストレージも削除する必要があります。
すべてのストレージが再利用できるようにするために、環境、ストレージの設定、プロビジョニング方法に基づいて必要な手順に従ってください。
2 - 例: Persistent Volumeを使用したWordpressとMySQLをデプロイする
このチュートリアルでは、WordPressのサイトとMySQLデータベースをMinikubeを使ってデプロイする方法を紹介します。2つのアプリケーションとも、データを保存するためにPersistentVolumeとPersistentVolumeClaimを使用します。
PersistentVolume(PV)とは、管理者が手動でプロビジョニングを行うか、StorageClassを使ってKubernetesによって動的にプロビジョニングされた、クラスター内のストレージの一部です。PersistentVolumeClaim(PVC)は、PVによって満たすことができる、ユーザーによるストレージへのリクエストのことです。PersistentVolumeとPersistentVolumeClaimは、Podのライフサイクルからは独立していて、Podの再起動、Podの再スケジューリング、さらにはPodの削除が行われたとしても、その中のデータは削除されずに残ります。
警告: シングルインスタンスのWordPressとMySQLのPodを使用しているため、ここで行うデプロイは本番のユースケースには適しません。WordPressを本番環境にデプロイするときは、WordPress Helm Chartを使用することを検討してください。
備考: このチュートリアルで提供されるファイルは、GAとなっているDeployment APIを使用しているため、Kubernetesバージョン1.9以降のためのものになっています。もしこのチュートリアルを古いバージョンのKubernetesで使いたい場合は、APIのバージョンを適切にアップデートするか、このチュートリアルの古いバージョンを参照してください。
目標
- PersistentVolumeClaimとPersistentVolumeを作成する
- 以下を含む
kustomization.yaml
を作成する- Secret generator
- MySQLリソースの設定
- WordPressリソースの設定
- kustomizationディレクトリを
kubectl apply -k ./
で適用する - クリーンアップする
始める前に
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:
バージョンを確認するには次のコマンドを実行してください:kubectl version
.
このページで示された例は、kubectl
1.14以降で動作します。以下の設定ファイルをダウンロードします。
PersistentVolumeClaimとPersistentVolumeを作成する
MySQLとWordpressはそれぞれ、データを保存するためのPersistentVolumeを必要とします。各PersistentVolumeClaimはデプロイの段階で作成されます。
多くのクラスタ環境では、デフォルトのStorageClassがインストールされています。StorageClassがPersistentVolumeClaim中で指定されていなかった場合、クラスターのデフォルトのStorageClassが代わりに使われます。
PersistentVolumeClaimが作成されるとき、StorageClassの設定に基づいてPersistentVolumeが動的にプロビジョニングされます。
警告: ローカルのクラスターでは、デフォルトのStorageClassにはhostPath
プロビジョナーが使われます。hostPath
ボリュームは開発およびテストにのみ適しています。hostPath
ボリュームでは、データはPodがスケジュールされたノード上の/tmp
内に保存されます。そのため、もしPodが死んだり、クラスター上の他のノードにスケジュールされたり、ノードが再起動すると、データは失われます。
備考:hostPath
プロビジョナーを使用する必要があるクラスターを立ち上げたい場合は、--enable-hostpath-provisioner
フラグをcontroller-manager
コンポーネントで設定する必要があります。
備考: Google Kubernetes Engine上で動作するKubernetesクラスターを使っている場合は、このガイドに従ってください。
kustomization.yamlを作成する
Secret generatorを追加する
Secretとは、パスワードやキーのような機密性の高いデータ片を保存するためのオブジェクトです。バージョン1.14からは、kubectl
がkustomizationファイルを使用したKubernetesオブジェクトの管理をサポートしています。kustomization.yaml
内のgeneratorによってSecretを作成することができます。
以下のコマンドを実行して、kustomization.yaml
の中にSecret generatorを追加します。YOUR_PASSWORD
の部分を使いたいパスワードに置換してください。
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
literals:
- password=YOUR_PASSWORD
EOF
MySQLとWordPressのためのリソースの設定を追加する
以下のマニフェストには、シングルインスタンスのMySQLのDeploymentが書かれています。MySQLコンテナはPersistentVolumeを/var/lib/mysql
にマウントします。MYSQL_ROOT_PASSWORD
環境変数には、Secretから得られたデータベースのパスワードが設定されます。
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
以下のマニフェストには、シングルインスタンスのWordPressのDeploymentが書かれています。WordPressコンテナはPersistentVolumeをウェブサイトのデータファイルのために/var/www/html
にマウントします。WORDPRESS_DB_HOST
環境変数に上で定義したMySQLのServiceの名前を設定すると、WordPressはServiceによってデータベースにアクセスします。WORDPRESS_DB_PASSWORD
環境変数には、kustomizeが生成したSecretから得たデータベースのパスワードが設定されます。
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
MySQLのDeploymentの設定ファイルをダウンロードします。
curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
WordPressの設定ファイルをダウンロードします。
curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
これらを
kustomization.yaml
ファイルに追加します。
cat <<EOF >>./kustomization.yaml
resources:
- mysql-deployment.yaml
- wordpress-deployment.yaml
EOF
適用と確認
kustomization.yaml
には、WordPressのサイトとMySQLデータベースのためのすべてのリソースが含まれています。次のコマンドでこのディレクトリを適用できます。
kubectl apply -k ./
これで、すべてのオブジェクトが存在していることを確認できます。
次のコマンドを実行して、Secretが存在していることを確認します。
kubectl get secrets
結果は次のようになるはずです。
NAME TYPE DATA AGE mysql-pass-c57bb4t7mf Opaque 1 9s
次のコマンドを実行して、PersistentVolumeが動的にプロビジョニングされていることを確認します。
kubectl get pvc
備考: PVがプロビジョニングされてバインドされるまでに、最大で数分かかる場合があります。結果は次のようになるはずです。
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mysql-pv-claim Bound pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002 20Gi RWO standard 77s wp-pv-claim Bound pvc-8cd0df54-4044-11e9-b2bb-42010a800002 20Gi RWO standard 77s
次のコマンドを実行して、Podが実行中であることを確認します。
kubectl get pods
備考: PodのStatusがRunning
の状態になる前に、最大で数分かかる場合があります。結果は次のようになるはずです。
NAME READY STATUS RESTARTS AGE wordpress-mysql-1894417608-x5dzt 1/1 Running 0 40s
次のコマンドを実行して、Serviceが実行中であることを確認します。
kubectl get services wordpress
結果は次のようになるはずです。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE wordpress LoadBalancer 10.0.0.89 <pending> 80:32406/TCP 4m
備考: MinikubeではServiceをNodePort
経由でしか公開できません。EXTERNAL-IPは常にpendingのままになります。次のコマンドを実行して、WordPress ServiceのIPアドレスを取得します。
minikube service wordpress --url
結果は次のようになるはずです。
http://1.2.3.4:32406
IPアドレスをコピーして、ブラウザーで読み込み、サイトを表示しましょう。
WordPressによりセットアップされた次のスクリーンショットのようなページが表示されるはずです。
警告: WordPressのインストールをこのページのまま放置してはいけません。もしほかのユーザーがこのページを見つけた場合、その人はインスタンス上にウェブサイトをセットアップして、悪意のあるコンテンツの配信に利用できてしまいます。
ユーザー名とパスワードを決めてWordPressをインストールするか、このインスタンスを削除してください。
クリーンアップ
次のコマンドを実行して、Secret、Deployment、Service、およびPersistentVolumeClaimを削除します。
kubectl delete -k ./
次の項目
- イントロスペクションとデバッグについてさらに学ぶ
- Jobについてさらに学ぶ
- Portフォワーディングについてさらに学ぶ
- コンテナへのシェルを取得する方法について学ぶ
3 - 例: StatefulSetを使用したCassandraのデプロイ
このチュートリアルでは、Apache CassandraをKubernetes上で実行する方法を紹介します。 データベースの一種であるCassandraには、データの耐久性(アプリケーションの 状態)を提供するために永続ストレージが必要です。 この例では、カスタムのCassandraのseed providerにより、Cassandraクラスターに参加した新しいCassandraインスタンスを検出できるようにします。
StatefulSetを利用すると、ステートフルなアプリケーションをKubernetesクラスターにデプロイするのが簡単になります。 このチュートリアルで使われている機能のより詳しい情報は、StatefulSetを参照してください。
備考:CassandraとKubernetesは、ともにクラスターのメンバーを表すために ノード という用語を使用しています。このチュートリアルでは、StatefulSetに属するPodはCassandraのノードであり、Cassandraクラスター( ring と呼ばれます)のメンバーでもあります。これらのPodがKubernetesクラスター内で実行されるとき、Kubernetesのコントロールプレーンは、PodをKubernetesのNode上にスケジュールします。
Cassandraノードが開始すると、 シードリスト を使ってring上の他のノードの検出が始まります。 このチュートリアルでは、Kubernetesクラスター内に現れた新しいCassandra Podを検出するカスタムのCassandraのseed providerをデプロイします。
目標
- Cassandraのheadless Serviceを作成して検証する。
- StatefulSetを使用してCassandra ringを作成する。
- StatefulSetを検証する。
- StatefulSetを編集する。
- StatefulSetとPodを削除する。
始める前に
Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:
このチュートリアルを完了するには、Pod、Service、StatefulSetの基本についてすでに知っている必要があります。
Minikubeのセットアップに関する追加の設定手順
注意:Minikubeは、デフォルトでは1024MiBのメモリと1CPUに設定されます。 デフォルトのリソース設定で起動したMinikubeでは、このチュートリアルの実行中にリソース不足のエラーが発生してしまいます。このエラーを回避するためにはMinikubeを次の設定で起動してください:
minikube start --memory 5120 --cpus=4
Cassandraのheadless Serviceを作成する
Kubernetesでは、Serviceは同じタスクを実行するPodの集合を表します。
以下のServiceは、Cassandra Podとクラスター内のクライアント間のDNSルックアップに使われます:
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandra
spec:
clusterIP: None
ports:
- port: 9042
selector:
app: cassandra
cassandra-service.yaml
ファイルから、Cassandra StatefulSetのすべてのメンバーをトラッキングするServiceを作成します。
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml
検証 (オプション)
Cassandra Serviceを取得します。
kubectl get svc cassandra
結果は次のようになります。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cassandra ClusterIP None <none> 9042/TCP 45s
cassandra
という名前のServiceが表示されない場合、作成に失敗しています。よくある問題のトラブルシューティングについては、Serviceのデバッグを読んでください。
StatefulSetを使ってCassandra ringを作成する
以下に示すStatefulSetマニフェストは、3つのPodからなるCassandra ringを作成します。
備考: この例ではMinikubeのデフォルトのプロビジョナーを使用しています。 クラウドを使用している場合、StatefulSetを更新してください。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cassandra
labels:
app: cassandra
spec:
serviceName: cassandra
replicas: 3
selector:
matchLabels:
app: cassandra
template:
metadata:
labels:
app: cassandra
spec:
terminationGracePeriodSeconds: 1800
containers:
- name: cassandra
image: gcr.io/google-samples/cassandra:v13
imagePullPolicy: Always
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
resources:
limits:
cpu: "500m"
memory: 1Gi
requests:
cpu: "500m"
memory: 1Gi
securityContext:
capabilities:
add:
- IPC_LOCK
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- nodetool drain
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra.default.svc.cluster.local"
- name: CASSANDRA_CLUSTER_NAME
value: "K8Demo"
- name: CASSANDRA_DC
value: "DC1-K8Demo"
- name: CASSANDRA_RACK
value: "Rack1-K8Demo"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /ready-probe.sh
initialDelaySeconds: 15
timeoutSeconds: 5
# These volume mounts are persistent. They are like inline claims,
# but not exactly because the names need to match exactly one of
# the stateful pod volumes.
volumeMounts:
- name: cassandra-data
mountPath: /cassandra_data
# These are converted to volume claims by the controller
# and mounted at the paths mentioned above.
# do not use these in production until ssd GCEPersistentDisk or other ssd pd
volumeClaimTemplates:
- metadata:
name: cassandra-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: fast
resources:
requests:
storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
type: pd-ssd
cassandra-statefulset.yaml
ファイルから、CassandraのStatefulSetを作成します:
# cassandra-statefulset.yaml を編集せずにapplyできる場合は、このコマンドを使用してください
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml
クラスターに合わせてcassandra-statefulset.yaml
を編集する必要がある場合、 https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml をダウンロードして、修正したバージョンを保存したフォルダからマニフェストを適用してください。
# cassandra-statefulset.yaml をローカルで編集する必要がある場合、このコマンドを使用してください
kubectl apply -f cassandra-statefulset.yaml
CassandraのStatefulSetを検証する
CassandraのStatefulSetを取得します
kubectl get statefulset cassandra
結果は次のようになるはずです:
NAME DESIRED CURRENT AGE cassandra 3 0 13s
StatefulSet
リソースがPodを順番にデプロイします。Podを取得して順序付きの作成ステータスを確認します
kubectl get pods -l="app=cassandra"
結果は次のようになるはずです:
NAME READY STATUS RESTARTS AGE cassandra-0 1/1 Running 0 1m cassandra-1 0/1 ContainerCreating 0 8s
3つすべてのPodのデプロイには数分かかる場合があります。デプロイが完了すると、同じコマンドは次のような結果を返します:
NAME READY STATUS RESTARTS AGE cassandra-0 1/1 Running 0 10m cassandra-1 1/1 Running 0 9m cassandra-2 1/1 Running 0 8m
1番目のPodの中でCassandraのnodetoolを実行して、ringのステータスを表示します。
kubectl exec -it cassandra-0 -- nodetool status
結果は次のようになるはずです:
Datacenter: DC1-K8Demo ====================== Status=Up/Down |/ State=Normal/Leaving/Joining/Moving -- Address Load Tokens Owns (effective) Host ID Rack UN 172.17.0.5 83.57 KiB 32 74.0% e2dd09e6-d9d3-477e-96c5-45094c08db0f Rack1-K8Demo UN 172.17.0.4 101.04 KiB 32 58.8% f89d6835-3a42-4419-92b3-0e62cae1479c Rack1-K8Demo UN 172.17.0.6 84.74 KiB 32 67.1% a6a1e8c2-3dc5-4417-b1a0-26507af2aaad Rack1-K8Demo
CassandraのStatefulSetを変更する
kubectl edit
を使うと、CassandraのStatefulSetのサイズを変更できます。
次のコマンドを実行します。
kubectl edit statefulset cassandra
このコマンドを実行すると、ターミナルでエディタが起動します。変更が必要な行は
replicas
フィールドです。 以下の例は、StatefulSetファイルの抜粋です:# Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: apps/v1 kind: StatefulSet metadata: creationTimestamp: 2016-08-13T18:40:58Z generation: 1 labels: app: cassandra name: cassandra namespace: default resourceVersion: "323" uid: 7a219483-6185-11e6-a910-42010a8a0fc0 spec: replicas: 3
レプリカ数を4に変更し、マニフェストを保存します。
これで、StatefulSetが4つのPodを実行するようにスケールされました。
CassandraのStatefulSetを取得して、変更を確かめます:
kubectl get statefulset cassandra
結果は次のようになるはずです:
NAME DESIRED CURRENT AGE cassandra 4 4 36m
クリーンアップ
StatefulSetを削除したりスケールダウンしても、StatefulSetに関係するボリュームは削除されません。 StatefulSetに関連するすべてのリソースを自動的に破棄するよりも、データの方がより貴重であるため、安全のためにこのような設定になっています。
警告: ストレージクラスやreclaimポリシーによっては、PersistentVolumeClaimを削除すると、関連するボリュームも削除される可能性があります。PersistentVolumeClaimの削除後にもデータにアクセスできるとは決して想定しないでください。
次のコマンドを実行して(単一のコマンドにまとめています)、CassandraのStatefulSetに含まれるすべてのリソースを削除します:
grace=$(kubectl get pod cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \ && kubectl delete statefulset -l app=cassandra \ && echo "Sleeping ${grace} seconds" 1>&2 \ && sleep $grace \ && kubectl delete persistentvolumeclaim -l app=cassandra
次のコマンドを実行して、CassandraをセットアップしたServiceを削除します:
kubectl delete service -l app=cassandra
Cassandraコンテナの環境変数
このチュートリアルのPodでは、Googleのコンテナレジストリのgcr.io/google-samples/cassandra:v13
イメージを使用しました。このDockerイメージはdebian-baseをベースにしており、OpenJDK 8が含まれています。
このイメージには、Apache Debianリポジトリの標準のCassandraインストールが含まれます。
環境変数を利用すると、cassandra.yaml
に挿入された値を変更できます。
環境変数 | デフォルト値 |
---|---|
CASSANDRA_CLUSTER_NAME | 'Test Cluster' |
CASSANDRA_NUM_TOKENS | 32 |
CASSANDRA_RPC_ADDRESS | 0.0.0.0 |
次の項目
- StatefulSetのスケールを行う方法を学ぶ。
- KubernetesSeedProviderについてもっと学ぶ。
- カスタムのSeed Providerの設定についてもっと学ぶ。