Help us understand the problem. What is going on with this article?

Kubernetes: conftest でマニフェストをテストする

More than 1 year has passed since last update.

この記事では conftest というツールを使って、Kubernetes のマニフェストをテストする方法を紹介します。conftest のバージョンは v0.11.0 で確認しています。

マニフェストのテスト

本番環境向けの Kubernetes のマニフェストでは様々な点を考慮する必要があります。考慮する点は環境や組織ごとに違うと思いますが、例えば以下のようなものが挙げられます。

  • privileged の利用を禁止したい
  • 新しい API Version を利用することを推奨する
  • livenessProbe / readinessProbe の設定を推奨する
  • resource request / limit の設定を推奨する

この記事では conftest というツールを使って、これらのポリシーを定義し、マニフェストがそれを満たしているかテストする方法を紹介します。

なお、PodSecurityPolicyopen-policy-agent/gatekeeper のような Kubernetes の API Server 側でポリシーをチェックする仕組みも存在します。conftest は API Server 側でチェックする仕組みと比べ、単独で動作するため CI などに組み込みやすいという特徴があります。一方 API Server 側のチェックに比べると強制力は弱いので、組み合わせて利用する形になるかと思います。

conftest とは

image.png

conftest は YAML や JSON などの構造化データに対してユニットテストを記述できるツールです。以下のような特徴があります。メインの開発者 @garethr さんは、kubetestkubeval など他にも Kubernetes 関連のツールを作っている方です。

  • YAML、JSON など構造化データに対するポリシーが書ける
    • 汎用的なツールで Kubernetes に特化しているわけではない
  • ポリシーはOpen Policy Agent (OPA) で使われているポリシー言語 Rego で記載する
    • ポリシー自体にもユニットテストがかける (opa test)
  • brew の tap が用意されていてインストールが簡単
  • ポリシーはOCI 準拠のレジストリに格納することもできる
    • ポリシーを社内で共有するといったことがやりやすい
  • チェックは必須の deny, 任意の warn の 2 つに別れていて CI にも導入しやすい
    • deny のチェックに引っかかったときだけ、コマンドの戻り値が 1 になる
  • クラスタ上のデータは参照できないので、Ingress の host 名の衝突などはチェックできない

conftest を試してみる

インストール

Mac の Homebrew で簡単にインストールが可能です。

brew tap instrumenta/instrumenta
brew install conftest

テスト対象のマニフェスト

テスト対象のマニフェストを用意します。hello-deploy.yaml として保存します。

hello-deploy.yaml
apiVersion: extensions/v1beta1  # API Version が古い
kind: Deployment
metadata:
  name: hello
spec:
  selector:
    matchLabels:
      run: hello
  template:
    metadata:
      labels:
        run: hello
    spec:
      containers:
      - image: nginx:1.17.3
        name: multi
        securityContext:  # privileged は禁止したい
          privileged: true

ポリシーを記載する

ここでは例として以下の 2 点をチェックしたいと思います。

  • Deployment の API Version が最新 (apps/v1)であること
    • 必須のチェックとはせずに警告だけを出す (warn)
  • privileged を利用していないこと
    • 必須のチェックとする (deny)

ポリシーは Open Policy Agent (OPA) で使われているポリシー言語 Rego で記載します。Rego 自体の文法については長くなるため、この記事では扱いません。文法の詳細はOPA の公式ドキュメント をご覧ください。

conftest では deny, warn の 2 種類のルールを使ってチェックを行います。deny の場合は一つでも引っかかればコマンドの戻り値が 1 (エラー)となりますが、warn の場合は戻り値には影響しません。そのため CI などで必須チェックとしたいものは deny、任意でチェックするものは warn にすることができます。なお fail-on-warn というオプションで warn の場合も戻り値を 1 にすることもできます。

conftest のデフォルトではポリシーはチェック対象のファイルと同じディレクトリの policy/ にある全てのファイルを読み込みます。以下を policy/mypolicy.rego として保存します。

policy/mypolicy.rego
package main

# deprecated な deployment のバージョンリスト
deprecated_deployment_version = [
  "extensions/v1beta1",
  "apps/v1beta1",
  "apps/v1beta2"
]

# 最新の API Version が使われているかのチェック
warn[msg] {
  input.kind == "Deployment"
  input.apiVersion == deprecated_deployment_version[i]
  msg = "最新の APIVersion apps/v1 を指定してください"
}

# privileged が使われているかのチェック
deny[msg] {
  input.kind == "Deployment"
  input.spec.template.spec.containers[_].securityContext.privileged == true
  msg = "privileged はセキュリティ上の理由で許可されていません"
}

ポリシーでテストする

