Docker 部署 Spring Boot 项目完整指南:从零到生产环境

前言

在当今的软件开发领域,容器化技术已经成为应用部署的标准实践。Docker 作为容器技术的领导者,彻底改变了应用的打包、分发和运行方式。本指南将详细讲解如何使用 Docker 容器化部署 Java Spring Boot 项目,涵盖从基础概念到生产环境部署的完整流程。

第一章:Docker 基础概念与优势

1.1 为什么选择 Docker?

在传统部署方式中,我们常常遇到以下问题:

  • 环境不一致:开发、测试、生产环境差异导致"在我机器上能运行"的问题
  • 依赖冲突:不同应用需要不同版本的运行时环境
  • 部署复杂:需要手动安装配置各种依赖和服务
  • 资源浪费:每个应用独占完整操作系统资源

Docker 通过容器化技术解决了这些问题:

  • 一致性:确保应用在任何环境运行一致
  • 隔离性:应用运行在独立容器中,互不干扰
  • 轻量级:容器共享主机操作系统内核,资源占用少
  • 可移植性:一次构建,随处运行

1.2 Docker 核心概念

  • 镜像(Image) :只读模板,包含运行应用所需的所有内容
  • 容器(Container) :镜像的运行实例,是真正的执行环境
  • 仓库(Registry) :存储和分发镜像的地方(如 Docker Hub)
  • Dockerfile:文本文件,包含构建镜像的指令

第二章:环境准备与安装

2.1 系统要求

  • Linux(Ubuntu 18.04+、CentOS 7+)、macOS 或 Windows 10/11
  • 至少 2GB RAM(建议 4GB 以上)
  • 10GB 可用磁盘空间

2.2 Docker 安装

Ubuntu/Debian 系统安装:

bash 复制代码
# 1. 卸载旧版本(如果存在)
sudo apt-get remove docker docker-engine docker.io containerd runc

# 2. 更新 apt 包索引
sudo apt-get update

# 3. 安装依赖包
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# 4. 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 5. 设置稳定版仓库
echo \
  "deb [arch=$(dpkg --print-architecture) 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

# 6. 更新 apt 包索引
sudo apt-get update

# 7. 安装 Docker Engine
sudo apt-get install docker-ce docker-ce-cli containerd.io

# 8. 验证安装是否成功
sudo docker --version

# 9. 启动 Docker 服务
sudo systemctl start docker
sudo systemctl enable docker

# 10. 测试运行 Hello World
sudo docker run hello-world

CentOS/RHEL 系统安装:

bash 复制代码
# 1. 卸载旧版本
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

# 2. 安装必要的包
sudo yum install -y yum-utils

# 3. 设置仓库
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

# 4. 安装 Docker Engine
sudo yum install docker-ce docker-ce-cli containerd.io

# 5. 启动 Docker
sudo systemctl start docker
sudo systemctl enable docker

# 6. 验证安装
sudo docker --version
sudo docker run hello-world

2.3 配置非 root 用户运行 Docker(可选但推荐)

bash 复制代码
# 创建 docker 用户组(如果不存在)
sudo groupadd docker

# 将当前用户添加到 docker 组
sudo usermod -aG docker $USER

# 重新登录或使用以下命令立即生效
newgrp docker

# 验证非 root 用户是否能运行 Docker 命令
docker run hello-world

2.4 安装 Docker Compose

bash 复制代码
# 下载最新版本的 Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.17.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 赋予执行权限
sudo chmod +x /usr/local/bin/docker-compose

# 创建软链接(如果需要)
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

# 验证安装
docker-compose --version

第三章:准备 Spring Boot 项目

3.1 创建示例 Spring Boot 项目

首先,我们创建一个简单的 Spring Boot 项目用于演示:

pom.xml(Maven 配置):

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>2.7.5</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>docker-demo</artifactId>
    <version>1.0.0</version>
    <name>docker-demo</name>
    <description>Spring Boot Docker Demo Project</description>
    
    <properties>
        <java.version>11</java.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

