预计时间
1 周
学习目标
- 编写多阶段 Dockerfile
- 理解 Volume 持久化
- 理解 Network 通信
- 用 Compose 编排多服务
一、Docker 是什么?
text
问题:
你:"我本地跑得好好的啊"
运维:"服务器上就是不行"
原因:Node 版本不同、系统库缺失、环境变量不一致
Docker 解决:
把代码 + 运行时 + 系统库 + 环境变量 → 打包成一个镜像
这个镜像在任何安装了 Docker 的机器上都能跑核心概念:
text
Dockerfile → 蓝本(源代码)
↓ build
Image → 模板(类)
↓ run
Container → 实例(对象)
类比:
Dockerfile = 菜谱
Image = 菜(做好了随时可以上桌)
Container = 吃的过程(菜在盘子里,吃完就没了)二、Dockerfile 详解
2.1 Next.js 前端
dockerfile
# ===== 构建阶段 =====
FROM node:20-alpine AS builder
WORKDIR /app
# 先 copy 依赖文件(利用缓存)
COPY package.json package-lock.json ./
RUN npm ci --silent
# 再 copy 源码
COPY . .
# 构建
RUN npm run build
# ===== 运行阶段 =====
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# 只 copy 必要的文件
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]多阶段构建的好处
构建阶段有 devDependencies、源代码、构建缓存 → 镜像很大(> 1GB) 运行阶段只 copy 最终产物 → 镜像小(< 200MB)
安全 + 快 + 省磁盘。
2.2 NestJS 后端
dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --silent
COPY . .
RUN npm run build # tsc 编译
RUN npm ci --silent --omit=dev # 生产依赖
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3001
CMD ["node", "dist/main"]2.3 关键指令
| 指令 | 作用 | 注意事项 |
|---|---|---|
FROM | 基础镜像 | 优先用 alpine(最小,5MB) |
WORKDIR | 设置工作目录 | 相当于 cd |
COPY | 复制文件 | .dockerignore 排除 node_modules |
RUN | 构建时执行 | 每条 RUN 产生一层,合并用 && |
EXPOSE | 声明端口 | 只是文档作用,不实际暴露 |
CMD | 容器启动命令 | 只能有一个,格式 CMD ["executable","param1"] |
三、Volume(持久化存储)
text
问题:
Container 里的数据在容器删除后就没了。
数据库跑在 container 里 → container 挂了 → 数据没了?
解决:Volume
把宿主机的目录"挂载"到容器里
容器删了,数据还在宿主机上三种类型:
yaml
# 1. Named Volume(Docker 管理,最常用)
volumes:
pgdata: # Docker 自动创建和管理
services:
postgres:
volumes:
- pgdata:/var/lib/postgresql/data
# 2. Bind Mount(指定宿主机路径,开发时用)
services:
app:
volumes:
- ./src:/app/src # 改代码实时生效
# 3. tmpfs(内存,重启就没了)
services:
app:
volumes:
- type: tmpfs
target: /app/cache四、Network(网络)
text
默认情况:
每个 container 在隔离网络中
Container A 不能直接访问 Container B
Compose 自动创建网络:
同一 compose 文件中的服务自动在同一网络中
服务名就是 hostname!
backend 可以这样连数据库:
不用 IP,直接用服务名 → postgres://postgres:5432网络模式:
yaml
# Bridge(默认)— 同一宿主机内通信
services:
app:
networks:
- app-network
db:
networks:
- app-network
networks:
app-network:
driver: bridge
# Host — 直接使用宿主机网络(性能好,Linux only)
services:
app:
network_mode: host五、Docker Compose 实战
完整 multi-service 编排
yaml
# docker-compose.yml
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- '3000:3000'
environment:
- NEXT_PUBLIC_API_URL=http://localhost:3001
depends_on:
backend:
condition: service_healthy
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- '3001:3001'
environment:
- DATABASE_URL=postgresql://user:pass@postgres:5432/knowledge
- REDIS_URL=redis://redis:6379
- JWT_SECRET=${JWT_SECRET}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3001/health']
interval: 10s
timeout: 5s
retries: 3
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=knowledge
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- '5432:5432'
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U user -d knowledge']
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
- redisdata:/data
command: redis-server --appendonly yes
minio: # S3 兼容对象存储(本地开发用)
image: minio/minio
ports:
- '9000:9000'
- '9001:9001'
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin
volumes:
- miniodata:/data
command: server /data --console-address ":9001"
volumes:
pgdata:
redisdata:
miniodata:bash
# 启动所有服务
docker compose up -d
# 查看日志
docker compose logs -f backend
# 停止
docker compose down
# 停止并删除数据卷
docker compose down -v.env 文件
bash
# .env(不要提交到 git)
DB_PASSWORD=your-secure-password
JWT_SECRET=your-jwt-secret
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin六、常用命令速查
bash
# 镜像
docker images # 列出本地镜像
docker rmi <image> # 删除镜像
docker pull node:20-alpine # 拉取镜像
# 容器
docker ps # 运行中的容器
docker ps -a # 包括已停止的
docker logs -f <container> # 实时日志
docker exec -it <container> /bin/sh # 进容器调试
docker rm <container> # 删除容器
docker rm -f <container> # 强制删除
# Compose
docker compose up -d # 后台启动
docker compose down # 停止并删除
docker compose restart backend # 重启单个服务
docker compose build --no-cache # 重新构建
# 清理
docker system prune -a # 清理所有不用资源实践
- 安装 Docker Desktop
- 把上面的
docker-compose.yml跑起来 - 进容器看看:
docker exec -it <backend-container> /bin/sh - 改一行代码 → rebuild → 观察 layer caching 效果
docker compose down -v→ 再up→ 确认数据是否还在