使用容器进行 Java 开发

先决条件

完成容器化您的应用程序中的容器化您的应用程序的步骤 。

概述

在本部分中,您将逐步为上一部分中容器化的应用程序设置本地开发环境。这包括:

  • 添加本地数据库并持久化数据
  • 创建开发容器以连接调试器
  • 配置 Compose 以在您编辑和保存代码时自动更新正在运行的 Compose 服务

添加本地数据库并持久化数据

您可以使用容器来设置本地服务,例如数据库。在本部分中,您将更新docker-compose.yaml文件以定义数据库服务和用于保存数据的卷。此外,此特定应用程序使用系统属性来定义数据库类型,因此您需要更新Dockerfile以在启动应用程序时传入系统属性。

在克隆存储库的目录中,docker-compose.yaml在 IDE 或文本编辑器中打开文件。docker init添加了示例数据库服务,但需要针对您独特的应用程序进行一些更改。

在该docker-compose.yaml文件中,您需要执行以下操作:

  • 取消注释所有数据库指令。您现在将使用数据库服务而不是本地存储来存储数据。
  • 删除顶级secrets元素以及db 服务内的元素。此示例使用环境变量作为密码而不是机密。
  • user从服务中删除该元素db。此示例在环境变量中指定用户。
  • 更新数据库环境变量。这些是由 Postgres 映像定义的。有关更多详细信息,请参阅 Postgres 官方 Docker 镜像
  • 更新服务的运行状况检查测试db并指定用户。默认情况下,运行状况检查使用 root 用户而不是petclinic您定义的用户。
  • 将数据库 URL 添加为服务中的环境变量server。这会覆盖 中定义的默认值 spring-petclinic/src/main/resources/application-postgres.properties

以下是更新后的compose.yaml文件。所有评论已被删除。

services:
  server:
    build:
      context: .
    ports:
      - 8080:8080
    depends_on:
      db:
        condition: service_healthy
    environment:
      - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
  db:
    image: postgres
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_PASSWORD=petclinic
    ports:
      - 5432:5432
    healthcheck:
      test: [ "CMD", "pg_isready", "-U", "petclinic" ]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

Dockerfile在 IDE 或文本编辑器中打开。在ENTRYPOINT指令中,更新指令以传入文件中指定的系统属性 spring-petclinic/src/resources/db/postgres/petclinic_db_setup_postgres.txt

- ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
+ ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]

保存并关闭所有文件。

现在,运行以下docker compose up命令来启动您的应用程序。

$ docker compose up --build

打开浏览器并在http://localhost:8080查看应用程序 。您应该会看到一个简单的宠物诊所应用程序。浏览应用程序。导航到“兽医”并通过能够列出兽医来验证应用程序是否已连接到数据库。

在终端中,按ctrl+c停止应用程序。

用于开发的 Dockerfile

您现在拥有的 Dockerfile 非常适合小型、安全的生产映像,其中仅包含运行应用程序所需的组件。开发时,您可能需要具有不同环境的不同图像。

例如,在开发映像中,您可能希望设置映像来启动应用程序,以便可以将调试器连接到正在运行的 Java 进程。

您可以添加一个新阶段,而不是管理多个 Dockerfile。然后,您的 Dockerfile 可以生成可供生产使用的最终映像以及开发映像。

将 Dockerfile 的内容替换为以下内容。

# syntax=docker/dockerfile:1

FROM eclipse-temurin:17-jdk-jammy as deps
WORKDIR /build
COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
    --mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests

FROM deps as package
WORKDIR /build
COPY ./src src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
    --mount=type=cache,target=/root/.m2 \
    ./mvnw package -DskipTests && \
    mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar

FROM package as extract
WORKDIR /build
RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted

FROM extract as development
WORKDIR /build
RUN cp -r /build/target/extracted/dependencies/. ./
RUN cp -r /build/target/extracted/spring-boot-loader/. ./
RUN cp -r /build/target/extracted/snapshot-dependencies/. ./
RUN cp -r /build/target/extracted/application/. ./
CMD [ "java", "-Dspring.profiles.active=postgres", "-Dspring-boot.run.jvmArguments='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000'", "org.springframework.boot.loader.launch.JarLauncher" ]

FROM eclipse-temurin:17-jre-jammy AS final
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser
USER appuser
COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]

保存并关闭Dockerfile.

在 中,您添加了一个基于该阶段Dockerfile标记的新阶段。在此阶段,您将提取的文件复制到公共目录,然后运行命令来启动应用程序。在命令中,您公开端口 8000 并声明 JVM 的调试配置,以便可以附加调试器。developmentextract

使用 Compose 进行本地开发

