Open14

Kube API Server

ピン留めされたアイテム
bells17bells17

Index

コードリーディング用ブランチ/タグ

https://github.com/kubernetes/kubernetes/tree/v1.21.2

TODO

bells17bells17

エントリーポイント

Run

リクエストに対して処理を行うチェーンの生成?

TODO 多分はじめの段階だとまだ良く理解できないと思う

中身

server.PrepareRun()

中身

s.GenericAPIServer.PrepareRun() 中身

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L294-L324
これ

prepared.Run(stopCh)

中身

bells17bells17

NonBlockingRun() - 実質的なメイン処理だと思うやつ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go#L369-L418

s.SecureServingInfo.Serve(s.Handler, s.ShutdownTimeout, internalStopCh)

RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)

s.RunPostStartHooks(stopCh)

bells17bells17

ServerRunOptions

API Serverのデフォルトオプション

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/options/options.go#L97-L144

で生成される

API Serverのフラグとコマンドラインオプションとの紐付け

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/options/options.go#L166-L286

ここでAPI Server固有のコマンドラインオプションと紐付けられてる

最終的なオプション値の調整作業

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L597-L709

でコマンドラインオプションなどの設定に基づき、API ServerのServerRunOptionsの値の調整を行い、options.ServerRunOptionsにセットされる

bells17bells17

CreateServerChain

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L197

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

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L306

etcd接続確認周り

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L311-L315

CheckEtcdServersがetcdへの接続を確認する関数で、これが成功を返すかタイムアウトになるまでポーリングする

capacilityセットアップ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L317-L326

ただ単にインスタンス初期化して渡してるだけっぽい
TODO このcapability何に使うん..?
TODO capabilityはAPI Serverの接続数とかそういうものを管理してる??

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/capabilities/capabilities.go#L49-L61

メトリクス設定

オプションからのメトリクスオプションの設定と適用

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L328-L329

klogのloggerを設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L331

serviceIPRange, secondaryServiceIPRange, apiServerServiceIPを取得、もしくはデフォルト値を設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L333-L346

configを生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L348-L381

クライアントのCA証明書設定用のプロバイダを設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L383-L399

こいつがCA証明書のファイルが指定されていた場合、それを定期的に読み込んで更新を行い、そいつをCA証明書として利用する仕組みのよう

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates/dynamic_serving_content.go#L79-L169

また、その下のrequestHeaderConfigはClientCAFileが無いと設定されないようなので、認証関連の処理を追加するということな気がしてる

buildGenericConfigで生成したdiscoveryを定期更新するgo routineを実行する関数をPostStartHookに追加

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L401-L403

AddPostStartHookってことだからAPI Server起動後に呼ばれるんだと思う

nodeTunneler, EgressSelectorがあればそれぞれconfigに設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L405-L422

ServiceAccountに関する設定をconfigに適用

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L424-L436

ServiceAccountに関する設定はここらへんで設定されたオプションのよう

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/authentication.go#L301-L306

最後に生成したconfig, serviceResolver, pluginInitializers を返してCreateKubeAPIServerConfigは終了

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L438

CreateKubeAPIServerConfig

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L208-L217

一言で言うとAPI Extention Serverを生成してる

API Extention Server用のconfigを生成

ここは中でもシンプルにconfigを設定してるだけであまり特別なことしてないのでシンプルに

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L208-L213

createAPIExtensionsServer

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L214-L217

中身はこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/apiextensions.go#L103-L105

その中身は以下のような感じ

c.GenericConfig.New

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L129

handlerChainを生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L549-L551

BuildHandlerChainFuncというのはこれっぽい

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L722-L771

この中で色んなhandlerChainが設定されている
これ中でhandler.ServeHTTPを呼び出しているので、実際のhttpリクエストの際に呼ばれるのはこの関数で最後に定義されたhandlerからOKだったら順番にchainが呼び出されるって流れになる気がしてる

例えば認可の処理だったら
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L59-L66
のように認可されたらhandler.ServeHTTPを呼び出すみたいになってる

ちなみにこのAuthorizerはRBACの場合↓が呼びされるんじゃないかと思ってる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L75-L127

これらのhandlerchainにTrackStartedとTrackCompletedが呼び出されていて、メトリクスの計測が行われているようだった

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filterlatency/filterlatency.go#L64-L96

また認証は認可よりもだいぶ前で
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L748
で行われている

例えばx509の証明書による認証の場合、以下が呼び出されて認証される?
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/authentication/request/x509/x509.go#L132-L175

NewAPIServerHandler

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L552

HandlerChainBuilderFnなどを元にAPI Server Handlerを生成してるだけ
なんかRestサーバー構築をしてるっぽい
あまり複雑なことはしてないけど中身はこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L73-L101

GenericAPIServer生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L554-L596

ちょっとよくわからないけど設定値の調整?

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L598-L609

postStartHooks, preShutdownHooksを設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L611-L625

sharedInformerの起動とsharedInformerの状態チェックをreadzチェックに追加

