Docker 容器化

用标准集装箱打包你的应用,"一次构建,到处运行"

前置知识:Linux 基本命令 + 第01章 CI/CD 流水线


阅读指南(初学者必看)

为什么你需要学习 Docker?

你一定经历过这些场景:

  • "在我电脑上能跑啊!" —— 环境不一致导致的问题
  • 新同事入职,配环境配了一天
  • 测试环境和生产环境配置不同,出了 Bug 测不出来

Docker 就是解决"环境一致性"问题的。 把应用和依赖一起打包成镜像,不管在哪运行都是同样的环境。

学完本章,你能回答:

  • Docker 镜像、容器、仓库分别是什么?
  • 怎么写一个生产级别的 Dockerfile(多阶段构建)?
  • 怎么用 Docker Compose 一键启动完整的游戏开发环境?
  • 镜像怎么优化才能又小又快?

本文结构

第一部分:Docker 核心概念(建立认知)
第二部分:Dockerfile 最佳实践(动手写)
第三部分:Docker Compose 编排(多服务管理)
第四部分:镜像优化与安全

一、Docker 核心概念

生活类比:Docker 就像"集装箱"。以前货物大小不一,装卸麻烦;现在所有货物装进标准集装箱,轮船和卡车只需要处理集装箱,不用管里面装什么。

Docker 三大概念:

镜像(Image)── 只读模板,包含应用和依赖
  → 就像装好货物的集装箱,可以复制

容器(Container)── 镜像的运行实例
  → 就像正在运输的集装箱

仓库(Registry)── 存放镜像的地方
  → 就像集装箱码头

镜像 vs 容器 vs 仓库

概念 类比 说明
镜像(Image) 集装箱的设计图纸 只读,可以创建多个容器
容器(Container) 正在运输的集装箱 运行中的实例,可启停
仓库(Registry) 集装箱码头 Docker Hub 是最大的公共仓库

Docker 常用命令速查

# 镜像操作
docker build -t game-server:v1 .     # 构建镜像
docker pull nginx:alpine              # 拉取镜像
docker images                         # 查看本地镜像
docker rmi game-server:v1             # 删除镜像

# 容器操作
docker run -d -p 8080:8080 game-server:v1  # 启动容器
docker ps                                   # 查看运行中的容器
docker logs <container-id>                  # 查看日志
docker exec -it <container-id> /bin/sh      # 进入容器
docker stop <container-id>                  # 停止容器
docker rm <container-id>                    # 删除容器

二、Dockerfile 最佳实践

游戏服务器 Dockerfile(多阶段构建)

# 游戏服务器 Dockerfile(多阶段构建)

# 阶段1:构建
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline    # 先下载依赖(缓存层)
COPY src ./src
RUN mvn package -DskipTests

# 阶段2:运行(更小的基础镜像)
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# JVM 容器感知(JDK 8u191+ 自动检测容器内存限制)
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=100"

COPY --from=builder /app/target/game-server.jar .
EXPOSE 8080 8443

# 使用非 root 用户运行
RUN addgroup -S gameserver && adduser -S gameserver -G gameserver
USER gameserver

HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar game-server.jar"]

游戏客户端 Nginx Dockerfile

# 游戏客户端 Nginx Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build:prod

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

优化前 vs 优化后

# ❌ 优化前(问题:镜像大、层多、每次改代码都重新装依赖)
FROM node:latest
COPY . /app
WORKDIR /app
RUN npm install
EXPOSE 3000
CMD ["node", "server.js"]

# ✅ 优化后(多阶段构建 + 缓存层优化)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
USER node
CMD ["node", "dist/server.js"]

多阶段构建的好处

对比项 单阶段构建 多阶段构建
镜像大小 ~800MB(含构建工具) ~200MB(只含运行时)
安全性 低(含源码和构建工具) 高(只有运行必需文件)
构建速度 每次全量构建 利用缓存层加速
攻击面

.dockerignore 文件

node_modules
.git
.env
*.log
.vscode
coverage
dist

三、Docker Compose 编排

# docker-compose.yml - 游戏全栈开发环境
version: '3.8'

