Admission Webhookのすゝめ
はじめに
最近公私ともにContoller開発を行うに当たり、validateやmutateを行うWebhookについて触れる機会が多いのです。そこで、自分なりに理解している範囲で現在のwebhook事情についてまとめてみます。
Webhookライブラリの種類
-
kubernetes-sigs/controller-runtime
言わずと知れたcontroller開発ライブラリです。
この中で提供されているwebhookを利用することで、kubebuilderなどのフレームワークでもwebhookが簡単に実装可能となっています。 -
knative/pkg/webhook
サーバーレスな基盤を構築することが可能なknativeですが、パッケージが個別で公開されており、その中でwebhookライブラリが使用できます。若干学習コストが高いです。 -
slok/kubewebhook
非常に軽量でシンプルなライブラリです。サンプルが提供されており、これをいじるだけで実装出来ます。上記2つと異なり、証明書の自動ローテーションを行うには工夫が必要。 -
snorwin/k8s-generic-webhook
kubebuilderなどフレームワークで作成するwebhookによく似たコードで実装可能なシンプルなライブラリです。フレームワークでの開発経験がある方であれば、使いこなせるのではないでしょうか。
今回はシンプルで拡張性の高いkubewebhookに絞って紹介します。(私のお気に入りだからというのは内緒
特徴
実装が非常に簡単であり、ライブラリ自体のコードも読みやすく書かれています。
また、通常のvalidateやmutateだけでなく、各処理をchainとしてつなげることが可能です。
- 以下validatorの該当箇所抜粋
type chain struct {
validators []Validator
logger log.Logger
}
// NewChain returns a new chain of validators.
// - If any of the validators returns an error, the chain will end.
// - If any of the validators returns an stopChain == true, the chain will end.
// - If any of the validators returns as no valid, the chain will end.
func NewChain(logger log.Logger, validators ...Validator) Validator {
return chain{
validators: validators,
logger: logger,
}
}
chainを用いた実装は以下のコードを確認ください。
kubewebhook提供サンプル
以下のサンプルを確認いただくと分かる通り、ほぼこれだけでmutateが完結します。
★流れ
実際のmutate処理作って
↓
MutatorFunc()に食わせてあげて
↓
Webhookの呼び出しをしてあげるだけ。
注意点として、処理対象のリソースの型をmetav1.Object
にアサーションしてあげるくらいです。
本番で使用する場合のサンプルまであります。最高です。
kubewebhookを使用した実装サンプル
Image tagが付与されていない場合にlatest
tagをデフォルト値として付与するMutatorを作成してみました。(すでにkubernetesのデフォルトはlatestであると理解していますが、kubectl describeの見た目を変えたかったのです。
出力結果
kubewebhookでmutateなりvaidateなりを行うと以下のようにjsonで出力され、op
からどのようなoperationがなされたのかがわかるようになっています。今回はImage tagの書き換えをおこなったため、replace
となっていますが、Fieldの追加を行うと、add
と出力されます。また、どのような変更がされたかも確認可能となっており、非常に親切なログの出し方と思います。
$ kubectl logs default-imagetag-webhook-5bd6b8b685-t26t4 -f
time="2022-07-11T00:41:14Z" level=info msg="Listening on :8080"
time="2022-07-11T00:42:12Z" level=debug msg="Webhook mutating review finished with: '[{\"op\":\"replace\",\"path\":\"/spec/containers/0/image\",\"value\":\"nginx:latest\"}]' JSON Patch" dry-run=false kind=v1/Pod name= ns=default op=create path=/mutate request-id=682f8ab2-6ace-4663-a748-96299cf4f650 trace-id= webhhok-type=mutating webhook-id=imageTagMutator webhook-kind=mutating wh-version=v1
time="2022-07-11T00:42:12Z" level=info msg="Admission review request handled" dry-run=false duration=276.777541ms kind=v1/Pod name= ns=default op=create path=/mutate request-id=682f8ab2-6ace-4663-a748-96299cf4f650 svc=http.Handler trace-id= webhook-id=imageTagMutator webhook-kind=mutating wh-version=v1
証明書ローテーションについて
WebhookのPodとkube-apiserver[1]間はTLSで認証にて通信を行います。
そのため、kube-apiserverで証明書がローテーションされる[2]と、Webhook PodにあるHTTPサーバの証明書も何らかの方法でローテーションしてあげる必要があります。
ただ、kubewebhookには証明書の自動ローテーション機能は実装されていません。
そこで、contoroller-runtimeのcertwatcherを利用することで自動ローテーションが可能です。
使い方は、同じくcontoroller-runtimeのpkg/webhook/server.goをご確認ください。
certwatcherは証明書の変更を監視しており、変更があるとロード対象の証明書を変更します。
そして、webhook側はcertWatcher.GetCertificateでtls.ClientHelloInfoを待ち受けているので、リクエストがあるたびに証明書をロードします。そのため、常に最新の証明書が使えるというわけです。
func (s *Server) Start(ctx context.Context) error {
:
cfg := &tls.Config{ //nolint:gosec
NextProtos: []string{"h2"},
GetCertificate: certWatcher.GetCertificate,
MinVersion: tlsMinVersion,
}
:
参考リンク
-
ここでいうkube-apiserverとはValidatingWebhookConfigurationやMutatingWebhookConfigurationを指します。 ↩︎
-
ValidatingWebhookConfigurationやMutatingWebhookConfigurationの証明書のローテーションはcert-managerで自動で行うか、自前で行うかの2択です。 ↩︎
Discussion