14 10 月 2026

很多人写 Dockerfile 就是「能跑就行」,结果镜像 2GB、构建十分钟、漏洞扫出一片红。这篇讲怎么写一个又小又快又安全的 Dockerfile:多阶段构建、缓存优化、层合并、安全基线。全部命令可抄。

需要现成的游戏云服务器,可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上,主流游戏都能快速部署,不想折腾容器化的直接用面板开服。

一、先看反面教材

FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y curl wget git vim nginx
COPY . /app
CMD ["/app/start.sh"]

问题清单:

问题后果
FROM ubuntu:latest今天构建成功,明天可能失败,无法复现
每条 RUN 一层层数爆炸,镜像虚胖
apt-get 不清理缓存白白多几百 MB
COPY . /app 太早改一行代码,后面全层缓存失效
装了 vim 等无关工具攻击面变大,镜像变大
没有 USER容器内以 root 跑,安全隐患
没有 HEALTHCHECK容器假死你不知道

下面逐条修。

二、基础镜像怎么选

镜像大小适用场景
ubuntu:24.04~78MB需要 apt 装一堆系统依赖
debian:bookworm-slim~74MB同上,更小一点
alpine:3.20~7MB编译型应用、Go/Node 二进制
python:3.12-slim~120MBPython 应用
node:20-slim~80MBNode 前端构建
gcr.io/distroless/*~20MB极致安全,只跑二进制
scratch0MBGo 静态编译直接丢进去

选型原则:

  • 能用 slim 就不用完整版,能锁版本就不用 latest
  • Alpine 是 musl libc,某些依赖(如部分 Python wheel、glibc 专用二进制)会踩坑,不确定就用 debian-slim
  • 游戏服涉及 Wine/32 位库的场景,老老实实选 ubuntu

三、多阶段构建(最重要的优化)

思路:构建阶段用完整镜像编译,运行阶段只把产物拷进干净镜像。中间几十个 G 的编译器、源码、缓存全部扔掉。

# ===== 构建阶段 =====
FROM golang:1.22 AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp .

# ===== 运行阶段 =====
FROM alpine:3.20
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /build/myapp /usr/local/bin/
USER nobody
ENTRYPOINT ["myapp"]

同一个 Go 程序:单阶段构建 ~1.1GB,多阶段后 ~25MB。

Node 项目同理:

FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html

Python 项目(含编译扩展):

FROM python:3.12-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
    && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip wheel --no-cache-dir -w /wheels -r requirements.txt

FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache --no-index --find-links=/wheels -r /app/requirements.txt || true

四、缓存优化:构建从 5 分钟到 30 秒

Docker 层缓存规则:某一层失效,它后面的层全部失效。

4.1 依赖清单先 COPY

# 错误示范:先 COPY 全部代码,改一行代码 npm ci 缓存全失效
COPY . .
RUN npm ci

# 正确示范:只变的部分放最后
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

4.2 合并 RUN、清理同层

# 差:apt 缓存留在层里,删了也占空间
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*

# 好:同一层内装完即清
RUN apt-get update \
    && apt-get install -y --no-install-recommends nginx \
    && rm -rf /var/lib/apt/lists/*

4.3 pip/npm 都加 no-cache

RUN pip install --no-cache-dir -r requirements.txt
RUN npm ci --only=production

4.4 BuildKit 缓存挂载(进阶)

# syntax=docker/dockerfile:1
RUN --mount=type=cache,target=/var/cache/apt \
    --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

构建时 DOCKER_BUILDKIT=1 docker build .,编译缓存不写进镜像层,反复构建飞快。

五、完整模板:Node 生产镜像

# syntax=docker/dockerfile:1
FROM node:20-slim AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production

FROM node:20-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
EXPOSE 3000
USER node
HEALTHCHECK --interval=30s --timeout=3s \
  CMD node -e "require('http').get('http://127.0.0.1:3000/health',r=>process.exit(r.statusCode===200?0:1))"
CMD ["node", "dist/main.js"]

要点回顾:slim 基础镜像、多阶段、依赖先拷、非 root 用户、健康检查、精简 node_modules。

六、.dockerignore 别忘

没有 .dockerignore,COPY . . 会把 node_modules、.git、日志、存档全部塞进构建上下文——构建慢、镜像脏、还可能泄露密钥。

.git
node_modules
*.log
.env
dist
data/
*.tmp
__pycache__

data/ 这类大目录尤其要排除,游戏服存档目录同理。

七、安全基线

项做法
不用 root镜像内建普通用户,USER appuser
不存密钥用 --secret 挂载或环境变量注入,别写进层
锁版本FROM python:3.12.4-slim,不用 latest
最小安装只装运行时必需包
只读根文件系统docker run --read-only,数据走卷
扫描镜像docker scout cves 或 Trivy
# Trivy 扫漏洞
docker run --rm aquasec/trivy image myapp:latest

# 看镜像分层大小,找出虚胖的层
docker history myapp:latest

镜像里出现 password、token 明文是常见事故——用 docker history 检查,如果写进去了,改代码重新构建,删层救不回来。

八、构建与发布工作流

# 打标签:版本号 + latest
docker build -t registry.example.com/myapp:1.2.3 -t registry.example.com/myapp:latest .

# 推送
docker push registry.example.com/myapp:1.2.3

# 服务器上拉取并重启
docker pull registry.example.com/myapp:1.2.3
docker compose up -d

铁律:生产环境跑带版本号的镜像,永远不要跑 latest。回滚就是 docker compose up -d 前把镜像标签改回旧版本号,30 秒搞定。

配合 CI 更香:GitHub Actions / Gitee Go 里跑 docker build + Trivy 扫描 + 推送,推代码自动出镜像,详细玩法见Docker 部署网站实战。

九、常见问题速查

现象原因与解法
构建超慢没写 .dockerignore,上下文里混了大目录
改代码全量重建COPY . . 放在了依赖安装之前
容器起不来秒退CMD/ENTRYPOINT 写错,docker logs 看报错
权限报错 Permission denied忘了 USER 或卷目录属主不对,chown 挂载点
镜像 2GB+没用多阶段、apt/pip 缓存没清、基础镜像太大
Alpine 里程序跑不了musl libc 不兼容,换 debian-slim

写好 Dockerfile 的收益是实打实的:构建从分钟级到秒级、镜像缩水 90%、漏洞扫描敢开强制门禁。把上面模板存进团队仓库,新人照抄就能写出合格镜像。

需要部署容器化的网站或游戏服,可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上,主流游戏都能快速部署。更多容器技巧见Docker 部署游戏服务器,端口规划见端口速查表。

延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:Redis 哨兵与集群模式:高可用部署实战
  2. 下一篇:Docker 安全加固与资源限制:容器不逃逸、不拖垮宿主机
发表评论 5

取消回复