← 全部文章
Docker生产实践优化安全

Docker 系列(八):多阶段构建、镜像优化与生产安全

2026年7月21日 · 15 分钟

从「能跑」到「能上生产」

前七篇让你能构建、编排应用。但直接把开发镜像丢上线,往往又大又不安全。这一篇把 生产环境真正在乎的东西讲透:体积、安全、稳定性

一、多阶段构建:镜像瘦身利器

问题:编译型/构建型应用需要一堆构建工具(编译器、devDependencies),但运行时根本 用不到。如果全打进镜像,体积会非常臃肿。

多阶段构建(multi-stage build) 的思路:用一个「构建阶段」编译产物,再把产物拷进 一个干净的「运行阶段」,构建工具全部丢弃。

# ---------- 阶段 1:构建 ----------
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci                    # 装全部依赖(含 devDependencies)
COPY . .
RUN npm run build             # 产出 dist/

# ---------- 阶段 2:运行(干净的最小镜像)----------
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev         # 只装生产依赖
COPY --from=builder /app/dist ./dist   # 只拷构建产物
CMD ["node", "dist/server.js"]

关键在 COPY --from=builder只从构建阶段挑出需要的产物,编译工具、源码、 devDependencies 统统留在被丢弃的 builder 阶段。最终镜像可能从 800MB 缩到 150MB。

你博客项目的 Dockerfile 用的正是这个思路——三阶段(deps / builder / runner), 最终只拷贝 Next.js 的 standalone 产物,镜像非常小。

二、更多瘦身技巧

  1. 选对基础镜像alpine > slim > 完整版。

  2. 合并 RUN 层并清理缓存

    RUN apt-get update && apt-get install -y curl \
        && rm -rf /var/lib/apt/lists/*     # 同层清理,才能真正减小体积
    

    注意:清理必须和安装在同一条 RUN里。分成两条 RUN 的话,删除只是在上层「遮盖」, 底层文件仍占体积。

  3. 用 .dockerignore 排除 node_modules.git、日志等(第四篇讲过)。

  4. docker history / dive 工具 分析每层占用,找出「体积大户」。

三、安全:别用 root 跑容器

默认情况下容器内进程以 root 运行,一旦被攻破,风险极大。生产镜像应该创建普通用户:

FROM node:22-alpine AS runner
WORKDIR /app

# 创建非 root 用户和组
RUN addgroup --system --gid 1001 nodejs \
  && adduser --system --uid 1001 nextjs

COPY --chown=nextjs:nodejs . .

USER nextjs        # 切换到非 root 用户
CMD ["node", "server.js"]

其他安全要点:

  • 最小权限:只装必需的包,减少攻击面。
  • 不要把密钥打进镜像:用环境变量或 secrets 注入,别 COPY .env(记得加进 .dockerignore)。
  • 固定基础镜像版本:用 node:22.11-alpine 而非 node:latest,避免不可控更新。
  • 只读文件系统(进阶):docker run --read-only,让容器无法被写入。

四、稳定性:健康检查与重启策略

在 Dockerfile 里加 HEALTHCHECK

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000 || exit 1

Docker 会定期探测,docker ps 的 STATUS 会显示 healthy / unhealthy,编排系统可 据此自动重启或摘流。

重启策略

docker run --restart unless-stopped ...   # 除非手动停,否则挂了就重启
策略行为
no默认,不自动重启
on-failure仅非正常退出时重启
always总是重启(包括 Docker 重启后)
unless-stopped总是重启,但你手动停的除外(生产常用

五、资源限制

别让单个容器吃光宿主机资源:

docker run -d \
  --memory="512m" \      # 内存上限 512MB
  --cpus="1.5" \          # 最多用 1.5 个 CPU 核
  myapp

Compose 里对应:

services:
  web:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "1.5"

六、镜像安全扫描

上线前扫一遍已知漏洞:

docker scout cves myapp:latest      # Docker 官方扫描工具
# 或社区流行的 trivy
trivy image myapp:latest

把扫描接入 CI,发现高危漏洞就阻断发布。

七、串起来:这正是你博客的部署链路

回顾我们给博客搭的 CI/CD,其实就是这套生产实践的落地:

git push
  → GitHub Actions 用【多阶段 Dockerfile】构建【小体积】镜像
  → 推送到镜像仓库(GHCR)
  → 服务器 docker pull + docker run(--restart unless-stopped)
  → Nginx 反向代理(容器只绑 127.0.0.1,不裸奔)

每一环都对应本系列的知识点:镜像分层、多阶段瘦身、非 root、重启策略、网络安全。

本篇小结

  • 多阶段构建:构建产物与构建工具分离,镜像体积大幅缩小。
  • 瘦身:选 alpine、同层清理缓存、用 .dockerignore
  • 安全:非 root 运行、不打包密钥、固定版本、最小权限。
  • 稳定:HEALTHCHECK + --restart unless-stopped + 资源限制。
  • 上线前用 docker scout / trivy 扫描漏洞并接入 CI。

系列结语

从「容器是什么」到「生产级部署」,八篇走完,你已经具备了在真实项目里用好 Docker 的 完整能力:

  1. 理解容器与镜像的本质
  2. 熟练操作容器生命周期
  3. 编写高效的 Dockerfile
  4. 持久化数据、连通网络
  5. 用 Compose 编排多服务
  6. 按生产标准优化与加固

接下来最好的学习方式就是——找一个你自己的项目,把它 Docker 化并部署上线。动手, 才是掌握 Docker 的唯一捷径。

相关水晶