项目结构:

Kotlin 复制代码
docker-demo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── dockerdemo/
│   │   │               ├── DockerDemoApplication.java
│   │   │               ├── controller/
│   │   │               │   └── HelloController.java
│   │   │               └── service/
│   │ │                   └── HelloService.java
│   │   └── resources/
│   │       ├── application.properties
│   │       └── static/
│   └── test/
├── Dockerfile
├── docker-compose.yml
└── pom.xml

HelloController.java:

java 复制代码
package com.example.dockerdemo.controller;

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

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

@RestController
@RequestMapping("/api")
public class HelloController {
    
    @GetMapping("/hello")
    public Map<String, Object> hello() {
        Map<String, Object> response = new HashMap<>();
        response.put("message", "Hello from Docker!");
        response.put("timestamp", LocalDateTime.now().toString());
        response.put("status", "success");
        return response;
    }
    
    @GetMapping("/health")
    public Map<String, Object> health() {
        Map<String, Object> response = new HashMap<>();
        response.put("status", "UP");
        response.put("service", "docker-demo");
        response.put("timestamp", System.currentTimeMillis());
        return response;
    }
}

application.properties:

XML 复制代码
# 应用配置
server.port=8080
server.servlet.context-path=/

# 应用信息
spring.application.name=docker-demo

# Actuator 配置
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always

# 日志配置
logging.level.com.example.dockerdemo=DEBUG
logging.file.name=/var/log/docker-demo/app.log

3.2 构建项目 JAR 包

javascript 复制代码
# 进入项目根目录
cd docker-demo

# 使用 Maven 打包项目
mvn clean package

# 或者使用 Maven Wrapper(如果项目中有 mvnw)
./mvnw clean package

# 打包成功后,在 target 目录下会生成 JAR 文件
# docker-demo-1.0.0.jar

第四章:编写 Dockerfile

4.1 Dockerfile 基础结构

Dockerfile 是一个文本文件,包含了一系列构建 Docker 镜像的指令。每条指令都会在镜像中创建一个新的层。

4.2 完整 Dockerfile 示例

在项目根目录创建 Dockerfile(注意没有文件扩展名):

javascript 复制代码
# 第一阶段:构建阶段
# 使用官方 Maven 镜像作为构建环境
FROM maven:3.8.4-openjdk-11-slim AS builder

# 设置工作目录
WORKDIR /app

# 复制 pom.xml 和源代码
COPY pom.xml .
COPY src ./src

# 下载依赖(利用 Docker 缓存层,如果 pom.xml 不变则不重新下载)
RUN mvn dependency:go-offline -B

# 构建应用(跳过测试以加快构建速度)
RUN mvn clean package -DskipTests

# 第二阶段:运行阶段
# 使用更小的 JRE 镜像作为运行环境
FROM openjdk:11-jre-slim

# 设置元数据标签(可选)
LABEL maintainer="your-email@example.com"
LABEL version="1.0"
LABEL description="Spring Boot application in Docker"

# 设置时区(根据需求调整)
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 创建一个非 root 用户运行应用(安全最佳实践)
RUN groupadd -r spring && useradd -r -g spring spring
USER spring

# 设置工作目录
WORKDIR /app

# 从构建阶段复制构建好的 JAR 文件
COPY --from=builder /app/target/docker-demo-*.jar app.jar

# 创建日志目录并设置权限
RUN mkdir -p /var/log/docker-demo && \
    chown -R spring:spring /var/log/docker-demo

# 暴露应用端口
EXPOSE 8080

# 设置 JVM 参数(根据实际情况调整)
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/docker-demo/heapdump.hprof"

# 启动应用
# 使用 exec 形式启动,确保 Java 进程成为 PID 1,能正确处理信号
ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar

# 健康检查指令
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:8080/api/health || exit 1

