1 - カスタムリソース

カスタムリソース はKubernetes APIの拡張です。このページでは、いつKubernetesのクラスターにカスタムリソースを追加するべきなのか、そしていつスタンドアローンのサービスを利用するべきなのかを議論します。カスタムリソースを追加する2つの方法と、それらの選択方法について説明します。

カスタムリソース

リソース は、Kubernetes APIのエンドポイントで、特定のAPIオブジェクトのコレクションを保持します。例えば、ビルトインの Pods リソースは、Podオブジェクトのコレクションを包含しています。

カスタムリソース は、Kubernetes APIの拡張で、デフォルトのKubernetesインストールでは、必ずしも利用できるとは限りません。つまりそれは、特定のKubernetesインストールのカスタマイズを表します。しかし、今現在、多数のKubernetesのコア機能は、カスタムリソースを用いて作られており、Kubernetesをモジュール化しています。

カスタムリソースは、稼働しているクラスターに動的に登録され、現れたり、消えたりし、クラスター管理者はクラスター自体とは無関係にカスタムリソースを更新できます。一度、カスタムリソースがインストールされると、ユーザーはkubectlを使い、ビルトインのリソースである Pods と同じように、オブジェクトを作成、アクセスすることが可能です。

カスタムコントローラー

カスタムリソースそれ自身は、単純に構造化データを格納、取り出す機能を提供します。カスタムリソースを カスタムコントローラー と組み合わせることで、カスタムリソースは真の 宣言的API を提供します。

宣言的APIは、リソースのあるべき状態を 宣言 または指定することを可能にし、Kubernetesオブジェクトの現在の状態を、あるべき状態に同期し続けるように動きます。 コントローラーは、構造化データをユーザーが指定したあるべき状態と解釈し、その状態を管理し続けます。

稼働しているクラスターのライフサイクルとは無関係に、カスタムコントローラーをデプロイ、更新することが可能です。カスタムコントローラーはあらゆるリソースと連携できますが、カスタムリソースと組み合わせると特に効果を発揮します。オペレーターパターンは、カスタムリソースとカスタムコントローラーの組み合わせです。カスタムコントローラーにより、特定アプリケーションのドメイン知識を、Kubernetes APIの拡張に変換することができます。

カスタムリソースをクラスターに追加するべきか?

新しいAPIを作る場合、APIをKubernetesクラスターAPIにアグリゲート(集約)するか、もしくはAPIをスタンドアローンで動かすかを検討します。

APIアグリゲーションを使う場合:スタンドアローンAPIを使う場合:
APIが宣言的APIが宣言的モデルに適さない
新しいリソースをkubectlを使い読み込み、書き込みしたいkubectlのサポートは必要ない
新しいリソースをダッシュボードのような、Kubernetes UIで他のビルトインリソースと同じように管理したいKubernetes UIのサポートは必要ない
新しいAPIを開発しているAPIを提供し、適切に機能するプログラムが既に存在している
APIグループ、名前空間というような、RESTリソースパスに割り当てられた、Kubernetesのフォーマット仕様の制限を許容できる(API概要を参照)既に定義済みのREST APIと互換性を持っていなければならない
リソースはクラスターごとか、クラスター内の名前空間に自然に分けることができるクラスター、または名前空間による分割がリソース管理に適さない。特定のリソースパスに基づいて管理したい
Kubernetes APIサポート機能を再利用したいこれらの機能は必要ない

宣言的API

宣言的APIは、通常、下記に該当します:

  • APIは、比較的少数の、比較的小さなオブジェクト(リソース)で構成されている
  • オブジェクトは、アプリケーションの設定、インフラストラクチャーを定義する
  • オブジェクトは、比較的更新頻度が低い
  • 人は、オブジェクトの情報をよく読み書きする
  • オブジェクトに対する主要な手続きは、CRUD(作成、読み込み、更新、削除)になる
  • 複数オブジェクトをまたいだトランザクションは必要ない: APIは今現在の状態ではなく、あるべき状態を表現する

命令的APIは、宣言的ではありません。 APIが宣言的ではない兆候として、次のものがあります:

  • クライアントから"これを実行"と命令がきて、完了の返答を同期的に受け取る
  • クライアントから"これを実行"と命令がきて、処理IDを取得する。そして処理が完了したかどうかを、処理IDを利用して別途問い合わせる
  • リモートプロシージャコール(RPC)という言葉が飛び交っている
  • 直接、大量のデータを格納している(例、1オブジェクトあたり数kBより大きい、または数千オブジェクトより多い)
  • 高帯域アクセス(持続的に毎秒数十リクエスト)が必要
  • エンドユーザーのデータ(画像、PII、その他)を格納している、またはアプリケーションが処理する大量のデータを格納している
  • オブジェクトに対する処理が、CRUDではない
  • APIをオブジェクトとして簡単に表現できない
  • 停止している処理を処理ID、もしくは処理オブジェクトで表現することを選択している

