📖 摘要
本文是一份超详细的Java项目Docker化实战手册,从环境准备到最终上线,手把手带你完成整个容器化部署流程。你将学会:
- Docker基础概念与核心原理
- 如何为Java项目编写高效的Dockerfile
- 多阶段构建优化镜像体积
- 镜像推送与容器运行的最佳实践
- 生产环境部署的注意事项
无论你是刚接触Docker的新手,还是想系统梳理容器化流程的开发者,这篇文章都能给你带来实实在在的帮助!
📚 目录
- 🔧 环境准备
- 📦 项目结构说明
- 🐳 Docker基础概念
- ✍️ 编写Dockerfile
- ⚙️ 多阶段构建优化
- 🏗️ 构建与运行镜像
- 🚢 推送镜像到仓库
- 🌐 生产环境部署
- 💡 总结与最佳实践
🔧 环境准备
1. 开发环境要求
- JDK 8+ (推荐JDK 11/17 LTS版本)
- Maven 3.6+ 或 Gradle 7+
- Docker Desktop (Mac/Windows) 或 Docker Engine (Linux)
- 推荐IDE: IntelliJ IDEA (社区版即可)
2. 安装Docker
不同操作系统的安装方式:
Linux (Ubuntu为例):
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc# 安装依赖
sudo apt-get update
sudo apt-get install \apt-transport-https \ca-certificates \curl \gnupg \lsb-release# 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg# 设置稳定版仓库
echo \"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null# 安装Docker引擎
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io# 验证安装
sudo docker run hello-world
Mac/Windows:
直接下载Docker Desktop安装包:
https://www.docker.com/products/docker-desktop
3. 验证安装
docker --version
# 输出类似: Docker version 20.10.12, build e91ed57docker-compose --version
# 输出类似: docker-compose version 1.29.2, build 5becea4c
📦 项目结构说明
我们以一个典型的Spring Boot项目为例:
my-java-app/
├── src/
│ ├── main/
│ │ ├── java/com/example/demo/
│ │ │ ├── DemoApplication.java
│ │ │ └── controllers/
│ │ └── resources/
│ │ ├── application.properties
│ │ └── static/
│ └── test/
├── target/
│ ├── my-java-app-0.0.1-SNAPSHOT.jar
│ └── ...
├── pom.xml
└── Dockerfile
🐳 Docker基础概念
在开始之前,先了解几个核心概念:
- 镜像(Image): 只读模板,包含运行应用所需的所有内容
- 容器(Container): 镜像的运行实例
- Dockerfile: 构建镜像的指令文件
- Registry: 镜像仓库(如Docker Hub)
Docker的优势:
- 🏗️ 一致的运行环境
- ⚡ 快速部署和扩展
- 📦 资源隔离
- 🔄 易于版本管理和回滚
✍️ 编写Dockerfile
基础Dockerfile示例
# 第一阶段:构建
FROM maven:3.8.4-openjdk-11 AS build
WORKDIR /app
COPY pom.xml .
# 利用缓存下载依赖
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests# 第二阶段:运行
FROM openjdk:11-jre-slim
WORKDIR /app
# 从构建阶段复制jar包
COPY --from=build /app/target/my-java-app-*.jar app.jar
# 暴露端口
EXPOSE 8080
# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]
逐行解析
-
FROM maven:3.8.4-openjdk-11 AS build
使用Maven镜像作为构建阶段的基础镜像,并命名为"build" -
WORKDIR /app
设置工作目录为/app -
COPY pom.xml .
复制pom.xml到工作目录 -
RUN mvn dependency:go-offline
下载所有依赖项(利用Docker缓存层) -
COPY src ./src
复制源代码 -
RUN mvn package -DskipTests
打包应用(跳过测试) -
FROM openjdk:11-jre-slim
第二阶段使用更小的JRE镜像 -
COPY --from=build /app/target/my-java-app-*.jar app.jar
从构建阶段复制生成的jar包 -
EXPOSE 8080
声明容器暴露的端口 -
ENTRYPOINT ["java", "-jar", "app.jar"]
容器启动时执行的命令
⚙️ 多阶段构建优化
多阶段构建有三大优势:
- 减小镜像体积 - 最终镜像只包含运行时必要内容
- 提高安全性 - 构建工具不会出现在生产镜像中
- 更清晰的构建流程 - 分离构建和运行环境
进阶优化技巧
-
使用.dockerignore文件
避免将不必要的文件复制到镜像中:.git .idea *.iml target/ *.log *.tmp
-
选择合适的基础镜像
openjdk:11-jdk
- 完整JDK(较大)openjdk:11-jre
- 仅运行时(较小)openjdk:11-jre-slim
- 更精简版本openjdk:11-alpine
- 基于Alpine Linux(最小)
-
层缓存优化
将不常变化的指令放在前面,充分利用缓存:# 先复制pom.xml并下载依赖 COPY pom.xml . RUN mvn dependency:go-offline# 然后复制源代码 COPY src ./src
🏗️ 构建与运行镜像
1. 构建镜像
# -t 指定镜像名称和标签
# . 表示使用当前目录的Dockerfile
docker build -t my-java-app:1.0 .
构建过程输出示例:
[+] Building 45.3s (12/12) FINISHED=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 37B 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 35B 0.0s=> [internal] load metadata for docker.io/library/openjdk:11-jre-slim 1.5s=> [internal] load metadata for docker.io/library/maven:3.8.4-openjdk-11 1.5s=> [build 1/5] FROM docker.io/library/maven:3.8.4-openjdk-11@sha256:9c... 0.0s=> [internal] load build context 0.1s=> => transferring context: 3.01kB 0.0s=> CACHED [build 2/5] WORKDIR /app 0.0s=> [build 3/5] COPY pom.xml . 0.0s=> [build 4/5] RUN mvn dependency:go-offline 20.1s=> [build 5/5] COPY src ./src 0.0s=> [build 6/5] RUN mvn package -DskipTests 18.2s=> [stage-1 1/2] FROM docker.io/library/openjdk:11-jre-slim@sha256:9c... 0.0s=> [stage-1 2/2] COPY --from=build /app/target/my-java-app-*.jar app.jar 0.1s=> exporting to image 0.1s=> => exporting layers 0.1s=> => writing image sha256:7e9b6e5ba... 0.0s=> => naming to docker.io/library/my-java-app:1.0 0.0s
2. 查看镜像
docker images# 输出示例
REPOSITORY TAG IMAGE ID CREATED SIZE
my-java-app 1.0 7e9b6e5ba123 2 minutes ago 215MB
3. 运行容器
# -d 后台运行
# -p 端口映射(主机端口:容器端口)
# --name 容器名称
docker run -d -p 8080:8080 --name my-app my-java-app:1.0
4. 查看运行状态
docker ps# 输出示例
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a3b8d7e6f5g4 my-java-app:1.0 "java -jar app.jar" 2 minutes ago Up 2 minutes 0.0.0.0:8080->8080/tcp my-app
5. 查看日志
docker logs -f my-app# 输出示例. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.6.3)
6. 停止和删除容器
# 停止容器
docker stop my-app# 删除容器
docker rm my-app# 删除镜像
docker rmi my-java-app:1.0
🚢 推送镜像到仓库
1. 登录Docker Hub
docker login -u your-username
2. 标记镜像
# 格式: docker tag local-image:tagname username/repository:tagname
docker tag my-java-app:1.0 your-username/my-java-app:1.0
3. 推送镜像
docker push your-username/my-java-app:1.0
4. 从仓库拉取运行
docker run -d -p 8080:8080 your-username/my-java-app:1.0
🌐 生产环境部署
1. 使用docker-compose
创建docker-compose.yml
文件:
version: '3.8'services:app:image: your-username/my-java-app:1.0container_name: my-java-appports:- "8080:8080"environment:- SPRING_PROFILES_ACTIVE=prodrestart: unless-stoppedvolumes:- ./logs:/app/logshealthcheck:test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]interval: 30stimeout: 10sretries: 3
启动服务:
docker-compose up -d
2. Kubernetes部署示例
创建deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:name: java-app
spec:replicas: 3selector:matchLabels:app: java-apptemplate:metadata:labels:app: java-appspec:containers:- name: java-appimage: your-username/my-java-app:1.0ports:- containerPort: 8080resources:requests:cpu: "500m"memory: "512Mi"limits:cpu: "1000m"memory: "1024Mi"livenessProbe:httpGet:path: /actuator/healthport: 8080initialDelaySeconds: 30periodSeconds: 10readinessProbe:httpGet:path: /actuator/healthport: 8080initialDelaySeconds: 20periodSeconds: 5
💡 总结与最佳实践
✅ 最佳实践总结
-
镜像优化:
- 使用多阶段构建减小镜像体积
- 选择合适的基础镜像(Alpine/slim版本)
- 定期更新基础镜像以获取安全补丁
-
构建优化:
- 合理利用层缓存(.dockerignore + 指令顺序)
- 固定基础镜像版本(避免使用latest标签)
- 在CI/CD中实现自动化构建
-
运行优化:
- 限制容器资源(cpu/memory)
- 配置健康检查
- 使用非root用户运行
- 正确处理信号(如SIGTERM)
-
安全建议:
- 不要将敏感信息硬编码在镜像中(使用环境变量/secret)
- 扫描镜像中的漏洞(使用docker scan)
- 最小化容器权限(避免–privileged)
🛠️ 常见问题解决
Q: 构建时下载依赖很慢怎么办?
A: 配置Maven镜像仓库:
RUN mkdir -p /root/.m2 && \echo 'aliyunhttps://maven.aliyun.com/repository/publiccentral' > /root/.m2/settings.xml
Q: 容器启动后立即退出?
A: 可能原因:
- 应用启动失败 - 查看日志
docker logs
- 没有前台进程 - 确保应用不是以daemon方式运行
- 端口冲突 - 检查端口映射
Q: 如何调试容器内的应用?
A: 进入运行中的容器:
docker exec -it /bin/bash
或者直接附加到进程:
docker attach
🎯 下一步学习建议
- 学习Docker网络和存储卷配置
- 掌握Docker Compose编排多容器应用
- 了解Kubernetes容器编排
- 探索CI/CD流水线与Docker集成
- 学习服务网格(如Istio)与容器化应用
通过本文的学习,你已经掌握了Java项目容器化的核心技能。Docker的世界还有很多值得探索的地方,继续实践,你将发现更多容器化带来的便利与效率提升!🚀
Happy Dockering! 🐳