在Docker中运行Java JAR包的实战教程
作者:不惑_
这篇文章详细介绍了使用Docker运行Java应用的全过程,从环境准备到构建与运行,再到进阶配置和最佳实践,需要的朋友可以参考下
1. 前言与环境准备
1.1 为什么使用 Docker 运行 Java 应用?
┌─────────────────────────────────────────────────────────────────┐ │ 传统部署 vs Docker 部署 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 传统部署: Docker 部署: │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ App.jar │ │ Container │ │ │ ├─────────────┤ │ ┌─────────┐ │ │ │ │ JDK 8 │ ──────► │ │ App.jar │ │ │ │ ├─────────────┤ │ ├─────────┤ │ │ │ │ CentOS │ │ │ JDK │ │ │ │ └─────────────┘ │ ├─────────┤ │ │ │ │ │ Linux │ │ │ │ ❌ 环境不一致 │ └─────────┘ │ │ │ ❌ 依赖冲突 └─────────────┘ │ │ ❌ 部署复杂 │ │ ✅ 环境一致 │ │ ✅ 隔离性好 │ │ ✅ 快速部署 │ └─────────────────────────────────────────────────────────────────┘
1.2 环境准备
# 检查 Docker 是否安装 docker --version # Docker version 24.0.0, build xxxxx # 检查 Docker 服务状态 systemctl status docker # 如未安装,执行以下命令(以 CentOS 为例) yum install -y docker-ce docker-ce-cli containerd.io systemctl start docker systemctl enable docker
1.3 项目结构
my-java-app/ ├── src/ │ └── main/ │ └── java/ │ └── com/example/ │ └── Application.java ├── target/ │ └── my-app-1.0.0.jar # 打包后的 JAR 文件 ├── Dockerfile # Docker 构建文件 ├── docker-compose.yml # Docker Compose 配置 └── pom.xml
2. 准备 Java 项目
2.1 示例 Spring Boot 应用
// Application.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/hello")
public String hello() {
return "Hello from Docker! 🐳";
}
@GetMapping("/health")
public String health() {
return "OK";
}
}
2.2 Maven 打包配置
<!-- pom.xml -->
<project>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 指定打包后的文件名 -->
<finalName>my-app</finalName>
</build>
</project>
2.3 打包 JAR 文件
# 使用 Maven 打包 mvn clean package -DskipTests # 验证 JAR 文件 ls -lh target/my-app.jar # 本地测试运行 java -jar target/my-app.jar
3. 编写 Dockerfile
3.1 基础版 Dockerfile
# Dockerfile # 使用官方 OpenJDK 镜像作为基础镜像 FROM openjdk:8-jdk-alpine # 维护者信息 LABEL maintainer="your-email@example.com" LABEL version="1.0" LABEL description="My Java Application" # 设置工作目录 WORKDIR /app # 复制 JAR 文件到容器 COPY target/my-app.jar app.jar # 暴露应用端口 EXPOSE 8080 # 启动命令 ENTRYPOINT ["java", "-jar", "app.jar"]
3.2 优化版 Dockerfile(推荐)
# Dockerfile
# ============ 第一阶段:构建阶段 ============
FROM maven:3.8.6-openjdk-8-slim AS builder
WORKDIR /build
# 先复制 pom.xml,利用 Docker 缓存机制
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 复制源代码并构建
COPY src ./src
RUN mvn clean package -DskipTests
# ============ 第二阶段:运行阶段 ============
FROM openjdk:8-jre-alpine
# 创建非 root 用户(安全最佳实践)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# 从构建阶段复制 JAR 文件
COPY --from=builder /build/target/*.jar app.jar
# 更改文件所有权
RUN chown -R appuser:appgroup /app
# 切换到非 root 用户
USER appuser
# 暴露端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
# JVM 参数优化
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
3.3 不同 JDK 版本选择
# ============ JDK 版本选择参考 ============ # JDK 8 (经典稳定版) FROM openjdk:8-jdk-alpine # 完整 JDK,105MB FROM openjdk:8-jre-alpine # 仅运行时,85MB (推荐) # JDK 11 (LTS 长期支持版) FROM openjdk:11-jdk-slim # 精简版 FROM eclipse-temurin:11-jre # Eclipse Temurin (推荐) # JDK 17 (最新 LTS 版) FROM eclipse-temurin:17-jdk-alpine FROM eclipse-temurin:17-jre-alpine # 推荐 # JDK 21 (最新 LTS 版) FROM eclipse-temurin:21-jre-alpine
3.4 镜像大小对比
┌─────────────────────────────────────────────────────────────┐ │ 镜像大小对比 │ ├─────────────────────────────┬───────────────────────────────┤ │ 基础镜像 │ 大小 │ ├─────────────────────────────┼───────────────────────────────┤ │ openjdk:8 │ ~500MB │ │ openjdk:8-jdk-alpine │ ~105MB │ │ openjdk:8-jre-alpine │ ~85MB ⭐ 推荐 │ │ eclipse-temurin:17-jre │ ~260MB │ │ eclipse-temurin:17-jre-alpine │ ~180MB │ └─────────────────────────────┴───────────────────────────────┘
4. 构建与运行镜像
4.1 构建 Docker 镜像
# 基本构建命令
docker build -t my-java-app:1.0.0 .
# 带详细日志的构建
docker build --progress=plain -t my-java-app:1.0.0 .
# 不使用缓存构建
docker build --no-cache -t my-java-app:1.0.0 .
# 构建时传递参数
docker build \
--build-arg JAR_FILE=target/my-app.jar \
-t my-java-app:1.0.0 .
4.2 查看镜像信息
# 列出所有镜像 docker images # 输出示例: # REPOSITORY TAG IMAGE ID CREATED SIZE # my-java-app 1.0.0 a1b2c3d4e5f6 30 seconds ago 145MB # 查看镜像详细信息 docker inspect my-java-app:1.0.0 # 查看镜像历史层 docker history my-java-app:1.0.0
4.3 运行容器
# 基本运行
docker run -d -p 8080:8080 --name my-app my-java-app:1.0.0
# 带环境变量运行
docker run -d \
-p 8080:8080 \
--name my-app \
-e SPRING_PROFILES_ACTIVE=prod \
-e DATABASE_URL=jdbc:mysql://db:3306/mydb \
my-java-app:1.0.0
# 带资源限制运行
docker run -d \
-p 8080:8080 \
--name my-app \
--memory=512m \
--cpus=1.0 \
my-java-app:1.0.0
# 完整运行命令
docker run -d \
--name my-app \
-p 8080:8080 \
-v /host/logs:/app/logs \
-v /host/config:/app/config:ro \
-e JAVA_OPTS="-Xms256m -Xmx512m" \
-e SPRING_PROFILES_ACTIVE=prod \
--memory=512m \
--cpus=1.0 \
--restart=unless-stopped \
my-java-app:1.0.0
4.4 运行参数说明
┌──────────────────────────────────────────────────────────────────┐ │ Docker Run 参数说明 │ ├────────────────────┬─────────────────────────────────────────────┤ │ 参数 │ 说明 │ ├────────────────────┼─────────────────────────────────────────────┤ │ -d │ 后台运行容器 │ │ -p 8080:8080 │ 端口映射 (宿主机:容器) │ │ --name my-app │ 指定容器名称 │ │ -e KEY=VALUE │ 设置环境变量 │ │ -v /host:/container│ 挂载数据卷 │ │ --memory=512m │ 限制内存使用 │ │ --cpus=1.0 │ 限制 CPU 使用 │ │ --restart │ 重启策略 (no/always/unless-stopped) │ │ --network │ 指定网络 │ └────────────────────┴─────────────────────────────────────────────┘
4.5 验证运行状态
# 查看运行中的容器 docker ps # 查看容器日志 docker logs -f my-app # 查看最近 100 行日志 docker logs --tail 100 my-app # 进入容器内部 docker exec -it my-app /bin/sh # 测试应用 curl http://localhost:8080/hello # 输出: Hello from Docker! 🐳 # 查看容器资源使用 docker stats my-app
5. 进阶配置
5.1 多环境配置
# Dockerfile with args
FROM eclipse-temurin:17-jre-alpine
ARG PROFILE=dev
ARG APP_PORT=8080
ENV SPRING_PROFILES_ACTIVE=${PROFILE}
ENV SERVER_PORT=${APP_PORT}
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE ${APP_PORT}
ENTRYPOINT ["sh", "-c", "java -jar app.jar"]
# 构建不同环境的镜像 docker build --build-arg PROFILE=dev -t my-app:dev . docker build --build-arg PROFILE=prod -t my-app:prod .
5.2 配置文件外置
# application.yml
server:
port: ${SERVER_PORT:8080}
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
datasource:
url: ${DATABASE_URL:jdbc:h2:mem:testdb}
username: ${DATABASE_USER:sa}
password: ${DATABASE_PASSWORD:}
logging:
level:
root: ${LOG_LEVEL:INFO}
file:
path: /app/logs
# 运行时挂载外部配置
docker run -d \
-p 8080:8080 \
-v /opt/config/application-prod.yml:/app/config/application.yml:ro \
-v /opt/logs:/app/logs \
my-java-app:1.0.0
5.3 JVM 参数优化
# Dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
# 容器环境 JVM 优化参数
ENV JAVA_OPTS="\
-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:InitialRAMPercentage=50.0 \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:+UseStringDeduplication \
-Djava.security.egd=file:/dev/./urandom \
-Dfile.encoding=UTF-8"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
5.4 时区配置
FROM eclipse-temurin:17-jre-alpine
# 设置时区为中国
ENV TZ=Asia/Shanghai
RUN apk add --no-cache tzdata && \
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
6. 使用 Docker Compose
6.1 基础配置
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
image: my-java-app:1.0.0
container_name: my-java-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- TZ=Asia/Shanghai
volumes:
- ./logs:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
6.2 完整微服务配置
# docker-compose.yml
version: '3.8'
services:
# Java 应用
app:
build: .
image: my-java-app:1.0.0
container_name: my-java-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DATABASE_URL=jdbc:mysql://mysql:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai
- DATABASE_USER=root
- DATABASE_PASSWORD=rootpassword
- REDIS_HOST=redis
- REDIS_PORT=6379
volumes:
- app-logs:/app/logs
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
networks:
- app-network
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
# MySQL 数据库
mysql:
image: mysql:8.0
container_name: my-mysql
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=mydb
- TZ=Asia/Shanghai
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
ports:
- "3306:3306"
networks:
- app-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# Redis 缓存
redis:
image: redis:7-alpine
container_name: my-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
restart: unless-stopped
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: my-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
app-logs:
mysql-data:
redis-data:
6.3 Docker Compose 常用命令
# 启动所有服务 docker-compose up -d # 查看服务状态 docker-compose ps # 查看服务日志 docker-compose logs -f app # 重新构建并启动 docker-compose up -d --build # 停止所有服务 docker-compose down # 停止并删除数据卷 docker-compose down -v # 重启单个服务 docker-compose restart app # 扩展服务实例 docker-compose up -d --scale app=3
7. 最佳实践
7.1 Dockerfile 最佳实践
# ✅ 最佳实践示例
FROM eclipse-temurin:17-jre-alpine
# 1. 使用标签
LABEL maintainer="dev@example.com" \
version="1.0.0" \
description="Production Java Application"
# 2. 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 3. 设置工作目录
WORKDIR /app
# 4. 最小化层数,清理缓存
RUN apk add --no-cache tzdata curl && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone && \
apk del tzdata
# 5. 复制文件并设置权限
COPY --chown=appuser:appgroup target/*.jar app.jar
# 6. 切换用户
USER appuser
# 7. 设置健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 8. 暴露端口
EXPOSE 8080
# 9. 使用 ENTRYPOINT
ENTRYPOINT ["java", \
"-XX:+UseContainerSupport", \
"-XX:MaxRAMPercentage=75.0", \
"-jar", "app.jar"]
7.2 安全最佳实践
┌─────────────────────────────────────────────────────────────────┐ │ Docker 安全最佳实践 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ✅ 使用非 root 用户运行应用 │ │ ✅ 使用最小化基础镜像 (alpine) │ │ ✅ 定期更新基础镜像 │ │ ✅ 扫描镜像漏洞 (Trivy, Snyk) │ │ ✅ 不在镜像中存储敏感信息 │ │ ✅ 使用 secrets 管理敏感数据 │ │ ✅ 限制容器资源使用 │ │ ✅ 只暴露必要端口 │ │ ✅ 使用只读文件系统 (--read-only) │ │ │ └─────────────────────────────────────────────────────────────────┘
7.3 镜像优化清单
# 1. 使用多阶段构建减少镜像大小 # 2. 使用 .dockerignore 排除无关文件 # .dockerignore .git .gitignore README.md .idea *.iml target/ !target/*.jar node_modules *.log docker-compose*.yml
8. 常见问题排查
8.1 问题排查命令
# 查看容器日志 docker logs my-app # 实时查看日志 docker logs -f my-app # 进入容器调试 docker exec -it my-app /bin/sh # 查看容器进程 docker top my-app # 查看容器资源使用 docker stats my-app # 检查容器详细信息 docker inspect my-app # 查看容器文件系统变化 docker diff my-app
8.2 常见问题与解决方案
┌─────────────────────────────────────────────────────────────────┐ │ 常见问题排查 │ ├────────────────────────┬────────────────────────────────────────┤ │ 问题 │ 解决方案 │ ├────────────────────────┼────────────────────────────────────────┤ │ OOM (内存不足) │ 增加 --memory 限制 │ │ │ 调整 JVM 参数 -Xmx │ ├────────────────────────┼────────────────────────────────────────┤ │ 容器启动后立即退出 │ 检查 ENTRYPOINT/CMD │ │ │ 查看 docker logs │ ├────────────────────────┼────────────────────────────────────────┤ │ 端口无法访问 │ 检查端口映射 -p │ │ │ 检查防火墙设置 │ ├────────────────────────┼────────────────────────────────────────┤ │ 时区不正确 │ 设置 TZ 环境变量 │ │ │ 挂载时区文件 │ ├────────────────────────┼────────────────────────────────────────┤ │ 中文乱码 │ 设置 LANG=C.UTF-8 │ │ │ 安装字体包 │ ├────────────────────────┼────────────────────────────────────────┤ │ 无法连接数据库 │ 检查网络配置 │ │ │ 使用容器名作为主机名 │ └────────────────────────┴────────────────────────────────────────┘
8.3 性能监控
# 实时监控资源使用 docker stats # 使用 jps 查看 Java 进程 docker exec my-app jps -l # 查看 JVM 堆内存使用 docker exec my-app jmap -heap <PID> # 导出堆转储文件 docker exec my-app jmap -dump:format=b,file=/tmp/heap.hprof <PID> docker cp my-app:/tmp/heap.hprof ./heap.hprof
📝 总结
┌─────────────────────────────────────────────────────────────────┐ │ Docker 部署 Java 应用流程 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 准备阶段 │ │ └── 打包 JAR 文件 (mvn package) │ │ │ │ 2. 容器化阶段 │ │ ├── 编写 Dockerfile │ │ ├── 构建镜像 (docker build) │ │ └── 测试运行 (docker run) │ │ │ │ 3. 部署阶段 │ │ ├── 推送镜像到仓库 (docker push) │ │ ├── 拉取镜像 (docker pull) │ │ └── 运行容器 (docker-compose up) │ │ │ │ 4. 运维阶段 │ │ ├── 日志监控 (docker logs) │ │ ├── 性能监控 (docker stats) │ │ └── 故障排查 (docker exec) │ │ │ └─────────────────────────────────────────────────────────────────┘
快速参考命令
# 完整工作流 mvn clean package -DskipTests # 1. 打包 docker build -t my-app:1.0.0 . # 2. 构建镜像 docker run -d -p 8080:8080 my-app:1.0.0 # 3. 运行容器 curl http://localhost:8080/hello # 4. 验证 docker logs -f <container-id> # 5. 查看日志
以上就是在Docker中运行Java JAR包的实战教程的详细内容,更多关于Docker运行Java JAR包的资料请关注脚本之家其它相关文章!