多分go routineでshared informer起動してると思われる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L627-L643

この起動されるshared informerというのは以下のよう
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/client-go/informers/factory.go#L127-L138

多分このへんpriority-and-fairnessのための値更新のためのgo routineをpostHookに追加してる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L645-L684

渡されたヘルスチェック処理をサーバーに追加

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L686-L698

TODO APIのパス一覧を返すプロバイダを設定?

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L700

各種API Serverのパスをサーバーに登録

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L702

NotFoundHandlerの追加登録処理?

TODO よくわかってない

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L704-L711

CreateKubeAPIServer

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L219-L222

API Serverの作成

中身はこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L238-L246

以下更に詳細

c.GenericConfig.New

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L354-L357

これはAPI Extention Serverの方と同じやつだと思うのでそっちを参照

kubectl logs用のエンドポイントを設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L359-L361

TODO docker logsとかはしてなさそうだからAPI Serverのログ取得?

必要に応じてOpenID用のパスを設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L363-L392

各種クラウドプロバイダなどとOpenID(Connect?)を利用してアカウント連携を行っているのはこのパスの処理?
AWSとかGCPのアカウントでクラスターにアクセスするのはここらへんの処理を利用している?

レガシーエンドポイントのインストール

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L399-L418

TODO v1なので何がレガシーなのか理解してない

ちなみに各種エンドポイントのRestサーバーへのインストールはここらへんで行われているっぽい

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L687-L1001

登録されるactionsは以下のあたりでマッピングされている

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L410-L511

(結構予測だが)ちなみに実際の各種CRUDなどの操作はおそらく以下のあたりが呼び出されてる(DELETEの例)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go#L983-L1073

それで普通にetcdを使ってる場合には最終的に↓のあたりが呼ばれているのだと思われる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L187-L197

通常の?エンドポイントのインストール

https://zenn.dev/bells17/scraps/81b6ade4cbd40d#comment-9cd49270de5148

tunnelerがあればtunneler go routineの起動とヘルスチェックの追加

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L454-L456

ClusterAuthenticationTrustControllerの起動と、あればClientCA更新のコントローラー起動

kube-system configmap/extension-apiserver-authenticationのメンテナンスを行うClusterAuthenticationTrustControllerの起動とあればClientCA更新のコントローラー起動を行う

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L458-L493

APIServerIdentity feature gateが有効な場合、lease controllerとleace gcの起動を行う

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/controlplane/instance.go#L495-L526

createAggregatorConfig

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L224-L228

AggregatorConfigを生成

StorageVersionAPI, APIServerIdentity feature gatesが有効な場合に対応するBuildHandlerChainFuncを生成して、httpのハンドラーチェインを生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/aggregator.go#L74-L80

ちなみにこのfeature gateのチェックを通らない場合には普通にDefaultBuildHandlerChainがセットされるっぽい感じ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L305

createAggregatorServer

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L229-L233

aggregatorConfig.Complete().NewWithDelegate

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/aggregator.go#L129-L132

c.GenericConfig.New("kube-aggregator", delegationTarget)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L170-L173

これはGenericAPIServerを生成してるだけなので、他のAPI Serverですでにで同じのが出てきてそう

apiregistrationClient, err := clientset.NewForConfig(c.GenericConfig.LoopbackClientConfig)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L175-L182

Apiregistration用のclientsetを生成してる
Apiregistrationというのは多分これのことでAPIServiceリソースがあるGVKのことっぽい

https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#apiservice-v1-apiregistration-k8s-io

https://kubernetes.io/ja/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/

resourceExpirationEvaluator, err := genericapiserver.NewResourceExpirationEvaluator(*c.GenericConfig.Version)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L198-L202

apiGroupInfo

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L204-L207

apiGroupInfoはメソッドは持っていないので情報を入れる箱の役割っぽい

enabledVersions

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L209-L215

TODO 必要なGVK情報を突っ込んでる??

/apis へのリクエストのハンドラーをここで設定してる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L217-L223

apiserviceRegistrationControllerを生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L225

軽くみた感じはこいつはReconcilation Loopで

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L366-L424

を呼び出して、APIServiceリソースを元にAPIAggregatorに登録を行い、APIAggregatorサーバーのハンドラーを登録してるみたい

逆にAPIServiceリソース削除時は

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L426-L445

を呼び出して削除するっぽい

ProxyClientCertFileなどがある場合に読み込みを行って、更新処理を行うコントローラーをAddPostStartHookOrDie経由で動かすようにしてる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L226-L241

ついで?にapiserviceRegistrationControllerにAddListnerを追加して証明書・秘密鍵の更新イベントを伝播させるようにしてる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L234

availableControllerなるものを生成してる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L243-L255

軽く見た感じこいつはAPIServiceに登録されたExtending API Serverのヘルスチェックを行うコントローラーっぽいようにみえる

informer起動

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L257-L261

apiserviceRegistrationController起動

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L262-L271