ConfigMapとカスタムリソースのどちらを使うべきか?

下記のいずれかに該当する場合は、ConfigMapを使ってください:

  • mysql.cnfpom.xmlのような、十分に文書化された設定ファイルフォーマットが既に存在している
  • 単一キーのConfigMapに、設定ファイルの内容の全てを格納している
  • 設定ファイルの主な用途は、クラスター上のPodで実行されているプログラムがファイルを読み込み、それ自体を構成することである
  • ファイルの利用者は、Kubernetes APIよりも、Pod内のファイルまたはPod内の環境変数を介して利用することを好む
  • ファイルが更新されたときに、Deploymentなどを介してローリングアップデートを行いたい

下記のほとんどに該当する場合、カスタムリソース(CRD、またはアグリゲートAPI)を使ってください:

  • 新しいリソースを作成、更新するために、Kubernetesのクライアントライブラリー、CLIを使いたい
  • kubectlのトップレベルサポートが欲しい(例、kubectl get my-object object-name)
  • 新しい自動化の仕組みを作り、新しいオブジェクトの更新をウォッチしたい、その更新を契機に他のオブジェクトのCRUDを実行したい、またはその逆を行いたい
  • オブジェクトの更新を取り扱う、自動化の仕組みを書きたい
  • .spec.status.metadataというような、Kubernetes APIの慣習を使いたい
  • オブジェクトは、制御されたリソースコレクションの抽象化、または他のリソースのサマリーとしたい

カスタムリソースを追加する

Kubernetesは、クラスターへカスタムリソースを追加する2つの方法を提供しています:

  • CRDはシンプルで、プログラミングなしに作成可能
  • APIアグリゲーションは、プログラミングが必要だが、データがどのように格納され、APIバージョン間でどのように変換されるかというような、より詳細なAPIの振る舞いを制御できる

Kubernetesは、さまざまなユーザーのニーズを満たすためにこれら2つのオプションを提供しており、使いやすさや柔軟性が損なわれることはありません。

アグリゲートAPIは、プロキシとして機能するプライマリAPIサーバーの背後にある、下位のAPIServerです。このような配置はAPIアグリゲーション(AA)と呼ばれています。ユーザーにとっては、単にAPIサーバーが拡張されているように見えます。

CRDでは、APIサーバーの追加なしに、ユーザーが新しい種類のリソースを作成できます。CRDを使うには、APIアグリゲーションを理解する必要はありません。

どのようにインストールされたかに関わらず、新しいリソースはカスタムリソースとして参照され、ビルトインのKubernetesリソース(Podなど)とは区別されます。

CustomResourceDefinition

CustomResourceDefinitionAPIリソースは、カスタムリソースを定義します。CRDオブジェクトを定義することで、指定した名前、スキーマで新しいカスタムリソースが作成されます。Kubernetes APIは、作成したカスタムリソースのストレージを提供、および処理します。 CRDオブジェクトの名前はDNSサブドメイン名に従わなければなりません。

これはカスタムリソースを処理するために、独自のAPIサーバーを書くことから解放してくれますが、一般的な性質としてAPIサーバーアグリゲーションと比べると、柔軟性に欠けます。

新しいカスタムリソースをどのように登録するか、新しいリソースタイプとの連携、そしてコントローラーを使いイベントを処理する方法例について、カスタムコントローラー例を参照してください。

APIサーバーアグリゲーション

通常、Kubernetes APIの各リソースは、RESTリクエストとオブジェクトの永続的なストレージを管理するためのコードが必要です。メインのKubernetes APIサーバーは PodService のようなビルトインのリソースを処理し、またカスタムリソースもCRDを通じて同じように管理することができます。

アグリゲーションレイヤーは、独自のAPIサーバーを書き、デプロイすることで、カスタムリソースに特化した実装の提供を可能にします。メインのAPIサーバーが、処理したいカスタムリソースへのリクエストを独自のAPIサーバーに委譲することで、他のクライアントからも利用できるようにします。

カスタムリソースの追加方法を選択する

CRDは簡単に使えます。アグリゲートAPIはより柔軟です。ニーズに最も合う方法を選択してください。

通常、CRDは下記の場合に適しています:

  • 少数のフィールドしか必要ない
  • そのリソースは社内のみで利用している、または小さいオープンソースプロジェクトの一部で利用している(商用プロダクトではない)

使いやすさの比較

CRDは、アグリゲートAPIと比べ、簡単に作れます。

