如何理解 Docker可以把 Docker 想成把应用和它需要的一切代码、运行时、库、配置打包成一个标准「箱子」在任何机器上都能同样运行。Docker Desktop 界面正好对应 Docker 的几个核心概念下文结合界面逐项说明。用生活类比理解Docker 概念类比镜像 (Image)一道菜的菜谱 / 模具 — 只读模板描述「这个应用长什么样」容器 (Container)按菜谱做出来的菜 — 镜像跑起来后的实例Docker Engine厨房 — 负责构建、运行、管理容器底部「Engine running」端口映射 (Ports)窗口 — 让外面能访问容器里的服务如6379:6379Volume冰箱 — 数据持久化容器删了数据还在解决什么问题痛点「在我电脑上能跑在你电脑上不行」— 环境不一致。传统方式装 Node、Redis、MySQL版本、路径、系统差异都可能导致问题。Docker 方式开发、测试、生产用同一套镜像不污染本机环境Redis 在容器里不必在本机安装一键启停、可复制、可迁移Docker 镜像、容器、网络、卷1. 镜像Image是什么一套已经装好的迷你操作系统 程序文件 启动方式可以直接运行只读模板分层存储不能直接「改完就保存」要 commit 或重新 build格式一般是名称:标签如redis:7-alpineredis官方 Redis 镜像7-alpineRedis 7 Alpine 小体积 Linux 基础镜像一个镜像可以创建多个容器同一菜谱做多道菜镜像里实际有什么以redis:7-alpine为例打开后大致是这样一棵目录树/ ← 一个完整的 Linux 根目录Alpine很小 ├── bin/sh, ls ... ← 基础命令 ├── usr/local/bin/redis-server ← Redis 7 程序 ├── etc/redis/... ← 默认配置 └── 默认启动命令: redis-server以你自己的 Node 项目为例镜像可能是/ ├── node:18 的运行环境 ← Node.js 18 ├── /app/package.json ├── /app/node_modules/ ← 依赖 ├── /app/dist/ ← 打包后的前端/后端代码 └── 默认启动命令: node /app/dist/server.js每个应用不一样是因为每个镜像的内容不一样——有人帮你做好官方镜像或你自己用 Dockerfile 做。两种镜像来源来源谁做的例子别人做好的Docker Hub 官方/社区redis:7-alpine、node:18、nginx你自己做的写 Dockerfile → docker build你的 projectManagement 前端项目Dockerfile自定义应用的「组装说明书」每个应用不同就用不同的 Dockerfile例如# 第1层基础环境 FROM node:18-alpine # 第2层拷贝依赖清单安装依赖 WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci # 第3层拷贝你的代码 COPY . . # 第4层构建 定义怎么启动 RUN npm run build CMD [npm, start]docker build会按这份说明书一层层叠起来最后得到镜像my-project:v1.0.0。所以 镜像 具体文件 运行环境 完整流程获取镜像二选一 ├── docker pull redis:7-alpine ← 下载别人做好的 └── docker build -t my-nodejs-api:v1 . ← 自己构建 ↓ 运行容器 └── docker run ... redis:7-alpine ← 从镜像创建并启动实例2. 容器Container是什么容器是镜像跑起来的一个实例每行是一个独立环境有自己的 ID、状态、端口、进程表里各列含义列示例说明Nameredis-study容器名字你起的Container ID9fe36f791e5a唯一 IDImageredis:7-alpine它从哪个镜像创建Port(s)6379:6379端口映射和网络有关Status已停止当前是否在跑容器里实际有什么容器不是镜像的完整拷贝而是镜像 一层薄薄的「可写层」 正在跑的进程容器 redis-study 运行时的结构 ┌─────────────────────────────────┐ │ 可写层临时 │ ← 运行时产生的改动写在这里 │ 例如/data/dump.rdb、日志、pid │ 删容器后这层默认消失 ├─────────────────────────────────┤ │ 镜像层只读来自 redis:7-alpine│ ← redis-server、Alpine 系统 │ 所有容器共享同一套镜像层不重复占空间 │ └─────────────────────────────────┘ 同时还在跑 进程: redis-server主进程容器活着就靠它 网络: 内网 IP 你映射的 6379 端口 可选: 挂载的卷见第 4 节容器是怎么来的容器不能凭空创建必须来自某个镜像# 一条命令做两件事① 从镜像创建容器 ② 启动它dockerrun-d--nameredis-study-p6379:6379 redis:7-alpine# │ │ │ │# │ │ │ └── 用哪个镜像# │ │ └── 端口映射见网络一节# │ └── 给容器起名# └── 后台运行执行后 Docker 内部大致做了这些1. 检查本地有没有 redis:7-alpine 镜像 → 没有就自动 pull 2. 在镜像之上创建可写层 3. 分配网络内网 IP 4. 执行镜像里定义的启动命令 → redis-server 跑起来 5. 容器状态变为 Running出现在 Docker Desktop 的 Containers 列表一个镜像 → 多个容器正确理解一个容器 镜像的一份运行实例通常只跑一个主服务。redis:7-alpine同一个镜像 │ ┌───────┴───────┐ ▼ ▼ redis-study redis-test 端口 6379 端口 6380 独立进程 独立进程 数据默认不共享 数据默认不共享dockerrun-d--nameredis-study-p6379:6379 redis:7-alpinedockerrun-d--nameredis-test-p6380:6379 redis:7-alpine# 两次 run同一个镜像得到两个互不影响的容器3. 网络Network是什么Docker 会在你的电脑上建一张虚拟局域网让多个容器像同一 WiFi 下的设备一样互访每个容器启动时会自动分配一个内网 IP如172.17.0.2-p 6379:6379是端口映射开一扇门和 IP 分配是两件不同的事网络里实际有什么以默认的bridge网络为例跑起redis-study和nodejs后大致是这样你的电脑 (Host) IP: 192.168.x.x局域网 localhost 127.0.0.1 │ │ Docker 虚拟网络 bridge172.17.0.0/16 │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ redis-study nodejs │ │ │ IP: 172.17.0.2 IP: 172.17.0.3 │ │ │ 监听 :6379 监听 :3000 │ │ │ │ │ └──────────────────────────────────────────────┘ │ └── -p 6379:6379 把 Host 的 6379 转发到 redis-study 的 6379每个容器内部就像一台小电脑有自己的 IP、端口、网卡和宿主机、和其他容器隔离。三种通信方式端口映射 vs 内网互访 vs 访问宿主机很多人只见过-p以为网络就是端口号。其实有三类常见场景方式 1从宿主机访问容器 → 用端口映射dockerrun-d--nameredis-study-p6379:6379 redis:7-alpine# └── 宿主机端口:容器端口你的 Node 应用宿主机 │ │ 连接 localhost:6379 ▼ 你电脑的 6379 端口 ←── Docker 在这里“接客” │ │ NAT / 端口映射-p 6379:6379 配置的规则 ▼ 容器 redis-study 内部: redis-server 在 172.17.0.2:6379 上监听-p没有给容器分配一个对外的公网 IP只是在宿主机上开一个端口转发到容器。方式 2容器之间互访 → 用内网 IP 或容器名# 在 nodejs 容器里连 redis同一 bridge 网络下redis-cli-h172.17.0.2-p6379更推荐自定义网络 容器名Docker 内置 DNS不用记 IPdockernetwork create my-netdockerrun-d--nameredis-study--networkmy-net redis:7-alpinedockerrun-d--namenodejs--networkmy-net node:18# nodejs 里直接写主机名IP 变了也不影响# 连接串: redis-study:6379方式 3从容器访问宿主机上的服务 → 用host.docker.internal适用场景PostgreSQL、Redis 等装在你本机或 WSL上而 Node API跑在容器里。dockerrun--rm-p8080:8080-eDATABASE_URLpostgresql://postgres:你的密码host.docker.internal:5432/nodejs_study?schemapublic-eREDIS_URLredis://host.docker.internal:6379-eJWT_SECRET你的JWT_SECRETnodejs-api部分含义docker run从镜像创建并启动一个容器--rm容器停止后自动删除不留 stopped 容器-p 8080:8080端口映射宿主机8080 → 容器内8080浏览器访问 localhost:8080 即访问容器里的 API-e KEYvalue向容器内注入环境变量Node 应用通过 process.env 读取PowerShell 换行续行符bash 里用 \nodejs-api要运行的镜像名需先 docker build -t nodejs-api .容器 nodejs-api 内部 │ │ 连接 host.docker.internal:5432 / :6379 │ 不能写 localhost —— 容器里的 localhost 是容器自己不是宿主机 ▼ Docker 提供的「回到宿主机」的特殊主机名 │ │ host.docker.internal → 转发到宿主机 ▼ 你的电脑 (Host) localhost:5432 ← PostgreSQL 监听在这里 localhost:6379 ← Redis 监听在这里为什么不能写localhost容器里的localhost永远指容器自己不是宿主机也不是别的容器谁在连想连谁写localhost:6379实际连到哪里你在宿主机上redis-study 容器宿主机自己 → 经-p转发进容器 ✅nodejs-api 容器redis-study 容器nodejs-api 自己→ 连不上 redis-study ❌应用redis-study:6379见方式 2nodejs-api 容器宿主机上的 Redisnodejs-api 自己→ 容器内没有 Redis ❌应用host.docker.internal:6379见方式 3为什么host.docker.internal:6379有时「也能连上」如果redis-study做了-p 6379:6379理论上nodejs-api 容器 → host.docker.internal:6379 → 宿主机 6379 → 端口映射 → redis-study 容器能通但这是绕宿主机一圈不是容器直连。Windows / MacDocker Desktophost.docker.internal默认可用无需额外配置。Linux部分环境没有该主机名启动时需加dockerrun --add-hosthost.docker.internal:host-gateway...Windows 上要注意的一点Docker Desktop 在 Windows 上跑在WSL2 虚拟机里用localhost:6379访问容器 → 一般没问题Desktop 做了转发容器 IP172.17.0.x→ 主要在 Docker 内部有效Windows 上不一定能直接 ping 通从本机访问容器优先用localhost 端口映射容器里连宿主机上的服务用host.docker.internal不要用localhost4. 卷Volume是什么挂在容器上的独立硬盘专门存要长期保留的数据典型用途数据库文件、用户上传、需要保留的配置卷里实际有什么以 Redis 为例数据默认写在/data目录。用卷数据在独立存储容器 redis-study └── /data ──挂载──► 卷 redis-data 容器里看到的 /data实际读写的是卷里的文件 docker rm redis-study → 容器没了但卷 redis-data 还在 docker run 新容器 -v redis-data:/data → 数据完整恢复三种挂载方式知道 volume 即可方式写法示例谁管理典型场景卷 volume-v redis-data:/dataDocker数据库、生产数据推荐绑定挂载 bind mount-v D:/mydata:/data你指定宿主机路径开发时同步本地代码tmpfs--tmpfs /tmp内存临时文件重启即没小白阶段优先用 volume路径不用自己管备份迁移也方便。