Dockerfile 描述如何构建镜像,.dockerignore 决定哪些文件不会进入构建上下文。优化 Node.js 镜像时,两者必须一起设计:先缩小上下文,再合理排列依赖安装和源码复制步骤,最后用多阶段构建只保留运行所需内容。
目录
- 先理解构建上下文
- Node.js 项目的 .dockerignore
- 常用 Dockerfile 指令
- 利用缓存安装依赖
- 多阶段运行镜像
- CMD、ENTRYPOINT 与 shell 形式
- 环境变量与密钥
- 构建与检查
- 常见问题
- 参考资料
先理解构建上下文
执行下面的命令时,最后的 . 表示当前目录是构建上下文:
docker build -t my-app:latest .
Dockerfile 中的 COPY 和 ADD 只能访问上下文里的文件。上下文包含大量日志、缓存和 node_modules 时,即使 Dockerfile 没有复制它们,也会增加扫描或传输成本。
Node.js 项目的 .dockerignore
node_modules
.next
dist
coverage
.git
.env*
*.log
Dockerfile*
docker-compose*.yml
README.md
根据构建需要调整:如果构建阶段需要某个环境模板,不要把它排除;真实密钥则不应该通过 COPY 写入镜像。.dockerignore 支持 ! 反向包含,且最后一个匹配规则决定结果:
*.md
!README.md
多个 Dockerfile 可以使用各自的忽略文件,例如 build.Dockerfile.dockerignore。Docker 官方说明,Dockerfile 专用忽略文件会优先于上下文根目录的 .dockerignore。
常用 Dockerfile 指令
| 指令 | 用途 |
|---|---|
FROM |
指定基础镜像或开始新的构建阶段 |
WORKDIR |
设置后续命令的工作目录 |
COPY |
从构建上下文复制文件 |
RUN |
在构建阶段执行命令并创建镜像层 |
ENV |
设置镜像运行环境变量 |
ARG |
声明仅构建阶段使用的参数 |
EXPOSE |
记录容器预期监听端口,不会自动发布端口 |
USER |
切换后续命令和运行进程的用户 |
CMD |
设置容器默认启动命令 |
能用 COPY 时不要因为习惯改用 ADD;ADD 还支持远程 URL、Git 和自动解压等额外行为,只有明确需要时再使用。
利用缓存安装依赖
先复制依赖清单,再安装依赖,最后复制源码:
# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:20-alpine AS build
WORKDIR /app
COPY /app/node_modules ./node_modules
COPY . .
RUN npm run build
只要 lockfile 没变,Docker 就有机会复用依赖层。若先 COPY . .,任何源码变化都会让后续依赖安装层失效。
多阶段运行镜像
Next.js standalone 项目可以只复制运行产物:
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
COPY /app/public ./public
COPY /app/.next/standalone ./
COPY /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
构建阶段可以包含编译工具和开发依赖,运行阶段只保留产物。最终镜像更小,攻击面也更少。具体复制路径取决于框架输出,不要未经验证直接套用。
CMD、ENTRYPOINT 与 shell 形式
推荐用 exec 形式:
CMD ["node", "server.js"]
它不会额外启动 shell,信号传递更直接。需要固定可执行程序、只让用户追加参数时再考虑 ENTRYPOINT。一个 Dockerfile 中只有最后一个 CMD 生效。
环境变量与密钥
ARG和ENV都不适合保存真正的构建密钥,因为它们可能出现在镜像历史或元数据中。- 使用 BuildKit secret mount 处理构建期私有凭据。
- 运行期密钥通过部署平台、Compose secrets 或容器编排系统注入。
- 不要复制
.env后再删除;文件可能已经存在于前一镜像层。
构建与检查
docker build --pull -t my-app:latest .
docker image ls my-app
docker history my-app:latest
docker run --rm -p 3000:3000 my-app:latest
--no-cache 会重新执行构建层,但不会自动拉取更新后的基础镜像;需要全新基础镜像时同时使用 --pull。日常构建不应默认禁用缓存,否则会掩盖 Dockerfile 分层问题并浪费时间。
常见问题
- COPY 找不到文件:检查文件是否在构建上下文内,或是否被
.dockerignore排除。 - 改一行代码却重新安装全部依赖:调整
COPY顺序,把 lockfile 放在源码之前。 - 镜像仍然很大:检查最终阶段是否复制了完整源码、开发依赖或构建缓存。
- 容器收不到停止信号:确认使用 exec 形式启动,并避免不必要的 shell 包装。
- 镜像中出现密钥:立即轮换密钥,再清理构建流程;删除当前层文件并不能清除旧层。
日常管理容器和镜像的命令可参考 Docker 常用命令大全。
参考资料
继续阅读
前端项目配置文件指南:env、Node、Git 与 ESLint
系统梳理 .env、.nvmrc、package.json engines、.gitignore、Babel 和 ESLint flat config 的职责、安全边界与团队协作方式。
12 分钟Docker 常用命令:容器、镜像、日志与清理
按实际排障流程整理 Docker 容器、镜像、日志、网络、数据卷和磁盘清理命令,解释 run、exec、logs、inspect 与 prune 的使用边界。
9 分钟pnpm 实战使用指南:从安装、迁移到 Workspace
pnpm 适合同时维护多个前端项目的开发者。它通过全局内容寻址存储复用依赖,能减少 node_modules 占用、提升安装速度,并用更严格的依赖结构减少幽灵依赖问题。
订阅 FreeMac
每周精选:免费 Mac 软件评测、可信来源更新、替代方案和少折腾指南。