应优先选用轻量基础镜像(如alpine或slim版本),避免latest标签,慎用add而多用copy,合理使用.dockerignore和多阶段构建,并正确配置entrypoint与cmd以确保容器稳定运行。

FROM 选错基础镜像,构建直接失败
很多新手一上来就写 FROM ubuntu:latest,结果跑 apt update 慢得像卡住,或者镜像体积动辄 800MB。不是所有镜像都适合当基础层——ubuntu:latest 包含大量调试工具和文档,而生产环境通常只需要运行时依赖。
- 优先用
alpine系列(如node:18-alpine、python:3.11-slim),体积小、启动快、攻击面小 - 避免
latest标签,改用具体版本号(如python:3.11.9-slim),防止某天基础镜像更新导致构建不一致 - 如果应用依赖 glibc(比如某些二进制 CLI 工具),别硬套
alpine(它用的是 musl),否则会报No such file or directory—— 这是动态链接失败,不是路径错了
COPY 和 ADD 混用导致缓存失效或权限混乱
COPY 和 ADD 看似都能复制文件,但行为差异很大:ADD 会自动解压 tar 包、支持远程 URL,而 COPY 就是纯拷贝。多数场景下该用 COPY。
- 只在明确需要解压本地 tar 包时才用
ADD;其他一律用COPY -
COPY ./src /app会保留源文件权限,但如果你在 macOS 或 Windows 上构建,可能带不了可执行位 —— 解决办法是在COPY后加RUN chmod +x /app/entrypoint.sh - 不要
COPY . /app,把node_modules、.git全塞进去。先写.dockerignore,内容包括:node_modules、.git、README.md
多阶段构建不写清楚 stage 名,RUN 指令找不到依赖
多阶段构建(multi-stage build)是为了减小最终镜像体积,但很多人漏写 AS 或搞混 FROM ... AS builder 和 COPY --from=builder 的对应关系。
- 第一阶段必须用
AS命名(如FROM golang:1.22 AS builder),否则--from=builder会报错invalid from flag value -
COPY --from=builder /app/myapp /usr/local/bin/myapp中的路径必须是 builder 阶段里真实存在的路径,不能凭空猜测 - 最终阶段别留
go、gcc这类编译工具,它们只该出现在 builder 阶段
ENTRYPOINT 和 CMD 组合出错,容器一启动就退出
ENTRYPOINT 定义容器主进程,CMD 是它的默认参数。两者配合不对,就会出现“容器起来又立刻 exit 0”这种静默失败。
- 推荐写法:
ENTRYPOINT ["/bin/sh", "-c"]+CMD ["your-app --port=8080"],这样 CMD 能被docker run后接的参数覆盖 - 如果写成
ENTRYPOINT ["./start.sh"],那start.sh必须有可执行权限,且第一行得是#!/bin/sh,否则报exec format error - 别在
CMD里写后台命令(如supervisord -c /etc/supervisord.conf &),& 会让容器因主进程退出而终止
Dockerfile 里最麻烦的从来不是语法,而是每个指令背后隐含的文件系统状态、用户权限、shell 执行上下文——这些不会报错,但会让你花两小时 debug 为什么日志没输出、端口没暴露、或者 ls /app 在构建时有文件、运行时却没了。










