レプリカを増やしても速くならないコントローラがある
一言でいうと
Kyvernoは、1つのプログラムではなく、admission・background・reports・cleanupの4つのコントローラーが、それぞれDeploymentとして動く構造です。そのため、高可用性(HA)もコントローラーごとに決め、アップグレードはイメージタグを上げるだけではいけません。ポリシーをクラスターに入れる前にCLIでテストし、入れたあとはkyverno_*メトリクスで見守ります。この記事は、インストール方法のドキュメント、高可用性の案内、アップグレードのドキュメント、CLIリファレンス、メトリクスリファレンスを1つにまとめて説明します。
なぜ必要なのか
アドミッションWebhookは、デフォルトがfail closedです。インストールのドキュメントは、このリスクを詳しく説明しています。APIサーバーがKyvernoに届かなければ、ポリシーに引っかかるリソースを作るリクエストは、ポリシーを評価できないという理由で失敗します。Podをnon-rootに強制するポリシーが1つあるクラスターで、KyvernoのPodがすべて停止すると、新しいPodを1つも作れません。そのため、本番環境はHAでインストールする必要があり、Kyverno自身のネームスペースはWebhookから除外しておかなければなりません(デフォルトの設定は、kyvernoとkube-systemを除外します)。
ここに、3つの運用の問いが続きます。どのコントローラーをいくつ起動するのか、新しいバージョンにどう上げるのか、ポリシーが実際に何を止めているかをどう見るのか。
どう動くのか
4つのコントローラーとHelmインストール
インストールのドキュメントは、コントローラーを次のように分けています。admission controllerは必須で、APIサーバーのWebhookコールバックを受け取り、validate・mutate・イメージ検証・PolicyExceptionを処理します。background controllerはgenerateとmutate-existingのルールを、reports controllerはPolicyReportを、cleanup controllerはCleanupPolicyを担当します。コントローラーごとにServiceAccountが別にあるので権限が分離され、デフォルトのインストールは、それぞれレプリカ1つです。
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --create-namespace \
--set admissionController.replicas=3 \
--set backgroundController.replicas=2 \
--set cleanupController.replicas=2 \
--set reportsController.replicas=2
上は、ドキュメントのHAインストールの例です。「完全な」HAデプロイとして、4つのコントローラーすべてをreplicas: 3にしたvaluesの例も一緒に載っています。YAMLマニフェストでもインストールできますが、タグ付きリリースのinstall.yamlをkubectl create -fする方式で、ドキュメントは、この方法では直接のアップグレードをサポートしないと念を押しています。PSS(Pod Security Standards)ポリシーのセットが必要なら、別のチャートkyverno/kyverno-policiesをインストールします。
レプリカの役割はコントローラーごとに違う
HAの案内が最も重要だと述べている事実です。admission controllerは、Webhookリクエストにリーダー選出(leader election)を使わないので、すべてのレプリカがリクエストを分担して処理します。レプリカが、可用性とスループットの両方に使われるのです。証明書とWebhookの管理だけを、リーダー1つが担当します。HAとして認められる最小のレプリカ数は3つです。一方、reports controllerとbackground controllerは、状態を持つサービスなのでリーダー選出を使い、レプリカがいくつあっても1つだけが働きます。そのため、この2つのレプリカは可用性にしか役立たず、スループットを上げるには、レプリカの数ではなく、個々のPodのリソース(垂直スケーリング)を増やす必要があります。インストールのドキュメントの「レプリカが多いからといって、すべてのコントローラーで性能が上がるわけではない」という文が、この意味です。
Webhook自体の設定にも、知っておくべき値があります。failurePolicyのデフォルトはFailで、ポリシーごとに変えたり、--forceFailurePolicyIgnoreで全体を変えたりできます。webhookTimeoutのデフォルトは10秒(1–30秒)です。デフォルトのresourceFiltersは、Event、Node、そしてkube-system・kube-public・kube-node-lease・kyvernoネームスペースのリソースを除外します。
CRDの種類
CRDのドキュメントは、kubectl explainですべてのタイプを見られると案内しています。アップグレードのドキュメントのv1.19の節に、種類が整理されています。
| 種類 | APIグループ/バージョン | 役割 |
|---|---|---|
Policy / ClusterPolicy |
kyverno.io/v1 |
従来のvalidate・mutate・generate・verifyImagesポリシー |
ValidatingPolicyなどのCELポリシー |
policies.kyverno.io/v1 |
ValidatingPolicy・MutatingPolicy・GeneratingPolicy・DeletingPolicy・ImageValidatingPolicy |
CleanupPolicy / ClusterCleanupPolicy |
kyverno.io/v2 |
スケジュールベースの整理 |
PolicyException |
kyverno.io/v2(レガシー)またはpolicies.kyverno.io |
例外 |
GlobalContextEntry |
kyverno.io/v2 (v2alpha1は非推奨) |
キャッシュされた外部データ |
PolicyReport / ClusterPolicyReport |
wgpolicyk8s.io/v1alpha2 |
最終レポート |
EphemeralReport / ClusterEphemeralReport |
reports.kyverno.io/v1 |
レポートの中間生成物 |
UpdateRequest |
内部タイプ | generate・mutate-existingの中間生成物 |
v1.19からCRDは、kyverno-apiというチャートの依存関係として管理され、crds.installの値がそれをオン・オフします。同じドキュメントは、v1.19でClusterPolicy・Policy・CleanupPolicy・レガシーのPolicyExceptionが非推奨(deprecated)になり、v1.20で削除されると予告しています。
アップグレード: タグだけ上げてはいけない理由
アップグレードのドキュメントの最初の文が、原則です。新しいバージョンには、CRDを含めて変わるサポート対象のリソースが多いので、イメージタグを上げるだけではアップグレードできません。1.10より前のバージョンから1.10以上へHelmでアップグレードするには、直接のアップグレードができず、チャートv2からv3への移行の案内に従う必要があります。マイナーバージョンを飛ばすなら、その間のすべてのマイナーのリリースノートを読まなければなりません。
v1.13の節がよい例です。ワイルドカードのview権限が外れたため、カスタムリソースを見るmutate・generateポリシーとレポートが影響を受け、例外(PolicyException)がデフォルトですべてのネームスペースで許可されていたことがセキュリティ問題(CVE-2024-48921)として変更され、features.policyExceptions.namespaceの値を明示する必要があり、古いCRD APIバージョンが削除されたため、Helmフックが自動的に移行を処理しました。v1.19では、ストレージバージョン(storage version)の移行のために、kyverno migrate --resource policyexceptions.kyverno.ioのようなCLIコマンドが追加されました。要点は1つです。アップグレードはコードの入れ替えではなく、CRD・権限・デフォルト値が一緒に変わる出来事なので、リリースノートがそのまま手順書です。
CLI: クラスターなしでテストする
Kyverno CLIは、コントローラーとは別の実行ファイルで、リファレンスの--kubeconfigの説明が「クラスターの外で実行するときだけ必要」と書いているように、クラスターなしで使います。CIでは、ポリシーテストの案内が示しているとおり、GitHub Action kyverno/action-install-cliでインストールします。核心となるコマンドは3つです。
kyverno apply policies/ -r resources/は、ポリシーをリソースのマニフェストに適用して結果を見せます。ポリシーはわかっていてリソースはわからない場合、つまり開発チームのPRに入ってきたマニフェストを検査するときに使います。
kyverno test <디렉터리 또는 git 저장소>(プレースホルダーはディレクトリまたはgitリポジトリです)は、逆にポリシーとリソースと期待結果をあらかじめ書いておいたテストマニフェスト(kyverno-test.yaml、-fでファイル名を変更)と、実際の結果を比べます。期待結果はpass・fail・skipで、kyverno create test -p policy.yaml -r resource.yaml --pass 정책이름,규칙이름,리소스이름,네임스페이스,종류(プレースホルダーはポリシー名、ルール名、リソース名、ネームスペース、種類です)で、テストファイルを作成できます。--git-branchでリモートリポジトリのブランチをテストし、--test-case-selector "policy=..., rule=..., resource=..."で一部だけを選び、-o junitのように出力形式を変えます。
kyverno jpは、Kyvernoのカスタム関数が加わったJMESPathのコマンドラインです。kyverno jp query -i object.yaml '식'(プレースホルダーは式です)で、ファイルに対して式を評価し、kyverno jp functionで関数の一覧を、kyverno jp function truncateのように特定の関数の説明を見て、kyverno jp parseで式の構文木を見ます。前のモジュールで見たとおり、kubectl get --raw ... | kyverno jp query "items | length(@)"で、apiCallの結果を事前に確認するのが定石です。
メトリクス: 何を見るのか
モニタリングの案内によれば、HelmインストールはコントローラーごとにmetricsServiceを作り、8000番ポートの/metricsでメトリクスを出力します。デフォルトのサービスタイプはClusterIPなので、クラスター内のPrometheusしかスクレイプできず、外からスクレイプするには、NodePort・LoadBalancerに変えます。公開範囲は、kyverno-metrics ConfigMapで調整します。namespaces.include/exclude(excludeが優先)、ヒストグラムのbucketBoundaries、そしてmetricsExposureで、メトリクスごとにオフにしたり(enabled: false)、ラベルのディメンションを落としたり(disabledLabelDimensions)、バケットを変えたりします。ネームスペースを絞ると、メモリ使用量が目に見えて減ると、ドキュメントが書いています。
メトリクスリファレンスが整理した主なメトリクスは、次のとおりです。
| メトリクス | 種類 | 何を見るか |
|---|---|---|
kyverno_policy_rule_info_total |
Gauge(有効なルールなら1) | 今クラスターにどんなポリシー・ルールがあるか。policy_type(cluster/namespaced)、policy_validation_mode(enforce/audit)、rule_type、status_ready |
kyverno_policy_results |
Counter | ルールの実行結果。rule_result(PASS/FAIL)、rule_execution_cause(admission_request/background_scan)、resource_kind |
kyverno_policy_execution_duration_seconds |
Histogram | ルール1つの実行の遅延 |
kyverno_admission_review_duration_seconds |
Histogram | リクエスト1つに対するアドミッション全体の遅延(すべてのポリシーの合計) |
kyverno_admission_requests_total |
Counter | アドミッションのリクエスト数とrequest_allowed |
rate(kyverno_policy_results{resource_kind="Pod", rule_execution_cause="admission_request"}[1m])*60
上はリファレンスに載っているクエリで、Podのリクエストが引き起こす、1分あたりのルール実行数です。実務で最初に見るのは2つです。アドミッションの遅延のヒストグラムがwebhookTimeoutに近づいていないか、そしてrule_result="FAIL"が、どのポリシー・ネームスペースで増えているか。前者は、fail closedのWebhookがクラスターを止める前の警告であり、後者は、ポリシーが実際に止めているものの一覧です。GrafanaダッシュボードのJSONはチャートの中にあり、grafana.enabledの値でデプロイできます。
現場での姿
ある組織が、reports controllerが遅いとしてレプリカを5つに増やしましたが、何も速くなりませんでした。リーダー1つだけが働くからです。答えはレプリカではなく、リーダーPodのCPU・メモリで、同時にkyverno-metricsのネームスペース除外で、メトリクスの負担を減らしました。
アップグレードでよくある事故は、Helmでマイナーバージョンを一度に2つ飛ばしながら、リリースノートを読まなかった場合です。1.13の権限変更により、カスタムリソースを対象にしていたgenerateポリシーが静かに止まり、kyverno_policy_rule_info_totalのstatus_ready="false"で、あとから発見しました。
次のクイズで確認すること
クイズでは、必須のコントローラーが何か、レプリカがスループットに役立つコントローラーと役立たないコントローラー、アップグレードでタグだけを上げてはいけない理由、kyverno testとkyverno applyの違い、kyverno jpの用途、そしてkyverno_policy_resultsのラベルを問います。