编辑此页面

组合 Dev Services

Compose Dev Services 提供了一种使用 Compose 规范定义自定义开发服务的方法。

Quarkus Dev Services 在开发和测试模式下自动配置未配置的服务。 大多数提供服务连接的扩展也提供了使用 Testcontainers 的开发服务实现。 每个开发服务都有一些默认配置,例如要使用的容器镜像,但在定制方面受到限制。

虽然这种自动化程度对于大多数用例来说很棒,但有时您需要对配置的服务进行更多控制和协调。

Compose Dev Services 通过允许您使用 Compose 规范定义自定义开发服务来扩展 Dev Services 的概念。 Compose 规范是一种以开发者为中心的标准,用于定义云和平台无关的基于容器的应用程序

Compose 是一种广泛使用的工具,用于定义和管理多容器应用程序,用于开发和测试目的。 YAML 描述文件通常命名为 compose.yml,定义了应用程序所需的服务、网络和卷。 Docker Compose 是参考实现,并且 Podman Desktop 还提供了对 Compose 规范的开箱即用支持。

Quarkus 会检测项目中名为 compose-devservices.yml 的 Compose 文件(或使用特定 Compose 文件),并在您的应用程序在开发或测试模式下运行时启动定义的服务。 提供开发服务的扩展会发现这些自定义服务,并使用它们而不是创建默认服务。 当开发模式或测试停止时,服务会自动停止并清理。

这种集成提供了无缝的开发体验,同时让您完全控制您的服务配置。

先决条件

要使用 Compose Dev Services,您需要

  1. 一个可用的本地容器环境:DockerPodman

  2. Compose 工具,例如 docker-composepodman-compose(请注意,docker composepodman compose 命令在内部调用这些二进制文件)

Compose Dev Services 仅与 Compose V2 兼容。

使用 Compose Dev Services

让我们通过从简单到更复杂场景的示例,了解如何使用 Compose Dev Services。

基本示例:PostgreSQL 数据库

在一个已经配置为使用 quarkus-jdbc-postgresql 扩展的 Quarkus 项目中,您可以在项目的根目录中创建一个 compose-devservices.yml 文件,并使用 Compose 定义一个自定义服务

services:
  db:
    image: postgres:17
    healthcheck:
      test: pg_isready -U myuser -d mydb
      interval: 5s
      timeout: 3s
      retries: 3
    ports:
      - '5432'
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb

当您在开发模式下运行应用程序或执行测试时,Compose Dev Services 将自动启动 compose-devservices.yml 文件中定义的 PostgreSQL 服务,而不是使用 quarkus-jdbc-postgresql 扩展提供的默认开发服务。

根据上述配置,PostgreSQL 容器端口 5432 将暴露给一个随机主机端口,并且应用程序数据源将通过提取连接信息(例如用户密码数据库名称)进行配置。

多服务示例:自定义网络和依赖关系

对于更复杂的场景,您可以定义自定义网络和服务依赖关系

services:
  db:
    image: postgres:17
    ports:
      - '5432'
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb
    networks:
      - backend
    volumes:
      - postgres-data:/var/lib/postgresql/data

  cache:
    image: redis:7
    command: redis-server --save 60 1 --loglevel warning
    ports:
      - '6379'
    networks:
      - backend
    depends_on:
      - db

  message-broker:
    image: rabbitmq:3-management
    ports:
      - '5672'
      - '15672'
    networks:
      - backend
      - frontend
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest

  api-gateway:
    image: nginx:latest
    ports:
      - '8080:80'
    networks:
      - frontend
    depends_on:
      - message-broker
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

networks:
  backend:
  frontend:

volumes:
  postgres-data:

请注意,可以根据需要自定义 compose-devservices.yml 文件,其中包含多个具有依赖关系的服务,以控制启动顺序、隔离服务通信的自定义网络以及用于数据持久性和配置的卷。

服务发现

由 Compose Dev Services 启动的服务会自动被提供开发服务的扩展发现。 每个扩展都会发现它需要的服务,并相应地配置应用程序。

扩展依赖于容器镜像名称和暴露的端口来发现服务。 例如,PostgreSQL Dev Service 查找镜像名称包含 postgres 且暴露的容器端口为 5432 的服务。

当找到匹配项时,扩展将

  1. 从容器中提取连接信息(连接 URL、凭据、数据库名称等)

  2. 配置应用程序以使用发现的服务

  3. 跳过创建自己的默认 Dev Service 容器

