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が準備完了にならないと、ローリングアップデートはそこで止まります。よくある原因を、起きやすい順に挙げます。
- 新しい設定でアプリが起動しない:
kubectl logs mysql-0 -n wordpress --previousで前回の出力を見ます。設定ファイルの誤りやデータ形式の非互換が多いです。 - PVCが割り当てられない:
kubectl describe pvc data-mysql-0 -n wordpressの Events を見ます。StorageClassがない、容量が足りない、ボリュームが別のゾーンにある、などの理由が出ます。 - 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やマネージドサービスを検討する。