📋 文章目录

  1. 前言
  2. 环境准备
  3. 创建SpringBoot项目
  4. 项目打包
  5. Docker安装(CentOS)
  6. 编写Dockerfile
  7. 构建Docker镜像
  8. 运行Docker容器
  9. Docker Compose编排(进阶)
  10. 常见问题与排坑
  11. 总结

一、前言

在传统部署方式中,我们需要在服务器上安装JDK、配置环境变量、手动上传jar包、编写启动脚本……流程繁琐且容易出错。

Docker的优势:

对比项 传统部署 Docker部署
环境一致性 ❌ 开发/测试/生产环境不一致 ✅ 镜像保证完全一致
部署速度 慢(手动配置) 快(一键启动)
资源隔离 ❌ 进程级别 ✅ 容器级别隔离
扩缩容 困难 简单(docker-compose scale)
回滚 复杂 秒级回滚(切换镜像版本)

本文将手把手带你完成Docker部署SpringBoot项目的全流程。


二、环境准备

开发环境:
├── JDK 17(或 JDK 8/11 均可)
├── Maven 3.8+
├── Spring Boot 3.2.x(或 2.7.x)
├── IDEA 2023+
└── Docker 24+

服务器环境:
├── CentOS 7.9 / Ubuntu 22.04
├── Docker 24+
└── 开放端口:8080

三、创建SpringBoot项目

3.1 使用Spring Initializr创建项目

IDEA → New Project → Spring Initializr

Group:    com.example
Artifact: docker-demo
Java:     17
依赖选择: Spring Web

3.2 编写一个测试接口

package com.example.dockerdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public Map<String, Object> hello() {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 200);
        result.put("message", "Docker部署SpringBoot成功!");
        result.put("time", LocalDateTime.now().toString());
        return result;
    }

    @GetMapping("/health")
    public String health() {
        return "UP";
    }
}

3.3 配置 application.yml

server:
  port: 8080

spring:
  application:
    name: docker-demo

# 日志配置
logging:
  level:
    root: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

3.4 本地验证

启动项目,访问 http://localhost:8080/hello

{
    "code": 200,
    "message": "Docker部署SpringBoot成功!",
    "time": "2024-12-20T10:30:00"
}

✅ 本地运行正常,接下来准备打包。


四、项目打包

4.1 确认 pom.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>docker-demo</artifactId>
    <version>1.0.0</version>
    <!-- ⚠️ 必须是jar包方式 -->
    <packaging>jar</packaging>
    <name>docker-demo</name>

    <properties>
        <java.version>17</java.version>
    </properties>

    <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>
                <!-- ⚠️ 指定最终jar包名称,方便Dockerfile引用 -->
                <configuration>
                    <finalName>docker-demo</finalName>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.2 执行Maven打包

# 方式一:命令行打包
mvn clean package -DskipTests

# 方式二:IDEA中操作
# 右侧 Maven面板 → Lifecycle → clean → package

打包成功后,在 target/ 目录下生成:

target/
└── docker-demo.jar    ← 这就是我们需要的文件

4.3 验证jar包

java -jar target/docker-demo.jar

✅ 确认jar包可以独立运行后,再进行Docker部署。


五、Docker安装(CentOS 7)

如果服务器已安装Docker,可跳过本节。

5.1 卸载旧版本

sudo yum remove docker \
                docker-client \
                docker-client-latest \
                docker-common \
                docker-latest \
                docker-latest-logrotate \
                docker-logrotate \
                docker-engine

5.2 安装依赖

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

5.3 设置阿里云镜像源

sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

5.4 安装Docker

sudo yum install -y docker-ce docker-ce-cli containerd.io

5.5 启动Docker

# 启动
sudo systemctl start docker

# 开机自启
sudo systemctl enable docker

# 验证安装
docker --version
# Docker version 24.0.7, build afdd53b

5.6 配置镜像加速器

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me"
  ]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker

⚠️ 注意:2024年起国内部分镜像源已失效,请根据实际情况选择可用源,或使用代理。

5.7 验证Docker

docker run hello-world

出现 Hello from Docker! 即安装成功。


六、编写Dockerfile

6.1 项目结构

docker-demo/
├── src/
├── target/
│   └── docker-demo.jar
├── Dockerfile              ← 新建
├── .dockerignore            ← 新建
└── pom.xml

6.2 基础版Dockerfile

# ============================================
# 基础版 Dockerfile
# ============================================

# 基础镜像:使用OpenJDK 17(精简版)
FROM openjdk:17-jdk-slim

