CNCF Landscape内のあるプロジェクトで活動の第一歩を踏み出したので、その模様をお伝えします。本記事が同じような興味を持ってる方の参考になればと思います。
本記事で得られること
- CNCFの新しいSandbox ProjectであるKyvernoの概要
- 成長が早いCloud Native界隈のOSSプロジェクトに参加するとどうなるか、の疑似体験
本活動の動機
近頃Cloud Native周辺の動向をウォッチしている。本も買ってお勉強したり、オンラインになったのを機にKubeConに参加して色々セッションを聴いていると、何か自分で手を動かしてみたくなってくるのだが、特に仕事でもCloud Nativeな技術(K8s)に触れる機会は今のところなく、個人的にマイクロサービスとしてK8s上で動かして何かしたい、ということも見当たらない。ということで、数あるCNCFのプロジェクトのどれかに個人的に参加してみることにした。が、星の数ほどあるCNCF Landscape上のプロジェクトの中から、参加するプロジェクトを決めるだけで悩ましい。ということで、CNCFに加わって一番日の浅いSandbox Projectの中から選択してみることにした。
https://www.cncf.io/sandbox-projects/
https://github.com/cncf/toc/blob/master/docs/PROJECTS.csv
Project | Accepted | Stage | About |
---|---|---|---|
SchemaHero | 11/10/2020 | Sandbox | A Kubernetes operator for declarative database schema management |
Cloud Development Kit for Kubernetes (cdk8s) | 11/10/2020 | Sandbox | Define Kubernetes native apps and abstractions using object-oriented programming |
cert-manager | 11/10/2020 | Sandbox | Automatically provision and manage TLS certificates in Kubernetes |
OpenKruise | 11/10/2020 | Sandbox | Automate everything on Kubernetes |
Tinkerbell | 11/10/2020 | Sandbox | a flexible bare metal provisioning engine |
Pravega | 11/10/2020 | Sandbox | Streaming Storage Platform |
Kyverno | 11/10/2020 | Sandbox | Kubernetes Native Policy Management |
利用言語とプロジェクトがターゲットとしている技術エリア、コミュニティの規模などから、Kyvernoをチョイスした。素人ながらPolicy managementにおいて現在Open Policy Agent(OPA)というのがソリューションの1つとして良く目にするが、OPAがK8sに限らず利用できるソリューションであるのに対して、K8s Nativeな形で機能を提供するという方向性にチャレンジ精神を感じたのも選択理由の1つだったりする。
Kyvernoとは
Kubernetesの中ではPolicy Mangementといくつか種類があり、ポッド間のネットワークトラフィックをセキュアに保つためのNetwork Policy
、ポッドの権限やアクセスポリシーを制御するPod Security Policy
、リソースの使用量を管理するQuotas and Limit Range
がある。Kyvernoでカバーする範囲はPod Security Policy
に該当し、Kubernetes上で動かす各ワークロードの設定をクラスタ全体で一貫したルールに則って運用したい場合に向いている。近年では、各企業が社内の開発部隊向けに自社のKubernetesプラットフォームを立ち上げて運用しているケースが多くある。このような社内システムを運用するプラットフォームチームが、社内の運用ルールを全チーム横断で展開する場合などに、本機能が有効である。
たとえば、Kubernetesクラスタをセキュアにするために実施すべき10のプラクティスなどをポリシーとしてリソース化して、あらゆる操作に当てはめると漏れなく、最低限の対策が展開できる。
まずはコミュニティにアクセスしてみる
コミュニケーションのチャネルはGoogle Group(ML)とSlackと月1回のCommunity Meetings。
Google GroupはMeeting連絡程度にしか使われておらず、Slackが主な模様。slack.k8s.io にサインアップして #kyverno
チャンネルへ。
すぐにレスポンスをくれて、good first issue
なissueを更新してくれて、手の付けやすいissueの用意までしてくれた。とてもinclusiveである。
ということでログレベルの修正タスクからチャレンジしてみることにする。
環境のセットアップ
kindでclusterを立ち上げる
今まではDocker Desktop For WindowsのKubernetesクラスタをWSL2環境から使っていたが、最近観た以下のKubeConのチュートリアルセッション(おすすめ!)で使っていたkind上でkyverno
を触ってみる。
kyvernoのインストール
Custom Resource DefinitionであるKyvernoはhelmで簡単にインストールができた
ポリシーリソースの構造を簡単に学ぶ
上記の図で十分ではあると思うが、Policy
は1つ以上のRule
ブロックで構成されており、各Rule
ブロックでは適用するリソースの条件をMatch
ブロックに記述し、ヒットした際のアクションをMutate
,Validate
or Generate
で記述する構造になっている。
簡単な動作テスト
Kubernetes上で動かすアプリケーションには以下のようなラベルをメタデータとして持つことを推奨しているが、決して必須ではない。ラベルがないからクラスタ上で動作しない、ということはない。が、組織内の運用上のルールで、外部ツールとの連携や効率的な情報のクエリを可能にするために、特定のメタデータについては入力必須とするようなポリシーをクラスタ横断的に制定したい場合の例である。
クラスタ上のあらゆるPod
に対して、app.kubernetes.io/name
のラベル付与を強制するポリシーになる。
# require-labels.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
spec:
validationFailureAction: enforce
rules:
- name: check-for-labels
match:
resources:
kinds:
- Pod
validate:
message: "label `app.kubernetes.io/name` is required"
pattern:
metadata:
labels:
app.kubernetes.io/name: "?*"
これを適用する。
$ kubectl create -f require-labels.yaml
clusterpolicy.kyverno.io/require-labels created
試しにメタデータを一切付与せずにコマンドラインからnginxのdeploymentを作成すると、以下の通りValidation error
となって、deploymentに失敗する。validationFailureAction
をaudit
にすると警告ログだけ残してdeploymentは成功するようにもできる。
$ kubectl create deployment nginx --image=nginx
error: failed to create deployment: admission webhook "nirmata.kyverno.resource.validating-webhook" denied the request:
resource Deployment/default/nginx was blocked due to the following policies
require-labels:
autogen-check-for-labels: 'Validation error: label `app.kubernetes.io/name` is required; Validation rule autogen-check-for-labels failed at path /spec/template/metadata/labels/app.kubernetes.io/name/'
一方、同じコマンドにラベルを付与するオプションを追加するとエラーもなく、deploymentによってpodの生成に成功する。
$ kubectl run nginx --image nginx --labels app.kubernetes.io/name=nginx
pod/nginx created
まずは問題の再現から
issueは、ログレベルをerror
からinfo
に修正するというものだ。まずは問題の事象を再現させて、それでコレをこんな風に直すよ、みたいな感じでコミュニティと会話しながら進めていこうと思う。K8s初級者にとっては、1つ目のハードルである事象の再現だ。
早速出てきたAnti Affinity
であるが、アプリケーションの可用性を少しでも高めるためにレプリカは、異なるホスト上に配置すべし、というプラクティスであると理解。逆に通信性能観点で同一ゾーンへの展開を強制するのがAffinity
らしい。
再現テストをするのに、さっそくローカルのクラスタを複数ノード構成に手直しが必要か。といっても簡単に変更できた。
$ cat kind.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
$ kind create cluster --config ./kind.yaml
$ k get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready master 117s v1.19.1
kind-worker Ready <none> 87s v1.19.1
kind-worker2 Ready <none> 87s v1.19.1
まずは何もポリシー適用せずに複数レプリカをデプロイしてみる。
# multi-replicas-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: busybox
distributed: required
name: busybox
spec:
replicas: 2
selector:
matchLabels:
app: busybox
distributed: required
template:
metadata:
labels:
app: busybox
distributed: required
spec:
containers:
- image: busybox:1.28
name: busybox
command: ["sleep", "9999"]
すると既に各ノードに分散してポッドがデプロイされていることが分かる。何回か試したが、毎回各ノードにポッドが1つずつデプロイされる。ポリシーがなくても良くないか?
$ kubectl apply -f multi-replicas-deploy.yaml
deployment.apps/busybox created
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-6c4fc5bbb7-cxnql 1/1 Running 0 14s 10.244.1.8 kind-worker2 <none> <none>
busybox-6c4fc5bbb7-hnppd 1/1 Running 0 14s 10.244.2.6 kind-worker <none> <none>
とりあえず、一度deploymentを削除してanti-affinityを強制するポリシーを適用してみる。結果はポリシー適用前と変わらなかったが、このポリシーが条件にヒットしたリソースをmutateするルールなので、deploymentのマニフェストを再確認するとコマンドからapplyしたマニフェストに対して、anti-affinityの設定が追加されていることが分かった。
$ git clone https://github.com/kyverno/kyverno.git
$ kubectl apply -f kyverno/samples/more/create_pod_antiaffinity.yaml
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-847bbfdbdc-w465p 1/1 Running 0 27s 10.244.1.9 kind-worker2 <none> <none>
busybox-847bbfdbdc-w55ft 1/1 Running 0 27s 10.244.2.7 kind-worker <none> <none>
$ kubectl get deployments.apps busybox -o yaml
...
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox
topologyKey: kubernetes.io/hostname
weight: 1
...
で、この状態からkyvernoのログを確認すると、issueにあった症状が確認されました。
E1211 17:34:12.069581 1 notequal.go:36] EngineMutate "msg"="Failed to resolve variable" "error"="could not find variable request.object.metadata.labels.app at path " "kind"="Deployment" "name"="kyverno" "namespace"="kyvernotest" "policy"="insert-podantiaffinity" "rule"="insert-podantiaffinity" "variable"=null
ログの出力個所から辿って、プログラムの構造も少しだけ理解したので、ログレベルをinfo
に変えて動かしてみることにする。が、ソースからkyvernoをインストールする方法が分からない。から、さっそくコミュニティに聞いてみることにした。
- Github上のwiki曰く、
kustomize
をインストールしてレポジトリのルートにてmake release
を実行すると、ローカルにdefinitions/install.yaml
ができるので、これを改めapplyするとローカルビルドのkyvernoがデプロイできるようである。
最後に
時間の都合上、issueの途中で本記事は一旦終わることにする。記事前半で見て頂けたかと思うが、コミュニティは非常に反応が早く、急に現れたKubernetes初級者の私にも時間を割いて丁寧に対応してくれた(と思っている)。新しいプロジェクトに限らず、もし興味があるプロジェクトがあるようであれば、門戸を叩いてみることを是非お勧めしたい。
本記事はissueクローズまで五月雨ではあるが更新していきたい。