KubernetesにおけるStatefulSetsと永続ストレージ
順序付きスケーリングと永続的なデータを使用してステートフルなアプリを展開する
Kubernetes StatefulSets は、安定したアイデンティティ、永続的なストレージ、および順序付きデプロイメントパターンを必要とするステートフルなアプリケーションを管理するための最適なソリューションです。データベース、分散システム、キャッシュレイヤーなどに不可欠です。
Kubernetes に初めて触れるか、クラスターを設定している場合は、開発用に k3s や MicroK8s のような Kubernetes distributions を検討し、本番環境では Kubespray で Kubernetes をインストール することをおすすめします。
この素晴らしい画像は AI model Flux 1 dev によって生成されました。
StatefulSets とは
StatefulSets は、ステートフルなアプリケーションを管理するために特別に設計された Kubernetes のワークロード API オブジェクトです。すべてのポッドを交換可能と見なす Deployments とは異なり、StatefulSets はポッドごとに一意のアイデンティティを維持し、順序と一意性に関する保証を提供します。
主な特徴:
- 安定したネットワーク識別子: 各ポッドは再起動しても変化しない予測可能なホスト名を取得します
- 永続ストレージ: ポッドが再スケジュールされても続く PersistentVolumeClaims が提供されます
- 順序付きデプロイメント: ポッドは順序に従って作成され(0, 1, 2…)、終了は逆順で行われます
- 順序付き更新: ローリング更新は順序に従って行われ、アプリケーションの安定性を確保します
PostgreSQL、MySQL、MongoDB、Cassandra、Elasticsearch、Kafka、ZooKeeper、Redis、etcd など、ポッドのアイデンティティとデータの永続性が重要なワークロードでは、StatefulSets が不可欠です。
Kubernetes における永続ストレージの理解
Kubernetes は、ポッドライフサイクルからストレージ管理を分離する高度なストレージ抽象化レイヤーを提供しています。
ストレージコンポーネント
PersistentVolume (PV): 管理者によってクラスター内でプロビジョニングされるストレージの一部、または StorageClass を介して動的に作成されるストレージ。PV はポッドとは独立して存在します。
PersistentVolumeClaim (PVC): ポッドによるストレージの要求。PVC はサイズ、アクセスモード、ストレージクラスなどの要件に合致する利用可能な PV にバインドされます。
StorageClass: AWS EBS、GCE PD、Azure Disk、NFS などのさまざまなプロビジョナーと、レプリケーション、パフォーマンス階層、バックアップポリシーなどのパラメータを定義するストレージの「クラス」を定義します。
アクセスモード
- ReadWriteOnce (RWO): 単一ノードで読み書き可能なボリューム
- ReadOnlyMany (ROX): 複数ノードで読み取り専用のボリューム
- ReadWriteMany (RWX): 複数ノードで読み書き可能なボリューム(特別なストレージバックエンドが必要)
StatefulSet のストレージアーキテクチャ
StatefulSets は volumeClaimTemplates を使用して、各ポッドレプリカに対して自動的に PersistentVolumeClaims を作成します。これは Deployments とは根本的に異なります。
volumeClaimTemplates の動作方法
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
この StatefulSet を作成すると:
- Kubernetes はポッド
mysql-0と PVCdata-mysql-0を作成します - 次にポッド
mysql-1と PVCdata-mysql-1を作成します - 最後にポッド
mysql-2と PVCdata-mysql-2を作成します
各ポッドは独自の 10GB の永続ボリュームを取得します。mysql-1 が削除または再スケジュールされた場合、Kubernetes は同じ data-mysql-1 PVC を再びアタッチし、すべてのデータを保持します。
StatefulSets 用のヘッドレスサービスの作成
StatefulSets は安定したネットワークアイデンティティを提供するためにヘッドレスサービスを必要とします:
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
clusterIP: None # これによりヘッドレスサービスになります
selector:
app: mysql
ports:
- port: 3306
name: mysql
これにより、各ポッドに対して DNS エントリが作成されます:
mysql-0.mysql-service.default.svc.cluster.localmysql-1.mysql-service.default.svc.cluster.localmysql-2.mysql-service.default.svc.cluster.local
アプリケーションはこれらの安定した DNS 名を使用して、特定のポッドインスタンスに直接接続できます。
StatefulSet のデプロイメントパターン
複雑な Kubernetes デプロイメントを管理するチームにとって、Helm Charts は、テンプレート、バージョン管理、依存関係管理を使用して StatefulSets をパッケージ化およびデプロイする強力な方法を提供します。Helm は、さまざまな環境における StatefulSet の構成を管理することを簡略化します。
順序付きスケーリング
3 から 5 レプリカにスケーリングする場合:
kubectl scale statefulset mysql --replicas=5
Kubernetes は順序に従ってポッドを作成します: mysql-3 → Ready になるまで待機 → mysql-4
5 から 3 レプリカにスケーリングする場合:
kubectl scale statefulset mysql --replicas=3
Kubernetes は逆順で終了します: mysql-4 → 終了まで待機 → mysql-3
ローリング更新
StatefulSets は 2 つの更新戦略をサポートします:
OnDelete: 手動更新 — ポッドは削除するまで更新されません RollingUpdate: 逆順のオーディナル順で自動的に順序付き更新
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # 2 以上のオーディナルを持つポッドのみを更新
partition パラメータはカナリーデプロイメントを可能にします — 高番号のポッドを最初に更新し、すべてのレプリカに展開する前にテストできます。
ストレージのベストプラクティス
動的プロビジョニング
常に StorageClasses を使用してボリュームの動的プロビジョニングを行ってください:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
iops: "3000"
throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Retain
allowVolumeExpansion: PVC を再作成せずにサイズ変更を可能にします
reclaimPolicy: Retain は PVC 削除後も PV データを保持し、Delete は自動的に削除します
PVC 保留ポリシー
Kubernetes 1.23+ は persistentVolumeClaimRetentionPolicy をサポートしています:
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain # StatefulSet が削除されたときに PVC を保持
whenScaled: Delete # スケーリングダウン時に PVC を削除
オプション:
- Retain: PVC を保持(デフォルトの動作、最も安全)
- Delete: 自動的に PVC を削除(開発環境に適しています)
バックアップ戦略
ボリュームスナップショット: VolumeSnapshot リソースを使用してポイントインタイムバックアップを作成します
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20251113
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: data-mysql-0
アプリケーションレベルのバックアップ: mysqldump、pg_dump、または Velero を使用してデータベース固有のバックアップを実施します
分散レプリケーション: MySQL レプリケーションや PostgreSQL ストリーミングレプリケーションなどのアプリケーションレベルのレプリケーションを最初の防御手段として構成します
実際のユースケース
データベースクラスター (PostgreSQL)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard"
resources:
requests:
storage: 20Gi
分散キャッシュ (Redis)
Redis クラスターでは、StatefulSet と慎重な設定が必要です:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis-service
replicas: 6
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command:
- redis-server
- "--appendonly"
- "yes"
- "--appendfsync"
- "everysec"
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
メッセージキュー (Kafka)
Kafka は、ログの永続ストレージとブローカー間の安定したネットワークアイデンティティの両方を必要とします:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
spec:
serviceName: kafka-service
replicas: 3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.5.0
ports:
- containerPort: 9092
name: kafka
env:
- name: KAFKA_BROKER_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: data
mountPath: /var/lib/kafka/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
モニタリングとトラブルシューティング
このセクションで使用される Kubernetes コマンドの包括的なリファレンスについては、Kubernetes Cheatsheet をご参照ください。
StatefulSet のステータスを確認する
# StatefulSet の詳細を表示
kubectl get statefulset mysql
kubectl describe statefulset mysql
# ポッドの作成順とステータスを確認
kubectl get pods -l app=mysql -w
# PVC のステータスを確認
kubectl get pvc
kubectl describe pvc data-mysql-0
一般的な問題
ポッドがPending状態にとどまる: PVC のステータスとストレージの可用性を確認してください
kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'
ストレージがいっぱい: StorageClass が許可する場合、PVC を拡張してください
kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
ポッドが終了しない: アプリケーションレベルのロックやボリュームのアンマウント問題を確認してください
kubectl delete pod mysql-0 --grace-period=0 --force
モニタリングするメトリクス
- ストレージ使用量: PVC の容量と使用率パーセンテージをモニタリング
- I/O パフォーマンス: IOPS、スループット、レイテンシーを追跡
- ポッド再起動: 頻繁な再起動はストレージの問題を示唆
- PVC バインディング時間: 慢性的なバインディングはプロビジョニングの問題を示唆
マイグレーション戦略
StatefulSets に移行する際には、Kubernetes クラスターが適切に構成されていることを確認してください。ホームラボや小規模クラスターの設定では、Kubernetes distributions の包括的な比較 をご確認いただき、ワークロード要件に合ったプラットフォームを選択してください。
Deployment から StatefulSet への移行
- volumeClaimTemplates 付き StatefulSet を作成
- Deployment をグラフィカルにスケーリングダウン
- バックアップから StatefulSet ポッドにデータを復元
- DNS/Service の参照を更新
- 古い Deployment と PVC を削除
移行前のバックアップ
# 既存の PVC をスナップショット
kubectl get pvc -o yaml > pvc-backup.yaml
# ボリュームスナップショットを作成
kubectl apply -f volume-snapshot.yaml
セキュリティの考慮事項
ストレージ暗号化
StorageClass パラメータを使用して暗号化を有効にします:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: encrypted-storage
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
encrypted: "true"
kmsKeyId: arn:aws:kms:us-east-1:123456789012:key/abcd-1234
アクセス制御
RBAC を使用して、StatefulSets と PVC の作成/変更を誰が行えるかを制限します:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: statefulset-manager
rules:
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "create", "delete"]
ネットワークポリシー
ポッド間の通信を制限します:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-network-policy
spec:
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 3306
パフォーマンス最適化
ストレージパフォーマンス階層
ワークロードに応じて適切な StorageClasses を選択してください:
- 高IOPS: 重いランダム読み書きを持つデータベース(gp3、io2)
- 高スループット: ログ集約、分析(st1、sc1)
- バランス: 一般的な用途(gp3)
ポッドの分散
可用性ゾーンにわたって StatefulSet ポッドを分散させるために pod anti-affinity を使用します:
spec:
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mysql
topologyKey: topology.kubernetes.io/zone
リソースリクエストと制限
一貫したパフォーマンスのために適切なリソースを設定します:
resources:
requests:
cpu: "2"
memory: "4Gi"
ephemeral-storage: "10Gi"
limits:
cpu: "4"
memory: "8Gi"
ephemeral-storage: "20Gi"
高度なパターン
初期コンテナ付きのステートフルアプリケーション
データベースの初期化に初期コンテナを使用します:
spec:
template:
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
if [[ ! -d /var/lib/mysql/mysql ]]; then
mysqld --initialize-insecure --datadir=/var/lib/mysql
fi
volumeMounts:
- name: data
mountPath: /var/lib/mysql
サイドカー付きのマルチコンテナポッド
バックアップサイドカーまたはモニタリングエージェントを追加します:
spec:
template:
spec:
containers:
- name: mysql
image: mysql:8.0
# ... mysql config ...
- name: backup-sidecar
image: mysql-backup:latest
volumeMounts:
- name: data
mountPath: /var/lib/mysql
readOnly: true
ダイナミック構成用の ConfigMaps
StatefulSet 定義から構成を分離します:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
max_connections=200
innodb_buffer_pool_size=2G
---
spec:
template:
spec:
containers:
- name: mysql
volumeMounts:
- name: config
mountPath: /etc/mysql/conf.d
volumes:
- name: config
configMap:
name: mysql-config
有用なリンク
- Kubernetes StatefulSets ドキュメント
- 永続ボリューム
- ストレージクラス
- ボリュームスナップショット
- StatefulSet のベストプラクティス
- CSI ドライバ
- Velero バックアップソリューション
- Rook Ceph ストレージ
- Kubernetes Cheatsheet
- Kubespray で Kubernetes をインストール
- 3ノードホームラボ向け Kubernetes distributions の比較
- Kubernetes distributions - kubeadm, k3s, MicroK8s, Minikube, Talos Linux, RKE2 の概要
- Helm Charts: Kubernetes パッケージ管理ガイド