Dockerfile 是"做奶茶的配方"?用 todos 全栈项目看懂 Docker 构建与发布

Dockerfile 是"做奶茶的配方"?用 todos 全栈项目看懂 Docker 构建与发布

上篇文章我们讲了一条请求在 Docker 里怎么走(nginx 反向代理 + 端口映射),评论区不少朋友说想看"怎么把一个项目真正发布成 Docker 镜像"。这篇就接着来。

先看一句话:

蜜雪冰城的 SOP:先加奶、再加奶、放 3 勺糖、摇匀。 任何人照着做,出来的味道都一样,就成了连锁店。

Dockerfile 就是这份 SOP 的程序化版本。

它是一个纯文本的"配方文件",里面按步骤写着"怎么做菜"。Docker 拿到这个文件,就能自动做出一模一样的镜像------不靠人记忆、不靠口口相传,任何人、任何电脑,产出完全一致。

这篇文章用一个真实的 todos 全栈项目(前端 React + 后端 NestJS + nginx),带你把它从头构建成镜像、再发布的完整流程。


一、Dockerfile:一份能自动执行的食谱

普通的菜谱是给人看的,Dockerfile 是给 Docker 引擎 读的。它长这样:

dockerfile 复制代码
# 1. 选一个基础镜像:上面装好了 Node 环境
FROM node

# 2. 在容器里切到 /app 文件夹,设为工作目录
WORKDIR /app

# 3. 复制"原料"(代码)进来
COPY package*.json ./

# 4. 按步骤"下锅":安装依赖
RUN npm install

# 5. 把剩余代码全部拷进容器
COPY . .

# 6. 告诉 Docker 这个容器该暴露哪个端口
EXPOSE 3000

# 7. 上桌:容器启动时执行这个命令
CMD ["node", "server.js"]

每一行都是一个指令,从上到下顺序执行,就像配方里"先加奶、再加奶、放 3 勺糖、摇匀"。

Dockerfile 指令 类比蜜雪 SOP
FROM 选什么牌子的原料 / 锅底
WORKDIR 在工作台上切菜(切到哪个目录)
COPY 把准备好的食材放进锅
RUN 下锅烹饪(执行命令)
EXPOSE 告诉打包箱这个服务会开哪个窗口
CMD 上桌要做什么(容器启动命令)

记法FROM 起头,COPY/RUN 中间干活,CMD 收尾启动。看到 Dockerfile 先找这三个,结构就清楚了。


二、构建到发布:四步走

把食谱变成真正能跑起来的服务,核心就这 4 条命令:

bash 复制代码
docker build -t my-todos .   # 1. 按 Dockerfile 构建镜像
docker login                  # 2. 登录镜像仓库(比如 Docker Hub / 私有仓库)
docker push my-todos          # 3. 把镜像推到远程仓库
docker pull my-todos          # 4. 在别的机器上拉下来跑

一条条拆开看:

docker build -t my-todos .

-t 给镜像起名字,末尾的 . 表示"用当前目录下的 Dockerfile"。

text 复制代码
docker build -t 镜像名 .
   ↑ 名字中间是命令     ↑ 找当前目录的 Dockerfile

理解点 :build 不是"看一遍文件",而是把 Dockerfile 的每一条指令执行一遍,每一层都生成一个缓存层。所以改个小配置再 build 会很快------只有变化的层会重做。

docker login → ③ docker push

镜像构建好后,光在本地没用。要发布给团队 / 服务器用,就推去镜像仓库(类似 git 的远程仓库,但是装的是"光盘"而不是代码)。

bash 复制代码
docker login      # 登录仓库,验证身份
docker push my-todos   # 把本地镜像上传到仓库

上传之后,任何能访问仓库的人 / 机器都能 docker pull my-todos 拉下来。

docker pull + 跑起来

把仓库里的镜像拉下来,run 一个容器就完成了部署:

bash 复制代码
docker pull my-todos
docker run -p 3000:3000 my-todos

Dockerfile 是发布项目的标准方式之一 ------它的价值在于:"构建一次,到处运行"。 本地开发环境、测试环境、生产环境,都是同一个镜像,杜绝"我这能跑你那不能跑"。


