Appearance
使用 Docker 部署 Java 单体应用
Docker 是一种轻量级的容器化技术,可以将应用程序及其依赖项打包到一个可移植的容器中。使用 Docker 部署 Java 单体应用可以确保应用在不同环境中运行的一致性,简化部署流程,提高开发和运维效率。
本文将介绍现代化的 Docker 部署流程,使用 多阶段构建(Multi-stage Builds) 技术,无需在本地安装 Java 环境即可完成应用的构建和打包。
Docker 简介
Docker 通过容器化技术实现了应用程序的隔离和封装。每个容器都包含运行应用所需的所有内容:代码、运行时、系统工具、系统库和设置。与虚拟机相比,Docker 容器更加轻量级,启动速度更快,资源占用更少。
编写 Dockerfile (多阶段构建)
传统的 Docker 部署方式需要先在本地打包 JAR 文件,然后再构建镜像。这种方式依赖本地开发环境。现代化的最佳实践是使用 多阶段构建,将构建过程也容器化。
多阶段 Dockerfile
在项目根目录下创建 Dockerfile:
dockerfile
# 第一阶段:构建阶段 (Builder)
# 使用 Maven 镜像进行编译打包
FROM maven:3.9-eclipse-temurin-17 AS builder
# 设置工作目录
WORKDIR /build
# 复制 pom.xml 和源码
COPY pom.xml .
COPY src ./src
# 执行打包 (跳过测试以加快速度)
RUN mvn clean package -DskipTests
# 第二阶段:运行阶段 (Runtime)
# 使用精简的 JRE 镜像运行应用
FROM eclipse-temurin:17-jre-jammy
# 设置工作目录
WORKDIR /app
# 从构建阶段复制生成的 JAR 文件
# 注意:target 目录下的 jar 包名称需要根据实际情况调整,或者在 pom.xml 中指定 finalName
COPY --from=builder /build/target/*.jar app.jar
# 暴露应用端口
EXPOSE 8080
# 设置 JVM 参数环境变量
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
# 创建非 root 用户运行应用 (安全最佳实践)
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /app
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 启动应用
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]多阶段构建的优势:
- 无需本地环境:只要安装了 Docker,任何人(包括运维人员)都可以构建应用,无需安装 JDK 和 Maven。
- 镜像体积小:最终镜像只包含运行所需的 JRE 和 JAR 文件,不包含 Maven 和源码,体积大大减小。
- 构建一致性:构建过程在容器中进行,避免了"由于环境不同导致的构建失败"。
构建 Docker 镜像
编写好 Dockerfile 后,在项目根目录执行以下命令构建镜像:
bash
# 构建镜像并打标签
docker build -t my-app:1.0.0 .
# 验证镜像是否构建成功
docker images | grep my-app本地测试运行:
bash
# 启动容器
docker run -d -p 8080:8080 --name my-app-test my-app:1.0.0
# 查看日志
docker logs -f my-app-test
# 测试完成后清理
docker stop my-app-test && docker rm my-app-test发布镜像到镜像仓库
构建完成后,需要将镜像推送到镜像仓库,以便服务器端可以拉取。
通用推送流程
使用 Docker Hub 流程:登录 -> 打标签 -> 推送。
bash
# 1. 登录仓库
docker login [registry-url]
# 2. 为镜像打标签 (格式:仓库地址/命名空间/镜像名:标签)
# 例如推送到 Docker Hub
docker tag my-app:1.0.0 your-username/my-app:1.0.0
# 3. 推送镜像
docker push your-username/my-app:1.0.0TIP
建议同时推送 latest 标签,方便快速拉取最新版本: docker tag my-app:1.0.0 your-username/my-app:latest && docker push your-username/my-app:latest
服务器端部署
在生产环境中,推荐使用 Docker Compose 来管理应用,它能以声明式的方式定义服务、网络和卷。
方式一:使用 Docker Compose (推荐)
- 在服务器上创建
docker-compose.yml文件:
yaml
version: '3.8'
services:
app:
image: your-username/my-app:latest
container_name: my-app
restart: unless-stopped
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- JAVA_OPTS=-Xms512m -Xmx1024m
volumes:
- ./logs:/app/logs
# 如果需要数据库,可以在这里定义
# depends_on:
# - mysql
# mysql:
# image: mysql:8.0
# ...- 启动服务:
bash
# 后台启动
docker-compose up -d
# 查看日志
docker-compose logs -f app
# 更新应用 (拉取新镜像并重启)
docker-compose pull
docker-compose up -d方式二:使用 Docker Run (简单场景)
对于简单的测试或临时运行,可以直接使用 docker run 命令:
bash
docker run -d \
--name my-app \
--restart unless-stopped \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e JAVA_OPTS="-Xms512m -Xmx1024m" \
your-username/my-app:latest完整部署流程示例
以下是一个基于最佳实践的完整部署流程:
1. 开发与构建 (本地)
bash
# 在项目根目录构建镜像 (无需本地安装 Maven)
docker build -t my-app:1.0.0 .
# 推送到仓库
docker tag my-app:1.0.0 your-username/my-app:1.0.0
docker push your-username/my-app:1.0.02. 服务器部署 (生产环境)
在服务器上准备 docker-compose.yml,然后执行:
bash
# 拉取最新镜像并启动
docker-compose pull && docker-compose up -d
# 检查运行状态
docker-compose ps最佳实践
1. 镜像优化
- 使用 .dockerignore:排除不必要的文件(如
.git,target,node_modules),加快构建速度。 - 利用层缓存 (Layer Caching):在 Dockerfile 中,先复制
pom.xml并下载依赖,再复制源码。这样只要依赖不变更,构建速度会非常快。
2. 安全性
- 非 Root 用户:始终创建并切换到非 root 用户运行应用。
- 敏感信息管理:绝对不要在 Dockerfile 中硬编码密码或密钥。使用环境变量或 Docker Secrets 注入。
3. 运维管理
- 资源限制:在
docker-compose.yml中配置deploy.resources限制 CPU 和内存,防止应用耗尽服务器资源。 - 日志管理:配置日志驱动(如
json-file),并设置max-size和max-file防止日志占满磁盘。 - 健康检查:务必配置
HEALTHCHECK,以便 Docker 或编排工具知道应用何时真正就绪。
总结
通过采用 多阶段构建 和 Docker Compose,我们实现了一个现代化、标准化且高效的 Java 应用部署流程:
- 构建:利用多阶段构建实现"一次编写,到处运行"的构建过程。
- 发布:通过镜像仓库进行版本管理和分发。
- 部署:使用 Docker Compose 以声明式的方式管理服务。
