Common Expression Language(CEL)でKubernetesのマニフェストのValidationを行う
こんにちは。@yashirookです。
2024年の4月にリリースされたKubernetes v1.30 で、Common Expression LanguageによるAdmission Control がGAになりました。
業務で行なっているKubernetesのクラスタ管理において魅力的な機能だったので、簡単に試してみた結果を記事にすることにしました。
KubernetesのAPIリソース作成時のValidationについて
従来は、KubernetesのAPIリソースに対する変更のValidationは、ValidatingAdmissionWebhook
を使って実現されていました。
ValidatingAdmissionWebhook
を利用する場合、Validationの処理を行うために自身で開発したアプリケーションや、Open Policy Agent(OPA)などを準備し、Webhookで送信されてきたHTTPリクエストからapplyされたObjectを取り出し、適切にレスポンスを返却する必要がありました。
ValidatingAdmissionPolicy
が登場したことにより、上記のような追加のコンポーネントを用意することなく、Common Expression Language(CEL)を使ったValidationを行うことができるようになりました。
ValidatingAdmissionPolicy
はKubernetes v1.26.0で alpha の機能として初めてリリースされました。以降、4回のリリースサイクルを経てGAとなり、比較的早期に昇格した機能だと感じています。
私個人としては、Kubernetes Projectの公式リソースにを使ってValidationを実現可能になったことで、以下の利点があると考えています。
- 信頼性
- Kubernetesが公式にサポートする機能であり、かつGAされたことで、本番導入時の保守・運用性の向上が見込めます
- Validationを行う際に他のコンポーネントが不要になり、通信や依存コンポーネントの障害による影響を受けにくい状態になりました
- 実装コスト
- Webhookを受け取りレスポンスを返却するために必要な要件を把握し、実装することはそれなりにボリュームがあり、クイックに機能を利用できることは魅力的だと思います
- 学習コスト
- 後に具体例を記載しますが、CELは比較的シンプルな文法で作成しようとするAPIのリソースのの内容を検証することができ、たとえばOpen Policy Agentで利用するRegoと比較すると学習コストは低いと感じます
それでは、実際に動作を検証する準備をしていきます。
環境構築
以下の検証環境で実施しました。
- OS: MacOS
- Docker Engine: 26.1.4
- Kubernetes: kind v0.24.0 go1.22.6 darwin/arm64
kind の構築
--image
オプションを利用し、Kubernetesのバージョンを指定してクラスタを作成します。
~ ❯ kind create cluster --image=kindest/node:v1.31.0
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.31.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a nice day! 👋
~ kind-kind/❯ kubectl get node
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane 76s v1.31.0
~ kind-kind/❯ kubectl version
Client Version: v1.31.0
Kustomize Version: v5.4.2
Server Version: v1.31.0
APIリソースの確認
~ kind-kind/❯ kubectl api-resources | grep validating
validatingadmissionpolicies admissionregistration.k8s.io/v1 false ValidatingAdmissionPolicy
validatingadmissionpolicybindings admissionregistration.k8s.io/v1 false ValidatingAdmissionPolicyBinding
validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration
validatingadmissionpolicies
, validatingadmissionpolicybindings
のAPIがそれぞれ存在し、admissionregistration.k8s.io
グループにおいてv1になっていることが確認できました。
従来のvalidatingwebhookconfigurations
も残っています。
Deployment
のレプリカ数を制限する
まず、簡単な例として Deployment
のレプリカ数を5以下に制限するValidatingAdmissionPolicy
を適用していきます。また以降、簡単のためにValidatingAdmissionPolicy
による制限のルールをポリシーと記載します。
APIリソースを確認したときに表示されていた、ValidatingAdmissionPolicy
, ValidatingAdmissionPolicyBinding
を利用してポリシーを適用していきます。
ValidatingAdmissionPolicy
の作成
ValidatingAdmissionPolicy
は、CELによる評価式(spec.validation
)や、評価を適用するAPIリソース(spec.matchConstraints
)などを定義するクラスタスコープのリソースです。
ValidatingAdmissionPolicy
リソースのマニフェストの用意
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: replicas-under-5
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
operations: ["CREATE", "UPDATE"]
validations:
- expression: "object.spec.replicas <= 5"
message: "Replicas must be less than or equal to 5"
spec.validations[0].expression
で記載されているのがValidationを行うための評価式です。object
がYAMLファイルで定義したAPIリソースの内容に相当し、.
つなぎで必要な要素を取り出して評価を行うことができます。
今回は特定の要素(int32)の値を比較する単純なルールですが、CELの組み込み関数やKubernetesのCELライブラリが使えるようになっており、多少複雑な評価式であっても記述できるようになっています。
また、spec.failurePolicy
はドキュメントにあまり記載されておらず、意味を掴みにくかったので、admissionregistration.k8s.io
のAPI定義を確認したところ、評価が何らかの原因で失敗した場合にどのように扱うかを定義するもののようで、デフォルトはFail
になっていました。
ValidatingAdmissionPolicy
リソースの作成
先ほど作成したリソースをクラスタに適用します。
~/workspace/test-kubernetes-validating-admission-policy/validatingadmissionpolicy kind-kind/❯ kubectl apply -f replicas-policy.yaml
validatingadmissionpolicy.admissionregistration.k8s.io/replicas-under-5 created
~/workspace/test-kubernetes-validating-admission-policy/validatingadmissionpolicy kind-kind/❯ kubectl get
validatingadmissionpolicies.admissionregistration.k8s.io
NAME VALIDATIONS PARAMKIND AGE
replicas-under-5 1 <unset> 41s
ValidatingAdmissionPolicyBinding
の作成
ValidatingAdmissionPolicyBinding
は、ValidatingAdmissionPolicy
で定義したポリシーを適用するリソースを定義するためのクラスタスコープのリソースです。
先ほど、Deployment
のレプリカ数に対するポリシーを作成しましたが、このリソースが作成されただけでは、実際のクラスタでValidationが行われることはありません。
今回は、environment: test
のラベルが付いたnamespaceに対してvalidationを行うような設定を定義したいと思います。
ValidatingAdmissionPolicyBinding
リソースのマニフェストの作成
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: replicas-under-5-binding
spec:
policyName: replicas-under-5
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: test
簡単にそれぞれ説明します。
- policyName
- 適用するポリシールール
- 先ほど作成した
ValidatingAdmissionPolicy
のリソース名を指定
- validationActions
- 失敗時のアクション
-
Deny
,Warn
,Audit
が選択可能で、今回は実際リクエストが拒否されることを確認するためDeny
に設定
- matchResources
- ポリシーを適用するリソースの指定
ValidatingAdmissionPolicy
リソースの作成
先ほど作成したリソースをクラスタに適用します。
~/workspace/test-kubernetes-validating-admission-policy/validatingadmissionpolicy kind-kind/❯ kubectl apply -f replicas-under-5-binding.yaml
validatingadmissionpolicybinding.admissionregistration.k8s.io/replicas-under-5-binding created
~/workspace/test-kubernetes-validating-admission-policy/validatingadmissionpolicy kind-kind/❯ kubectl get validatingadmissionpolicybindings.admissionregistration.k8s.io
NAME POLICYNAME PARAMREF AGE
replicas-under-5-binding replicas-under-5 <unset> 3s
ポリシーが適用されることの検証
今回は、default
namespaceでDeploymentを作成し、検証を実施しようと思います。
事前準備
ルールが適用されるようにするために、まずラベルの付与を行なっておきます。
~/workspace/test-kubernetes-validating-admission-policy/validatingadmissionpolicy kind-kind/❯ kubectl label ns default environment=test
namespace/default labeled
~/workspace/test-kubernetes-validating-admission-policy/validatingadmissionpolicy kind-kind/❯ kubectl get ns default --show-labels
NAME STATUS AGE LABELS
default Active 94m environment=test,kubernetes.io/metadata.name=default
無効なリクエストをapplyしたときの検証
レプリカ数が10のDeploymentの作成を試みます。今回はレプリカ数が5を超えるDeploymentの作成や更新は拒否している想定なので、このリソースの作成は失敗することが期待されます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: invalid-deployment
namespace: default
spec:
replicas: 10
selector:
matchLabels:
app: invalid-deployment
template:
metadata:
labels:
app: invalid-deployment
spec:
containers:
- name: invalid-deployment
image: nginx:latest
ports:
- containerPort: 80
このDeploymentリソースを作成しようとすると、期待通りエラーメッセージが出力されました。
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl apply -f deployment/invalid-deployment.yaml
The deployments "invalid-deployment" is invalid: : ValidatingAdmissionPolicy 'replicas-under-5' with binding 'replicas-under-5-binding' denied request: Replicas must be less than or equal to 5
また、ValidatingAdmissionPolicy
のspec.validations[0].message
で定義した以下のエラーメッセージが含まれていることも確認できました。
Replicas must be less than or equal to 5
有効なリクエストをapplyしたときの検証
念の為、レプリカ数が5のDeploymentの作成を試みます。今回はレプリカ数が5を超えるDeploymentの作成や更新を拒否している想定なので、このリソースの作成は成功することが期待されます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: valid-deployment
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: valid-deployment
template:
metadata:
labels:
app: valid-deployment
spec:
containers:
- name: valid-deployment
image: nginx:latest
ports:
- containerPort: 80
applyすると成功し、Podが起動していることを確認できました。
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl apply -f deployment/valid-deployment.yaml
deployment.apps/valid-deployment created
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl get po
NAME READY STATUS RESTARTS AGE
valid-deployment-57d6875f7-2l6tq 1/1 Running 0 11s
valid-deployment-57d6875f7-8fzmq 1/1 Running 0 11s
valid-deployment-57d6875f7-dz6tw 1/1 Running 0 11s
valid-deployment-57d6875f7-nhplm 1/1 Running 0 11s
valid-deployment-57d6875f7-t7nxp 1/1 Running 0 11s
上記の検証から、定義したポリシーが期待通り動作し、Validationが実現できたことが確認できました。
次節以降では、他にもよくありそうなユースケースとして、livenessProbeなどのヘルスチェックが作成されていること、privilegedオプションがされていないことを担保するポリシーをそれぞれ定義してみようと思います。
livenessProbeが設定されていないコンテナを制限する
livenessProbeを設定し、何かしらの形でコンテナプロセスに対するヘルスチェックが行われていることを担保したいというユースケースを想定して、ポリシーを実装していこうと思います。
リソースの作成
ValidatingAdmissionPolicy
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: probe-must-be-set
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
operations: ["CREATE", "UPDATE"]
variables:
- name: containers
expression: "object.spec.template.spec.containers"
validations:
- expression: "variables.containers.all(c, has(c.livenessProbe))"
message: "All containers must have a liveness probe"
ここで、**spec.variables
と、spec.validations[0].expression
で現れるcontainers.all
**内容について、それぞれ簡単に解説しておきます。
variables
expressionの可読性を高めるためにvariablesを定義することができます。今回は、containersをvariablesとして定義して、spec.validations[0].expression
においてvariables.contaienrs
でアクセスしています。
階層が深くなるリソースなどでは、関心領域を絞るために有効活用できるように思います。
containers.all
containersにはPodで作成しようとするそれぞれのコンテナの定義内容を含むobjectが含まれた配列になっており、配列に対してはall関数でそれぞれの要素に対する検証を行うことができます。
variables.containers.all(c, has(c.livenessProbe))
におけるc
は配列中の個々のオブジェクトに対応します。この動きは、JavaScriptなどで配列に対して利用するmap関数のようなものと思っていただいて差し支えないと思います。
全てのc
に対して、第2引数で設定した関数の真偽値がtrue
になった場合のみ、validationが通過することになります。
これを利用することで、Podで起動するコンテナの数によらず、可読性を保ちつつシンプルな方法でValidationが実現することができます。
ValidatingAdmissionPolicyBinding
以下のマニフェストを使用します。spec.policyName
を変更しています。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: privileged-binding
spec:
policyName: privileged
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: test
ポリシーが適用されていることの検証
以下のような、2つのコンテナを含むPodを起動するDeployment
の起動を試みます。app
, sidecar
コンテナを起動しますが、前者はlivenessProbeが設定されており、後者はされておりません。
apiVersion: apps/v1
kind: Deployment
metadata:
name: no-probe-deployment
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: no-probe-deployment
template:
metadata:
labels:
app: no-probe-deployment
spec:
containers:
- name: app
image: nginx:latest
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
- name: sidecar
image: nginx:latest
ports:
- containerPort: 8000
Deploymentの作成を試みます。livenessProbeが設定されていないコンテナを含むため、拒否されることが期待されます。
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl apply -f deployment/no-probe-deployment.yaml
The deployments "no-probe-deployment" is invalid: : ValidatingAdmissionPolicy 'probe-must-be-set' with binding 'probe-must-be-set-binding' denied request: All containers must have a liveness probe
想定通り、リソースの作成が拒否されることが確認できました。もちろん、2つ目のコンテナに livenessProbeを追加するとリソースは問題なく作成できるようになります。
やや冗長になるため、トグル以下に記載しているので、興味のある方は見てみてください。
両方のコンテナprobeを設定した場合
以下のように、Probeを追加したマニフェストを用意します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: probe-deployment
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: probe-deployment
template:
metadata:
labels:
app: probe-deployment
spec:
containers:
- name: app
image: nginx:latest
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
- name: sidecar
image: nginx:latest
ports:
- containerPort: 8000
livenessProbe:
httpGet:
path: /
port: 8000
上記の内容を適用すると、正常にリソースが作成されます。
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ k apply -f deployment/probe-deployment.yaml
deployment.apps/probe-deployment created
privilegedオプションが付与されているコンテナを制限する
不適切にprivilegedオプションが設定され、セキュリティ上の大きなリスクとなるワークロードが作成されないことを担保したいというユースケースを想定して、ポリシーを実装していこうと思います。
リソースの作成
ValidatingAdmissionPolicy
以下のようにポリシーを定義しました。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: privileged
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
operations: ["CREATE", "UPDATE"]
variables:
- name: containers
expression: object.spec.template.spec.containers
- name: securityContexts
expression: 'variables.containers.map(c, c.?securityContext)'
validations:
- expression: "variables.securityContexts.all(sc, sc.?privileged != optional.of(true))"
message: "Containers must not allow privileged mode"
今回は、securityContextという存在しない可能性のある(Optionalな)要素にアクセスしているため、少し工夫が必要です。
まず、map(c, c.?securityContext)
やall(sc, sc.?privileged != optional.of(true))
などmap関数が使われていますが、配列要素のオブジェクトがもつフィールドにアクセスするときに?
が指定されています。このような表記をすることで、Optionalなフィールドをの値が評価できます。
そして、privilegedオプションの値をoptional.of(true)
と比較しています。Optionalな値を比較するためには、単純にtrueと比較するのでなく、このような形にする必要があるとのことでした。
詳しくは、CEL - Optional ValuesのProposalに記載されています。
ValidatingAdmissionPolicyBinding
以下のマニフェストを使用します。spec.policyName
を変更しています。
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: privileged-binding
spec:
policyName: privileged
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: test
ポリシーが適用されていることの検証
以下のDeployment
を作成する
apiVersion: apps/v1
kind: Deployment
metadata:
name: privileged-deployment
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: privileged-deployment
template:
metadata:
labels:
app: privileged-deployment
spec:
containers:
- name: app
image: nginx:latest
ports:
- containerPort: 80
securityContext:
privileged: true
- name: sidecar
image: nginx:latest
ports:
- containerPort: 8000
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl apply -f deployment/privileged-deployment.yaml
The deployments "privileged-deployment" is invalid: : ValidatingAdmissionPolicy 'privileged' with binding 'privileged-binding' denied request: Containers must not allow privileged mode
app
コンテナに、privileged
オプションが有効化されているので、リソースの作成が失敗することを確認できました。
簡単なValidationであれば実現できるイメージが湧いてきましたね。次は、運用面を想定し、取得できるメトリクスを確認してみようと思います。
ValidatingAdmissionPolicy
に違反した時のアクション
前節までの検証結果で、簡単なValidationについては問題なく実装できることがわかりました。
適切なポリシーを組み込んでいくことで、ワークロードやクラスタの信頼性は高められるでしょう。
個人的には、Validationのルールを新たに導入する際には、鶴の一声的にいきなりさまざまなポリシーを有効化し、既存のワークフローに対して突然大きな変更をかけるよりは、段階的にチームが自己組織的に変更の必要性に気づき、チームの意思で対応が進んでいくような姿を好みます。
そんな個人的な嗜好性の上では、いきなりリクエストを拒否するのでなく、段階的にリリースするために取りうる手段が気になるところなので、以下に関連して調べてみたことを記載していきます。
validationActions
Validationに違反した際の挙動は、ValidatingAdmissionPolicyBinding
のspec.validationActions
で指定することができます。
これまでの検証ではすべて[Deny]
に設定しており、実際にポリシーに違反するリソースの作成は弾かれ失敗していました。
spec.validationActions
には、以下の値が設定できます。(詳しくは、公式ドキュメントを参照)
validationActions |
動作 |
---|---|
Deny |
リクエストを拒否し、クライアントにエラーを返す |
Warn |
リクエストを受け付け、クライアントにWarningを表示する |
Audit |
Validationの結果をAuditとして記録する。リクエストの受け付けには関与しない。 |
Deny
の他にも、Warn
やAudit
で運用をスタートすることも可能なようでした。
Warnは第一歩としては良いように思うのですが、多くの場合リリースはCI/CDのワークフローの中で実行されるので、Warnのメッセージに気づけるかどうかはCI/CDの実装とメッセージに対する意識に依存する問題は残るように思います。利用しているCI/CDの使用を踏まえ、運用設計が必要だと感じました。
次に、Audit
については、単純に有効にしただけでは、kube-apiserverのログの出力内容や、eventに変化は見られませんでした。
おそらく、kube-apiserverのauditを有効にしている場合にログに出力されるのではないかと思うのですが、今回の記事のスコープからは省略させていただきます。興味はあるので、どこかで追記もしくは別記事を行いたいと思います。
今回調べた範囲では、Validationを有効化していくにあたり、対象になるNamespaceやワークロードのスコープを小さくしたり、WarnモードやAuditモードを活用することが段階的に適用するための手段になりうるように感じました。
ValidatingAdmissionPolicy
に関連するメトリクス
次に、よりマクロな観点で運用状況を把握するため、ValidatingAdmissionPolicy
に関連して使用できるメトリクスを調べてみます。
公式ドキュメントによると、Kubernetes v1.30.0時点では以下のメトリクスが利用可能で、いずれもαです。(v1.31でのメトリクスのドキュメントは2024年9月9日現在ではまだ用意されていないようでしたが、新しいメトリクスが追加されていたり、早いペースで変更が加えられているように思います)
metrics | 説明 |
---|---|
apiserver_validating_admission_policy_check_duration_seconds | ポリシーの検証に要した時間 |
apiserver_validating_admission_policy_check_total | ポリシーの検証が行われた回数 |
apiserver_validating_admission_policy_definition_total | 作成されているポリシーの数 |
実際に、クラスタにPrometheusとkube-state-metricsをインストールしてブラウザからメトリクスを参照してみましょう。今回のインストール方法は、以下の折りたたみの中に記載しました。
Prometheusとkube-state-metricsのインストール
Prometheus
helmを使ってインストールします。
~ kind-kind/❯ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
"prometheus-community" has been added to your repositories
~ kind-kind/❯ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "ub-helm-repo" chart repository
...Successfully got an update from the "prometheus-community" chart repository
Update Complete. ⎈Happy Helming!⎈
~ kind-kind/❯ helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --create-namespace
NAME: prometheus
LAST DEPLOYED: Sat Sep 7 09:20:31 2024
NAMESPACE: monitoring
STATUS: deployed
REVISION: 1
NOTES:
kube-prometheus-stack has been installed. Check its status by running:
kubectl --namespace monitoring get pods -l "release=prometheus"
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus
instances using the Operator.
~ kind-kind/❯ kubectl get po -n monitoring
NAME READY STATUS RESTARTS AGE
alertmanager-prometheus-kube-prometheus-alertmanager-0 2/2 Running 0 62s
prometheus-grafana-7f7b8c64d-c2qgq 3/3 Running 0 78s
prometheus-kube-prometheus-operator-648f8c94ff-k92zs 1/1 Running 0 78s
prometheus-kube-state-metrics-5694684fbc-z76jg 1/1 Running 0 78s
prometheus-prometheus-kube-prometheus-prometheus-0 2/2 Running 0 62s
prometheus-prometheus-node-exporter-hclrs 1/1 Running 0 78s
Prometheusとともに、kube-state-metricsもデプロイできていることを確認できました。
NodePort経由でServiceにアクセスできるようにします。
apiVersion: v1
kind: Service
metadata:
name: prometheus-nodeport
namespace: monitoring
spec:
type: NodePort
ports:
- port: 9090
targetPort: 9090
nodePort: 30090
selector:
app.kubernetes.io/name: prometheus
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl apply -f monitoring/prometheus-nodeport-svc.yaml
service/prometheus-nodeport created
~/workspace/test-kubernetes-validating-admission-policy kind-kind/❯ kubectl get svc -n monitoring | grep NodePort
prometheus-nodeport NodePort 10.96.194.39 <none> 9090:30090/TCP 2m
~ kind-kind/❯ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kind-control-plane Ready control-plane 11h v1.31.0 192.168.247.2 <none> Debian GNU/Linux 12 (bookworm) 6.10.6-orbstack-00249-g92ad2848917c containerd://1.7.18
http://<INTERNAL-IP>:30090/ にブラウザからアクセスすると、PrometheusのUIが参照できます。
ポリシーの違反が見られた回数を可視化する
PrometheusのWebUIでたとえば以下のようなクエリを実行すると、validationのチェックが行われた数の時系列データを確認することができます。
作成したポリシーや、enforcement_actionのlabelがついており、それらで集計することができました。
sum by (policy, enforcement_action, namespace) (increase(apiserver_validating_admission_policy_check_total[1m]))
ドキュメントを読むと評価(check)が行われた回数が表示されるように見えるのですが、時系列データの挙動としてはvalidationが失敗した場合に限りカウンタがインクリメントされるような挙動が観測されました。
クエリの問題なのか、まだalpha版なのでkube-state-metricsの利用バージョンとKubernetes Metricsのドキュメントの間に差異があるのかもしれません(取れているLabelの値)。
より欲しい時系列データとしてはポリシーに抵触したリクエストが発生している回数になるので、欲しいものが見られているようには思えるのですが、やはりドキュメントの記載内容とギャップがあるのは気持ち悪さがあります。検証方法や環境起因による勘違いの可能性もあるので、有識者がいらっしゃいましたらご指摘いただけますと幸いです。
いずれにせよ、運用目線ではどんなリソースがどれほどvalidationに引っかかっているのか把握をしてアクションを打てるようにしておきたいですが、現状では実際に弾かれているリソースを直接特定できるような情報はLabelとして含まれていないようでした。
ただ、メトリクスもv1.30->v1.31の間で色々変更は入っていそうなので、この辺りの動向は注視して、アップデートがあれば加筆修正をしたいと思います。
まとめ
CELを使って、Kubernetesリソース作成・変更時のValidationを行う検証をしました。これまでのWebhookと比較して、かなり簡便かつシンプルにValidationを実現できていることを実感できたので、個人的にはCELで補える範囲のValidationではどんどんこちらを使っていきたいと思いました。
CELの機能や有効になっているライブラリの全体感はまだ把握できていないのですが、かなり多くのことはCELで実現できる印象を持っており、採用される事例も増えていくだろうなという印象を持ちました。
ただし、運用の面でより使いやすくなるための課題も見えました。
- ポリシーに抵触しているリクエストを特定し、クラスタ管理者側で対策できるようにする仕組みを用意する必要があるように感じました
- この方法として、今のところkube-apiserverのAuditログが目的にマッチしているのではないかという仮説をもっているので、これは近いうちに検証をしたいと思いました。
- CELのテストはCEL Playgroundなどを利用してできますが、実際に適用するマニフェストベースでE2E的にテストを実行できるツールなどが出てくると良いなと感じました。
ちなみに、MutatingAdmissionPolicy
もKEPでリクエストが上がってきており、1.32にalphaリリースがマイルストーンとして設定されているようです。
そのうち、Mutating(リクエストの書き換え)はValidationよりいくらか複雑に思いますが、こちらもCELを用いて実現できるようになるのかもしれません。
こちらの動向も楽しみです。
Discussion