availableController起動

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L272-L276

StorageVersionManagerのあれこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L278-L326

前半部分は待機処理

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L283-L303

後半部分は

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L304-L324

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のバージョンアップも成功してるということ?

一応中身は

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storageversion/manager.go#L113-L160

のような感じになっていてアップデート処理が行われている

実際にデータ処理を行うクライアントは

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storageversion/manager.go#L120

にあるclient-goにあるInternalなクライアントのStorageVersionsリソース用のクライアントが利用されている
Internalなリソースあるんだという感じだ

bells17bells17

createAPIExtensionsServer について

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L214

ここで生成されるわけだけど

でapiExtensionsServer.GenericAPIServerとapiExtensionsServer.Informersを使うために生成してる感じ?

んでこのGenericAPIServerは

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L231-L266

あたりでAddPostStartHookOrDieされていたり

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L197-L218

でCRDのハンドラーを入れてたりする

なのでこのAPI Server内部で動くAPIExtensionsServerというのはCRDリソースをハンドリングして処理するための、内部的なExntending API Serverという感じ??

んで対称的に独自のExtending API ServerはAPIServiceリソースによって追加されてハンドリングを動的に追加/削除を行うAPI Serverという感じになる?

前に読んだときの

https://zenn.dev/bells17/scraps/81b6ade4cbd40d#createapiextensionsserver

ではここらへんあまり深堀りしていなかった

CRDリソースのマッピング

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L138-L173

ここらへんでマッピングして処理できるようにしてる感じ?

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L175-L218

ここらへんで対応するhttp側のハンドラーを設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L207-L213

またNewCustomResourceDefinitionHandler内でEventHandlerを設定しているので、このハンドラー自体がCustom Controllerのように動作しそう

起動する各種コントローラーを生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L220-L229

informerを起動

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L231-L234

AddPostStartHookOrDieで各種コントローラーが起動するよう設定

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L235-L258

CRDリソースがローカルキャッシュに同期されるまで待機

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L259-L266

各種コントローラーについて

NamingConditionController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go#L123-L221

特にここで処理しているCRDのステータス管理を行うことが責務のコントローラーに見える

establishingController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish/establishing_controller.go#L115-L150

具体的な処理はせずただステータス更新をしてるだけに見える
何らかの必要な初期化処理を行っている??

CRDリソースのイベント以外に影響がなさそうなコントローラーだから、わざわざコントローラーを切り出して処理している意味がわからない

nonStructuralSchemaController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema/nonstructuralschema_controller.go#L132-L185

なんとなくだけどCRDスキーマがバリデーションエラーになってないかチェックしてる?

apiApprovalController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/apiapproval/apiapproval_controller.go#L127-L179

CRDの api-approved.kubernetes.io アノテーションの値を見てCRDの状態チェックを行う?
となるとこのアノテーションが別のところで設定されてくるはず

finalizingController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L109-L176

CRDリソースのfinalizer処理を行っている感じ

discoveryController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go#L77-L196

bells17bells17

API Serverの一番始めのハンドラー

一番始めのAPIExtentionServerが生成するGenericAPIServerのAPIServerHandlerは

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L73-L101

で、こいつのServeHTTPが

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L122-L155

だから、これが一番始めのhttpハンドラーになる?

でもAPIExtentionServer自体のServeHTTPも

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L187-L190

であるから、こっちが先?

これべつのところも読まないとわからなそう

そのためこのapiServerHandler.ServeHTTPが一番始めのハンドラーであることがわかる: https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/handler.go#L187-L190

/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"
      }
    }
  ]
}

参考
https://www.mtioutput.com/entry/kube-curl-apiserver

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
bells17bells17

openapicontroller.NewAggregationController について

一つ上のスクラップで貼ったけどこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L356

んでそれが
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L70-L73
でNewされるタイミングでキューされたりするときに利用されるspecDownloaderとopenAPIAggregatorについても調べる

specDownloader

このdownloaderというのは実質

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/downloader.go#L52-L103

のDownloadメソッドだけだった

このメソッド何がすごいって(はじめの方とかは多分)http serverまだ起動してないのに

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/downloader.go#L70

で無理くりhandlerのServeHTTPにリクエストしてopenAPISpecを取得してくるところ
一応どんな感じで取れるのかの確認をしてみたけど curl http://localhost:8080/openapi/v2 の結果は長すぎたのでgistに貼った

https://gist.github.com/bells17/747706251b4e0986db567ff38ed9d200

くっそ長いけどこんな感じで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

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/aggregator.go#L75-L133

で生成されてる

ちなみにBuildOpenAPISpecの中で呼び出されるOpenAPIConfigのGetDefinitionsの中身は多分

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/generated/openapi/cmd/models-schema/main.go

を元に自動生成される pkg/generated/openapi/zz_generated.openapi.go ファイル
(手元にはあるけど、githubには無いから多分ビルド時とかに自動生成される系だと思う)

openAPIAggregationController

