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

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

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

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

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

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

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

相关推荐
while(1){yan}1 天前
Spring事务
java·数据库·spring boot·后端·java-ee·mybatis
J2虾虾1 天前
Docker启动超时,吓得我一身汗
运维·docker·容器
码农小卡拉1 天前
Ubuntu22.04 安装 Docker 及 Docker Compose v2 详细教程
ubuntu·docker·容器
小唐同学爱学习1 天前
如何解决海量数据存储
java·数据库·spring boot·mysql
*.✧屠苏隐遥(ノ◕ヮ◕)ノ*.✧1 天前
《苍穹外卖》- day01 开发环境搭建
spring boot·后端·spring·maven·intellij-idea·mybatis
曹轲恒1 天前
@PropertySource、@ImportResource、@Bean
java·spring boot·mybatis
EasyNVR1 天前
docker版EasyNVR如何使用同步插件教程(包含网盘挂载,路径映射等)
docker·容器·音视频
岁岁种桃花儿1 天前
详解kubectl get replicaset命令及与kubectl get pods的核心区别
运维·nginx·容器·kubernetes·k8s
浅水壁虎1 天前
任务调度——XXLJOB2(调度中心)
java·spring boot·spring
源码获取_wx:Fegn08951 天前
计算机毕业设计|基于springboot + vue景区管理系统(源码+数据库+文档)
java·vue.js·spring boot·后端·课程设计