支持的扩展和发现标准

以下是具有开发服务支持的平台扩展列表

扩展 镜像名称 暴露的端口

AMQP

amqp, activemq-artemis, rabbitmq

5672

Apicurio 注册表

apicurio

8080

DB2

db2

50000

MySQL

mysql

3306

PostgreSQL

postgres

5432

MariaDB

mariadb

3306

Microsoft SQL Server

mssql

1433

Oracle 数据库

oracle

1521

Kafka

kafka, strimzi, redpanda

9092

Keycloak

keycloak

8080

Kubernetes

kube-apiserver, k3s, kindest/node

6443

MongoDB

mongo

27017

MQTT

hivemq, eclipse-mosquitto

1883

RabbitMQ

rabbitmq

5672

Pulsar

pulsar

6650

Redis

redis

6379

Infinispan

infinispan

11222

Elasticsearch

elasticsearch, opensearch

9200

可观测性

lgtm

16686, 9411, 9090, 3000

配置用于服务发现的自定义镜像

您可以通过在 application.properties 文件中设置适当的属性来自定义用于服务发现的镜像名称。 例如,要使用自定义 Kafka 镜像

quarkus.kafka.devservices.image-name=my-custom-kafka:latest

每个提供 Dev Services 的扩展都有其自己的配置属性来定制镜像名称。 有关详细信息,请参阅特定扩展文档。

使用特定的 Compose 文件

默认情况下,Compose Dev Services 在项目根目录中查找名为 compose-devservices.[yml|yaml]docker-compose-devservices.[yml|yaml] 的文件。

您可以通过在 application.properties 文件中设置 quarkus.compose.devservices.files 属性来指定要使用的自定义文件

# Use a specific compose file for all modes
quarkus.compose.devservices.files=src/main/docker/compose.yml

# Use different compose files for different config profiles
%dev.quarkus.compose.devservices.files=src/main/docker/dev-compose.yml
%test.quarkus.compose.devservices.files=src/test/resources/test-compose.yml

这允许您根据 Quarkus 配置配置文件使用不同的开发服务环境。 您还可以通过用逗号分隔来指定多个文件

quarkus.compose.devservices.files=src/main/docker/base-compose.yml,src/main/docker/extra-services.yml

使用 Compose 配置文件

使用配置文件,Compose 文件可以定义一组活动配置文件,以便为各种用法和环境调整启动的服务。 您可以通过在 application.properties 文件中设置 quarkus.compose.devservices.profiles 属性来指定要激活的配置文件

services:
  db:
    image: postgres:17
    profiles:
      - dev
      - local
    ports:
      - '5432'
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb
%test.quarkus.compose.devservices.profiles=dev,local

忽略服务

您可以通过将 io.quarkus.devservices.compose.ignore 标签添加到 Compose 文件中的服务来配置 Compose Dev Services 以忽略特定服务

services:
  db:
    image: postgres:17
    labels:
      io.quarkus.devservices.compose.ignore: true
    ports:
      - '5432'
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb

当一个服务将此标签设置为 true 时,它将从服务发现中排除,并且扩展程序将不会将其用于 Dev Services。

服务准备就绪

Compose Dev Services 提供了多种方法来确保服务在您的应用程序尝试使用它们之前已准备就绪。

运行状况检查

Compose Dev Services 尊重您在 Compose 文件中定义的健康检查。 建议为您的服务配置健康检查,以确保它们在您的应用程序尝试使用它们之前已准备就绪

services:
  db:
    image: postgres:17
    healthcheck:
      test: pg_isready -U myuser -d mydb
      interval: 5s
      timeout: 3s
      retries: 3
    ports:
      - '5432'
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb

等待日志

您可以配置 Compose Dev Services 以等待特定的日志消息出现在容器日志中,然后再将服务视为已准备就绪。 这对于不提供健康检查但在准备就绪时记录消息的服务很有用。

要等待特定的日志消息,请将带有前缀 io.quarkus.devservices.compose.wait_for.logs 的标签添加到您的服务

services:
  app:
    image: my-application:latest
    ports:
      - '8080'
    labels:
      io.quarkus.devservices.compose.wait_for.logs: .*Server is now running.*

您还可以通过设置数字后缀值来指定日志消息应出现的次数:io.quarkus.devservices.compose.wait_for.logs.3: .Worker started.

等待端口

默认情况下,Compose Dev Services 会等待所有暴露的端口都可用,然后才将服务视为已准备就绪。 可以使用标签自定义此行为

