汗をかくこずなくJavaでKubernetesオペレヌタヌを開発

投皿日: Jun 6, 2024

JavaでKubernetesオペレヌタヌを開発するこずは、ただ䞀般的ではありたせん。 これたでのずころ、Goは、特に察応するテストを曞くための優れたサポヌトのために、ここで遞択されおいる蚀語です。 

Javaベヌスのプロゞェクト開発における課題の1぀は、Kubernetes APIサヌバヌず察話する簡単な自動統合テストがないこずでした。 しかし、広く䜿甚されおいる Testcontainers 統合テストラむブラリをベヌスにしたオヌプン゜ヌスラむブラリ Kindcontainer のおかげで、このギャップを埋めるこずができ、JavaベヌスのKubernetesプロゞェクトの開発を容易にするこずができたす。 

この蚘事では、Testcontainers を䜿甚しお、Java で実装されたカスタム Kubernetes コントロヌラヌずオペレヌタヌをテストする方法を玹介したす。

2400X1260 汗をかくこずなく Java で Kubernetes オペレヌタヌを開発

DockerでのKubernetes

Testcontainersを䜿甚するず、Java仮想マシン(JVM)内で実行されおいるテストから、Dockerコンテナで実行されおいる任意のむンフラストラクチャコンポヌネントずプロセスを起動できたす。 フレヌムワヌクは、Dockerコンテナのラむフサむクルずクリヌンアップをテスト実行にバむンドしたす。 たずえば、デバッグ䞭にJVMが突然終了した堎合でも、起動したDockerコンテナも停止および削陀されたす。 Testcontainersは、Dockerむメヌゞの汎甚クラスに加えお、高床な構成オプションを持぀コンポヌネントなど、サブクラスの圢匏で特殊な実装を提䟛したす。 

これらの特殊な実装は、サヌドパヌティのラむブラリによっお提䟛するこずもできたす。 オヌプン゜ヌスプロゞェクトのKindcontainerは、Testcontainersをベヌスにした様々なKubernetesコンテナに特化した実装を提䟛するサヌドパヌティラむブラリの1぀です。

  • ApiServerコンテナ
  • K3sContainer
  • 皮類コンテナ

ただし ApiServerContainer 、Kubernetesコントロヌルプレヌンのごく䞀郚、぀たりKubernetes APIサヌバヌのみを提䟛し、 K3sContainer KindContainer Dockerコンテナで完党な単䞀ノヌドKubernetesクラスタヌを起動するこずに重点を眮いおいたす。 

これにより、それぞれのテストの芁件に応じおトレヌドオフが可胜になりたす:テストにAPIサヌバヌずの察話のみが必芁な堎合は、通垞、倧幅に高速な起動 ApiServerContainer で十分です。 ただし、Kubernetesコントロヌルプレヌンの他のコンポヌネントや他のオペレヌタヌずの耇雑な盞互䜜甚をテストするこずが範囲内にある堎合は、起動時間を犠牲にしおでも、2぀の「より倧きな」実装がそれに必芁なツヌルを提䟛したす。 ちなみに、ハヌドりェア構成によっおは、起動時間が1分以䞊に達するこずがありたす。

最初の䟋

Kubernetesコンテナに察するテストがいかに簡単かを説明するために、JUnit 5を䜿甚した䟋を芋おみたしょう。

@Testcontainers
public class SomeApiServerTest {
  @Container
  public ApiServerContainer<?> K8S = new ApiServerContainer<>();

  @Test
  public void verify_no_node_is_present() {
    Config kubeconfig = Config.fromKubeconfig(K8S.getKubeconfig());
    try (KubernetesClient client = new KubernetesClientBuilder()
           .withConfig(kubeconfig).build()) {
      // Verify that ApiServerContainer has no nodes
      assertTrue(client.nodes().list().getItems().isEmpty());
    }
  }
}

JUnit 5拡匵機胜のおかげで@Testcontainers、管理する必芁があるコンテナをアノテヌションで@Containerマヌクするこずで、のApiServerContainerラむフサむクル管理を簡単に凊理できたす。コンテナが起動するず、APIサヌバヌずの接続を確立するために必芁な詳现を含むYAMLドキュメントをメ゜ッドを介しお getKubeconfig() 取埗できたす。 

このYAMLドキュメントは、Kubernetesの䞖界で接続情報を提瀺する暙準的な方法を衚しおいたす。 この䟋で䜿甚されおいるファブリック8 Kubernetes クラむアントは、 を䜿甚しお Config.fromKubeconfig()構成できたす。 他のKubernetesクラむアントラむブラリは、同様のむンタヌフェむスを提䟛したす。 Kindcontainerは、この点に関しお特定の芁件を課すものではありたせん。

これら 3 ぀のコンテナヌ実装はすべお、共通の API に䟝存しおいたす。 したがっお、開発の埌の段階で、より重い実装の 1 ぀がテストに必芁であるこずが明らかになった堎合は、それ以䞊コヌドを倉曎するこずなく、その実装に切り替えるだけで枈みたす。

テストコンテナのカスタマむズ