CRDアグリゲートAPI
プログラミングが不要で、ユーザーはCRDコントローラーとしてどの言語でも選択可能Go言語でプログラミングし、バイナリとイメージの作成が必要
追加のサービスは不要。CRDはAPIサーバーで処理される追加のサービス作成が必要で、障害が発生する可能性がある
CRDが作成されると、継続的なサポートは無い。バグ修正は通常のKubernetesマスターのアップグレードで行われる定期的にアップストリームからバグ修正の取り込み、リビルド、そしてアグリゲートAPIサーバーの更新が必要かもしれない
複数バージョンのAPI管理は不要。例えば、あるリソースを操作するクライアントを管理していた場合、APIのアップグレードと一緒に更新される複数バージョンのAPIを管理しなければならない。例えば、世界中に共有されている拡張機能を開発している場合

高度な機能、柔軟性

アグリゲートAPIは、例えばストレージレイヤーのカスタマイズのような、より高度なAPI機能と他の機能のカスタマイズを可能にします。

機能詳細CRDアグリゲートAPI
バリデーションエラーを予防し、クライアントと無関係にAPIを発達させることができるようになる。これらの機能は多数のクライアントがおり、同時に全てを更新できないときに最も効果を発揮するはい、ほとんどのバリデーションはOpenAPI v3.0 validationで、CRDに指定できる。その他のバリデーションはWebhookのバリデーションによりサポートされているはい、任意のバリデーションが可能
デフォルト設定上記を参照はい、OpenAPI v3.0 validationdefaultキーワード(1.17でGA)、またはMutating Webhookを通じて可能 (ただし、この方法は古いオブジェクトをetcdから読み込む場合には動きません)はい
複数バージョニング同じオブジェクトを、違うAPIバージョンで利用可能にする。フィールドの名前を変更するなどのAPIの変更を簡単に行うのに役立つ。クライアントのバージョンを管理する場合、重要性は下がるはいはい
カスタムストレージ異なる性能のストレージが必要な場合(例えば、キーバリューストアの代わりに時系列データベース)または、セキュリティの分離(例えば、機密情報の暗号化、その他)いいえはい
カスタムビジネスロジックオブジェクトが作成、読み込み、更新、また削除されるときに任意のチェック、アクションを実行するはい、Webhooksを利用はい
サブリソースのスケールHorizontalPodAutoscalerやPodDisruptionBudgetなどのシステムが、新しいリソースと連携できるようにするはいはい
サブリソースの状態ユーザーがspecセクションに書き込み、コントローラーがstatusセクションに書き込む際に、より詳細なアクセスコントロールができるようにする。カスタムリソースのデータ変換時にオブジェクトの世代を上げられるようにする(リソース内のspecとstatusでセクションが分離している必要がある)はいはい
その他のサブリソース"logs"や"exec"のような、CRUD以外の処理の追加いいえはい
strategic-merge-patchContent-Type: application/strategic-merge-patch+jsonで、PATCHをサポートする新しいエンドポイント。ローカル、サーバー、どちらでも更新されうるオブジェクトに有用。さらなる情報は"APIオブジェクトをkubectl patchで決まった場所で更新"を参照いいえはい
プロトコルバッファプロトコルバッファを使用するクライアントをサポートする新しいリソースいいえはい
OpenAPIスキーマサーバーから動的に取得できる型のOpenAPI(Swagger)スキーマはあるか、許可されたフィールドのみが設定されるようにすることで、ユーザーはフィールド名のスペルミスから保護されているか、型は強制されているか(言い換えると、「文字列」フィールドに「int」を入れさせない)はい、OpenAPI v3.0 validation スキーマがベース(1.16でGA)はい

一般的な機能

CRD、またはアグリゲートAPI、どちらを使ってカスタムリソースを作った場合でも、Kubernetesプラットフォーム外でAPIを実装するのに比べ、多数の機能が提供されます:

機能何を実現するか
CRUD新しいエンドポイントが、HTTP、kubectlを通じて、基本的なCRUD処理をサポート
Watch新しいエンドポイントが、HTTPを通じて、KubernetesのWatch処理をサポート
Discoverykubectlやダッシュボードのようなクライアントが、自動的にリソースの一覧表示、個別表示、フィールドの編集処理を提供
json-patch新しいエンドポイントがContent-Type: application/json-patch+jsonを用いたPATCHをサポート
merge-patch新しいエンドポイントがContent-Type: application/merge-patch+jsonを用いたPATCHをサポート
HTTPS新しいエンドポイントがHTTPSを利用
ビルトイン認証拡張機能へのアクセスに認証のため、コアAPIサーバー(アグリゲーションレイヤー)を利用
ビルトイン認可拡張機能へのアクセスにコアAPIサーバーで使われている認可メカニズムを再利用(例、RBAC)
ファイナライザー外部リソースの削除が終わるまで、拡張リソースの削除をブロック
Admission Webhooks拡張リソースの作成/更新/削除処理時に、デフォルト値の設定、バリデーションを実施
UI/CLI 表示kubectl、ダッシュボードで拡張リソースを表示
未設定 対 空設定クライアントは、フィールドの未設定とゼロ値を区別することができる
クライアントライブラリーの生成Kubernetesは、一般的なクライアントライブラリーと、タイプ固有のクライアントライブラリーを生成するツールを提供
ラベルとアノテーションツールがコアリソースとカスタムリソースの編集方法を知っているオブジェクト間で、共通のメタデータを提供

