この記事について
業務でKyvernoを使ってますが、一部機能しか使っておらず
全機能を把握できてないため調査のためにこの記事を書いてます。
Kyvernoとは
KyvernoとはKubernetes向けのポリシーエンジンです。
ポリシーとはk8sリソースを作成する際のルールです。
例えば・・・
- Podには必ず○○というLabelがついていないといけない
- latestタグの利用禁止
- などなど
OpenPolicyAgentとの大きな違いとして、
Regoのような特別な言語を用いずにポリシーを定義できる点だと思います。
Kyvernoでできること
- リソースが新規作成された際に別のリソースを新規作成できる
- リソースが新規作成・更新された際にパッチを当てられる
- リソースの作成された際に、ポリシーに則ってるか検証し、作成を拒否することができる
これらの機能をそれぞれ触ってみたいと思います!
インストール
マニフェストがGitHubで用意されてるのでそれを使います。
ちなみに今回使うKyvernoのバージョンは v1.4.1
です。
$ kubectl apply -f https://raw.githubusercontent.com/kyverno/kyverno/v1.4.1/definitions/install.yaml
さわってみた
Generate Resources
Generate Resourcesとは、リソースの新規作成 or 更新されたのをトリガーに
新たなリソースを作成する機能です。
https://kyverno.io/docs/writing-policies/generate/
とりあえずやってみましょう!
各Namespaceで同じSecret情報を使いたいことがあると思います。
そういうシーンを想定して、Namespaceが作成されたのをトリガーにすでに存在するSecretをコピーするポリシーを書いてみます。
まずコピー元となるSecretをdefault
namespaceに作成します。
apiVersion: v1
kind: Secret
metadata:
name: sample-secret
namespace: default
type: Opaque
data:
password: aG9nZQo= # hoge
次にこのSecretをコピーするポリシーを定義します。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sync-secrets
annotations:
policies.kyverno.io/title: Sync Secrets
policies.kyverno.io/category: Sample
policies.kyverno.io/subject: Secret
policies.kyverno.io/description: >-
copy sample secret from default namespace to requested namespace.
spec:
background: false
rules:
- name: sync-sample-secret
match:
resources:
kinds:
- Namespace
generate:
kind: Secret
name: sample-secret
namespace: "{{request.object.metadata.name}}"
synchronize: true
clone:
namespace: default
name: sample-secret
ではNamespaceを作成して動作を確認してみましょう。
$ kubectl create namespace test
namespace/test created
# 関係あるところだけ抜粋
$ kubectl get secret sample-secret -o yaml -n test
apiVersion: v1
data:
password: aG9nZQo=
kind: Secret
metadata:
name: sample-secret
namespace: test
type: Opaque
ということで、やりたいことは出来ました。
疑問
やってみて出てきた疑問とその答えを書きます。
spec.rules.generate.synchronizeとは?
今回のサンプルでは synchronize: true
としました。これは一体なにを意味するのでしょう?
今回コピーして作られたリソースはソースコード管理ができません。
マニフェストをYamlで定義してGit管理してGitOpsをしてる場合、このリソースは直接編集できてしまいます。
それを防止するために、synchronize
を true
にすると、コピー元のリソースと同期をしてくれます。
# コピーしたSecretリソースのパスワード部分変更
$ k edit secret sample-secret -n test
secret/sample-secret edited
# コピー元
$ k get secret sample-secret -o yaml -n default | grep password | head -1
password: aG9nZQo=
# コピー先。editで更新したのに元に戻ってる
$ k get secret sample-secret -o yaml -n test | grep password | head -1
password: aG9nZQo=
さて、先ほどはコピー先を編集してみましたが、コピー元を編集するとどうなるでしょうか。
# コピー元のSecretリソースのパスワード部分変更
$ k edit secret sample-secret -n default
secret/sample-secret edited
# コピー元
$ k get secret sample-secret -o yaml -n default | grep password | head -1
password: YWFhCg==
# コピー先
$ k get secret sample-secret -o yaml -n test | grep password | head -1
password: YWFhCg==
こちらも同様に同期してくれます。便利ですね!
コピー(Clone)以外にできることは?
今回はCloneという機能を使いました。
clone:
namespace: default
name: sample-secret
あとはマニフェストの定義をそのまま書くことも出来ます。
generate:
synchronize: true
kind: ConfigMap
name: zk-kafka-address
# generate the resource in the new namespace
namespace: "{{request.object.metadata.name}}"
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"
Mutate Resources
Mutate Resourcesとは、マッチしたリソースに対してパッチをあてる機能です。
https://kyverno.io/docs/writing-policies/mutate/
ではさわって行きたいと思います。
今回想定するシーンは、「あるNamespace配下のPodには必ず共通するConfigMapを読み込ませたい」です。
ではまずNamespaceを作成しましょう。
$ kubectl create namespace test2
namespace/test2 created
次に読み込ませたいConfigMapを作成します。
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-configmap
namespace: test2
data:
environment_name: staging
次に test2
配下のPodに対して上記ConfigMapを読み込ませるポリシーを書きましょう。
ConfigMapに記載されている値が環境変数になるような感じで!
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-configmap-policy
annotations:
policies.kyverno.io/title: add configmap
policies.kyverno.io/category: Sample
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Add env to pods which place to test2 namespace.
spec:
rules:
- name: "Set common environment variables"
match:
resources:
kinds:
- Pod
namespaces:
- test2
mutate:
overlay:
spec:
containers:
- (name): "*"
env:
- name: ENVIRONMENT
valueFrom:
configMapKeyRef:
name: sample-configmap
key: environment_name
最後にテスト用のDeploymentを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
namespace: test2
spec:
selector:
matchLabels:
app: test
replicas: 1
template:
metadata:
labels:
app: test
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
このDeploymentで作成されるPodに対してConfigMapで定義した値を環境変数に設定する設定が加わってるはずです。
見てみましょう。
$ kubectl describe pod -n test2 test-deployment-c49854788-s2wg4 | grep -i ENVIRONMENT
Environment:
ENVIRONMENT: <set to the key 'environment_name' of config map 'sample-configmap'> Optional: false
このように自動でenv設定を挿入するような設定が出来ました。
疑問
spec.rules.mutate.overlayとは?
mutateは以下の3つのパッチの当て方を指定できます。
- overlay
- 追加・置き換えたいマニフェストを記述する方法です
- Strategic Merge Patch
- overlayと同じく、追加や置き換えの記述が出来ます
- 削除も可能です
- RFC 6902 JSONPatch
- リソースに対して json patchを当てる方法です。kustomizeでいう
patchesJson6902
- Strategi Merge Patchでは実現できない更新をしたいときに使います
- 例えばarrayの一部要素を書き換えたいとか
- リソースに対して json patchを当てる方法です。kustomizeでいう
matchで指定できるものって?
ここに詳しく書いてあります。
https://kyverno.io/docs/writing-policies/match-exclude/
リソースの名前やラベルでも対象を制限できますし、除外設定も行えます。
Validate Resources
新規・既存リソースが定義したポリシーを満たしているかを検査する機能です。
今回想定するシーンは「Podには必ずresources.requests/limitsを入れる。入れてない場合Podを生成しない」です。
まずその制限をするポリシーを書きます。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: all-containers-need-requests-and-limits
spec:
validationFailureAction: enforce
background: false
rules:
- name: check-container-resources
match:
resources:
kinds:
- Pod
namespaces:
- test2
validate:
message: "All containers must have CPU and memory resource requests and limits defined."
pattern:
spec:
containers:
- name: "*"
resources:
limits:
memory: "?*"
cpu: "?*"
requests:
memory: "?*"
cpu: "?*"
mutateのとき使ったDeploymentを適用してみます。
すると・・・
$ kubectl apply -f test.yaml
Error from server: error when creating "test.yaml": admission webhook "validate.kyverno.svc" denied the request:
resource Deployment/test2/test-deployment was blocked due to the following policies
all-containers-need-requests-and-limits:
autogen-check-container-resources: 'validation error: All containers must have CPU
and memory resource requests and limits defined. Rule autogen-check-container-resources
failed at path /spec/template/spec/containers/0/resources/limits/'
# Podは作成されていない
$ kubectl -n test2 get pods
No resources found in test2 namespace.
ということで、やりたいことは実現できました。
疑問
spec.backgroundとは?
ここを true
にすると、既存リソースに対してもポリシーのチェックを行うようになります。
https://kyverno.io/docs/writing-policies/background/
既存リソースがポリシー違反になった場合、既存リソースはそのままですが、
ClusterPolicyReport
または PolicyReport
というリソースが作成されます。
$ kubectl get policyreport -A
NAMESPACE NAME PASS FAIL WARN ERROR SKIP AGE
test2 polr-ns-test2 0 1 0 0 0 67s
中身を見てみると、エラーになった理由が記載されています。
この spec.background
はデフォルトはtrueで、mutateやgenerateには影響ない項目です。
spec.validationFailureActionで指定できるものは?
- enforce
- ポリシー違反の場合、新規リソースの場合は、そのリソースは作成されません
- audit
- ポリシー違反の場合、
ClusterPolicyReport
またはPolicyReport
というリソースが作成されます - ポリシー違反でもそのリソースは作成されます
- ポリシー違反の場合、
その他便利機能
今回はさわってないですが、使いこなすときに便利そうな機能があったので紹介しておきます。
変数
リクエストされたリソースの情報を変数として使えます。
https://kyverno.io/docs/writing-policies/variables/
またConfigMapの情報も呼び出せます。
これはConfigMapで定義した値を元にValidationを行ったりすることができるというものです。
https://kyverno.io/docs/writing-policies/external-data-sources/
前提条件
match
や exclude
よりもさらに細かい条件指定を行いたい場合に
preconditions
というものが使えます。
https://kyverno.io/docs/writing-policies/preconditions/