4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【本気で学ぶKubernetes】StatefulSet入門 - Deploymentとの違いを理解する

Posted at

はじめに

こんにちは!

本記事は「本気で学ぶKubernetes」シリーズの第9回です。このシリーズでは、Kubernetesの基礎から実践まで、段階的に学んでいきます。

このシリーズは、第1回から順に読むことで体系的に学べる構成にしています。
まだご覧になっていない方は、ぜひ最初からご覧ください!

Kubernetesとは?クラスタ構成の全体像をつかむ

前回は、KubernetesのProbe(ヘルスチェック)について触れました。Readiness Probe、Liveness Probe、Startup Probeを使ってコンテナの健康状態を監視する方法を学びました。

【本気で学ぶKubernetes】コンテナのヘルスチェックを行うProbeに入門

今回は、StatefulSetについて学んでいきたいと思います。

この記事は人間がKubernetesの公式ドキュメントを読み漁りながら、人間の手で書いていますのでご安心ください!

Deploymentの限界について

DeploymentではPod名やIPアドレスがランダムに割り当てられたり、起動順序が保証されていない他、コンテナの再起動時にPodのデータが失われてしまうという特徴があります。

そのためWebサーバーやAPIサーバーといったいわゆる「ステートレス」なアプリケーションを動かすのに適しているリソースとも言えます。

データベースやキューのように「状態を持つ」アプリケーションを動かす場合、つまり「ステートフル」なアプリケーションをKubernetesで動かしたい場合はDeploymentでは対応しきれません

そこで使用されるのが今回のトピックのStatefulSetです。

StatefulSetとは

StatefulSetは、「ステートフル」なアプリケーションを動かすためのKubernetesのリソースです。

Deploymentと比較すると以下のような特徴があります。

  • Pod名の固定
    • StatefulSetで作成されるPodは、web-0web-1web-2のように、固定の名称を動的につけることができます。
    • Podを削除して再作成しても、同じ名前で作り直されます。
  • 起動・削除の順序を保証
    • StatefulSetでは、Podの起動・削除が順序通りに行われます。
    • 起動時:0→1→2の順で、1つずつ起動
    • 削除時:2→1→0の順で、1つずつ削除
  • Pod専用の永続ストレージを利用
    • StatefulSetでは、volumeClaimTemplatesという機能を使って、各Podに専用のPersistentVolumeClaimを自動的に作成できます。

出典: Kubernetes公式ドキュメント - StatefulSets

StorageClassとHeadless Serviceについて

StatefulSetを理解するためには、2つの前提知識が必要です。

StorageClass

以前の記事では、PersistentVolume(PV)PersistentVolumeClaim(PVC)について触れました。
その時はPVを作成して手動でPodにPVCを割り当てるといったことを行いましたが、Podが立ち上がるたびにそれぞれのPodに専用ストレージを自動付与するためにPVを作るのは現実的ではないと思います。

そこで登場するのがStorageClassというリソースを使用します。

StorageClassを使うと、PVCを作成したタイミングで自動的にPVを作ることができます。

minikubeには、デフォルトでstandard StorageClassが用意されていますのでコマンドで確認してみます。

kubectl get storageclass

# NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
# standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  10d

(default)と表示されているStorageClassが、PVCを作成したときに自動的に使われるようです。

StatefulSetのvolumeClaimTemplatesとこのStorageClassと組み合わせることで、各PodにPVCとPVを自動的に作成することができるようになります。

出典: Kubernetes公式ドキュメント - Storage Classes

Headless Service

こちらも以前の記事では、ServiceのタイプとしてClusterIP、NodePort、LoadBalancer、ExternalNameといったネットワークの概念について触れました

これらのServiceは、複数のPodに対して負荷分散を行ってアクセスを行います。

しかし、実際に運用していくにあたってはデータベースのマスターノードだけに書き込むなど「特定のPod」に直接アクセスしたい場合が考えられます。

Headless Serviceを使うとServiceとしてのロードバランシング自体は無効化して、代わりに各Podに固有に割り当てられるDNS名を付与してアクセスできるようにします。

例えば、nginx-serviceというHeadless Serviceとweb-0というPodがあったとすると、

web-0.nginx-service.default.svc.cluster.localという完全修飾ドメイン名(FQDN)で直接アクセスできるようになります。(以下はフォーマット)

<Pod名>.<Service名>.<Namespace名>.svc.cluster.local

出典: Kubernetes公式ドキュメント - Headless Services

StatefulSetを試してみる

今回も例の如く実際にマニフェストを作成して挙動を確認していこうと思います。

nginxを3台構成で起動するようにマニフェストを作成します。

