「Kyverno Chainsaw」で宣言的なE2Eテストを実施する

2024年10月29日(火)
長澤 翼
第8回の今回は、Kubernetes Operatorのエンドツーエンド(E2E)テストツールである「Kyverno Chainsaw」について紹介します。

はじめに

こんにちは。3-shakeのSreake事業部に所属する長澤翼(@toversus26)です。第8回目の今回は、Kubernetes Operatorのエンドツーエンド(E2E)テストツールである「Kyverno Chainsaw」について紹介します。

Kyverno Chainsawは、Kubernetes OperatorのE2Eの挙動を宣言的に定義してテストするCLIツールです。2023年12月にKubernetes向けのポリシーエンジンを開発しているKyvernoから初期バージョンがリリースされました。2024年9月17日時点でバージョンはv0.2.9となっています。Kyverno内で利用されていることもあり比較的活発に開発が進められています。

開発の背景

Kyverno Chainsawが開発された経緯は公式の「Kyverno Chainsaw - The ultimate end to end testing tool!」の記事にまとめられています。

Kubernetes Operatorの品質を担保するためにE2Eテストを実装することは重要です。Kubernetes Operatorでは主にGinkgoを利用したBehavior-Driven Development(BDD)を採用しており、実装が想定通りかを確認します。しかし、Kubernetes OperatorのE2Eテストを実装して保守するには、時間やコストが掛かります。E2Eテストのコストが高くなるとテストのカバー率が悪くなり、結果としてKubernetes Operatorの品質が低下してしまいます。とりわけOSSプロジェクトの場合、プロジェクト毎にE2Eテストの実装方法(テスト用の内部フレームワーク)が異なるため、学習コストが高くなりがちです。この課題を解決するために、YAML形式で宣言的にE2Eテストを書くことができるシンプルなKyverno Chainsawが開発されました。

基本的な使い方

Kyverno Chainsawは、実行するテストの流れをYAML形式で宣言します。例えば、Deploymentリソースを作成したときにPodが正しく作成されるかを確認するテストを書いてみましょう。

以下のファイルをchainsaw-test.yamlとして保存します。

01apiVersion: chainsaw.kyverno.io/v1alpha1
02kind: Test
03metadata:
04  name: deployment-to-pod
05spec:
06  # steps以下にテストの流れを定義
07  steps:
08  - try:
09    # Deploymentリソースを作成
10    - apply:
11        resource:
12          apiVersion: apps/v1
13          kind: Deployment
14          metadata:
15            name: nginx-deployment
16            labels:
17              app: nginx
18          spec:
19            replicas: 1
20            selector:
21              matchLabels:
22                app: nginx
23            template:
24              metadata:
25                labels:
26                  app: nginx
27              spec:
28                containers:
29                - name: nginx
30                  image: nginx:1.14.2
31                  ports:
32                  - containerPort: 80
33    # DeploymentからPodが作成され、Podが正常に動作していることを確認
34    - assert:
35        resource:
36          apiVersion: v1
37          kind: Pod
38          metadata:
39            labels:
40              app: nginx
41          status:
42            containerStatuses:
43            - name: nginx
44              ready: true
45              restartCount: 0
46              state:
47                running: {}
48            phase: Running

Kyverno ChainsawではTestと呼ばれるカスタムリソース風の設定ファイルにE2Eテストを定義します。

  • Testの中のsteps配下にテストの流れを記載する。stepには4つのアクションを記述できるが、今回はtryのみを利用
  • tryのアクションの下に実際に実行するオペレーションを定義する。オペレーションは複数用意されているが、今回の例ではapplyassertの基本的なオペレーションのみを利用している。tryのアクションの中で定義したオペレーションが1つでもエラーになると、テストは失敗となる
  • applyでDeploymentリソースを作成する。実際にリソースが作成されるためテストを実行する際にKubernetesクラスタが必要。今回はresourceにインラインでリソースを定義しているが、fileを利用することでリソースを定義したYAMLファイルを参照することもできる
  • assertでDeploymentリソースからPodが作成されることと、Podが正常な状態であることを確認する。 一定時間経過してもPodが期待通りの状態に遷移しない場合はタイムアウトエラーとなり、テストが失敗する