4.3 Dockerfile 指令详解

  1. FROM:指定基础镜像

    • 使用多阶段构建:第一阶段用于构建,第二阶段用于运行
    • 选择合适的基础镜像:官方镜像、版本固定、轻量级
  2. WORKDIR:设置工作目录

    • 后续指令都在此目录下执行
    • 如果目录不存在会自动创建
  3. COPY:复制文件到镜像

    • COPY pom.xml .:复制单个文件
    • COPY src ./src:复制目录
    • COPY --from=builder:从上一构建阶段复制文件
  4. RUN:执行命令

    • 在镜像构建过程中执行
    • 每条 RUN 指令都会创建一个新的镜像层
  5. ENV:设置环境变量

    • 在镜像中设置环境变量
    • 可以被后续指令和容器运行时使用
  6. USER:指定运行用户

    • 安全最佳实践:不使用 root 用户运行应用
  7. EXPOSE:声明端口

    • 仅声明容器运行时监听的端口
    • 实际映射由 docker rundocker-compose 处理
  8. ENTRYPOINT:容器启动命令

    • 容器启动时执行的命令
    • 使用 exec 形式确保信号正确传递
  9. HEALTHCHECK:健康检查

    • 定期检查容器健康状态
    • 帮助 Docker 和编排系统了解应用状态

4.4 优化 Dockerfile 的技巧

  1. 利用缓存:将不常变化的指令放在前面
  2. 多阶段构建:减小最终镜像大小
  3. 使用 .dockerignore:排除不需要的文件
  4. 合并 RUN 指令:减少镜像层数

创建 .dockerignore 文件:

复制代码
# 忽略所有以 . 开头的文件
.*
# 忽略 IDE 配置文件
.vscode/
.idea/
*.iml
# 忽略构建输出
target/
build/
out/
# 忽略日志文件
*.log
logs/
# 忽略临时文件
*.tmp
*.temp
tmp/
temp/
# 忽略 Git 相关
.git/
.gitignore
# 忽略 Docker 相关
Dockerfile*
docker-compose*
.dockerignore
# 忽略文档
*.md
README

第五章:构建 Docker 镜像

5.1 基础构建命令

bash 复制代码
# 进入项目根目录(确保 Dockerfile 在此目录)
cd /path/to/docker-demo

# 构建镜像
# -t: 给镜像打标签(名称:版本)
# .: 构建上下文路径(当前目录)
docker build -t docker-demo:1.0.0 .

# 查看构建过程输出
# 可以看到每一层的构建情况

5.2 详细的构建过程

让我们分解构建过程:

bash 复制代码
# 1. 验证 Dockerfile 语法
# 可以使用 docker build --dry-run 验证语法(需要 buildx)
docker buildx build --dry-run .

# 2. 实际构建镜像(详细输出)
docker build -t docker-demo:1.0.0 --progress=plain .

# 3. 查看构建的镜像
docker images

# 输出示例:
# REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
# docker-demo   1.0.0     abc123def456   2 minutes ago    215MB

5.3 镜像构建优化

bash 复制代码
# 1. 使用缓存加速构建
# 如果依赖没有变化,Docker 会使用缓存
docker build -t docker-demo:1.0.0 .

# 2. 不使用缓存(强制重新构建所有层)
docker build -t docker-demo:1.0.0 --no-cache .

# 3. 指定目标构建阶段
# 多阶段构建时,可以指定只构建到某个阶段
docker build -t docker-demo:builder --target builder .

# 4. 清理构建缓存
docker builder prune

# 5. 查看镜像构建历史
docker history docker-demo:1.0.0

5.4 镜像标签管理

bash 复制代码
# 给镜像添加多个标签
docker build -t docker-demo:1.0.0 -t docker-demo:latest .

# 给现有镜像添加新标签
docker tag docker-demo:1.0.0 docker-demo:stable

# 查看所有镜像
docker images

# 删除镜像
docker rmi docker-demo:1.0.0

