本記事ではOPA/Gatekeeper (以下、Gatekeeper)を活用して特定条件にマッチするKuberentesリソースにMutationポリシーを適用する試験を行います。「Kyvernoで特定条件にマッチするKuberentesリソースにMutationポリシーを適用する」のGatekeeper版です。
OPA/Gatekeeperとは?
GatekeeperはOpen Policy Agent (OPA)と呼ばれるポリシー定義のための標準化された仕様で書かれたルールをKubernetesで利用するためのCRDやk8sオペレーターを提供します。Gatekeeperは、Kyvernoと同様にAdmission Controlの仕組みを活用してKubernetes APIへのリクエスト処理をフックし、KubernetesリソースのValidation(検証)
やMutation(追加、更新、削除)
などの処理を追加できます。2022年7月現在、Gatekeeper v3.9.0が最新のバージョンとなっています。
Mutationポリシー
Mutationはv3.4.0でようやくAlpha版がリリースされたGatekeeperにおいては比較的新しい機能になります。なお、2022年7月現在、最新のv3.9.0においてもベータリリース状態であるため、プロダクション環境での利用については十分に注意が必要といえるでしょう。
Mutaionポリシーの定義には次の3つのCRDが利用できます。
-
AssignMetadata: リソースのmetadataセクションの変更を定義するCRD。metadataはとてもセンシティブであり変更により意図しない結果が生じる可能性があるため変更の自由度は制限されています。現時点で、AssignMetadataの変更は、
label
とannotation
のみに制限されています。さらに、既存のlabelやannotationの変更できないのでご注意ください。 - Assign: リソースのmetadataセクション以外の変更を定義するCRD
-
ModifySet: リストへのエントリーの追加や削除を定義するCRD。例えば、
spec.containers
の引数一覧にエントリーを追加・削除するとか。ModifySet CRDの設定例。
Mutation機能のサンプル集はこちらになります。
なお、本記事後半でAssignMetadataを活用したサンプルを紹介します。
Validationポリシー
この記事の本題はMutationポリシーではありますが、Validationポリシーについて簡単に説明しておきます。
Validationポリシーには次のConstraintTemplateとConstaint CRDを利用します。
-
ConstraintTemplate
- ポリシーはConstraintとして定義しますが、そのConstraint CRDを定義するテンプレート
- ポリシーの内容はRego言語で記述しConstraintTemplateに埋め込みます
-
Constraint
- Constraintリソースを通じてGatekeeperはポリシーを実行します
- Constraint CRDはConstraintTemplateで定義されます
実際のサンプルでConstraintTemplate
とConstraint
を解説します。
これは、K8sRequiredLabels
という名前のConstraint CRDを定義したConstraintTemplate
になります。Regoで必須ラベルポリシーのviolation(ポリシー違反)の定義をしています。また、spec.crd.spec.validation.openAPIV3Schema
で関連するConstraintのparameters
フィールドのスキーマ定義をしています。
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
次に、上記ConstraintTemplateで定義されたConstraint CRD、K8sRequiredLabels
で、具体的にどのように
Gatekeeperはポリシーを実行するのかを定義したのが下記になります。これでGatekeeperがすべての名前空間でgatekeeper
ラベルが設定されているか検査します。
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-gk
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
# Note that "labels" is now contained in an array item, rather than an object key under "parameters"
- labels: ["gatekeeper"]
その他ConstraintTemplateとConstraintのサンプルについてはこちらを確認ください。
事前準備
それでは本題であるGatekeeperを活用して特定条件にマッチするKuberentesリソースにMutationポリシーを適用する試験を行っていきます。まずは、デプロイ先となるk8sクラスタとGatekeeperのインストールを行います。
KINDでKubernetesクラスタ作成
k8sクラスタをkind (Kubernetes in Docker)でローカルに構築します。
まずは、次のようなcontrol planeノード x 1、 workerノード x 1のk8sクラスタのためのkindクラスタ構成ファイル(cluster.yaml)を作成します。
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
次に、kind cliコマンドで、k8s v1.21.10のノードイメージでk8sクラスタを作成します。
K8S_NODE_IMAGE=v1.21.10
kind create cluster --name my-kind-cluster \
--image=kindest/node:${K8S_NODE_IMAGE} \
--config cluster.yaml
クラスタ構築が完了したら念の為にクラスタにアクセスしてみます。
kubectl get node
NAME STATUS ROLES AGE VERSION
my-kind-cluster-control-plane Ready master 37m v1.21.10
my-kind-cluster-worker Ready <none> 37m v1.21.10
Gatekeeperのインストール
こちらの手順に従いGatekeeperをクラスタにインストールします。なお、他にもHelm chartからのインストールも可能です。
# Gatekeeper v3.8.xのインストール
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.8/deploy/gatekeeper.yaml
# 最新版のインストールはこちら
# 著者が2022/07時点で試したときはGatekeeperイメージ pullが失敗したためv3.8.xをインストール
# kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
インストール後、gatekeeper-systemネームスペースに下記のようなPodが動いていることが確認できます。
kubectl get pods -n gatekeeper-system
NAME READY STATUS RESTARTS AGE
gatekeeper-audit-84dd4759f5-584xg 1/1 Running 0 6m42s
gatekeeper-controller-manager-6548c75cf9-7kt9h 1/1 Running 0 6m42s
gatekeeper-controller-manager-6548c75cf9-m2bmw 1/1 Running 0 6m42s
gatekeeper-controller-manager-6548c75cf9-vrg9z 1/1 Running 0 6m42s
特定ネームスペース作成されるPodにラベルを追加する
いよいよ本題の試験を行います。GatekeeperのMutation用CRDの1つであるAssignMetadataをつかって特定ネームスペース作成されるPodにラベルを追加してみます。
AssignMetadata (Mutationポリシー)をデプロイ
次のように、set-label-policy
という名前のAssignMetadataリソースをクラスタにデプロイします。 このAssignMetadataリソースにはネームスペースtestns1
に作成されるPodに、foo=bar
というラベルを追加するルールを定義しています。
kubectl apply -f - << EOF
apiVersion: mutations.gatekeeper.sh/v1beta1
kind: AssignMetadata
metadata:
name: set-label-policy
spec:
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
namespaces: ["testns1"]
location: "metadata.labels.foo"
parameters:
assign:
value: "bar"
EOF
なお、AssignMetadataの設定に関する詳細はこちらのページが参照になります。
サンプルPodをデプロイしポリシーが適用されることを確認する
まずは、テストで利用するネームスペース(testns1
とtestns2
)を作成します。
kubectl apply -f - << EOF
apiVersion: v1
kind: Namespace
metadata:
name: testns1
---
apiVersion: v1
kind: Namespace
metadata:
name: testns2
EOF
次に、サンプルアプリケーションのマニフェストをapp.yamlに出力します。
cat << EOF > app.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: httpbin
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
template:
metadata:
labels:
app: httpbin
spec:
serviceAccountName: httpbin
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF
testns1
とtestns2
の両ネームスペースにサンプルアプリケーションをデプロイします。
kubectl apply -f app.yaml -n testns1
kubectl apply -f app.yaml -n testns2
最後に、下記のように両ネームスペースにデプロイされたPodのラベルをチェックします。
kubectl get po -n testns1 --show-labels
NAME READY STATUS RESTARTS AGE LABELS
httpbin-75bc8bcf78-7wlhv 0/1 ContainerCreating 0 5s app=httpbin,foo=bar,pod-template-hash=75bc8bcf78
kubectl get pods -n testns2 --show-labels
NAME READY STATUS RESTARTS AGE LABELS
httpbin-75bc8bcf78-h9h22 0/1 ContainerCreating 0 3s app=httpbin,pod-template-hash=75bc8bcf78
無事、AssignMetadata、set-label-policy
のルールが適用され、testns1に作成されたPodにfoo=bar
というラベルを追加されていることがわかります。
さいごに
こちらの記事と本記事にてそれぞれKyvernoとGatekeeperのMutation機能を活用して特定条件にマッチするKuberentesリソースに変更を加える検証をしてみました。
両方使ってみた感想としては、簡単な処理であればどちらもさほど使用感は変わらないんじゃないかと(簡単な処理だからということは大いにあるが^^;)。Validation機能を含めて簡単に比較してみると、k8sネイティブのPolicy as CodeソリューションであるKyvernoのほうが直感的で学習カーブは低く、またMutationについてはGenerateルールなどGatekeeperにはない有望な機能も充実していて、Gatekeeperより一歩進んでいる印象ではあります。一方、Validationについて不雑な検査をする場合はRegoによる記述が可能なGatekeeperに分があるかと思います。どちらをつかうかは要件次第ということになるかと思います。
さいごにGatekeeperのMutation機能について話を戻すと、先述のとおり2022年7月現在ベータリリース状態であり、リソースのmetadetaセクションの変更に利用するAssignMetadataについてはlabel
とannotation
のみ(+既存のlabelやannotationの変更はできない)という制限があります。リリース頻度も高いので今後の動向に注目しつつ、プロダクション環境で利用するには十分に注意を払う必要があるかとおもいます。