Kubernetes (K8s) クラスターで最も重要なコンポーネントの 1 つは、すべてのクラスター管理アクティビティのエントリ ポイントである API サーバーです。この記事から、K8s API サーバーのコードを詳細に分析し、アプリケーションのエントリ ポイント、フレームワーク、etcd との通信について説明します。 アプリケーションのエントリポイントK8s API サーバーのメイン エントリ ポイントは、cmd/kube-apiserver/apiserver.go ファイルにあります。 // cmd/kube-apiserver/apiserver.go
// apiserver はクラスターのメイン API サーバーおよびマスターです。 // クラスター管理 API を提供する役割を担います。 パッケージメイン
輸入( 「オス」
「k8s.io/コンポーネントベース/cli」 _ "k8s.io/component-base/logs/json/register" // JSON ログ形式の登録に使用 _ "k8s.io/component-base/metrics/prometheus/clientgo" // すべてのprometheus client-goプラグインをロードします _ "k8s.io/component-base/metrics/prometheus/version" // バージョンインジケーターの登録に使用 「k8s.io/kubernetes/cmd/kube-apiserver/app」 )
関数main (){ コマンド: = app 。新しいAPIサーバーコマンド() コード: = cli 。実行(コマンド) os 。終了(コード) } app.NewAPIServerCommand() は cobra コマンド オブジェクトを構築し、cli.Run はコマンドを実行するため、NewAPIServerCommand 関数が cobra.Command オブジェクトを構築する方法を直接確認できます。 // cmd/kube-apiserver/app/server.go
// NewAPIServerCommand はデフォルトのパラメータを持つ *cobra.Command オブジェクトを作成します func NewAPIServerCommand () *コブラ。指示{ // NewServerRunOptions はデフォルトのパラメータを使用して新しい ServerRunOptions オブジェクトを作成します。 // apiserver を実行するには ServerRunOption オブジェクトが必要です s : =オプション。新しいサーバー実行オプション() cmd : = &コブラ。指示{ 使用方法: "kube-apiserver" 、 Long : ` Kubernetes APIサーバーはデータを検証し、構成します ポッド、サービス、レプリケーションコントローラ、および その他。 APIサーバーはREST操作を提供し、 他のすべてのコンポーネントが相互作用するクラスターの共有状態。`,
// ...... RunE : func ( cmd * cobra .コマンド, args []文字列)エラー{ verflag.PrintAndExitIfRequested ( ) fs : = cmdです。フラグ()
err : = sの場合。ログ。検証して適用します();エラー!=ゼロ{ エラーを返す } クリフラグ。印刷フラグ( fs )
エラー: = checkNonZeroInsecurePort ( fs ) err != nil の場合{ エラーを返す } // デフォルトオプションを設定する 完了オプション、エラー: =完了( s ) err != nil の場合{ エラーを返す } // オプションを確認する エラーの場合: = completedOptions 。検証します(); len (エラー) != 0 { utilerrors.NewAggregate ( errs )を返す } Run ( completedOptions 、 genericapiserver.SetupSignalHandler ())を返します。 }, }
// ......
戻りコマンド } この関数の中心的な機能は、Complete(s) 関数を使用して、apiserver の起動に必要なデフォルトのパラメータを生成し、起動のためにそのデフォルトのパラメータを Run 関数に渡すことです。 // cmd/kube-apiserver/app/server.go // 実行は指定された APIServer を実行し、終了できません。 func Run ( completeOptionscompletedServerRunOptions , stopCh <- chan struct {} ) error {
// サービス チェーンを作成する (3 つのサーバー コンポーネントを含む) サーバー、 err : = CreateServerChain ( completeOptions 、 stopCh )
// ヘルスチェック、生存チェック、OpenAPI ルート登録など、サービスを開始する前の準備。 準備完了、エラー: =サーバー。準備実行()
// 正式に実行を開始する 返却準備完了。実行( stopCh ) } Run 関数では、CreateServerChain 関数を使用して、委任を通じて接続された APIServer オブジェクトを作成します。 // cmd/kube-apiserver/app/server.go
// CreateServerChain は委任を通じて接続された APIServer を作成します func CreateServerChain ( completedOptionscompletedServerRunOptions , stopCh <- chan struct { } ) ( * aggregatorapiserver.APIAggregator , error ) { // CreateKubeAPIServerConfig は、APIServer を実行するためのすべての構成リソースを作成しますが、リソースは実行しません。 kubeAPIServerConfig 、 serviceResolver 、 pluginInitializer 、 err : = CreateKubeAPIServerConfig ( completeOptions ) を作成します。
// // APIExtensionsServer 構成を作成する apiExtensionsConfig 、 err : = createAPIExtensionsConfig ( * kubeAPIServerConfig.GenericConfig 、 kubeAPIServerConfig.ExtraConfig.VersionedInformers 、 pluginInitializer 、 completedOptions.ServerRunOptions 、 completedOptions.MasterCount 、 serviceResolver 、 webhook 。 NewDefaultAuthenticationInfoResolverWrapper ( kubeAPIServerConfig.ExtraConfig.ProxyTransport 、 kubeAPIServerConfig.GenericConfig.EgressSelector 、 kubeAPIServerConfig.GenericConfig.LoopbackClientConfig 、 kubeAPIServerConfig.GenericConfig.TracerProvider ) )
// APIExtensionsServerを作成し、ルートを登録する apiExtensionsServer 、 err : = createAPIExtensionsServer ( apiExtensionsConfig 、 genericapiserver.NewEmptyDelegateWithCustomHandler ( notFoundHandler ) )
// KubeAPIServerを作成し、ルートを登録する kubeAPIServer 、エラー: = CreateKubeAPIServer ( kubeAPIServerConfig 、 apiExtensionsServer。GenericAPIServer )
// // aggregatorServer 構成を作成する aggregatorConfig 、 err : = createAggregatorConfig ( * kubeAPIServerConfig.GenericConfig 、 completedOptions.ServerRunOptions 、 kubeAPIServerConfig.ExtraConfig.VersionedInformers 、 serviceResolver 、 kubeAPIServerConfig.ExtraConfig.ProxyTransport 、 pluginInitializer )
// aggregatorServer を作成し、ルートを登録する aggregatorServer 、 err : = createAggregatorServer ( aggregatorConfig 、 kubeAPIServer.GenericAPIServer 、 apiExtensionsServer.Informers )
aggregatorServerを返す、 nil } 上記の関数では、CreateServerChain によって APIExtensionServer、KubeAPIServer、AggregatorServer の 3 つのサーバーが作成されることがわかります。 APIServer は、さまざまな種類のリクエストを処理するために、次の 3 つのコンポーネントに依存しています。 - APIExtensionServer: 主に CustomResourceDefinition (CRD) リクエストの処理を担当します。
- KubeAPIServer: 一般的な処理、認証、承認に加えて、K8s 組み込みリソースに対するリクエストの処理を主に担当します。
- AggregatorServer: 主にアグリゲーター処理を担当し、集約された K8s サービスにリクエストを転送するプロキシ サーバーとして機能します。
各サーバーには対応する構成があります。上記の関数の apiExtensionServer と aggregatorServer の構成は kubeAPIServerConfig に依存する必要があり、これらの ServerConfig はすべて GenericConfig に依存する必要があることがわかります。 CreateKubeAPIServerConfig 関数は kubeAPIServerConfig を作成します。この関数では、次のコードに示すように、buildGenericConfig を呼び出すことによって GenericConfig オブジェクトが作成されます。 // cmd/kube-apiserver/app/server.go // CreateKubeAPIServerConfig は、APIServer を実行するためのすべての構成リソースを作成します。 func CreateKubeAPIServerConfig ( s完了したServerRunOptions ) ( *コントロールプレーン.Config 、 aggregatorapiserver.ServiceResolver 、 []入場料。プラグイン初期化子、 エラー、 ){ proxyTransport : = CreateProxyTransport () // 共通構成を構築する genericConfig 、 versionedInformers 、 serviceResolver 、 pluginInitializers 、 admissionPostStartHook 、 storageFactory 、 err : = buildGenericConfig ( s.ServerRunOptions 、 proxyTransport )
// ......
config : = &コントロールプレーン.設定{ 汎用設定:汎用設定、 追加構成:コントロールプレーン。追加構成{ APIリソース構成ソース:ストレージファクトリー。 APIリソース構成ソース、 ストレージファクトリー:ストレージファクトリー、 イベントTTL : s 。イベントTTL 、 KubeletClientConfig : s 。 KubeletConfig 、 EnableLogsSupport : s 。 EnableLogsHandler 、 プロキシトランスポート:プロキシトランスポート、
サービスIP範囲: s 。プライマリサービスクラスタIP範囲、 APIServerServiceIP : s.APIServerServiceIP 、 セカンダリサービスIP範囲: s 。セカンダリサービスクラスタIP範囲、
APIサーバーサービスポート: 443 、
ServiceNodePortRange : s 。サービスノードポート範囲、 KubernetesServiceNodePort : s 。 KubernetesServiceNodePort 、
EndpointReconcilerType :リコンサイラー。タイプ( s . EndpointReconcilerType )、 マスターカウント: s 。マスターカウント、
サービスアカウント発行者: s 。サービスアカウント発行者、 ServiceAccountMaxExpiration : s 。サービスアカウントトークン最大有効期限、 有効期限を延長: s 。認証。サービス アカウント。有効期限を延長、
バージョン管理された情報提供者:バージョン管理された情報提供者、
IdentityLeaseDurationSeconds : s 。アイデンティティリース期間秒数、 IdentityLeaseRenewIntervalSeconds : s 。アイデンティティリース更新間隔秒数、 }, }
// ......
config 、 serviceResolver 、 pluginInitializers 、 nil を返します。 }
関数buildGenericConfig ( s *オプション。サーバー実行オプション、 プロキシトランスポート* http 。輸送、 )(...){ // 一般的な構成オブジェクトを作成する genericConfig = genericapiserver 。 NewConfig (レガシースキーム.コーデック)
// ......
//認証インスタンスを作成する lastErr = sの場合。認証。 ApplyTo ( & genericConfig.Authentication 、 genericConfig.SecureServing 、 genericConfig.EgressSelector 、 genericConfig.OpenAPIConfig 、 clientgoExternalClient 、 versionedInformers ) ; lastErr != nil { 戻る }
// ... // openapi/swagger 構成、OpenAPIConfig は OpenAPI 仕様を生成するために使用されます getOpenAPIDefinitions : = openapi です。 GetOpenAPIDefinitionsWithoutDisabledFeatures (生成されたopenapi 。 GetOpenAPIDefinitions ) 一般的な設定。 OpenAPIConfig = genericapiserver 。 DefaultOpenAPIConfig ( getOpenAPIDefinitions 、 openapinamer.NewDefinitionNamer ( legacyscheme.Scheme 、 extensionsapiserver.Scheme 、 aggregatorscheme.Scheme )) 一般的な設定。 OpenAPIConfig 。情報。タイトル= 「Kubernetes」 一般的な設定。 LongRunningFunc =フィルター。基本的な長時間実行リクエストチェック ( セットします。 NewString ( "watch" 、 "proxy" )、 セットします。 NewString ( "attach" 、 "exec" 、 "proxy" 、 "log" 、 "portforward" )、 )
// storageFactoryConfig オブジェクトは、etcd 認証、アドレス、ストレージ プレフィックスなど、kube-apiserver が etcd と対話する方法を定義します。 // このオブジェクトは、リソース情報、リソース エンコーディング情報、リソース ステータスなどのリソースの保存方法も定義します。 storageFactoryConfig : = kubeapiserver 。新しいストレージファクトリー構成() ストレージファクトリ構成。 APIリソース構成= genericConfig 。マージされたリソース構成 StorageFactoryConfig が完了しました。エラー: = storageFactoryConfigです。完了( s . Etcd )
storageFactory 、 lastErr =完了したStorageFactoryConfig 。新しい()
lastErr = sの場合。などなど。ストレージファクトリーにストレージファクトリーを適用します。 lastErr != nil { 戻る }
// ......
// SharedInformerFactory を初期化する kubeClientConfig : = genericConfig です。ループバッククライアント構成 clientgoExternalClient 、エラー: = clientgoclientset 。新しいConfig ( kubeClientConfig ) versionedInformers = clientgoinformers です。 NewSharedInformerFactory ( clientgoExternalClient 、 10 *時間.分)
// 認証構成。内部的に authenticatorConfig.New() を呼び出します。 // K8s は 9 つの認証メカニズムを提供しており、それぞれがインスタンス化されると認証子になります。 lastErr = sの場合。認証。 ApplyTo ( & genericConfig.Authentication 、 genericConfig.SecureServing 、 genericConfig.EgressSelector 、 genericConfig.OpenAPIConfig 、 clientgoExternalClient 、 versionedInformers ) ; lastErr != nil { 戻る }
// 認証インスタンスを作成します。 K8s は 6 つの認証メカニズムも提供します。各承認メカニズムは、インスタンス化された後に承認者になります。 一般的な設定。承認。承認者、 genericConfig 。 RuleResolver 、 err = BuildAuthorizer ( s 、 genericConfig . EgressSelector 、 versionedInformers )
// ... //監査 lastErr = sです。監査。適用先( genericConfig )
// アドミッションコントローラ // k8s リソースは、認証と承認が通過した後、etcd に永続化される前に、アドミッション コントロール ロジックに入ります。 // アドミッション制御には、要求されたリソースに対するカスタム操作(検証、変更、拒否)が含まれます。 // アドミッションコントローラは、プラグインデータ構造を通じて一様に登録、保存、管理されます admissionConfig : = & kubeapiserveradmission 。設定{ 外部情報提供者:バージョン管理情報提供者、 LoopbackClientConfig : genericConfig 。ループバッククライアント構成、 CloudConfigFile : s 。クラウドプロバイダー。 CloudConfigFile 、 } serviceResolver = buildServiceResolver ( s.EnableAggregatorRouting 、 genericConfig.LoopbackClientConfig.Host 、 versionedInformers ) pluginInitializers 、 admissionPostStartHook 、 err = admissionConfig 。新規( proxyTransport 、 genericConfig . EgressSelector 、 serviceResolver 、 genericConfig . TracerProvider )
エラー= s 。入場料。適用先( ジェネリック構成、 バージョン化された情報提供者、 kubeClientConfig 、 機能.DefaultFeatureGate 、 プラグイン初期化子...)
// ...
} それでは、これら 3 つのサーバーがどのように構築されているかを見てみましょう。 go-restfulフレームワークここでは、APIServer がこのフレームワークを使用するため、まず go-restful フレームワークを理解する必要があります。次のコードは go-restful の公式例です。このデモを理解すると、基本的に go-restful フレームワークの使い方がわかります。 パッケージメイン
輸入( "ログ" 「ネット/http」
RESTfulスペック「github.com/emicklei/go-restful-openapi/v2」 レストフル「github.com/emicklei/go-restful/v3」 「github.com/go-openapi/spec」 )
// UserResource はユーザードメインへの REST レイヤーです タイプUserResource構造体{ // 通常はDAO(データアクセスオブジェクト)を使用します ユーザーマップ[文字列]ユーザー }
// WebService は、ユーザー リソースに対する REST 要求を処理できる新しいサービスを作成します。 func ( u UserResource ) WebService () * restful 。 Webサービス{ ws : = new ( restful.WebService )を追加します。 ws 。 パス( "/users" )。 ( restful . MIME_XML 、 restful . MIME_JSON )を消費します。 ( restful . MIME_JSON 、 restful . MIME_XML )を生成します// ルートごとにこれを指定することもできます
tags : = []文字列{ "users" }
ws 。ルート( ws . GET ( "/" )。To ( u . findAllUsers )。 // ドキュメント Doc ( 「すべてのユーザーを取得」 )。 メタデータ( restfulspec . KeyOpenAPITags 、 tags )。 書き込みます([]ユーザー{})。 ( 200 、 "OK" 、[]ユーザー{})を返します。
ws 。ルート( ws . GET ( "/{user-id}" )。To ( u . findUser )。 // ドキュメント Doc ( 「ユーザーを取得する」 )。 Param ( ws . PathParameter ( "user-id" , "ユーザーの識別子" ). DataType ( "integer" ). DefaultValue ( "1" )). メタデータ( restfulspec . KeyOpenAPITags 、 tags )。 書き込みます(ユーザー{})。 // レスポンスで ( 200 、 "OK" 、 User {} )を返します。 ( 404 、 "見つかりません" 、 nil )を返します)
ws 。ルート( ws . PUT ( "/{user-id}" )。To ( u . updateUser )。 // ドキュメント ドキュメント( 「ユーザーの更新」 )。 Param ( ws . PathParameter ( "user-id" , "ユーザーの識別子" ). DataType ( "string" )). メタデータ( restfulspec . KeyOpenAPITags 、 tags )。 リクエストから( User {})) //を読み取ります
ws 。ルート( ws . PUT ( "" ). To ( u . createUser ). // ドキュメント Doc ( 「ユーザーを作成する」 )。 メタデータ( restfulspec . KeyOpenAPITags 、 tags )。 リクエストから( User {})) //を読み取ります
ws 。ルート( ws . DELETE ( "/{user-id}" )。To ( u . removeUser )。 // ドキュメント Doc ( 「ユーザーを削除する」 )。 メタデータ( restfulspec . KeyOpenAPITags 、 tags )。 Param ( ws . PathParameter ( "user-id" , "ユーザーの識別子" ). DataType ( "string" )))
WSを返す }
// http://localhost:8080/users を取得します // func ( u UserResource ) findAllUsers ( request * restful . Request 、 response * restful . Response ) { リスト: = []ユーザー{} _について、それぞれ: =範囲u 。ユーザー{ list = append (リスト、各) } response.WriteEntity (リスト) }
// http://localhost:8080/users/1 を取得します // func ( u UserResource ) findUser ( request * restful.Request , response * restful.Response ) { id : =リクエスト。 PathParameter ( "ユーザーID" ) usr : = uです。ユーザー[ ID ] len ( usr.ID ) == 0の場合{ 応答。 WriteErrorString ( http . StatusNotFound 、 「ユーザーが見つかりませんでした。」 ) }それ以外{ レスポンス.WriteEntity (usr ) } }
// http://localhost:8080/users/1 を配置します // <User><Id>1</Id><Name>メリッサ ラズベリー</Name></User> // func ( u * UserResource ) updateUser ( request * restful . Request 、 response * restful . Response ) { usr : = new (ユーザー) エラー: =リクエスト。エンティティの読み取り( & usr ) エラー== nilの場合{ あなた。ユーザー[ usr . ID ] = * usr レスポンス.WriteEntity (usr ) }それ以外{ 応答。書き込みエラー( http . StatusInternalServerError 、 err ) } }
// http://localhost:8080/users/1 を配置します // <User><Id>1</Id><Name>メリッサ</Name></User> // func ( u * UserResource ) createUser ( request * restful . Request 、 response * restful . Response ) { usr : =ユーザー{ ID :リクエスト. PathParameter ( "ユーザーID" ) } エラー: =リクエスト。エンティティの読み取り( & usr ) エラー== nilの場合{ あなた。ユーザー[ usr . ID ] = usr 応答。 WriteHeaderAndEntity ( http . StatusCreated 、 usr ) }それ以外{ 応答。書き込みエラー( http . StatusInternalServerError 、 err ) } }
// http://localhost:8080/users/1 を削除 // func ( u * UserResource ) removeUser ( request * restful.Request , response * restful.Response ) { id : =リクエスト。 PathParameter ( "ユーザーID" ) 削除( u .ユーザー、 id ) }
関数main (){ u : = UserResource { map [ string ] User {}} 安らかな。デフォルトコンテナ。 ( u.WebService ())を追加します
config : = restfulspec です。設定{ Web サービス: RESTful 。 RegisteredWebServices (), // 表示されるサービスを制御します APIパス: "/apidocs.json" 、 PostBuildSwaggerObjectHandler :強化SwaggerObject } 安らかな。デフォルトコンテナ。 ( restfulspec.NewOpenAPIService ( config ))を追加します。
// オプションとして、REST API に優れた Web UI を提供する Swagger サービスをインストールできます。 // Swagger HTML5 アセットをダウンロードし、以下の構成で FilePath の場所を変更する必要があります。 // http://localhost:8080/apidocs/?url=http://localhost:8080/apidocs.json を開く http://www.youtube.com/watch?v=vUyQyYyxcハンドル( "/apidocs/" 、 http . StripPrefix ( "/apidocs/" 、 http . FileServer ( http . Dir ( "/Users/emicklei/Projects/swagger-ui/dist" ))))
ログ。 Printf ( "localhost:8080 でリッスンを開始します" ) ログ。致命的( http .ListenAndServe ( ":8080" 、 nil )) }
関数enrichSwaggerObject ( swo * spec.Swagger ){ スウォ。情報= &仕様.情報{ InfoProps :仕様。情報プロパティ{ タイトル: 「UserService」 、 説明: 「ユーザーを管理するためのリソース」 、 お問い合わせ先: & spec .連絡先情報{ ContactInfoProps :仕様。連絡先情報プロパティ{ 名前: 「ジョン」 、 メールアドレス: "[email protected]" 、 URL : "http://johndoe.org" 、 }, }, ライセンス: & spec .ライセンス{ ライセンスプロパティ: spec 。ライセンスプロパティ{ 名前: 「MIT」 、 URL : "http://mit.org" 、 }, }, バージョン: "1.0.0" 、 }, } スウォ。タグ= [] spec 。タグ{ spec .タグ{ TagProps : spec .タグプロパティ{ 名前: 「ユーザー」 、 説明: 「ユーザーの管理」 }}} }
// ユーザーは単なるサンプルタイプです タイプUser構造体{ ID文字列`json : "id"説明: "ユーザーの識別子" ` 名前文字列`json : "name"説明: "ユーザー名"デフォルト: "john" ` 年齢int `json : "年齢"説明: "ユーザーの年齢"デフォルト: "21" ` } このサンプル コードでは、go-restful のコア機能を使用して、ユーザーの追加、削除、クエリ、変更を実装するシンプルな RESTful API を実装します。コンテナ、Web サービス、ルートといういくつかのコア概念があります。 - コンテナー: 複数の Web サービスと http.ServerMux を含むサーバー コンテナー。
- WebService: サービスは複数のルートから構成されます。 WebService は実際には、上記の例の /users など、特定のオブジェクトに関連するサービスを表します。 /users の RESTful API を実装するには、追加、削除、確認、変更を行うルート (Route のコレクション) を追加する必要があります。
- ルート: URL、http メソッド、受信および応答メディア タイプ、処理関数を含むルーティング。各ルートは、メソッドとパスに従って対応するメソッドにマッピングされます。これは、メソッド/パスから関数へのマッピング関係を抽象化したものです。たとえば、上記の例の ws.Route(ws.GET("/{user-id}").To(u.findUser)) は、パス /users/{user-id} に対する GET リクエストであり、findUser メソッドにルーティングされて処理されます。
- コンテナは Web サービスのコレクションです。コンテナに複数の Web サービスを追加できます。コンテナーは ServeHTTP() メソッドを実装しているため、本質的には http ハンドラーであり、http サーバーで直接使用できます。
Kubernetes での go-restful の使用は比較的基本的なものであり、最も基本的なルーティング機能が使用されます。 K8s には、CRD などのカスタム リソース オブジェクトを含む多くの組み込みリソース オブジェクトがあるため、これらのリソースに対応するインターフェースは最初から直接ハードコードされているのではなく、一連のコードを通じて動的に登録されます。したがって、次の分析は、APIServer が次のルーティング処理を提供できるようにする方法を見つけることです。 GET / apis / apps / v1 / namespaces / { namespace } /デプロイメント/ { name } POST / apis / apps / v1 / namespaces / { namespace } /デプロイメント
GET / apis / apps / v1 / namespaces / { namespace } / daemonsets / { name } POST / apis / apps / v1 /名前空間/ {名前空間} /デーモンセット go-restful の基本を理解した後、これら 3 つのサーバーがどのようにインスタンス化されるかを学習できます。 |