Docker - 02 - 生成镜像的文件Dockerfile
写Dockerfile之前先懂 3 个概念概念一句话镜像 Image一套已经装好的迷你操作系统 程序文件 启动方式可以直接运行容器 Container镜像跑起来就是容器层 LayerDockerfile 每一行指令 ≈ 一层某层输入变了该层及之后全部重做两个关键指令的区别RUN — 构建镜像时执行装依赖、generate 等CMD — 容器启动时执行跑你的应用第 0 步新建文件 配套 .dockerignore在项目根目录创建 Dockerfile无后缀首字母大写常见。同时准备 .dockerignore避免把不该拷进镜像的东西带进去node*modules .env .git tests *.md为什么需要nodemodules 会在容器里用 npm ci 重装.env 含密钥不应进镜像tests、.md 与运行无关减小镜像体积。第 1 步选基础镜像 — FROMFROM node:22-bookworm-slim你在做什么 告诉 Docker「从哪个现成环境开始搭」。怎么选部分含义node:22Node.js 22bookworm-slimDebian 12 精简版体积小为什么不用 AlpinePrisma 在 Alpinemusl上常出兼容问题Debian 更稳小白检查点 本地 node -v 尽量与镜像大版本一致本项目用 22。第 2 步定工作目录 — WORKDIRWORKDIR /app你在做什么 在容器里创建并进入 /app后面 COPY、RUN 默认都在这里执行。类比 相当于先 mkdir /app cd /app。第 3 步先只拷依赖清单 — COPY第一次COPY package.json package-lock.json ./你在做什么 只复制 package.json 和 package-lock.json还不拷源码。为什么先拷这两个Docker 按层缓存依赖很少变、源码经常变。先装依赖、后拷代码改 .ts 文件时不必重新 npm ci构建更快。改 package.json → 从第 3 步起全部重做只改 src/*.ts → 第 35 步缓存命中只重做后面的 COPY第 4 步安装依赖 — RUN npm ciRUN npm ci你在做什么 构建镜像时按 package-lock.json 精确安装依赖。RUN npm ci --omitdev,只安装生产依赖npm ci vs npm installnpm cinpm install依据严格按 lock 文件可能改 lock场景CI / Docker 构建本地开发为什么装 devDependencies本项目 “start”: “tsx ./index.ts”tsx 在 devDependencies构建时 prisma generate 也需要 prisma CLI。容器启动走 npm start没有这些会失败。生产环境若改用 node dist/index.js 编译产物可再考虑多阶段构建本项目直接用 tsx所以需要 dev 依赖。第 5 步Prisma 单独处理 — COPY RUN generateCOPY prisma ./prisma RUN npx prisma generate你在做什么只拷 prisma/含 schema.prisma 等在容器内生成 Prisma Client → src/generated/prisma两个常见误区误区真相COPY . . 会自带 ClientCOPY 只复制文件不会执行 generate本地已 generate拷进去就行Windows 生成的是 Windows 引擎Linux 容器里往往跑不了为什么放在 COPY . . 之前功能clone/CI 里通常没有生成物.gitignore 已忽略必须在镜像里 generate缓存只改业务代码时不重复 generate改了什么影响只改 src/*.tsgenerate 层缓存命中改 prisma/schema重跑 generate改 package.json从 npm ci 起全重跑第 6 步复制其余源码 — COPY . .COPY . .你在做什么 把项目其余文件拷进 /app受 .dockerignore 过滤。不会覆盖什么已在容器里 npm ci 的 node_modules 不会被宿主机覆盖.dockerignore 排除了 node_modules第 5 步已 generate 的 Client 保留除非本地有旧生成物被拷进去——本项目 ignore 了一般没问题第 7 步声明端口 — EXPOSEEXPOSE 8080你在做什么 文档性声明「这个容器内的 API 监听 8080」与 index.ts 里 PORT 8080 一致。注意EXPOSE 不会自动映射到宿主机不影响 PostgreSQL :5432、Redis :6379它们是别的容器访问需手动映射dockerrun-p3000:8080 your-image# 浏览器 localhost:3000 → 容器内 8080第 8 步启动命令 — CMDCMD [npm, start]你在做什么 容器启动时执行 npm start等价于 tsx ./index.ts。推荐写法 JSON 数组形式 [“npm”, “start”]避免 shell 解析问题。与 RUN 再记一次指令时机RUN npm ci构建镜像时CMD [“npm”, “start”]每次 docker run 启动容器时完整 Dockerfile 一览FROM node:22-bookworm-slim WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY prisma ./prisma RUN npx prisma generate COPY . . EXPOSE 8080 CMD [npm, start]写完后验证步骤# 1. 构建镜像在项目根目录有 Dockerfile 的地方dockerbuild-tnodejs-api.对应到你的命令部分含义docker build构建镜像-t nodejs-api给镜像打标签名字叫 nodejs-api.构建上下文 当前目录# 2. 运行容器需配好 DATABASE_URL、REDIS 等环境变量dockerrun-p8080:8080 --env-file .env my-node-api# 3. 浏览器或 curl 访问curlhttp://localhost:8080构建时观察输出若只改了源码应看到前几步 CACHED说明层缓存生效。小白易错清单错误后果正确做法先 COPY . . 再 npm ci改一行代码就重装全部依赖先拷 lock再 npm ci最后拷源码省略 prisma generate容器启动报找不到 Client构建时在 Linux 容器内 generate用 npm install 代替 npm ci依赖版本不稳定Docker 构建用 npm ci以为 EXPOSE 就能访问宿主机访问不到加 -p 宿主机端口:8080把 .env 打进镜像密钥泄露写进 .dockerignore运行时 -e 或 --env-file 传入用 Alpine Prisma各种 libc/引擎报错本项目选 Debian slim推荐写作顺序记忆口诀FROM 选环境 ↓ WORKDIR 定目录 ↓ COPY 依赖清单 → RUN 装依赖 ← 利用缓存 ↓ COPY prisma → RUN generate ← 跨平台 缓存 ↓ COPY 源码 ↓ EXPOSE 声明端口 ↓ CMD 启动命令