# 作者信息
LABEL maintainer="your-email@example.com"
LABEL description="SpringBoot Docker Demo"
LABEL version="1.0.0"

# 设置工作目录
WORKDIR /app

# 复制jar包到容器中
COPY target/docker-demo.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]

6.3 ⭐ 生产级Dockerfile(推荐)

# ============================================
# 生产级 Dockerfile(推荐使用)
# ============================================

# -------- 第一阶段:构建阶段 --------
FROM maven:3.9-eclipse-temurin-17 AS builder

WORKDIR /build
COPY pom.xml .
# 先下载依赖(利用Docker缓存层)
RUN mvn dependency:go-offline -B

COPY src ./src
RUN mvn clean package -DskipTests -B

# -------- 第二阶段:运行阶段 --------
FROM eclipse-temurin:17-jre-alpine

LABEL maintainer="your-email@example.com"

# 创建非root用户(安全最佳实践)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 从构建阶段复制jar包
COPY --from=builder /build/target/docker-demo.jar app.jar

# 设置文件权限
RUN chown appuser:appgroup app.jar

# 切换到非root用户
USER appuser

# 暴露端口
EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

# JVM优化参数
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError"

# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar"]

生产级Dockerfile的优势:

✅ 多阶段构建 → 镜像体积更小(从 600MB → 200MB)
✅ 非root用户 → 安全性更高
✅ 健康检查   → 自动监控容器状态
✅ JVM参数    → 内存可控,避免OOM
✅ 依赖缓存   → 加速重复构建

6.4 .dockerignore 文件

