Docker 容器化入门:从「我电脑能跑」到「哪台机器都能跑」
本文是一篇 Docker 学习日志。用一个「Nginx 反向代理 Node 服务」的最小 demo,把 Docker 最核心的概念------**镜像(image)、容器(container)、端口映射(-p)、卷挂载(-v)**一次性讲透。文末还附上「用 Docker 跑 MySQL」和运维必考的「正向代理 / 反向代理」知识点。
目录
- [一、为什么需要 Docker](#一、为什么需要 Docker "#%E4%B8%80%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81docker")
- [二、Docker 是什么:一句话类比](#二、Docker 是什么:一句话类比 "#%E4%BA%8Cdocker%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%80%E5%8F%A5%E8%AF%9D%E7%B1%BB%E6%AF%94")
- [三、两个核心概念:image 与 container](#三、两个核心概念:image 与 container "#%E4%B8%89%E4%B8%A4%E4%B8%AA%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5image-%E4%B8%8E-container")
- 四、环境准备
- [五、实战:Nginx 反向代理 Node 服务](#五、实战:Nginx 反向代理 Node 服务 "#%E4%BA%94%E5%AE%9E%E6%88%98nginx-%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86-node-%E6%9C%8D%E5%8A%A1")
- [六、端口映射 -p 详解](#六、端口映射 -p 详解 "#%E5%85%AD%E7%AB%AF%E5%8F%A3%E6%98%A0%E5%B0%84--p-%E8%AF%A6%E8%A7%A3")
- [七、卷挂载 -v 详解](#七、卷挂载 -v 详解 "#%E4%B8%83%E5%8D%B7%E6%8C%82%E8%BD%BD--v-%E8%AF%A6%E8%A7%A3")
- 八、常用命令速查
- [九、附:用 Docker 跑一个 MySQL](#九、附:用 Docker 跑一个 MySQL "#%E4%B9%9D%E9%99%84%E7%94%A8-docker-%E8%B7%91%E4%B8%80%E4%B8%AA-mysql")
- [十、运维考点:正向代理 vs 反向代理](#十、运维考点:正向代理 vs 反向代理 "#%E5%8D%81%E8%BF%90%E7%BB%B4%E8%80%83%E7%82%B9%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86-vs-%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86")
- 十一、总结
一、为什么需要 Docker
每个开发者大概都遇到过这样的对话:
甲:「我电脑上跑得好好的呀。」 乙:「那为什么我 clone 下来跑不起来?」
这就是**「我电脑能跑,你电脑怎么跑」问题的本质。一个 Web 项目,除了你的代码本身,还依赖一大坨有版本要求的运行环境**:
- Node(还要对版本,比如
Node 16 + npm 8) - Redis、MySQL 这类中间件
- Next.js / React 这类框架
- 各种系统级依赖
举一个真实的例子 🌰:
你到公司接手了一个 N 年前的 Vue2 老项目,要求
Node 16 + npm 8,但你自己电脑装的是Node 22。装回老版本又怕影响别的项目,来回切换环境,折腾一下午项目还没跑起来。
Docker 干的事情,就是把这「代码 + 运行环境」整体打包成一个集装箱,在任何一台装了 Docker 的机器上,一条命令就能跑起来,和宿主机环境完全隔离、互不干扰。
二、Docker 是什么:一句话类比
先给一个非常直观的类比:
ini
Agent = LLM + Harness(tool + mcp + rag + skill + ...)
Docker = 应用 + 运行环境
- Agent 是把「大模型」和「工具/知识/技能」这些能力打包在一起,成为一个能干活的东西;
- Docker 是把「应用程序」和「它需要的运行环境」打包在一起,成为一个能运行的东西。
再往生活里想:Docker 就像海运的万吨巨轮,用标准化的**集装箱(container)**装货,不管里面装的是衣服还是家具,码头(宿主机)都按统一标准吊装。开发者就是那个「货主」,你只负责把货(应用 + 环境)装箱,剩下的运输(部署)交给 Docker。
三、两个核心概念:image 与 container
这是新手最容易混淆的一对概念,用「光盘」和「DVD 播放」来理解就通了:
| 概念 | 类比 | 说明 |
|---|---|---|
| image(镜像) | 光盘 | 只读的「应用程序 + 环境」模板,安装包一样的存在 |
| container(容器) | 光盘放进 DVD 播放 | image 运行起来后的实例,可读可写,运行态 |
几个关键关系:
- image 是静态的模板 ,container 是 image 的运行实例;
- 一个 image 可以同时启动成多个 container,彼此隔离;
- 就像「从 git 仓库
pull代码到本地」,你也可以docker pull一个镜像,再docker run它。
记住一句话:image 是「光盘」,container 是「正在播放的光盘」。
四、环境准备
到 Docker 官网 下载 Docker Desktop 安装即可(Windows / macOS 一键安装,Linux 走包管理器)。
装好后验证:
bash
docker --version
# Docker version 24.0.7, build ...
docker run hello-world
# 打印出一段欢迎信息,说明 Docker 正常工作
五、实战:Nginx 反向代理 Node 服务
下面这个 demo 要演示一件很「运维」的事:用户访问 http://localhost(80 端口),由 Nginx 代理转发给一个跑在 1234 端口上的 Node 服务。
5.1 先写一个极简 Node 服务
demo/index.js:
javascript
// node 早期是 commonjs 规范,这里用了 ESM
import { createServer } from 'http';
const server = createServer((req, res) => {
res.end('hello world');
});
server.listen(1234, '0.0.0.0', () => {
console.log('server is running at http://0.0.0.0:1234');
});
注意 0.0.0.0:它表示「监听所有网卡」,这样容器外部(宿主机)才能访问到这个端口。
demo/package.json(重点是 "type": "module",让 Node 识别 import 语法):
json
{
"name": "demo",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module"
}
5.2 写 Nginx 反向代理配置
demo/nginx.conf:
nginx
# nginx.conf
events {}
http {
server {
listen 80; # nginx 监听 80 端口
location / {
# 把请求反向代理到宿主机的 1234 端口(也就是我们的 Node 服务)
proxy_pass http://host.docker.internal:1234;
proxy_set_header Host $host;
}
}
}
host.docker.internal是 Docker 提供的特殊主机名,指向宿主机,用来让容器访问宿主机的服务。
5.3 一条命令启动 Nginx 容器
bash
docker run \
--name my-nginx-demo \
-p 80:80 \
-v D:/workspace/zj-ai/backend/docker/demo/nginx.conf:/etc/nginx/nginx.conf \
-d nginx
先在本机把 Node 服务跑起来(node demo/index.js),再执行上面命令。然后在浏览器打开 http://localhost,就能看到 hello world ------ 你的请求经历了一段完整的旅程:
csharp
浏览器输入 http://localhost
→ 宿主机 80 端口(-p 映射到容器 80)
→ 容器内 nginx 监听 80
→ nginx.conf 把请求 proxy_pass 到 host.docker.internal:1234
→ 宿主机 1234 端口的 Node 服务
→ 返回 hello world
下面把这条命令里的每个参数拆开讲。
六、端口映射 -p 详解
bash
-p 80:80
# │ └── 容器内部端口(container 的 80,nginx 的监听端口)
# └───── 宿主机端口(本机的 80)
规则:-p 宿主机端口:容器端口。
- 用户浏览器访问
http://localhost:80(浏览器默认就是 80,可以省略端口号); - Docker 把宿主机 80 端口的请求,转发给容器内部的 80 端口;
- 容器内的 nginx 监听的正是 80 端口。
为什么要做这一层映射?因为容器有自己独立的网络命名空间 ,它内部的端口默认对外是不可见的。-p 就是把「宿主机端口」和「容器端口」搭一座桥。
常见用法:宿主机
8080映射容器80,写-p 8080:80,访问http://localhost:8080。
七、卷挂载 -v 详解
bash
-v D:/workspace/zj-ai/backend/docker/demo/nginx.conf:/etc/nginx/nginx.conf
# │ 宿主机文件路径 │ 容器内路径
# └────────────────────── 冒号分隔,左边宿主机,右边容器 ──────┘
规则:-v 宿主机路径:容器路径。
它的作用是:把宿主机上的一个文件或目录,映射到容器内部的指定路径。
- 这里我们把本机的
nginx.conf挂载到容器内的/etc/nginx/nginx.conf; - 好处:改配置不用重新打包镜像,直接改本机文件、重启容器即可生效;
- 数据卷(volume)也是持久化数据的关键手段------容器删了,挂载的数据还在宿主机上。
想象成「共享文件夹」:你在宿主机里改文件,容器里立刻就能看到,两边其实是同一份数据。
八、常用命令速查
bash
# 拉取镜像
docker pull nginx
docker pull mysql:8.0
# 运行镜像为容器(-d 后台运行)
docker run -d --name xxx -p 80:80 nginx
# 查看正在运行的容器
docker ps
# 停止 / 删除容器(常用组合拳:先停后删)
docker stop $(docker ps -q) # 停止所有运行中的容器
docker rm $(docker ps -aq) # 删除所有容器
# 删除镜像
docker rmi nginx
# 进入容器内部的 Linux 终端
docker exec -it xxx /bin/bash
参数速记:
| 参数 | 作用 |
|---|---|
--name |
给容器起名字 |
-d |
后台运行(detached) |
-p |
端口映射 |
-v |
卷挂载 |
-e |
设置环境变量 |
-it |
交互式终端 |
九、附:用 Docker 跑一个 MySQL
不用在本地装 MySQL,一条命令搞定:
bash
# 拉取 MySQL 8.0 镜像(版本号要写清楚,不同版本行为不一样)
docker pull mysql:8.0
# 运行:宿主机 3307 映射容器 3306,并设置 root 密码
docker run -d \
--name mysql-demo \
-p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0
-p 3307:3306:宿主机 3307 → 容器 3306(因为本地可能已经装了 MySQL 占了 3306,所以映射到 3307);-e MYSQL_ROOT_PASSWORD=123456:通过环境变量给容器传root密码。
进入容器操作数据库:
bash
docker exec -it mysql-demo /bin/bash # 进入容器 Linux 终端
mysql -uroot -p # 输入密码 123456 登录 MySQL
这就是 Docker 最爽的地方:装一个 MySQL 只要两条命令,不想用了
docker rm一键删干净,不污染宿主机环境。
十、运维考点:正向代理 vs 反向代理
这是运维、后端面试的高频考点,用「用户上网」这条链路一次讲清。
10.1 正向代理(Forward Proxy)
代理客户端。用户知道自己要访问目标网站,但通过一个「中间人」去访问。
用户 → 正向代理服务器 → 目标网站
典型场景:翻墙 / 公司内网代理。客户端明确把请求发给代理,代理替它去请求真正的服务器。
10.2 反向代理(Reverse Proxy)
代理服务端。用户以为自己在直接访问网站,其实背后是一台代理服务器在帮忙转发、负载均衡。
scss
用户 → (以为是目标) → 反向代理(nginx) → 真正的后端服务(1234 端口)
典型场景:我们上面的 demo 就是反向代理------用户只看到 localhost:80,根本不知道后端其实跑在 1234 端口。
一句话区分:
- 正向代理:客户端知道代理,服务器不知道真实客户端;
- 反向代理:客户端不知道真实服务器,服务器知道「我背后是谁」。
反向代理的价值:隐藏后端真实地址、负载均衡、统一入口(SSL、限流、缓存),所以 Nginx 在大厂架构里几乎是标配。
十一、总结
这篇文章我们用一个小 demo,把 Docker 最核心的东西串了起来:
- 为什么用:解决「我电脑能跑,你电脑跑不了」的环境不一致问题;
- 核心概念:image 是「光盘」(只读模板),container 是「播放中的光盘」(运行实例);
- 两条关键命令参数 :
-p端口映射(打通宿主机和容器),-v卷挂载(共享文件 / 持久化数据); - 一个实战 :
Nginx 反向代理 Node,理解了「正向代理」和「反向代理」的本质区别。
Docker 的下一步是 Dockerfile (把应用也打包进镜像)和 docker-compose(一次编排多个容器),有了本文的基础,上手会非常顺。
一句话记住 Docker 的定位 :
Docker = 应用 + 运行环境,就像Agent = LLM + Harness,都是「打包起来、随处可用」的思想。