Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Docker容器化部署(可选)

方法论: Docker是现代部署的标准方式,理解“集装箱“类比,就能理解容器化。


📖 本节目标

学完本节,你将理解:

  • ✅ Docker是什么,为什么要用它
  • ✅ Dockerfile编写和镜像构建
  • ✅ docker-compose多容器编排
  • ✅ 完整的容器化部署流程

预计用时: 35分钟

前置要求:

  • ✅ 已学习04章服务器部署
  • ✅ 理解前后端分离架构

1. Docker是什么?

1.1 生活类比

Docker = 集装箱运输系统

传统部署Docker容器化
散装货物(直接装车)标准集装箱
每种货物装法不同所有集装箱规格统一
换车很麻烦集装箱可以直接吊到任何车上
装卸慢,容易损坏快速装卸,保护货物

软件部署中:

传统部署:
你的电脑 (Node 16, Ubuntu 20)  ✅ 能运行
测试服务器 (Node 14, CentOS 7) ❌ 出错了!
生产服务器 (Node 18, Ubuntu 22) ❌ 又出错!

容器化部署:
你的电脑 (Docker)  ✅ 容器运行正常
测试服务器 (Docker) ✅ 同样的容器,一样运行
生产服务器 (Docker) ✅ 同样的容器,一样运行

"在我电脑上能跑" 不再是玩笑!

1.2 Docker解决的核心问题

问题1: 环境不一致

开发: macOS + Node 18 + Python 3.11
服务器: Ubuntu + Node 16 + Python 3.9

结果: 代码在本地能跑,服务器跑不起来!

Docker解决: 把应用和环境打包在一起


问题2: 依赖冲突

项目A 需要 Node 14
项目B 需要 Node 18

同一台服务器上怎么办?

Docker解决: 每个项目独立容器,互不影响


问题3: 部署复杂

传统部署步骤:
1. 安装Node.js
2. 安装MySQL
3. 配置Nginx
4. 安装Redis
5. 配置防火墙
6. ...20个步骤

Docker部署:
docker-compose up -d

一个命令启动所有服务! ⚡

1.3 核心概念

概念类比说明
镜像(Image)集装箱模板只读的应用模板,包含代码和环境
容器(Container)实际运行的集装箱镜像的运行实例
Dockerfile集装箱制造图纸定义如何构建镜像
Docker Hub集装箱仓库存储和分享镜像
docker-compose多个集装箱一起管理编排多个容器

关系图:

Dockerfile (图纸)
    │
    ▼ docker build
Image (模板)
    │
    ▼ docker run
Container (运行中的应用)

2. 安装Docker

2.1 Linux安装(Ubuntu)

# 1. 更新包列表
sudo apt update

# 2. 安装依赖
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common

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

# 4. 添加Docker仓库
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

# 5. 安装Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io

# 6. 安装docker-compose
sudo apt install -y docker-compose-plugin

# 7. 验证安装
docker --version
docker compose version

2.2 macOS/Windows安装

macOS:

  1. 下载 Docker Desktop for Mac
  2. 拖拽到Applications
  3. 启动Docker Desktop

Windows:

  1. 下载 Docker Desktop for Windows
  2. 运行安装程序
  3. 启动Docker Desktop(需要WSL2)

2.3 配置国内镜像加速

# 创建配置文件
sudo mkdir -p /etc/docker
sudo vim /etc/docker/daemon.json

# 添加以下内容
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com",
    "https://mirror.ccs.tencentyun.com"
  ]
}

# 重启Docker
sudo systemctl restart docker

# 验证
docker info | grep -A 5 "Registry Mirrors"

2.4 允许非root用户使用Docker

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

# 重新登录或执行
newgrp docker

# 测试
docker ps
# 不需要sudo了!

3. Dockerfile编写

3.1 Dockerfile基础语法

指令作用示例
FROM基础镜像FROM node:18-alpine
WORKDIR工作目录WORKDIR /app
COPY复制文件COPY package.json .
RUN执行命令(构建时)RUN npm install
CMD默认命令(运行时)CMD ["npm", "start"]
EXPOSE暴露端口EXPOSE 3000
ENV环境变量ENV NODE_ENV=production