話しは戻ってきてopenAPIAggregationControllerを生成するNewAggregationControllerのところから

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L70-L73

ここではopenAPIAggregator.GetAPIServiceNamesが呼び出されていて、中身はこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/aggregator.go#L64-L73

これで生成されたspecAggregator.openAPISpecsの名前が全部取得されopenAPIAggregationControllerのキューに入る

openAPIAggregationController.Run

そして調整ループを動かすopenAPIAggregationController.RunはPrepareRunのタイミングでAddPostStartHookOrDieでサーバー起動後に実行される

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L96-L136

ちなみに

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L121-L127

を見て気づいたんだけど中身が

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator/aggregator.go#L59-L62

となっていて生成されたOpenAPISpecと同じapiServiceNameかどうかを持ってローカルから取得できるかどうかを判断してるっぽかった
のでこの判定のためにOpenAPISpecは生成してるという感じなのかな?という

また

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go#L131-L133

で対象のapiServiceNameが削除されているのは恐らくだけど独自のextended api serverが無くなったときのケース向けの対応なのかな?

AddAPIService/UpdateAPIService/RemoveAPIServiceについて

APIServiceRegistrationController.sync

結論からだけどAddAPIService/UpdateAPIService/RemoveAPIServiceはAPIServiceRegistrationController.syncを通して呼ばれるよう

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go#L79-L90

このコントローラーのイベントハンドリング元はAPIServiceリソースのよう
(他にあるのかは不明)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go#L68-L72

このapiserviceRegistrationControllerは生成時にAPIAggregatorをAPIHandlerManagerとして受け取る

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L225

APIAggregator.AddAPIService/APIAggregator.RemoveAPIService

APIAggregator.AddAPIService/APIAggregator.RemoveAPIServiceは

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L366-L445

にある

APIAggregator.AddAPIService

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L366-L424

といったフローでs.openAPIAggregationController.AddAPIServiceやs.openAPIAggregationController.UpdateAPIServiceが呼び出されてた

APIAggregator.RemoveAPIService

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go#L426-L445

削除時はこんな感じでハンドラーへの登録解除したり、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#L109-L180

内部で最終的に

https://bambootuna.com/go/396/

にあるような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"
      ]
    }
  ]
}
bells17bells17

CRDのハンドラーやコントローラーについて

crdHandler

CRDリソースのハンドリングは

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L197-L218

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L234-L384

のような感じでcrdHandlerがやってるっぽい

ServeHTTP

とても長い

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L234-L384

ちなみにmetrics.InstrumentHandlerFuncというのは処理時間の計測処理を追加してるっぽい

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L379

WithWaitGroupの方はwatchや/debug/pprof/のような処理時間が長いリクエストを処理する際にはCRD単位で1リクエストずつ処理するように制御を行うフィルタのよう
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L380

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#L612-L959

EventHandler

crdHandlerは少し特殊で、コントローラーを使わずにInformerのイベントハンドラーで直接処理を行なっている

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go#L207-L213

内容としてはいずれも、必要があればローカルキャッシュであるr.customStorageの更新処理を行うというものになっている

discoveryController

ここらへん

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L220

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L251

CRDリソースがイベント元になっている

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go#L66-L70

調整ループはこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go#L77-L196

最終的に

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go#L178-L193

でやっているように、CRD定義に基づいたGroupレベルのDiscovery APIと、その下位のVersionレベルのDiscovery APIのハンドラーを設定するのがこのdiscoveryControllerの役割のよう

もし削除されたりしてGroupそのものやバージョンが無くなった場合には

あたりでハンドラーをリセットしている

また面白そうだったのは

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go#L128-L132

で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

ここらへん

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L221

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L244

syncは以下のあたり

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go#L234-L285

役割としてはCRDとして登録する

  • Plural Name
  • Singular Name
  • ShortNames
  • Kind
  • ListKind

といった名前が同じGroup内の他のCRDですでに使われていて競合しないかを確認することになる
確認が問題なく行われれば

  • namesAcceptedConditionがConditionTrue
  • establishedConditionがReason=Installing

となり対象のCRDインストールプロセスが先に進む

各種名前の競合チェック処理は全てcalculateNamesAndConditionsで行われている

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go#L123-L221

ちなみに例えば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

ここらへん

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L222

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L246

このコントローラーは他のCRDコントローラーとは違っていて複数同時に動くものらしい

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema/nonstructuralschema_controller.go#L199-L201

このコントローラーはnamingControllerのCR定義の構造バリデーション版で、spec.XXXなどのCR定義構造にエラーがある場合にはNonStructuralSchemaというステータスのコンディションエラーが付与されるよう

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema/nonstructuralschema_controller.go#L132-L185

このコントローラーでもnamingControllerと同様にバリデーションエラーが無いかの計算処理を行う関数を利用して状態チェックを行うよう

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema/nonstructuralschema_controller.go#L85-L130

apiApprovalController

次はこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L223

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L247

これも同様に複数同時に動くよう

