Slide 1

Slide 1 text

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


Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

メルペイのシステム概要
 ● マイクロサービスアーキテクチャ
 ● Google Cloud Platform(GCP)を利用
 ● アプリケーションはKubernetes上で稼働
 ● マイクロサービスはそれぞれ下記が割り当てられる
 ○ GCP Project
 ○ Kubernetes Namespace


Slide 5

Slide 5 text

構成とフローの概略図
 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)

Slide 6

Slide 6 text

Production Readiness Checklist
 ● サービスを安全にリリースするためのチェックリスト
 ● マイクロサービスのリリース前にチェックを行う
 ● 例)
 ○ Kubernetes
 ■ CPUやMemoryのrequestsとlimits 
 ■ preStop
 ■ livenessProbe / readinessProbe
 ■ runAsNonRoot
 ■ HorizontalPodAutoscaler
 ■ PodDisruptionBudget
 ○ GCP
 ■ Databaseのバックアップ
 ■ Cloud StorageのObject Lifecycle Management 


Slide 7

Slide 7 text

Open Policy Agent(OPA)
 https://www.openpolicyagent.org/docs/latest/ ● OSSの汎用ポリシーエンジン
 ● CNCFのincubatingのプロジェクト
 ● Regoというポリシー記述言語で
 ポリシーを書く
 ● ポリシーと入力されたデータを評価し結 果を返す


Slide 8

Slide 8 text

Gatekeeper
 ● Gatekeeper
 ○ OPAをKubernetes上で使えるよう にしたもの
 ○ KubernetesのAdmission Controllerと連携してOPAを利用
 ○ Webhook先をOPAにすることで API Server側でポリシーチェック
 https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/

Slide 9

Slide 9 text

Gatekeeper
 ● ConstraintTemplateとConstraint
 というCRDでポリシーを定義
 ● Regoによるポリシーは
 ConstraintTemplate内に記述
 https://github.com/open-policy-agent/gatekeeper

Slide 10

Slide 10 text

Conftest
 ● Regoで記述したポリシーと入力データをコマンドラインで検証するツール
 ● CI上で使われることを想定している
 ● リモートにあるRegoのポリシーを利用できる
 ● 様々なフォーマットに対応
 ❏ YAML ❏ JSON ❏ INI ❏ TOML ❏ HOCON ❏ HCL ❏ HCL1
 ❏ CUE ❏ Dockerfile ❏ EDN ❏ VCL ❏ XML ❏ Jsonnet


Slide 11

Slide 11 text

なぜConftestを使い始めたか
 ● もともとCIでterraform planやkubectl validate/dry-runでの失敗を通知していて、ポ リシーチェックも導入しやすかった
 ● Conftestが様々なフォーマットに対応している
 ○ GCPのクラウドリソースはHCL形式のTerraformで管理
 ○ KubernetesのマニフェストはYAMLで管理
 ● KubernetesではRegoで記述したポリシーをCIとAPI Server側で両側面でチェックで きる
 ○ CI時のチェックはConftest
 ○ API Server側でのチェックはGatekeeper


Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

テストシナリオ
 ● 例)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未満の場合は事前に気付きたい 


Slide 14

Slide 14 text

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以上であること


Slide 15

Slide 15 text

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名は任意。


Slide 16

Slide 16 text

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 
 


Slide 17

Slide 17 text

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


Slide 18

Slide 18 text

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]) } ルールに合致した場合に返す値 


Slide 19

Slide 19 text

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両方でポリシーを使う場合は入 力データを評価するような判定を入れると良い) 


Slide 20

Slide 20 text

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が代入される。 


Slide 21

Slide 21 text

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が省略 []

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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を生成


Slide 25

Slide 25 text

コマンド実行時の出力(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に組み込んで、
 適切な情報をディベロッパーにフィードバック


Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

ポリシーの開発とテスト
 ● 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} } }

Slide 28

Slide 28 text

テスト実行
 ● 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

Slide 29

Slide 29 text

まとめ
 ● Production Readiness Checklistを適切にチェックし安全にリリースするためにOpen Policy AgentのConftestを利用し始めた
 ● KubernetesのYAML manifestやTerraformのHCL等をCIでチェック
 ● Regoで記述したポリシーのテストもCIで実施


Slide 30

Slide 30 text

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