2026年8月20日

2026年8月20日

KubernetesのStatefulSetの使い方|固定のPod名とボリューム・Headless Service・更新と削除の注意点

StatefulSetは、データベースのように「Podごとに名前とデータが決まっていてほしい」アプリを動かすためのリソースです。Podには mysql-0、mysql-1 のような番号付きの固定の名前が付き、Podごとに専用の永続ボリュームが作られ、作り直されても同じ名前と同じボリュームに戻ります。この記事では、Deploymentとの違い、MySQLを1台動かす例でのYAMLの書き方、Headless Serviceによる名前解決、更新・スケール・削除のときの注意点、止まったときの調べ方を説明します。

内容は、Kubernetesの現行のサポート対象の版(2026年10月現在)で共通して使える項目を中心にしています。新しい版で追加された項目は、その旨を書いています。

Deploymentとの違い

項目 Deployment StatefulSet
Pod名 ランダムな文字列が付く <名前>-0、<名前>-1 と番号で固定
ボリューム 全Podで共有するか、持たない volumeClaimTemplates でPodごとに専用のPVCを作る
起動・停止の順序 順不同 既定では0番から順に起動し、逆順に停止
名前解決 Serviceの名前で、どれかのPodに届く Headless Serviceで、Podごとの名前で届く

WordPressの本体は、Pod間でデータを持ち合わない作りにしやすく(アップロードファイルを共有ストレージやオブジェクトストレージに置く)、通常はDeploymentで動かします。StatefulSetを使うのは、データベースやキャッシュ、メッセージキューのように、各Podが自分のデータを持つアプリです。

ただし、StatefulSetは「名前とボリュームを固定する」だけで、レプリケーションやフェイルオーバー、バックアップはしてくれません。MySQLを複数台で冗長化するには、アプリ側の設定を自分で組むか、MySQL向けのOperatorを使うことになります。運用の手間を考えると、本番のデータベースはマネージドサービスにする選択も有力です。

YAMLを書く(MySQLを1台動かす例)

apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: wordpress
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
    - name: mysql
      port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: wordpress
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: mysql
          image: mysql:8.4
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-root
                  key: password
            - name: MYSQL_DATABASE
              value: wordpress
          ports:
            - containerPort: 3306
          readinessProbe:
            exec:
              command: ["sh", "-c", "mysqladmin ping -h 127.0.0.1 --silent"]
            periodSeconds: 10
            timeoutSeconds: 5
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              memory: 1Gi
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi

ポイントは次のとおりです。

  • **Headless Service(clusterIP: None)**:StatefulSetの serviceName で指定します。これがあると、各Podに mysql-0.mysql.wordpress.svc.cluster.local のような個別の名前が付きます。先にServiceを作っておきます。
  • **volumeClaimTemplates**:Podごとに data-mysql-0 のような名前のPVCが作られます。storageClassName を省くとクラスターの既定のStorageClassが使われます。既定がないクラスターでは指定が必要です。
  • パスワードはSecretから:mysql-root というSecretを先に作っておきます。YAMLに直接書かないでください。
  • **terminationGracePeriodSeconds**:停止の合図からの猶予時間です。データベースが書き込みを終えて正常に止まる時間を確保します。

WordPress側からは、Serviceの名前 mysql(別の名前空間からなら mysql.wordpress)を WORDPRESS_DB_HOST に指定すれば接続できます。

作成と確認

kubectl apply -f mysql-statefulset.yaml
kubectl rollout status statefulset/mysql -n wordpress
kubectl get statefulset,pods,pvc -n wordpress

kubectl get pvc で、data-mysql-0 が Bound(ボリュームが割り当て済み)になっていることを確かめます。Pending のままなら、StorageClassの指定やストレージの準備に問題があります。

スケールと起動の順序

replicas を3にすると、mysql-1 は mysql-0 が準備完了(Ready)になってから作られ、mysql-2 は mysql-1 の後になります。減らすときは番号の大きいほうから順に止まります。これが既定の podManagementPolicy: OrderedReady です。

順序が要らないアプリでは Parallel にすると、まとめて起動・停止できます。ただし、このポリシーは作成後に変更できません。

ここで注意したいのが、スケールで減らしてもPVCは消えない点です。mysql-2 を止めても data-mysql-2 は残り、後で3台に戻すと同じデータに再び接続されます。データを守るための既定の動きですが、使わないPVCの分のストレージは確保されたままになります。比較的新しい版では persistentVolumeClaimRetentionPolicy で、削除時やスケールダウン時にPVCを消すかどうかを選べます。データが消える設定なので、使う前に版の対応状況とバックアップの手順を確認してください。

更新する

kubectl apply -f mysql-statefulset.yaml
kubectl rollout status statefulset/mysql -n wordpress

既定の RollingUpdate では、番号の大きいPodから1つずつ止めて作り直します。前のPodが準備完了になるまで次に進みません。

partition を使うと、一部のPodだけ先に更新できます。

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 2

この設定では、番号が2以上のPodだけが新しい設定になり、0番と1番は古いままです。新しいイメージを1台で試し、問題がなければ partition を0に下げて残りを更新する、という段階的な更新に使えます。

データベースのメジャーバージョンを上げる更新は、データの形式が変わることがあり、元に戻せないこともあります。イメージのタグを変える前に、論理バックアップ(MySQLなら mysqldump など)を取り、ステージング環境で同じ手順を試してください。

更新が止まったときと削除するとき

更新の途中で新しいPodが準備完了にならないと、ローリングアップデートはそこで止まります。よくある原因を、起きやすい順に挙げます。

  1. 新しい設定でアプリが起動しない:kubectl logs mysql-0 -n wordpress --previous で前回の出力を見ます。設定ファイルの誤りやデータ形式の非互換が多いです。
  2. PVCが割り当てられない:kubectl describe pvc data-mysql-0 -n wordpress の Events を見ます。StorageClassがない、容量が足りない、ボリュームが別のゾーンにある、などの理由が出ます。
  3. Readinessが通らない:起動に時間がかかるならstartupProbeを足します。

テンプレートを正しい内容に戻しても、壊れた状態のPodが自動では作り直されず、止まったままになることがあります。この場合は、テンプレートを直したうえで、問題のPodを kubectl delete pod で削除すると、正しい設定で作り直されます。PVCは残るのでデータは消えませんが、削除の前にバックアップがあることを確かめてください。

StatefulSetを削除するときは、いきなり kubectl delete statefulset を実行するより、先に kubectl scale statefulset mysql --replicas=0 -n wordpress で全Podを順に止めるほうが安全です。StatefulSetの削除そのものは、Podが止まる順序を保証しないためです。削除後もPVCは残るので、データを消したい場合だけ、バックアップを確かめてからPVCを削除します。

まとめ

  • StatefulSetは、Pod名とPodごとのボリュームを固定する。データベースなど各Podがデータを持つアプリ向けで、WordPress本体は通常Deploymentで動かす。
  • Headless Serviceを先に作り、serviceName で指定する。ボリュームは volumeClaimTemplates でPodごとに作られる。
  • スケールや削除でPVCは消えない。消す設定を使う場合はバックアップを先に確保する。
  • 更新は番号の大きいPodから1つずつ進む。partition で段階的に更新できる。
  • 冗長化やバックアップは自分で用意するか、Operatorやマネージドサービスを検討する。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