Kyverno chainsawでテストを実行するには、事前にKubernetesクラスタを準備します。kindなどのツールを利用してKubernetesクラスタを準備するか、既存のクラスタを利用する場合はコンテキストを正しく設定してください。

1kind create cluster

公式のインストール手順に従ってCLI をインストールして実行します。「.」を指定すると、現在のディレクトリから再起的にchainsaw-test.yamlを探してテストを実行します。

01❯ chainsaw test .
02Version: 0.2.9
03(...)
04Running tests...
05=== RUN   chainsaw
06=== PAUSE chainsaw
07=== CONT  chainsaw
08=== RUN   chainsaw/deployment-to-pod
09=== PAUSE chainsaw/deployment-to-pod
10=== CONT  chainsaw/deployment-to-pod
11    | 13:35:52 | deployment-to-pod | @chainsaw | CREATE    | OK    | v1/Namespace @ chainsaw-cuddly-elephant
12    | 13:35:52 | deployment-to-pod | step-1    | TRY       | BEGIN |
13    | 13:35:52 | deployment-to-pod | step-1    | APPLY     | RUN   | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
14    | 13:35:52 | deployment-to-pod | step-1    | CREATE    | OK    | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
15    | 13:35:52 | deployment-to-pod | step-1    | APPLY     | DONE  | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
16    | 13:35:52 | deployment-to-pod | step-1    | ASSERT    | RUN   | v1/Pod @ chainsaw-cuddly-elephant/*
17    | 13:35:53 | deployment-to-pod | step-1    | ASSERT    | DONE  | v1/Pod @ chainsaw-cuddly-elephant/*
18    | 13:35:53 | deployment-to-pod | step-1    | TRY       | END   |
19    | 13:35:53 | deployment-to-pod | step-1    | CLEANUP   | BEGIN |
20    | 13:35:53 | deployment-to-pod | step-1    | DELETE    | OK    | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
21    | 13:35:53 | deployment-to-pod | step-1    | CLEANUP   | END   |
22    | 13:35:53 | deployment-to-pod | @chainsaw | CLEANUP   | BEGIN |
23    | 13:35:53 | deployment-to-pod | @chainsaw | DELETE    | OK    | v1/Namespace @ chainsaw-cuddly-elephant
24    | 13:35:58 | deployment-to-pod | @chainsaw | CLEANUP   | END   |
25--- PASS: chainsaw (0.00s)
26    --- PASS: chainsaw/deployment-to-pod (6.27s)
27PASS
28Tests Summary...
29- Passed  tests 1
30- Failed  tests 0
31- Skipped tests 0
32Done.

今回の例を元に、Chainsawを実行したときのテストの流れを見ていきます。

  1. ランダムな名前で新しくテスト用のnamespaceを作成
  2. Deploymentリソースを作成
  3. Podリソースが作成され、正常に起動するのを待つ
  4. Deploymentリソースを削除
  5. テスト用のnamespaceを削除

chainsaw-test.yamlを書き換えて必ず失敗するテストに置き換えてみましょう。今回はassertの中で検証するPodのステータスのフェーズをCompletedに変更します。

1--- chainsaw-test.yaml
2+++ chainsaw-test.update.yaml
3@@ -45,4 +45,4 @@
4               restartCount: 0
5               state:
6                 running: {}
7-            phase: Running
8+            phase: Completed

再度テストを実行すると、今度はassertの段階でタイムアウトが発生してテストが失敗します。

01❯ chainsaw test .
02Version: 0.2.9
03(...)
04=== RUN   chainsaw
05=== PAUSE chainsaw
06=== CONT  chainsaw
07=== RUN   chainsaw/deployment-to-pod
08=== PAUSE chainsaw/deployment-to-pod
09=== CONT  chainsaw/deployment-to-pod
10    | 13:48:17 | deployment-to-pod | @chainsaw | CREATE    | OK    | v1/Namespace @ chainsaw-stirred-gopher
11    | 13:48:17 | deployment-to-pod | step-1    | TRY       | BEGIN |
12    | 13:48:17 | deployment-to-pod | step-1    | APPLY     | RUN   | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
13    | 13:48:17 | deployment-to-pod | step-1    | CREATE    | OK    | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
14    | 13:48:17 | deployment-to-pod | step-1    | APPLY     | DONE  | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
15    | 13:48:17 | deployment-to-pod | step-1    | ASSERT    | RUN   | v1/Pod @ chainsaw-stirred-gopher/*
16    | 13:48:47 | deployment-to-pod | step-1    | ASSERT    | ERROR | v1/Pod @ chainsaw-stirred-gopher/*
17        === ERROR
18        ----------------------------------------------------------------
19        v1/Pod/chainsaw-stirred-gopher/nginx-deployment-77d8468669-96d9b
20        ----------------------------------------------------------------
21        * status.phase: Invalid value: "Running": Expected value: "Completed"
22 
23        --- expected
24        +++ actual
25        @@ -3,13 +3,27 @@
26         metadata:
27           labels:
28             app: nginx
29        +  name: nginx-deployment-77d8468669-96d9b
30           namespace: chainsaw-stirred-gopher
31        +  ownerReferences:
32        +  - apiVersion: apps/v1
33        +    blockOwnerDeletion: true
34        +    controller: true
35        +    kind: ReplicaSet
36        +    name: nginx-deployment-77d8468669
37        +    uid: 85049a1c-0307-48eb-aa35-ff1805f0bdfd
38         status:
39           containerStatuses:
40        -  - name: nginx
42        +    image: docker.io/library/nginx:1.14.2
43        +    imageID: docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
44        +    lastState: {}
45        +    name: nginx
46             ready: true
47             restartCount: 0
48        +    started: true
49             state:
50        -      running: {}
51        -  phase: Completed
52        +      running:
53        +        startedAt: "2024-09-15T04:48:18Z"
54        +  phase: Running
55    | 13:48:47 | deployment-to-pod | step-1    | TRY       | END   |
56    | 13:48:47 | deployment-to-pod | step-1    | CLEANUP   | BEGIN |
57    | 13:48:47 | deployment-to-pod | step-1    | DELETE    | OK    | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
58    | 13:48:47 | deployment-to-pod | step-1    | CLEANUP   | END   |
59    | 13:48:47 | deployment-to-pod | @chainsaw | CLEANUP   | BEGIN |
60    | 13:48:47 | deployment-to-pod | @chainsaw | DELETE    | OK    | v1/Namespace @ chainsaw-stirred-gopher
61    | 13:48:52 | deployment-to-pod | @chainsaw | CLEANUP   | END   |
62--- FAIL: chainsaw (0.00s)
63    --- FAIL: chainsaw/deployment-to-pod (35.29s)
64FAIL
65Tests Summary...
66- Passed  tests 0
67- Failed  tests 1
68- Skipped tests 0
69Done with failures.
70Error: some tests failed

Chainsawは失敗したテストを調査するときに必要な情報を併せて表示してくれます。

  • テストが失敗した原因を表示する。今回の例で言うとstatus.phase: Invalid value: "Running": Expected value: "Completed"の部分。PodのフェーズがCompletedとなることを期待しているが、実際にはRunningであることが分かる
  • 実際のPodリソースとassertで指定したマニフェストのメタデータとステータスの差分を表示する。今回はPodの名前やOwnerReference、Podステータスの一部の情報を記載していないため、それらの差分も一緒に表示されている

他にもcatchアクションを定義することでPod EventやPodの状態、Podのログなど追加で表示したい情報をカスタマイズできます。

01--- chainsaw-test.yaml
02+++ chainsaw-test.update.yaml
03@@ -46,3 +46,11 @@
04               state:
05                 running: {}
06             phase: Completed
07+    catch:
08+    - events: {}
09+    - get:
10+        apiVersion: v1
11+        kind: Pod
12+        selector: app=nginx
13+    - podLogs:
14+        selector: app=nginx

Chainsawのテストを実行すると、リソースを削除する前に上記で追加した情報を表示してくれます。

01=== RUN   chainsaw
02=== PAUSE chainsaw
03=== CONT  chainsaw
04=== RUN   chainsaw/deployment-to-pod
05=== PAUSE chainsaw/deployment-to-pod
06=== CONT  chainsaw/deployment-to-pod
07    | 14:09:53 | deployment-to-pod | @chainsaw | CREATE    | OK    | v1/Namespace @ chainsaw-allowing-terrier
08    | 14:09:53 | deployment-to-pod | step-1    | TRY       | BEGIN |
09    | 14:09:53 | deployment-to-pod | step-1    | APPLY     | RUN   | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
10    | 14:09:53 | deployment-to-pod | step-1    | CREATE    | OK    | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
11    | 14:09:53 | deployment-to-pod | step-1    | APPLY     | DONE  | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
12    | 14:09:53 | deployment-to-pod | step-1    | ASSERT    | RUN   | v1/Pod @ chainsaw-allowing-terrier/*
13    | 14:10:23 | deployment-to-pod | step-1    | ASSERT    | ERROR | v1/Pod @ chainsaw-allowing-terrier/*
14    (...)
15    | 14:10:23 | deployment-to-pod | step-1    | TRY       | END   |
16    | 14:10:23 | deployment-to-pod | step-1    | CATCH     | BEGIN |
17    | 14:10:23 | deployment-to-pod | step-1    | CMD       | RUN   |
18        === COMMAND
19        /opt/homebrew/bin/kubectl get events -n chainsaw-allowing-terrier
20    | 14:10:24 | deployment-to-pod | step-1    | CMD       | LOG   |
21        === STDOUT
22        LAST SEEN   TYPE     REASON              OBJECT                                   MESSAGE
23        31s         Normal   Scheduled           pod/nginx-deployment-77d8468669-2z4rx    Successfully assigned chainsaw-allowing-terrier/nginx-deployment-77d8468669-2z4rx to kind-control-plane
24        30s         Normal   Pulled              pod/nginx-deployment-77d8468669-2z4rx    Container image "nginx:1.14.2" already present on machine
25        30s         Normal   Created             pod/nginx-deployment-77d8468669-2z4rx    Created container nginx
26        30s         Normal   Started             pod/nginx-deployment-77d8468669-2z4rx    Started container nginx
27        31s         Normal   SuccessfulCreate    replicaset/nginx-deployment-77d8468669   Created pod: nginx-deployment-77d8468669-2z4rx
28        31s         Normal   ScalingReplicaSet   deployment/nginx-deployment              Scaled up replica set nginx-deployment-77d8468669 to 1
29    | 14:10:24 | deployment-to-pod | step-1    | CMD       | DONE  |
30    | 14:10:24 | deployment-to-pod | step-1    | CMD       | RUN   |
31        === COMMAND
32        /opt/homebrew/bin/kubectl get pods -l app=nginx -n chainsaw-allowing-terrier
33    | 14:10:24 | deployment-to-pod | step-1    | CMD       | LOG   |
34        === STDOUT
35        NAME                                READY   STATUS    RESTARTS   AGE
36        nginx-deployment-77d8468669-2z4rx   1/1     Running   0          31s
37    | 14:10:24 | deployment-to-pod | step-1    | CMD       | DONE  |
38    | 14:10:24 | deployment-to-pod | step-1    | CMD       | RUN   |
39        === COMMAND
40        /opt/homebrew/bin/kubectl logs --prefix -l app=nginx -n chainsaw-allowing-terrier --all-containers
41    | 14:10:24 | deployment-to-pod | step-1    | CMD       | DONE  |
42    | 14:10:24 | deployment-to-pod | step-1    | CATCH     | END   |
43    | 14:10:24 | deployment-to-pod | step-1    | CLEANUP   | BEGIN |
44    | 14:10:24 | deployment-to-pod | step-1    | DELETE    | OK    | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
45    | 14:10:24 | deployment-to-pod | step-1    | CLEANUP   | END   |
46    | 14:10:24 | deployment-to-pod | @chainsaw | CLEANUP   | BEGIN |
47    | 14:10:24 | deployment-to-pod | @chainsaw | DELETE    | OK    | v1/Namespace @ chainsaw-allowing-terrier
48    | 14:10:29 | deployment-to-pod | @chainsaw | CLEANUP   | END   |
49--- FAIL: chainsaw (0.00s)
50    --- FAIL: chainsaw/deployment-to-pod (35.56s)
51FAIL
52Tests Summary...
53- Passed  tests 0
54- Failed  tests 1
55- Skipped tests 0
56Done with failures.
57Error: some tests failed

ユースケース

Kyverno ChainsawはKubernetes Operator向けに開発されたツールですが、Kubernetesクラスタを運用する際にも利用できます。クラスタ管理者はKubernetesクラスタやクラスタ上で動作するサードパーティ製のツールを運用しています。これらのバージョン更新や設定変更の後に、想定通りに動作しているかを確認します。この動作確認をGinkgoなどを利用したE2Eテストとして実装して自動化している人たちも一部いますが、ほとんどが手動だと思います。あらかじめKyverno Chainsawで確認したい項目を宣言的に定義しておくことで、定常作業を効率化できます。

  • マネージドKubernetesクラスタやアドオンのバージョン更新後の動作確認
    • Pod間で通信できるか
    • Podに外部ボリュームをマウントできるか
  • External Secrets OperatorのExternalSecretsリソースで外部のシークレット管理ツールに保存した秘密情報をSecretに同期できるか
  • IstioのVirtualServiceとテスト用のPodを作成して外部ロードバランサ経由で疎通できるか
  • KarpenterがPending状態のPodを検知してノードを自動スケールできるか
  • Validating Admission Policy(VAP)で定義したポリシーが期待通りに動作するか

公式ドキュメントのVAPのポリシーを例にKyverno ChainsawでVAPをテストしてみましょう。今回確認するVAPのルールでは、Deploymentのレプリカ数が5以下であることを強制しています。以下のVAPのリソースは既に作成されている前提でテストを書いてみましょう。

01apiVersion: admissionregistration.k8s.io/v1
02kind: ValidatingAdmissionPolicy
03metadata:
04  name: replicalimit-policy.example.com
05spec:
06  failurePolicy: Fail
07  matchConstraints:
08    resourceRules:
09    - apiGroups:   ["apps"]
10      apiVersions: ["v1"]
11      operations:  ["CREATE", "UPDATE"]
12      resources:   ["deployments"]
13  validations:
14    - expression: "object.spec.replicas <= 5"
15chainsaw-test.yamlにE2Eテストを定義します。
16apiVersion: chainsaw.kyverno.io/v1alpha1
17kind: Test
18metadata:
19  name: vap-replicalimit-policy
20spec:
21  # テスト時に作成するnamespaceの名前を指定
22  namespace: vap-replicalimit-policy
23  steps:
24  - try:
25    - apply:
26        resource:
27          apiVersion: admissionregistration.k8s.io/v1
28          kind: ValidatingAdmissionPolicyBinding
29          metadata:
30            name: replicalimit-policy-test.example.com
31          spec:
32            policyName: replicalimit-policy.example.com
33            validationActions: [Deny]
34            # テスト時に作成したnamespaceに対してポリシーを適用
35            matchResources:
36              namespaceSelector:
37                matchLabels:
38                  kubernetes.io/metadata.name: vap-replicalimit-policy
39  # VAPが適用されるまで待機
40  - try:
41    - sleep:
42        duration: 30s
43  # ポリシーに準拠したレプリカ数を指定したDeploymentを作成
44  # Deploymentが作成できれば良いのでassertは不要
45  - try:
46    - apply:
47        resource:
48          apiVersion: apps/v1
49          kind: Deployment
50          metadata:
51            name: nginx-1
52          spec:
53            replicas: 1
54            selector:
55              matchLabels:
56                app: nginx-1
57            template:
58              metadata:
59                labels:
60                  app: nginx-1
61              spec:
62                containers:
63                - name: nginx
64                  image: nginx:1.14.2
65  # ポリシーに準拠していないレプリカ数を指定したDeploymentを作成
66  - try:
67    - apply:
68        resource:
69          apiVersion: apps/v1
70          kind: Deployment
71          metadata:
72            name: nginx-10
73          spec:
74            replicas: 10
75            selector:
76              matchLabels:
77                app: nginx-10
78            template:
79              metadata:
80                labels:
81                  app: nginx-10
82              spec:
83                containers:
84                - name: nginx
85                  image: nginx:1.14.2
86        expect:
87        - match:
88            apiVersion: apps/v1
89            kind: Deployment
90            metadata:
91              name: nginx-10
92          check:
93            # エラーとなる場合はテストが成功
94            ($error != null): true

今回のテストでは、事前に作成されているVAPが想定通りの挙動かを確認します。

  • テストを実行するnamespaceの名前を明示的に指定し、Validating Admission Policy Bindingを作成してVAPをテストを実行するnamespaceに紐付ける
  • ポリシーに準拠していないDeploymentを作成するテストでは、Operation checkの機能を利用してリソースの作成に失敗することを確認する。このようにKyverno Chainsawでは操作に失敗するテストも定義できる

E2Eテストを実行します。レプリカ数が10のDeploymentを作成するテストでエラーが発生していますが、テストには通過していることが分かります。リソースを反映したときのエラーメッセージも同時に表示してくれるため、想定したVAPのルールでエラーが発生しているかも確認できます。

01❯ chainsaw test .
02Version: 0.2.9
03(...)
04Running tests...
05=== RUN   chainsaw
06=== PAUSE chainsaw
07=== CONT  chainsaw
08=== RUN   chainsaw/vap-replicalimit-policy
09=== PAUSE chainsaw/vap-replicalimit-policy
10=== CONT  chainsaw/vap-replicalimit-policy
11    | 14:50:29 | vap-replicalimit-policy | @chainsaw | CREATE    | OK    | v1/Namespace @ vap-replicalimit-policy
12    | 14:50:29 | vap-replicalimit-policy | step-1    | TRY       | BEGIN |
13    | 14:50:29 | vap-replicalimit-policy | step-1    | APPLY     | RUN   | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
14    | 14:50:29 | vap-replicalimit-policy | step-1    | CREATE    | OK    | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
15    | 14:50:29 | vap-replicalimit-policy | step-1    | APPLY     | DONE  | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
16    | 14:50:29 | vap-replicalimit-policy | step-1    | TRY       | END   |
17    | 14:50:29 | vap-replicalimit-policy | step-2    | TRY       | BEGIN |
18    | 14:50:29 | vap-replicalimit-policy | step-2    | SLEEP     | RUN   |
19    | 14:50:59 | vap-replicalimit-policy | step-2    | SLEEP     | DONE  |
20    | 14:50:59 | vap-replicalimit-policy | step-2    | TRY       | END   |
21    | 14:50:59 | vap-replicalimit-policy | step-3    | TRY       | BEGIN |
22    | 14:50:59 | vap-replicalimit-policy | step-3    | APPLY     | RUN   | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
23    | 14:50:59 | vap-replicalimit-policy | step-3    | CREATE    | OK    | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
24    | 14:50:59 | vap-replicalimit-policy | step-3    | APPLY     | DONE  | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
25    | 14:50:59 | vap-replicalimit-policy | step-3    | TRY       | END   |
26    | 14:50:59 | vap-replicalimit-policy | step-4    | TRY       | BEGIN |
27    | 14:50:59 | vap-replicalimit-policy | step-4    | APPLY     | RUN   | apps/v1/Deployment @ vap-replicalimit-policy/nginx-10
28    | 14:50:59 | vap-replicalimit-policy | step-4    | CREATE    | WARN  | apps/v1/Deployment @ vap-replicalimit-policy/nginx-10
29        === ERROR
30        deployments.apps "nginx-10" is forbidden: ValidatingAdmissionPolicy 'replicalimit-policy.example.com' with binding 'replicalimit-policy-test.example.com' denied request: failed expression: object.spec.replicas <= 5
31    | 14:50:59 | vap-replicalimit-policy | step-4    | APPLY     | DONE  | apps/v1/Deployment @ vap-replicalimit-policy/nginx-10
32    | 14:50:59 | vap-replicalimit-policy | step-4    | TRY       | END   |
33    | 14:50:59 | vap-replicalimit-policy | step-3    | CLEANUP   | BEGIN |
34    | 14:50:59 | vap-replicalimit-policy | step-3    | DELETE    | OK    | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
35    | 14:50:59 | vap-replicalimit-policy | step-3    | CLEANUP   | END   |
36    | 14:50:59 | vap-replicalimit-policy | step-1    | CLEANUP   | BEGIN |
37    | 14:50:59 | vap-replicalimit-policy | step-1    | DELETE    | OK    | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
38    | 14:50:59 | vap-replicalimit-policy | step-1    | CLEANUP   | END   |
39    | 14:50:59 | vap-replicalimit-policy | @chainsaw | CLEANUP   | BEGIN |
40    | 14:51:00 | vap-replicalimit-policy | @chainsaw | DELETE    | OK    | v1/Namespace @ vap-replicalimit-policy
41    | 14:51:05 | vap-replicalimit-policy | @chainsaw | CLEANUP   | END   |
42--- PASS: chainsaw (0.00s)
43    --- PASS: chainsaw/vap-replicalimit-policy (35.36s)
44PASS
45Tests Summary...
46- Passed  tests 1
47- Failed  tests 0
48- Skipped tests 0
49Done.

このように、Kyverno ChainsawはVAPの挙動を確認するテストツールとしても利用できます。VAPのテストツールとしては、同じくKyvernoが開発しているkyverno testがあります。なおkyverno testでテストを実行する際にKubernetesクラスタは不要ですが、2024年9月17日時点で以下の問題があり、痒いところに手が届いていませんでした。

その点、Kyverno ChainsawはE2Eテストのレポート機能が優れているため、十分に活用できます。また、汎用的な作りをしているため他のユースケースとも併用可能です。

おわりに

今回は、Kyverno Chainsawによる宣言的なE2Eテストの利用例を見てきました。Kubernetes Operatorの開発以外でもクラスタ管理者の定常作業の動作確認としても利用できます。基本的な機能の学習コストは低く、YAMLのマニフェストを管理するだけで良いので、メンテナンス性も高く感じました。

また、今回は紹介していませんが、Kyvernoの機能を元にした少し複雑な機能もあります。ただ、複雑な機能を使うくらいならGoでテストコードを記述した方が柔軟かつ見通しも良いはずです。

Kyverno Chainsawを使ってE2Eテストの導入のハードルを下げてみてはどうでしょうか。

株式会社スリーシェイク Sreake事業部
スマホ向けゲームのKubernetesエンジニアを経て、2023年3月に3-shakeのSreake事業部にJoin。Kubernetesやエコシステム周りを観察するのが好きです。
---
スリーシェイクは、ITインフラ領域の技術力に強みをもつテクノロジーカンパニーです。SREコンサルティング事業「Sreake」では、AWS/Google Cloud/Kubernetesに精通したプロフェッショナルが技術戦略から設計・開発・運用を一貫してサポートしています。また、ノーコード型ETLツール「Reckoner」、フリーランスエンジニア特化型人材紹介サービス「Relance」、セキュリティサービス「Securify」を提供しています。
会社HP: https://3-shake.com/

連載バックナンバー

仮想化/コンテナ技術解説
第13回

「kubeshark」でKubernetesのトラフィックをリアルタイムに可視化する

2025/3/7
第13回の今回は、KubernetesのAPIトラフィックアナライザである「kubeshark」によるリアルタイムのトラフィック可視化について解説します。
仮想化/コンテナ技術解説
第12回

「OpenClarity」によるk8sワークロードの脆弱性スキャン

2025/2/21
第12回の今回は、セキュリティリスクを検出するOSSツール「OpenClarity」におけるKubernetesワークロードの脆弱性スキャンを解説します。
仮想化/コンテナ技術解説
第11回

「Longhorn」で分散ブロックストレージを簡単に管理する

2025/2/7
第11回の今回は、Kubernetes向けの分散ブロックストレージ「Longhorn」の特徴と導入方法、バックアップ・リストアの手順を解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています