LoginSignup
10
4

More than 1 year has passed since last update.

OPA/Gatekeeperで特定条件にマッチするKuberentesリソースにMutationポリシーを適用する

Last updated at Posted at 2022-07-19

本記事では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ポリシー

Mutationv3.4.0でようやくAlpha版がリリースされたGatekeeperにおいては比較的新しい機能になります。なお、2022年7月現在、最新のv3.9.0においてもベータリリース状態であるため、プロダクション環境での利用については十分に注意が必要といえるでしょう。

Mutaionポリシーの定義には次の3つのCRDが利用できます。

  • AssignMetadata: リソースのmetadataセクションの変更を定義するCRD。metadataはとてもセンシティブであり変更により意図しない結果が生じる可能性があるため変更の自由度は制限されています。現時点で、AssignMetadataの変更は、labelannotationのみに制限されています。さらに、既存の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で定義されます

実際のサンプルでConstraintTemplateConstraintを解説します。
これは、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をデプロイしポリシーが適用されることを確認する

まずは、テストで利用するネームスペース(testns1testns2)を作成します。

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

testns1testns2の両ネームスペースにサンプルアプリケーションをデプロイします。

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についてはlabelannotationのみ(+既存のlabelやannotationの変更はできない)という制限があります。リリース頻度も高いので今後の動向に注目しつつ、プロダクション環境で利用するには十分に注意を払う必要があるかとおもいます。

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4