Kubernetes(K8s)学習ノート(第14回):クラスターストレージとステートフルアプリケーション(後編):StatefulSet によるステートフルアプリケーションの管理 h2>
本ノートは Kubernetes シリーズの第14回であり、ステートフルアプリケーションの管理に焦点を当てています。StatefulSet の主要な機能、Headless Service と固定 DNS、PVC テンプレートによるストレージの自動作成、Nginx/etcd/Redis/MySQL クラスタのデプロイ実践などを網羅しています。すべてのコマンドおよび YAML の例は整理され、注釈が付けられています。全文は約 4700 字で、16個のYAMLサンプル、55以上のコマンド例、および12枚の比較表を含み、Kubernetesのステートフルアプリケーション管理に関する完全なガイドとなっています。
関連知識:
Kubernetes(K8s) 学習ノート(第13回):クラスターストレージとステートフルアプリケーション(前編):動的なボリュームプロビジョニング------StorageClass + Provisioner
その他のKubernetesシリーズ:Kubernetes入門から上級まで
本ノートは Kubernetes シリーズの第14回であり、ステートフルアプリケーションの管理に焦点を当てています。StatefulSet の主要な機能、Headless Service と固定 DNS、PVC テンプレートによるストレージの自動作成、Nginx/etcd/Redis/MySQL クラスタのデプロイ実践などを網羅しています。すべてのコマンドおよび YAML の例は整理され、注釈が付けられています。全文は約 4700 字で、16個のYAMLサンプル、55以上のコマンド例、および12枚の比較表を含み、Kubernetesのステートフルアプリケーション管理に関する完全なガイドとなっています。
関連知識:
Kubernetes(K8s) 学習ノート(第13回):クラスターストレージとステートフルアプリケーション(前編):動的なボリュームプロビジョニング------StorageClass + Provisioner
その他のKubernetesシリーズ:Kubernetes入門から上級まで
私のホームページ:AOwhisky。ここでは、運用管理に関する体系的な知識のまとめやその他の興味深いコンテンツを掲載しています。ぜひ一緒に学び、議論しましょう~
- -- 編集・執筆:Whisky --- 2026年7月4日
目次
- 序章:「ステートレス」から「ステートフル」へ
- ステートレス vs ステートフルアプリケーション
- StatefulSet の主要な特徴
- StatefulSet と Deployment の比較
- 実習1:Nginx クラスタのデプロイ
- 実習2:etcd クラスタのデプロイ
- 実習3:Redis クラスタのデプロイ(マスター・スレーブ + センチネル)
- 実験4:MySQLクラスタのデプロイ(マスター・スレーブ同期)
- StatefulSetの更新ポリシー
- StatefulSetのPod管理
- まとめと知識ポイント一覧
序章:「ステートレス」から「ステートフル」へ
これまでの3回(ResourceQuota + LimitRange、HPA、動的ボリュームプロビジョニング)では、クラスタのリソース管理とストレージプロビジョニングの基盤構築を完了しました。さて、ここでより重要な課題に直面しています:ステートフルなアプリケーションをどのように管理するか?
Kubernetes において、Deployment はステートレスなアプリケーションを管理するための強力なツールです- -----すべてのPodが完全に同一であり、自由にスケールアウト・スケールイン、ローリングアップデート、再起動・再構築が可能で、データの損失やステータスの変化を心配する必要がありません。しかし現実の世界では、ほとんどのアプリケーションはステートフルです:
- データベース:各ノードには独立したストレージがあり、マスターとスレーブの役割を混同してはなりません
- 分散ミドルウェア: 各ノードには固定のネットワーク識別子があり、クラスターは順序通りに起動する必要がある
- メッセージキュー:ノード間で安定した通信アドレスが必要
これらのアプリケーションには以下が必要です:
- 固定のネットワーク識別子:Podの再起動後にIPは変化しますが、ホスト名は不変でなければなりません
- 独立した永続ストレージ:各Podは独自のデータボリュームを持ち、共有できない
- 順序通りの起動と停止:マスターノードが先に起動し、スレーブノードが後に起動する。スケールダウン時は後ろから順に削除される
StatefulSetは、まさにこれらの要件を満たすために設計されたものです。
今号のを一言でまとめると:StatefulSet により、ステートフルなアプリケーションも Kubernetes 上でステートレスなアプリケーションのようにスムーズに動作する---- --固定された識別子、安定したストレージ、順序立った操作。これら3つの特性はどれも欠かすことができません。
一、ステートレスアプリケーション vs ステートフルアプリケーション
1.1 核心的な違い
| 観点 | ステートレスアプリケーション(Stateless) | ステートフルアプリケーション(Stateful) |
|---|---|---|
| 主な特徴 | リクエストやセッションデータを一切保存せず、各リクエストは独立している | 永続化されたセッションやデータに依存し、リクエスト間に依存関係がある |
| データストレージ | データはリクエスト中にのみ存在し、永続化されない | データの永続化が必要で、固定のストレージに依存する |
| すべてのインスタンスが完全に同一であり、自由にスケールアウト/スケールインや置換が可能 | インスタンスには識別子(番号など)があり、起動/スケールアウト・スケールインには順序の要件がある | |
| ネットワーク識別子 | 共有 IP/ドメイン名、固定のネットワーク識別子なし | 固定のネットワーク識別子あり(例:Headless Service + DNS) |
| 代表的な用途 | Web サービス、API ゲートウェイ、静的ページ | データベース、Redis、ZooKeeper、Kafka |
1.2 なぜステートレスアプリケーションが推奨されるのか?
ステートレスアプリケーションはクラウドネイティブのベストプラクティスです:
- インスタンスを自由に交換でき、障害からの自己回復能力が高い
- スケールイン/アウトの際にデータ移行を待つ必要がなく、 秒単位で完了
- ローリングアップデートが簡単で、順序を考慮する必要がない
ベストプラクティス:アプリケーションを可能な限り「ステートレスなビジネス層 + ステートフルなデータ層」に分割することで、スケーラビリティを向上させると同時に、データの安全性を確保します。
二、StatefulSet の主要な特性
2.1 3つの主要な特性
| 特性 | 説明 | ||
|---|---|---|---|
| 固定識別子 | 各 Pod には一意のシリアル番号(0 から順次増加)が割り当てられ、ホスト名は「StatefulSet 名-シリアル番号」に固定される | ||
| 順序付き操作 | デプロイ/スケールアウトは 0→N-1 の順序で作成されます。スケールイン/削除は N-1→0 の逆順で削除されます。更新は N-1→0 の逆順でローリング更新されます | ||
| 安定したストレージ | PVC テンプレートを通じて各 Pod に個別の PVC を自動作成し、Pod の再構築後は元の PVC を自動的に再利用 |
| 操作 | 順序 | 説明 |
|---|---|---|
| 作成/スケールアウト | 0 → 1 → 2 .. . | 前のPodがRunningおよびReady状態になって初めて、次の |
| 削除/スケールダウン | ... → 2 → 1 → 0 | 前のPodが完全に削除されてから、次のPodが削除されます |
| ローリング更新 | ... → 2 → 1 → 0 | デフォルトでは最後の番号から更新を開始します(逆順) |
なぜ順序通りの操作が必要なのか?
ステートフルなアプリケーションには通常、起動時の依存関係があります:
- マスターノード(ordinal=0)は先に起動し、クラスタを初期化する必要があります
- スレーブノード(ordinal>0)は、マスターノードのネットワークアドレスに依存してクラスタに参加します
- 順不同で起動すると、スレーブノードがマスターノードを見つけられない可能性があります
StatefulSet の順序付き操作により、以下が保証されます:
- 起動順序:クラスタにまず「マスター」が存在し、その後で「スレーブ」が存在することを保証します
- 終了順序:スケールダウン時には、まず「スレーブ」を削除し、その後で「マスター」を削除することで、データ損失を防ぎます
- 更新順序:最後のノードから更新を開始し、クラスタの最小可用性を維持します
2.4 永続ストレージ
PVC テンプレート(volumeClaimTemplates):
- 各 Pod に対して個別の PVC を自動的に作成
- PVC の命名形式:
<template-name> -<statefulset-name>-<ordinal> - Podが削除された後も、PVCおよびPV は引き続き保持され、データは失われません
- Podが再構築されると、元のPVCが自動的に再利用されます
例 :StatefulSet nginx、PVC テンプレート nginx-data
nginx-data-nginx-0nginx-data-nginx-1nginx-data-nginx-2
重要 :StatefulSet を削除する際、 PVC は自動的に削除されません。データをクリーンアップするには、PVCを手動で削除する必要があります。
3. StatefulSet と Deployment の比較
| コントローラー | StatefulSet | Deployment |
|---|---|---|
| 適用シナリオ | ステートフルアプリケーション(データベース、分散クラスタ、メッセージキュー) | ステートレスアプリケーション(Webサービス、APIインターフェース、静的サービス) |
| ネットワーク | ヘッドレスサービス(固定DNS解決) | ClusterIP/LoadBalancer(共有IP) |
| ストレージ | 必須:PVC + PV(固定ストレージボリュームのバインド) | オプション:PVC(データの永続化要件なし) |
| スケールアウト・スケールイン | インスタンス番号順にスケールアウト・スケールイン(例:0→1→2) | 順序なし、瞬時に完了 |
| 更新ポリシー | 順序付き更新(例:最後のインスタンスから開始) | ローリング更新/再構築、順序なし |
| 再起動/再構築 | インスタンス再起動後、元のデータ/識別情報を復元する必要がある | インスタンス再起動後も影響なし |
4. 実験1:Nginxクラスタのデプロイ
要件:3ノードのNginxクラスターをデプロイし、データの永続化、固定ネットワーク識別子、順序付きデプロイを実現する。
4.1 環境の準備
root@master30:~# kubectl create ns statefulset
root@master30:~# kubectl config set-context --current --namespace statefulset
4.2 Headless Serviceの作成
root@master30:~# cat > nginx-service.yaml <<『EOF』
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: statefulset
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
targetPort: 80
name: nginx-port
EOF
root@master30:~# kubectl apply -f nginx-service.yaml
root@master30:~# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 5s code>
4.3 StatefulSetの作成
root@master30:~# cat > nginx-statefulset.yaml <<『EOF』
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
namespace: statefulset
spec:
serviceName: nginx
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: nginx-data
spec:
accessModes: [ "
ReadWriteOnce「 ]
storageClassName: 」nfs-storage" # 前回のデプロイで使用した NFS Provisioner を使用
resources:
requests:
storage: 10Gi
EOF
root@master30:~# kubectl apply -f nginx-statefulset.yaml
4.4 Pod の作成順序を確認する
root@master30:~# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
nginx-0 1/1 Running 0 10s
nginx-1 1/1 Running 0 30s
nginx -2 1/1 Running 0 50s
重要な観察点 :Podはnginx-0 → nginx-1 → nginx-2 の順序で作成され、各Podの間隔は約20秒です。
4.5 PVCの検証
root@master30:~# kubectl get pvcNAME STATUS VOLUME CAPACITYnginx-data-nginx-0 Bound pvc-8eb06bf1-a5de-4e94-bf83-514941f73c9f 10Ginginx-data-nginx-1 Bound pvc-e853eccd-8060 -4326-8c45-0b88c34a3d97 10Ginginx-data-nginx-2 Bound pvc-27d944f7-1e62-4352-9023-3874e6867ef9 10Gi
4.6 固定DNSの検証
root@master30:~# kubectl run test --image=nginx -it -- bashroot@test:/# curl nginx-0.nginxnginx-data-nginx-0root@test:/# curl nginx-1.nginxnginx-data-nginx-1root@test:/# curl nginx-2.nginxnginx-data-nginx-2
4.7 データの永続化の確認
# nginx-2 を削除root@master30:~# kubectl delete pod nginx-2# 再作成されるのを待つroot@master30:~# kubectl get podsNAME READY STATUS RESTARTS AGEnginx-0 1/1 Running 0 10mnginx-1 1/1 Running 0 10mnginx-2 1/1 Running 0 8s
# データが依然として存在することを確認
root@master30:~# kubectl exec test -- curl nginx-2.nginx
nginx-data-nginx-2
4.8 実験のまとめ
- StatefulSet は、Pod が 0 から N -1の順序でPodが作成されることを保証します
- 各Podは独立したPVCを取得し、データが永続化されます
- Podが再作成されてもDNS名は変わらず、データは失われません
- Headless Serviceは各Podに固定のDNS解決を提供します
4.9 環境のクリーンアップ
root@ master30:~# kubectl delete sts nginx
root@master30:~# kubectl delete svc nginx
root@master30:~# kubectl delete pvc nginx-data-nginx-{0..2}
五、 実験2:etcdクラスタのデプロイ
要件:3ノードのetcdクラスタをデプロイし、データの永続化、固定ネットワーク識別子、クラスタの自動ネットワーク構成を実現する。
5.1 NFSストレージの準備
root@master30:~# mkdir -m 777 -p /shares/etcd/data-{0..2}
root@master30:~# systemctl restart nfs-server.service
5.2 Headless Serviceの作成
root@master30:~# cat > etcd-service.yaml <<『EOF』
apiVersion: v1
kind: Service
metadata:
name: etcd
namespace: statefulset
spec:
clusterIP: None
selector:
app: etcd
ports:
- port: 2379
name: client
- port: 2380
name: peer
EOF
root@master30:~# kubectl apply -f etcd-service.yaml
5.3 PVの作成(静的設定)
root@master30:~# cat > etcd-pv.yaml <<『EOF』
apiVersion: v1
kind: PersistentVolume
metadata:
name: etcd-pv-0
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: etcd-storage
nfs:
server: 10.1.8.30
path: /shares/etcd/data-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: etcd-pv-1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: etcd-storage
nfs:
server: 10.1.8.30
path: /shares/etcd/data-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: etcd-pv-2
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: etcd-storage
nfs:
server: 10.1.8.30
path: /shares/etcd/data-2
EOF
root@master30:~# kubectl apply -f etcd-pv.yaml
ヒント: 本番環境では、PVを手動で作成するのではなく、動的ボリュームプロビジョニング(StorageClass)の使用を推奨します。
5.4 StatefulSetの作成
root@master30:~# cat > etcd-statefulset.yaml <<『EOF』
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
namespace: statefulset
spec:
serviceName: etcd
replicas: 3
selector:
matchLabels:
app: etcd
template:
metadata:
labels:
app: etcd
spec:
containers:
- name: etcd
image: registry.aliyuncs.com/google_containers/etcd:3.5.10-0
command:
- /usr/local/bin/etcd
ports:
- containerPort: 2379
name: client
- containerPort: 2380
name: peer
env:
- name: ETCD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: ETCD_DATA_DIR
value: /var/lib/etcd
- name: ETCD_INITIAL_ADVERTISE_PEER_URLS
value: " http://$(ETCD_NAME).etcd.statefulset.svc.cluster.local:2380「
- name: ETCD_LISTEN_PEER_URLS
value: 」http://0.0.0.0:2380「
- name: ETCD_LISTEN_CLIENT_URLS
value: 」http: //0.0.0.0:2379「
- name: ETCD_ADVERTISE_CLIENT_URLS
value: 」http://$(ETCD_NAME).etcd.statefulset.svc.cluster.local:2379"
- name: ETCD_INITIAL_CLUSTER
value: "etcd-0=http://etcd-0.etcd.statefulset.svc.cluster.local:2380,etcd-1=http://etcd-1.etcd.statefulset.svc.cluster.local: 2380,etcd-2=http://etcd-2.etcd.statefulset.svc.cluster.local:2380"
- name: ETCD_INITIAL_CLUSTER_TOKEN
value: 「etcd-token」
- name: ETCD_INITIAL_CLUSTER_STATE
value: 「new」
volumeMounts:
- name: etcd-data
mountPath: /var/lib/etcd
resources:
requests:
cpu: 100m
memory: 256Mi
volumeClaimTemplates:
- metadata:
name: etcd-data
spec:
accessModes: [ 「ReadWriteOnce」 ]
storageClassName: 「etcd-storage」
resources:
requests:
storage: 10Gi
EOF
root@master30:~# kubectl apply -f etcd-statefulset.yaml
5.5 etcd クラスタの状態の確認
root@master30:~# kubectl exec etcd-0 - - etcdctl member list
8747632212a8465, started, etcd-2, http://etcd-2.etcd.statefulset.svc.cluster.local:2380, http://etcd-2.etcd.statefulset.svc.cluster.local:2379, false
881e7c876792f042, started, etcd-0, http://etcd-0.etcd.statefulset.svc.cluster.local: 2380, http://etcd-0.etcd.statefulset.svc.cluster.local:2379, false
b6d311da39fc1138, started, etcd-1, http://etcd-1.etcd.statefulset.svc.cluster.local:2380, http://etcd-1. etcd.statefulset.svc.cluster.local:2379, false
5.6 データの永続化の検証
# データの書き込み
root@master30:~# kubectl exec etcd-0 -- etcdctl put /users/user1/name laowang
# etcd-2の削除
root@master30:~# kubectl delete pod etcd-2
# 再構築を待って検証
root@master30:~# kubectl exec etcd-2 -- etcdctl get /users/user1/name
/users/user1/name
laowang
5.7 実験のまとめ
- etcd クラスタは StatefulSet を通じて固定ネットワーク識別子を実現
- 各 etcd ノードは
$ {ETCD_NAME}環境変数を通じて自身の名前を取得する ETCD_INITIAL_CLUSTERを使用し、固定の DNS 名でクラスタの自動ネットワーク構成を実現- Pod を削除してもデータは失われない(PVC は保持される)
5.8 環境のクリーンアップ
root@master30:~# kubectl delete sts etcd
root@master30:~# kubectl delete svc etcd
root@master30:~# kubectl delete pvc etcd-data-etcd-{0..2}
root@master30:~# kubectl delete pv etcd-pv-{0..2}
6. 実習3:Redisクラスタのデプロイ(マスター・スレーブ+センチネル)
要件:3ノードのRedisクラスタ(マスター1台、スレーブ2台)をデプロイし、固定ネットワーク識別子、データの永続化、センチネルによる高可用性を実現する。
6.1 NFSストレージの準備
root@master30:~# mkdir -m 777 -p /shares/redis/data-{0..2}
root@master30:~# systemctl restart nfs-server.service
6.2 ヘッドレスサービスの作成
root@master30:~# cat > redis-service.yaml <<『EOF』
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: statefulset
spec:
clusterIP: None
selector:
app: redis
ports:
- port: 6379
name: redis
- port: 26379
name: sentinel
EOF
root@master30:~# kubectl apply -f redis-service.yaml
6.3 PVの作成
root@master30:~# cat > redis-pv.yaml <<『EOF』
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv-0
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: redis-storage
nfs:
server: 10.1.8.30
path: /shares/redis/data-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv-1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: redis-storage
nfs:
server: 10.1.8.30
path: /shares/redis/data-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-pv-2
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: redis-storage
nfs:
server: 10.1.8.30
path: /shares/redis/data-2
EOF
root@master30:~# kubectl apply -f redis -pv.yaml
6.4 StatefulSetの作成
root@master30:~# cat > redis-statefulset.yaml <<『EOF』
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: statefulset
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.0-alpine
ports:
- containerPort: 6379
name: redis
- containerPort: 26379
name: sentinel
command: [「/bin/sh」, 「-c」]
args:
- |
NODE_ID=$(hostname | awk -F『-』 『{print $2}』)
cat > /data/redis.conf << EOF
bind 0.0.0.0
protected-mode no
port 6379
dir /data
appendonly yes
requirepass 「redis123」
masterauth 「redis123」
EOF
if [ 「$NODE_ID」 != 『0』 ]; then
echo 「replicaof redis-0.redis.statefulset.svc.cluster.local 6379」 >> /data/redis.conf
fi
redis-server /data/redis.conf &
until nslookup redis-0.redis.statefulset.svc.cluster.local; do
echo 「DNSの解決を待機中...」
sleep 2
done
cat > /data/sentinel.conf << EOF
bind 0.0.0.0
protected-mode no
port 26379
sentinel resolve-hostnames yes
sentinel announce-hostnames yes
sentinel monitor mymaster redis-0.redis.statefulset.svc.cluster.local 6379 2
sentinel auth-pass mymaster redis123
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
EOF
redis-sentinel /data/sentinel.conf
volumeMounts:
- name: redis-data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ 「ReadWriteOnce」 ]
storageClassName: 「redis-storage」
resources:
requests:
storage: 10Gi
EOF
root@master30:~# kubectl apply -f redis-statefulset.yaml
6.5 マスター・スレーブのステータスを確認
root@master30:~# kubectl exec redis-0 -- redis-cli -a redis123 info replication
警告: 『-a』 オプションでパスワードを使用することは安全ではない可能性があります。
# レプリケーション
role:master
connected_slaves:2
slave0:ip=10.224.113.181,port=6379,state=online,lag=1
slave1:ip=10.224.19.24,port=6379,state=online,lag=1
6.6 センチネルの状態を確認 h4>
root@master30:~# kubectl exec redis-0 -- redis-cli -p 26379 sentinel master mymaster
1) 「name」
2) 「mymaster」
3) 「ip」
4) 「redis-0.redis.statefulset.svc.cluster.local」
5) 『port』
6) 「6379」
6.7 実験のまとめ
hostname | awk -F『-』 『{print $2}』 を使用して Pod の番号を取得し、マスターとスレーブの役割を決定
- redis-0 をマスターノードとして固定し、他のノードをスレーブノードとする
- センチネルの設定
sentinel monitor mymaster redis-0.redis.statefulset.svc.cluster.local 6379 2 マスターノードを監視
sentinel resolve-hostnames yes ホスト名の解決を有効化し、センチネルが DNS 名を通じてマスターノードを確実に特定できるようにする
6.8 環境のクリーンアップ
root@master30:~# kubectl delete sts redis
root@master30:~# kubectl delete svc redis
root@master30:~# kubectl delete pvc redis-data-redis-{0..2}
root@ master30:~# kubectl delete pv redis-pv-{0..2}
7. 実験4:MySQLクラスタのデプロイ(マスター・スレーブ同期)
root@master30:~# kubectl exec redis-0 -- redis-cli -p 26379 sentinel master mymaster
1) 「name」
2) 「mymaster」
3) 「ip」
4) 「redis-0.redis.statefulset.svc.cluster.local」
5) 『port』
6) 「6379」hostname | awk -F『-』 『{print $2}』 を使用して Pod の番号を取得し、マスターとスレーブの役割を決定sentinel monitor mymaster redis-0.redis.statefulset.svc.cluster.local 6379 2 マスターノードを監視sentinel resolve-hostnames yes ホスト名の解決を有効化し、センチネルが DNS 名を通じてマスターノードを確実に特定できるようにするroot@master30:~# kubectl delete sts redis
root@master30:~# kubectl delete svc redis
root@master30:~# kubectl delete pvc redis-data-redis-{0..2}
root@ master30:~# kubectl delete pv redis-pv-{0..2}要件:2ノードのMySQLクラスタ(マスター1台、スレーブ1台)をデプロイし 、データの永続化、固定ネットワーク識別子、マスター・スレーブの自動同期を実現する。
7.1 NFS ストレージの準備
root@master30:~# mkdir -m 777 -p /shares/mysql/data-{0,1}
root@master30:~# systemctl restart nfs-server.service
7.2 ヘッドレスサービスの作成
root@master30:~# cat > mysql-service.yaml <<『EOF』
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: statefulset
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
name: mysql-port
EOF
root@master30:~# kubectl apply -f mysql-service.yaml
7.3 PVの作成
root@master30:~# cat > mysql-pv.yaml <<『EOF』
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv-0
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: mysql-storage
nfs:
server: 10.1.8.30
path: /shares/mysql/data-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv-1
spec:
capacity: storage: 10Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: mysql-storage nfs: server: 10.1.8.30 path: /shares/mysql/data-1EOFroot@master30:~# kubectl apply -f mysql-pv.yaml
7.4 StatefulSetの作成
root@master30:~# cat > mysql-statefulset.yaml <<『EOF』apiVersion: apps/v1kind: StatefulSetmetadata: name: mysql namespace: statefulsetspec: serviceName: mysql replicas: 2 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 ports: - containerPort: 3306 name: mysql env: - name: MYSQL_ROOT_PASSWORD value: 「123456」 volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: [ 「ReadWriteOnce」 ] storageClassName: 「mysql-storage」 resources: requests: storage: 10GiEOFroot@master30: ~# kubectl apply -f mysql-statefulset.yaml
7.5 Pod のドメイン名解決の検証
root@master30:~# kubectl run test-pod --image=busybox -- sleep 3600root@master30:~# kubectl exec test-pod -- ping -c2 mysql-1.mysqlPING mysql-1.mysql (10.224.113.148): 56 data bytes64 bytes from 10.224.113.148: seq=0 ttl=62 time=1.586 ms
7.6 データの永続化の確認
# データベースの作成root@master30:~# kubectl exec -it mysql-1 -- mysql -u root -p123456 -e 『create database wordpress;』# mysql-1の削除root@master30:~# kubectl delete pod mysql-1# 再構築を待ってからデータを検証root@master30:~# kubectl exec -it mysql-1 -- mysql -u root -p123456 -e 『show databases;』+--------------------+| Database |+--------------------+| information_schema || mysql || performance_schema || sys || wordpress |+-------- ------------+
7.7 実験のまとめ
- MySQLのマスター・スレーブ同期には追加の設定が必要ですが、本実験では主にStatefulSetのデータ永続化機能を実証しました
- Podを削除してもデータが失われず、PVCの永続化効果が確認されました
- DNS名を固定することで、マスター・スレーブ構成の安定性が向上しました
7.8 環境のクリーンアップ
root@master30:~# kubectl delete sts mysql
root@master30:~# kubectl delete svc mysql
root@master30:~# kubectl delete pvc mysql-data-mysql- {0..1}
root@master30:~# kubectl delete pv mysql-pv-{0..1}
8. StatefulSet の更新ポリシー
8.1 更新ポリシーの種類
| ポリシー | 動作 | 適用シナリオ |
|---|---|---|
| RollingUpdate(デフォルト) | 最後の番号から逆順にローリング更新 | ほとんどのシナリオ |
| OnDelete | Pod を手動で削除した場合にのみ更新 | 更新のタイミングを完全に制御する必要がある |
8.2 更新戦略の設定
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # インデックスが 2 以上の Pod のみを更新
partition パラメータ:
- カナリアリリースやグレーリリースに使用
- インデックスが partition 以上の Pod が更新される
- インデックスが partition 未満の Pod は変更されない
9. StatefulSet の Pod 管理 h3>
9.1 Pod の置換
# 指定した Pod を削除(再作成がトリガーされ、データは保持される)
kubectl delete pod <pod-name>
9.2 Pod のステータスの取得
kubectl get sts
kubectl get pods -l app=<label>
kubectl describe sts <name>
9.3 スケールアウト・スケールイン
# スケールアウト
kubectl scale sts <name> --replicas=5
# スケールイン(最大インデックスから順に削除)
kubectl scale sts <name> --replicas=2
10. まとめと知識ポイント一覧
10.1 StatefulSet の主要な特徴のまとめ
# 指定した Pod を削除(再作成がトリガーされ、データは保持される)
kubectl delete pod <pod-name>kubectl get sts
kubectl get pods -l app=<label>
kubectl describe sts <name># スケールアウト
kubectl scale sts <name> --replicas=5
# スケールイン(最大インデックスから順に削除)
kubectl scale sts <name> --replicas=2| 特性 | 説明 | 主要な設定 |
|---|---|---|
| 固定ID | 各Podには一意のシークエンス番号と固定のホスト名がある | Headless Service + serviceName |
| 順序付き操作 | 作成/更新は順序通り、スケールダウンは逆順 | StatefulSet コントローラーによって自動的に保証される |
| 安定したストレージ strong> | 各Podに独立したPVC、削除後も保持 | volumeClaimTemplates |
| 固定DNS | Podは <name>.<service>.<ns>.svc.cluster.local 経由でアクセス可能 |
ヘッドレスサービス |
10.2 StatefulSet と Deployment の比較
| コントローラー | StatefulSet | Deployment |
|---|---|---|
| 適用シナリオ | ステートフルアプリケーション | ステートレスアプリケーション |
| ネットワーク | ヘッドレスサービス(固定DNS) | ClusterIP/LoadBalancer(共有IP) |
| ストレージ | 必須:PVC + PV | オプション:PVC |
| スケーリング | 順序あり(0→1→2) | 順不同 |
| 更新ポリシー | 順序付き更新(逆順) | ローリング更新 |
10.3 各実験の比較
| 実験 | アプリケーション | Pod 数 | 主な特徴 |
|---|---|---|---|
| 実験1 | Nginx | 3 | 基本的な StatefulSet の機能のデモ |
| 実験2 | etcd | 3 | クラスタの自動ネットワーク構成、固定DNS |
| 実験3 | Redis | 3 | マスター・スレーブ + センチネルによる高可用性 |
| 実験4 | MySQL | 2 | データの永続化検証 |
10.4 よく使うコマンド一覧
| 操作 | コマンド |
|---|---|
| StatefulSetの作成 | kubectl apply -f sts.yaml |
| StatefulSetの確認 | kubectl get sts |
| StatefulSetの詳細を表示 | kubectl describe sts <name> |
| Podの順序を確認する | kubectl get pods -w |
| PVCの確認 | kubectl get pvc |
| StatefulSetのスケール | kubectl scale sts <name> --replicas=<n> |
| StatefulSetの削除 | kubectl delete sts <name> |
10.5 よくあるエラーのトラブルシューティング
| エラー | 原因 | 解決方法 |
|---|---|---|
| Pod がずっと「Pending」状態のまま | PVC をバインドできない、またはノードリソースが不足している | PV のステータスとノードリソースを確認してください |
| 作成順序に異常がある | Headless Service が正しく構成されていない | clusterIP: None |
| データ損失 | PVC が誤って削除された | Retain 回収ポリシーを使用 |
| DNS 解決に失敗 | Service 名が一致しない | serviceName が Service 名と一致していることを確認 |
| マスター・スレーブ間の同期に失敗 | ネットワーク接続不可または設定エラー | Pod間のネットワーク接続性を確認 |
10.6 本番環境におけるベストプラクティス
| 実践 | 説明 |
|---|---|
| 動的ボリュームプロビジョニングを使用 | PVの手動作成を避け、 StorageClass を使用して自動的に管理する |
| リソース制限を設定する | ステートフルアプリケーションに対して適切な CPU/メモリ要求および制限を設定する |
| レプリカ数を適切に設定する | 本番環境では少なくとも3ノード (etcd/Redisの過半数要件を満たす) |
| データを定期的にバックアップ | ステートフルアプリケーションのデータは重要な資産であるため、定期的にバックアップする必要がある |
| 「Retain」リサイクルポリシーを使用 | PVCの誤削除によるデータ損失を防止 |
| PodDisruptionBudgetの設定 | ステートフルアプリケーションが同時にエジェクションされないように保護 |
ノート完結のお知らせ:これで、Kubernetesシリーズノート (第1回~第14回)はすべて完了しました。インフラストラクチャ、クラスタのデプロイ、コンテナ管理、ネットワークポリシー、スケジューリング管理、リソース管理、ストレージ管理からステートフルアプリケーションに至るまでの全プロセスを網羅しています。読者の皆様には、「 まずアーキテクチャを理解 → クラスタのデプロイを実践 → 日常の運用管理を習得 → ストレージとステートフルアプリケーションの応用」という順序で、段階的に学習することをお勧めします。今後、サービスメッシュ(Istio)、Kubernetesのセキュリティポリシー、GitOpsによる継続的デプロイなど、より高度な内容が登場した場合は、必要に応じて学習範囲を広げていくことができます。ご学習ありがとうございました!
--- 編集・執筆:Whisky --- 2026年7月4日