Kubernetes永続化ストレージの深淵:PV・PVC・StorageClassを完全掌握し、ステートフルの恐怖を克服する
こんにちは。テックリードの私だ。
Kubernetes(以下、K8s)の導入初期、多くのエンジニアが「コンテナはエフェメラル(はかない)である」という教義に縛られ、データベースなどのステートフルなワークロードをクラスタに乗せることに恐怖を覚える。ポッドが再起動するたびにデータが消失する――それを避けるために、ホストパスクやローカルストレージのハックで泥沼にはまった経験はないだろうか?
だが、恐れる必要はない。K8sのストレージ抽象化レイヤーである PV (PersistentVolume)、PVC (PersistentVolumeClaim)、そして StorageClass の関係性を完璧に理解し、動的プロビジョニングを血肉化すれば、インフラのプロビジョニングスピードは劇的に跳ね上がり、ステートフルアプリはステートレス並みに容易く扱えるようになる。
今回は、現場の生産性を極限まで高める実践知見とともに、このストレージの三位一体を徹底解説する。
—
1. コンテナのステートレス性とストレージの課題
K8sの根幹思想は「ポッドは使い捨てである」という点にある。Podが死ねば、その内部のエフェメラルストレージ(Container File System)に書き込まれたデータは宇宙の塵と消える。
ログや一時ファイルならそれで良い。しかし、PostgreSQL、MySQL、Redis、あるいはElasticsearchといった永続データを扱うミドルウェアを動かす場合、この仕様は致命的だ。
ここで必要になるのが、「Podのライフサイクルからストレージを完全に切り離す(Decoupling)」というアプローチである。クラスタ管理者がストレージを用意し、開発者は「これだけの容量の領域が欲しい」と宣言するだけで、基盤側の複雑さを意識せずに永続領域を手に入れる。この分離こそが、K8sストレージ設計の美しさであり、本質だ。
—
2. PV, PVC, StorageClassの関係性を図解
K8sのストレージ機構は、インフラ担当者とアプリケーション開発者の「関心の分離」を見事に実現している。
+——————————————————-+
| Cluster Administrator (インフラ担当) |
| |
| [ StorageClass ] (プロビジョナーや容量・性能を定義) |
| │ |
| ▼ (要求に応じて自動作成) |
| [ PersistentVolume (PV) ] (実際のクラウドストレージ) |
+———┼─────────────────────────────────────────────+
│ バインド (Binding)
+———┼─────────────────────────────────────────────+
| Application Developer (アプリ開発者) |
| │ |
| [ PersistentVolumeClaim (PVC) ] (ストレージの「利用券」)|
| │ |
| ▼ マウント |
| [ Pod / Container ] |
+——————————————————-+
- PersistentVolume (PV): クラスタ内の物理的なストレージ(AWS EBS, GCP Persistent Disk, Azure Disk, NFS等)。インフラ管理者がプロビジョニングするか、後述のStorageClassによって動的に生成される。
- PersistentVolumeClaim (PVC): 開発者が記述する「ストレージの要求書」。容量(例: `10Gi`)やアクセスモード(例: `ReadWriteOnce`)を指定する。
- StorageClass (SC): PVを動的にプロビジョニングするための「レシピ」。どのクラウドプロバイダのどのタイプ(gp3, io2など)のストレージを使うかを定義する。
—
3. 動的プロビジョニングの設定方法:静的の呪縛からの解放
かつてはインフラエンジニアが事前にPVを手動作成(静的プロビジョニング)し、PVCと手動で紐付けるという前時代的な苦行が存在した。今やそんな時代遅れのワークフローは捨て去るべきだ。「動的プロビジョニング(Dynamic Provisioning)」こそが、開発スピードを極限まで高める鍵となる。
以下のStorageClassとPVCのベストプラクティス構成を見てほしい。
01. StorageClassの定義(AWS EBS gp3を例に)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ebs-gp3-sc
annotations:
storageclass.kubernetes.io/is-default-class: “true” # クラスタのデフォルトストレージに指定
provisioner: ebs.csi.aws.com # AWS EBS CSIドライバを指定
parameters:
type: gp3
iops: “3000”
throughput: “125”
reclaimPolicy: Delete # PVC削除時にPVも削除する(生産性重視。本番の永続DBではRetain推奨)
volumeBindingMode: WaitForFirstConsumer # 超重要:PodがスケジュールされるまでPV作成を遅延させ、正しいAZに配置させる
allowVolumeExpansion: true # 後からの容量拡張を許可する
> プロの知見: `volumeBindingMode: WaitForFirstConsumer` の魔術
> マルチAZ環境において、この設定を入れないと、PVC作成直後にランダムなAZでPVがプロビジョニングされ、いざPodをデプロイした際に「ストレージとPodが別AZにいてアタッチできない」という絶望的なエラー(Multi-Attach error)に直面する。この設定は、Podがどのノード(=どのAZ)に配置されるか決まってからPVをプロビジョニングさせるための、絶対に入れるべき神設定である。
02. PersistentVolumeClaim (PVC) の定義
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: database
spec:
accessModes:
- ReadWriteOnce # 単一ノードからの読み書き
storageClassName: ebs-gp3-sc
resources:
requests:
storage: 50Gi # 50GBを要求
—
4. ステートレスからステートフルアプリ(DB等)への応用
PVCを手に入れたら、いよいよステートフルなアプリケーション(PostgreSQLなど)をデプロイする。ここで、通常の `Deployment` ではなく `StatefulSet` を使うのがK8sにおける定石だ。
以下に、実務でそのまま使えるPostgreSQLのプロダクションレディなマニフェストを示す。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: database
spec:
serviceName: “postgres-headless”
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: postgres-storage
spec:
accessModes: [ “ReadWriteOnce” ]
storageClassName: ebs-gp3-sc
resources:
requests:
storage: 50Gi
この `volumeClaimTemplates` を使うことで、StatefulSetのレプリカ(例: `postgres-0`, `postgres-1`)ごとに一意かつ適切なPVCが自動生成され、ポッドが万が一クラッシュして別ノードに再スケジュールされても、同じPV(データ)が確実に再アタッチされる。
—
5. 現場の生産性を爆発させるプロの実践テクニック
ここからは、日々のK8s運用や開発スピードを劇的に高める、現場のシニアエンジニアが隠し持つテクニックを伝授しよう。
A. 開発スピードを高めるキーボードショートカット & エイリアス (kubectl)
ストレージの状態確認やデバッグで毎回 `kubectl get persistentvolumeclaim` と打っているようでは手首が破壊される。`.bashrc` 或いは `.zshrc` に以下を仕込め。
alias k=”kubectl”
alias kgp=”kubectl get pods”
alias kgpvc=”kubectl get pvc”
alias kgpv=”kubectl get pv”
alias kgsc=”kubectl get storageclass”
PVCとPVの紐づきを美しく一覧化するワンライナー
alias k-storage=’kubectl get pvc -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase,VOLUME:.spec.volume,CAPACITY:.spec.resources.requests.storage,STORAGECLASS:.spec.storageClassName’
B. 絶対入れるべき神プラグイン:`krew` と `kubectl-df-pv`
ストレージの容量圧迫は障害の元凶である。どのPVがどれだけ容量を使っているかを瞬時に把握するために、K8sのパッケージマネージャである `krew` を使って以下のプラグインを導入せよ。
プラグインのインストール
kubectl krew install df-pv
kubectl krew install neat # YAMLから自動生成フィールドを削ぎ落として美しく出力する神ツール
実行例:
kubectl df-pv
これによって、各PVの総容量、使用量、使用率がディスクの `df` コマンドの要領で一目でリスト化される。容量枯渇アラートを鳴らす前の日常的な健康診断に必須のツールだ。
C. チーム開発で役立つ設定の共有化ルール (Kustomize / Helm)
環境(dev, stg, prd)ごとにストレージのサイズ(`storage: 50Gi` vs `storage: 500Gi`)や `storageClassName` をハードコーディングするのは素人のやることだ。
Kustomize もしくは Helm を用い、ベースの設定に対してパッチを当てる構成を強制せよ。
ディレクトリ構成例(Kustomize):
deploy/
├── base/
│ ├── kustomization.yaml
│ ├── pvc.yaml
│ └── statefulset.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── storage-patch.yaml
└── prd/
├── kustomization.yaml
└── storage-patch.yaml
チームメンバー全員がこの構成に従うことで、ローカル環境(MinikubeやKindでの `standard` ストレージクラス)から本番のクラウド環境への移行が、コードの書き換えなしでシームレスに実現できる。
—
最後に:ストレージの恐怖を「制御下」に置け
K8sにおける永続化ストレージは、もはや「難解なブラックボックス」ではない。
StorageClassが基盤を抽象化し、PVCが宣言的に要求し、StatefulSetがライフサイクルを担保する。このモダンなパイプラインを正しく構築すれば、データベースの運用すらK8sの上でエレガントに自動化できる。
あなたのクラスタのストレージは、本当にモダンになっているか? 今すぐマニフェストを見直し、動的プロビジョニングの波に乗ってほしい。インフラの自動化に終わりはない。さらなる高みを目指せ。