services:
  # 游戏网关
  gateway:
    build: ./gateway
    ports:
      - "8080:8080"
      - "8443:8443"
    depends_on:
      - room-server
      - match-server
    environment:
      - ROOM_SERVER_URL=ws://room-server:9000
      - MATCH_SERVER_URL=http://match-server:9001

  # 房间服务器
  room-server:
    build: ./room-server
    ports:
      - "9000:9000"
    depends_on:
      - redis
      - mysql
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/game
      - SPRING_REDIS_HOST=redis

  # 匹配服务器
  match-server:
    build: ./match-server
    ports:
      - "9001:9001"
    depends_on:
      - redis

  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    command: redis-server --maxmemory 512mb

  # MySQL
  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: game
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

  # Prometheus(监控)
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  # Grafana(看板)
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"

volumes:
  mysql-data:

Docker Compose 常用命令

docker-compose up -d          # 启动所有服务(后台)
docker-compose down           # 停止并删除所有容器
docker-compose logs -f        # 查看所有服务日志
docker-compose ps             # 查看服务状态
docker-compose build          # 重新构建镜像
docker-compose restart <svc>  # 重启单个服务

四、镜像优化与安全

镜像优化原则

  1. 镜像越小越好:减少攻击面、加快传输、减少启动时间
  2. 层数越少越好:减少构建时间,但可读性和缓存要平衡
  3. 不要存敏感信息:密码、密钥用环境变量或 Docker Secrets
  4. 一个容器只做一件事:网关是网关,数据库是数据库

安全最佳实践

措施 说明
非 root 用户 容器被攻破也限于容器内部权限
最小基础镜像 Alpine 比 Debian 小且攻击面小
健康检查 HEALTHCHECK 让 Docker 知道容器是否正常工作
只读文件系统 read_only: true 防止运行时文件被篡改
资源限制 限制 CPU 和内存,防止单个容器拖垮宿主机

自问自答

Q:Docker 和虚拟机有什么区别? A:虚拟机模拟完整的操作系统(有自己的内核),Docker 容器共享宿主机内核。所以 Docker 启动快(秒级 vs 分钟级)、占用少(MB vs GB)、性能接近原生。

Q:为什么要用多阶段构建? A:构建阶段需要 Maven、JDK 等工具(~600MB),但运行阶段只需要 JRE(~100MB)。多阶段构建把构建产物复制到一个更小的基础镜像中,最终镜像体积大幅减小,也更安全。

Q:Docker Compose 和 Kubernetes 有什么区别?什么时候用哪个? A:Docker Compose 适合单机开发/测试环境,定义和运行多容器应用。Kubernetes 适合生产环境,提供自动扩缩容、自愈、滚动更新等能力。开发用 Compose,生产用 K8s。

Q:游戏服务器的 Dockerfile 为什么要用非 root 用户? A:安全最佳实践。如果容器被攻破,root 用户可以访问宿主机资源。非 root 用户即使被攻破,影响范围也限于容器内部。

Q:Dockerfile 中 RUN npm install 放在 COPY . . 之后有什么问题? A:每次代码改动都会重新安装依赖,构建极慢。解决:先 COPY package.json,再 RUN npm install,最后 COPY 源码。


实践任务

  • 任务1:编写游戏服务器的 Dockerfile(多阶段构建),对比单阶段与多阶段的镜像大小差异
  • 任务2:用 Docker Compose 搭建完整开发环境(服务器 + Redis + MySQL + 监控)
  • 任务3:优化镜像大小(对比 Alpine vs 标准镜像,利用缓存层)
  • 任务4:配置 Docker 健康检查,验证容器异常时自动重启
  • 任务5:编写 .dockerignore,排除不需要的文件
  • 任务6:编写 Docker 部署文档,记录镜像构建、推送、运行的全流程

初学者常见错误

错误1:镜像越来越大

问题: 每次构建都叠加新层,不清理缓存和临时文件。 解决: 使用多阶段构建;同一 RUN 中合并命令并清理。

错误2:容器内应用监听 127.0.0.1

问题: 容器内的 127.0.0.1 只绑定到容器内部,外部访问不到。 解决: 应用监听 0.0.0.0 或 ::。

错误3:没有配置资源限制

问题: 某个容器内存泄漏导致宿主机 OOM,影响所有服务。 解决: 在 docker-compose.yml 中配置 deploy.resources.limits


与其他章节的关联

本章内容 关联章节 关联点
Docker 镜像构建 第01章 CI/CD 流水线 CI/CD 自动构建 Docker 镜像
Docker Compose 第03章 Kubernetes 编排 Compose 是单机版,K8s 是集群版
容器网络 第03章 Kubernetes 编排 K8s 的 Pod 网络基于容器网络
容器监控 第06章 游戏性能监控 容器内采集指标暴露给 Prometheus

上一章:01-CICD流水线 | 下一章:03-Kubernetes编排