Introduce Conftest

78f8131c19213b056eeb01899b47a182?s=47 yuhara
September 08, 2020

Introduce Conftest

78f8131c19213b056eeb01899b47a182?s=128

yuhara

September 08, 2020
Tweet

Transcript

  1. CloudNative Days Tokyo 2020
 
 
 RegoによるOPAポリシーの開発と
 Conftestを活用したポリシーチェックの実現
 
 Merpay

    SRE
 Takaaki Yuhara

  2. Agenda
 Conftest利用の背景
 Regoで記述したポリシーとConftestの活用
 ポリシーの開発とテスト
 01 02 03

  3. Conftest利用の背景
 Regoで記述したポリシーとConftestの活用
 ポリシーの開発とテスト
 01 02 03

  4. メルペイのシステム概要
 • マイクロサービスアーキテクチャ
 • Google Cloud Platform(GCP)を利用
 • アプリケーションはKubernetes上で稼働
 •

    マイクロサービスはそれぞれ下記が割り当てられる
 ◦ GCP Project
 ◦ Kubernetes Namespace

  5. 構成とフローの概略図
 Kubernetes Project Project A Project B Service A Team

    Service B Team Repository(GCP) Namespace A Namespace B Repository(k8s) Cloud Spanner Cloud Spanner Cloud Pub/Sub BigQuery Kubernetes Cluster CircleCI YAML HCL (Terraform)
  6. Production Readiness Checklist
 • サービスを安全にリリースするためのチェックリスト
 • マイクロサービスのリリース前にチェックを行う
 • 例)
 ◦

    Kubernetes
 ▪ CPUやMemoryのrequestsとlimits 
 ▪ preStop
 ▪ livenessProbe / readinessProbe
 ▪ runAsNonRoot
 ▪ HorizontalPodAutoscaler
 ▪ PodDisruptionBudget
 ◦ GCP
 ▪ Databaseのバックアップ
 ▪ Cloud StorageのObject Lifecycle Management 

  7. Open Policy Agent(OPA)
 https://www.openpolicyagent.org/docs/latest/ • OSSの汎用ポリシーエンジン
 • CNCFのincubatingのプロジェクト
 • Regoというポリシー記述言語で


    ポリシーを書く
 • ポリシーと入力されたデータを評価し結 果を返す

  8. Gatekeeper
 • Gatekeeper
 ◦ OPAをKubernetes上で使えるよう にしたもの
 ◦ KubernetesのAdmission Controllerと連携してOPAを利用
 ◦

    Webhook先をOPAにすることで API Server側でポリシーチェック
 https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/
  9. Gatekeeper
 • ConstraintTemplateとConstraint
 というCRDでポリシーを定義
 • Regoによるポリシーは
 ConstraintTemplate内に記述
 https://github.com/open-policy-agent/gatekeeper

  10. Conftest
 • Regoで記述したポリシーと入力データをコマンドラインで検証するツール
 • CI上で使われることを想定している
 • リモートにあるRegoのポリシーを利用できる
 • 様々なフォーマットに対応
 ❏

    YAML ❏ JSON ❏ INI ❏ TOML ❏ HOCON ❏ HCL ❏ HCL1
 ❏ CUE ❏ Dockerfile ❏ EDN ❏ VCL ❏ XML ❏ Jsonnet

  11. なぜConftestを使い始めたか
 • もともとCIでterraform planやkubectl validate/dry-runでの失敗を通知していて、ポ リシーチェックも導入しやすかった
 • Conftestが様々なフォーマットに対応している
 ◦ GCPのクラウドリソースはHCL形式のTerraformで管理


    ◦ KubernetesのマニフェストはYAMLで管理
 • KubernetesではRegoで記述したポリシーをCIとAPI Server側で両側面でチェックで きる
 ◦ CI時のチェックはConftest
 ◦ API Server側でのチェックはGatekeeper

  12. Conftest利用の背景
 Regoで記述したポリシーとConftestの活用
 ポリシーの開発とテスト
 01 02 03

  13. テストシナリオ
 • 例)HorizontalPodAutoscalerのspec.minReplicasが3以上であること
 apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: php-apache

    spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource:       <以下、省略> minReplicasが省略されていたり、
 値が3未満の場合は事前に気付きたい 

  14. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } • 例)HorizontalPodAutoscalerのspec.minReplicasが3以上であること

  15. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } package名は任意。

  16. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } ルール名は deny, violation, warn を使うことができる。 
 deny, violationではルールに合致するとExit codeが1, warnではルールに合致してもExit codeが0 
 ルール名をviolation_xxxなどのように指定してもOK 
 

  17. デバッグ時の出力
 内部でdata.<package名>.<ルール名> という形で扱われているためルール名を分けておくと便利 


  18. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } ルールに合致した場合に返す値 

  19. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } 入力データ(input)のkindがHorizontalPodAutoscalerであるか評価 
 (Gatekeeperでは入力データが input.review.objectなのでConftestとGatekeeper両方でポリシーを使う場合は入 力データを評価するような判定を入れると良い) 

  20. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } input.spec.minReplicas が3以上であればgoodという変数にtrueが代入。3未満ならfalseが代入される。 

  21. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } パイプの左のgoodという変数に値を渡しています。つまりsatisfiedは要素が1つのarrayとなります。ただし minReplicasが省略されている場合はgoodが空なのでsatisfiedは要素が0のarrayです。 
 satisfied minReplicasが3以上 [true] minReplicasが3未満 [false] minReplicasが省略 []
  22. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } any関数でsatisfied内に1つでもtrueがある場合はtrueとなる。 
 satisfied any(satisfied) minReplicasが3以上 [true] true minReplicasが3未満 [false] false minReplicasが省略 [] false
  23. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } spec.minReplicasが3以上でない場合を条件として合致させたいため、否定のnotを使用 
 satisfied any(satisfied) not any(satisfied) minReplicasが3以上 [true] true false minReplicasが3未満 [false] false true minReplicasが省略 [] false true
  24. Regoでポリシーを記述
 package hpa description := "This is production readiness check

    itme, please check http://~" violation_min_replicas_is_not_set[{"msg": msg, "description": description}] { input.kind = "HorizontalPodAutoscaler" satisfied := [good | good := input.spec.minReplicas >= 3] not any(satisfied) msg := sprintf("minReplicas in HorizontalPodAutoscaler %s must be set", [input.metadata.name]) } 最後にmsgを生成

  25. コマンド実行時の出力(JSON)
 [ { "filename": "sample-hpa.yaml", "warnings": [], "failures": [ {

    "msg": "minReplicas in HorizontalPodAutoscaler sample must be set", "metadata": { "description": "This is a production readiness check item, please check https://~" } } ], "successes": [] } ] $ conftest test --policy policy/hpa --namespace hpa --input yaml --output json sample-hpa.yaml ConftestをCIに組み込んで、
 適切な情報をディベロッパーにフィードバック

  26. Agenda
 Conftest利用の背景
 Regoで記述したポリシーとConftestの活用
 ポリシーの開発とテスト
 01 02 03

  27. ポリシーの開発とテスト
 • OPAではポリシーをコードで開発、運用できる(Policy as Code)
 • Regoのポリシーをテストするフレームワークがある
 • テスト用のルールはtest_xxxとする
 •

    withキーワードを使って入力データをMockのデータに置き換えできる
 package hpa msg := "minReplicas in HorizontalPodAutoscaler sample must be set" test_min_replicas_less_than_three { violation_min_replicas_is_not_set[{"msg": msg, "description": description}] with input as { "kind": "HorizontalPodAutoscaler", "metadata": {"name": "php-apache"}, "spec": {"maxReplicas": 10, "minReplicas": 1} } }
  28. テスト実行
 • opa testコマンドを使ってテストを実行
 • --coverage --format=jsonオプションをつけるとカバレッジも報告される
 $ opa test

    -v hpa.rego hpa_test.rego data.hpa.test_min_replicas_less_than_three: PASS (2.977518ms) -------------------------------------------------------------------------------- PASS: 1/1 $ opa test --coverage --format=json hpa.rego hpa_test.rego | jq .coverage 60
  29. まとめ
 • Production Readiness Checklistを適切にチェックし安全にリリースするためにOpen Policy AgentのConftestを利用し始めた
 • KubernetesのYAML manifestやTerraformのHCL等をCIでチェック


    • Regoで記述したポリシーのテストもCIで実施

  30. • 本日の内容は、下記のブログでも紹介しています
 https://engineering.mercari.com/blog/entry/introduce_conftest/