😬

kubernetesのステートフルセットを利用して障害に強いpodを作成する①

2021/03/16に公開

はじめに

くーばねてすを倒すために今回はkubernetesのステートフルセットを利用して障害に強いpodを作成する方法を調べた!(^^)/

概要

■ステートフルセットとは
■どんなときに利用できるか
■ステートフルセットとdeploymentの違い
■ステートフルセット記述例

を調べた(>_<)

■ステートフルセットとは

https://kubernetes.io/ja/docs/concepts/workloads/controllers/statefulset/
https://kubernetes.io/ja/docs/tutorials/stateful-application/basic-stateful-set/
https://kubernetes.io/ja/docs/tasks/run-application/run-replicated-stateful-application/
ステートフルセットはステートフル(状態維持)なアプリケーションを管理するためのオブジェクトで永続ボリュームとpodの組み合わせを制御している。

データを永続的に保持するのが難しいpodやコンテナの永続ボリュームを維持するためにステートフルセットというオブジェクトを利用することで解決することができる。
ステートフルセットはpodと永続ボリュームの対応関係を管理し、永続ボリュームのデータ保護を優先するように設計されている。

■どんなときに利用できるか

ステートフルセットは下記の1つ以上の項目を要求するアプリケーションにおいて最適である。
イメージ的にはPodTemplateにPVC(永続ボリューム要求)も追加した感じになる。

・安定した一意のネットワーク識別子が求められるとき。
・安定した永続ストレージが必要なとき。
・規則的で安全なデプロイとスケーリングを行いたいとき。
・規則的で自動化されたローリングアップデートを行いたいとき。

・安定した一意のネットワーク識別子が求められるとき。
ステートフルセットで管理されるPodは順序付けされた一意の識別子が付与される。
この識別子がネットワークの識別子にも利用される。
ステートフルセット管理下の各Podは、そのステートフルセット名とPodの順序番号から派生してホストネームが割り当てられる。 作成されたホストネームの形式は(StatefulSet名)-(順序番号)となる。
・安定した永続ストレージが必要なとき。
StatefulSetはPersistentVolumeClaimで指定された永続ボリューム要求に対してそれぞれに1つずつのPVが作成され、Podにマウントされる。
StatefulSetによって作成されたPVCは、StatefulSetが削除された際にはPVCは削除されない。PVCを削除する場合は手動で削除する必要があるので気をつける。
・規則的で安全なデプロイとスケーリングを行いたいとき。
ステートフルセットでデプロイ、スケーリングを行いたいときは安全で順序付けされた動作の元実行される。
・規則的で自動化されたローリングアップデートを行いたいとき。
ステートフルセットを利用することでで安全で順序付けされた動作の元実行される。

Podのスケジュール(または再スケジュール)をまたいでも永続的で安定したネットワーク識別子と規則的なデプロイや削除、スケーリングを要求したい場合、ユーザーはステートフルセットを使ってアプリケーションをデプロイすることができる。

■ステートフルセットとdeploymentの違い

pod名と永続ボリューム名のマッチ

deploymentでレプリカした場合にpodはランダムのIDが付与されるのに対して、
ステートフルセットで管理されるPodは順序付けされた一意の識別子が付与される。
例えば、ステートフルセットでレプリカ数を5と設定した場合、ステートフルセット管理下で起動するpodは末尾に1から5の連番の数字が付与されて命名される。また同じ末尾の連番がPVCにも付与される。
podと永続ボリュームの名前がマッチするということだ。
|||
| --- | --- | --- |
|deployment|ランダムID|
|ステートフルセット|連番の数字のID|

サービスとの連携と名前解決

ステートフルセットは、PodのドメインをコントロールするためにHeadless Serviceを使用する必要がある。なのでユーザーはこのServiceを作成する必要がある。
Headless Serviceによって管理された代表ドメイン名の
絶対ドメイン名は、[サービス名].[ネームスペース].[クラスタドメイン名]となる。
相対ドメイン名は[サービス名]となる。

クライアントがサービス名でIPアドレスを解決するとステートフルセット管理下のpodのIPアドレスをランダムに返す。

管理するServiceはステートフルセットのspec/serviceNameでサービス名を設定すると末尾にpodの連番が付与された個々のIPアドレスを取得することができる。

代表ドメイン名
|ドメイン名||
| --- | --- | --- |
|絶対ドメイン名|[サービス名].[ネームスペース].[クラスタドメイン名]|
|相対ドメイン名|[サービス名]|
podのドメイン名
|ドメイン名||
| --- | --- | --- |
|絶対ドメイン名|[pod名].[サービス名].[ネームスペース].[クラスタドメイン名]|
|相対ドメイン名|[pod名].[サービス名]|

※clusterIP(/spec/clusterIP)の値を"None"に設定することにより、"Headless"とよばれるServiceを作成できる。

pod喪失時(deploymentとの違い)

通常のdeployment管理下でpodが喪失されたとき、レプリカされるpodはランダムな名前で複製されるが、
ステートフルセット管理下でpodが喪失されたとき、喪失前と同じ名前でpodは複製される。
対応した永続ボリュームは存続し続け、また対応podの永続ボリュームとなる。
※podは変わらなくても、podのIPアドレスは変わってしまうので気を付ける。
|||
| --- | --- | --- |
|deployment|ランダムIDでレプリカ(ボリュームは引き継がれない)|
|ステートフルセット|同名でレプリカ(永続ボリュームが引き継がれる)|

Node喪失時(deploymentとの違い)

deploymentの場合、Node喪失時は代替のpodが他Nodeで起動されるのに対し、
ステートフルセットでは永続ボリュームのデータ保護を第一優先として設計されているため、Nodeが停止しマスターと連携できなくなってしまった場合、新たなpodは他Nodeで作成されることはない。仮に新たなpodを起動する(更新されてないデータ下でpodが生き残っていたなど二重起動状態になってしまう)ことにより永続ボリュームのデータを更新してしまった場合、永続ボリュームのデータは破壊されてしまうからである。
|||
| --- | --- | --- |
|deployment|他Nodeで代替podが作成される|
|ステートフルセット|他Nodeで代替podは作成されない|

例外的にpodが作成される場合
・障害Nodeをkubernetesクラスタから除外したとき
・Node上のpodを強制終了する場合
・障害停止したNodeを再起動した場合
など、podの二重起動を避け、もとのデータが破壊されないような状態であれば新たなpodを作成することができる。この場合、外部からこのようなアクションをする必要がある。

ステートフルセットのpodの順番制御

・レプリカ数に達するまでpodと永続ボリュームのセットは順番に作成される。
・レプリカ数を増やす連番が追加されたpodと永続ボリュームが作成される。またレプリカ数を減らした場合は大きい数字から順にpodと永続ボリュームは削除される。
・ローリングアップデートされる場合も順番に更新されていく。

■ステートフルセット記述例

ステートフルセットを使用するときの決まり
①ClusterIP: None、ヘッドレスモードを使用すること 
②serviceName: 連携するサービス名を記述すること
③template: マウントポイントを指定すること
④volumeClaimTemplates: レプリカ数の設定だけでセットで永続ボリュームが作成される

apiVersion: v1
kind: Service
metadata:
  name: nginx  # この名前がDNS名になる
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None      # ①ヘッドレスモードを使用すること
  selector:
    app: nginx     # 後述のステートフルセットと関連付ける
---
apiVersion: apps/v1        #ここからステートフルセットの記述
kind: StatefulSet
metadata:
  name: web
spec:     # ステートフルセットの仕様
  selector:
    matchLabels:
      app: nginx # 後述するpodと対応させるためにmatchLabelsを利用する。
  serviceName: "nginx" # ②連携するサービス名を記述すること
  replicas: 3 # default は 1 
  template:     # podのテンプレート
    metadata:
      labels:
        app: nginx #StatefulSetと関連付けする為にmatchLabelsの値と一致させる
    spec:
      terminationGracePeriodSeconds: 10
      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

ここではserviceマニフェスト見比べやすいように---でマニフェストを区切ってステートフルセットの内容が記述してている。

---
apiVersion: apps/v1        #ここからステートフルセットの記述
kind: StatefulSet
metadata:
  name: web
spec:     # ステートフルセットの仕様
  selector:
    matchLabels:
      app: nginx # 後述するpodと対応させるためにmatchLabelsを利用する。
  serviceName: "nginx"
  replicas: 3 # default は 1

↑ここでは対応させるpodとの対応関係を示すためにmatchLabelsを指定して作成する。なのでpodのテンプレートでラベルも指定すること。
|設定項目|意味|
| --- | --- | --- |
|apiVersion|APIバージョン。apps/v1とする|
|kind|ステートフルセットなのでStatefulSet|
|metadata|nameが必須項目なので設定する。|
|spec|ステートフルセットの仕様を記述する|
|spec/selector/matchLabels|podを対応づける為にmatchLabelsを利用する。podのラベルと一致しないとエラーが発生する。|
|spec/serviceName|ヘッドレスサービスの名前をセットする|
|spec/replicas|起動するpodの数を指定する|

↓その下にpodのテンプレートを記述している。

  template:     # podのテンプレート
    metadata:
      labels:
        app: nginx #StatefulSetと関連付けする為にmatchLabelsの値と一致させる
    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

↑podに対応するステートフルセットを関係づける為にラベルを記述すること。
|設定項目|意味|
| --- | --- | --- |
|template/metadata/labels|ステートフルセットのselectorと一致させる|
|template/spec/containers|podのコンテナについて記述する|

その下にボリューム要求のテンプレートを記述する。PVCのリストのことだ。ここで指定したPVCの名前は少なくとも1つのコンテナの volumeMount で指定されている必要がある。

  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

volumeClaimTemplates設定項目
|設定項目|意味|
| --- | --- | --- |
|metadata/name|ここで設定した名前はPVCのプレフィックスになる|
|spec|ボリュームの仕様を設定する|

まとめ

負けない!(>_<)

Discussion