statefulset-nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  clusterIP: None  # Headless Serviceにする
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx-service"  # Headless Serviceを指定
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:  # Pod専用のPVCを自動作成
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 100Mi

ポイントは2点、もちろん先ほど触れたHeadless ServicevolumeClaimTemplatesです。

clusterIP: Noneを設定することで、Headless Serviceになります。
また、volumeClaimTemplatesを指定することで各Podに対してPVCを作成するようにしています。

マニフェストをデプロイしてみます。

kubectl apply -f statefulset-nginx.yaml

# service/nginx-service created
# statefulset.apps/web created

デプロイをしてすぐにPodの状態を確認してみました。

kubectl get pods -w

# NAME            READY   STATUS              RESTARTS          AGE
# web-0           0/1     ContainerCreating   0                 1s
# web-0           1/1     Running             0                 1s
# web-1           0/1     Pending             0                 0s
# 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                 1s
# web-2           0/1     Pending             0                 0s
# 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                 1s

Deploymentでは、3つのPodが同時に起動していましたが、StatefulSetではweb-0から順番にPodが起動していることがわかりますね。

kubectl get pods

# NAME    READY   STATUS    RESTARTS   AGE
# web-0   1/1     Running   0          30s
# web-1   1/1     Running   0          25s
# web-2   1/1     Running   0          20s

以前までDeploymentで作成した場合はnginx-deployment-abc123-xyzといったようなランダムな名称が付けられていましたが、StatefulSetの場合はweb-0web-1web-2という名称が付いています。

PVCが自動的に作成されているか確認してみます。

kubectl get pvc

# NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# www-web-0   Bound    pvc-12345678-1234-1234-1234-123456789012   100Mi      RWO            standard       1m
# www-web-1   Bound    pvc-23456789-2345-2345-2345-234567890123   100Mi      RWO            standard       1m
# www-web-2   Bound    pvc-34567890-3456-3456-3456-345678901234   100Mi      RWO            standard       1m

www-web-0www-web-1www-web-2という3つのPVCが自動的に作成されていて、volumeClaimTemplatesで指定したwwwという名前と、Pod名を組み合わせた命名規則になっていることがわかるかと思います。

データの永続性を確認してみる

StatefulSetでは、Podを削除しても同じPodが同じストレージを使い続けることができると記載しましたが、その挙動についても確認しておきたいと思います。

web-0のストレージにindex.htmlファイルを配置します

kubectl exec web-0 -- bash -c "echo 'Hello from web-0' > /usr/share/nginx/html/index.html"

# ファイルが作成されたかの確認
kubectl exec web-0 -- cat /usr/share/nginx/html/index.html

# Hello from web-0

web-0を削除してみます。

kubectl delete pod web-0

# pod "web-0" deleted

Podの状態を確認してみると、web-0が再作成されていることがわかります。

kubectl get pods

# NAME    READY   STATUS              RESTARTS   AGE
# web-0   0/1     ContainerCreating   0          3s
# web-1   1/1     Running             0          5m
# web-2   1/1     Running             0          5m

再作成されたweb-0にて最初に追加したファイルを確認してみます。

kubectl exec web-0 -- cat /usr/share/nginx/html/index.html

# Hello from web-0

web-0が削除されたとしても、同じ名前のweb-0が作り直されて同じPVCであるwww-web-0をマウントしているためデータが消えずに残っています。

クリーンアップ

挙動確認が終わったので作成したリソースを削除しておきます。

kubectl delete -f statefulset-nginx.yaml

# service "nginx-service" deleted
# statefulset.apps "web" deleted

またStatefulSetを削除しても、PVCは自動的に削除されませんので手動で削除しておきましょう!

kubectl delete pvc www-web-0 www-web-1 www-web-2

# persistentvolumeclaim "www-web-0" deleted
# persistentvolumeclaim "www-web-1" deleted
# persistentvolumeclaim "www-web-2" deleted

StatefulSetを削除してもPVCが残るのは、データの誤削除を防ぐためにこの仕様になっているようです。

まとめと次回予告

今回は、StatefulSetについて触れてきました。

今まで使ってきたDeploymentは「ステートレス」なアプリケーション向けで、今回のStatefulSetは「ステートフル」なアプリケーション向けだということがわかりました。

基本的には、データが消えても問題なくどのPodでも同じ役割ならDeployment、データを永続化したりPod間で役割をかえるならStatefulSetを使うということを押さえておくと良いかと思います。

実際には外部サービスとうまく連携させて使うことが多いと思いますが、ステートフルなアプリケーションを設計していく場合はこちらも念頭においておいた方が良いですね。

次回は、KubernetesにおけるIngressやHTTP(S) Load Balancerについて触れていきたいと思います。

それでは、また明日!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?