当前的 Compose 文件不会启动您的开发容器。为此,您必须更新 Compose 文件以适应开发阶段。另外,更新服务器服务的端口映射以提供调试器的访问。

在 IDE 或文本编辑器中打开petclinic并创建一个名为 docker-compose.dev.yml.将以下说明复制并粘贴到文件中。

services:
  server:
    build:
      context: .
      target: development
    ports:
      - 8080:8080
      - 8000:8000
    depends_on:
      db:
        condition: service_healthy
    environment:
      - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
  db:
    image: postgres
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_PASSWORD=petclinic
    ports:
      - 5432:5432
    healthcheck:
      test: [ "CMD", "pg_isready", "-U", "petclinic" ]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

现在,启动您的应用程序并确认它正在运行。

$ docker compose up --build

最后,测试您的 API 端点。运行以下curl命令:

$ curl  --request GET \
  --url http://localhost:8080/vets \
  --header 'content-type: application/json'

您应该收到以下回复:

{"vetList":[{"id":1,"firstName":"James","lastName":"Carter","specialties":[],"nrOfSpecialties":0,"new":false},{"id":2,"firstName":"Helen","lastName":"Leary","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":3,"firstName":"Linda","lastName":"Douglas","specialties":[{"id":3,"name":"dentistry","new":false},{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":2,"new":false},{"id":4,"firstName":"Rafael","lastName":"Ortega","specialties":[{"id":2,"name":"surgery","new":false}],"nrOfSpecialties":1,"new":false},{"id":5,"firstName":"Henry","lastName":"Stevens","specialties":[{"id":1,"name":"radiology","new":false}],"nrOfSpecialties":1,"new":false},{"id":6,"firstName":"Sharon","lastName":"Jenkins","specialties":[],"nrOfSpecialties":0,"new":false}]}

连接调试器

您将使用 IntelliJ IDEA 附带的调试器。您可以使用此 IDE 的社区版本。在 IntelliJ IDEA 中打开您的项目,转到“运行”菜单,然后选择“编辑配置”。添加类似于以下内容的新远程 JVM 调试配置:

Java 连接调试器

设置断点。

打开src/main/java/org/springframework/samples/petclinic/vet/VetController.java并在函数内添加断点showResourcesVetList

要启动调试会话,请选择“运行”菜单,然后选择“调试NameOfYourConfiguration”

调试菜单

您现在应该在 Compose 应用程序的日志中看到该连接。

撰写日志文件

您现在可以调用服务器端点。

$ curl --request GET --url http://localhost:8080/vets

您应该已经看到标记行上的代码中断,现在您可以像平常一样使用调试器。您还可以检查和观察变量、设置条件断点、查看堆栈跟踪以及执行一系列其他操作。

调试器代码断点

ctrl+c终端停止您的应用程序。

自动更新服务

使用 Compose Watch 在您编辑和保存代码时自动更新正在运行的 Compose 服务。有关 Compose Watch 的更多详细信息,请参阅 使用 Compose Watch

docker-compose.yaml在 IDE 或文本编辑器中打开文件,然后添加 Compose Watch 指令。以下是更新后的docker-compose.yaml 文件。

services:
  server:
    build:
      context: .
      target: development
    ports:
      - 8080:8080
      - 8000:8000
    depends_on:
      db:
        condition: service_healthy
    environment:
      - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
    develop:
      watch:
        - action: rebuild
          path: .
  db:
    image: postgres
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_PASSWORD=petclinic
    ports:
      - 5432:5432
    healthcheck:
      test: [ "CMD", "pg_isready", "-U", "petclinic" ]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

运行以下命令以使用 Compose Watch 运行您的应用程序。

$ docker compose watch

打开 Web 浏览器并在http://localhost:8080查看应用程序 。您应该会看到 Spring Pet Clinic 主页。

对本地计算机上应用程序源文件的任何更改现在都将自动反映在正在运行的容器中。

spring-petclinic/src/main/resources/templates/fragments/layout.html在 IDE 或文本编辑器中打开,并Home通过添加感叹号来更新导航字符串。

-   <li th:replace="~{::menuItem ('/','home','home page','home','Home')}">
+   <li th:replace="~{::menuItem ('/','home','home page','home','Home!')}">

保存更改layout.html,然后您可以在容器自动重建的同时继续开发。

容器重建并运行后,刷新 http://localhost:8080,然后验证Home!现在出现在菜单中。

ctrl+c终端中的 来停止 Compose Watch。

概括

在本节中,您了解了在本地运行数据库并保存数据。您还创建了一个包含 JDK 并允许您附加调试器的开发映像。最后,您设置 Compose 文件以公开调试端口,并配置 Compose Watch 来实时重新加载您的更改。

相关信息:

下一步

在下一节中,您将了解如何在 Docker 中运行单元测试。