StatefulSetとPersistentVolumeを使ってステートフルアプリケーションを動かす
StatefulSetの利用
StatefulSetはDeploymentに似たワークロードAPIですが、その名の通りステートフルワークロードを管理するためのさまざまな機能を備えています。重要な特徴は、管理下のPodの再スケジューリング(再作成)をまたがって同一のストレージをマウントできることです。一意のネットワーク識別子を付与されるなどほかの特徴もありますが本記事では割愛します。
以前のサンプルアプリケーションに含まれる、Articleサービスの開発用マニフェストからデータベースサービスのマニフェストを抜粋しました。さらに前回のものから変更してPV/PVCを利用するようになっています。
apiVersion: apps/v1 kind: StatefulSet metadata: labels: app: articledb name: articledb spec: replicas: 1 serviceName: articledb selector: matchLabels: app: articledb template: metadata: labels: app: articledb spec: containers: - name: postgres image: postgres:13-alpine env: - name: POSTGRES_PASSWORD value: 'postgres' - name: POSTGRES_DB value: 'article' - name: PGDATA value: /var/lib/postgresql/data/pgdata resources: {} ports: - name: postgres containerPort: 5432 protocol: TCP volumeMounts: - name: dbschema mountPath: /docker-entrypoint-initdb.d/schema.sql subPath: schema.sql - name: article-data mountPath: /var/lib/postgresql/data volumes: - name: dbschema configMap: name: article-init-db volumeClaimTemplates: - metadata: name: article-data spec: accessModes: - ReadWriteOnce storageClassName: local-path resources: requests: storage: 1Gi
マニフェストの前半部分、volumeClaimTemplatesより前はDeploymentの場合とほとんど同じですが、1か所だけDeploymentではあり得ないところがあります。9行目のserviceNameというフィールドです。これはこのStatefulSetに対応するServiceリソースを名前で参照しています。今回のマニフェストではそれほど重要ではないので、そういうフィールドがあることだけ触れておきます。また、よく見るとvolumeMountsの指定は2つあるのにvolumesには1つしかVolumeが定義されていません。これは後述のvolumeClaimTemplatesから作成されるVolumeをStatefulSetがPodを作成するときに追加するためです。
コンテナはpostgres:13-alpineイメージを利用しており、PostgreSQLの設定はDocker Hubの説明に従って認証パスワード*1やDB名を指定しています。また、PGDATA環境変数により、後述する永続ストレージにPostgreSQLのデータベースファイルを格納するよう設定しています。
*1 ここでは開発用マシンなどクローズドな環境で利用することを想定し、脆弱なパスワードをマニフェストに直接指定しています。本番環境などではSecretリソース等を利用して設定すべきです。そもそも、マニフェストに記載してgitリポジトリ等に保存すべきではありません。
volumeClaimTemplatesにPersistentVolumeClaimのテンプレートが設定されています。このテンプレートをもとに、StatefulSetのPodごとにPVCが作成されます。ここでは名前とアクセスモード、ストレージ容量を指定して、さらにstorageClassNameでlocal-pathを指定しています。これは動的プロビジョニングを利用して後述するlocal-path-provisionerによりPVを作成するためです。先にも書いた通り、ここでPVCのテンプレートを追加したVolumeはPodTemplateのvolumesフィールドには追加せず、.metadata.nameに指定した名前でvolumeMountsフィールドからマウント先を指定します。
サンプルアプリケーションでのPV/PVCの利用
サンプルアプリケーションにPV/PVCを導入して利用することで、データベースサービスの情報が保存されることを見てみましょう。
サンプルアプリケーションにPV/PVCをを導入する前に、利用するCSI Driverを導入します。利用するのはcsi-driver-host-pathというデモ・テスト用*2のCSI Driverです。リポジトリの手順に従って導入します。
*2 リポジトリの説明にある通り、hostPathを利用しているためノード障害などでデータが失われます。デモやCSI Driverのサンプルとして実装されており、本番環境での利用は想定されていません。
まずは、kubectlコマンドが利用できる場所に当該リポジトリを取得します。
git clone 'https://github.com/kubernetes-csi/csi-driver-host-path.git' cd csi-driver-host-path
用意されているスクリプトを実行します。
./deploy/kubernetes-latest/deploy.sh
以下のコマンドでPodが正常に稼働しているか確認できます。
$ kubectl get po -l app.kubernetes.io/name=csi-hostpathplugin NAME READY STATUS RESTARTS AGE csi-hostpathplugin-0 8/8 Running 0 8m33s
次に、StorageClassを作成します。リポジトリに含まれている設定例を利用します。
kubectl apply -f examples/csi-storageclass.yaml
ここで導入されるStorageClassリソースを見てみましょう。StorageClassの定義を抜粋すると、以下のようになっています。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: csi-hostpath-sc provisioner: hostpath.csi.k8s.io reclaimPolicy: Delete volumeBindingMode: Immediate allowVolumeExpansion: true
ここで、provisioner: hostpath.csi.k8s.ioというProvisionerを指定しており、この名前はcsi-driver-host-pathのデフォルトのProvisioner名*3になっています。
*3 具体的にはソースコードにデフォルト値の定義があります。
PVCを利用するマニフェストは、前回リポジトリのchapter6-use-pvcブランチで公開していますので、前回取得済みの場合は以下のコマンドでブランチを切り替えれば取得できます。
git pull git checkout chapter6-use-pvc
まだ取得していなかったり、上記コマンドでエラーが出たりする場合*4は別のディレクトリに以下のコマンドで新しくcloneしても構いません。
*4 ファイルを変更していると発生する場合があります。学習の成果ですね!
git clone --branch chapter6-use-pvc -- https://gitlab.com/creationline/thinkit-kubernetes-sample1-manifests.git
取得したリポジトリのoverlays/develop/statefulset-articledb.yamlを確認して上記と同様の内容であることを確認したら、次のコマンドでデプロイしましょう。
kubectl apply -k overlays/develop/
もし、前回でデプロイしたアプリケーションが残っていた場合は、以下のようなエラーが出るはずです。
The StatefulSet "accesscountdb" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden The StatefulSet "articledb" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden The StatefulSet "rankdb" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden
その場合は、以下のコマンドで一度アプリケーションを削除します。
kubectl delete -k overlays/develop/
削除後にPodが正しく終了しているか(kubectl get podの結果に表示されないか)を確認し、再度上記のコマンドでアプリケーションをデプロイします。
エラーなくデプロイできたら稼働状況を確認しましょう。まずはPodから見ていきます。
$ kubectl get pod NAME READY STATUS RESTARTS AGE pod/article-656969d8fc-qm9h9 1/1 Running 0 5m44s pod/rank-668b486b7-vhgfn 1/1 Running 0 5m44s pod/website-5c8ff7886b-drz6b 1/1 Running 0 5m44s pod/accesscount-7787ffd8f-rf2fw 1/1 Running 0 5m44s pod/articledb-0 1/1 Running 0 5m44s pod/accesscountdb-0 1/1 Running 0 5m44s pod/rankdb-0 1/1 Running 0 5m44s
正しく動作しているようです。PVとPVCも見てみましょう。
$ kubectl get pvc,pv NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/accesscount-data-accesscountdb-0 Bound pvc-b0f8e6e6-8bb3-45e1-af31-a99addf644f3 1Gi RWO local-path 5m44s persistentvolumeclaim/article-data-articledb-0 Bound pvc-c123f106-d200-4331-87e3-6b7670a12f97 1Gi RWO local-path 5m44s persistentvolumeclaim/rank-data-rankdb-0 Bound pvc-c4a2c4ff-2f77-49f8-897a-2bbfd686075f 1Gi RWO local-path 5m44s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-b0f8e6e6-8bb3-45e1-af31-a99addf644f3 1Gi RWO Delete Bound default/accesscount-data-accesscountdb-0 local-path 5m37s persistentvolume/pvc-c123f106-d200-4331-87e3-6b7670a12f97 1Gi RWO Delete Bound default/article-data-articledb-0 local-path 5m37s persistentvolume/pvc-c4a2c4ff-2f77-49f8-897a-2bbfd686075f 1Gi RWO Delete Bound default/rank-data-rankdb-0 local-path 5m36s
PV/PVCのSTATUSがそれぞれBound(割り当て済み)になっており、PVCはVOLUME列に、PVはCLAIM列に割り当てられたリソース名が表示されています。
デプロイされたことが確認できたら、記事を作成してみましょう。以下のようにcurlコマンドで記事エントリを作成します。<ノードのIPアドレス>は第3回で設定したIngressによるアクセスのためのアドレスで、ワーカーノードとして利用しているマシンのグローバルIPアドレスです。
$ curl -XPOST -H 'Content-Type: application/json' --data-binary '{"title":"Some Title","body":"Interesting body is here.","author":"John, Doe"}' http:///api/articles/ {"title":"Some Title","author":"John, Doe","body":"Interesting body is here."}
作成されたエントリを確認すると、
$ curl http://<ノードのIPアドレス>/api/articles/ [{"id":1,"title":"Some Title","author":"John, Doe"}]
のように返ってきます(記事作成コマンドを複数回実行しているとその回数分だけ登録されます)。
ここで、Podが何らかの原因で再起動しなければならなくなったとしましょう。状況を再現するため、次のコマンドで同じネームスペースのPodをすべて一度終了します。
$ kubectl get pod NAME READY STATUS RESTARTS AGE rank-668b486b7-fx94q 1/1 Running 0 41s article-656969d8fc-4qkgv 1/1 Running 0 41s website-5c8ff7886b-6pslv 1/1 Running 0 41s accesscount-7787ffd8f-hsqbt 1/1 Running 0 41s articledb-0 1/1 Running 0 35s rankdb-0 1/1 Running 0 34s accesscountdb-0 1/1 Running 0 33s
$ kubectl delete pod --all --wait=false pod "rank-668b486b7-fx94q" deleted pod "article-656969d8fc-4qkgv" deleted pod "website-5c8ff7886b-6pslv" deleted pod "accesscount-7787ffd8f-hsqbt" deleted pod "articledb-0" deleted pod "rankdb-0" deleted pod "accesscountdb-0" deleted
$ kubectl get pod NAME READY STATUS RESTARTS AGE rank-668b486b7-fx94q 1/1 Terminating 0 57s article-656969d8fc-4qkgv 1/1 Terminating 0 57s website-5c8ff7886b-6pslv 1/1 Terminating 0 57s accesscount-7787ffd8f-hsqbt 1/1 Terminating 0 57s articledb-0 1/1 Terminating 0 51s rankdb-0 1/1 Terminating 0 50s accesscountdb-0 1/1 Terminating 0 49s website-5c8ff7886b-55zb9 0/1 ContainerCreating 0 3s accesscount-7787ffd8f-r6lc7 0/1 ContainerCreating 0 3s rank-668b486b7-vlvn2 1/1 Running 0 3s article-656969d8fc-f59pw 1/1 Running 0 3s
deleteコマンドが実行された直後の状態を見るため、kubectl delete pod コマンドに--wait=falseオプションを付けて実行しています。直後のget podコマンドで一度すべてのPodがTerminatingになっていることが分かります。
少し待って正常に動作していることを確認します。
$ kubectl get pod NAME READY STATUS RESTARTS AGE rank-668b486b7-vlvn2 1/1 Running 0 43s article-656969d8fc-f59pw 1/1 Running 0 43s website-5c8ff7886b-55zb9 1/1 Running 0 43s accesscount-7787ffd8f-r6lc7 1/1 Running 0 43s accesscountdb-0 1/1 Running 0 27s articledb-0 1/1 Running 0 27s rankdb-0 1/1 Running 0 26s
Podが正常に動作していることを確認後、再度エントリを確認してみます。
$ curl http://<ノードのIPアドレス>/api/articles/ [{"id":1,"title":"Some Title","author":"John, Doe"}]
articleエントリが残っていること*5が確認できました。
*5 以前のマニフェストでデプロイして同じことを行うとPV/PVCが使われていないためarticleエントリが残りません。
おわりに
今回はPV/PVCとStatefulSetを用いて、ステートフルアプリケーションを扱う方法を解説しました。ステートフルアプリケーションの管理はアプリケーションそのものの特性や要件によって、さまざまなことを考慮する必要がありますが、Kubernetesで扱えないものではないので、特徴を理解して利用しましょう。また、今現在も活発に開発が行われている部分ですので、最新の情報をチェックして活用していきましょう。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Oracle Cloud Hangout Cafe Season7 #1「Kubnernetes 超入門」(2023年6月7日開催)
- CSIによるKubernetesのストレージ機能
- 詳解KubernetesにおけるPersistentVolume
- CNDT2020シリーズ:ヤフージャパンのインフラを支えるゼットラボが語るKubernetesストレージの深い話
- kustomizeで復数環境のマニフェストファイルを簡単整理
- KubernetesのWorkloadsリソース(その2)
- OpenShift:アプリケーションの構成と運用
- KubernetesのマニフェストをMagnumで実行する
- KubernetesのConfig&Storageリソース(その1)
- Kubernetesアプリケーションのモニタリングことはじめ