# 强制删除镜像(即使有容器使用)
docker rmi -f docker-demo:1.0.0

# 清理所有未使用的镜像
docker image prune -a

第六章:运行 Docker 容器

6.1 基础运行命令

bash 复制代码
# 最基本的方式运行容器
docker run docker-demo:1.0.0

# 但这会占用终端,且容器停止后会自动删除

6.2 完整的运行配置

bash 复制代码
# 完整的容器运行命令
docker run -d \
  --name docker-demo-app \
  -p 8080:8080 \
  -e "SPRING_PROFILES_ACTIVE=prod" \
  -v /path/to/logs:/var/log/docker-demo \
  --restart unless-stopped \
  --memory=512m \
  --cpus="1.0" \
  --health-cmd="curl -f http://localhost:8080/api/health || exit 1" \
  --health-interval=30s \
  --health-timeout=3s \
  --health-retries=3 \
  docker-demo:1.0.0

参数解释:

  1. -d:后台运行(detached mode)
  2. --name:为容器指定名称
  3. -p:端口映射(主机端口:容器端口)
  4. -e:设置环境变量
  5. -v:卷挂载(主机目录:容器目录)
  6. --restart:重启策略
  7. --memory:内存限制
  8. --cpus:CPU 限制
  9. --health-*:健康检查配置

6.3 容器管理命令

bash 复制代码
# 查看运行中的容器
docker ps

# 查看所有容器(包括停止的)
docker ps -a

# 查看容器日志
docker logs docker-demo-app

# 实时查看日志
docker logs -f docker-demo-app

# 查看最后 N 行日志
docker logs --tail 100 docker-demo-app

# 查看容器资源使用情况
docker stats docker-demo-app

# 进入容器内部(交互式 shell)
docker exec -it docker-demo-app /bin/bash

# 在容器内执行单个命令
docker exec docker-demo-app ls -la /app

# 停止容器
docker stop docker-demo-app

# 启动已停止的容器
docker start docker-demo-app

# 重启容器
docker restart docker-demo-app

# 删除容器(必须先停止)
docker rm docker-demo-app

# 强制删除运行中的容器
docker rm -f docker-demo-app

# 查看容器详细信息
docker inspect docker-demo-app

# 查看容器端口映射
docker port docker-demo-app

# 复制文件到容器
docker cp localfile.txt docker-demo-app:/app/

# 从容器复制文件
docker cp docker-demo-app:/app/logs/app.log ./

6.4 网络配置

bash 复制代码
# 创建自定义网络(推荐)
docker network create app-network

# 查看网络列表
docker network ls

# 在自定义网络中运行容器
docker run -d \
  --name docker-demo-app \
  --network app-network \
  docker-demo:1.0.0

# 查看网络详情
docker network inspect app-network

# 连接容器到网络
docker network connect app-network docker-demo-app

# 断开容器与网络的连接
docker network disconnect app-network docker-demo-app

# 删除网络
docker network rm app-network

6.5 数据持久化

bash 复制代码
# 1. 使用绑定挂载(bind mount)
docker run -d \
  -v /host/path/logs:/var/log/docker-demo \
  docker-demo:1.0.0

# 2. 使用 Docker 卷(volume)
# 创建卷
docker volume create app-logs

# 使用卷
docker run -d \
  -v app-logs:/var/log/docker-demo \
  docker-demo:1.0.0

# 查看卷列表
docker volume ls

# 查看卷详情
docker volume inspect app-logs

# 清理未使用的卷
docker volume prune

# 3. 使用临时文件系统(tmpfs)
docker run -d \
  --tmpfs /tmp \
  docker-demo:1.0.0

第七章:使用 Docker Compose 编排

7.1 为什么需要 Docker Compose?

  • 管理多个容器的启动顺序和依赖关系
  • 简化复杂的容器配置
  • 方便本地开发环境搭建
  • 支持环境变量和配置文件管理

7.2 创建 docker-compose.yml

