午前2時47分、アラートが鳴り響いた。また開発者がリソース制限なしでDeploymentをプッシュしたのだ。Podが暴走してノードのCPUをすべて食いつぶし、他の3つのサービスまで道連れにした。この1ヶ月で同じ会話を6回繰り返していた。ポストモーテムは毎回同じ結論で終わる。「もっとガードレールが必要だ。」
その夜、私はようやく腰を据えて、何週間も先送りにしてきた作業に取りかかった。KyvernoによるPolicy as Codeの実装だ。それから6ヶ月、同種のインシデントはゼロ。実際に本番運用して学んだことをここにまとめる。
手動レビューがスケールしない理由
多くのチームはコードレビューをセーフティネットとして使い始める。誰かがマージ前にYAMLを目で確認する。エンジニアが2人でクラスターが1つなら、それで十分だ。しかし15人のエンジニアが4つの環境にデプロイするようになると、すぐに破綻する。
根本的な問題はこうだ。レビュー時のポリシー適用はあくまで勧告であり、強制力がない。深夜にCIがグリーンになっているからと、疲れた状態でPRを承認してしまう。securityContextの漏れが見逃される。本番環境でPodがrootとして動き続ける。Git Hooksのような自動チェックでコミット前に問題を弾くアプローチも有効だが、Kubernetesレイヤーへの到達を防ぐことはできない。
Policy as Codeはポリシーの適用をクラスター自体に移す。APIサーバーがスケジューリングされる前に非準拠のリソースを拒否する。例外なし、疲労なし、深夜の驚きなし。
アプローチ比較:OPA/Gatekeeper vs Kyverno vs Admission Webhook
選択肢を評価し始めたとき、主に3つのアプローチが浮かび上がった。それぞれ同じ問題を異なる方法で解決している。
Open Policy Agent (OPA) + Gatekeeper
OPAは歴史が長く、実績のある選択肢だ。専用のポリシー言語であるRegoと、Kubernetesとの統合レイヤーであるGatekeeperを使用する。ポリシーのエコシステムは成熟しており、専任のプラットフォームチームを持つ大企業に支持されている。
ただし、Regoの学習曲線は急峻だ。リソース制限を必須にするルールを書くだけで20行ほどのロジックになり、多くのエンジニアには直感的でない。失敗したポリシーのデバッグはRegoのトレースを読むことを意味する。専任のプラットフォームエンジニアがいないチームでは、すぐにボトルネックになる。
カスタムAdmission Webhook
GoやHTTPを話せる任意の言語で独自のWebhookを書くこともできる。完全な柔軟性と外部依存ゼロ。ただし、そのコードのすべての行を自分たちで管理することになる。バグ、TLS証明書のローテーション、可用性要件も含めて。Webhookが壊れると、すべてのデプロイがブロックされる。
このアプローチがうまく機能するのを見たのは、6人のプラットフォームチームを持つ会社で一度きりだ。それ以外の場所では、誰も触りたがらない技術的負債になっていった。
Kyverno
KyvernoはKubernetesネイティブだ。ポリシーはKubernetesリソースであり、YAMLで書かれたCRDとして定義される。Deploymentマニフェストを読めるチームなら、Kyvernoのポリシーも読める。新しい言語を覚える必要はない。
チームが実際に必要とするケースの90%をカバーする3つのユースケースに対応している。バリデーション(不正な設定を拒否)、ミューテーション(リソースを自動的に修正または補完)、ジェネレーション(関連リソースを自動作成)だ。
メリットとデメリット:率直な評価
Kyvernoのメリット
- 参入障壁が低い — ポリシーはYAMLなので、チームの誰でも読める
- Auditモード — 適用前に
Auditモードで何が失敗するか確認できる - ミューテーションのサポート — サイドカーの自動注入、ラベルの追加、デフォルト値の設定が可能
- ポリシーレポート — 名前空間をまたいだコンプライアンス状況を表示する組み込みCRD
- 活発な開発 — CNCFインキュベーティングプロジェクトで頻繁にリリースされる
Kyvernoのデメリット
- 複雑なロジックが書きにくい — 深くネストした条件分岐はYAMLで書くと煩雑になる
- Regoの方が表現力が高い — 高度なロジックが必要なポリシーではOPAが優れている
- Webhookの可用性が重要 — KyvernoはWebhookとして動作するため、本番環境ではHA構成が必要
- 比較的新しい — OPAよりコミュニティ提供のポリシーコンテンツは少ないが、差は急速に縮まっている
私の見解
ほとんどのチーム、特に専任のプラットフォームエンジニアがいないチームにとって、Kyvernoは正しいデフォルト選択だ。私たちは週に約200回のデプロイをさばく4つのクラスターで運用している。展開に要したのは2日間。6ヶ月間、コアのセットアップに一度も手を加えていない。セキュリティツールに求める「退屈なほどの安定性」がここにある。
本番環境向け推奨セットアップ
ポリシーを1つも書く前に、デプロイを正しく行うことが先決だ。KyvernoのWebhookがダウンすると、そのスコープ内のすべてのAPIサーバーリクエストがブロックされる。これは致命的だ。
本番環境では最低3レプリカで実行すること:
# Helmでインストール(本番環境推奨)
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno \
--namespace kyverno \
--create-namespace \
--set replicaCount=3 \
--set admissionController.replicas=3 \
--set backgroundController.replicas=2 \
--set cleanupController.replicas=2
インストール後にWebhookの設定を確認すること。デフォルトでKyvernoはfailurePolicy: Failに設定されており、Kyvernoに到達できない場合はすべてのリソース作成が失敗する。初期展開時はfailurePolicy: Ignoreへの切り替えを検討し、Kyvernoが安定していることを確認した後に厳格化するとよい。
# すべてのPodが起動していることを確認
kubectl get pods -n kyverno
# WebhookのConfig確認
kubectl get validatingwebhookconfigurations | grep kyverno
kubectl get mutatingwebhookconfigurations | grep kyverno
実装ガイド:まずAuditから、次にEnforce
いきなりEnforceモードで始めるのは、チームの信頼を失う最速の方法だ。重要なデプロイが悪いタイミングでポリシーにブロックされると、取り組み全体が台無しになる。まずAuditから始めよう。
ステップ1:リソース制限を必須にする(そもそものインシデントの発端)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
annotations:
policies.kyverno.io/title: リソース制限を必須にする
policies.kyverno.io/description: すべてのコンテナはCPUとメモリの制限を定義しなければならない。
spec:
validationFailureAction: Audit # まずここから始め、後でEnforceに変更する
background: true
rules:
- name: check-resource-limits
match:
any:
- resources:
kinds:
- Pod
validate:
message: "すべてのコンテナにCPUとメモリの制限が必要です。"
pattern:
spec:
containers:
- name: "*"
resources:
limits:
memory: "?*"
cpu: "?*"
kubectl apply -f require-resource-limits.yaml
# 失敗するものを確認(Auditモード)
kubectl get policyreport -A
kubectl get clusterpolicyreport
ステップ2:特権コンテナをブロックする
rootまたは特権モードで動作するコンテナは典型的な攻撃ベクターであり、本番環境では想像以上によく見かける。CI/CDパイプラインにおけるシークレット管理と合わせて、このポリシーでコンテナのセキュリティを強化する:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged-containers
spec:
validationFailureAction: Audit
background: true
rules:
- name: check-privileged
match:
any:
- resources:
kinds:
- Pod
validate:
message: "特権コンテナは許可されていません。"
pattern:
spec:
containers:
- =(securityContext):
=(privileged): "false"
ステップ3:ミューテーションでラベルを自動注入する
ミューテーションはバリデーション以上にKyvernoの真価が発揮される場面だ。ラベルが欠けているDeploymentを拒否する代わりに、自動的に追加してしまえばよい:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-labels
spec:
rules:
- name: add-team-label
match:
any:
- resources:
kinds:
- Deployment
mutate:
patchStrategicMerge:
metadata:
labels:
+(managed-by): "kyverno"
+(environment): "production"
ステップ4:ポリシーレポートを確認する
# クラスター全体のすべての違反を確認
kubectl get policyreport -A -o wide
# 特定の名前空間の詳細レポート
kubectl describe policyreport -n default
# 失敗のみをフィルタリング
kubectl get policyreport -A -o json | \
jq '.items[].results[] | select(.result == "fail")'
Enforceに移行する前に、Auditレポートを1週間眺めること。ワークロードの既存の違反を修正する。その後、ポリシーを1つずつEnforceに切り替える。絶対に一括で変更してはいけない:
# ポリシーをAuditからEnforceへ変更
kubectl patch clusterpolicy require-resource-limits \
--type=merge \
-p '{"spec":{"validationFailureAction":"Enforce"}}'
ステップ5:Kyvernoポリシーライブラリを活用する
カスタムポリシーを書く前に、公式ライブラリを確認すること。Pod Security Standards、CISベンチマーク、一般的なベストプラクティスはすでにカバーされている:
# コミュニティポリシーの参照と適用
kubectl apply -f https://raw.githubusercontent.com/kyverno/policies/main/pod-security/baseline/disallow-host-namespaces/disallow-host-namespaces.yaml
ポリシーを骨抜きにしない例外処理
一部のワークロードは正当に例外を必要とする。特権アクセスが必要なノードレベルの監視エージェントはその典型例だ。KyvernoはexcludeブロックとPolicyExceptionリソース(Kyverno 1.9以降で利用可能)を使ってこれをきれいに処理する。
apiVersion: kyverno.io/v2beta1
kind: PolicyException
metadata:
name: allow-monitoring-privileged
namespace: monitoring
spec:
exceptions:
- policyName: disallow-privileged-containers
ruleNames:
- check-privileged
match:
any:
- resources:
kinds:
- Pod
namespaces:
- monitoring
names:
- node-exporter-*
例外はKubernetesリソースであり、バージョン管理され、レビュー可能で、監査可能だ。深夜にオンコール担当者が行った文書化されていないバイパスの決定は、もう存在しない。
6ヶ月後の景色
あの最初のデプロイ?今ならAPIサーバーに到達することすらない。開発者は何が足りないかを明確に説明したエラーメッセージを受け取る。修正してもう一度プッシュすれば動く。3時間の障害とポストモーテムの代わりに、2分で完結する。
ポリシーレポートはコンプライアンス監査への対応も変えた。「すべての本番ワークロードがrootアクセスなしで動作しているか?」という問いへの回答が、1週間の手作業レビューから30秒に短縮された。
コードレビューだけを唯一の強制レイヤーとして本番でKubernetesを動かすのはリスクだ。今週、Kyvernoをauditモードでインストールしてみてほしい。セットアップは10分で終わり、最初のポリシーレポートには十中八九、想定外の何かが映し出されるはずだ。

