适合有 JavaScript / Node.js 开发经验、但零容器经验的工程师。
📌 导语
你接手了一个三年前的 Vue2 老项目,文档写着需要 Node 16 + npm 8,而你电脑装的是 Node 22 ------ 跑不起来。这几乎是每个开发者都会遇到的困境:「我的电脑能跑,你的电脑怎么跑?」。这篇文章就带你一次看懂 Docker 怎么解决这个问题。
一、💡 核心概念速览
Docker 是一个应用容器化工具,解决的核心问题就是上面那句「我的电脑能跑,你的电脑怎么跑」。
这句话背后有两组精妙的等式:
text
Agent = LLM + Harness(tool + mcp + rag + skill ...)
Docker = 应用 + 运行环境
你的代码从来不是孤立的------它依赖 Node、Redis、Next、React、MySQL 等一堆有版本要求的运行环境 。Docker 做的就是把这些依赖和代码打包成一个整体的容器,从而方便地部署到任何设备上。
经典类比 :镜像(Image)相当于「光盘」,是「应用程序 + 环境」的隔离封装;容器(Container)相当于「DVD」,是光盘被「运行」起来的实例。获取镜像就像
git pull一样简单。
二、🔧 关键命令与操作清单
1. 运行容器:docker run
以一个「启动 nginx 镜像」的例子来演示 docker run,逐字拆解每个参数:
bash
docker run \
--name my-nginx-demo \
-p 80:80 \
-v D:\workspace\nginx.conf:/etc/nginx/nginx.conf \
-d nginx
| 参数 | 含义 |
|---|---|
--name my-nginx-demo |
给容器起名字 |
-p 80:80 |
本机80端口 : 容器80端口,做端口映射(80 是 nginx 监听端口) |
-v 本机路径:容器路径 |
把本机配置文件挂载进容器 |
-d nginx |
后台运行 nginx 镜像 |
因为 nginx 配置里
listen 80,而用户浏览器访问的是http://localhost:80,所以 需要用-p把「本机 80」转发映射到「容器 80」。
2. 构建与分发镜像
bash
docker build -t my-hello . # 用 Dockerfile 构建镜像,-t 指定名字
docker login # 登录镜像仓库(如 GitHub)
docker push my-hello # 推送镜像
docker pull my-hello # 拉取镜像
3. 运维常用命令
bash
docker pull mysql:8.0 # 拉取任意想要的镜像
docker stop $(docker ps -q) # 停止所有运行中的容器
docker rm $(docker ps -aq) # 删除所有容器
docker rmi nginx # 删除 nginx 镜像
三、📦 Dockerfile:容器化的「标准操作手册」
Dockerfile 可以类比成蜜雪冰城的标准操作手册(SOP):写清「先加 xx、再加 xx、再加 xx」,任何人照着做,出来的味道都一样,于是就成了连锁店。
它本质上是一个文本配方文件,里面写着一行行做菜的指令,Docker 照着做就能自动「做」出一个一模一样的镜像:
dockerfile
# 1. 选一个基础镜像
FROM node
# 2. 在容器里切到 /app 文件夹,设置工作目录
WORKDIR /app
# 3. 把本地代码复制进容器
COPY index.js .
# 4. 启动命令
CMD ["node", "index.js"]
配合一句 console.log('hello,Docker! 我跑到容器里了'),一个可运行的最小镜像就诞生了。
Dockerfile 是发布项目的标准方式之一。
四、🌐 Nginx 反向代理:一条真实数据流
从「运维」视角看一次用户请求的完整走向(正向代理 + 反向代理):
text
用户上网 intent
-> browser(Chrome)【正向代理】
-> localhost:80
-> docker -p(端口映射)
-> container:80
-> -v 映射的配置文件(本机 -> /etc/nginx/nginx.conf)
-> nginx:80(配置中代理了 1314 端口)
<- 1314(反向代理,Node 服务实际监听端口)
对应的配置与 Node 服务:
nginx
# nginx.conf
events {}
http {
server {
listen 80;
location / {
proxy_pass http://host.docker.internal:1314;
proxy_set_header Host $host;
}
}
}
js
// 早期 commonjs 规范,Node 服务监听 1314 端口
server.listen(1314, '0.0.0.0', () => {
console.log('node service run on 1314');
});
因为 nginx 监听 80 端口的高并发访问、并把请求转发给 1314,所以 用户(localhost)根本不知道后端实际跑在哪个端口------这就是反向代理的意义。nginx 常用于「高并发、代理转发」场景。
五、⚠️ 常见误区与避坑指南
-
混淆「镜像」与「容器」 :镜像(Image)是「光盘」------静止、隔离的封装;容器(Container)是「DVD」------光盘被运行起来的实例。构建得到的是镜像,
docker run出来才是容器。 -
端口映射方向写反 :
-p 80:80是「本机:容器」,不是「容器:本机」。本机端口必须写在冒号左边。 -
host.docker.internal不能少 :容器内访问宿主机服务要用这个地址,不能写localhost------因为容器里的localhost指向容器自己。 -
别忽略「运行环境」的版本问题:Node 项目依赖特定 Node/npm 版本,换了环境就「跑不起来」。容器的价值在于把「有版本要求的运行环境」一并打包,而不仅是打包代码。
-
跨域本质是「同源策略」 :全栈项目中
5173:3000之所以跨域,是因为同源策略把前后端不同端口视为「不同源」。解决思路有三类:CORS 服务器端配置响应头 、Vite 代理配置 、axios baseURL:'/api'配合 Nginx 反向代理。
六、🎯 实战场景映射
场景一:全栈项目容器化与跨域
一个「前端 React + TS + zustand + axios,后端 Nest.js + Todo Module」的全栈项目,核心考点落在跨域:
text
后端接口统一为 /api/todos
前端 axios 封装时把 baseURL 设为 '/api',配合 Nginx 反向代理统一转发
规避「5173:3000」的同源限制
跨域(CORS,Cross-Origin Resource Sharing,跨域资源共享)的解决方式:CORS 服务器端配置响应头 、或 Vite 代理配置。
这一步把「nginx 反向代理」和「全栈跨域」两个知识点,落地到日常开发最头疼的「前后端联调」场景。
场景二:多镜像编排(Milvus)
当一个服务需要多个镜像时,用 docker-compose 文件来编排多个 image 的工作流。
一个 Milvus 的 docker-compose.yml 可以一口气编排三个服务:
etcd(元数据存储)、minio(对象存储)、standalone(Milvus 主服务)- 通过
depends_on声明启动顺序(standalone 依赖 etcd 与 minio) - 通过
healthcheck做健康检查、ports映射端口、volumes挂载持久化目录
yaml
services:
etcd:
image: quay.io/coreos/etcd:v3.5.25
# ... 省略配置
minio:
image: minio/minio
# ... 省略配置
standalone:
image: milvusdb/milvus:v3.0.0
command: ["milvus", "run", "standalone"]
depends_on:
- etcd
- minio
对应的业务代码是「Milvus 向量库 + OpenAI Embeddings」的 AI 应用,用 MilvusClient 建集合、建索引、插入向量数据------这是「容器编排」和「AI 向量检索」结合的典型落地。
七、小结
一套清晰的学习主线:
text
为什么(版本不一致跑不起来) -> 是什么(镜像=光盘,容器=DVD)
-> 怎么做(run 跑容器 -> Dockerfile 建镜像 -> push/pull 分发)
-> 进阶(nginx 反代 -> compose 编排 -> 全栈与 AI 落地)
掌握这六步,你就已经能把手头的 Node 项目、全栈项目、甚至 AI 向量库服务都「装进集装箱」,在任何一台机器上原样跑起来了。