Javaã§Kubernetesãªãã¬ãŒã¿ãŒãéçºããããšã¯ããŸã äžè¬çã§ã¯ãããŸããã ãããŸã§ã®ãšãããGoã¯ãç¹ã«å¯Ÿå¿ãããã¹ããæžãããã®åªãããµããŒãã®ããã«ãããã§éžæãããŠããèšèªã§ããÂ
JavaããŒã¹ã®ãããžã§ã¯ãéçºã«ããã課é¡ã®1ã€ã¯ãKubernetes APIãµãŒããŒãšå¯Ÿè©±ããç°¡åãªèªåçµ±åãã¹ãããªãããšã§ããã ããããåºã䜿çšãããŠãã Testcontainers çµ±åãã¹ãã©ã€ãã©ãªãããŒã¹ã«ãããªãŒãã³ãœãŒã¹ã©ã€ãã©ãª Kindcontainer ã®ãããã§ããã®ã®ã£ãããåããããšãã§ããJavaããŒã¹ã®Kubernetesãããžã§ã¯ãã®éçºã容æã«ããããšãã§ããŸããÂ
ãã®èšäºã§ã¯ãTestcontainers ã䜿çšããŠã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ã«ç€ºããŸãã

幞ããªããšã«ã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ãæ€èšããããã«ãªãããšãé¡ã£ãŠããŸãã
ããã«è©³ãã
- Testcontainers ã® Web ãµã€ãã«ã¢ã¯ã»ã¹ããŸãã
- ç¡æã¢ã«ãŠã³ããäœæããŠãTestcontainers Cloud ã®äœ¿çšãéå§ããŠãã ããã
- Docker ãã¹ã¯ãããã®ææ°ãªãªãŒã¹ãå ¥æããŸãã
- 質åããããŸãã? Docker ã³ãã¥ããã£ããæäŒãããŸãã
- ããã«ãŒã¯åããŠã§ãã? å§ããŸãããã
- Docker Newsletter ã賌èªããŠãã ããã