Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Kubernetes: PVC in a StatefulSet, and the “Forbidden updates to statefulset spec” error

2025年09月17日に「Dev.to」が公開したITニュース「Kubernetes: PVC in a StatefulSet, and the “Forbidden updates to statefulset spec” error」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

KubernetesのStatefulSetで永続ボリューム(PVC)のサイズ変更は直接できず、「Forbidden updates to statefulset spec」エラーが発生する。解決策は、まずPVCを手動でサイズ変更する。その後、StatefulSetを依存オブジェクトを残して削除し、再度作成する。これにより、サービスを止めずにPVCサイズを拡張できる。

ITニュース解説

Kubernetesというシステムをご存知だろうか。これは、アプリケーションを動かすためのたくさんのコンピュータ(サーバー)を効率的に管理し、自動化するための強力なツールである。システムエンジニアを目指す上で、Kubernetesの理解は非常に重要だ。今回は、Kubernetesで動くアプリケーションが使うデータを保存する場所(ストレージ)のサイズを大きくしたい場合に直面する問題と、その解決策について解説する。

多くのアプリケーションは、データを永続的に保存する必要がある。Kubernetesでは、この永続的なストレージを「Persistent Volume(PV)」と「Persistent Volume Claim(PVC)」という仕組みで提供する。PVは実際のストレージリソースそのものであり、PVCはアプリケーションがそのPVを使いたいと要求する(Claimする)ものだ。例えるなら、PVが実際の土地で、PVCがその土地の利用申請書のようなものだ。そして、アプリケーションが稼働する「Pod」は、このPVCを通じてストレージを利用する。

アプリケーションには、データが失われると困る「ステートフル(有状態)」なものと、データが失われても問題ない「ステートレス(無状態)」なものがある。データベースのようにデータを保存し続けるアプリケーションはステートフルであり、Kubernetesではこれを管理するために「StatefulSet(ステートフルセット)」という特別な種類のリソースを使用する。StatefulSetは、各Podが固有の識別子を持ち、それぞれに対応する永続的なストレージ(PVC)を持つことを保証する。これは、複数のPodで構成されるデータベースなどが、それぞれ独自のデータを持ちながら協調して動作するために不可欠な機能だ。

さて、今回の問題は、StatefulSetで利用しているPVCのストレージサイズが、当初設定した30ギガバイト(GiB)では足りなくなってしまい、もっと大きくしたいという状況から始まる。例えば、VictoriaLogsというログ管理システムをKubernetes上で動かしており、そのログデータがどんどん増えてストレージがひっ迫してきた、というケースを想定する。

通常、Kubernetesのリソース設定はYAMLファイルというテキスト形式で行い、これをkubectl applyコマンドで適用することで変更を反映させる。しかし、StatefulSetの設定でPVCのサイズを指定する部分、具体的にはspec.volumeClaimTemplates[*].spec.resources.requests.storageという箇所は「Immutable(変更不可能)」とされている。これは、一度StatefulSetを作成したら、その部分の値を直接変更して再適用しようとしても、Kubernetesがそれを許可しないという意味だ。

記事では、このImmutableなフィールドを変更しようとすると、「Forbidden: updates to statefulset spec for fields other than ... are forbidden」というエラーが発生すると説明している。これは、StatefulSetの仕様上、特定のフィールド(例えばPodの複製数であるreplicasなど)以外は変更を許可しないというKubernetesの設計思想によるものだ。この制約があるため、単に設定ファイルの中のストレージサイズを変更してapplyしても、望む結果は得られない。

もし、StatefulSetではなく、データが永続的でなくてもよい「Deployment(デプロイメント)」という種類のリソースを使っていたら、ストレージの扱い方は少し異なる。その場合、手動で新しいPVCを作成し、既存のデータを移行して新しいPVCに接続するという選択肢もあったかもしれない。しかし、今回はStatefulSetであり、既存のPVに大切なデータが保存されているため、サービスを停止させたり、データの移行作業を行ったりすることは避けたい。そこで、サービスを停止させずに、既存のデータをそのまま利用してストレージサイズを拡張する方法を探ることになる。

この問題を解決するためには、いくつかの前提条件がある。まず、現在利用している「StorageClass(ストレージクラス)」が「AllowVolumeExpansion: True」をサポートしている必要がある。StorageClassとは、ストレージの種類や性能、再利用ポリシーなどを定義するKubernetesのリソースだ。例えば、AWSのEBS(Elastic Block Store)を利用する場合、gp2-retainという名前のStorageClassを作成し、その設定でallowVolumeExpansion: trueを指定しておくことで、後からストレージサイズを拡張できる状態にしておく必要がある。この設定がなければ、ストレージの拡張自体が技術的に不可能となる。