3.2 前端Dockerfile示例

# Dockerfile (前端React/Vue项目)

# 阶段1: 构建阶段
FROM node:18-alpine AS builder

WORKDIR /app

# 复制package.json
COPY package*.json ./

# 安装依赖
RUN npm install

# 复制源代码
COPY . .

# 打包
RUN npm run build

# 阶段2: 运行阶段(多阶段构建,减小镜像体积)
FROM nginx:alpine

# 复制打包结果到Nginx
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制Nginx配置(可选)
# COPY nginx.conf /etc/nginx/conf.d/default.conf

# 暴露80端口
EXPOSE 80

# 启动Nginx
CMD ["nginx", "-g", "daemon off;"]

为什么用多阶段构建?

单阶段:
镜像大小: 1.2GB (包含Node.js, npm, 源代码, node_modules...)

多阶段:
镜像大小: 25MB (只包含Nginx和打包后的静态文件)

体积减少98%! ⚡

3.3 后端Dockerfile示例

# Dockerfile (Node.js后端)

FROM node:18-alpine

# 安装dumb-init(更好的进程管理)
RUN apk add --no-cache dumb-init

# 设置工作目录
WORKDIR /app

# 非root用户运行(安全)
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 复制package.json
COPY --chown=nodejs:nodejs package*.json ./

# 安装生产依赖
RUN npm ci --only=production

# 复制源代码
COPY --chown=nodejs:nodejs . .

# 切换到非root用户
USER nodejs

# 暴露端口
EXPOSE 3000

# 使用dumb-init启动
CMD ["dumb-init", "node", "server.js"]

3.4 Python后端Dockerfile示例

# Dockerfile (Python FastAPI)

FROM python:3.11-slim

WORKDIR /app