このコントローラーはどうやらGroupが

  • k8s.io
  • kubernetes.io

のCRDについて"api-approved.kubernetes.io"というアノテーションが正しいフォーマットで設定されているかをチェックするコントローラーらしい

https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2337-k8s.io-group-protection/README.md

これがKEPのようで、CRDでk8s.ioなどのグループを使われないように保護するのが目的らしい
なので普通にCRDを作っている場合には気にする必要のないコントローラーとなりそう

finalizingController

ようやくfinalizingController

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L224-L228

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go#L248

これも同様に複数同時に動くよう

調整ループはこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go#L109-L176

先にまとめておくとこのコントローラーはCRD削除時のfinalizerの処理を行うもので、CRD削除前に対象のCRのデータを全て削除するまでCRDの削除を待機させるためのコントローラーのよう

処理のフローはこんな感じ

ちなみにこのfinalizerは

https://github.com/kubernetes/kubernetes/blob/v1.21.1/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go#L145-L147

で付与されているようで、処理を見てる感じetcdからCRDの実データを削除する中の処理の1つとして付与されているように見える
なので、多分CRD削除処理行なった際に自動で付与される?

bells17bells17

DefaultBuildHandlerChain の中身

genericfilters.WithPanicRecovery(handler, c.RequestInfoResolver)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L769

エラーハンドラーを設定

genericapifilters.WithRequestReceivedTimestamp(handler)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L768

リクエスト時刻を記録

genericfilters.WithHSTS(handler, c.HSTSDirectives)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L767

HTTP の Strict-Transport-Security レスポンスヘッダー (HSTS)を設定

HSTSについては
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Strict-Transport-Security

デフォルトのHSTSDirectivesは何もないっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/server_run_options.go#L190-L192

genericapifilters.WithCacheControl(handler)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L766

Cache-Control: no-cache, private headerを追加してる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/cachecontrol.go#L23-L33

genericapifilters.WithWarningRecorder(handler)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L765

warningを記録するレコードをcontextに設定?
そもそもここでいうwarningとは?

genericapifilters.WithWarningRecorder(handler)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L765

audit用のアノテーションをrequestのcontextに設定
何に使うアノテーション?

genericfilters.WithProbabilisticGoaway(handler, c.GoawayChance)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L761-L763

httpsの場合に利用されるみたい
https2のGOAWAYというストリームのコネクションを終了することをクライアントに通知するフレームを送るための設定を行うらしい

GOAWAYについてはここらへん

https://datatracker.ietf.org/doc/html/rfc7540#section-6.8

https://www.nslabs.jp/http2-client-implementation-sample-common.rhtml

https://stackoverflow.com/questions/55087292/how-to-handle-http-2-goaway-with-httpclient

デフォルトは0で、GOAWAYを送信しない設定らしい

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L207-L211

genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L760

request情報からrequestInfoを生成してrequestのcontextに設定

requestInfoは以下のような構造体で

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go#L41-L65

requestInfoの生成処理は

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go#L88-L247

となり、API Serverがリクエストに応じた処理を行う際に利用されるっぽい

genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L759

WithWaitGroupの方はwatchや/debug/pprof/のような処理時間が長いリクエストを処理する際にはCRD単位で1リクエストずつ処理するように制御を行うフィルタのよう

genericapifilters.WithRequestDeadline(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc, c.Serializer, c.RequestTimeout)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L757-L758

呼び出し元はこちら

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/request_deadline.go#L45-L103

コメント曰くこういう感じらしい

WithRequestDeadlineは、指定されたリクエストに適用可能なタイムアウト期間を決定し、適切な期限で新しいコンテキストを設定します。
auditWrapperは、失敗したリクエストを監査するhttp.Handlerを提供します。
longRunningは、指定された要求が長時間実行されている要求である場合にtrueを返します。
requestTimeoutMaximumは、デフォルトの要求タイムアウト値を指定します。

genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L753-L755

これがlongRunningなAPIじゃない場合にラップを行い、リクエストが長時間に渡る場合にタイムアウト処理を行うフィルタのよう

渡されるc.LongRunningFuncはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L476-L479

BasicLongRunningRequestCheckの中身はこちら
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/filters/longrunning.go#L27-L41

genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L751

CORS設定をヘッダーに行う

関数の中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/vendor/k8s.io/apiserver/pkg/server/filters/cors.go

許可されるOriginはデフォルトでは無し
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/server_run_options.go#L186-L188

CORSについてはこれを参照
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

ここまでがHeaderやタイムアウト処理などの前処理系のフィルタ: これ以降は実際の認証処理などが行われていく(なのでこれ以降の個々の処理時間が計測されているのでは?)

genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L743-L749

認証処理

c.Authentication.Authenticator はここで生成されてるっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/authentication.go#L490-L493

c.Authentication.APIAudiencesはこちら
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/authentication.go#L466-L469

認証失敗時のhaldlerはこちら
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L743-L746

