【ITニュース解説】100 Days of DevOps: Day 60
2025年10月02日に「Dev.to」が公開したITニュース「100 Days of DevOps: Day 60」について初心者にもわかりやすく解説しています。
ITニュース概要
Kubernetesで、コンテナが消えてもデータが消えないPersistent Volumeのデプロイ方法を解説。PV, PVC, Pod, ServiceのYAMLを使い、ウェブアプリを構築・公開する具体的な手順と検証結果を示し、データの永続化を実現した。
ITニュース解説
Kubernetesは、コンテナ化されたアプリケーションのデプロイ、管理、スケーリングを自動化するための強力なプラットフォームである。システムエンジニアを目指す上で、この技術は避けて通れない重要な知識の一つと言える。特に、アプリケーションがデータを保存し、そのデータを永続的に保つ方法は、安定したサービスを提供する上で極めて重要だ。今回解説するのは、Kubernetesで永続的なストレージを扱うための「Persistent Volume(永続ボリューム)」という機能を使った、ウェブアプリケーションのデプロイ例である。
通常のKubernetesの「Pod(ポッド)」、つまりアプリケーションの実行単位は、何らかの理由で停止したり再起動したりすると、その中に保存されていたデータは消滅してしまう。これでは、データベースやユーザーがアップロードしたファイルなど、消えては困るデータを扱うアプリケーションには利用できない。そこで登場するのがPersistent Volumeの仕組みだ。
Persistent Volume(PV)は、Kubernetesクラスター内で利用可能な「物理的なストレージ」を抽象化したものだ。これは、クラスター管理者によって事前にプロビジョニング(準備)される。今回の例では、「pv.yaml」というファイルでこのPVを定義している。このファイルを見ると、「apiVersion: v1」とあり、Kubernetes APIのバージョンを指定している。「kind: PersistentVolume」は、これがPersistent Volumeリソースであることを示している。「metadata.name: pv-devops」はこのPVの名前で、「spec」の部分には具体的なストレージの特性が記述されている。 「storageClassName: manual」は、このPVが「manual」という名前のストレージクラスに属することを示し、特定のストレージタイプと紐付ける役割がある。「capacity.storage: 5Gi」は、このPVが5ギガバイトのストレージ容量を持つことを示している。「accessModes: ReadWriteOnce」は、このPVが一度に一つのPodから読み書き可能であることを意味する。他にも複数のPodから読み書きできるモードなどがあるが、このモードは最も一般的だ。「hostPath.path: /mnt/dba」は、このPVがKubernetesノード上の「/mnt/dba」というディレクトリをストレージとして利用することを指定している。これは開発環境やテスト環境で手軽に永続ストレージを試すためによく使われる方法で、本番環境ではNFSやクラウドストレージサービスなどが利用されることが多い。
次に登場するのが「Persistent Volume Claim(PVC)」だ。これは、PodがPVを直接利用するのではなく、アプリケーションが必要とするストレージの「要求」を定義するものだ。PVがクラスター管理者が提供するストレージそのものであるのに対し、PVCはアプリケーション側が「このくらいの容量のストレージがほしい」と要求するものだ。PodはPVCを通じてストレージを要求し、Kubernetesは定義された条件に合うPVをPVCに割り当てる。この仕組みにより、Podは特定の物理ストレージを意識することなく、必要なストレージ容量やアクセスモードを要求できるため、柔軟性が高まる。 今回の「pvc.yaml」ファイルでは、「kind: PersistentVolumeClaim」と定義されている。「metadata.name: pvc-devops」はこのPVCの名前だ。「spec」では、「storageClassName: manual」で特定のストレージクラスを要求し、「accessModes: ReadWriteOnce」で読み書きモードを指定している。「resources.requests.storage: 1Gi」は、このPVCが1ギガバイトのストレージを要求していることを示している。PVが5ギガバイトの容量を持っているのに対し、PVCが1ギガバイトしか要求していない点に注目しよう。これにより、PVの一部分だけをPVCに割り当てることができる。
これらのストレージの準備ができたところで、実際にアプリケーションを動かすPodを定義する「pod.yaml」を見てみよう。「kind: Pod」でPodリソースを定義し、「metadata.name: pod-devops」で名前を付けている。「spec」の中の「containers」では、実際に動かすコンテナの設定を行う。「name: container-devops」はコンテナ名、「image: httpd:latest」はウェブサーバーとして広く使われるApache HTTPサーバーの最新版イメージを使うことを指定している。「ports.containerPort: 80」は、コンテナが80番ポートで外部からの接続を待ち受けることを意味する。 重要なのは、「volumeMounts」と「volumes」の部分だ。「volumeMounts.name: devops-storage」は、このコンテナ内で「devops-storage」というボリュームを使うことを示し、「mountPath: /usr/local/apache2/htdocs」は、そのボリュームをコンテナ内のこのパスにマウント(接続)することを指定している。Apache HTTPサーバーのデフォルト設定では、このパスにウェブコンテンツを配置する。そして「volumes.name: devops-storage」が、「persistentVolumeClaim.claimName: pvc-devops」として、先ほど定義した「pvc-devops」というPVCをこのボリュームに割り当てることを指定している。これにより、ウェブアプリケーションが出力するデータは、Podが停止してもPVに保存され、永続的に保持されるようになるのだ。
最後に、「Service(サービス)」の定義である「service.yaml」について説明する。Podはそれぞれ固有のIPアドレスを持つが、Podが再起動したり、別のノードに移動したりすると、そのIPアドレスは変わってしまう可能性がある。これでは、外部からアプリケーションに安定してアクセスすることができない。Serviceは、このようなPodの変動に対応し、Podのグループに安定したアクセスポイントを提供する役割を担う。
「kind: Service」でServiceリソースを定義し、「metadata.name: web-devops」で名前を付けている。「spec」の中の「type: NodePort」は、ServiceをKubernetesクラスターの各ノードの特定のポートを通じて外部に公開することを意味する。このタイプを選ぶと、クラスター内のどのノードのIPアドレスに指定されたNodePortでアクセスしても、アプリケーションに到達できるようになる。「selector.app: web-devops」は、このServiceがどのPodにトラフィックを転送するかを決定する基準だ。pod.yamlでPodに「labels.app: web-devops」というラベルを付けていたが、このラベルを持つPodにトラフィックを送るようにServiceが設定されている。これにより、Serviceは自動的に適切なPodを見つけ、トラフィックをルーティングしてくれるのだ。「ports.protocol: TCP」はTCPプロトコルを使用し、「port: 80」はServiceがクラスター内で80番ポートで待ち受け、「targetPort: 80」は、そのトラフィックをPodの80番ポートに転送することを意味する。そして「nodePort: 30008」は、クラスターの各ノードの30008番ポートを通じて、このServiceにアクセスできるように設定している。
これらのリソースは、特定の順番でKubernetesクラスターに適用される必要がある。まず、kubectl apply -f pv.yamlコマンドでPersistent Volumeをデプロイする。これにより、クラスター内に永続ストレージの準備が完了する。次に、kubectl apply -f pvc.yamlでPersistent Volume Claimをデプロイし、Kubernetesが適切なPVをこのPVCに割り当てる。その後、kubectl apply -f service.yamlでServiceをデプロイする。ServiceはPodのエンドポイントを必要とするが、まだPodがないため、この時点ではトラフィックを転送する先がない状態だ。最後に、kubectl apply -f pod.yamlでPodをデプロイする。Podが起動すると、ServiceはPodのエンドポイントを検出し、トラフィックをルーティングできるようになる。この順番を守ることで、依存関係にあるリソースが正しく設定され、スムーズなデプロイが可能になる。
デプロイが完了したら、それぞれのコンポーネントが意図通りに動作しているかを「検証」する必要がある。kubectl get pv pv-devopsコマンドを実行すると、PVのステータスが表示される。「STATUS: Bound」と表示されていれば、PVがPVCに正常に結合されていることを意味する。「CLAIM: default/pvc-devops」は、defaultネームスペースのpvc-devopsというPVCに割り当てられていることを示す。同様に、kubectl get pvc pvc-devopsを実行すると、PVCのステータスが表示され、こちらも「STATUS: Bound」であれば、PVCがPVに正常に結合されていることを確認できる。さらに、「VOLUME: pv-devops」と表示され、どのPVに結びついているかが分かる。
kubectl get podsコマンドを実行すると、デプロイしたPodのリストとステータスが表示される。「NAME: pod-devops」、「READY: 1/1」、「STATUS: Running」と表示されていれば、Pod内のコンテナが正常に動作していることを示している。
最後に、kubectl describe service web-devopsコマンドでServiceの詳細を確認する。ここで最も重要なのは、「Endpoints」の項目だ。Serviceは、selectorに一致するPodのIPアドレスとポートを自動的に発見し、それをEndpointsとして表示する。今回の例では「Endpoints: 10.244.0.5:80」と表示されており、Serviceが正常にPodを検出して、外部からのトラフィックをそのPodに転送する準備ができていることが確認できる。これにより、ウェブアプリケーションがNodePort 30008を通じて外部に公開され、アクセス可能になったことを意味する。
このように、Kubernetesでは、永続ストレージを扱うためにPVとPVCという二つの抽象化レイヤーを使い、PodとServiceを組み合わせてアプリケーションを安定稼働させる。これらの仕組みを理解することは、現代のクラウドネイティブなアプリケーション開発において、システムエンジニアが持つべき基礎的なスキルの一つだ。一つ一つの設定が何を意味するのかを把握し、実際に手を動かしてデプロイと検証を繰り返すことで、より深くKubernetesの理解を深めることができるだろう。