javascript 复制代码
version: '3.8'

# 定义网络(所有服务共享)
networks:
  app-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

# 定义卷
volumes:
  app-logs:
    driver: local
  mysql-data:
    driver: local

# 定义服务
services:
  # Spring Boot 应用服务
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - BUILD_VERSION=${BUILD_VERSION:-1.0.0}
    image: docker-demo:${BUILD_VERSION:-1.0.0}
    container_name: docker-demo-app
    restart: unless-stopped
    ports:
      - "8080:8080"
      - "8081:8081"  # 管理端口(如果需要)
    environment:
      - SPRING_PROFILES_ACTIVE=${PROFILE:-dev}
      - JAVA_OPTS=${JAVA_OPTS:--Xms256m -Xmx512m}
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_NAME=docker_demo
      - DB_USER=${DB_USER:-root}
      - DB_PASSWORD=${DB_PASSWORD:-password}
    volumes:
      - app-logs:/var/log/docker-demo
      - ./config:/app/config:ro  # 只读挂载配置文件
    networks:
      - app-network
    depends_on:
      mysql:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
        reservations:
          memory: 256M
          cpus: '0.5'
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    # 开发模式配置(覆盖默认)
    profiles: ["dev"]
    ports:
      - "8080:8080"
      - "5005:5005"  # 远程调试端口
    environment:
      - JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Xms256m -Xmx512m

  # MySQL 数据库服务
  mysql:
    image: mysql:8.0
    container_name: docker-demo-mysql
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-password}
      - MYSQL_DATABASE=docker_demo
      - MYSQL_USER=${DB_USER:-appuser}
      - MYSQL_PASSWORD=${DB_PASSWORD:-password}
    volumes:
      - mysql-data:/var/lib/mysql
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
      - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf:ro
    networks:
      - app-network
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
    command: 
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci

  # Redis 缓存服务
  redis:
    image: redis:7-alpine
    container_name: docker-demo-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - app-network
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-redispass}
    healthcheck:
      test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3

  # Nginx 反向代理
  nginx:
    image: nginx:1.21-alpine
    container_name: docker-demo-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - ./nginx/logs:/var/log/nginx
    networks:
      - app-network
    depends_on:
      - app
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 10s
      retries: 3

  # 监控服务(Prometheus + Grafana)
  monitoring:
    image: prom/prometheus:latest
    container_name: docker-demo-prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    networks:
      - app-network
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'

  grafana:
    image: grafana/grafana:latest
    container_name: docker-demo-grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
    networks:
      - app-network
    depends_on:
      - monitoring

7.3 创建环境变量文件

创建 .env 文件(不会被提交到版本控制):

复制代码
# 应用配置
BUILD_VERSION=1.0.0
PROFILE=prod
JAVA_OPTS=-Xms256m -Xmx512m

# 数据库配置
DB_USER=appuser
DB_PASSWORD=StrongPassword123!
DB_NAME=docker_demo

# Redis 配置
REDIS_PASSWORD=RedisPass123!

# Grafana 配置
GRAFANA_PASSWORD=GrafanaAdmin123!

7.4 Docker Compose 命令

bash 复制代码
# 1. 构建并启动所有服务(后台运行)
docker-compose up -d

# 2. 构建镜像(不启动)
docker-compose build

# 3. 启动已存在的服务
docker-compose start

# 4. 停止所有服务
docker-compose stop

# 5. 停止并删除所有容器、网络
docker-compose down

# 6. 停止并删除所有容器、网络、卷
docker-compose down -v

# 7. 查看服务状态
docker-compose ps

# 8. 查看服务日志
docker-compose logs

# 9. 查看特定服务日志
docker-compose logs app

# 10. 实时查看日志
docker-compose logs -f app

# 11. 进入容器
docker-compose exec app /bin/bash

# 12. 在服务上执行命令
docker-compose exec app ls -la

# 13. 重启服务
docker-compose restart app

# 14. 扩展服务实例数
docker-compose up -d --scale app=3

# 15. 拉取服务镜像
docker-compose pull

# 16. 查看服务配置
docker-compose config

# 17. 暂停服务
docker-compose pause app

# 18. 恢复暂停的服务
docker-compose unpause app

# 19. 使用特定环境文件
docker-compose --env-file .env.production up -d

# 20. 使用特定配置文件
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# 21. 只启动部分服务
docker-compose up -d app mysql

# 22. 重新构建服务
docker-compose up -d --build app

# 23. 清理未使用的资源
docker-compose down --rmi local

7.5 多环境配置

创建 docker-compose.override.yml(用于开发环境):

javascript 复制代码
version: '3.8'

services:
  app:
    ports:
      - "8080:8080"
      - "5005:5005"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Xms256m -Xmx512m
    volumes:
      - ./src:/app/src

创建 docker-compose.prod.yml(用于生产环境):

javascript 复制代码
version: '3.8'

services:
  app:
    restart: always
    deploy:
      resources:
        limits:
          memory: 1g
          cpus: '2.0'
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS=-Xms512m -Xmx1024m -XX:+UseG1GC

第八章:生产环境部署实践

8.1 安全最佳实践

bash 复制代码
# 1. 使用非 root 用户运行容器
# 在 Dockerfile 中已设置
USER spring

# 2. 扫描镜像安全漏洞
docker scan docker-demo:1.0.0

# 3. 使用内容信任
export DOCKER_CONTENT_TRUST=1
docker build -t docker-demo:1.0.0 .

# 4. 限制容器能力
docker run -d \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  docker-demo:1.0.0

# 5. 设置只读根文件系统
docker run -d \
  --read-only \
  --tmpfs /tmp \
  -v app-logs:/var/log/docker-demo \
  docker-demo:1.0.0

8.2 日志管理

javascript 复制代码
# docker-compose.yml 中的日志配置
logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"
    compress: "true"
    labels: "production"
    env: "os,customer"
bash 复制代码
# 查看日志驱动
docker info --format '{{.LoggingDriver}}'

# 使用日志驱动
docker run -d \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  docker-demo:1.0.0

# 使用 syslog 驱动
docker run -d \
  --log-driver syslog \
  --log-opt syslog-address=udp://syslog-server:514 \
  docker-demo:1.0.0

8.3 资源限制与监控

bash 复制代码
# 设置资源限制
docker run -d \
  --memory="512m" \
  --memory-swap="1g" \
  --cpus="1.5" \
  --cpu-shares="1024" \
  --blkio-weight="500" \
  docker-demo:1.0.0

# 查看容器资源使用
docker stats docker-demo-app

# 设置容器重启策略
docker run -d \
  --restart always \		# 总是重启
  --restart on-failure \	# 失败时重启
  --restart unless-stopped \	# 除非手动停止,否则重启
  docker-demo:1.0.0

8.4 镜像仓库与持续集成

bash 复制代码
# 登录 Docker Hub
docker login

# 给镜像打标签(用于推送到仓库)
docker tag docker-demo:1.0.0 yourusername/docker-demo:1.0.0
docker tag docker-demo:1.0.0 yourusername/docker-demo:latest

# 推送到 Docker Hub
docker push yourusername/docker-demo:1.0.0
docker push yourusername/docker-demo:latest

# 从仓库拉取镜像
docker pull yourusername/docker-demo:1.0.0

# 使用私有仓库
docker tag docker-demo:1.0.0 registry.example.com/yourproject/docker-demo:1.0.0
docker push registry.example.com/yourproject/docker-demo:1.0.0

# 配置私有仓库(非安全)
docker run -d \
  -p 5000:5000 \
  --name registry \
  registry:2

# 推送镜像到私有仓库
docker tag docker-demo:1.0.0 localhost:5000/docker-demo:1.0.0
docker push localhost:5000/docker-demo:1.0.0

