Kube API Server
Index
コードリーディング用ブランチ/タグ
TODO
- kubectl exec/log/attach周りのAPI Server側の実装をチェック
- Extending API Serverの登録処理を確認(どのように登録されてAddAPIServiceまでたどり着くのか?多分sample-apiserverあたりを試す?): https://github.com/kubernetes/sample-apiserver/blob/master/docs/minikube-walkthrough.md に書いてあるとおり普通に手動でAPIServiceを作成するだけなのでは?
- CRDのハンドリング処理を確認(Extending API ServerのCRD版を調べる)
- Admission Webhook関連処理を調べる
- 認証認可などのServerChainについて調べる: https://zenn.dev/link/comments/1fd911674ab64f
- OpenAPIについて調べる
-
名前空間やリソースクォータ周りがどのように実装されているか?
- https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/aggregated-api-servers.md#handling-global-policies
- 多分これ、普通にAdmission Controllerのプラグインの1つとして実装されている
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/admission.go
-
egressSelectorについて
- https://kubernetes.io/docs/concepts/architecture/control-plane-node-communication/#ssh-tunnels
- https://kubernetes.io/docs/tasks/extend-kubernetes/setup-konnectivity/
- コントロールプレーンからノードへのログ取得したりattachしたりといった通信を行う際にSSLで安全な通信を行うためのプロキシを提供する機能のよう
- 逆にこれを設定していないと、コントロールプレーンからノードへの接続はhttpによる暗号化されていない通信になるとのこと
- server side apply
- egressSelectorとkonnectivityの実装詳細
- OpenID Connectを使った実行基盤とのユーザー連携
エントリーポイント
Run
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L122
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L142
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L178-L194
リクエストに対して処理を行うチェーンの生成?
TODO 多分はじめの段階だとまだ良く理解できないと思う
中身
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L198 - 各種nodeやpod, serviceに接続するための何かを生成?
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L203 - config, serviceにつなぎにいくための何か、アドミッションプラグイン間のリソース共有を行う何かを生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L209 - apiExtensionsConfigの生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L214 - apiExtensionsServerの生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L219 - kubeAPIServerの生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L225 - aggregatorConfigの生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L229 - aggregatorServerの生成
server.PrepareRun()
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L188
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L333:25
中身
- .io/kube-aggregator/pkg/apiserver/apiserver.go#L334-L340 - s.openAPIConfigがあればopenAPIAggregationController.Runを実行するPostStartHookを追加
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L342 - s.GenericAPIServer.PrepareRun() GenericAPIServer側でもここでPrepareRunしてる
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L344-L360 - 要はs.openAPIAggregationControllerなるものは生成してる? TODO: これ何?
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L359 - preparedAPIAggregatorなるものを生成してる
s.GenericAPIServer.PrepareRun() 中身
これ
-
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L296 - TODO これは渡されてるもの調べないとわからない
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L298-L302 - s.openAPIConfig に基づくサーバー設定をwebサーバーに設定してる? TODO: わりかし重要そうだけどよくわからない
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L304-L310 - Healthz/Livez/Readyzのパスハンドラーを設定
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L312-L321 - サーバー終了のauditログ設定をしてる? TODO 詳細不明
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L323 - return preparedGenericAPIServer{s}
prepared.Run(stopCh)
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L193
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L362-L364
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L326-L367
中身
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L331-L342 - 終了時のハンドリング処理っぽい
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L344-L348 - メイン実行処理はこれっぽい
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L350 - 終了イベントを待つ ~ つまりアプリケーション停止時に以降の処理を行う
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L352-L356 - PreShutdownHooksの終了処理を行う
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L359 - readyz側の終了処理を待つ
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L361 - メイン処理の終了処理を待つ
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L363-L364 - コメントの通りのすべての終了処理を待つっぽい感じ
NonBlockingRun() - 実質的なメイン処理だと思うやつ
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L377-L383 - auditログを取ったりするバックエンドの起動処理? TODO: auditのbackend周りの仕組みを知らない
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L385-L396 - API Serverの起動処理っぽい感じがする
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L398-L409 - シグナルなど上位からの終了命令を受けてのサーバーの終了処理
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L411 - s.RunPostStartHooks(stopCh)の実行
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L413-L415 - systemdへの起動通知
s.SecureServingInfo.Serve(s.Handler, s.ShutdownTimeout, internalStopCh)
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L152-L155 - TLS設定
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L157-L162 - サーバー生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L164-L173 - バッファサイズの設定
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L175-L180 - http2のストリームの同時接続数?の設定
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L182-L183 - http2のストリームの同時処理容量の設定
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L185-L190 - http2サーバーの設定を行う?
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L192-L195 - tlsエラーログの設定
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L197-L198 - RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)の実行
RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L217-L225 - 上位の終了処理受けた際のサーバーのgracefulな停止処理
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L227-L245 - サーバーの起動
s.RunPostStartHooks(stopCh)
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/hooks.go#L164-L166 - 登録されたs.preShutdownHooksをそれぞれgoroutineで実行
ServerRunOptions
API Serverのデフォルトオプション
で生成される
API Serverのフラグとコマンドラインオプションとの紐付け
ここでAPI Server固有のコマンドラインオプションと紐付けられてる
最終的なオプション値の調整作業
でコマンドラインオプションなどの設定に基づき、API ServerのServerRunOptionsの値の調整を行い、options.ServerRunOptionsにセットされる
CreateServerChain
CreateNodeDialer
-
https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L249
-
nodeTunnelerについてはSSHのチャネルを作るものっぽいが、オプションのSSHUserはデフォルトでは無しで、このオプション自体deprecated予定のようなので、基本は使われない?
-
proxyTransportについてはAPI Server用に設定された*http.Transportを返すだけのよう TODO 現段階だとよくわからない
CreateKubeAPIServerConfig
buildGenericConfig
- ベースとなるapiserver configを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L454
- API Serverで有効となっているGVK一覧(のようなもの?)を生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L661-L717
- 生成したapiserver configをs.GenericServerRunOptionsに流し込む?(多分この時がs.GenericServerRunOptionsに対する初期値の流し込みだと思う): https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L457-L459
- s.SecureServingの設定(が、ここは結構やってること複雑): https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L461-L463
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go#L38-L79
- s.SecureServingOptionsのapply(これも複雑): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go#L44-L46
- net.Listenerをここで設定(これがGeneric API Serverのnet.Listenerになる?): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving.go#L217-L299
- 引数で受け取ったConfigを新たなConfigで上書き: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving.go#L255-L259
- メインとなるx509の鍵と証明書を設定: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving.go#L261-L279
- 設定に応じてSNI certsを設定: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving.go#L287-L296
- SNIについて: https://www.idcf.jp/rentalserver/user-support/knowledge/ssl/sni.html
- TODO: この証明書の設定や読み込みなどを行っているDynamicCertKeyPairContentだが、Run()を実行するとgo routineが実行されて、定期的に証明書などのリフレッシュを行うよう: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_serving_content.go#L125-L169
- ループバッククライアント設定などがなければここで終了?: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go#L48-L50
- ループバッククライアント向けの設定?: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/serving_with_loopback.go#L52-L78
- コマンドラインオプションのfeatureフラグ設定に基づいてgenericConfigをアップデート: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L464-L466
- どのGVKを有効にするのかをアップデート?(TODO: よくわかってない): https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L467-L469
- TODO EgressSelectorなるものの設定を行う(このEgressSelectorというのはKonnectivityというやつのこと? https://kubernetes.io/docs/tasks/extend-kubernetes/setup-konnectivity/): https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L470-L472
- Generic API ServerのOpenAPI周りの設定?: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L474-L479
- StorageFactoryを設定(StorageFactoryはAPI Serverのデータをetcdなどのバックエンドに保存するためのインターフェイス?): https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L484-L494
- sharedInformerを生成(このクライアント生成でLoopbackClientConfigを利用しているので、さっき出てきたloopbackというのは自分のAPI Serverに接続するためのクライアントということだったっぽい): https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L511-L517
- 認証周りの設定っぽい: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L519-L522
- 認証モードや設定に応じたAuthorizerを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L524-L531
- auditログ周りの設定: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L533-L536
- pluginInitializers, admissionPostStartHook周り: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L538-L548
- resolverというserviceのClusterIPなどの情報を元に実際のエンドポイントを生成するのを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L543
- pluginInitializersというInitizlizerという種類のプラグイン?とdiscoveryのキャッシュを30秒ごとに更新するgo routineを内部で実行するadmissionPostStartHookを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L544
- 設定で有効にしたAdmissionPluginを生成?して、AdmissionConfigurationに基づいた設定を渡した状態でAdmissionControlを生成しているっぽい TODO もうちょい調べる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L550-L558
- APIリクエストの優先度コントロール機能: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L560-L562
etcd接続確認周り
CheckEtcdServersがetcdへの接続を確認する関数で、これが成功を返すかタイムアウトになるまでポーリングする
capacilityセットアップ
ただ単にインスタンス初期化して渡してるだけっぽい
TODO このcapability何に使うん..?
TODO capabilityはAPI Serverの接続数とかそういうものを管理してる??
メトリクス設定
オプションからのメトリクスオプションの設定と適用
klogのloggerを設定
serviceIPRange, secondaryServiceIPRange, apiServerServiceIPを取得、もしくはデフォルト値を設定
configを生成
クライアントのCA証明書設定用のプロバイダを設定
こいつがCA証明書のファイルが指定されていた場合、それを定期的に読み込んで更新を行い、そいつをCA証明書として利用する仕組みのよう
また、その下のrequestHeaderConfigはClientCAFileが無いと設定されないようなので、認証関連の処理を追加するということな気がしてる
buildGenericConfigで生成したdiscoveryを定期更新するgo routineを実行する関数をPostStartHookに追加
AddPostStartHookってことだからAPI Server起動後に呼ばれるんだと思う
nodeTunneler, EgressSelectorがあればそれぞれconfigに設定
ServiceAccountに関する設定をconfigに適用
ServiceAccountに関する設定はここらへんで設定されたオプションのよう
最後に生成したconfig, serviceResolver, pluginInitializers を返してCreateKubeAPIServerConfigは終了
CreateKubeAPIServerConfig
一言で言うとAPI Extention Serverを生成してる
API Extention Server用のconfigを生成
ここは中でもシンプルにconfigを設定してるだけであまり特別なことしてないのでシンプルに
createAPIExtensionsServer
中身はこれ
その中身は以下のような感じ
c.GenericConfig.New
handlerChainを生成
BuildHandlerChainFuncというのはこれっぽい
この中で色んなhandlerChainが設定されている
これ中でhandler.ServeHTTPを呼び出しているので、実際のhttpリクエストの際に呼ばれるのはこの関数で最後に定義されたhandlerからOKだったら順番にchainが呼び出されるって流れになる気がしてる
例えば認可の処理だったら
のように認可されたらhandler.ServeHTTPを呼び出すみたいになってるちなみにこのAuthorizerはRBACの場合↓が呼びされるんじゃないかと思ってる
これらのhandlerchainにTrackStartedとTrackCompletedが呼び出されていて、メトリクスの計測が行われているようだった
また認証は認可よりもだいぶ前で
で行われている例えばx509の証明書による認証の場合、以下が呼び出されて認証される?
NewAPIServerHandler
HandlerChainBuilderFnなどを元にAPI Server Handlerを生成してるだけ
なんかRestサーバー構築をしてるっぽい
あまり複雑なことはしてないけど中身はこれ
GenericAPIServer生成
ちょっとよくわからないけど設定値の調整?
postStartHooks, preShutdownHooksを設定
sharedInformerの起動とsharedInformerの状態チェックをreadzチェックに追加
多分go routineでshared informer起動してると思われる
この起動されるshared informerというのは以下のよう
多分このへんpriority-and-fairnessのための値更新のためのgo routineをpostHookに追加してる
渡されたヘルスチェック処理をサーバーに追加
TODO APIのパス一覧を返すプロバイダを設定?
各種API Serverのパスをサーバーに登録
NotFoundHandlerの追加登録処理?
TODO よくわかってない
CreateKubeAPIServer
API Serverの作成
中身はこれ
以下更に詳細
c.GenericConfig.New
これはAPI Extention Serverの方と同じやつだと思うのでそっちを参照
kubectl logs用のエンドポイントを設定
TODO docker logsとかはしてなさそうだからAPI Serverのログ取得?
必要に応じてOpenID用のパスを設定
各種クラウドプロバイダなどとOpenID(Connect?)を利用してアカウント連携を行っているのはこのパスの処理?
AWSとかGCPのアカウントでクラスターにアクセスするのはここらへんの処理を利用している?
レガシーエンドポイントのインストール
TODO v1なので何がレガシーなのか理解してない
ちなみに各種エンドポイントのRestサーバーへのインストールはここらへんで行われているっぽい
登録されるactionsは以下のあたりでマッピングされている
(結構予測だが)ちなみに実際の各種CRUDなどの操作はおそらく以下のあたりが呼び出されてる(DELETEの例)
それで普通にetcdを使ってる場合には最終的に↓のあたりが呼ばれているのだと思われる
通常の?エンドポイントのインストール
tunnelerがあればtunneler go routineの起動とヘルスチェックの追加
ClusterAuthenticationTrustControllerの起動と、あればClientCA更新のコントローラー起動
kube-system configmap/extension-apiserver-authenticationのメンテナンスを行うClusterAuthenticationTrustControllerの起動とあればClientCA更新のコントローラー起動を行う
APIServerIdentity feature gateが有効な場合、lease controllerとleace gcの起動を行う
createAggregatorConfig
AggregatorConfigを生成
StorageVersionAPI, APIServerIdentity feature gatesが有効な場合に対応するBuildHandlerChainFuncを生成して、httpのハンドラーチェインを生成
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L716-L720
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/storageversion.go#L36-L112
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L722-L771
ちなみにこのfeature gateのチェックを通らない場合には普通にDefaultBuildHandlerChainがセットされるっぽい感じ
createAggregatorServer
aggregatorConfig.Complete().NewWithDelegate
c.GenericConfig.New("kube-aggregator", delegationTarget)
これはGenericAPIServerを生成してるだけなので、他のAPI Serverですでにで同じのが出てきてそう
apiregistrationClient, err := clientset.NewForConfig(c.GenericConfig.LoopbackClientConfig)
Apiregistration用のclientsetを生成してる
Apiregistrationというのは多分これのことでAPIServiceリソースがあるGVKのことっぽい
resourceExpirationEvaluator, err := genericapiserver.NewResourceExpirationEvaluator(*c.GenericConfig.Version)
apiGroupInfo
apiGroupInfoはメソッドは持っていないので情報を入れる箱の役割っぽい
enabledVersions
TODO 必要なGVK情報を突っ込んでる??
/apis へのリクエストのハンドラーをここで設定してる
apiserviceRegistrationControllerを生成
軽くみた感じはこいつはReconcilation Loopで
を呼び出して、APIServiceリソースを元にAPIAggregatorに登録を行い、APIAggregatorサーバーのハンドラーを登録してるみたい
逆にAPIServiceリソース削除時は
を呼び出して削除するっぽい
ProxyClientCertFileなどがある場合に読み込みを行って、更新処理を行うコントローラーをAddPostStartHookOrDie経由で動かすようにしてる
ついで?にapiserviceRegistrationControllerにAddListnerを追加して証明書・秘密鍵の更新イベントを伝播させるようにしてる
availableControllerなるものを生成してる
軽く見た感じこいつはAPIServiceに登録されたExtending API Serverのヘルスチェックを行うコントローラーっぽいようにみえる
informer起動
apiserviceRegistrationController起動
availableController起動
StorageVersionManagerのあれこれ
前半部分は待機処理
後半部分は
go routineで10分毎にs.GenericAPIServer.StorageVersionManager.UpdateStorageVersionsが実行されるように設定されていて、このアップデートが1度完了するまでまた待機処理を行ってる感じ
StorageVersionって確か
APIリクエスト -> API Server -> etcdで保存されるときの実際のデータのフォーマット的なやつだった気がする
んでStorageVersionはKubernetesがバージョンアップされたときに新しいバージョンのフォーマットにアプデしなきゃいけないうんぬんの話が前にKubernetes Meetup Tokyoで聴いた気がする
なのでこの処理はその各データのStorageVersionのアップデートを定期的にしてくれてる?
となるとKubernetesバージョンアップ時に手動でStorageVersionのアップデートを行うみたいのは不要ということ?
Kubernetesバージョンアップ時にかならずこの処理を行って完了するまでwaitするなら、API Serverをバージョンアップして、正常に起動できた時点でStorageVersionのバージョンアップも成功してるということ?
一応中身は
のような感じになっていてアップデート処理が行われている
実際にデータ処理を行うクライアントは
にあるclient-goにあるInternalなクライアントのStorageVersionsリソース用のクライアントが利用されている
Internalなリソースあるんだという感じだ
createAPIExtensionsServer について
ここで生成されるわけだけど
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L219
- https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L229
でapiExtensionsServer.GenericAPIServerとapiExtensionsServer.Informersを使うために生成してる感じ?
んでこのGenericAPIServerは
あたりでAddPostStartHookOrDieされていたり
でCRDのハンドラーを入れてたりする
なのでこのAPI Server内部で動くAPIExtensionsServerというのはCRDリソースをハンドリングして処理するための、内部的なExntending API Serverという感じ??
んで対称的に独自のExtending API ServerはAPIServiceリソースによって追加されてハンドリングを動的に追加/削除を行うAPI Serverという感じになる?
前に読んだときの
ではここらへんあまり深堀りしていなかった
CRDリソースのマッピング
ここらへんでマッピングして処理できるようにしてる感じ?
ここらへんで対応するhttp側のハンドラーを設定
またNewCustomResourceDefinitionHandler内でEventHandlerを設定しているので、このハンドラー自体がCustom Controllerのように動作しそう
起動する各種コントローラーを生成
informerを起動
AddPostStartHookOrDieで各種コントローラーが起動するよう設定
CRDリソースがローカルキャッシュに同期されるまで待機
各種コントローラーについて
NamingConditionController
特にここで処理しているCRDのステータス管理を行うことが責務のコントローラーに見える
establishingController
具体的な処理はせずただステータス更新をしてるだけに見える
何らかの必要な初期化処理を行っている??
CRDリソースのイベント以外に影響がなさそうなコントローラーだから、わざわざコントローラーを切り出して処理している意味がわからない
nonStructuralSchemaController
なんとなくだけどCRDスキーマがバリデーションエラーになってないかチェックしてる?
apiApprovalController
CRDの api-approved.kubernetes.io
アノテーションの値を見てCRDの状態チェックを行う?
となるとこのアノテーションが別のところで設定されてくるはず
finalizingController
CRDリソースのfinalizer処理を行っている感じ
discoveryController
API Serverの一番始めのハンドラー
一番始めのAPIExtentionServerが生成するGenericAPIServerのAPIServerHandlerは
で、こいつのServeHTTPが
だから、これが一番始めのhttpハンドラーになる?
でもAPIExtentionServer自体のServeHTTPも
であるから、こっちが先?
これべつのところも読まないとわからなそう
↓
- 最終的にここで起動される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L236
- このサーバーはここで作られる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/secure_serving.go#L157-L162
- secureServerに渡されるハンドラーはpreparedGenericAPIServer.Handlerが利用される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L390
- このpreparedGenericAPIServerはここで生成される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L342
- preparedGenericAPIServer.HandlerはGenericAPIServer.Handlerであることがここからわかる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L289-L292
- このGenericServerはここで生成されている: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L185
- GenericServer.Handlerの実態はapiServerHandlerであることがわかる https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L572
そのためこのapiServerHandler.ServeHTTPが一番始めのハンドラーであることがわかる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L187-L190
- apiServerHandler.ServeHTTPの実態はa.FullHandlerChain.ServeHTTPであり: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L96
- a.FullHandlerChainはhandlerChainBuilder(director)によって生成される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L96
- handlerChainBuilderの中身はBuildHandlerChainFunc: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L549-L551
- BuildHandlerChainFuncはデフォルトでDefaultBuildHandlerChainが利用される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L305
- DefaultBuildHandlerChainの中身はこれ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L722-L771
- DefaultBuildHandlerChainに渡されるapiHandlerは認可処理の後に実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L723
- このapiHandlerに相当するのは先程handlerChainBuilder(director)として渡されてたdirectorというやつ
- このdirectorのServeHTTPはこれ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L122-L155
- この /apis については個別処理が必要らしいので独自に行なってるとのこと(/apisのサンプルはこの下に試した例を貼ってる): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L128-L138
- その他は全てこちらで処理される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L140-L149
- それでこのGenericAPIServer.HandlerのNonGoRestfulMuxやs.Handler.GoRestfulContainer.Addを通して後からパスを追加している: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L773-L801
- (下に貼ったけど)APIServiceリソースにv1などのcore APIなどの登録されていたので、APIServiceリソースに登録するAddAPIServiceを通してデータ登録を行なっている?: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L366-L424
- v1などの組み込みリソースはここでキューされる -> syncで登録される?: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L70-L73
- openAPIAggregationControllerの生成はこちら: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L356
- OpenAPIConfigはデフォルト値がここでちゃんと入るので、普通は上のopenAPIAggregationControllerの生成は行われるはず: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L474-L475
/apis のサンプル
kubectl proxy --port=8080
でproxyを起動後以下のように /apis を呼び出すと APIGroupList として APIGroup の一覧が取得できた
curl http://localhost:8080/apis
{
"kind": "APIGroupList",
"apiVersion": "v1",
"groups": [
{
"name": "apiregistration.k8s.io",
"versions": [
{
"groupVersion": "apiregistration.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "apiregistration.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "apiregistration.k8s.io/v1",
"version": "v1"
}
},
{
"name": "apps",
"versions": [
{
"groupVersion": "apps/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "apps/v1",
"version": "v1"
}
},
{
"name": "events.k8s.io",
"versions": [
{
"groupVersion": "events.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "events.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "events.k8s.io/v1",
"version": "v1"
}
},
{
"name": "authentication.k8s.io",
"versions": [
{
"groupVersion": "authentication.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "authentication.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "authentication.k8s.io/v1",
"version": "v1"
}
},
{
"name": "authorization.k8s.io",
"versions": [
{
"groupVersion": "authorization.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "authorization.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "authorization.k8s.io/v1",
"version": "v1"
}
},
{
"name": "autoscaling",
"versions": [
{
"groupVersion": "autoscaling/v1",
"version": "v1"
},
{
"groupVersion": "autoscaling/v2beta1",
"version": "v2beta1"
},
{
"groupVersion": "autoscaling/v2beta2",
"version": "v2beta2"
}
],
"preferredVersion": {
"groupVersion": "autoscaling/v1",
"version": "v1"
}
},
{
"name": "batch",
"versions": [
{
"groupVersion": "batch/v1",
"version": "v1"
},
{
"groupVersion": "batch/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "batch/v1",
"version": "v1"
}
},
{
"name": "certificates.k8s.io",
"versions": [
{
"groupVersion": "certificates.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "certificates.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "certificates.k8s.io/v1",
"version": "v1"
}
},
{
"name": "networking.k8s.io",
"versions": [
{
"groupVersion": "networking.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "networking.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "networking.k8s.io/v1",
"version": "v1"
}
},
{
"name": "extensions",
"versions": [
{
"groupVersion": "extensions/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "extensions/v1beta1",
"version": "v1beta1"
}
},
{
"name": "policy",
"versions": [
{
"groupVersion": "policy/v1",
"version": "v1"
},
{
"groupVersion": "policy/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "policy/v1",
"version": "v1"
}
},
{
"name": "rbac.authorization.k8s.io",
"versions": [
{
"groupVersion": "rbac.authorization.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "rbac.authorization.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "rbac.authorization.k8s.io/v1",
"version": "v1"
}
},
{
"name": "storage.k8s.io",
"versions": [
{
"groupVersion": "storage.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "storage.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "storage.k8s.io/v1",
"version": "v1"
}
},
{
"name": "admissionregistration.k8s.io",
"versions": [
{
"groupVersion": "admissionregistration.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "admissionregistration.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "admissionregistration.k8s.io/v1",
"version": "v1"
}
},
{
"name": "apiextensions.k8s.io",
"versions": [
{
"groupVersion": "apiextensions.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "apiextensions.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "apiextensions.k8s.io/v1",
"version": "v1"
}
},
{
"name": "scheduling.k8s.io",
"versions": [
{
"groupVersion": "scheduling.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "scheduling.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "scheduling.k8s.io/v1",
"version": "v1"
}
},
{
"name": "coordination.k8s.io",
"versions": [
{
"groupVersion": "coordination.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "coordination.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "coordination.k8s.io/v1",
"version": "v1"
}
},
{
"name": "node.k8s.io",
"versions": [
{
"groupVersion": "node.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "node.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "node.k8s.io/v1",
"version": "v1"
}
},
{
"name": "discovery.k8s.io",
"versions": [
{
"groupVersion": "discovery.k8s.io/v1",
"version": "v1"
},
{
"groupVersion": "discovery.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "discovery.k8s.io/v1",
"version": "v1"
}
},
{
"name": "flowcontrol.apiserver.k8s.io",
"versions": [
{
"groupVersion": "flowcontrol.apiserver.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "flowcontrol.apiserver.k8s.io/v1beta1",
"version": "v1beta1"
}
},
{
"name": "monitoring.coreos.com",
"versions": [
{
"groupVersion": "monitoring.coreos.com/v1",
"version": "v1"
},
{
"groupVersion": "monitoring.coreos.com/v1alpha1",
"version": "v1alpha1"
}
],
"preferredVersion": {
"groupVersion": "monitoring.coreos.com/v1",
"version": "v1"
}
},
{
"name": "topolvm.cybozu.com",
"versions": [
{
"groupVersion": "topolvm.cybozu.com/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "topolvm.cybozu.com/v1",
"version": "v1"
}
},
{
"name": "metrics.k8s.io",
"versions": [
{
"groupVersion": "metrics.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "metrics.k8s.io/v1beta1",
"version": "v1beta1"
}
}
]
}
参考
apiserviceの情報を確認した
v1などのリソースも全てapiserviceリソースに登録されていることがわかった
kubectl get apiservice
NAME SERVICE AVAILABLE AGE
v1. Local True 4h32m
v1.admissionregistration.k8s.io Local True 4h32m
v1.apiextensions.k8s.io Local True 4h32m
v1.apps Local True 4h32m
v1.authentication.k8s.io Local True 4h32m
v1.authorization.k8s.io Local True 4h32m
v1.autoscaling Local True 4h32m
v1.batch Local True 4h32m
v1.certificates.k8s.io Local True 4h32m
v1.coordination.k8s.io Local True 4h32m
v1.discovery.k8s.io Local True 4h32m
v1.events.k8s.io Local True 4h32m
v1.monitoring.coreos.com Local True 4h31m
v1.networking.k8s.io Local True 4h32m
v1.node.k8s.io Local True 4h32m
v1.policy Local True 4h32m
v1.rbac.authorization.k8s.io Local True 4h32m
v1.scheduling.k8s.io Local True 4h32m
v1.storage.k8s.io Local True 4h32m
v1.topolvm.cybozu.com Local True 4h32m
v1alpha1.monitoring.coreos.com Local True 4h31m
v1beta1.admissionregistration.k8s.io Local True 4h32m
v1beta1.apiextensions.k8s.io Local True 4h32m
v1beta1.authentication.k8s.io Local True 4h32m
v1beta1.authorization.k8s.io Local True 4h32m
v1beta1.batch Local True 4h32m
v1beta1.certificates.k8s.io Local True 4h32m
v1beta1.coordination.k8s.io Local True 4h32m
v1beta1.discovery.k8s.io Local True 4h32m
v1beta1.events.k8s.io Local True 4h32m
v1beta1.extensions Local True 4h32m
v1beta1.flowcontrol.apiserver.k8s.io Local True 4h32m
v1beta1.metrics.k8s.io monitoring/prometheus-adapter True 4h31m
v1beta1.networking.k8s.io Local True 4h32m
v1beta1.node.k8s.io Local True 4h32m
v1beta1.policy Local True 4h32m
v1beta1.rbac.authorization.k8s.io Local True 4h32m
v1beta1.scheduling.k8s.io Local True 4h32m
v1beta1.storage.k8s.io Local True 4h32m
v2beta1.autoscaling Local True 4h32m
v2beta2.autoscaling Local True 4h32m
root@development-awesome:~# kubectl get apiservice v1. -o yaml
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
creationTimestamp: "2021-07-07T10:24:18Z"
labels:
kube-aggregator.kubernetes.io/automanaged: onstart
name: v1.
resourceVersion: "25"
uid: 3b48b6c6-186c-4f15-a354-2172d1c9563d
spec:
groupPriorityMinimum: 18000
version: v1
versionPriority: 1
status:
conditions:
- lastTransitionTime: "2021-07-07T10:24:18Z"
message: Local APIServices are always available
reason: Local
status: "True"
type: Available
root@development-awesome:~# kubectl get apiservice v1beta1.metrics.k8s.io -o yaml
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apiregistration.k8s.io/v1","kind":"APIService","metadata":{"annotations":{},"labels":{"app.kubernetes.io/component":"metrics-adapter","app.kubernetes.io/name":"prometheus-adapter","app.kubernetes.io/part-of":"kube-prometheus","app.kubernetes.io/version":"0.8.4"},"name":"v1beta1.metrics.k8s.io"},"spec":{"group":"metrics.k8s.io","groupPriorityMinimum":100,"insecureSkipTLSVerify":true,"service":{"name":"prometheus-adapter","namespace":"monitoring"},"version":"v1beta1","versionPriority":100}}
creationTimestamp: "2021-07-07T10:25:27Z"
labels:
app.kubernetes.io/component: metrics-adapter
app.kubernetes.io/name: prometheus-adapter
app.kubernetes.io/part-of: kube-prometheus
app.kubernetes.io/version: 0.8.4
name: v1beta1.metrics.k8s.io
resourceVersion: "1440"
uid: 5f400140-77a7-4433-a0eb-d83d118d24b3
spec:
group: metrics.k8s.io
groupPriorityMinimum: 100
insecureSkipTLSVerify: true
service:
name: prometheus-adapter
namespace: monitoring
port: 443
version: v1beta1
versionPriority: 100
status:
conditions:
- lastTransitionTime: "2021-07-07T10:26:02Z"
message: all checks passed
reason: Passed
status: "True"
type: Available
openapicontroller.NewAggregationController について
一つ上のスクラップで貼ったけどこれ
んでそれが
でNewされるタイミングでキューされたりするときに利用されるspecDownloaderとopenAPIAggregatorについても調べるspecDownloader
このdownloaderというのは実質
のDownloadメソッドだけだった
このメソッド何がすごいって(はじめの方とかは多分)http serverまだ起動してないのに
で無理くりhandlerのServeHTTPにリクエストしてopenAPISpecを取得してくるところ
一応どんな感じで取れるのかの確認をしてみたけど curl http://localhost:8080/openapi/v2
の結果は長すぎたのでgistに貼った
くっそ長いけどこんな感じでopenAPISpecが取れるんだなぁ
実際にこのレスポンスにあるURLをcurlで叩くとレスポンスがちゃんと返ってくる
curl http://localhost:8080/api/v1/componentstatuses
{
"kind": "ComponentStatusList",
"apiVersion": "v1",
"metadata": {
},
"items": [
{
"metadata": {
"name": "controller-manager",
"creationTimestamp": null
},
"conditions": [
{
"type": "Healthy",
"status": "False",
"message": "Get \"http://127.0.0.1:10252/healthz\": dial tcp 127.0.0.1:10252: connect: connection refused"
}
]
},
{
"metadata": {
"name": "scheduler",
"creationTimestamp": null
},
"conditions": [
{
"type": "Healthy",
"status": "True",
"message": "ok"
}
]
},
{
"metadata": {
"name": "etcd-0",
"creationTimestamp": null
},
"conditions": [
{
"type": "Healthy",
"status": "True",
"message": "{\"health\":\"true\"}"
}
]
}
]
}
curl http://localhost:8080/api/v1/namespaces/default/configmaps
{
"kind": "ConfigMapList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "91289"
},
"items": [
{
"metadata": {
"name": "kube-root-ca.crt",
"namespace": "default",
"uid": "5c39fae6-7131-4a4b-9bed-f75c0b6f3603",
"resourceVersion": "433",
"creationTimestamp": "2021-07-07T10:24:34Z",
"managedFields": [
{
"manager": "kube-controller-manager",
"operation": "Update",
"apiVersion": "v1",
"time": "2021-07-07T10:24:34Z",
"fieldsType": "FieldsV1",
"fieldsV1": {"f:data":{".":{},"f:ca.crt":{}}}
}
]
},
"data": {
"ca.crt": "-----BEGIN CERTIFICATE-----\nMIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl\ncm5ldGVzMB4XDTIxMDcwNzEwMjQwOFoXDTMxMDcwNTEwMjQwOFowFTETMBEGA1UE\nAxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALMF\n5d5B3A+AXCvE4lQDZ5J9bHWMtZh2OyCrdrfn/v0Ek6KVjCLsz0x4e/LBcS6ZuIAF\nIXVjx8+pXiuI/A71wl43haEMoZ57s34yKEfXm5jQX/PYJ4FzUnbsqDfyxn1sbvem\n2iJGDSsDHhq/RXgCKUj9v5XcakUJxLM0op6pIKAO6+c2IjpH53W/hziS8tf854aw\nPNC5lAyB2w6ZbDCsJqPHcYsHXYemngVpGNTAbLRUS6gPjUOhP7aokwMHMk0I2Nix\n3RZnBRfU0FQL2eGIl82z1GxW8KCwJSwmshQPAlITjiz9idOfjDa90TUnAvwp9Uti\n341s3ADwswzjcwkWqHsCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB\n/wQFMAMBAf8wHQYDVR0OBBYEFPZVPJhJH4+MV9ecJe9/g2E+ALxkMA0GCSqGSIb3\nDQEBCwUAA4IBAQCKF1pp5x+jjOtMyv2JkNKoIrGkQHOaIP5Uc+ao3+IvZC/32HAZ\n2RIb4iSP6/fj3iOvQnLM585ZtGTHcxAfgmBnc8uX5roSL1I3L1t7NZ9teGr9dPwW\nqZ5ifH/TIHTalAcu2IbPRgf3zd53r95/9PhEO0B54qvDlb8+IFe8aWR/UsAOHS+U\nwgEcM0zmafOfJNpX3xxtcpph81mJHrShmlV1s1JaEBS5eDevDZ/eCeMSc9djxu03\nzfCevPJVINSRa4kR//R57yXVlO8byvk3kWWoN11IC04Mc/CRCRmWuAyghqocPqrn\nJizzJ9nAF0/Jxpdz3B9vXlZECo1BpF2GvxIh\n-----END CERTIFICATE-----\n"
}
}
]
}
TODO このDownloadメソッドはReconcilation Loopの途中で実行されるけど、どういう用途で利用されるか把握していない
↓
openAPIAggregatorを生成するBuildAndRegisterAggregatorの内部でも使用されてた
openAPIAggregator
openAPIAggregatorはBuildAndRegisterAggregator
で生成されてる
- ここでOpenAPIConfigとrestful.WebServiceを元にOpenAPISpecが生成される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/aggregator.go#L83-L87
- 生成したOpenAPISpecや各delegate(apiExtensionsServer/kubeAPIServer/aggregatorServerのこと)を元にspecを生成(生成したOpenAPISpecだけでCRD以外全部入ってる気がするけど何故か生成してる): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/aggregator.go#L89-L106
- ここで生成したspecを静的コンテンツとして配信できる状態にして、配信パスをpathHandlerに設定(このpathHandlerがaggregatorのHandler): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/aggregator.go#L117-L130
ちなみにBuildOpenAPISpecの中で呼び出されるOpenAPIConfigのGetDefinitionsの中身は多分
を元に自動生成される pkg/generated/openapi/zz_generated.openapi.go
ファイル
(手元にはあるけど、githubには無いから多分ビルド時とかに自動生成される系だと思う)
openAPIAggregationController
話しは戻ってきてopenAPIAggregationControllerを生成するNewAggregationControllerのところから
ここではopenAPIAggregator.GetAPIServiceNamesが呼び出されていて、中身はこれ
これで生成されたspecAggregator.openAPISpecsの名前が全部取得されopenAPIAggregationControllerのキューに入る
openAPIAggregationController.Run
そして調整ループを動かすopenAPIAggregationController.RunはPrepareRunのタイミングでAddPostStartHookOrDieでサーバー起動後に実行される
で
- syncHandlerで必要に応じてspecの更新が行われる https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L112
- syncHandlerの中身はsync: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L138-L156
- 更新結果に応じてrequeueが調整される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L119-L133
ちなみに
を見て気づいたんだけど中身が
となっていて生成されたOpenAPISpecと同じapiServiceNameかどうかを持ってローカルから取得できるかどうかを判断してるっぽかった
のでこの判定のためにOpenAPISpecは生成してるという感じなのかな?という
また
で対象のapiServiceNameが削除されているのは恐らくだけど独自のextended api serverが無くなったときのケース向けの対応なのかな?
AddAPIService/UpdateAPIService/RemoveAPIServiceについて
APIServiceRegistrationController.sync
結論からだけどAddAPIService/UpdateAPIService/RemoveAPIServiceはAPIServiceRegistrationController.syncを通して呼ばれるよう
このコントローラーのイベントハンドリング元はAPIServiceリソースのよう
(他にあるのかは不明)
このapiserviceRegistrationControllerは生成時にAPIAggregatorをAPIHandlerManagerとして受け取る
APIAggregator.AddAPIService/APIAggregator.RemoveAPIService
APIAggregator.AddAPIService/APIAggregator.RemoveAPIServiceは
にある
APIAggregator.AddAPIService
- s.proxyHandlers[apiService.Name]があればproxyHandler.updateAPIServiceを実行し、s.openAPIAggregationController.UpdateAPIServiceを実行する(このproxyHandlerについてはこの下で解説): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L369-L377
- proxyHandlerが無い場合には生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L379-L393
- proxyHandlerを利用してs.openAPIAggregationController.AddAPIServiceを実行: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L394-L396
- s.proxyHandlersにproxyHandlerを追加して、API Serverのハンドラーにパスを追加: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L397-L399
- 追加でgroupdiscoveryのAPIへの登録が不要な場合はここで終了: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L401-L409
- groupdiscoveryのAPIへの登録が必要な場合はここで登録: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L411-L423
といったフローでs.openAPIAggregationController.AddAPIServiceやs.openAPIAggregationController.UpdateAPIServiceが呼び出されてた
APIAggregator.RemoveAPIService
削除時はこんな感じでハンドラーへの登録解除したり、s.openAPIAggregationController.RemoveAPIService呼び出したりしてる
proxyHandler.HTTPServer
proxyHandlerもHTTPServerがあり、これが呼び出される
- そもそもローカルで呼び出されるものについてはそれを呼び出して終わりになる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go#L110-L123
- ここでバリデーション: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go#L125-L139
- ここで登録された情報を元にプロキシするextended api serverにリクエスト(実際単にreverse proxyしてるだけな気がする): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go#L141-L179
内部で最終的に
にあるようなhttputil.NewSingleHostReverseProxyを利用しているのでreverse proxyしてること自体はあってると思う
TODO 詳細調べる
http2を利用してコネクションを貼ってるところが普通にNewSingleHostReverseProxy使うだけとの違い??
APIAggregator.AddAPIServiceで登録されたハンドラーの確認
試しに手元のクラスターに入ってる独自のAPI Serviceっぽいものがあったので確認してみた
kubectl get apiservice v1beta1.metrics.k8s.io
NAME SERVICE AVAILABLE AGE
v1beta1.metrics.k8s.io monitoring/prometheus-adapter True 7h
curl http://localhost:8080/apis/metrics.k8s.io/v1beta1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "metrics.k8s.io/v1beta1",
"resources": [
{
"name": "nodes",
"singularName": "",
"namespaced": false,
"kind": "NodeMetrics",
"verbs": [
"get",
"list"
]
},
{
"name": "pods",
"singularName": "",
"namespaced": true,
"kind": "PodMetrics",
"verbs": [
"get",
"list"
]
}
]
}
curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/nodes/autoresizer-e2e-worker
{
"kind": "NodeMetrics",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"name": "autoresizer-e2e-worker",
"creationTimestamp": "2021-07-07T17:22:02Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/arch": "amd64",
"kubernetes.io/hostname": "autoresizer-e2e-worker",
"kubernetes.io/os": "linux",
"topology.topolvm.cybozu.com/node": "autoresizer-e2e-worker"
}
},
"timestamp": "2021-07-07T17:22:02Z",
"window": "1m0s",
"usage": {
"cpu": "464m",
"memory": "9863324Ki"
}
}
CRDとして登録してあるtopolvmの方はこんな感じ(CRDはまたCRDにのハンドラー周りの把握が必要そう)
kubectl get apiservice v1.topolvm.cybozu.com
NAME SERVICE AVAILABLE AGE
v1.topolvm.cybozu.com Local True 7h2m
curl http://localhost:8080/apis/topolvm.cybozu.com/v1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "topolvm.cybozu.com/v1",
"resources": [
{
"name": "logicalvolumes",
"singularName": "logicalvolume",
"namespaced": false,
"kind": "LogicalVolume",
"verbs": [
"delete",
"deletecollection",
"get",
"list",
"patch",
"create",
"update",
"watch"
],
"storageVersionHash": "FRzzIk+aR6Y="
},
{
"name": "logicalvolumes/status",
"singularName": "",
"namespaced": false,
"kind": "LogicalVolume",
"verbs": [
"get",
"patch",
"update"
]
}
]
}
CRDのハンドラーやコントローラーについて
crdHandler
CRDリソースのハンドリングは
のような感じでcrdHandlerがやってるっぽい
ServeHTTP
とても長い
- リクエスト先がGroupやVersionなどのDiscovery APIだった場合はそれぞれのハンドラーを呼び出す https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L244-L268
- リクエスト先が正しくないようであれば404: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L272-L315
- getOrCreateServingInfoForで対象CRDのuidや名前を元にハンドラーで利用するCRD情報であるcrdInfoを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L319-L331
- リクエストバージョンがCRDのバージョンで含まれてるかチェックを行い、なければ404: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L332-L335
- deprecated警告があれば追加: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L337-L340
- リクエストに応じたhandlerFuncを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L342-L376
- ハンドラーを実行: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L378-L383
ちなみにmetrics.InstrumentHandlerFuncというのは処理時間の計測処理を追加してるっぽい
WithWaitGroupの方はwatchや/debug/pprof/のような処理時間が長いリクエストを処理する際にはCRD単位で1リクエストずつ処理するように制御を行うフィルタのよう
getOrCreateServingInfoFor
対象CRDのuidや名前を元にハンドラーで利用するCRD情報であるcrdInfoを生成する
- uidで調べてすでに生成済みのローカルキャッシュがあればそれを返す: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L615-L618
- 見つからない場合、Listerから最新のCRDのキャッシュを取得して、そちらのuidで調べて生成済のローカルキャッシュがあれば返す: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L623-L634
- 見つからない場合、crdInfo情報の生成を行う: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L636-L949
- 生成したcrdInfoをローカルキャッシュであるr.customStorageに保存して、返す: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L951-L958
EventHandler
crdHandlerは少し特殊で、コントローラーを使わずにInformerのイベントハンドラーで直接処理を行なっている
内容としてはいずれも、必要があればローカルキャッシュであるr.customStorageの更新処理を行うというものになっている
discoveryController
ここらへん
CRDリソースがイベント元になっている
調整ループはこれ
最終的に
でやっているように、CRD定義に基づいたGroupレベルのDiscovery APIと、その下位のVersionレベルのDiscovery APIのハンドラーを設定するのがこのdiscoveryControllerの役割のよう
もし削除されたりしてGroupそのものやバージョンが無くなった場合には
あたりでハンドラーをリセットしている
また面白そうだったのは
でCRDがTerminatingの場合にはcreate/update/patchのAPIを受け付けなくしているところ
また、調べていく中でVersionレベルのDiscovery APIのハンドラーというのが初めて出てきた気がするので一応curlで確認してみた
curl http://localhost:8080/apis/topolvm.cybozu.com
{
"kind": "APIGroup",
"apiVersion": "v1",
"name": "topolvm.cybozu.com",
"versions": [
{
"groupVersion": "topolvm.cybozu.com/v1",
"version": "v1"
}
],
"preferredVersion": {
"groupVersion": "topolvm.cybozu.com/v1",
"version": "v1"
}
}
curl http://localhost:8080/apis/topolvm.cybozu.com/v1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "topolvm.cybozu.com/v1",
"resources": [
{
"name": "logicalvolumes",
"singularName": "logicalvolume",
"namespaced": false,
"kind": "LogicalVolume",
"verbs": [
"delete",
"deletecollection",
"get",
"list",
"patch",
"create",
"update",
"watch"
],
"storageVersionHash": "FRzzIk+aR6Y="
},
{
"name": "logicalvolumes/status",
"singularName": "",
"namespaced": false,
"kind": "LogicalVolume",
"verbs": [
"get",
"patch",
"update"
]
}
]
}
namingController
ここらへん
syncは以下のあたり
役割としてはCRDとして登録する
- Plural Name
- Singular Name
- ShortNames
- Kind
- ListKind
といった名前が同じGroup内の他のCRDですでに使われていて競合しないかを確認することになる
確認が問題なく行われれば
- namesAcceptedConditionがConditionTrue
- establishedConditionがReason=Installing
となり対象のCRDインストールプロセスが先に進む
各種名前の競合チェック処理は全てcalculateNamesAndConditionsで行われている
ちなみに例えばCRDはこんな感じにStatusなどが設定されている
kubectl get crd logicalvolumes.topolvm.cybozu.com -o yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.6.0
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apiextensions.k8s.io/v1","kind":"CustomResourceDefinition","metadata":{"annotations":{"controller-gen.kubebuilder.io/version":"v0.6.0"},"creationTimestamp":null,"name":"logicalvolumes.topolvm.cybozu.com"},"spec":{"group":"topolvm.cybozu.com","names":{"kind":"LogicalVolume","listKind":"LogicalVolumeList","plural":"logicalvolumes","singular":"logicalvolume"},"scope":"Cluster","versions":[{"name":"v1","schema":{"openAPIV3Schema":{"description":"LogicalVolume is the Schema for the logicalvolumes API","properties":{"apiVersion":{"description":"APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources","type":"string"},"kind":{"description":"Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds","type":"string"},"metadata":{"type":"object"},"spec":{"description":"LogicalVolumeSpec defines the desired state of LogicalVolume","properties":{"deviceClass":{"type":"string"},"name":{"type":"string"},"nodeName":{"type":"string"},"size":{"anyOf":[{"type":"integer"},{"type":"string"}],"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","x-kubernetes-int-or-string":true}},"required":["name","nodeName","size"],"type":"object"},"status":{"description":"LogicalVolumeStatus defines the observed state of LogicalVolume","properties":{"code":{"description":"A Code is an unsigned 32-bit error code as defined in the gRPC spec.","format":"int32","type":"integer"},"currentSize":{"anyOf":[{"type":"integer"},{"type":"string"}],"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","x-kubernetes-int-or-string":true},"message":{"type":"string"},"volumeID":{"description":"INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run \"make\" to regenerate code after modifying this file","type":"string"}},"type":"object"}},"type":"object"}},"served":true,"storage":true,"subresources":{"status":{}}}]},"status":{"acceptedNames":{"kind":"","plural":""},"conditions":[],"storedVersions":[]}}
creationTimestamp: "2021-07-08T10:18:07Z"
generation: 1
name: logicalvolumes.topolvm.cybozu.com
resourceVersion: "565"
uid: 5362e7de-3412-42f7-9a5a-3496e46916ea
spec:
conversion:
strategy: None
group: topolvm.cybozu.com
names:
kind: LogicalVolume
listKind: LogicalVolumeList
plural: logicalvolumes
singular: logicalvolume
scope: Cluster
versions:
- name: v1
schema:
openAPIV3Schema:
description: LogicalVolume is the Schema for the logicalvolumes API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: LogicalVolumeSpec defines the desired state of LogicalVolume
properties:
deviceClass:
type: string
name:
type: string
nodeName:
type: string
size:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
required:
- name
- nodeName
- size
type: object
status:
description: LogicalVolumeStatus defines the observed state of LogicalVolume
properties:
code:
description: A Code is an unsigned 32-bit error code as defined in
the gRPC spec.
format: int32
type: integer
currentSize:
anyOf:
- type: integer
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
message:
type: string
volumeID:
description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
of cluster Important: Run "make" to regenerate code after modifying
this file'
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: LogicalVolume
listKind: LogicalVolumeList
plural: logicalvolumes
singular: logicalvolume
conditions:
- lastTransitionTime: "2021-07-08T10:18:07Z"
message: no conflicts found
reason: NoConflicts
status: "True"
type: NamesAccepted
- lastTransitionTime: "2021-07-08T10:18:07Z"
message: the initial names have been accepted
reason: InitialNamesAccepted
status: "True"
type: Established
storedVersions:
- v1
nonStructuralSchemaController
ここらへん
このコントローラーは他のCRDコントローラーとは違っていて複数同時に動くものらしい
このコントローラーはnamingControllerのCR定義の構造バリデーション版で、spec.XXXなどのCR定義構造にエラーがある場合にはNonStructuralSchemaというステータスのコンディションエラーが付与されるよう
このコントローラーでもnamingControllerと同様にバリデーションエラーが無いかの計算処理を行う関数を利用して状態チェックを行うよう
apiApprovalController
次はこれ
これも同様に複数同時に動くよう
このコントローラーはどうやらGroupが
k8s.io
kubernetes.io
のCRDについて"api-approved.kubernetes.io"
というアノテーションが正しいフォーマットで設定されているかをチェックするコントローラーらしい
これがKEPのようで、CRDでk8s.ioなどのグループを使われないように保護するのが目的らしい
なので普通にCRDを作っている場合には気にする必要のないコントローラーとなりそう
finalizingController
ようやくfinalizingController
これも同様に複数同時に動くよう
調整ループはこれ
先にまとめておくとこのコントローラーはCRD削除時のfinalizerの処理を行うもので、CRD削除前に対象のCRのデータを全て削除するまでCRDの削除を待機させるためのコントローラーのよう
処理のフローはこんな感じ
- 削除状態の
"customresourcecleanup.apiextensions.k8s.io"
というfinalizerがあるものをターゲットのCRDとして処理する: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L118-L121 - まずはじめに処理中ステータスにするhttps://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L125-L139
- 対象のCRDのGroupとリソースが
apiservices.apiregistration.k8s.io
かcustomresourcedefinitions.apiextensions.k8s.io
だったら特に処理は行わずにfinalizerしてCRDを削除する: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L141-L150 - CRDのステータスがEstablishedであれば、CRDの登録されたCRデータを削除する: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L151-L159
- Establishedでなければメッセージだけコンディションとして記録してfinalizerを削除してCRDを削除する: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L160-L167
- 削除処理はこんな感じで、namespaceごとにDELETECOLLECTIONでまとめて削除を行い、全データ削除まで最長1分間待機を行う: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L178-L260
- 削除処理に失敗した場合はコンディションだけ更新して処理しなおし: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L153-L159
- 削除に成功したり、スキップ対象であればfinalizerを削除してからコンディション更新して終了: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L169-L175
ちなみにこのfinalizerは
で付与されているようで、処理を見てる感じetcdからCRDの実データを削除する中の処理の1つとして付与されているように見える
なので、多分CRD削除処理行なった際に自動で付与される?
TIPS
handlerのGoRestfulContainerとNonGoRestfulMuxについて
こことか見ると最終的にマージされてる?
でも実際にリクエストハンドリングされるときは?
DefaultBuildHandlerChain の中身
genericfilters.WithPanicRecovery(handler, c.RequestInfoResolver)
エラーハンドラーを設定
genericapifilters.WithRequestReceivedTimestamp(handler)
リクエスト時刻を記録
genericfilters.WithHSTS(handler, c.HSTSDirectives)
HTTP の Strict-Transport-Security レスポンスヘッダー (HSTS)を設定
HSTSについては
デフォルトのHSTSDirectivesは何もないっぽい
genericapifilters.WithCacheControl(handler)
Cache-Control: no-cache, private headerを追加してる
genericapifilters.WithWarningRecorder(handler)
warningを記録するレコードをcontextに設定?
そもそもここでいうwarningとは?
genericapifilters.WithWarningRecorder(handler)
audit用のアノテーションをrequestのcontextに設定
何に使うアノテーション?
genericfilters.WithProbabilisticGoaway(handler, c.GoawayChance)
httpsの場合に利用されるみたい
https2のGOAWAYというストリームのコネクションを終了することをクライアントに通知するフレームを送るための設定を行うらしい
GOAWAYについてはここらへん
デフォルトは0で、GOAWAYを送信しない設定らしい
genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
request情報からrequestInfoを生成してrequestのcontextに設定
requestInfoは以下のような構造体で
requestInfoの生成処理は
となり、API Serverがリクエストに応じた処理を行う際に利用されるっぽい
genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
WithWaitGroupの方はwatchや/debug/pprof/のような処理時間が長いリクエストを処理する際にはCRD単位で1リクエストずつ処理するように制御を行うフィルタのよう
genericapifilters.WithRequestDeadline(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc, c.Serializer, c.RequestTimeout)
呼び出し元はこちら
コメント曰くこういう感じらしい
WithRequestDeadlineは、指定されたリクエストに適用可能なタイムアウト期間を決定し、適切な期限で新しいコンテキストを設定します。
auditWrapperは、失敗したリクエストを監査するhttp.Handlerを提供します。
longRunningは、指定された要求が長時間実行されている要求である場合にtrueを返します。
requestTimeoutMaximumは、デフォルトの要求タイムアウト値を指定します。
genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc)
これがlongRunningなAPIじゃない場合にラップを行い、リクエストが長時間に渡る場合にタイムアウト処理を行うフィルタのよう
渡されるc.LongRunningFuncはこれ
BasicLongRunningRequestCheckの中身はこちら
genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
CORS設定をヘッダーに行う
関数の中身はこれ
許可されるOriginはデフォルトでは無し
CORSについてはこれを参照
ここまでがHeaderやタイムアウト処理などの前処理系のフィルタ: これ以降は実際の認証処理などが行われていく(なのでこれ以降の個々の処理時間が計測されているのでは?)
genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences)
認証処理
c.Authentication.Authenticator はここで生成されてるっぽい
c.Authentication.APIAudiencesはこちら
認証失敗時のhaldlerはこちら
c.Authentication.Authenticatorの生成処理の中身はこれ
authenticator
NewDynamicVerifyOptionsSecure
509.NewDynamic(config.ClientCAContentProvider.VerifyOptions, x509.CommonNameUserConversion)
例えばこれの中身はこんな感じ
tokenAuthenticators
NewAuthenticatedGroupAdder
unionAuthRequestHandler
これが登録されたauthenticatorを順々に実行して1つでも通ったらそれを返すようになっている
返されるレスポンスのインターフェイスはこれ
authenticatorを使った認証処理の中身
- authenticatorを使った認証処理を行うhttps://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L56
- メトリクス記録処理をdeferで実行: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L57-L60
- 認証失敗時はfail handlerを呼び出し: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L61-L67
- audienceチェック(失敗時はfail handlerを呼び出し): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L69-L74
- Authorizationヘッダーを削除: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L76-L77
- 認証されたユーザー情報をrequestのcontextに追加: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L79
genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
処理としては簡単に確認した感じ、リクエスト内容やユーザーをポリシーと照らし合わせて、audit eventの出力を行い、response writerをaudit eventを出力できるようにラップすることで以降の処理でもaudit eventを出力できるように設定をしてるっぽい
また、panic発生時?にそのイベントを拾ってpanicのエラーをaudit eventとして出力できるようになっている
中身はこれ
- audit eventを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go#L47-L52
- 生成したイベントがなかったりしたら次のハンドラーを呼び出し(event leve=Noneとかだとイベントが無い): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go#L53-L57
- audio eventを出力: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go#L59-L64
- response writerをラップ(リクエスト処理終了時にもaudit eventを出力するためにやってる?): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go#L66-L74
- deferで関数を実行し、以降のハンドラーの処理で処理でpanicが起きた際にイベントを取得してaudit eventを出力: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go#L76-L111
genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)
中身はこれ
ここらへんの機能っぽい
kubectl --as=<user> --as-group=<group>
みたいにすることで Impersonate-User
や Impersonate-Grou
といったHTTPヘッダーが設定され、ユーザーのなりすましが可能な機能があるらしい
- HTTPヘッダーからなりすますユーザー情報を取得(なければ次のハンドラーを実行): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go#L43-L52
- なりすます元ユーザー情報を取得: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go#L54-L59
- なりすます権限があるかなどの認証を行い、なりすますユーザーを決定(最後に認証されたユーザーがなりすましユーザーになる): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go#L61-L151
- ユーザー情報をなりすますユーザーに入れ替え、audit eventを送信: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go#L153-L164
- なりすましに関するHTTP Headerを削除: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go#L166-L173
FlowControl
TODO これは別でやった気がするのでスキップ
genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)
認可処理
中身はこれ
渡しているAuthorizer
ベースとなるAuthorizerの生成
例えばRBACならこれが利用されると思われる
RBACのAuthorizer自身はこれ
メソッドはこれ
これがAuthorize()内部で使用されるresolverのメソッド
VisitRulesForにわたすvisitはこれ
これでRBACの権限が満たされてるかチェックしてる気がする
(OKならv.allowed = trueを入れてる)
VisitRulesForでこんな感じでvisitを渡すことで認可できるか判断させてる感じ
でVisitRulesFor呼び出し後にruleCheckingVisitor.allowed == trueかチェックしてOKなら認可ということで完了
認可できないリクエストが来た場合にはログを出す
更に認可不可エラーを返す
AuthorizerがAuthorizeClientBearerTokenで更にBearerToken用のAuthorizerを入れてラップされる
WithAuthorizationnの流れ
- 認可のためのユーザーとrequestInfoを入れたAttributesRecordを生成: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L54-L58
- 登録されたAuthorizerをそれぞれ実行していって、1つでもOKだったら認可され、audit eventが出力されて次のハンドラーが呼び出される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L59-L66
- エラーだったらエラー発生をaudit eventに出力してInternal Server Errorをレスポンスで返して終了: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L67-L71
- そうでなければForbidden返す: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L73-L76
おさらい
これで一通りデフォルトフィルタの流れが整理できた
Admission Webhook関連
Admission Webhookがリクエストするサービスへのリゾルバー
デフォルトだとaggregatorapiserver.NewClusterIPServiceResolverというのが利用されてるぽい
組み込みのAdmission Controlについて
組み込みのAdmission Controlerを登録するのはこれっぽい
呼び出し側はこれっぽい 更にそれを呼び出してるのはこれつまりAPIServer初期化時にはすでに設定されていることになる
これがどのように利用されるかというと、ややこしいけどこんな感じになっていた
- CreateKubeAPIServerでAPI Serverのインスタンスが生成される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L219-L222
- これによってcontrolplane.Instanceが生成される: https://github.com/kubernetes/kubernetes/blob/21fba79d453b0bab7153f46916126c754d10341e/pkg/controlplane/instance.go
- この中でInstallAPIsというのが実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L450-L452
- ここで色んなAPIのパスがハンドラーにインストールされるが、その中にm.GenericAPIServer.InstallAPIGroupsというのがある: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L620-L622
- InstallAPIGroupsの中ではグループ毎にinstallAPIResourcesというのが実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L503-L505
- この中でapiGroupVersionというのが取得され、apiGroupVersion.InstallRESTが実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L429-L445
- installRestではAPIInstallerのgroupにAPIGroupVersionが渡され、APIInstaller.Installが実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go#L105-L121
- Installの中で更にパスごとにregisterResourceHandlersが実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L114
- registerResourceHandlersの中で APIInstaller.group.Admitが取り出される(これがadmission.Interfaceで実行するAdmission Controllerをまとめて実行するものになっている): https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L192
- これらが各Verbsの処理実行時に渡される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L764
- そしてここらへんでmutatingのadmitが実行される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go#L146-L156
- validatingはこのrequestFunc内部で実行されるUpdateの引数を生成するためのAdmissionToValidateObjectFuncやrest.AdmissionToValidateObjectUpdateFuncで利用され、バリデーション処理の結果を返す関数を生成する: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go#L182-L195
- AdmissionToValidateObjectFuncはこんな感じ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go#L159-L192
- AdmissionToValidateObjectUpdateFuncはこんな感じ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go#L254-L279
- AdmissionToValidateObjectFuncを利用するwithAuthorizationはこんな感じ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go#L229-L259
- んでr.Updateというを実行するのだが、これはAPIGroupVersionのAPIGroupVersion.Storageのrest.Storageであった: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go#L52
- このAPIGroupVersionはgetAPIGroupVersionによってAPIGroupInfoとgroupVersionを元に生成される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L542-L551
- このAPIGroupInfoはだいぶさかのぼって、InstallAPIsの中でrestStorageBuilder.NewRESTStorageというので生成されていた: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L589-L592
- んでこのrestStorageBuilderというのはめっちゃいっぱいある: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L420-L449
- 例としてapiserverinternalrestのNewRESTStorageというのはこんな感じ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/apiserverinternal/rest/storage.go#L33-L45
- APIGroupVersion.Storageの元になるapiGroupInfo.VersionedResourcesStorageMapはこんな感じで生成されている: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/apiserverinternal/rest/storage.go#L37-L43
- 生成処理そのものはこれ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/apiserverinternal/rest/storage.go#L47-L57
- storageversionstorage.NewRESTの中身はこれ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/apiserverinternal/storageversion/storage/storage.go#L40-L64
- ということでr.Updateの中身はこれ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go#L489-L678
- 戻ってきてr.Updateに渡されるwithAuthorizationやAdmissionToValidateObjectUpdateFuncで生成した関数はこんな感じで利用される: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go#L561-L565
- んでそもそもこれはe.Storage.GuaranteedUpdateというやつに渡される関数の中で実行されることになるのだが、e.Storage.GuaranteedUpdateの中身はこれ: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L285-L422
- この中の実際のデータアップデート処理の前で実行され、チェックやデータのアップデートが行われる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L340
e.Storage.Storageの中身
各ストレージのe.Storage.Storageは以下のようにしてopts.Decoratorによって設定される
このoptsはたどるとRESTOptionsGetterが渡されているよう(少なくともapiserverの場合は)
GetRESTOptions
Decorator
更にその中身
newETCD3Storage
最終的に生成されるやつ
ということで、このetcdのstoreオブジェクトが最終的にe.Storage.Storageの中身になるのだと思われる
(CRDのデータとかだとまた変わる?)
EnableWatchCacheというオプションがtrueだとetcdクライアントとの間にキャッシュ管理コンポーネントを挟んでキャッシュを利用する仕組みがあるよう(デフォルトだとfalseのよう)
Admission Webhookを実行するAdmission Controller周りについて
validating/mutatint周りの登録はこれ?
呼び出し側はこれっぽいそれがこっちのNewAdmissionOptionsで生成される
そして追加のプラグインが登録されたりする
ちなみにクッソわかりづらいがここにデフォルトでONのプラグインが定義されている
(なぜ関数はデフォルトでOFFのプラグイン一覧を返すのに、その関数の中でデフォルトONのプラグインを定義しているのか...)
ここにあるValidatingWebhook/MutatingWebhookのプラグインがそれぞれValidatingWebhook/MutatingWebhookを他のadmission controllerと一緒に実行するマンになっている
Admission Webhook関連
ここでobjInfo.UpdatedObjectが実行されている
objInfo自体はここで生成されていてAdmission Webhookを行うためのapplyAdmissionも一緒に渡されている
UpdatedObjectの中身はこれ
ちなみにこれと一緒にobjInfoに埋め込まれてるapplyPatchは元々のobjectをパッチでアップデートする処理?
(PATCHメソッドの処理について見てるからこれがある感じ?)
おまけ
restfulUpdateResourceなどにadmitが渡されるというところまでは話をしたけど、このrestfulUpdateResourceがどのように実行されるかまとめてなかった
ここからの話し
このrestfulUpdateResourceなどがどのように実行されるかというとInstrumentRouteFuncの中身をみるとこんな感じになっている
ここでrouteFuncというrestfulUpdateResourceなどで生成されたrouterが最終的に呼ばれてリクエストが処理されている
その後に呼ばれるMonitorRequestでリクエストのメトリクスが記録される
のでこのメトリクス取得のためにこのhandlerがラップしてるのだと思われる
そのハンドラーがこのパスとverbのときに処理するよっていうrouterを生成する
(例えばverbがPUTならws.PUT(path)のような形で生成するなど)
で最後にここで生成したrouterがまとめて登録される
データ永続化周り
暗号化/復号化周り
このあたりの話し
こんな感じで複数のtransformerが利用できる
これらのtransformerがgroup resourceごとに生成され、対象のリソースに対する暗号化/復号化が行われる
これがここでstorageFactoryにセットされる
storageFactoryはここらへんで利用されている
storageFactoryはRESTOptionsGetterに埋め込まれる
んでGetRESTOptionsが呼ばれた際に必要なStorageFactoryから必要なオブジェクトなどがstorageConfigに移植される
実際に移植を行なっているのはStorageFactory.NewConfig(resource)の中でこれ
特にここでtransformerを取り出してる
このStorageConfigはここで使用される模様
このDecoratorというのはこいつのよう
中身はこれ
その中で呼ばれてるのはこいつで
更に呼ばれてるのはこれ
普通はetcd3が使われると思うのでこれが呼び出されるはず
んでtransformerはここで使われている
最終的にetcd3のstoreオブジェクトのstore.transformerに格納される
んでこいつがGet系の処理をするときは
のようにしてtransformer.TransformFromStorageでデコードされてCreate系の処理をするときは
のようにしてtransformer.TransformToStorageでエンコードされるよう暗号化/復号化にKMS Providerを利用する場合
KMSの時に生成されるtransformerはこれ
envelopeServiceFactoryの中身はこれ
この中のkmsClientを生成している
クライアントとの接続はgRPCで行うようなので、そのコネクション確立をクライアント生成時に行なっているみたい
envelopePrefixTransformerはこれ
envelopeServiceのメソッドはここらへんで利用されている
このDecrypt/Encryptはこれで呼び出しごとにkmsClientにリクエストを行なっている
このkmsClient自体はunix domain socketで接続するようなので、つまりgRPCのインターフェイスに沿ったkms serverをローカルで立てて、API ServerのkmsClientが通信するのはこのローカルで動いているkms serverということで、実際にKMS Providerと通信を行うのはこのkms serverになるんじゃなかろうか
ドキュメントにもそれっぽいことが書かれている(kms serverと読んでいたのはk8s的にはkms pluginというのが正しそう)
KMSのインターフェイスはこれになりそう
例えばAWSのKMS Plugin実装はこちらのよう
kms plugin(server)実装はこれっぽい
でここらへんでAWS KMS側のEncrypt/Decryptを呼び出してる
このAWS KMS側のEncrypt/Decryptを呼び出すsvcはAWS KMSのインターフェイスになっているので合ってそう
このsvcになる実態はこれ
このクライアントの中身はこれだけ
watch周りの仕組み
ついでにwatch周りの仕組みを見ていく
ここでそれっぽいのが出てきてた
中身はこれ
このwatcherはLIST処理のところで利用されている
WATCH/WATCHLIST verbsあるようだけど、deprecatedとのことなので多分LIST処理のところだけ全部読めば良い気がする
restfulListResourceはこれ
handler.ListResourceはこれ
watch関連の処理はここらへんっぽい
rw.Watchの中身はこれ
内部で実行されるwatchChan.Runはこれ
実行されるgoroutineはこの2つ
watchChan.startWatching
必要に応じて初期化処理
ここでetcdライブラリのwatchを呼び出し
読んでるのはこれ(etcd側のライブラリなのでこれ以上深堀りしないでおく)
etcd側のwatchのドキュメントはこれっぽい
特に内部のgRPCのwatche streamを利用しているよう
ここで最終的にsendEventしてレスポンスを送ってるっぽい
イベントはwc.incomingEventChanに送っている https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L408
processEvent
processEvent側ではwc.incomingEventChanのイベントを処理してる
こんな感じでwc.resultChanにイベントを送ってるこのresultChanはResultChan()で取得できるようになっている
続き
生成したwatcherをserveWatchに渡している
serveWatchを実行する関数を渡されているRecordLongRunningは最終的に関数をここで実行している
(RecordLongRunningは長時間実行のリクエスト情報を記録したりしてるだけっぽいので詳細はスキップ)
serveWatchはこれ
最終的にはWatchServerオブジェクトを生成してServeHTTPを実行している
ServeHTTPの中身はこれ
websocketでのリクエストの場合はwebsocketで処理するっぽい
(これはdeprecatedになるやつ?)
先程のResultChanはここで取得してる
んでここでイベントを受け取って処理してる
最終的にここでイベントを処理してe.Encodeでレスポンスの書き込みしてる
eはここで生成している
eにわたすframerはここで生成していてwオブジェクトを渡している
e.Encodeの中身はこれ
これで最終的にクライアント側にWatchのイベントをユーザーに返すところまでたどり着いた
kubectl exec/attach/log 周り
それっぽいのを見つけた
ここで使われている
呼び出し元はこれ
ここでrestStorageMapを生成している
多分ここ
exec/attach/logなどのサブリソースは全てCONNECTメソッドを実装していてそれを呼び出している
proxyサブリソースは"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"メソッドがある
それ以外では"GET", "POST"メソッドがある
method変数は特にhandler設定には使われてないので、HTTPメソッドによる動作差は無い?
handlerの中身はこれ
ざっくり
- オプションを生成
- mutating admissionをadmit
- validating admissionをadmit
- connecter.Connectで生成したhandlerのhandler.ServeHTTPを実行