c.Authentication.Authenticatorの生成処理の中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/authenticator/config.go#L93-L215

authenticator

NewDynamicVerifyOptionsSecure

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/authenticator/config.go#L100-L111

509.NewDynamic(config.ClientCAContentProvider.VerifyOptions, x509.CommonNameUserConversion)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/authenticator/config.go#L113-L117

例えばこれの中身はこんな感じ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/authentication/request/x509/x509.go#L132-L175

tokenAuthenticators

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/authenticator/config.go#L119-L195

NewAuthenticatedGroupAdder

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/authenticator/config.go#L206

unionAuthRequestHandler

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/authentication/request/union/union.go#L52-L71

これが登録されたauthenticatorを順々に実行して1つでも通ったらそれを返すようになっている

返されるレスポンスのインターフェイスはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/authentication/authenticator/interfaces.go#L54-L65

authenticatorを使った認証処理の中身

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L37-L82

genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L739-L741

処理としては簡単に確認した感じ、リクエスト内容やユーザーをポリシーと照らし合わせて、audit eventの出力を行い、response writerをaudit eventを出力できるようにラップすることで以降の処理でもaudit eventを出力できるように設定をしてるっぽい
また、panic発生時?にそのイベントを拾ってpanicのエラーをaudit eventとして出力できるようになっている

中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go#L38-L113

genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L735-L737

中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go#L40-L177

ここらへんの機能っぽい
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation

kubectl --as=<user> --as-group=<group> みたいにすることで Impersonate-UserImpersonate-Grou といったHTTPヘッダーが設定され、ユーザーのなりすましが可能な機能があるらしい

FlowControl

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L727-L733

TODO これは別でやった気がするのでスキップ

genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L723-L725

認可処理

中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L44-L78

渡しているAuthorizer

ベースとなるAuthorizerの生成

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L524-L528

例えばRBACならこれが利用されると思われる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/authorizer/config.go#L128-L136

RBACのAuthorizer自身はこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L159-L166

メソッドはこれ

https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L75-L127

これがAuthorize()内部で使用されるresolverのメソッド
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/rbac/validation/rule.go#L179-L237

VisitRulesForにわたすvisitはこれ
これでRBACの権限が満たされてるかチェックしてる気がする
(OKならv.allowed = trueを入れてる)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L63-L73

VisitRulesForでこんな感じでvisitを渡すことで認可できるか判断させてる感じ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/rbac/validation/rule.go#L200-L204

でVisitRulesFor呼び出し後にruleCheckingVisitor.allowed == trueかチェックしてOKなら認可ということで完了
https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L78-L81

認可できないリクエストが来た場合にはログを出す
https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L83-L120

更に認可不可エラーを返す
https://github.com/kubernetes/kubernetes/blob/v1.21.2/plugin/pkg/auth/authorizer/rbac/rbac.go#L122-L126

AuthorizerがAuthorizeClientBearerTokenで更にBearerToken用のAuthorizerを入れてラップされる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/config.go#L833-L864

WithAuthorizationnの流れ

おさらい

これで一通りデフォルトフィルタの流れが整理できた

bells17bells17

Admission Webhook関連

Admission Webhookがリクエストするサービスへのリゾルバー

デフォルトだとaggregatorapiserver.NewClusterIPServiceResolverというのが利用されてるぽい

https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L719-L721

組み込みのAdmission Controlについて

組み込みのAdmission Controlerを登録するのはこれっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/plugins.go#L105-L139
呼び出し側はこれっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/admission.go#L44-L64
更にそれを呼び出してるのはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/options/options.go#L105

つまりAPIServer初期化時にはすでに設定されていることになる

これがどのように利用されるかというと、ややこしいけどこんな感じになっていた

e.Storage.Storageの中身

各ストレージのe.Storage.Storageは以下のようにしてopts.Decoratorによって設定される
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go#L1394-L1403

このoptsはたどるとRESTOptionsGetterが渡されているよう(少なくともapiserverの場合は)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/apiextensions.go#L74

GetRESTOptions
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go#L249-L279

Decorator
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/storage_decorator.go#L39-L51

更にその中身
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go#L30-L40

newETCD3Storage
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go#L225-L258

最終的に生成されるやつ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L85-L106

ということで、このetcdのstoreオブジェクトが最終的にe.Storage.Storageの中身になるのだと思われる
(CRDのデータとかだとまた変わる?)

EnableWatchCacheというオプションがtrueだとetcdクライアントとの間にキャッシュ管理コンポーネントを挟んでキャッシュを利用する仕組みがあるよう(デフォルトだとfalseのよう)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go#L275

Admission Webhookを実行するAdmission Controller周りについて

validating/mutatint周りの登録はこれ?
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/plugins.go#L27-L32
呼び出し側はこれっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/admission.go#L71-L92

それがこっちのNewAdmissionOptionsで生成される
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/admission.go#L53

そして追加のプラグインが登録されたりする
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/admission.go#L54-L59