services:
  app:
    image: my-application:latest
    ports:
      - '8080'
    labels:
      # Change to `true` to disable waiting for ports
      io.quarkus.devservices.compose.wait_for.ports.disable: false
      # Or set a custom timeout for port waiting
      io.quarkus.devservices.compose.wait_for.ports.timeout: "30s"

全局超时

您可以使用 quarkus.devservices.timeout 属性配置 Dev Services 启动的全局超时

quarkus.devservices.timeout=120s

此属性设置等待所有 Dev Services 启动的最长时间。 默认值为 60 秒。

排序服务

Compose Dev Services 按照服务在 Compose 文件中定义的顺序启动服务。 如果您需要按特定顺序启动服务,您可以使用 depends_on 属性

services:
  db:
    image: postgres:17
    ports:
      - '5432'
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb

  app:
    image: my-application:latest
    ports:
      - '8080'
    depends_on:
      - db

将服务配置暴露给应用程序

Compose Dev Services 会自动将发现的服务的配置暴露给您的应用程序。 例如,当发现具有以下 compose 服务描述的数据库服务时

services:
  db:
    image: mysql:17
    ports:
      - '9906:3306'
    labels:
      io.quarkus.devservices.compose.jdbc.parameters: characterEncoding=UTF-8&useUnicode=true
    environment:
      MYSQL_USER: myuser
      MYSQL_PASSWORD: mypassword
      MYSQL_DATABASE: mydb

将自动设置以下应用程序属性

quarkus.datasource.username=myuser
quarkus.datasource.password=mypassword
quarkus.datasource.jdbc.url=jdbc:mysql://:9906/mydb?characterEncoding=UTF-8&useUnicode=true

实际值是从服务标签和环境变量中提取的。 主机设置为 Docker 主机 IP 地址,端口设置为主机上映射的端口。

对于数据库服务,如果设置了标签 io.quarkus.compose.devservices.jdbc.parameters,则参数将添加到 JDBC URL。

使用服务标签进行自定义配置映射

您可以使用服务标签自定义如何将环境变量和暴露的端口映射到应用程序配置属性。

映射环境变量

要将环境变量映射到特定的应用程序配置属性,请使用 io.quarkus.devservices.compose.config_map.env.<env-var-name> 标签。

让我们以 MQTT 代理为例

services:
  mqtt:
    image: eclipse-mosquitto:2.0.21
    ports:
    - '1883'
    environment:
      MQTT_USER: user
      MQTT_PASSWORD: pass
    labels:
      io.quarkus.devservices.compose.config_map.env.MQTT_USER: mp.messaging.connector.smallrye-mqtt.username
      io.quarkus.devservices.compose.config_map.env.MQTT_PASSWORD: mp.messaging.connector.smallrye-mqtt.password
    volumes:
      - ./conf:/mosquitto/config

其中 ./conf 包含 mosquitto.conf 文件

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883

allow_anonymous false
password_file /mosquitto/config/password.txt

并且 password.txt 文件包含用户和加密密码

user:$7$101$EyGShytu3v+...==

在此示例中

  • 值为 userMQTT_USER 环境变量映射到 mp.messaging.connector.smallrye-mqtt.username 属性

  • 值为 passMQTT_PASSWORD 环境变量映射到 mp.messaging.connector.smallrye-mqtt.password 属性

由于 password_filemosquitto.conf 中配置,因此不使用环境变量来设置用户名和密码,而是将它们映射到应用程序属性。

映射暴露的端口

要将暴露的端口映射到特定的应用程序配置属性,请使用 io.quarkus.devservices.compose.config_map.port.<container-port> 标签

services:
  my-service:
    image: my-service-image
    ports:
      - '7432:5432'
      - '9080:8080'
    labels:
      # Map port 5432 to quarkus.datasource.jdbc.port
      io.quarkus.devservices.compose.config_map.port.5432: quarkus.datasource.jdbc.port
      # Map port 8080 to quarkus.http.port
      io.quarkus.devservices.compose.config_map.port.8080: quarkus.http.port

在上面的示例中: - 端口 7432 映射到 quarkus.datasource.jdbc.port 属性 - 端口 9080 映射到 quarkus.http.port 属性

映射的属性将包含映射到容器端口的主机端口,如果您使用的是动态端口映射(例如,- '5432' 而不是 - '7432:5432'),则该端口可能与容器端口不同。

将端口映射暴露给运行中的容器