記事では、このStorageClassの定義例として、AWSのEBSをプロビジョニングするgp2-retainが示されている。また、古いEBSドライバーの利用に関する注意点も挙げられているが、今回のストレージ拡張という目的からは一時的に本筋ではないとされている。

実際に問題を再現してみよう。まず、シンプルなStatefulSetを作成する。このStatefulSetは、volumeClaimTemplatesを使って1GiBのストレージを要求するPVCを一つ作成するように定義されている。これをKubernetesにデプロイすると、data-demo-sts-0という名前のPVCが作成され、そのサイズが1GiBであることが確認できる。

次に、このStatefulSetの定義ファイルを編集し、volumeClaimTemplates内のストレージサイズを1GiBから2GiBに増やして、再度kubectl applyコマンドで適用してみる。すると、予想通り「Forbidden: updates to statefulset spec for fields other than ... are forbidden」というエラーメッセージが表示され、StatefulSetのストレージサイズを直接更新できないことが確認できた。

では、いよいよ解決策について説明する。この問題を回避し、サービスを停止させずにストレージサイズを拡張する方法は、以下の手順で実現できる。

最初のステップは、手動でPVCのサイズを変更することだ。StatefulSetの定義ファイルでは変更できなかったが、KubernetesのAPIを通じて直接PVCのリソースを編集することは可能だ。具体的には、kubectl edit pvc data-demo-sts-0のようなコマンドを使って、resources.requests.storageの値を1GiBから2GiBに変更し、保存してエディタを終了する。

このPVCの編集が保存されると、Kubernetesの背後で動いているコントローラーがこの変更を検知し、実際にストレージの拡張プロセスが開始される。kubectl describe pvcコマンドでPVCのイベントを確認すると、「ExternalExpanding」や「Resizing」といったメッセージが表示され、ストレージが拡張されている様子がわかる。数秒から数分後には「FileSystemResizeSuccessful」というメッセージが表示され、ストレージの拡張が完了したことが示される。このとき、kubectl get pvcで確認すると、PVCの容量が2GiBに増えていることが確認できるだろう。

そして、このストレージの拡張は、そのPVCを使っているPodの中でも自動的に反映される。Podの中に入ってdf -h /dataのようなコマンドでファイルシステムの使用状況を確認すると、ストレージが2GiBに増えていることが確認できる。これは、ストレージ拡張がアプリケーションのダウンタイムなしに行われていることを意味する。

しかし、この時点では、StatefulSetの定義ファイル自体のvolumeClaimTemplatesにはまだ古いサイズ(1GiB)が記述されたままだ。もしここで、この古い定義のStatefulSetを再度kubectl applyしようとすると、またしても変更禁止エラーが発生する。StatefulSetはあくまで「理想の状態」を維持しようとするため、手動で変更されたPVCのサイズと、StatefulSetの定義に記述されているPVCのサイズが食い違っていると、整合性が取れないと判断するのだ。

そこで、次の重要なステップは、StatefulSetを削除し、再作成することだ。ただし、通常の削除方法では、そのStatefulSetが管理しているPodやPVCも一緒に削除されてしまうため、大切なデータが失われてしまう可能性がある。これを避けるために、kubectl delete statefulset demo-sts --cascade=orphanという特別なコマンドを使用する。

--cascade=orphanオプションは非常に重要だ。これは、「StatefulSet自体は削除するが、そのStatefulSetが管理している子オブジェクト(この場合はPodとPVC)は削除せずに残しておく」という意味を持つ。これにより、StatefulSetは削除されるが、PVCとその中に保存されているデータ、そしてデータを利用しているPodはそのまま残り、サービスは停止しない。kubectl get podコマンドで確認すると、StatefulSetが削除された後もPodが引き続き「Running」状態であることがわかるだろう。

StatefulSetが削除され、しかしPodとPVCが残された状態になったら、最後に新しいストレージサイズ(2GiB)が記述されたStatefulSetの定義ファイルを再度kubectl applyコマンドで適用する。このとき、Kubernetesは新しいStatefulSetが作成され、既存のPodとPVCを再利用して、新しい定義が正しく反映された状態を確立する。StatefulSetのvolumeClaimTemplatesに記述された2GiBという値は、もはや変更を試みるのではなく、新しく作成されるStatefulSetの「理想の状態」としてKubernetesに認識される。

この一連の作業により、StatefulSetの持つImmutableな制約を回避しながら、既存のPVCのストレージサイズをダウンタイムなしで拡張し、データも安全に保持することができた。システムエンジニアにとって、このようなKubernetesの特性や制約を理解し、適切な解決策を見つける能力は非常に重要となる。この手順は、StatefulSetで永続ストレージを利用するアプリケーションの運用において、ストレージ容量の計画を誤った場合や、予期せぬデータ増大に対応する際に役立つ実践的な知識となるだろう。

関連コンテンツ

関連IT用語