DockerDockerfileNode.js工程化

Dockerfile 与 .dockerignore:Node.js 镜像构建指南

从构建上下文、Dockerfile 分层和多阶段构建讲起,给出 Node.js 项目的 Dockerfile 与 .dockerignore 示例,并解释缓存、镜像体积和敏感文件边界。

·更新于 ·阅读约 11 分钟·计算中...
Dockerfile 与 .dockerignore:Node.js 镜像构建指南

Dockerfile 描述如何构建镜像,.dockerignore 决定哪些文件不会进入构建上下文。优化 Node.js 镜像时,两者必须一起设计:先缩小上下文,再合理排列依赖安装和源码复制步骤,最后用多阶段构建只保留运行所需内容。

目录

先理解构建上下文

执行下面的命令时,最后的 . 表示当前目录是构建上下文:

命令
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 --from=deps /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 --from=build --chown=nextjs:nodejs /app/public ./public
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /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 后再删除;文件可能已经存在于前一镜像层。

构建与检查

终端 · zsh
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 常用命令大全。

参考资料

订阅 FreeMac

每周精选:免费 Mac 软件评测、可信来源更新、替代方案和少折腾指南。