在某些情况下,容器需要在运行时知道它们映射到的主机端口。 例如,Kafka 代理需要向客户端公布其外部可访问的地址。

您可以使用 io.quarkus.devservices.compose.exposed_ports 标签来指定容器内将写入端口映射的文件路径

services:
  kafka:
    image: apache/kafka-native:3.9.0
    ports:
      - '9092'
    labels:
      io.quarkus.devservices.compose.exposed_ports: /etc/kafka/docker/ports

当容器启动时,Quarkus 会在指定路径复制一个包含环境变量样式端口映射的文件

PORT_9092=54321
PORT_8080=12345
# ... other ports as needed

每行都遵循 PORT_<containerPort>=<hostPort> 格式,其中 containerPort 是容器暴露的端口,hostPort 是主机上动态分配的端口。 容器命令可以等到文件存在,然后 source 此文件以将这些映射作为环境变量访问

#!/bin/bash

# Wait for the ports file to be created
while [ ! -f /etc/kafka/docker/ports ]; do
  sleep 0.1;
done;

# Source the file to load PORT_* variables
source /etc/kafka/docker/ports;

# Use the port mappings
export KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://:$PORT_9092;
# Run the Kafka broker executable
/etc/kafka/docker/run

使用 Compose 控制服务生命周期

Compose Dev Services 提供了多个配置选项来控制服务如何在应用程序实例之间启动、停止和共享。

当应用程序在开发或测试模式下启动时,Compose Dev Services 会根据配置决定是启动服务还是发现已经运行的服务。 当应用程序停止时,Compose Dev Services 会停止它启动的服务。

Compose 项目名称

Compose 使用项目名称来标识资源,例如服务、网络、卷等,以便命名空间和隔离不同的 compose 项目。 这使得能够发现现有资源并在应用程序停止时清理资源。

Compose Dev Services 按如下方式确定项目名称

  1. 如果设置了 quarkus.compose.devservices.project-name 属性,则将其用作项目名称

  2. 如果在 Compose 文件中指定了顶级 name 属性,则将其用作项目名称

  3. 否则,将选择默认名称 quarkus-devservices-<application-name>

发现已启动的 Compose 服务

确定项目名称后,Compose Dev Services 首先尝试发现具有该项目名称的现有服务。

已经启动的 compose 项目可能已经由本地运行的具有相同项目名称的另一个 Quarkus 应用程序启动,或者手动使用 docker compose uppodman compose up 命令启动。

无论服务是如何启动的,Compose Dev Services 都会配置开发服务和注入的配置属性,但不会在应用程序停止时停止服务。

如果在项目中未找到任何 compose 文件,并且未设置 quarkus.compose.devservices.project-name,Compose Dev Services 将不会尝试发现任何服务,并且将被禁用。

Compose Dev Services 用于测试

对于 Quarkus 测试,默认情况下使用格式为 quarkus-devservices-<application-name>-<random-suffix> 的生成的项目名称,以确保测试运行和运行开发模式服务之间的隔离。 这样,Quarkus 测试会启动在 compose 文件中定义的服务的单独副本。 例如,当在开发模式下运行连续测试时,测试将拥有自己隔离的服务集。

您可以通过设置 quarkus.compose.devservices.reuse-project-for-tests=true 属性来更改此行为。 这允许测试重用在开发模式下单独启动的服务。

使用启动/停止控件

在默认生命周期中,Compose Dev Services 会在应用程序启动和停止时自动启动和停止服务。 为了进行更细粒度的控制,您可以使用 start-servicesstop-services 配置属性。

当设置 quarkus.compose.devservices.start-services=false 时,Compose Dev Services 将仅尝试发现具有已确定项目名称的现有服务,但即使存在 compose 文件,也不会启动任何新服务。

当设置 quarkus.compose.devservices.stop-services=false 时,服务将在应用程序停止后继续运行。 这允许服务被其他应用程序或同一应用程序的后续运行重用。

您还可以使用 quarkus.compose.devservices.stop-timeout 属性配置停止服务的超时时间。 超时后,Compose Dev Services 将强制停止服务。 默认超时时间故意设置为较短的 1 秒,以便快速清理,但您可以根据需要增加它

# application.properties
quarkus.compose.devservices.stop-timeout=10s

与现有 Dev Services 的关系