8.5 备份与恢复

bash 复制代码
# 备份容器数据
docker run --rm \
  --volumes-from docker-demo-app \
  -v $(pwd):/backup \
  ubuntu tar czf /backup/app-data-$(date +%Y%m%d).tar.gz /var/log/docker-demo

# 备份 Docker 卷
docker run --rm \
  -v app-logs:/data \
  -v $(pwd):/backup \
  ubuntu tar czf /backup/app-logs-$(date +%Y%m%d).tar.gz /data

# 导出容器
docker export docker-demo-app > docker-demo-container.tar

# 导入容器
cat docker-demo-container.tar | docker import - docker-demo:imported

# 保存镜像为文件
docker save -o docker-demo-1.0.0.tar docker-demo:1.0.0

# 从文件加载镜像
docker load -i docker-demo-1.0.0.tar

# 创建数据卷容器(用于数据共享)
docker create -v /var/log/docker-demo \
  --name app-data \
  busybox /bin/true

# 使用数据卷容器
docker run -d \
  --volumes-from app-data \
  docker-demo:1.0.0

第九章:常见问题与解决方案

9.1 构建问题

问题1:构建速度慢

bash 复制代码
# 解决方案:使用构建缓存和镜像加速器

# 1. 配置 Docker 镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com",
    "https://registry.docker-cn.com"
  ]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

# 2. 使用构建缓存
docker build -t docker-demo:1.0.0 --cache-from docker-demo:latest .

问题2:镜像过大

复制代码
# 解决方案:使用多阶段构建和轻量级基础镜像

# 使用 alpine 版本的镜像
FROM openjdk:11-jre-slim  # 而不是 openjdk:11

# 或者使用更小的发行版
FROM openjdk:11-jre-alpine

9.2 运行问题

问题1:应用启动后立即退出

bash 复制代码
# 解决方案:检查日志,确保应用在前台运行

# 错误的 CMD:
CMD java -jar app.jar &

# 正确的 CMD:
CMD ["java", "-jar", "app.jar"]
# 或使用 ENTRYPOINT:
ENTRYPOINT ["java", "-jar", "app.jar"]

问题2:端口被占用

bash 复制代码
# 解决方案:更改端口或停止占用进程

# 检查端口占用
sudo netstat -tlnp | grep :8080

# 停止占用进程
sudo kill <PID>

# 或者使用不同端口
docker run -d -p 8081:8080 docker-demo:1.0.0

9.3 网络问题

问题:容器间无法通信

bash 复制代码
# 解决方案:使用自定义网络

# 创建网络
docker network create my-network

# 将容器连接到同一网络
docker run -d --network my-network --name app1 docker-demo:1.0.0
docker run -d --network my-network --name app2 docker-demo:1.0.0

# 在容器内部可以通过容器名访问

# 从 app1 访问 app2
docker exec app1 curl http://app2:8080/api/health

9.4 数据持久化问题

问题:容器重启后数据丢失

bash 复制代码
# 解决方案:使用数据卷

# 创建命名卷
docker volume create app-data

# 使用卷
docker run -d \
  -v app-data:/app/data \
  docker-demo:1.0.0

# 或者使用绑定挂载
docker run -d \
  -v /host/path/data:/app/data \
  docker-demo:1.0.0

第十章:进阶部署方案

10.1 使用 Docker Swarm(集群部署)

bash 复制代码
# 初始化 Swarm 集群
docker swarm init

# 查看节点
docker node ls

# 创建 overlay 网络
docker network create --driver overlay app-network

# 部署服务栈
docker stack deploy -c docker-compose.yml docker-demo

# 查看服务
docker service ls

# 查看服务详情
docker service ps docker-demo_app

# 扩展服务
docker service scale docker-demo_app=3

# 更新服务
docker service update --image docker-demo:2.0.0 docker-demo_app

# 回滚服务
docker service update --rollback docker-demo_app

# 删除服务栈
docker stack rm docker-demo

