Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像

Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像

前几篇讲的是:

  • 怎么拉别人做好的镜像;
  • 怎么用 docker run 启动容器;
  • 怎么理解 Docker 网络。

但是学 Docker 最关键的一步是:

如何把自己的项目做成镜像。

这个过程靠的就是 Dockerfile。


1. Dockerfile 是什么?

Dockerfile 是一个文本文件,里面写着构建镜像的步骤。

你可以把它理解成:

text 复制代码
Dockerfile = 镜像制作说明书

比如一个最小 Node 项目的 Dockerfile:

dockerfile 复制代码
FROM node:22-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

然后执行:

bash 复制代码
docker build -t my-node-app:1.0 .

就能构建出一个镜像:

bash 复制代码
docker images

运行:

bash 复制代码
docker run -d --name my-node-app -p 3000:3000 my-node-app:1.0

2. Dockerfile 的核心指令

2.1 FROM:基于哪个镜像

dockerfile 复制代码
FROM node:22-alpine

任何 Dockerfile 基本都从 FROM 开始。

它表示:

我的镜像不是从零开始,而是基于一个已有镜像继续加工。

比如:

dockerfile 复制代码
FROM nginx:alpine
FROM node:22-alpine
FROM mongo:7

实际项目中,不建议随便写 latest,因为它会变化。

更推荐写明确版本:

dockerfile 复制代码
FROM node:22-alpine

2.2 WORKDIR:设置工作目录

dockerfile 复制代码
WORKDIR /app

后面的命令默认都在 /app 目录下执行。

相当于:

bash 复制代码
cd /app

如果目录不存在,Docker 会自动创建。


2.3 COPY:复制文件到镜像里

dockerfile 复制代码
COPY package*.json ./
COPY . .

第一句:把本地的 package.jsonpackage-lock.json 复制到镜像的 /app

第二句:把当前目录其他文件复制进去。

为什么不直接先 COPY . .

因为 Docker 构建有缓存机制。

更推荐这样写:

dockerfile 复制代码
COPY package*.json ./
RUN npm ci
COPY . .

这样只要依赖文件没变,npm ci 这一层就可以复用缓存,加快构建。


2.4 RUN:构建阶段执行命令

dockerfile 复制代码
RUN npm ci
RUN npm run build

RUN 是在构建镜像时执行。

它和 CMD 的区别非常重要:

指令 执行时机
RUN docker build 构建镜像时
CMD docker run 启动容器时

2.5 ENV:设置默认环境变量

dockerfile 复制代码
ENV NODE_ENV=production

这样容器运行时默认有这个环境变量。

但更敏感的配置,比如数据库密码,不建议写死在 Dockerfile 里。

更推荐运行时传入:

bash 复制代码
docker run -e MONGO_URL=xxx my-api:1.0

或者在 Compose 里配置。


2.6 EXPOSE:声明容器使用哪个端口

dockerfile 复制代码
EXPOSE 3000

注意:

EXPOSE 只是声明,不等于自动映射端口。

真正让宿主机访问容器,还要靠:

bash 复制代码
-p 3000:3000

EXPOSE 更像是告诉别人:这个容器内部服务监听的是 3000 端口。


2.7 CMD:容器启动命令

dockerfile 复制代码
CMD ["npm", "start"]

它表示容器启动后默认执行什么命令。

推荐使用 JSON 数组形式:

dockerfile 复制代码
CMD ["node", "dist/main.js"]

而不是:

dockerfile 复制代码
CMD node dist/main.js

数组形式更清晰,也更适合信号处理。


3. 为 NestJS 后端写 Dockerfile

假设项目是 NestJS 后端:

text 复制代码
nest-server/
  src/
  package.json
  package-lock.json
  tsconfig.json
  nest-cli.json

可以写:

dockerfile 复制代码
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

COPY package*.json ./
RUN npm ci --omit=dev

COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD ["node", "dist/main.js"]

这是一个多阶段构建。

它的思路是:

text 复制代码
第一阶段 deps:安装完整依赖
第二阶段 builder:编译 TypeScript
第三阶段 runner:只保留生产运行需要的文件

好处是最终镜像更干净。


4. 为 React 前端写 Dockerfile

假设前端是 React/Vite:

text 复制代码
web/
  src/
  package.json
  index.html
  vite.config.ts

Dockerfile:

dockerfile 复制代码
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine AS runner
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

思路:

text 复制代码
Node 阶段:负责安装依赖、打包前端
Nginx 阶段:只负责托管 dist 静态文件

前端最终不需要 Node.js 运行环境,只需要 Nginx 托管静态资源。


5. .dockerignore 很重要

很多人写 Dockerfile,会忘记 .dockerignore

它类似 .gitignore,用于告诉 Docker 构建时不要把某些文件复制进去。

建议:

dockerignore 复制代码
node_modules
dist
build
.git
.gitignore
Dockerfile
.dockerignore
npm-debug.log
.env
.env.*

如果不写 .dockerignore,可能会导致:

  • 构建上下文很大;
  • 本地 node_modules 被复制进镜像;
  • .env 等敏感文件进镜像;
  • 构建速度变慢。

6. 构建镜像

在 Dockerfile 所在目录执行:

bash 复制代码
docker build -t my-api:1.0 .

解释:

部分 含义
docker build 构建镜像
-t my-api:1.0 镜像名和标签
. 构建上下文是当前目录

查看镜像:

bash 复制代码
docker images

运行镜像:

bash 复制代码
docker run -d --name my-api -p 3000:3000 my-api:1.0

查看日志:

bash 复制代码
docker logs -f my-api

7. 镜像标签怎么理解?

镜像名通常长这样:

text 复制代码
my-api:1.0

其中:

text 复制代码
my-api  是镜像名
1.0     是 tag

如果不写 tag,默认是 latest

bash 复制代码
docker build -t my-api .

等价于:

bash 复制代码
docker build -t my-api:latest .

但在实际项目里,不建议完全依赖 latest

更推荐:

bash 复制代码
docker build -t my-api:2026-06-29 .
docker build -t my-api:v1.0.0 .
docker build -t my-api:commit-abc123 .

这样出问题时更容易回滚。


8. 第四篇小结

Dockerfile 的主线是:

text 复制代码
选择基础镜像
  ↓
设置工作目录
  ↓
复制依赖声明文件
  ↓
安装依赖
  ↓
复制项目代码
  ↓
构建项目
  ↓
声明端口
  ↓
指定启动命令

常见指令:

指令 作用
FROM 基础镜像
WORKDIR 工作目录
COPY 复制文件
RUN 构建阶段执行命令
ENV 默认环境变量
EXPOSE 声明端口
CMD 容器启动命令

下一篇讲 Docker Compose:不用手写一堆 docker run,用一个 YAML 同时启动前端、后端和 MongoDB。


参考资料

相关推荐
lichenyang4531 小时前
Docker 学习笔记(三):Docker 网络、bridge、子网和容器互通
docker·容器
lichenyang4531 小时前
Docker 学习笔记(二):docker run 的参数到底在控制什么?
docker·容器
Patrick_Wilson5 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
Suroy5 天前
DockerView-Go:用 Go 写一个终端 Docker 监控工具,顺便做了个 Web 仪表盘
docker
云恒要逆袭5 天前
运行你的第一个Docker容器
后端·docker·容器
宋均浩6 天前
# Docker 镜像瘦身实战:从 1.2G 到 80MB 的五个优化步骤
ci/cd·docker
程序员老赵7 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
WangMingHua1117 天前
LM Studio Docker 部署——本地大模型一键启动
docker
曲幽8 天前
别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了
python·docker·web·pot·translate·libretranslate·arogstranslate