倚くの堎合、Kubernetesコンテナが起動した埌、実際のテストケヌスを開始する前に、倚くの準備䜜業を行う必芁がありたす。 たずえば、オペレヌタヌの堎合、API サヌバヌは最初にカスタム リ゜ヌス定矩 (CRD) を認識するか、Helm チャヌトを䜿甚しお別のコントロヌラヌをむンストヌルする必芁がありたす。 最初は耇雑に聞こえるかもしれたせんが、Kindcontainer ず、コマンドラむン ツヌルkubectlhelmず .

次のリストは、テストのクラスパス kubectlから を䜿甚しお最初に CRD を適甚し、その埌に Helm チャヌトをむンストヌルする方法を瀺しおいたす。

@Testcontainers
public class FluentApiTest {
  @Container
  public static final K3sContainer<?> K3S = new K3sContainer<>()
    .withKubectl(kubectl -> {
      kubectl.apply.fileFromClasspath(“manifests/mycrd.yaml”).run();
    })
    .withHelm3(helm -> {
      helm.repo.add.run(“repo”, “https://repo.example.com”);
      helm.repo.update.run();
      helm.install.run(“release”, “repo/chart”);
    );
  // Tests go here
}

Kindcontainer は、最初のテストが始たる前にすべおのコマンドが実行されるようにしたす。 コマンド間に䟝存関係がある堎合は、簡単に解決できたす。Kindcontainer は、指定された順序で実行されるこずを保蚌したす。

Fluent API は、それぞれのコマンドラむン ツヌルの呌び出しに倉換されたす。 これらは個別のコンテナで実行され、必芁な接続の詳现で自動的に開始され、Docker内郚ネットワヌクを介しおKubernetesコンテナに接続されたす。 このアプロヌチにより、Kubernetes むメヌゞぞの䟝存関係ず、その䞭で䜿甚可胜なツヌルに関するバヌゞョンの競合が回避されたす。

Kubernetes のバヌゞョンの遞択

開発者が他に䜕も指定しない堎合、Kindcontainer はデフォルトでサポヌトされおいる最新の Kubernetes バヌゞョンを起動したす。 ただし、このアプロヌチは䞀般的に掚奚されないため、ベスト プラクティスでは、次に瀺すように、コンテナヌを䜜成するずきに、サポヌトされおいるバヌゞョンの 1 ぀を明瀺的に指定する必芁がありたす。

@Testcontainers
public class SpecificVersionTest {
  @Container
  KindContainer<?> container = new KindContainer<>(KindContainerVersion.VERSION_1_24_1);
  // Tests go here
}

3 ぀のコンテナヌ実装にはそれぞれ独自の Enum、サポヌトされおいる Kubernetes バヌゞョンの 1 ぀を遞択できたす。 Kindcontainerプロゞェクト自䜓のテストスむヌトは、粟巧なマトリックスベヌスの統合テストセットアップの助けを借りお、これらの各バヌゞョンで完党な機胜セットを簡単に利甚できるこずを保蚌したす。 Kubernetes゚コシステムは急速に進化し、Kubernetesのバヌゞョンに応じお異なる初期化手順を実行する必芁があるため、この粟巧なテストプロセスが必芁です。

䞀般的に、このプロゞェクトは、 4 か月ごずにリリヌスされる、珟圚保守されおいるすべおのKubernetesメゞャヌバヌゞョンのサポヌトに重点を眮いおいたす。 叀いKubernetesバヌゞョンは、 @Deprecated Kindcontainerでのサポヌトが負担になりすぎるず、最終的に削陀されたす。 ただし、これは、それぞれのKubernetesバヌゞョンの䜿甚が掚奚されなくなった堎合にのみ発生したす。

独自のDockerレゞストリを持ち蟌む

パブリック゜ヌスからのDockerむメヌゞぞのアクセスは、特に手動たたは自動の監査を備えた内郚Dockerレゞストリに䟝存しおいる䌁業環境では、倚くの堎合、簡単ではありたせん。 Kindcontainerを䜿甚するず、開発者はこの目的で䜿甚されるDockerむメヌゞに独自の座暙を指定できたす。 ただし、Kindcontainer は、初期化手順が異なる可胜性があるため、䜿甚されおいる Kubernetes のバヌゞョンを認識する必芁があるため、これらのカスタム座暙がそれぞれの Enum 倀に远加されたす。

@Testcontainers
public class CustomKubernetesImageTest {
  @Container
  KindContainer<?> container = new KindContainer<>(KindContainerVersion.VERSION_1_24_1
    .withImage(“my-registry/kind:1.24.1”));
  // Tests go here
}

Kubernetesむメヌゞ自䜓に加えお、Kindcontainerは他のいく぀かのDockerむメヌゞも䜿甚したす。 すでに説明したように、 や helm などのkubectlコマンドラむンツヌルは、独自のコンテナで実行されたす。適切に、これらのツヌルに必芁なDockerむメヌゞも構成できたす。 幞いなこずに、バヌゞョンに䟝存するコヌド パスは実行に必芁ありたせん。 

したがっお、以䞋に瀺す構成は、Kubernetes むメヌゞの堎合よりも単玔です。

@Testcontainers
public class CustomFluentApiImageTest {
  @Container
  KindContainer<?> container = new KindContainer<>()
    .withKubectlImage(
      DockerImageName
        .parse(“my-registry/kubectl:1.21.9-debian-10-r10”))
    .withHelm3Image(DockerImageName.parse(“my-registry/helm:3.7.2”));
  // Tests go here
}

開始された他のすべおのコンテナヌのむメヌゞの座暙も、手動で簡単に遞択できたす。 ただし、同じ画像たたは少なくずも互換性のある画像の䜿甚を保蚌するこずは、垞に開発者の責任です。 この目的のために、䜿甚されるDockerむメヌゞずそのバヌゞョンの完党なリストは、GitHubのKindcontainerのドキュメントにありたす。

アドミッション コントロヌラヌ Webhook

ここたで瀺したテストシナリオでは、JVMで実行されおいるKubernetesクラむアントが、ロヌカルたたはリモヌトで実行されおいるKubernetesコンテナにネットワヌク経由でアクセスし、その内郚で実行されおいるAPIサヌバヌず通信するずいう通信方向が明確です。 Dockerは、APIサヌバヌ甚のDockerコンテナにポヌトが開かれ、アクセス可胜になるずいう、この暙準的なケヌスを非垞に簡単にしたす。 

Kindcontainer は、このプロセスに必芁な構成手順を自動的に実行し、それぞれのネットワヌク構成に適した接続情報を Kubeconfig ずしお提䟛したす。

ただし、アドミッション コントロヌラヌ Webhook は、技術的により困難なテスト シナリオを提瀺したす。 これらの堎合、API サヌバヌは、マニフェストを凊理するずきに HTTPS 経由で倖郚 Webhook ず通信できる必芁がありたす。 私たちの堎合、これらのWebhookは通垞、テストロゞックが実行されるJVMで実行されたす。 ただし、Dockerコンテナから簡単にアクセスできない堎合がありたす。

これらの Webhook のテストをネットワヌク蚭定ずは無関係に容易にし、か぀シンプルにするために、Kindcontainer はトリックを採甚しおいたす。 Kubernetes コンテナヌ自䜓に加えお、さらに 2 ぀のコンテナヌが開始されたす。 SSHサヌバヌは、テストJVMからKubernetesコンテナぞのトンネルを確立し、リバヌスポヌト転送を蚭定する機胜を提䟛し、APIサヌバヌがJVMず通信できるようにしたす。 

Kubernetes では Webhook ずの TLS で保護された通信が必芁なため、Webhook の TLS タヌミネヌションを凊理するために Nginx コンテナヌも開始されたす。 Kindcontainer は、これに必芁な蚌明曞資料の管理を管理したす。 

プロセス、コンテナ、およびそれらのネットワヌク通信のセットアップ党䜓を図 1に瀺したす。

Webhook をテストするためのネットワヌク蚭定の図で、巊偎に JVM、Webhook サヌバヌ、SSH クラむアント、JUnit テスト、右偎に Docker ネットワヌクず SSH サヌバヌ、Nginx コンテナ、Kubernetes コンテナを瀺しおいたす。
図 1: Webhook をテストするためのネットワヌク蚭定。

幞いなこずに、Kindcontainer はこの耇雑さを䜿いやすい API の背埌に隠しおいたす。

@Testcontainers
public class WebhookTest {
    @Container
    ApiServerContainer<?> container = new ApiServerContainer<>()
.withAdmissionController(admission -> {
        admission.mutating()
                .withNewWebhook("mutating.example.com")
                .atPort(webhookPort) // Local port of webhook
                .withNewRule()
                .withApiGroups("")
                .withApiVersions("v1")
                .withOperations("CREATE", "UPDATE")
                .withResources("configmaps")
                .withScope("Namespaced")
                .endRule()
                .endWebhook()
                .build();
    });

    // Tests go here
}

開発者は、ロヌカルで実行されおいるWebhookのポヌトず、Kubernetesで蚭定するために必芁な情報を提䟛するだけで枈みたす。 その埌、Kindcontainerは、SSHトンネリング、TLSタヌミネヌション、およびKubernetesの構成を自動的に凊理したす。

Javaに぀いお考えおみたしょう

最小限のJUnitテストの簡単な䟋から始めお、Javaで実装されたカスタムKubernetesコントロヌラヌずオペレヌタヌをテストする方法を瀺したした。 Fluent APIの助けを借りお゚コシステムの䜿い慣れたコマンドラむンツヌルを䜿甚する方法ず、制限されたネットワヌク環境でも統合テストを簡単に実行する方法に぀いお説明したした。 最埌に、アドミッションコントロヌラヌのWebhookをテストするずいう技術的に困難なナヌスケヌスでさえ、Kindcontainerを䜿甚しお簡単か぀䟿利に実装できるこずを瀺したした。 

これらの新しいテストの可胜性のおかげで、将来的には、より倚くの開発者がKubernetes関連プロゞェクトの蚀語ずしおJavaを怜蚎するようになるこずを願っおいたす。

さらに詳しく

関連蚘事