1. 序章

このチュートリアルでは、Java用の KubernetesAPIの調査を続けます。 今回は、ページングと非同期呼び出しの2つの機能に焦点を当てます。

2. ページング

一言で言えば、ページングを使用すると、チャンク、別名ページで大きな結果セットを反復処理できます。したがって、このメソッドの名前です。 Kubernetes Java APIのコンテキストでは、この機能はリソースのリストを返すすべてのメソッドで使用できます。 これらのメソッドには、結果を反復処理するために使用できる2つのオプションのパラメーターが常に含まれています。

  • limit :1回のAPI呼び出しで返されるアイテムの最大数
  • continue :返された結果セットの開始点をサーバーに通知する継続トークン

これらのパラメーターを使用すると、サーバーに過度の負荷をかけることなく、任意の数のアイテムを反復処理できます。 さらに良いことに、結果を保持するためにクライアント側で必要なメモリの量も制限されます。

次に、これらのパラメーターを使用して、このメソッドを使用してクラスター内で使用可能なすべてのポッドのリストを取得する方法を見てみましょう。

ApiClient client = Config.defaultClient();
CoreV1Api api = new CoreV1Api(client);
String continuationToken = null;
do {
    V1PodList items = api.listPodForAllNamespaces(
      null,
      continuationToken, 
      null,
      null, 
      2, 
      null, 
      null,
      null,
      10,
      false);
    continuationToken = items.getMetadata().getContinue();
    items.getItems()
      .stream()
      .forEach((node) -> System.out.println(node.getMetadata()));
} while (continuationToken != null);

ここで、 listPodForAllNamespaces() API呼び出しの2番目のパラメーターには継続トークンが含まれ、5番目のパラメーターはlimitパラメーターです。 制限は通常は固定値ですが、続行には少し余分な労力が必要です。

最初の呼び出しでは、 null 値を送信し、これがページ化された要求シーケンスの最初の呼び出しであることをサーバーに通知します。 応答を受信すると、対応するリストメタデータフィールドから使用するcontinue値の次の新しい値を取得します。

使用可能な結果がなくなると、この値は null になるため、このファクトを使用して、反復ループの終了条件を定義します。

2.1. ページネーションの落とし穴

ページングメカニズムは非常に単純ですが、覚えておく必要のある詳細がいくつかあります。

  • 現在、APIはサーバー側の並べ替えをサポートしていません。 現在、並べ替えのストレージレベルのサポートが不足していることを考えると、これがすぐに変更される可能性はほとんどありません。
  • continue を除くすべての呼び出しパラメーターは、呼び出し間で同じである必要があります
  • continue 値は、不透明なハンドルとして扱う必要があります。 その価値については決して仮定してはいけません
  • 反復は一方向です以前に受け取ったものを使用して結果セットに戻ることはできません継続するトークン
  • 返されたリストのメタデータにはremainingItemCountフィールドが含まれていますが、その値は信頼できず、すべての実装でサポートされていません。

2.2. リストデータの一貫性

Kubernetesクラスタは非常に動的な環境であるため、ページ付けされた呼び出しシーケンスに関連付けられた結果セットがクライアントによって読み取られている間に変更される可能性があります。 この場合、Kubernetes APIはどのように動作しますか?

Kubernetesのドキュメントで説明されているように、リストAPIは resourceVersion パラメーターをサポートします。このパラメーターは、 resourceVersionMatch とともに、特定のバージョンを選択して含める方法を定義します。 ただし、ページ化された結果セットの場合、動作は常に同じです:「トークンを続行、正確」。

これは、返されるリソースのバージョンが、ページ付けされたリストの呼び出しが開始されたときに使用可能なバージョンに対応していることを意味します。 このアプローチは一貫性を提供しますが、後で変更された結果は含まれません。 たとえば、大規模なクラスター内のすべてのポッドの反復が完了するまでに、一部のポッドはすでに終了している可能性があります。

3. 非同期呼び出し

これまで、Kubernetes APIを同期的に使用してきました。これは、単純なプログラムには適していますが、クラスターから応答を受け取って処理するまで呼び出し元のスレッドをブロックするため、リソース使用の観点からはあまり効率的ではありません。 この動作は、たとえばGUIスレッドでこれらの呼び出しを開始した場合に、アプリケーションの応答性を著しく損なうことになります。

幸い、ライブラリはコールバックに基づく非同期モードをサポートしており、呼び出し元にすぐに制御を返します

CoreV1Api クラスを調べると、同期 xxx()メソッドごとに、 xxxAsync()バリアントもあることがわかります。 たとえば、 listPodForAllNamespaces()の非同期メソッドは listPodForAllNamespacesAsync()です。 引数は同じですが、コールバック実装用のパラメーターが追加されています。

3.1. コールバックの詳細

コールバックパラメータオブジェクトは、汎用インターフェイスを実装する必要があります ApiCallback これには4つのメソッドしか含まれていません。

  • onSuccess:呼び出しが成功した場合にのみ呼び出されます。 最初の引数のタイプは、同期バージョンによって返されるものと同じです
  • onFailure:サーバーの呼び出し中にエラーが発生したか、応答にエラーコードが含まれています
  • onUploadProgress :アップロード中に呼び出されます。 このコールバックを使用して、長時間の操作中にユーザーにフィードバックを提供できます
  • onDownloadProgress onUploadProgress と同じですが、ダウンロード用です

非同期呼び出しも通常の結果を返しません。 代わりに、 OkHttpの(Kubernetes APIで使用される基盤となるRESTクライアント) Call インスタンスを返します。これは、受信中の呼び出しへのハンドルとして機能します。 このオブジェクトを使用して、完了状態をポーリングしたり、必要に応じて、完了前にキャンセルしたりできます。

3.2. 非同期呼び出しの例

想像できるように、どこにでもコールバックを実装するには、多くの定型コードが必要です。 これを回避するために、このタスクを少し単純化する呼び出しヘルパーを使用します。

// Start async call
CompletableFuture<V1NodeList> p = AsyncHelper.doAsync(api,(capi,cb) ->
  capi.listNodeAsync(null, null, null, null, null, null, null, null, 10, false, cb)
);
p.thenAcceptAsync((nodeList) -> {
    nodeList.getItems()
      .stream()
      .forEach((node) -> System.out.println(node.getMetadata()));
});
// ... do something useful while we wait for results

ここで、ヘルパーは非同期呼び出し呼び出しをラップし、それをより標準的なCompleteableFutureに適合させます。 これにより、 ReactorProjectのライブラリなどの他のライブラリで使用できるようになります。 この例では、すべてのメタデータを標準出力に出力する完了ステージを追加しました。

いつものように、先物を扱うときは、発生する可能性のある並行性の問題に注意する必要があります。 このコードのオンラインバージョンには、この単純なコードでも少なくとも3つのスレッドが使用されたことを明確に示すデバッグログがいくつか含まれています。

  • 非同期呼び出しを開始するメインスレッド
  • 実際のHTTP呼び出しを行うために使用されるOkHttpのスレッド
  • 結果が処理される完了スレッド

4. 結論

この記事では、KubernetesJavaAPIでページングと非同期呼び出しを使用する方法を見てきました。

いつものように、例の完全なソースコードはGitHubにあります。