ちなみにクッソわかりづらいがここにデフォルトでONのプラグインが定義されている
(なぜ関数はデフォルトでOFFのプラグイン一覧を返すのに、その関数の中でデフォルトONのプラグインを定義しているのか...)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/plugins.go#L143-L161

ここにあるValidatingWebhook/MutatingWebhookのプラグインがそれぞれValidatingWebhook/MutatingWebhookを他のadmission controllerと一緒に実行するマンになっている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/options/plugins.go#L150-L151

Admission Webhook関連

ここでobjInfo.UpdatedObjectが実行されている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go#L524-L528

objInfo自体はここで生成されていてAdmission Webhookを行うためのapplyAdmissionも一緒に渡されている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go#L585

UpdatedObjectの中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go#L188-L211

ちなみにこれと一緒にobjInfoに埋め込まれてるapplyPatchは元々のobjectをパッチでアップデートする処理?
(PATCHメソッドの処理について見てるからこれがある感じ?)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go#L479-L514

おまけ

restfulUpdateResourceなどにadmitが渡されるというところまでは話をしたけど、このrestfulUpdateResourceがどのように実行されるかまとめてなかった

ここからの話し
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L764

このrestfulUpdateResourceなどがどのように実行されるかというとInstrumentRouteFuncの中身をみるとこんな感じになっている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go#L406-L432

ここでrouteFuncというrestfulUpdateResourceなどで生成されたrouterが最終的に呼ばれてリクエストが処理されている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go#L428

その後に呼ばれるMonitorRequestでリクエストのメトリクスが記録される
のでこのメトリクス取得のためにこのhandlerがラップしてるのだと思われる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go#L375-L404

そのハンドラーがこのパスとverbのときに処理するよっていうrouterを生成する
(例えばverbがPUTならws.PUT(path)のような形で生成するなど)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L768-L778

で最後にここで生成したrouterがまとめて登録される
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L992-L1000

bells17bells17

データ永続化周り

暗号化/復号化周り

このあたりの話し

https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

https://kubernetes.io/docs/tasks/administer-cluster/kms-provider/

こんな感じで複数のtransformerが利用できる

https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/transformer.go#L115-L209

これらのtransformerがgroup resourceごとに生成され、対象のリソースに対する暗号化/復号化が行われる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go#L199-L202

これがここでstorageFactoryにセットされる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/kubeapiserver/default_storage_factory_builder.go#L136-L144

storageFactoryはここらへんで利用されている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/cmd/kube-apiserver/app/server.go#L484-L500

storageFactoryはRESTOptionsGetterに埋め込まれる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go#L220

んでGetRESTOptionsが呼ばれた際に必要なStorageFactoryから必要なオブジェクトなどがstorageConfigに移植される
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go#L287-L293

実際に移植を行なっているのはStorageFactory.NewConfig(resource)の中でこれ
特にここでtransformerを取り出してる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/storage/storage_factory.go#L263-L268

このStorageConfigはここで使用される模様
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go#L1394-L1403

このDecoratorというのはこいつのよう
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go#L252

中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/storage_decorator.go#L39-L51

その中で呼ばれてるのはこいつで
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/registry/generic/storage_decorator.go#L53-L58

更に呼ばれてるのはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/vendor/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go

普通はetcd3が使われると思うのでこれが呼び出されるはず
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go#L225-L258

んでtransformerはここで使われている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go#L253-L257

最終的にetcd3のstoreオブジェクトのstore.transformerに格納される
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L85-L106

んでこいつがGet系の処理をするときは
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L134
のようにしてtransformer.TransformFromStorageでデコードされて

Create系の処理をするときは
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L161-L164
のようにしてtransformer.TransformToStorageでエンコードされるよう

暗号化/復号化にKMS Providerを利用する場合

KMSの時に生成されるtransformerはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go#L243-L250

envelopeServiceFactoryの中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go#L55-L89

この中のkmsClientを生成している
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go#L87

クライアントとの接続はgRPCで行うようなので、そのコネクション確立をクライアント生成時に行なっているみたい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go#L65-L81

envelopePrefixTransformerはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go#L367-L376

envelopeServiceのメソッドはここらへんで利用されている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go#L243-L250
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/envelope.go#L131

このDecrypt/Encryptはこれで呼び出しごとにkmsClientにリクエストを行なっている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go#L139-L163

このkmsClient自体はunix domain socketで接続するようなので、つまりgRPCのインターフェイスに沿ったkms serverをローカルで立てて、API ServerのkmsClientが通信するのはこのローカルで動いているkms serverということで、実際にKMS Providerと通信を行うのはこのkms serverになるんじゃなかろうか
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go#L74

ドキュメントにもそれっぽいことが書かれている(kms serverと読んでいたのはk8s的にはkms pluginというのが正しそう)
https://kubernetes.io/docs/tasks/administer-cluster/kms-provider/#implementing-a-kms-plugin