Compose Dev Services 与 Quarkus 扩展提供的现有 Dev Services 实现一起工作。 服务发现过程遵循以下优先级顺序

  1. Dev Service 实现首先尝试通过共享服务标签(例如,由其他应用程序启动的服务)定位服务

  2. 如果未找到共享服务,它们将回退到发现由 Compose Dev Services 启动的服务

  3. 如果未找到 Compose 服务,则使用默认的 Dev Services 实现(通常是启动基于 Testcontainers 的服务)

当 Compose Dev Service 创建或发现一个项目时,常规的开发服务实现在 compose 项目默认网络中创建容器。 这确保了所有服务都可以使用其服务名称作为同一网络中的主机名相互通信。

配置参考

构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖

配置属性

类型

默认

Compose 开发服务已启用或禁用

环境变量:QUARKUS_COMPOSE_DEVSERVICES_ENABLED

显示更多

布尔值

true

Compose 开发服务配置的相对于项目根目录的文件路径列表,如果未提供,将在项目根目录中查找 compose 文件

环境变量:QUARKUS_COMPOSE_DEVSERVICES_FILES

显示更多

字符串列表

compose 项目的名称,用于发现运行中的容器,如果未提供,将生成项目名称

环境变量:QUARKUS_COMPOSE_DEVSERVICES_PROJECT_NAME

显示更多

字符串

要激活的 Compose 配置文件

环境变量:QUARKUS_COMPOSE_DEVSERVICES_PROFILES

显示更多

字符串列表

要传递给 compose 命令的附加选项列表

环境变量:QUARKUS_COMPOSE_DEVSERVICES_OPTIONS

显示更多

字符串列表

是否在启动时运行 compose up 并启动容器,禁用后,服务将按项目名称发现

环境变量:QUARKUS_COMPOSE_DEVSERVICES_START_SERVICES

显示更多

布尔值

true

是否在关闭时运行 compose down 并停止容器

环境变量:QUARKUS_COMPOSE_DEVSERVICES_STOP_SERVICES

显示更多

布尔值

true

是否使用测试容器 Ryuk 资源收割器来清理容器

环境变量:QUARKUS_COMPOSE_DEVSERVICES_RYUK_ENABLED

显示更多

布尔值

true

是否在 compose down 时删除卷

环境变量:QUARKUS_COMPOSE_DEVSERVICES_REMOVE_VOLUMES

显示更多

布尔值

true

在 compose down 时要删除哪些镜像

默认情况下,将删除本地构建的没有自定义标签的镜像。

环境变量:QUARKUS_COMPOSE_DEVSERVICES_REMOVE_IMAGES

显示更多

all, local

要传递给所有 Compose 实例的环境变量

环境变量:QUARKUS_COMPOSE_DEVSERVICES_ENV_VARIABLES__ENV_VARIABLES_

显示更多

Map<String,String>

服务的规模配置:配置特定服务的实例数

环境变量:QUARKUS_COMPOSE_DEVSERVICES_SCALE__SCALE_

显示更多

Map<String,Integer>

是否将容器日志跟踪到控制台

环境变量:QUARKUS_COMPOSE_DEVSERVICES_FOLLOW_CONTAINER_LOGS

显示更多

布尔值

false

是否在启动容器之前构建镜像。

如果未提供,Compose 镜像将按每个服务 pull-policy 构建。 当 true 时,强制在启动容器之前构建所有镜像。 当 false 时,跳过在启动容器之前重新构建镜像。

环境变量:QUARKUS_COMPOSE_DEVSERVICES_BUILD

显示更多

布尔值

是否为测试重用项目,禁用后,将为每次测试运行创建一个新项目

环境变量:QUARKUS_COMPOSE_DEVSERVICES_REUSE_PROJECT_FOR_TESTS

显示更多

布尔值

false

停止服务的超时时间,超时后将强制停止服务

环境变量:QUARKUS_COMPOSE_DEVSERVICES_STOP_TIMEOUT

显示更多

Duration 

1S

关于 Duration 格式

要写入持续时间值,请使用标准的 java.time.Duration 格式。 有关更多信息,请参阅 Duration#parse() Java API 文档

您还可以使用简化的格式,以数字开头

  • 如果该值仅为一个数字,则表示以秒为单位的时间。

  • 如果该值是一个数字后跟 ms,则表示以毫秒为单位的时间。

在其他情况下,简化格式将被转换为 java.time.Duration 格式以进行解析

  • 如果该值是一个数字后跟 hms,则在其前面加上 PT

  • 如果该值是一个数字后跟 d,则在其前面加上 P

相关内容