写在前面

容器化让"在我电脑上能跑"问题消失,但不等于安全 + 可控。这一篇不讲 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 升到 Kubernetes 的核心概念。