10.2 使用 Kubernetes(生产级编排)

创建 deployment.yaml

javascript 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: docker-demo-deployment
  labels:
    app: docker-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: docker-demo
  template:
    metadata:
      labels:
        app: docker-demo
    spec:
      containers:
      - name: docker-demo
        image: yourusername/docker-demo:1.0.0
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "250m"
        livenessProbe:
          httpGet:
            path: /api/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /api/health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: docker-demo-service
spec:
  selector:
    app: docker-demo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer

10.3 CI/CD 流水线示例

.gitlab-ci.yml 示例:

javascript 复制代码
stages:
  - build
  - test
  - dockerize
  - deploy

variables:
  DOCKER_IMAGE: registry.example.com/yourproject/docker-demo:$CI_COMMIT_REF_SLUG
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

cache:
  paths:
    - .m2/repository/
    - target/

build:
  stage: build
  image: maven:3.8.4-openjdk-11
  script:
    - mvn clean compile

test:
  stage: test
  image: maven:3.8.4-openjdk-11
  script:
    - mvn test

docker-build:
  stage: dockerize
  image: docker:20.10
  services:
    - docker:20.10-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE

deploy:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache openssh-client
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - ssh-keyscan $DEPLOY_SERVER >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - |
      ssh $DEPLOY_USER@$DEPLOY_SERVER "
        docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY &&
        docker pull $DOCKER_IMAGE &&
        docker stop docker-demo-app || true &&
        docker rm docker-demo-app || true &&
        docker run -d \
          --name docker-demo-app \
          -p 8080:8080 \
          --restart unless-stopped \
          $DOCKER_IMAGE
      "
  only:
    - master

总结

通过本文的详细讲解,您应该已经掌握了使用 Docker 部署 Spring Boot 项目的完整流程。我们从基础概念开始,逐步深入,涵盖了:

  1. 环境准备:在不同系统上安装 Docker 和 Docker Compose

  2. 项目准备:创建 Spring Boot 项目并配置 Dockerfile

  3. 镜像构建:使用多阶段构建优化镜像大小

  4. 容器运行:各种运行选项和参数配置

  5. 服务编排:使用 Docker Compose 管理多容器应用

  6. 生产部署:安全、监控、备份等最佳实践

  7. 问题解决:常见问题排查和解决方案

  8. 进阶方案:Docker Swarm 和 Kubernetes 部署

Docker 作为现代应用部署的标准工具,掌握它对于开发者和运维人员都至关重要。实践是学习的最佳方式,建议您按照本文的步骤实际操作,遇到问题时查阅官方文档和社区资源。

记住,好的部署策略应该具备:

  • 可重复性:每次部署结果一致

  • 可观测性:完善的日志和监控

  • 可恢复性:快速回滚和故障恢复能力

  • 安全性:最小权限原则和安全配置

  • 自动化:尽可能自动化部署流程

相关推荐
磊 子30 分钟前
redis详解2
java·spring boot·redis
程序员阿明1 小时前
spring boot3 集成jjwt(java-jwt)版本的
java·spring boot·python
coppher1 小时前
Ubuntu 22.04 amd64 离线安装 Docker 完整教程
linux·docker
虚伪的空想家3 小时前
k8s集群configmap和secrets备份脚本
linux·容器·kubernetes
SXJR3 小时前
k8s中的Pod
云原生·容器·kubernetes
文静小土豆3 小时前
K8s 滚动更新在 Java 应用中的实践与优化
java·容器·kubernetes
gelald3 小时前
Spring Boot - 自动配置原理
java·spring boot·后端
w6100104663 小时前
CKA-2026-Ingress
云原生·容器·kubernetes·cka
希望永不加班3 小时前
SpringBoot 集成测试:@SpringBootTest 与 MockMvc
java·spring boot·后端·log4j·集成测试
bloglin999994 小时前
docker logs 如何一直监听日志输出
运维·docker·容器