Kubernetes(K8s)学習ノート(第14回):クラスターストレージとステートフルアプリケーション(後編):StatefulSet によるステートフルアプリケーションの管理

Kubernetes(K8s)学習ノート(第14回):クラスターストレージとステートフルアプリケーション(後編):StatefulSet によるステートフルアプリケーションの管理

本ノートは 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日

目次

  1. 序章:「ステートレス」から「ステートフル」へ
  2. ステートレス vs ステートフルアプリケーション
  3. StatefulSet の主要な特徴
  4. StatefulSet と Deployment の比較
  5. 実習1:Nginx クラスタのデプロイ
  6. 実習2:etcd クラスタのデプロイ
  7. 実習3:Redis クラスタのデプロイ(マスター・スレーブ + センチネル)
  8. 実験4:MySQLクラスタのデプロイ(マスター・スレーブ同期)
  9. StatefulSetの更新ポリシー
  10. StatefulSetのPod管理
  11. まとめと知識ポイント一覧

序章:「ステートレス」から「ステートフル」へ

これまでの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つの主要な特性

2.2 固定ネットワーク識別子

StatefulSet は、Headless Service(ClusterIP: None)を関連付ける必要があり、各 Pod に固定の DNS 解決を提供します:

<statefulset-name>-<ordinal>.<service-name>.<namespace>.svc.cluster.local

:StatefulSet nginx、Service nginx、Namespace default

  • nginx-0.nginx.default.svc. cluster.local
  • nginx-1.nginx.default.svc.cluster.local
  • nginx-2.nginx.default.svc.cluster.local

Pod 名の安定したソース

StatefulSet 内の Pod の名前は 2 つの部分で構成されます:< statefulset-name>-<ordinal>。この名称は安定しており、予測可能なもので、Podの再起動や再構築によって変化することはありません。

  • ordinalは0から順次増加し、重複することはありません
  • Pod が再構築された後も、ordinal は変更されません
  • metadata.name フィールドを通じて、Pod は自身の名前を取得できます

環境変数による Pod 名の取得

env:
- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name

2.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 の順序付き操作により、以下が保証されます:

  1. 起動順序:クラスタにまず「マスター」が存在し、その後で「スレーブ」が存在することを保証します
  2. 終了順序:スケールダウン時には、まず「スレーブ」を削除し、その後で「マスター」を削除することで、データ損失を防ぎます
  3. 更新順序:最後のノードから更新を開始し、クラスタの最小可用性を維持します

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-0
  • nginx-data-nginx-1
  • nginx-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

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-0nginx-1nginx-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 センチネルの状態を確認

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クラスタのデプロイ(マスター・スレーブ同期)

要件: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 管理

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 の主要な特徴のまとめ

特性 説明 主要な設定
固定ID 各Podには一意のシークエンス番号と固定のホスト名がある Headless Service + serviceName
順序付き操作 作成/更新は順序通り、スケールダウンは逆順 StatefulSet コントローラーによって自動的に保証される
安定したストレージ 各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日