「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として保存します。

apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
  name: deployment-to-pod
spec:
  # steps以下にテストの流れを定義
  steps:
  - try:
    # Deploymentリソースを作成
    - apply:
        resource:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: nginx-deployment
            labels:
              app: nginx
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: nginx
            template:
              metadata:
                labels:
                  app: nginx
              spec:
                containers:
                - name: nginx
                  image: nginx:1.14.2
                  ports:
                  - containerPort: 80
    # DeploymentからPodが作成され、Podが正常に動作していることを確認
    - assert:
        resource:
          apiVersion: v1
          kind: Pod
          metadata:
            labels:
              app: nginx
          status:
            containerStatuses:
            - name: nginx
              ready: true
              restartCount: 0
              state:
                running: {}
            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クラスタを準備するか、既存のクラスタを利用する場合はコンテキストを正しく設定してください。

kind create cluster

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

❯ chainsaw test .
Version: 0.2.9
(...)
Running tests...
=== RUN   chainsaw
=== PAUSE chainsaw
=== CONT  chainsaw
=== RUN   chainsaw/deployment-to-pod
=== PAUSE chainsaw/deployment-to-pod
=== CONT  chainsaw/deployment-to-pod
    | 13:35:52 | deployment-to-pod | @chainsaw | CREATE    | OK    | v1/Namespace @ chainsaw-cuddly-elephant
    | 13:35:52 | deployment-to-pod | step-1    | TRY       | BEGIN |
    | 13:35:52 | deployment-to-pod | step-1    | APPLY     | RUN   | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
    | 13:35:52 | deployment-to-pod | step-1    | CREATE    | OK    | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
    | 13:35:52 | deployment-to-pod | step-1    | APPLY     | DONE  | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
    | 13:35:52 | deployment-to-pod | step-1    | ASSERT    | RUN   | v1/Pod @ chainsaw-cuddly-elephant/*
    | 13:35:53 | deployment-to-pod | step-1    | ASSERT    | DONE  | v1/Pod @ chainsaw-cuddly-elephant/*
    | 13:35:53 | deployment-to-pod | step-1    | TRY       | END   |
    | 13:35:53 | deployment-to-pod | step-1    | CLEANUP   | BEGIN |
    | 13:35:53 | deployment-to-pod | step-1    | DELETE    | OK    | apps/v1/Deployment @ chainsaw-cuddly-elephant/nginx-deployment
    | 13:35:53 | deployment-to-pod | step-1    | CLEANUP   | END   |
    | 13:35:53 | deployment-to-pod | @chainsaw | CLEANUP   | BEGIN |
    | 13:35:53 | deployment-to-pod | @chainsaw | DELETE    | OK    | v1/Namespace @ chainsaw-cuddly-elephant
    | 13:35:58 | deployment-to-pod | @chainsaw | CLEANUP   | END   |
--- PASS: chainsaw (0.00s)
    --- PASS: chainsaw/deployment-to-pod (6.27s)
PASS
Tests Summary...
- Passed  tests 1
- Failed  tests 0
- Skipped tests 0
Done.

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

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

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

--- chainsaw-test.yaml
+++ chainsaw-test.update.yaml
@@ -45,4 +45,4 @@
               restartCount: 0
               state:
                 running: {}
-            phase: Running
+            phase: Completed

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

❯ chainsaw test .
Version: 0.2.9
(...)
=== RUN   chainsaw
=== PAUSE chainsaw
=== CONT  chainsaw
=== RUN   chainsaw/deployment-to-pod
=== PAUSE chainsaw/deployment-to-pod
=== CONT  chainsaw/deployment-to-pod
    | 13:48:17 | deployment-to-pod | @chainsaw | CREATE    | OK    | v1/Namespace @ chainsaw-stirred-gopher
    | 13:48:17 | deployment-to-pod | step-1    | TRY       | BEGIN |
    | 13:48:17 | deployment-to-pod | step-1    | APPLY     | RUN   | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
    | 13:48:17 | deployment-to-pod | step-1    | CREATE    | OK    | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
    | 13:48:17 | deployment-to-pod | step-1    | APPLY     | DONE  | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
    | 13:48:17 | deployment-to-pod | step-1    | ASSERT    | RUN   | v1/Pod @ chainsaw-stirred-gopher/*
    | 13:48:47 | deployment-to-pod | step-1    | ASSERT    | ERROR | v1/Pod @ chainsaw-stirred-gopher/*
        === ERROR
        ----------------------------------------------------------------
        v1/Pod/chainsaw-stirred-gopher/nginx-deployment-77d8468669-96d9b
        ----------------------------------------------------------------
        * status.phase: Invalid value: "Running": Expected value: "Completed"

        --- expected
        +++ actual
        @@ -3,13 +3,27 @@
         metadata:
           labels:
             app: nginx
        +  name: nginx-deployment-77d8468669-96d9b
           namespace: chainsaw-stirred-gopher
        +  ownerReferences:
        +  - apiVersion: apps/v1
        +    blockOwnerDeletion: true
        +    controller: true
        +    kind: ReplicaSet
        +    name: nginx-deployment-77d8468669
        +    uid: 85049a1c-0307-48eb-aa35-ff1805f0bdfd
         status:
           containerStatuses:
        -  - name: nginx
        +  - containerID: containerd://c13e12c915348fa814fc7846bd80058803bd6756cfaf34b2fc83fcbbc29e78a2
        +    image: docker.io/library/nginx:1.14.2
        +    imageID: docker.io/library/nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d
        +    lastState: {}
        +    name: nginx
             ready: true
             restartCount: 0
        +    started: true
             state:
        -      running: {}
        -  phase: Completed
        +      running:
        +        startedAt: "2024-09-15T04:48:18Z"
        +  phase: Running
    | 13:48:47 | deployment-to-pod | step-1    | TRY       | END   |
    | 13:48:47 | deployment-to-pod | step-1    | CLEANUP   | BEGIN |
    | 13:48:47 | deployment-to-pod | step-1    | DELETE    | OK    | apps/v1/Deployment @ chainsaw-stirred-gopher/nginx-deployment
    | 13:48:47 | deployment-to-pod | step-1    | CLEANUP   | END   |
    | 13:48:47 | deployment-to-pod | @chainsaw | CLEANUP   | BEGIN |
    | 13:48:47 | deployment-to-pod | @chainsaw | DELETE    | OK    | v1/Namespace @ chainsaw-stirred-gopher
    | 13:48:52 | deployment-to-pod | @chainsaw | CLEANUP   | END   |
--- FAIL: chainsaw (0.00s)
    --- FAIL: chainsaw/deployment-to-pod (35.29s)
FAIL
Tests Summary...
- Passed  tests 0
- Failed  tests 1
- Skipped tests 0
Done with failures.
Error: 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のログなど追加で表示したい情報をカスタマイズできます。

--- chainsaw-test.yaml
+++ chainsaw-test.update.yaml
@@ -46,3 +46,11 @@
               state:
                 running: {}
             phase: Completed
+    catch:
+    - events: {}
+    - get:
+        apiVersion: v1
+        kind: Pod
+        selector: app=nginx
+    - podLogs:
+        selector: app=nginx

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

=== RUN   chainsaw
=== PAUSE chainsaw
=== CONT  chainsaw
=== RUN   chainsaw/deployment-to-pod
=== PAUSE chainsaw/deployment-to-pod
=== CONT  chainsaw/deployment-to-pod
    | 14:09:53 | deployment-to-pod | @chainsaw | CREATE    | OK    | v1/Namespace @ chainsaw-allowing-terrier
    | 14:09:53 | deployment-to-pod | step-1    | TRY       | BEGIN |
    | 14:09:53 | deployment-to-pod | step-1    | APPLY     | RUN   | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
    | 14:09:53 | deployment-to-pod | step-1    | CREATE    | OK    | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
    | 14:09:53 | deployment-to-pod | step-1    | APPLY     | DONE  | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
    | 14:09:53 | deployment-to-pod | step-1    | ASSERT    | RUN   | v1/Pod @ chainsaw-allowing-terrier/*
    | 14:10:23 | deployment-to-pod | step-1    | ASSERT    | ERROR | v1/Pod @ chainsaw-allowing-terrier/*
    (...)
    | 14:10:23 | deployment-to-pod | step-1    | TRY       | END   |
    | 14:10:23 | deployment-to-pod | step-1    | CATCH     | BEGIN |
    | 14:10:23 | deployment-to-pod | step-1    | CMD       | RUN   |
        === COMMAND
        /opt/homebrew/bin/kubectl get events -n chainsaw-allowing-terrier
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | LOG   |
        === STDOUT
        LAST SEEN   TYPE     REASON              OBJECT                                   MESSAGE
        31s         Normal   Scheduled           pod/nginx-deployment-77d8468669-2z4rx    Successfully assigned chainsaw-allowing-terrier/nginx-deployment-77d8468669-2z4rx to kind-control-plane
        30s         Normal   Pulled              pod/nginx-deployment-77d8468669-2z4rx    Container image "nginx:1.14.2" already present on machine
        30s         Normal   Created             pod/nginx-deployment-77d8468669-2z4rx    Created container nginx
        30s         Normal   Started             pod/nginx-deployment-77d8468669-2z4rx    Started container nginx
        31s         Normal   SuccessfulCreate    replicaset/nginx-deployment-77d8468669   Created pod: nginx-deployment-77d8468669-2z4rx
        31s         Normal   ScalingReplicaSet   deployment/nginx-deployment              Scaled up replica set nginx-deployment-77d8468669 to 1
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | DONE  |
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | RUN   |
        === COMMAND
        /opt/homebrew/bin/kubectl get pods -l app=nginx -n chainsaw-allowing-terrier
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | LOG   |
        === STDOUT
        NAME                                READY   STATUS    RESTARTS   AGE
        nginx-deployment-77d8468669-2z4rx   1/1     Running   0          31s
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | DONE  |
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | RUN   |
        === COMMAND
        /opt/homebrew/bin/kubectl logs --prefix -l app=nginx -n chainsaw-allowing-terrier --all-containers
    | 14:10:24 | deployment-to-pod | step-1    | CMD       | DONE  |
    | 14:10:24 | deployment-to-pod | step-1    | CATCH     | END   |
    | 14:10:24 | deployment-to-pod | step-1    | CLEANUP   | BEGIN |
    | 14:10:24 | deployment-to-pod | step-1    | DELETE    | OK    | apps/v1/Deployment @ chainsaw-allowing-terrier/nginx-deployment
    | 14:10:24 | deployment-to-pod | step-1    | CLEANUP   | END   |
    | 14:10:24 | deployment-to-pod | @chainsaw | CLEANUP   | BEGIN |
    | 14:10:24 | deployment-to-pod | @chainsaw | DELETE    | OK    | v1/Namespace @ chainsaw-allowing-terrier
    | 14:10:29 | deployment-to-pod | @chainsaw | CLEANUP   | END   |
--- FAIL: chainsaw (0.00s)
    --- FAIL: chainsaw/deployment-to-pod (35.56s)
FAIL
Tests Summary...
- Passed  tests 0
- Failed  tests 1
- Skipped tests 0
Done with failures.
Error: 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のリソースは既に作成されている前提でテストを書いてみましょう。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: replicalimit-policy.example.com
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["deployments"]
  validations:
    - expression: "object.spec.replicas <= 5"
chainsaw-test.yamlにE2Eテストを定義します。
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
  name: vap-replicalimit-policy
spec:
  # テスト時に作成するnamespaceの名前を指定
  namespace: vap-replicalimit-policy
  steps:
  - try:
    - apply:
        resource:
          apiVersion: admissionregistration.k8s.io/v1
          kind: ValidatingAdmissionPolicyBinding
          metadata:
            name: replicalimit-policy-test.example.com
          spec:
            policyName: replicalimit-policy.example.com
            validationActions: [Deny]
            # テスト時に作成したnamespaceに対してポリシーを適用
            matchResources:
              namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: vap-replicalimit-policy
  # VAPが適用されるまで待機
  - try:
    - sleep:
        duration: 30s
  # ポリシーに準拠したレプリカ数を指定したDeploymentを作成
  # Deploymentが作成できれば良いのでassertは不要
  - try:
    - apply:
        resource:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: nginx-1
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: nginx-1
            template:
              metadata:
                labels:
                  app: nginx-1
              spec:
                containers:
                - name: nginx
                  image: nginx:1.14.2
  # ポリシーに準拠していないレプリカ数を指定したDeploymentを作成
  - try:
    - apply:
        resource:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: nginx-10
          spec:
            replicas: 10
            selector:
              matchLabels:
                app: nginx-10
            template:
              metadata:
                labels:
                  app: nginx-10
              spec:
                containers:
                - name: nginx
                  image: nginx:1.14.2
        expect:
        - match:
            apiVersion: apps/v1
            kind: Deployment
            metadata:
              name: nginx-10
          check:
            # エラーとなる場合はテストが成功
            ($error != null): true

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

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

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

❯ chainsaw test .
Version: 0.2.9
(...)
Running tests...
=== RUN   chainsaw
=== PAUSE chainsaw
=== CONT  chainsaw
=== RUN   chainsaw/vap-replicalimit-policy
=== PAUSE chainsaw/vap-replicalimit-policy
=== CONT  chainsaw/vap-replicalimit-policy
    | 14:50:29 | vap-replicalimit-policy | @chainsaw | CREATE    | OK    | v1/Namespace @ vap-replicalimit-policy
    | 14:50:29 | vap-replicalimit-policy | step-1    | TRY       | BEGIN |
    | 14:50:29 | vap-replicalimit-policy | step-1    | APPLY     | RUN   | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
    | 14:50:29 | vap-replicalimit-policy | step-1    | CREATE    | OK    | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
    | 14:50:29 | vap-replicalimit-policy | step-1    | APPLY     | DONE  | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
    | 14:50:29 | vap-replicalimit-policy | step-1    | TRY       | END   |
    | 14:50:29 | vap-replicalimit-policy | step-2    | TRY       | BEGIN |
    | 14:50:29 | vap-replicalimit-policy | step-2    | SLEEP     | RUN   |
    | 14:50:59 | vap-replicalimit-policy | step-2    | SLEEP     | DONE  |
    | 14:50:59 | vap-replicalimit-policy | step-2    | TRY       | END   |
    | 14:50:59 | vap-replicalimit-policy | step-3    | TRY       | BEGIN |
    | 14:50:59 | vap-replicalimit-policy | step-3    | APPLY     | RUN   | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
    | 14:50:59 | vap-replicalimit-policy | step-3    | CREATE    | OK    | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
    | 14:50:59 | vap-replicalimit-policy | step-3    | APPLY     | DONE  | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
    | 14:50:59 | vap-replicalimit-policy | step-3    | TRY       | END   |
    | 14:50:59 | vap-replicalimit-policy | step-4    | TRY       | BEGIN |
    | 14:50:59 | vap-replicalimit-policy | step-4    | APPLY     | RUN   | apps/v1/Deployment @ vap-replicalimit-policy/nginx-10
    | 14:50:59 | vap-replicalimit-policy | step-4    | CREATE    | WARN  | apps/v1/Deployment @ vap-replicalimit-policy/nginx-10
        === ERROR
        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
    | 14:50:59 | vap-replicalimit-policy | step-4    | APPLY     | DONE  | apps/v1/Deployment @ vap-replicalimit-policy/nginx-10
    | 14:50:59 | vap-replicalimit-policy | step-4    | TRY       | END   |
    | 14:50:59 | vap-replicalimit-policy | step-3    | CLEANUP   | BEGIN |
    | 14:50:59 | vap-replicalimit-policy | step-3    | DELETE    | OK    | apps/v1/Deployment @ vap-replicalimit-policy/nginx-1
    | 14:50:59 | vap-replicalimit-policy | step-3    | CLEANUP   | END   |
    | 14:50:59 | vap-replicalimit-policy | step-1    | CLEANUP   | BEGIN |
    | 14:50:59 | vap-replicalimit-policy | step-1    | DELETE    | OK    | admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding @ replicalimit-policy-test.example.com
    | 14:50:59 | vap-replicalimit-policy | step-1    | CLEANUP   | END   |
    | 14:50:59 | vap-replicalimit-policy | @chainsaw | CLEANUP   | BEGIN |
    | 14:51:00 | vap-replicalimit-policy | @chainsaw | DELETE    | OK    | v1/Namespace @ vap-replicalimit-policy
    | 14:51:05 | vap-replicalimit-policy | @chainsaw | CLEANUP   | END   |
--- PASS: chainsaw (0.00s)
    --- PASS: chainsaw/vap-replicalimit-policy (35.36s)
PASS
Tests Summary...
- Passed  tests 1
- Failed  tests 0
- Skipped tests 0
Done.

このように、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/

連載バックナンバー

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

「K8sGPT」の未来と生成AIを用いたKubernetes運用の最前線

2024/11/14
第9回の今回は、Kubernetesのトラブルシュートを生成AIで補助する「K8sGPT」について紹介します。
仮想化/コンテナ技術解説
第8回

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

2024/10/29
第8回の今回は、Kubernetes Operatorのエンドツーエンド(E2E)テストツールである「Kyverno Chainsaw」について紹介します。
仮想化/コンテナ技術解説
第7回

「kwok」でKubernetesクラスターをシュミレーションする

2024/9/19
第7回の今回は、大規模なKubernetesクラスターの検証を容易にする「kwok」について紹介します。

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

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

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

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