很多人写 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 | ~120MB | Python 应用 |
node:20-slim | ~80MB | Node 前端构建 |
gcr.io/distroless/* | ~20MB | 极致安全,只跑二进制 |
scratch | 0MB | Go 静态编译直接丢进去 |
选型原则:
- 能用 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/htmlPython 项目(含编译扩展):
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 build4.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=production4.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 部署游戏服务器,端口规划见端口速查表。
延伸阅读
- 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
- 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
- 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
- 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
- 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
- RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
- 短链接服务搭建:Shlink 自托管,链接数据自己掌握
- 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道

[...]docker build --pull --no-cache -t myapp:1.2.4 .漏洞修在基础镜像里,你不重建就永远打不上补丁。CI 里加一条定时任务自动构建,见Dockerfile 编写与镜像优化。5.4 私密信息不进镜像# 错误:Dockerfile 里写密码,任何人 docker history 都能看到[...]
2026-10-14 回复
[...]docker system df -v # 明细到每个镜像/容器/卷Dockerfile 编写规范见Dockerfile 编写与镜像优化,网站部署全流程见Docker 部署网站实战,Compose 编排基础见Docker Compose 编排实战。数据卷和网络是 Docker 的两条命脉:卷决定数据活多久,网络决定容器怎么说话。记住三件事——要留的数据进卷、容器互访用服务名、U[...]
2026-10-15 回复
[...]docker compose up -d版本策略和镜像瘦身见Dockerfile 编写与镜像优化。四、CI 自动构建(GitHub Actions)仓库里放 .github/workflows/build.yml:name: Build and Push[...]
2026-10-15 回复
[...]docker system prune -f --volumes六、扩展:往这套里加什么需求加什么自己的博客Typecho 容器(Typecho 搭建)数据库MySQL/PostgreSQL 容器(SQLite 与 MySQL 怎么选先想清楚)游戏服用容器跑也行(Docker 部署游戏服务器),但注意性能和存档位置密码管理Vaultwarden(Bitwarden 自建)RSS 阅读FreshRS[...]
2026-10-18 回复
[...]netsh advfirewall firewall add rule name="GameRange" dir=in action=allow protocol=UDP localport=26901-26903完整的 Windows 环境准备(远程桌面连接、运行库安装、SteamCMD 用法、开机自启、8 个常见坑)见:《Windows 云服务器开服通用前置教程》→ htt[...]
2026-10-18 回复