はじめに
フューチャーインスペース株式会社の岩崎です。
みなさん、ArgoCDは使っていますか?
業務でEKS(Elastic Kubernetes Service)クラスタにArgoCDをデプロイして、Kubernetesリソースを管理しています。
ArgoCDはGitOpsに則ったCDツールで、Web UIが優れていてKubernetesリソースの作成や更新がとても簡単で便利ですね。
しかし、ArgoCDからkube-prometheus-stackアプリケーションの削除時に、StatefulSetで作成されたGrafanaのPVC(Persistent Volume Claim 永続化ボリューム要求)も想定外に削除されてしまうことに気づきました。
※ StatefulSetはステートフルなアプリケーションを管理するオブジェクトで、本来はStatefulSetで関連付けられたPVCは削除されません。
GrafanaのPVCには、ダッシュボードやアラートなどの設定が入っているため、PVCが削除される度にGrafanaを設定しなおす必要がありました。
そこで、同環境でデプロイしているGatekeeperから、PVCの削除を防げないかを模索していたところ、「Validating Admission Webhook」でArgoCDによるPVCの削除リクエストを拒否できたので、設定から検証までを書いていきます。
環境/構成
- OS: Amazon Linux2
- EKS: 1.23
- ArgoCD: v2.4.15
- Gatekeeper: v3.10.0
Validating Admission Webhook
Validating Admission Webhookの前に、KubernetesのAdmission Controlを理解する必要があります。
調べるにあたって、以下の記事がとてもわかりやすかったので、載せておきます。
・https://blog.mosuke.tech/entry/2022/05/15/admission-webhook-1/
Validating Admission Webhookはリクエストがポリシーを満たしているか否かを監視し、ポリシーに反したリクエストが飛んで来た場合は、そのリクエストを拒否するように動作します。
そして、Validating Admission Webhookのポリシーの作成には、以下の3つが必要になります。
- Gatekeeper
- Constraint-Template
- Constraint
Gatekeeper
Gatekeeperは汎用的なポリシーエンジンOpen Policy Agent(OPA)をベースに作成されており、KubernetesのAdmission Controlの仕組みを活用し、Kubernetes APIへのリクエストに対して、Mutation(追加・更新・削除)、Validation(検証)などのポリシーをカスタマイズできます。
Constraint-Template
Constraint-TemplateはConstraintに必要なパラメータを用意します。Rego言語で記述したポリシーの定義を埋め込んだ、Constraint CRDを定義するテンプレートです。
Constraint
Constraintは条件に合致したリクエストを拒否します。Constraint-Templateで定義した内容に従って、監視対象のリソースの種類とアノテーションやラベルなどといったリクエストの拒否条件を記述します。
PVCの削除を防止するポリシーを作成
本題のPVCの削除を防止するポリシーを作成します。
今回はPVCリソースを削除しないポリシーをnamespace毎に管理する必要があったため、「特定のnamespaceにおけるPVCリソースを削除しないポリシー」を作成していきます。
Gatekeeperデプロイ
公式ドキュメント通りにgatekeeper.yamlをArgoCDでデプロイします。
なお、デフォルトのGatekeeperのValidating Admission Webhookでは、CREATE, UDPATE(作成、更新)を監視する設定になっているため、次のように、ValidatingWebhookConfigurationリソースのwebhooks.rules.operations
にDELETE(削除)を追加することで、削除リクエストも監視対象に設定する必要があります。
参考: https://open-policy-agent.github.io/gatekeeper/website/docs/customize-admission/#how-to-enable-validation-of-delete-operations
apiVersion: admissionregistration.k8s.io/v1 |
Constraint-Templateデプロイ
以下の通り、k8sdeletepvc
CRDとポリシーを作成します。
apiVersion: templates.gatekeeper.sh/v1 |
Constraint-Templateのspec.crd.spec.validation.openAPIV3Schema
以降には、ConstraintのParametersフィールドに関するスキーマを定義しています。また、spec.targets
以降にnamespaceと操作情報に関するポリシーの定義を記述しています。
input.review
はデプロイしているKubernetesリソースから値を取得し、input.parameters
は後に説明するConstraint.yamlのparametersフィールドの値を参照します。
上記のテンプレートでは、input.review.operation
でArgoCDのCREATE
やDELETE
などの操作情報を取得、input.review.namespace
で対象リソースのnamespaceを取得します。
これらをAND条件で判別することにより、「特定のnamespaceにおけるリソースを削除しないポリシー」を実現しています。
Rego言語を用いてポリシーを記述するにあたり、input.review
(Kubernetesリソース)から取得できる情報は公式ドキュメントにまとめられています。
また、Regoで記述したポリシーをテストできるサイトもあり、想定通りのポリシーになっているかの確認に便利だったので、載せておきます。
https://play.openpolicyagent.org/
Constraintデプロイ
constraint-template.yamlで定義したConstraint CRD : k8sdeletepvc
でポリシーの内容を明示的に宣言します。
以下のマニフェストをデプロイすることで、「monitoringのnamespaceにおけるPVCリソースの削除を防止するポリシー」が作成されます。
apiVersion: constraints.gatekeeper.sh/v1beta1 |
作成したポリシーの検証
作成したポリシーがmonitoringのnamespaceにおけるPVCリソースの削除を防止するかを確認します。
今回は、削除されたGrafanaのPVCstorage-kube-prometheus-stack-grafana-0
を対象にアプリケーションを削除してもPVCが残っているかの検証とDELETE以外のリクエストは問題なく承認されるかの検証を行います。
ArgoCDによる削除
storage-kube-prometheus-stack-grafana-0
はkube-prometheus-stackのサブチャートで定義されているので、kube-prometheus-stackのアプリケーションを削除します。
削除前のArgoCDの画面は以下の通りです。
ArgoCDの画面からアプリケーションをForegroundで削除した結果が以下になります。
エラーが起こり、APP CONDITIONSにて以下のエラーログが表示されます。
admission webhook "validation.gatekeeper.sh" denied the request: [pvc-constraint] : DELETE request detected in monitoring namespace. Cancel the request for PVC to prevent deletion
pvc-constraint(Constraint名)より、リクエストが拒否されたエラーログが表示され、Comstraint-Templateで記述した通りのエラー文があることから、作成したポリシーによってPVCリソースの削除リクエストが拒否されたことがわかります。
また、リクエストが拒否されると、ArgoCDが削除処理状態(一部リソースがSyncを受け付けない)になるため、アプリケーションをNon-cascading削除することで、ArgoCDの削除処理状態を外します。
これで、PVCを削除することなく、アプリケーションを削除できることを確認しました。
ちなみにですが、kubectlコマンドでも削除リクエストが拒否されることが確認できます。
kubectl delete pvc -n monitoring storage-kube-prometheus-stack-grafana-0 |
ArgoCDによるPVCの作成
先ほどの検証で、削除リクエストが想定通り拒否されることが確認できました。
では、削除以外のリクエストは承認されるかをPVCリソースのデプロイ(作成リクエスト)で検証を行います。
まず、storage-kube-prometheus-stack-grafana-0
を削除した状態が以下の通りです。
kube-prometheus-stackにSyncをかけてPVCをデプロイします。
無事にPVCが作成されたため、作成リクエストが無事に承認されたことが確認できました。
これは、Constraint-Templateで説明した通り、namespace
とoperation
をAND条件で判別しているため、operationがCREATE(作成)の場合は、リクエストが承認されてPVCがデプロイされます。
そのため、PVCがデプロイしている状態で、operationがDELETE(削除)の場合は、リクエストが拒否されるため、ArgoCDによる削除と同様のエラーが表示されます。
以上より、削除以外のリクエストは承認されることが確認できました。
一工夫加える(削除管理)
以上より、「各namespaceのPVCリソースの削除リクエストを拒否するポリシー」ができました。
しかし、この状態では1点だけ問題が発生してしまいます。それは、ポリシーを削除しない限り、Constraintの条件を満たしているリソースの削除ができないことです。
時と場合によっては、リソースを削除することはあると思います。リソースを削除する度にポリシーを外す運用では、外したポリシーを再度適用するのを忘れるリスクがあります。そのため、PVCリソースを削除するためのフラグをラベルで管理します。
Constraint-Template(削除用ラベル)
Constraint-Templateで作成したマニフェストを以下のように編集します。
-------- |
ラベルは複数個管理できるため、Parametersフィールドの定義もラベルを複数個管理できるようにarray型で定義しています。
また、削除用のラベルが複数個でも対応するように、配列の減算を用います。上記では、Constraintで定義したラベルをすべて含んでいたら、delete_labels
の要素が0になり、含んでいなければ、要素が1以上になります。今回は、Constraintで定義したラベルをすべて含む場合に削除リクエストを承認するので、count(delete_labels) > 0
で比較を行っています。
Constraint(削除用ラベル)
parametersフィールドにlabelsを追加するだけです。
spec: |
上記では、ラベルを1つ設定していますが、2つ以上設定することもできます。
以上で、PVCリソースにdelete-pvc
というラベルを含んでいれば、削除リクエストが承認されます。
これにより、想定外なPVCの削除がなくなり、ポリシーを外すことなくいつでもPVCリソースが削除可能な環境になりました。
まとめ
GatekeeperのValidating Admission Webhookを用いてPVCリソースの削除リクエストを拒否できました。バックアップを取得していれば、リソースの復元は可能ですが、リソースを予期せぬ削除から守ることも重要だと思います。
また、ポリシーを記述するRegoですが、Kubernetesリソースのデータ取得とルールの記述方法を押さえれば、自由にポリシーを作れるのではと感じました。
最後に、この検証を始めたときはKubernetesのAPIリクエストのことを全く理解しておらず、Rego言語も初めて知りました。何も知らない状態から調べていったので、少々時間がかかりましたが、Kubernetesへの理解が深まり、ポリシーを自分の手で作成できるようになったので、とても良い勉強になりました。
参考記事
- OPA/Gatekeeper
- Admission Webhook
- Constraint-Template、Constraint
- Rego