Dev Service の作成
開発モードで外部サービスを置き換えるために、エクステンション用の Dev Service を開発する方法について説明します。
要件
このガイドを完成させるには、以下が必要です:
-
ざっと 15 minutes
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
動作するコンテナランタイム(Docker, Podman)
-
外部サービスのコンテナ化バージョン(すべてのDev Serviceがコンテナに依存しているわけではありませんが、ほとんどのDev Serviceはコンテナに依存しています。)
Dev Service の作成
エクステンションが外部サービスに接続するためのAPIを提供する場合、devサービスの実装を提供するのはよい考えです。
まず、 デプロイメント モジュールの ビルド・ファイルに、以下の依存関係を追加してください:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devservices-deployment</artifactId>
</dependency>
implementation("io.quarkus:quarkus-devservices-deployment")
次に、 DevServicesResultBuildItem
を返す新しいビルドステップをエクステンションプロセッサクラスに追加します。 hello-world イメージを使いますが、あなたのサービスに適したイメージを設定してください。
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
public DevServicesResultBuildItem createContainer() {
DockerImageName dockerImageName = DockerImageName.parse("hello-world");
GenericContainer container = new GenericContainer<>(dockerImageName)
.withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT)
.waitingFor(Wait.forLogMessage(".*Started.*", 1))
.withReuse(true);
container.start();
String newUrl = "http://%s:%d".formatted(container.getHost(),
container.getMappedPort(SERVICE_PORT));
Map<String, String> configOverrides = Map.of("some-service.base-url", newUrl);
return new DevServicesResultBuildItem.RunningDevService(FEATURE,
container.getContainerId(),
container::close,
configOverrides).toBuildItem();
}
このコードを使用すると、エクステンションをテストアプリケーションに追加して quarkus dev
を実行すると、コンテナーの起動を確認できるはずです。
ただし、ポートが公開されていないため、アプリケーションは接続できません。ポートを公開するには、コンテナー構造に withExposedPorts
を追加します。
以下に例を示します。
GenericContainer container = new GenericContainer<>(dockerImageName)
.withExposedPorts(SERVICE_PORT, OTHER_SERVICE_PORT);
Testcontainers
は、これらのポートをホスト上のランダムなポートにマッピングします。
これはポートの競合を回避しますが、コンテナ内のサービスにアプリケーションがどのように接続するかという新たな問題を引き起こします。
アプリケーションが接続できるようにするには、エクステンションはマッピングされたポートを使用してサービスのデフォルト設定をオーバーライドする必要があります。 これはコンテナーを起動した後に実行する必要があります。 以下に例を示します。
container.start();
String serviceUrl = "http://%s:%d".formatted(container.getHost(),
container.getMappedPort(SERVICE_PORT));
Map<String, String> configOverrides = Map.of("some-service.base-url",
serviceUrl);
他の設定のオーバーライドも同じマップに含めることができます。
コンテナーの起動を待機する
コンテナーが起動するまで待つには、コンテナー構築に .waitingFor
呼び出しを追加する必要があります。以下に例を示します。
container.waitingFor(Wait.forLogMessage(".*Started.*", 1));
ポートが開くのを待つという方法もあります。 待機方法については Testcontainers のドキュメント を参照してください。
Dev Service の設定
Dev Service の起動プロセスを設定するには、ビルドステップのコンストラクターで ConfigPhase.BUILD_TIME
設定クラスを受け入れます。
以下に例を示します。
@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
public DevServicesResultBuildItem createContainer(MyConfig config) {}
たとえば、この設定を使用して固定ポートを設定したり、イメージ名を設定したりすることができます。
if (config.port.isPresent()) {
String portBinding = "%d:%d".formatted(config.port.get(), SERVICE_PORT);
container.setPortBindings(List.of(portBinding));
}
再利用の制御
開発モードでは、ライブリロードにより、Quarkus が頻繁に再起動する場合があります。デフォルトでは、これによりテストコンテナーも再起動されます。 Quarkus の再起動は通常非常に高速ですが、コンテナーの再起動にはさらに長い時間がかかる場合があります。 コードを変更するたびにコンテナーが再起動するのを防ぐには、コンテナーを再利用可能としてマークします。
container.withReuse(true);
一部の Dev Service は、プロセッサー自体のコンテナーの状態を追跡する高度な再利用ロジックを実装しています。 サービスに複雑な要件がある場合や、インスタンス間で共有する必要がある場合は、これが必要になることがあります。