三、实战:todos 全栈项目怎么组成镜像

前面是理论,来看我们真实的 todos 项目长什么样。它是一个三件套:

text 复制代码
todos-fullstack/
├── todos/          # 前端:React + TypeScript + zustand
├── todos-backend/  # 后端:NestJS + Todo Module
└── (nginx)         # 反向代理:把前端 80 → 后端 3000

项目架构非常典型:

text 复制代码
浏览器
  │  :80
  ▼
nginx(反向代理)
  │  转发
  ▼
NestJS 后端  :3000
  ▲
  └── Todo Module(CRUD:增删改查)

每个部分都有自己的 Dockerfile

前端(React + Vite):

dockerfile 复制代码
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build          # 打包出静态文件

FROM nginx
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

后端(NestJS):

dockerfile 复制代码
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build          # 编译 TS 到 dist
EXPOSE 3000
CMD ["node", "dist/main.js"]

跨域问题在这套架构里怎么解

前端请求后端时,浏览器会拦跨域(前端是 80,后端是 3000,端口不同 = 跨域)。两种主流解法:

解法一:Nginx 反向代理(推荐,生产用)前端和后端同源------所有请求都走 80,由 nginx 转发:

nginx 复制代码
server {
    listen 80;
    location /api/ {
        proxy_pass http://backend:3000;   # 80 → 3000,前端无感知
    }
}

同源后浏览器根本不认为在跨域,一劳永逸。

解法二:后端开启 CORS(开发用) 后端直接允许跨域,适合本地调试。NestJS 里一行:

typescript 复制代码
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({ origin: true, credentials: true });
  await app.listen(3000);
}

记住:生产环境多用 Nginx 反代(同源),开发环境可以偷懒用 CORS。 两者目的都是解决跨域,只是放的位置不同。


四、命令速查

收尾把构建发布相关的命令整理成一张表:

命令 作用 类比
docker build -t 名 . 按 Dockerfile 构建镜像 按 SOP 做了一杯奶茶
docker login 登录镜像仓库 出示员工卡进连锁店后台
docker push 名 镜像推到远程仓库 把配方发到总部中央库
docker pull 名 从仓库拉镜像 从总部取配方
docker run 名 跑一个容器 照配方做一杯递给你
docker images 看本地镜像列表 看看库存

结尾

回到开头那个比喻:

Dockerfile 就是蜜雪冰城的 SOP。 它把"怎么从零做一个能跑的 todo 应用"写成了人(引擎)能照做的指令:FROM 选环境 → COPY 拷代码 → RUN 装依赖 → CMD 启动。构建成镜像后 push / pull,就完成了标准化发布。

一句话收尾:

Dockerfile 是发布项目的标准方式之一------把"我这能跑"变成"哪都能跑"。

留个问题给你:我们演示的后端用 node dist/main.js 直接跑。如果要把后端也压进 nginx 反代后面的容器网络 里(而不是端口直通),proxy_pass 应该写 localhost 还是别的?这正好衔接上一篇的 Docker 网络话题,欢迎评论区聊聊。


相关推荐
meilindehuzi_a1 小时前
从浏览器输入 URL 到页面显示:DNS、Nginx、后端与 CDN 全链路解析
运维·nginx
NGINX开源社区2 小时前
使用 F5 NGINX Gateway Fabric 实现 AI 交付标准化
nginx·gateway·fabric
lswzw3 小时前
K8s容器网络排查实战:从宿主机穿透命名空间定位端口监听问题
开发语言·docker·容器
wazmlp00188736918 小时前
k8s pod 管理
docker·容器·kubernetes
江湖有缘19 小时前
Docker开源项目: 基于Ubuntu系统部署ExpenseOwl自托管记账工具实战教程
ubuntu·docker·开源
流星白龙21 小时前
【Docker】9.Docker 镜像仓库实战
运维·docker·容器
张洛闻Eren21 小时前
云原生k8s【第六课】:K8s 访问控制
运维·docker·云原生·容器·kubernetes·k8s
用户938515635071 天前
从同源策略到Nginx代理:React+NestJS全栈项目跨域实战
后端·docker
识途老码1 天前
docker运行sqlserver
docker·容器·sqlserver