在 Docker 中运行 Java JAR 包实战教程

📚 目录

  1. 前言与环境准备
  2. [准备 Java 项目](#准备 Java 项目)
  3. [编写 Dockerfile](#编写 Dockerfile)
  4. 构建与运行镜像
  5. 进阶配置
  6. [使用 Docker Compose](#使用 Docker Compose)
  7. 最佳实践
  8. 常见问题排查

1. 前言与环境准备

1.1 为什么使用 Docker 运行 Java 应用?

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                      传统部署 vs Docker 部署                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   传统部署:                     Docker 部署:                     │
│   ┌─────────────┐              ┌─────────────┐                 │
│   │   App.jar   │              │  Container  │                 │
│   ├─────────────┤              │ ┌─────────┐ │                 │
│   │    JDK 8    │  ──────►     │ │ App.jar │ │                 │
│   ├─────────────┤              │ ├─────────┤ │                 │
│   │   CentOS    │              │ │   JDK   │ │                 │
│   └─────────────┘              │ ├─────────┤ │                 │
│                                │ │  Linux  │ │                 │
│   ❌ 环境不一致                 │ └─────────┘ │                 │
│   ❌ 依赖冲突                   └─────────────┘                 │
│   ❌ 部署复杂                                                   │
│                                ✅ 环境一致                      │
│                                ✅ 隔离性好                      │
│                                ✅ 快速部署                      │
└─────────────────────────────────────────────────────────────────┘

1.2 环境准备

bash 复制代码
# 检查 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 应用

java 复制代码
// 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 打包配置

xml 复制代码
<!-- 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 文件

bash 复制代码
# 使用 Maven 打包
mvn clean package -DskipTests

# 验证 JAR 文件
ls -lh target/my-app.jar

# 本地测试运行
java -jar target/my-app.jar

3. 编写 Dockerfile

3.1 基础版 Dockerfile

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 复制代码
# 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 版本选择

dockerfile 复制代码
# ============ 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 镜像

bash 复制代码
# 基本构建命令
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 查看镜像信息

bash 复制代码
# 列出所有镜像
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 运行容器

bash 复制代码
# 基本运行
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 验证运行状态

bash 复制代码
# 查看运行中的容器
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 复制代码
# 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"]
bash 复制代码
# 构建不同环境的镜像
docker build --build-arg PROFILE=dev -t my-app:dev .
docker build --build-arg PROFILE=prod -t my-app:prod .

5.2 配置文件外置

yaml 复制代码
# 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
bash 复制代码
# 运行时挂载外部配置
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 复制代码
# 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 时区配置

dockerfile 复制代码
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 基础配置

yaml 复制代码
# 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 完整微服务配置

yaml 复制代码
# 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 常用命令

bash 复制代码
# 启动所有服务
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 最佳实践

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 镜像优化清单

bash 复制代码
# 1. 使用多阶段构建减少镜像大小
# 2. 使用 .dockerignore 排除无关文件

# .dockerignore
.git
.gitignore
README.md
.idea
*.iml
target/
!target/*.jar
node_modules
*.log
docker-compose*.yml

8. 常见问题排查

8.1 问题排查命令

bash 复制代码
# 查看容器日志
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 性能监控

bash 复制代码
# 实时监控资源使用
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)                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

快速参考命令

bash 复制代码
# 完整工作流
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. 查看日志
相关推荐
一勺菠萝丶2 小时前
解决Java中IP地址访问HTTPS接口的SSL证书验证问题
java·tcp/ip·https
墨着染霜华2 小时前
IntelliJ IDEA 设置导出与导入完整指南(备份 / 迁移 / 团队共享)
java·ide·intellij-idea
浮游本尊2 小时前
Java学习第32天 - 性能优化与架构设计
java
五阿哥永琪2 小时前
Nacos注册/配置中心
java·开发语言
无敌最俊朗@2 小时前
Qt多线程阻塞:为何信号失效?
java·开发语言
__万波__2 小时前
二十三种设计模式(十四)--命令模式
java·设计模式·命令模式
一起养小猫2 小时前
《Java数据结构与算法》第四篇(三)二叉树遍历详解_CSDN文章
java·开发语言·数据结构
少许极端2 小时前
算法奇妙屋(十九)-子序列问题(动态规划)
java·数据结构·算法·动态规划·子序列问题