
Docker 容器化部署实战:从单应用到微服务架构
为什么容器化正在重塑后端部署
传统部署方式有个老问题:"在我机器上能跑啊"。
开发环境跑得好好的代码,上线就崩。原因无非是环境差异——系统版本不同、依赖缺失、配置不一致。Docker 用容器把应用和它的依赖打包在一起,从根源上解决了这个问题。
但这只是容器化的浅层优势。真正改变游戏规则的是:
- 环境一致性 — 开发、测试、生产共用同一镜像,没有"环境差异"这回事
- 秒级启动 — 容器基于宿主内核,启动时间毫秒级,远快于虚拟机
- 资源隔离 — 每个容器有独立的文件系统、网络栈、进程空间
- 弹性伸缩 — 加一台宿主机就能水平扩展,K8s 自动编排
- 不可变基础设施 — 出了问题不是"上机器修",而是直接替换容器
本文从 Docker 基础出发,逐步深入到 Docker Compose 多服务编排,最后讨论生产环境的最佳实践。
第一章:Docker 核心概念

镜像与容器
Docker 有两个最基本的概念:
- 镜像(Image) — 一个只读模板,包含运行应用所需的全部文件。好比一个类(Class)。
- 容器(Container) — 镜像的运行实例,可读可写。好比这个类的一个实例(Instance)。
# 拉取镜像
docker pull nginx:alpine
# 查看本地镜像
docker images
# 启动容器
docker run -d --name my-nginx -p 8080:80 nginx:alpine
# 查看运行中的容器
docker ps
# 进入容器内部
docker exec -it my-nginx sh
# 停止并删除容器
docker stop my-nginx && docker rm my-nginx
镜像分层
Docker 镜像由多层只读层叠加而成。每一层对应 Dockerfile 中的一条指令。
FROM alpine:3.18 ← 基础层
RUN apk add --no-cache python3 ← 安装层
COPY app.py /app/ ← 文件层
CMD ["python3", "/app/app.py"] ← 元数据层
这种分层结构的优势是缓存复用。如果你改了 app.py,只需要重建最后一层,前面三层直接从缓存取。
数据持久化
容器删除后数据会丢失。持久化有两种方式:
# 1. 绑定挂载(Bind Mount)— 开发常用
docker run -v /host/data:/container/data nginx
# 2. 卷(Volume)— 生产推荐
docker volume create my-data
docker run -v my-data:/container/data nginx
经验法则:开发用绑定挂载方便调试,生产用 Volume 性能和隔离性更好。
第二章:Dockerfile 编写实战

多阶段构建
这是 Docker 最重要的优化技巧之一。
反面案例(单个阶段,镜像体积 1.2GB):
FROM node:18
WORKDIR /app
COPY . .
RUN npm install && npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]
正面案例(多阶段构建,镜像体积 150MB):
# 第一阶段:编译
FROM node:18-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 第二阶段:运行
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /build/dist ./dist
COPY --from=builder /build/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/server.js"]
多阶段构建把编译环境和运行环境分开了。最终镜像只包含运行所需的文件,不包含编译器、构建工具、临时文件。
容器安全实践
# 不用 root 运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# 只复制必需文件,而非整个目录
COPY --chown=appuser:appgroup ./bin/app /app/
# 设置只读根文件系统
第三章:Docker Compose 多服务编排
当你的应用不止一个服务时(比如前端 + 后端 + 数据库),一个个 docker run 不是办法。Docker Compose 用 YAML 文件定义整个服务栈。
标准 Web 服务栈
version: "3.8"
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DB_HOST=db
- REDIS_HOST=redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
healthcheck:
test: ["CMD", "pg_isready", "-U", "myapp"]
interval: 10s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
volumes:
pgdata:
redisdata:
secrets:
db_password:
file: ./secrets/db_password.txt
常用 Compose 命令
# 启动所有服务
docker compose up -d
# 查看日志
docker compose logs -f app
# 重新构建并启动
docker compose up -d --build
# 扩缩容
docker compose up -d --scale app=3
# 停止并清理
docker compose down -v
环境管理
不同环境(开发/测试/生产)用 overlay 文件:
# 开发环境
docker compose up -d
# 生产环境(覆盖配置)
docker compose -f compose.yaml -f compose.prod.yaml up -d
compose.prod.yaml 可以覆盖资源限制、副本数、日志配置等:
services:
app:
deploy:
replicas: 3
resources:
limits:
cpus: "0.5"
memory: "512M"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
第四章:生产环境最佳实践
镜像体积优化
| 策略 | 效果 | 方法 |
|---|---|---|
| 选择 Alpine 基础镜像 | 减少 80% 体积 | FROM node:18-alpine |
| 多阶段构建 | 减少 70-90% | 编译阶段→运行阶段 |
| 合并 RUN 指令 | 减少层数 | RUN apt-get update && apt-get install ... |
| .dockerignore | 避免冗余文件 | 排除 node_modules、.git、*.md |
| 清理缓存 | 减少大小 | npm cache clean --force、apt-get clean |
健康检查
Docker 需要知道你的容器是否"活着":
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
日志管理
默认 Docker 日志文件会无限增长。务必设置轮转:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
资源限制
不让一个容器吃掉所有内存:
services:
app:
deploy:
resources:
limits:
cpus: "0.5"
memory: "256M"
reservations:
cpus: "0.25"
memory: "128M"
第五章:常见坑与排查
容器退出码速查
| 退出码 | 含义 | 排查方向 |
|---|---|---|
| 0 | 正常退出 | — |
| 1 | 应用错误 | 看应用日志 |
| 137 | SIGKILL(OOM) | 检查内存限制 |
| 139 | SIGSEGV(段错误) | 内存泄漏/非法指针 |
| 143 | SIGTERM(优雅关闭) | 检查 shutdown hook |
调试三板斧
# 1. 看日志
docker logs <container_id>
# 2. 进容器
docker exec -it <container_id> sh
# 3. 检查进程和资源
docker stats <container_id>
docker inspect <container_id>
网络排错
# 查看容器网段
docker network inspect bridge
# 容器间 DNS 解析
docker exec app ping db
# 端口映射检查
docker port <container_id>

总结
Docker 容器化不是银弹,但它解决了一个真实的核心问题——环境一致性和部署标准化。
走完这套实践只需要几步:
- 写 Dockerfile — 多阶段构建、小镜像、安全配置
- 写 Compose — 定义服务栈、网络、持久化
- 本地跑通 —
docker compose up验证 - 上生产 — 加健康检查、资源限制、日志轮转
从单台服务器到 K8s 集群,这套思路是通用的。容器化越早做,后面踩的坑越少。
评论区