カスタムリソースのインストール準備

クラスターにカスタムリソースを追加する前に、いくつか認識しておくべき事項があります。

サードパーティのコードと新しい障害点

CRDを作成しても、勝手に新しい障害点が追加されてしまうことはありませんが(たとえば、サードパーティのコードをAPIサーバーで実行することによって)、パッケージ(たとえば、Chart)またはその他のインストールバンドルには、多くの場合、CRDと新しいカスタムリソースのビジネスロジックを実装するサードパーティコードが入ったDeploymentが含まれます。

アグリゲートAPIサーバーのインストールすると、常に新しいDeploymentが付いてきます。

ストレージ

カスタムリソースは、ConfigMapと同じ方法でストレージの容量を消費します。多数のカスタムリソースを作成すると、APIサーバーのストレージ容量を超えてしまうかもしれません。

アグリゲートAPIサーバーも、メインのAPIサーバーと同じストレージを利用するかもしれません。その場合、同じ問題が発生しえます。

認証、認可、そして監査

CRDでは、APIサーバーのビルトインリソースと同じ認証、認可、そして監査ロギングの仕組みを利用します。

もしRBACを使っている場合、ほとんどのRBACのロールは新しいリソースへのアクセスを許可しません。(クラスター管理者ロール、もしくはワイルドカードで作成されたロールを除く)新しいリソースには、明示的にアクセスを許可する必要があります。多くの場合、CRDおよびアグリゲートAPIには、追加するタイプの新しいロール定義がバンドルされています。

アグリゲートAPIサーバーでは、APIサーバーのビルトインリソースと同じ認証、認可、そして監査の仕組みを使う場合と使わない場合があります。

カスタムリソースへのアクセス

Kubernetesのクライアントライブラリーを使い、カスタムリソースにアクセスすることが可能です。全てのクライアントライブラリーがカスタムリソースをサポートしているわけでは無いですが、GoPython のライブラリーはサポートしています。

カスタムリソースは、下記のような方法で操作できます:

  • kubectl
  • kubernetesの動的クライアント
  • 自作のRESTクライアント
  • Kubernetesクライアント生成ツールを使い生成したクライアント(生成は高度な作業ですが、一部のプロジェクトは、CRDまたはAAとともにクライアントを提供する場合があります)

次の項目

2 - アグリゲーションレイヤーを使ったKubernetes APIの拡張

アグリゲーションレイヤーを使用すると、KubernetesのコアAPIで提供されている機能を超えて、追加のAPIでKubernetesを拡張できます。追加のAPIは、service-catalogのような既製のソリューション、または自分で開発したAPIのいずれかです。

アグリゲーションレイヤーは、カスタムリソースとは異なり、kube-apiserverに新しい種類のオブジェクトを認識させる方法です。

アグリゲーションレイヤー

アグリゲーションレイヤーは、kube-apiserverのプロセス内で動きます。拡張リソースが登録されるまでは、アグリゲーションレイヤーは何もしません。APIを登録するには、ユーザーはKubernetes APIで使われるURLのパスを"要求"した、APIService オブジェクトを追加します。それを追加すると、アグリゲーションレイヤーはAPIパス(例、/apis/myextension.mycompany.io/v1/…)への全てのアクセスを、登録されたAPIServiceにプロキシします。

APIServiceを実装する最も一般的な方法は、クラスター内で実行されるPodで拡張APIサーバー を実行することです。クラスター内のリソース管理に拡張APIサーバーを使用している場合、拡張APIサーバー("extension-apiserver"とも呼ばれます)は通常、1つ以上のコントローラーとペアになっています。apiserver-builderライブラリは、拡張APIサーバーと関連するコントローラーの両方にスケルトンを提供します。

応答遅延

拡張APIサーバーは、kube-apiserverとの間の低遅延ネットワーキングが必要です。 kube-apiserverとの間を5秒以内に往復するためには、ディスカバリーリクエストが必要です。

拡張APIサーバーがそのレイテンシ要件を達成できない場合は、その要件を満たすように変更することを検討してください。また、kube-apiserverでEnableAggregatedDiscoveryTimeout=false フィーチャーゲートを設定することで、タイムアウト制限を無効にすることができます。この非推奨のフィーチャーゲートは将来のリリースで削除される予定です。

次の項目