.git
.gitignore
.idea
*.iml
*.md
.DS_Store
target/*.jar.original

七、构建Docker镜像

7.1 上传文件到服务器

# 方式一:scp上传整个项目(使用生产级Dockerfile时)
scp -r ./docker-demo root@your-server-ip:/opt/

# 方式二:仅上传jar包和Dockerfile(使用基础版Dockerfile时)
scp target/docker-demo.jar root@your-server-ip:/opt/docker-demo/
scp Dockerfile root@your-server-ip:/opt/docker-demo/

7.2 构建镜像

cd /opt/docker-demo

# 构建镜像(注意最后有个点 ".")
docker build -t docker-demo:1.0.0 .

构建过程输出:

[+] Building 45.2s (12/12) FINISHED
 => [internal] load build definition from Dockerfile          0.0s
 => [internal] load .dockerignore                             0.0s
 => [builder 1/5] FROM maven:3.9-eclipse-temurin-17          10.2s
 => [builder 2/5] WORKDIR /build                              0.1s
 => [builder 3/5] COPY pom.xml .                              0.1s
 => [builder 4/5] RUN mvn dependency:go-offline -B           20.5s
 => [builder 5/5] COPY src ./src                              0.1s
 => [builder 6/6] RUN mvn clean package -DskipTests -B        8.3s
 => [stage-1 1/4] FROM eclipse-temurin:17-jre-alpine          3.2s
 => [stage-1 2/4] COPY --from=builder ...                     0.1s
 => exporting to image                                         0.3s
 => => naming to docker.io/library/docker-demo:1.0.0          0.0s

✅ 构建成功!

7.3 查看镜像

docker images

# 输出:
REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
docker-demo    1.0.0     a1b2c3d4e5f6   30 seconds ago   198MB

💡 多阶段构建后镜像仅 198MB,而单阶段构建通常在 500-700MB


八、运行Docker容器

8.1 基础运行

docker run -d \
  --name docker-demo \
  -p 8080:8080 \
  docker-demo:1.0.0

参数说明:

-d              后台运行
--name          容器名称
-p 8080:8080    端口映射(宿主机端口:容器端口)
docker-demo:1.0.0   镜像名:标签

8.2 ⭐ 生产级运行命令

docker run -d \
  --name docker-demo \
  --restart=always \
  -p 8080:8080 \
  -v /opt/docker-demo/logs:/app/logs \
  -v /etc/localtime:/etc/localtime:ro \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e JAVA_OPTS="-Xms512m -Xmx1024m" \
  --memory=1024m \
  --cpus=1.0 \
  docker-demo:1.0.0

参数详解:

--restart=always          容器异常退出自动重启
-v .../logs:/app/logs     日志目录挂载到宿主机(持久化)
-v /etc/localtime:...     同步宿主机时区(解决时间差8小时问题)
-e SPRING_PROFILES_ACTIVE 指定Spring运行环境
-e JAVA_OPTS              覆盖JVM参数
--memory=1024m            限制容器最大内存
--cpus=1.0                限制CPU使用

8.3 验证部署

# 1. 查看容器状态
docker ps

# 输出:
CONTAINER ID   IMAGE              STATUS          PORTS                    NAMES
f7a8b9c0d1e2   docker-demo:1.0.0  Up 30 seconds   0.0.0.0:8080->8080/tcp   docker-demo
# 2. 查看启动日志
docker logs -f docker-demo

# 输出:
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.1)

2024-12-20 10:30:00 [main] INFO  c.e.d.DockerDemoApplication - Started in 2.345 seconds
# 3. 测试接口
curl http://localhost:8080/hello

# 输出:
{"code":200,"message":"Docker部署SpringBoot成功!","time":"2024-12-20T10:30:15"}
# 4. 健康检查
docker inspect --format='{{.State.Health.Status}}' docker-demo
# 输出:healthy

✅ 部署成功! 外网访问:http://服务器公网IP:8080/hello


九、Docker Compose编排(进阶)

实际项目通常需要 SpringBoot + MySQL + Redis 一起部署。

9.1 项目结构

docker-demo/
├── docker-compose.yml      ← 编排文件
├── Dockerfile
├── src/
├── pom.xml
└── config/
    └── mysql/
        └── init.sql        ← 数据库初始化脚本

9.2 docker-compose.yml

version: '3.8'

services:
  # ==================== SpringBoot应用 ====================
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: docker-demo-app
    restart: always
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/demo_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=root123456
      - SPRING_REDIS_HOST=redis
      - SPRING_REDIS_PORT=6379
      - SPRING_REDIS_PASSWORD=redis123456
      - JAVA_OPTS=-Xms512m -Xmx1024m
    volumes:
      - ./logs:/app/logs
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - app-network
    deploy:
      resources:
        limits:
          memory: 1024M
          cpus: '1.0'

  # ==================== MySQL ====================
  mysql:
    image: mysql:8.0
    container_name: docker-demo-mysql
    restart: always
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root123456
      - MYSQL_DATABASE=demo_db
      - MYSQL_CHARSET=utf8mb4
      - TZ=Asia/Shanghai
    volumes:
      - mysql-data:/var/lib/mysql
      - ./config/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
    command: --default-authentication-plugin=mysql_native_password
             --character-set-server=utf8mb4
             --collation-server=utf8mb4_unicode_ci
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot123456"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - app-network

  # ==================== Redis ====================
  redis:
    image: redis:7-alpine
    container_name: docker-demo-redis
    restart: always
    ports:
      - "6379:6379"
    command: redis-server --requirepass redis123456 --appendonly yes
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "redis123456", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-network

# ==================== 数据卷 ====================
volumes:
  mysql-data:
    driver: local
  redis-data:
    driver: local

# ==================== 网络 ====================
networks:
  app-network:
    driver: bridge

9.3 数据库初始化脚本

-- config/mysql/init.sql

CREATE TABLE IF NOT EXISTS `user` (
    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
    `username` VARCHAR(50) NOT NULL COMMENT '用户名',
    `email` VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

INSERT INTO `user` (`username`, `email`) VALUES
('admin', 'admin@example.com'),
('test', 'test@example.com');

9.4 application-prod.yml

server:
  port: 8080

spring:
  datasource:
    url: ${SPRING_DATASOURCE_URL}
    username: ${SPRING_DATASOURCE_USERNAME}
    password: ${SPRING_DATASOURCE_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5

  data:
    redis:
      host: ${SPRING_REDIS_HOST}
      port: ${SPRING_REDIS_PORT}
      password: ${SPRING_REDIS_PASSWORD}
      timeout: 5000ms

9.5 一键启动

# 构建并启动所有服务
docker-compose up -d --build

# 查看所有服务状态
docker-compose ps

# 输出:
NAME                  STATUS                   PORTS
docker-demo-app       Up 30 seconds (healthy)  0.0.0.0:8080->8080/tcp
docker-demo-mysql     Up 45 seconds (healthy)  0.0.0.0:3306->3306/tcp
docker-demo-redis     Up 45 seconds (healthy)  0.0.0.0:6379->6379/tcp
# 查看应用日志
docker-compose logs -f app

# 停止所有服务
docker-compose down

# 停止并删除数据卷(⚠️ 会删除数据库数据)
docker-compose down -v

十、常见问题与排坑

❌ 问题1:容器启动后立即退出

# 查看退出日志
docker logs docker-demo

# 常见原因:
# 1. jar包损坏 → 重新打包
# 2. JDK版本不匹配 → 检查Dockerfile中的基础镜像版本
# 3. 端口被占用 → netstat -tlnp | grep 8080

❌ 问题2:时区差8小时

# 解决方案一:挂载宿主机时区
-v /etc/localtime:/etc/localtime:ro

# 解决方案二:设置环境变量
-e TZ=Asia/Shanghai

# 解决方案三:Dockerfile中设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

❌ 问题3:容器内无法连接MySQL/Redis

# 错误示例:使用localhost
spring.datasource.url=jdbc:mysql://localhost:3306/demo_db  ❌

# 正确写法:使用服务名(docker-compose中定义的service名)
spring.datasource.url=jdbc:mysql://mysql:3306/demo_db  ✅

# 或使用宿主机IP
spring.datasource.url=jdbc:mysql://172.17.0.1:3306/demo_db  ✅

❌ 问题4:镜像体积过大

# 查看镜像大小
docker images

# 优化方案对比:
# openjdk:17           → 471MB ❌
# openjdk:17-jdk-slim  → 409MB 
# eclipse-temurin:17-jre-alpine → 198MB ✅(推荐)

# 使用多阶段构建进一步优化(见第六节生产级Dockerfile)

❌ 问题5:构建缓存失效导致速度慢

# 优化技巧:先复制pom.xml下载依赖,再复制源码
# 这样只要pom.xml不变,依赖层就会使用缓存

COPY pom.xml .
RUN mvn dependency:go-offline    ← 这一层会被缓存
COPY src ./src
RUN mvn package                  ← 只有源码变化才重新构建

十一、常用Docker运维命令速查表

# ==================== 镜像操作 ====================
docker images                           # 查看所有镜像
docker build -t name:tag .              # 构建镜像
docker rmi image_id                     # 删除镜像
docker image prune -a                   # 清理无用镜像

# ==================== 容器操作 ====================
docker ps                               # 查看运行中的容器
docker ps -a                            # 查看所有容器(含停止的)
docker start container_name             # 启动容器
docker stop container_name              # 停止容器
docker restart container_name           # 重启容器
docker rm container_name                # 删除容器
docker rm -f $(docker ps -aq)           # ⚠️ 删除所有容器

# ==================== 日志与调试 ====================
docker logs -f container_name           # 实时查看日志
docker logs --tail 100 container_name   # 查看最后100行日志
docker exec -it container_name /bin/sh  # 进入容器
docker stats                            # 查看资源占用
docker inspect container_name           # 查看容器详情

# ==================== Docker Compose ====================
docker-compose up -d                    # 后台启动
docker-compose up -d --build            # 重新构建并启动
docker-compose down                     # 停止并删除
docker-compose logs -f service_name     # 查看某个服务日志
docker-compose restart service_name     # 重启某个服务
docker-compose ps                       # 查看服务状态

十二、完整部署流程总结

┌─────────────────────────────────────────────────────┐
│                    本地开发环境                        │
│                                                       │
│   1. 编写代码 → 2. 本地测试 → 3. mvn clean package    │
│                                                       │
└─────────────────┬───────────────────────────────────┘
                  │ 上传到服务器
                  ▼
┌─────────────────────────────────────────────────────┐
│                    服务器环境                          │
│                                                       │
│   4. 编写Dockerfile                                   │
│           │                                           │
│           ▼                                           │
│   5. docker build -t app:1.0.0 .                      │
│           │                                           │
│           ▼                                           │
│   6. docker run -d -p 8080:8080 app:1.0.0             │
│           │                                           │
│           ▼                                           │
│   7. 访问 http://IP:8080/hello  ✅                    │
│                                                       │
└─────────────────────────────────────────────────────┘

写在最后

本文从零开始,完整介绍了Docker部署SpringBoot项目的全过程:

  • ✅ 基础部署:Dockerfile + docker run
  • ✅ 生产优化:多阶段构建、非root用户、健康检查、JVM调优
  • ✅ 编排部署:Docker Compose管理 SpringBoot + MySQL + Redis
  • ✅ 排坑指南:时区、网络、镜像体积等常见问题

📌 觉得有用的话,记得点赞👍收藏⭐关注🔔三连!有问题欢迎评论区交流!


推荐阅读:

  • 《Nginx反向代理+Docker部署SpringBoot实现HTTPS》
  • 《Jenkins+Docker实现SpringBoot自动化部署(CI/CD)》
  • 《Docker部署微服务架构全流程实战》
  • 《Kubernetes(K8s)部署SpringBoot进阶指南》
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