以下のように conftest test でテスト対象を指定して、ポリシーを満たしているかテストします。

image.png

$ conftest test hello-deploy.yaml
WARN - hello-deploy.yaml - 最新の APIVersion apps/v1 を指定してください
FAIL - hello-deploy.yaml - privileged はセキュリティ上の理由で許可されていません

マニフェストを修正して、テストが通るようになったことを確認します。

$ conftest test hello-deploy.yaml
# 問題ない場合は何も表示されない

OCI レジストリでポリシーを管理する

conftest は Docker Registry など OCI 準拠のレジストリを使ってポリシーを管理することができます。複数の環境でポリシーを共有したい場合に便利でしょう。

ローカルに Docker Registry を立て試してみます。(実際に利用する場合は Docker Hub など外部のレジストリを使うことになります)

# Docker Registry をローカルに立てる
$ docker run -it --rm -p 5000:5000 registry:2.7.1

policy ディレクトリがあるディレクトリで、policy を OCI レジストリにアップロードします。v0.11.0 では成功しても特に何も表示されません。

$ conftest push localhost:5000/mypolicy:v0.1 policy

他のディレクトリに移動してアップロードした policy ファイルを取得できるか試してみます。

# 別の temp ディレクトリに移動
$ cd $(mktemp -d)

# pull する
$ conftest pull localhost:5000/mypolicy:v0.1

# ポリシーファイルが取得できている
$ ls policy
mypolicy.rego

ポリシーの情報は conftest.toml という設定ファイルで記述することもできます。

conftest.toml
[[policies]]
repository = "localhost:5000/mypolicy"
tag = "v0.1"

以上のファイルを置いた状態で、conftest に --update オプションを付けて実行すると自動的にレジストリからポリシーを取得してテストを実行してくれます。

$ conftest test --update hello-deploy.yaml
WARN - hello-deploy.yaml - 最新の APIVersion apps/v1 を指定してください
FAIL - hello-deploy.yaml - privileged はセキュリティ上の理由で許可されていません

ポリシーのユニットテストを記載する

ポリシーが複雑になってくると、ポリシー自体のユニットテストを記載したくなるかもしれません。OPA 自体の仕組みを使ってユニットテストを書くことが可能です。詳しくは公式ドキュメントのHow Do I Test Policies?をご覧ください。

$ opa test -v .
data.main.test_deprecated_deployment: PASS (491ns)
data.main.test_deprecated_deployment#01: PASS (354ns)
data.main.test_deprecated_deployment#02: PASS (348ns)
data.main.test_deprecated_deployment_allowed: PASS (253ns)
data.main.test_privileged_deployment: PASS (243ns)
data.main.test_privileged_deployment_allowed: PASS (239ns)
--------------------------------------------------------------------------------
PASS: 6/6

ルール名に test_ という prefix がついたものがテストとして扱われます。慣例的にファイル名にも _test.rego とつける場合が多いようですが、必須ではありません。以下は先程のポリシーに対するユニットテストの例になります。

policy/mypolicy_test.rego
package main

test_deprecated_deployment {
  warn["最新の APIVersion apps/v1 を指定してください"] with input as {"kind": "Deployment", "apiVersion": "extensions/v1beta1"}
}

test_deprecated_deployment {
  warn["最新の APIVersion apps/v1 を指定してください"] with input as {"kind": "Deployment", "apiVersion": "apps/v1beta1"}
}

test_deprecated_deployment {
  warn["最新の APIVersion apps/v1 を指定してください"] with input as {"kind": "Deployment", "apiVersion": "apps/v1beta2"}
}

test_deprecated_deployment_allowed {
  # 最新の API Version の場合は warn が出ない
  not warn["最新の APIVersion apps/v1 を指定してください"] with input as {"kind": "Deployment", "apiVersion": "apps/v1"}
}

test_privileged_deployment {
  deny["privileged はセキュリティ上の理由で許可されていません"] with input as
  {
    "kind": "Deployment",
    "spec": {
      "template": {
        "spec": {
          "containers": [
            {
              "name": "app1",
              "image": "nginx",
            },
            {
              "name": "app2",
              "image": "nginx",
              "securityContext": { # privileged が使われている
                "privileged": true,
              }
            }
          ],
        }
      }
    }
  }
}

test_privileged_deployment_allowed {
  not deny["privileged はセキュリティ上の理由で許可されていません"] with input as
  {
    "kind": "Deployment",
    "spec": {
      "template": {
        "spec": {
          "containers": [
            {
              "name": "app1",
              "image": "nginx",
            },
            {
              "name": "app2",
              "image": "nginx",
            }
          ],
        }
      }
    }
  }
}
zlab
技術で新しい世界へシフトする。
https://zlab.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした