Lab 5: PersistentVolumeとStatefulSet

このラボでは、PersistentVolume(PV)とPersistentVolumeClaim(PVC)でデータを永続化し、StatefulSetでステートフルなアプリケーションを管理する方法を学びます。

1. StorageClassの確認

まず利用可能なStorageClassを確認します。

# StorageClass一覧
kubectl get storageclasses

# デフォルトのStorageClassを確認
kubectl describe storageclass microk8s-hostpath
💡 StorageClass: ストレージの種類(速度、バックアップ有無など)を定義するリソースです。この環境では microk8s-hostpath が利用できます。

2. PersistentVolumeClaimの作成

PVCを使ってストレージを要求します。

cat << 'EOF' > my-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: microk8s-hostpath
  resources:
    requests:
      storage: 1Gi
EOF

kubectl apply -f my-pvc.yaml

# PVCの状態を確認(STATUS が Bound になればOK)
kubectl get pvc

3. PVCを使うPodの作成

cat << 'EOF' > pvc-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pvc-writer
spec:
  containers:
  - name: writer
    image: nginx
    volumeMounts:
    - name: data
      mountPath: /usr/share/nginx/html
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: my-pvc
EOF

kubectl apply -f pvc-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-writer --timeout=60s

4. データの永続化を確認

# Pod内にファイルを作成
kubectl exec pvc-writer -- sh -c 'echo "Hello from PV!" > /usr/share/nginx/html/index.html'

# ファイルの内容を確認
kubectl exec pvc-writer -- cat /usr/share/nginx/html/index.html

# Podを削除
kubectl delete pod pvc-writer

# 同じPVCを使う新しいPodを作成
kubectl apply -f pvc-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-writer --timeout=60s

# データが残っていることを確認!
kubectl exec pvc-writer -- cat /usr/share/nginx/html/index.html
💡 ポイント: Podを削除してもPVCとPVに保存されたデータは残ります。これがPersistentVolumeの意味です。

5. クリーンアップ(PVC)

kubectl delete pod pvc-writer
kubectl delete pvc my-pvc

6. StatefulSetの作成

StatefulSetは各Podに固定の名前と専用のPVCを割り当てます。データベースなどステートフルなアプリケーションに使われます。

cat << 'EOF' > mysql-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql-headless
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "rootpass"
        - name: MYSQL_DATABASE
          value: "testdb"
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: microk8s-hostpath
      resources:
        requests:
          storage: 2Gi
EOF

kubectl apply -f mysql-statefulset.yaml

7. StatefulSetの特徴を確認

# StatefulSetの状態確認
kubectl get statefulsets

# Pod名が連番であることを確認
kubectl get pods -l app=mysql

# 専用のPVCが作成されていることを確認
kubectl get pvc
💡 StatefulSetの特徴:
・Pod名が固定(mysql-0, mysql-1, ...)
・各Podに専用のPVCが割り当てられる(mysql-data-mysql-0)
・Podの起動・停止が順序通りに行われる

8. データの永続性を確認

# Pod起動を待つ
kubectl wait --for=condition=Ready pod/mysql-0 --timeout=120s

# MySQLに接続してデータを作成
kubectl exec -it mysql-0 -- mysql -uroot -prootpass -e "
  USE testdb;
  CREATE TABLE messages (id INT AUTO_INCREMENT PRIMARY KEY, text VARCHAR(255));
  INSERT INTO messages (text) VALUES ('Hello from StatefulSet!');
  SELECT * FROM messages;
"
# Podを削除(StatefulSetが自動で再作成する)
kubectl delete pod mysql-0

# 再起動を待つ
kubectl wait --for=condition=Ready pod/mysql-0 --timeout=120s

# データが残っていることを確認
kubectl exec -it mysql-0 -- mysql -uroot -prootpass -e "USE testdb; SELECT * FROM messages;"
⚠️ 注意: StatefulSetのPodを削除してもPVCは削除されません。StatefulSet自体を削除しても、PVCは手動で削除する必要があります(データ保護のため)。

9. StatefulSetのスケーリング

# レプリカ数を2に増やす
kubectl scale statefulset mysql --replicas=2

# Podが順番に起動することを確認(mysql-1が追加される)
kubectl get pods -l app=mysql -w

# 各Podに個別のPVCが割り当てられている
kubectl get pvc

10. クリーンアップ

# StatefulSet削除
kubectl delete -f mysql-statefulset.yaml

# PVCは手動で削除(データごと削除される)
kubectl delete pvc mysql-data-mysql-0
kubectl delete pvc mysql-data-mysql-1

# すべてクリーンになったことを確認
kubectl get all
kubectl get pvc

📝 練習問題

課題: PVCとStatefulSetの理解を深めましょう。
  1. 1GiのPVCを作成し、nginx Podにマウントして独自の index.html を書き込む
  2. Podを削除して再作成し、データが永続化されていることを確認する
  3. StatefulSetでnginxを3レプリカ作成し、各Podの名前が web-0, web-1, web-2 であることを確認する
  4. すべてのリソース(StatefulSet、Service、PVC)を削除する

解答例

# 1-2. PVCとPodの永続化確認
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: web-pvc
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: microk8s-hostpath
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: web-writer
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: web-pvc
EOF

kubectl wait --for=condition=Ready pod/web-writer --timeout=60s
kubectl exec web-writer -- sh -c 'echo "My persistent page" > /usr/share/nginx/html/index.html'
kubectl delete pod web-writer
# 再作成して確認
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: web-writer
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: web-pvc
EOF
kubectl wait --for=condition=Ready pod/web-writer --timeout=60s
kubectl exec web-writer -- cat /usr/share/nginx/html/index.html

# 3. StatefulSet
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: web-headless
spec:
  clusterIP: None
  selector:
    app: web-sts
  ports:
  - port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: web-headless
  replicas: 3
  selector:
    matchLabels:
      app: web-sts
  template:
    metadata:
      labels:
        app: web-sts
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

kubectl get pods -l app=web-sts

# 4. クリーンアップ
kubectl delete statefulset web
kubectl delete service web-headless
kubectl delete pod web-writer
kubectl delete pvc web-pvc

← トップに戻る