写在前面
容器化让"在我电脑上能跑"问题消失,但不等于安全 + 可控。这一篇不讲 Docker 入门(那是「快速搭建」09 篇),讲生产环境里那些没人提醒就会踩的坑。
1. 多阶段构建
把"build 环境"和"runtime 环境"拆开——只有运行需要的东西进最终镜像:
# build 阶段
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# runtime 阶段
FROM node:22-alpine AS runtime
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
效果:
- 镜像更小(不含 webpack / typescript / 测试依赖)
- 攻击面更小(runtime 没编译器)
- 拉取更快
2. 永远用非 root 用户跑
默认进程是 root——容器逃逸时直接拿到宿主 root。
RUN addgroup -S app && adduser -S app -G app
USER app
或用基础镜像自带的非 root(如 node:*-alpine 自带 node 用户):
USER node
3. 标签策略:不要 :latest
FROM node:latest # ❌ 今天的 latest 明天可能 break
FROM node:22-alpine # ✓ 主版本固定
FROM node:22.11.0-alpine # ✓✓ 完全锁定
最稳的是用镜像 digest(SHA256):
FROM node:22-alpine@sha256:1234abcd...
部署自家应用同理:每次 build 打唯一 tag(git SHA / 时间戳),别用 :latest 部署到生产。
4. .dockerignore 别忘
.git
node_modules
.env
*.log
**/test/
.DS_Store
防止:build 上下文太大、敏感文件被 COPY 进镜像。
5. 健康检查(HEALTHCHECK)
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
K8s 里通常用 readinessProbe / livenessProbe 替代(下一篇);纯 docker compose 还是有用。
6. 镜像扫描:CI 里跑
主流工具:
- Trivy(Aqua Security):开源,社区活跃度最高
- Grype(Anchore):开源
- Snyk Container:商业 + 免费档
- 云原生:AWS ECR / Aliyun ACR / GitHub Container Registry 自带扫描
GitHub Actions 例:
- uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/me/myapp:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
发现 high/critical 漏洞 → 流水线失败。
7. 选基础镜像:alpine vs distroless vs slim
| 基础 | 大小(约) | 调试方便度 | 安全 |
|---|---|---|---|
node:22 |
大 | 高(自带 shell + 工具) | 攻击面大 |
node:22-slim |
中 | 中 | 中 |
node:22-alpine |
小 | 中(musl libc,偶有兼容问题) | 较好 |
gcr.io/distroless/nodejs22 |
最小 | 难(连 shell 都没有) | 最好 |
经验:
alpine是大多数项目的甜点;critical 服务上distroless;调试期不要省。
8. 别在镜像里塞 secret
ENV API_KEY=sk-abc123 # ❌ docker history 能扒出来
正确做法:runtime 注入(docker run -e / docker compose env_file / K8s Secret / 编排器自带 secret)。
9. 构建可重现
- 用
package-lock.json/requirements.txt锁版本 RUN apt-get update && apt-get install -y --no-install-recommends ...(避免装一堆推荐包)- 多阶段拆分清晰
- 用 BuildKit 缓存(默认开)
推荐阅读
- Docker 官方 Dockerfile 最佳实践
- Distroless — Google 出品的极简 runtime 镜像
- Trivy — 开源镜像扫描
- Snyk: 10 best practices for containers — 写得清楚的实战清单
下一篇:从单机 Docker 升到 Kubernetes 的核心概念。