# 安装系统依赖
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc && \
    rm -rf /var/lib/apt/lists/*

# 复制requirements.txt
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制源代码
COPY . .

# 创建非root用户
RUN useradd -m -u 1000 appuser && \
    chown -R appuser:appuser /app
USER appuser

# 暴露端口
EXPOSE 8000

# 启动应用
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

4. 构建和运行镜像

4.1 构建镜像

# 基本构建
docker build -t my-app:latest .

# -t: 镜像标签(名称:版本)
# .: Dockerfile所在目录

# 指定Dockerfile
docker build -t my-app -f Dockerfile.prod .

# 查看构建过程
docker build -t my-app --progress=plain .

4.2 运行容器

# 基本运行
docker run -d -p 3000:3000 --name my-container my-app

# -d: 后台运行
# -p 宿主机端口:容器端口
# --name: 容器名称

# 带环境变量
docker run -d \
  -p 3000:3000 \
  -e NODE_ENV=production \
  -e DATABASE_URL=mysql://... \
  --name my-app \
  my-app:latest

# 挂载数据卷
docker run -d \
  -p 3000:3000 \
  -v $(pwd)/data:/app/data \
  --name my-app \
  my-app

4.3 常用管理命令

# 查看运行中的容器
docker ps

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

# 查看容器日志
docker logs my-container
docker logs -f my-container  # 实时查看

# 进入容器
docker exec -it my-container /bin/sh

# 停止容器
docker stop my-container

# 启动容器
docker start my-container

# 重启容器
docker restart my-container

# 删除容器
docker rm my-container
docker rm -f my-container  # 强制删除运行中的容器

# 删除镜像
docker rmi my-app:latest

# 清理未使用的资源
docker system prune -a

5. docker-compose编排

5.1 为什么需要docker-compose?

场景: 一个完整的Web应用

需要运行:
- 前端容器
- 后端容器
- 数据库容器
- Redis缓存容器
- Nginx代理容器

手动运行5个docker run命令? 太麻烦!

docker-compose解决:

# 一个配置文件管理所有容器
docker-compose.yml

# 一个命令启动所有服务
docker compose up -d

5.2 docker-compose.yml示例

version: '3.8'

services:
  # 前端服务
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - app-network

  # 后端服务
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=mysql://root:password@db:3306/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    networks:
      - app-network
    restart: unless-stopped

  # 数据库
  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_DATABASE=myapp
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - app-network
    restart: unless-stopped

  # Redis缓存
  redis:
    image: redis:7-alpine
    networks:
      - app-network
    restart: unless-stopped

# 数据卷
volumes:
  db-data:

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

5.3 docker-compose命令

# 启动所有服务
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs
docker compose logs -f backend  # 查看某个服务

# 重启服务
docker compose restart backend

# 停止所有服务
docker compose stop

# 停止并删除所有容器
docker compose down

# 停止并删除所有容器+数据卷
docker compose down -v

# 重新构建并启动
docker compose up -d --build

6. 实战: 完整项目容器化

6.1 项目结构

my-project/
├── frontend/
│   ├── src/
│   ├── package.json
│   └── Dockerfile
├── backend/
│   ├── src/
│   ├── package.json
│   └── Dockerfile
├── docker-compose.yml
└── .dockerignore

6.2 .dockerignore配置

# .dockerignore (放在Dockerfile同级目录)

node_modules
npm-debug.log
.env
.git
.gitignore
README.md
dist
build
.DS_Store

6.3 完整部署流程

# 1. 克隆代码
git clone https://github.com/yourname/project.git
cd project

# 2. 配置环境变量
cp .env.example .env
vim .env

# 3. 启动所有服务
docker compose up -d --build

# 4. 查看日志
docker compose logs -f

# 5. 访问应用
# http://your-server-ip

# 6. 进入容器调试
docker compose exec backend sh

# 7. 重启某个服务
docker compose restart backend

# 8. 查看资源使用
docker stats

7. 镜像优化技巧

7.1 选择合适的基础镜像

镜像大小适用场景
node:181GB开发环境
node:18-slim200MB轻量生产环境
node:18-alpine120MB最小化生产环境
# ✅ 推荐: alpine镜像
FROM node:18-alpine

# ❌ 不推荐: 完整镜像
FROM node:18

7.2 利用构建缓存

# ✅ 正确顺序: 先复制package.json
COPY package*.json ./
RUN npm install
COPY . .

# ❌ 错误顺序: 代码变化导致重新安装依赖
COPY . .
RUN npm install

7.3 多阶段构建

# 构建阶段: 包含开发工具
FROM node:18 AS builder
RUN npm run build

# 运行阶段: 只包含必需文件
FROM node:18-alpine
COPY --from=builder /app/dist ./dist

7.4 清理缓存

# ✅ 一条RUN命令,减少层数
RUN apt-get update && \
    apt-get install -y gcc && \
    rm -rf /var/lib/apt/lists/*

# ❌ 多条RUN,增加镜像大小
RUN apt-get update
RUN apt-get install -y gcc
RUN rm -rf /var/lib/apt/lists/*

8. 生产环境最佳实践

8.1 安全性

# ✅ 使用非root用户
RUN adduser -D appuser
USER appuser

# ✅ 只暴露必需端口
EXPOSE 3000

# ✅ 使用secrets管理密钥
# docker compose secrets

8.2 健康检查

# Dockerfile添加健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
  CMD node healthcheck.js || exit 1
# docker-compose.yml
services:
  backend:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

8.3 日志管理

services:
  backend:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

8.4 资源限制

services:
  backend:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

9. Docker 实战场景 🔥

9.1 场景1: 从零部署一个前端应用

完整流程:

# 1. 进入前端项目目录
cd my-react-app

# 2. 创建Dockerfile
cat > Dockerfile <<'EOF'
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EOF

# 3. 创建.dockerignore
echo "node_modules
.git
.env" > .dockerignore

# 4. 构建镜像
docker build -t my-app:v1.0 .

# 5. 运行容器
docker run -d -p 80:80 --name my-app my-app:v1.0

# 6. 验证
curl http://localhost

# 7. 查看日志
docker logs my-app

9.2 场景2: 快速启动开发环境

使用docker-compose快速启动数据库:

# 创建docker-compose.yml
cat > docker-compose.yml <<'EOF'
version: '3.8'

services:
  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: myapp
    volumes:
      - mysql-data:/var/lib/mysql

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  mysql-data:
EOF

# 启动所有服务
docker compose up -d

# 连接MySQL测试
mysql -h 127.0.0.1 -u root -ppassword

# 停止所有服务
docker compose down

9.3 场景3: 容器调试和排错

进入容器内部调试:

# 进入运行中的容器
docker exec -it my-app sh

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

# 查看容器内的文件
docker exec my-app cat /etc/nginx/nginx.conf

# 复制文件到容器
docker cp local-file.txt my-app:/app/

# 从容器复制文件出来
docker cp my-app:/var/log/nginx/error.log ./

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

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

# 查看容器进程
docker top my-app

9.4 场景4: 镜像管理

镜像的完整生命周期:

# 查看本地镜像
docker images

# 搜索Docker Hub上的镜像
docker search nginx

# 拉取镜像
docker pull nginx:alpine

# 给镜像打标签
docker tag my-app:v1.0 myregistry.com/my-app:v1.0

# 推送到远程仓库
docker push myregistry.com/my-app:v1.0

# 删除本地镜像
docker rmi my-app:v1.0

# 删除所有未使用的镜像
docker image prune -a

# 查看镜像历史
docker history my-app:v1.0

# 保存镜像为tar文件
docker save -o my-app.tar my-app:v1.0

# 从tar文件加载镜像
docker load -i my-app.tar

9.5 场景5: 容器更新和回滚

零停机更新应用:

# 1. 构建新版本镜像
docker build -t my-app:v2.0 .

# 2. 启动新容器(不同端口测试)
docker run -d -p 8080:80 --name my-app-v2 my-app:v2.0

# 3. 测试新版本
curl http://localhost:8080

# 4. 如果测试通过,停止旧容器
docker stop my-app

# 5. 删除旧容器
docker rm my-app

# 6. 启动新容器到80端口
docker run -d -p 80:80 --name my-app my-app:v2.0

# 如果新版本有问题,快速回滚:
# 停止新版本
docker stop my-app
docker rm my-app

# 重新启动旧版本
docker run -d -p 80:80 --name my-app my-app:v1.0

9.6 场景6: 批量容器管理

一键操作多个容器:

# 停止所有运行的容器
docker stop $(docker ps -q)

# 删除所有停止的容器
docker container prune

# 删除所有容器(包括运行中的)
docker rm -f $(docker ps -aq)

# 按名称过滤删除
docker rm -f $(docker ps -a | grep "my-app" | awk '{print $1}')

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

# 查看容器占用的磁盘空间
docker system df

# 清理所有未使用的资源
docker system prune -a --volumes

9.7 场景7: 日志管理

查看和管理容器日志:

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

# 实时跟踪日志
docker logs -f my-app

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

# 查看带时间戳的日志
docker logs -t my-app

# 查看最近30分钟的日志
docker logs --since 30m my-app

# 查看某时间段的日志
docker logs --since 2024-01-01T00:00:00 my-app

# 导出日志到文件
docker logs my-app > app.log 2>&1

9.8 场景8: 网络管理

容器网络配置:

# 查看所有网络
docker network ls

# 创建自定义网络
docker network create my-network

# 在自定义网络中运行容器
docker run -d --name app1 --network my-network nginx

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

# 从网络断开容器
docker network disconnect my-network my-app

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

# 删除网络
docker network rm my-network

# 容器间通信示例
docker run -d --name db --network my-network mysql
docker run -d --name app --network my-network \
  -e DB_HOST=db \  # 直接用容器名作为主机名
  my-app

9.9 Docker 命令速查表

场景命令说明
镜像操作
构建镜像docker build -t my-app:v1.0 .从Dockerfile构建
查看镜像docker images列出本地镜像
拉取镜像docker pull nginx:alpine从仓库下载
删除镜像docker rmi my-app:v1.0删除本地镜像
清理镜像docker image prune -a删除未使用镜像
容器操作
运行容器docker run -d -p 80:80 --name app nginx后台运行
查看容器docker ps运行中的容器
查看所有docker ps -a包括停止的
停止容器docker stop app优雅停止
启动容器docker start app启动已停止的
重启容器docker restart app重启容器
删除容器docker rm app删除停止的容器
强制删除docker rm -f app删除运行中的
调试操作
查看日志docker logs -f app实时日志
进入容器docker exec -it app sh交互式shell
查看进程docker top app容器内进程
查看统计docker stats app资源使用
查看详情docker inspect app完整配置
Compose操作
启动服务docker compose up -d后台启动所有
停止服务docker compose down停止并删除
查看状态docker compose ps服务状态
查看日志docker compose logs -f所有服务日志
重启服务docker compose restart重启所有服务
清理操作
清理容器docker container prune删除停止的容器
清理镜像docker image prune -a删除未使用镜像
清理全部docker system prune -a清理所有未使用资源

9.10 Docker Compose 实战技巧

环境变量管理:

# 创建.env文件
cat > .env <<'EOF'
MYSQL_ROOT_PASSWORD=secretpassword
MYSQL_DATABASE=myapp
REDIS_PORT=6379
APP_PORT=3000
EOF

# docker-compose.yml使用环境变量
version: '3.8'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}

  app:
    build: .
    ports:
      - "${APP_PORT}:3000"

多环境配置:

# docker-compose.yml - 基础配置
# docker-compose.dev.yml - 开发环境
# docker-compose.prod.yml - 生产环境

# 开发环境启动
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# 生产环境启动
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

10. 常见问题

Q1: 容器内时间不对

解决:

services:
  backend:
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro

Q2: 容器无法访问宿主机MySQL

原因: MySQL绑定了127.0.0.1

解决:

# 修改MySQL配置
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

# 改为
bind-address = 0.0.0.0

# 重启MySQL
sudo systemctl restart mysql

Q3: 修改代码后没生效

原因: 需要重新构建镜像

# 重新构建
docker compose up -d --build

# 或强制重建
docker compose build --no-cache
docker compose up -d

Q4: 容器启动失败

排查步骤:

# 1. 查看容器日志
docker compose logs backend

# 2. 查看容器状态
docker compose ps

# 3. 进入容器调试
docker compose exec backend sh

# 4. 检查端口冲突
netstat -tuln | grep 3000

11. 总结

核心要点

  1. Docker = 标准化的运行环境

    • 一次打包,到处运行
    • 环境一致性
    • 快速部署
  2. Dockerfile编写原则

    • 使用alpine镜像
    • 多阶段构建
    • 利用构建缓存
    • 非root用户运行
  3. docker-compose编排

    • 一个文件管理所有服务
    • 一个命令启动/停止
    • 网络和数据卷自动管理
  4. 生产环境要点

    • 健康检查
    • 日志管理
    • 资源限制
    • 安全配置

Docker 命令记忆口诀

镜像三步: build(构建) → pull(拉取) → rmi(删除) 容器三步: run(运行) → stop(停止) → rm(删除) 调试三板斧: logs(日志) → exec(进入) → inspect(检查) Compose三连: up(启动) → ps(查看) → down(停止)

常用操作速查

# 快速启动MySQL开发环境
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass mysql:8.0

# 快速启动Redis
docker run -d -p 6379:6379 redis:alpine

# 查看运行容器
docker ps

# 停止所有容器
docker stop $(docker ps -q)

# 清理所有未使用资源
docker system prune -a

检查清单

Docker化项目前检查:

  • ✅ Dockerfile编写完成
  • ✅ .dockerignore配置正确
  • ✅ docker-compose.yml编写完成
  • ✅ 环境变量配置
  • ✅ 本地测试通过
  • ✅ 数据持久化(volumes)
  • ✅ 健康检查配置
  • ✅ 非root用户运行

实战学习路径

第1周: 基础命令
├── docker run/stop/rm
├── docker images/ps
└── docker logs/exec

第2周: Dockerfile
├── 编写Dockerfile
├── 多阶段构建
└── 镜像优化

第3周: docker-compose
├── 编写docker-compose.yml
├── 多容器编排
└── 环境变量管理

第4周: 生产实践
├── 容器监控
├── 日志管理
├── 性能优化
└── 安全加固

下一步

  • 学习Kubernetes(K8s)容器编排
  • 学习CI/CD自动化部署
  • 学习Docker Swarm集群
  • 学习监控和日志收集

记住: Docker不是万能的,但它解决了部署一致性问题!