KMSのインターフェイスはこれになりそう
https://github.com/kubernetes/kubernetes/blob/v1.21.2/vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.proto

例えばAWSのKMS Plugin実装はこちらのよう
https://github.com/kubernetes-sigs/aws-encryption-provider

kms plugin(server)実装はこれっぽい
https://github.com/kubernetes-sigs/aws-encryption-provider/blob/master/cmd/server/main.go

でここらへんでAWS KMS側のEncrypt/Decryptを呼び出してる
https://github.com/kubernetes-sigs/aws-encryption-provider/blob/master/pkg/plugin/plugin.go#L183-L249

このAWS KMS側のEncrypt/Decryptを呼び出すsvcはAWS KMSのインターフェイスになっているので合ってそう
https://github.com/kubernetes-sigs/aws-encryption-provider/blob/master/pkg/plugin/plugin.go#L81

このsvcになる実態はこれ
https://github.com/kubernetes-sigs/aws-encryption-provider/blob/master/cmd/server/main.go#L71

このクライアントの中身はこれだけ
https://github.com/kubernetes-sigs/aws-encryption-provider/blob/master/pkg/cloud/cloud.go#L26-L55

watch周りの仕組み

ついでにwatch周りの仕組みを見ていく

ここでそれっぽいのが出てきてた
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go#L102

中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/vendor/k8s.io/apiserver/pkg/storage/etcd3/watcher.go

このwatcherはLIST処理のところで利用されている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L729

WATCH/WATCHLIST verbsあるようだけど、deprecatedとのことなので多分LIST処理のところだけ全部読めば良い気がする
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L907-L952

restfulListResourceはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L1189-L1193

handler.ListResourceはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/get.go#L169-L288

watch関連の処理はここらへんっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/get.go#L247-L273

rw.Watchの中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L110-L124

内部で実行されるwatchChan.Runはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L154-L186

実行されるgoroutineはこの2つ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L224-L272
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L274-L300

watchChan.startWatching

必要に応じて初期化処理
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L228-L234

ここでetcdライブラリのwatchを呼び出し
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L242

読んでるのはこれ(etcd側のライブラリなのでこれ以上深堀りしないでおく)
https://github.com/etcd-io/etcd/blob/main/client/v3/watch.go#L289-L378

etcd側のwatchのドキュメントはこれっぽい
https://etcd.io/docs/v3.3/learning/api/#watch-api

特に内部のgRPCのwatche streamを利用しているよう
https://etcd.io/docs/v3.3/learning/api/#watch-streams

ここで最終的にsendEventしてレスポンスを送ってるっぽい
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L257-L265

イベントは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のイベントを処理してる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L281-L292
こんな感じでwc.resultChanにイベントを送ってる

このresultChanはResultChan()で取得できるようになっている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go#L192-L194

続き

生成したwatcherをserveWatchに渡している
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/get.go#L263-L271

serveWatchを実行する関数を渡されているRecordLongRunningは最終的に関数をここで実行している
(RecordLongRunningは長時間実行のリクエスト情報を記録したりしてるだけっぽいので詳細はスキップ)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go#L372

serveWatchはこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L63-L139

最終的にはWatchServerオブジェクトを生成してServeHTTPを実行している
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L110-L138

ServeHTTPの中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L162-L262

websocketでのリクエストの場合はwebsocketで処理するっぽい
(これはdeprecatedになるやつ?)
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L171-L175

先程のResultChanはここで取得してる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L209

んでここでイベントを受け取って処理してる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L218-L259

最終的にここでイベントを処理してe.Encodeでレスポンスの書き込みしてる
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L240-L254

eはここで生成している
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L193

eにわたすframerはここで生成していてwオブジェクトを渡している
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L185

e.Encodeの中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go#L129-L137

これで最終的にクライアント側にWatchのイベントをユーザーに返すところまでたどり着いた

bells17bells17

kubectl exec/attach/log 周り

それっぽいのを見つけた
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/core/pod/rest/subresources.go

ここで使われている
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/core/pod/storage/storage.go#L105-L117

呼び出し元はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/core/rest/storage_core.go#L169-L174

ここでrestStorageMapを生成している
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/core/rest/storage_core.go#L270-L278

多分ここ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L953-L988

exec/attach/logなどのサブリソースは全てCONNECTメソッドを実装していてそれを呼び出している

proxyサブリソースは"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"メソッドがある
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/core/pod/rest/subresources.go#L47

それ以外では"GET", "POST"メソッドがある
https://github.com/kubernetes/kubernetes/blob/v1.21.2/pkg/registry/core/pod/rest/subresources.go#L80

method変数は特にhandler設定には使われてないので、HTTPメソッドによる動作差は無い?
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go#L963

handlerの中身はこれ
https://github.com/kubernetes/kubernetes/blob/v1.21.2/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go#L159-L211

ざっくり

  • オプションを生成
  • mutating admissionをadmit
  • validating admissionをadmit
  • connecter.Connectで生成したhandlerのhandler.ServeHTTPを実行