容器为跨开发、测试和生产阶段的 Java 应用程序提供一致的可移植环境。 本文介绍 Java 应用程序的容器化概念,并指导你创建、调试、优化和将容器化 Java 应用程序部署到 Azure 容器应用。
在本文中,你将了解 Java 开发人员的基本容器化概念以及以下技能:
- 为容器化 Java 应用程序设置开发环境。
- 创建针对 Java 工作负载优化的 Dockerfiles。
- 使用容器配置本地开发工作流。
- 调试容器化 Java 应用程序。
- 优化用于生产的 Java 容器。
- 将容器化 Java 应用程序部署到 Azure 容器应用。
通过容器化 Java 应用程序,可以获得一致的环境、简化的部署、高效的资源利用率和改进的可伸缩性。
Java 应用程序的容器
容器使用其依赖项打包应用程序,确保环境之间的一致性。 对于 Java 开发人员,这意味着将应用程序、其依赖项、Java 运行时环境/Java 开发工具包(JRE/JDK)和配置文件捆绑到单个可移植单元中。
容器化在虚拟化上具有关键优势,因此非常适合云开发。 与虚拟机相比,容器在服务器的主机 OS 内核上运行。 这有利于已在 Java 虚拟机(JVM)中运行的 Java 应用程序。 容器化 Java 应用程序可增加最少的开销,并提供显著的部署优势。
容器生态系统包括以下关键组件:
- 图像 - 蓝图。
- 容器 - 运行实例。
- 注册表 - 存储映像的位置。
- 容器编排器 - 大规模管理容器的系统。
Docker 是最受欢迎的容器化平台,它通过 Azure 容器应用在 Azure 生态系统中得到很好的支持。
设置开发环境
本部分将指导你安装必要的工具和配置开发环境,以生成、运行和调试容器化 Java 应用程序。
安装所需的工具
若要容器化 Java 应用程序,需要在开发计算机上安装以下工具:
- Docker Desktop。 提供适用于 Windows 或 macOS 的 Docker 引擎、CLI 和 Docker Compose。
- Visual Studio Code。 作为免费代码编辑器提供。
- 以下 Visual Studio Code 扩展:
使用以下命令验证安装:
docker --version
docker compose version
配置 Visual Studio Code 进行容器开发
要进行容器中的 Java 开发,请通过安装 Java 扩展包和配置 JDK 来设置 Visual Studio Code。 使用开发容器扩展可以打开容器中的任何文件夹,并使用 Visual Studio Code 在该容器内的完整功能集。
若要使 Visual Studio Code 能够自动生成并连接到开发容器,请在项目中创建 .devcontainer/devcontainer.json 文件。
例如,以下示例配置定义了 Java 生成:
{
"name": "Java Development",
"image": "mcr.microsoft.com/devcontainers/java:21",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"ms-azuretools.vscode-docker"
]
}
},
"forwardPorts": [8080, 5005],
"remoteUser": "vscode"
}
此配置使用 Azure 的 Java 开发容器映像,添加基本扩展,并转发应用程序端口和 8080
调试端口 5005
。
创建 Dockerfile
Dockerfile 包含有关生成 Docker 映像的说明。 对于 Java 应用程序, Dockerfile 通常包含以下组件:
- 具有 JDK 或 JRE 的基本映像。
- 复制应用程序文件的说明。
- 用于设置环境变量的命令。
- 入口点的配置。
选择基础映像
选择正确的基础映像至关重要。 请考虑以下选项:
DESCRIPTION | 名称 | 注解 |
---|---|---|
Azure Java 开发映像 | mcr.microsoft.com/java/jdk:21-zulu-ubuntu |
完整 JDK 并针对 Azure 进行优化 |
Azure Java 生产环境映像 | mcr.microsoft.com/java/jre:21-zulu-ubuntu |
仅限运行时并针对 Azure 进行优化 |
官方 OpenJDK 开发映像 | openjdk:21-jdk |
完整 JDK(Java 开发工具包) |
官方 OpenJDK 生产映像 | openjdk:21-jre |
仅限运行时 |
对于开发环境,请使用完整的 JDK 映像。 在生产环境中,使用 JRE 或无操作系统镜像来最大程度地减少应用程序的大小和攻击面。
Azure Java 镜像内置了特定于 Azure 的优化,并定期更新安全补丁,使它们成为针对 Azure 容器应用的应用程序的理想选择。
基本 Dockerfile 示例
以下示例展示了一个用于 Java 应用程序的简单 Dockerfile。
FROM mcr.microsoft.com/java/jdk:21-zulu-ubuntu
WORKDIR /app
COPY target/myapp.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
对于 Spring Boot 应用程序,可以将 Dockerfile 设置为以下基础:
FROM mcr.microsoft.com/java/jdk:21-zulu-ubuntu
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-Dspring.profiles.active=docker", "-jar", "app.jar"]
对于生产部署,请使用以下示例中所示的 JRE 映像来减小大小并最大程度地减少应用程序的攻击面:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Dserver.port=8080"
ENTRYPOINT ["java", ${JAVA_OPTS}, "-jar", "app.jar"]
使用容器进行本地开发
容器旨在在不同上下文中执行。 在本部分中,你将了解用于容器的本地开发流。
将 Docker Compose 用于多容器应用程序
大多数 Java 应用程序与数据库、缓存或其他服务进行交互。 Docker Compose 可帮助你使用简单的 YAML 配置文件定义和协调多容器应用程序。
什么是 Docker Compose?
Docker Compose 是一种工具,可用于执行以下任务:
- 在单个文件中定义多容器应用程序。
- 管理应用程序生命周期,包括启动、停止和重新生成。
- 维护隔离的环境。
- 创建用于服务通信的网络。
- 使用卷持久化地保存数据。
示例:使用数据库的 Java 应用程序
以下 compose.yml 文件使用 PostgreSQL 数据库配置 Java 应用程序:
version: '3.8'
services:
app:
build: . # Build from Dockerfile in current directory
ports:
- "8080:8080" # Map HTTP port
- "5005:5005" # Map debug port
environment:
- SPRING_PROFILES_ACTIVE=dev
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp
volumes:
- ./target:/app/target # Mount target directory for hot reloads
depends_on:
- db # Ensure database starts first
db:
image: postgres:13
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=myapp
ports:
- "5432:5432" # Expose PostgreSQL port
volumes:
- postgres-data:/var/lib/postgresql/data # Persist database data
volumes:
postgres-data: # Named volume for database persistence
此文件具有以下特征:
- 服务可以按名称相互引用 - 例如,
db
在 JDBC URL 中。 - Docker Compose 会自动为服务创建网络。
- Java 应用程序等待数据库启动,因为
depends_on
。 - 使用命名卷在重启后,数据库数据会持续保留。
常见 Docker Compose 命令
创建 compose.yml 文件后,使用以下命令管理应用程序:
# Build images without starting containers
docker compose build
# Start all services defined in compose.yml
docker compose up
# Start in detached mode (run in background)
docker compose up -d
# View running containers managed by compose
docker compose ps
# View logs from all containers
docker compose logs
# View logs from a specific service
docker compose logs app
# Stop all services
docker compose down
# Stop and remove volumes (useful for database resets)
docker compose down -v
开发工作流
使用 Docker Compose 的典型 Java 开发工作流包含以下步骤:
- 创建 compose.yml 文件和 Dockerfile。
- 运行
docker compose up
以启动所有服务。 - 对 Java 代码进行更改。
- 重新生成应用程序。 根据配置,可能需要重启容器。
- 测试容器化环境中的更改。
- 完成后,运行
docker compose down
。
使用 Docker 运行单个容器
对于不需要多个互连服务的更简单方案,可以使用 docker run
命令启动单个容器。
以下 Docker 命令适用于 Java 应用程序:
# Run a Java application JAR directly
docker run -p 8080:8080 myapp:latest
# Run with environment variables
docker run -p 8080:8080 -e "SPRING_PROFILES_ACTIVE=prod" myapp:latest
# Run in detached mode (background)
docker run -d -p 8080:8080 myapp:latest
# Run with a name for easy reference
docker run -d -p 8080:8080 --name my-java-app myapp:latest
# Run with volume mount for persistent data
docker run -p 8080:8080 -v ./data:/app/data myapp:latest
调试容器化应用程序
调试容器化 Java 应用程序有时很有挑战性,因为代码在容器内的隔离环境中运行。
标准调试方法并不总是直接应用,但配置正确,可以建立与应用程序的远程调试连接。 本部分介绍如何配置容器进行调试,将开发工具连接到正在运行的容器,以及如何排查与容器相关的常见问题。
设置远程调试
调试容器化 Java 应用程序需要公开调试端口并配置 IDE 以连接到它。 可以使用以下步骤完成这些任务:
若要启用调试,请修改 Dockerfile ,使其包含以下内容:
注释
相反,您可以修改容器的启动命令。
FROM mcr.microsoft.com/java/jdk:21-zulu-ubuntu WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 5005 ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]
配置 Visual Studio Code 的 launch.json 文件以连接到调试端口,如以下示例所示:
{ "version": "0.2.0", "configurations": [ { "type": "java", "name": "Debug in Container", "request": "attach", "hostName": "localhost", "port": 5005 } ] }
使用映射到主机的端口
5005
启动容器,然后在 Visual Studio Code 中启动调试器。
排查容器问题
当容器不按预期运行时,可以检查应用的日志以调查问题。
使用以下命令对应用程序进行故障排除。 在运行这些命令之前,请确保将占位符 (<...>
) 替换为自己的值。
# View logs
docker logs <CONTAINER_ID>
# Follow logs in real-time
docker logs -f <CONTAINER_ID>
# Inspect container details
docker inspect <CONTAINER_ID>
# Get a shell in the container
docker exec -it <CONTAINER_ID> bash
对于特定于 Java 的问题,请启用 JVM 标志以便进行更好的诊断,如以下示例所示:
ENTRYPOINT ["java", "-XX:+PrintFlagsFinal", "-XX:+PrintGCDetails", "-jar", "app.jar"]
下表列出了常见问题和相应的解决方案:
错误 | 可能的解决方法 |
---|---|
内存不足 | 增加容器内存限制 |
连接超时 | 检查网络配置是否存在错误。 验证端口和路由规则。 |
权限问题 | 验证文件系统权限。 |
Classpath 问题 | 检查 JAR 结构和依赖项。 |
优化 Java 容器
容器中的 Java 应用程序需要特别考虑以获得最佳性能。 JVM 是在容器通用之前设计的。 如果未正确配置容器,则使用容器可能会导致资源分配问题。
通过微调内存设置、优化映像大小和配置垃圾回收,可以显著提高容器化 Java 应用程序的性能和效率。 本部分介绍 Java 容器的基本优化,重点介绍内存管理、启动时间和资源利用率。
容器中的 JVM 内存配置
JVM 不会自动检测 Java 8 中的容器内存限制。 对于 Java 9+,默认情况下会启用容器感知。
将 JVM 配置为遵循容器限制,如以下示例所示:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
以下 JVM 标志对于容器化应用程序非常重要:
-
-XX:MaxRAMPercentage=75.0
。 将最大堆的大小设置为可用内存的百分比。 -
-XX:InitialRAMPercentage=50.0
。 设置初始堆大小。 -
-Xmx
和-Xms
。 这些标志也可用,但它们需要固定值。
准备生产部署
将容器化 Java 应用程序移动到生产环境需要除基本功能之外考虑事项。
生产环境需要可靠的安全性、可靠的监视、适当的资源分配和配置灵活性。
本部分介绍准备 Java 容器以供生产使用所需的基本做法和配置。 本部分重点介绍安全、运行状况检查和配置管理,以确保应用程序在生产环境中可靠运行。
安全最佳做法
使用以下做法保护容器化 Java 应用程序:
默认安全上下文。 以非根用户身份运行应用程序,如以下示例所示:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu WORKDIR /app COPY target/*.jar app.jar RUN addgroup --system javauser && adduser --system --ingroup javauser javauser USER javauser ENTRYPOINT ["java", "-jar", "app.jar"]
主动查找问题。 使用以下命令定期扫描容器映像中是否存在漏洞:
docker scan myapp:latest
基本图像新鲜度。 使基础映像保持最新状态。
机密管理。 实现适当的机密管理。 例如,不要将敏感数据硬编码到应用程序中,并尽可能使用密钥保管库。
受限的安全上下文。 将最小特权原则应用于所有安全上下文。
文件系统访问。 尽可能使用只读文件系统。
健康检查和监控
使用 探测 检查应用程序运行状况,以确保应用程序正常运行。
对于 Spring Boot 应用程序,添加 Actuator 依赖项以提供全面的健康检查端点,如以下示例所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
将应用程序配置为以适合容器环境(如 JSON)的格式输出日志。
部署到 Azure 容器应用
本部分将指导你为 Azure 容器应用部署准备 Java 容器,并重点介绍关键配置注意事项。
为 Azure 准备容器
端口配置。 确保您的容器监听 Azure 平台提供的端口,如以下示例所示:
FROM mcr.microsoft.com/java/jre:21-zulu-ubuntu WORKDIR /app COPY target/*.jar app.jar ENV PORT=8080 EXPOSE ${PORT} CMD java -jar app.jar --server.port=${PORT}
探测健康状况。 为 Azure 的运行情况和就绪情况检查实现 运行状况探测 。
日志配置。 将 日志记录 配置为输出到
stdout
/stderr
。为意外情况做好准备。 使用超时配置设置适当的正常关闭处理。 有关详细信息,请参阅 Azure 容器应用中的应用程序生命周期管理。