一、数据管理
1.1、特性
| 特性 | 说明 |
|---|---|
| 持久化 | 容器删除后数据仍然保留 |
| 共享 | 多个容器可以挂载同一个数据卷 |
| 即时生效 | 对数据卷的修改立即可见 |
| 不影响镜像 | 数据卷中的数据不会打包进镜像 |
| 性能更好 | 绕过 UnionFS,直接读写 |
1.2、基本操作
1.2.1、创建数据卷
bash
$ docker volume create my-vol
创建在宿主机上 。
docker volume create 创建的是 Docker 管理的命名卷,存放在宿主机的 Docker 存储目录里
1.2.2、挂载数据卷
方式一:--mount:推荐
bash
$ docker run -d \
--name web \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx
参数说明:
| 参数 | 说明 |
|---|---|
| source | 数据卷名称(不存在会自动创建) |
| target | 容器内挂载路径 |
| readonly | 可选,只读挂载 |
方式二:-v:简写
bash
$ docker run -d \
--name web \
-v my-vol:/usr/share/nginx/html \
nginx
格式:-v 数据卷名:容器路径:选项
对比
| 特性 | --mount |
-v |
|---|---|---|
| 语法 | 键值对,更清晰 | 冒号分隔,更简洁 |
| 数据卷(Volume)挂载行为 | 卷不存在会自动创建,与 -v 结果一致 |
卷不存在会自动创建 |
| 绑定挂载(Bind Mount)行为 | ⭐ 宿主机路径不存在会报错,不会自动创建 | 宿主机路径不存在会自动创建为目录 |
| 推荐程度 | ✅ 推荐(更明确安全,避免误创建) | 常用(更简洁) |
数据卷(Volume)挂载 VS 绑定挂载(Bind Mount)
两者的区别只在于宿主机上的"位置和管理权"
| 特性 | Bind Mount | Volume |
|---|---|---|
| 数据位置 | 宿主机任意路径 | Docker 管理的目录 |
| 路径指定 | 必须是绝对路径 | 卷名 |
| 可移植性 | 依赖宿主机路径 | 更好(Docker 管理) |
| 性能 | 依赖宿主机文件系统 | 优化的存储驱动 |
| 适用场景 | 开发环境、配置文件 | 生产数据持久化 |
| 备份 | 直接访问文件 | 需要通过 Docker |
两者都是"数据存宿主机、容器只是去读写"。唯一区别是这块宿主机存储的位置由 Docker 安排还是由你指定。
什么时候用哪个?
| 需求 | 推荐方案 |
|---|---|
| 开发时同步代码 | Bind Mount |
| 持久化数据库数据 | Volume |
| 共享配置文件 | Bind Mount |
| 容器间共享数据 | Volume |
| 备份方便 | Bind Mount(直接访问) |
| 生产环境 | Volume |
开发环境要频繁改文件 → bind mount;生产环境存数据要安全省心 → volume。
二、网络配置
2.1、 DNS
- Docker 在每个自定义网络的容器中内置 DNS 服务器(127.0.0.11)
- 容器名 → IP 自动解析:容器 IP 变化也不影响,靠名字稳定找到对方(服务发现)
- 外部域名(如 google.com)→ 转发给上游 DNS 处理
bash
docker network create mynet
docker run -d --name web --network mynet nginx
docker run --rm --network mynet alpine ping web # ✅ 解析为 172.18.0.2
2.2、创建自定义网络
bash
## 创建网络
$ docker network create mynet
2.3、使用自定义网络
启动容器时通过 --network 参数指定连接的网络
bash
## 启动容器并连接到自定义网络
$ docker run -d --name web --network mynet nginx
$ docker run -d --name db --network mynet postgres
## 在 web 容器中可以直接用容器名访问 db
$ docker exec web ping db
PING db (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.083 ms
2.4、同一网络内的容器
同一自定义网络内的容器可以直接通过容器名通信
bash
## 创建网络
$ docker network create app-net
## 启动应用和数据库
$ docker run -d --name redis --network app-net redis
$ docker run -d --name app --network app-net myapp
## app 容器中可以用 redis:6379 连接 Redis
2.5、连接到多个网络
一个容器可以同时连接到多个网络,这对于需要跨网络通信的中间件容器特别有用:
bash
## 启动容器
$ docker run -d --name multi-net-container --network frontend nginx
## 再连接到另一个网络
$ docker network connect backend multi-net-container
## 查看容器的网络
$ docker inspect multi-net-container --format '{{json .NetworkSettings.Networks}}'
容器 multi-net-container
├── 网卡1 → frontend 网络(IP 如 172.18.0.2)
└── 网卡2 → backend 网络(IP 如 172.19.0.2)
2.6、外部访问容器
容器运行在自己的隔离网络环境中(通常是 Bridge 模式)。为了让外部网络访问容器内的服务,我们需要将容器的端口映射到宿主机的端口。
1. 指定映射
使用 -p <宿主机端口>:<容器端口> 格式:
bash
## 将宿主机的 8080 端口映射到容器的 80 端口
$ docker run -d -p 8080:80 nginx
2.7、网络隔离
Docker 网络提供了天然的隔离能力,不同网络之间的容器默认无法通信。这是 Docker 网络安全的重要基础。不同网络之间默认隔离,容器只能与同一网络中的容器直接通信:
bash
## 创建两个网络
$ docker network create frontend
$ docker network create backend
## 容器 A 在 frontend
$ docker run -d --name web --network frontend nginx
## 容器 B 在 backend
$ docker run -d --name db --network backend postgres
## web 无法直接访问 db(不同网络)
$ docker exec web ping db
ping: db: Name or service not known
跨网络通信
如果确实需要某个容器跨网络通信,可以将其同时连接到多个网络:
bash
## 创建一个中间件容器,连接到两个网络
$ docker run -d --name api --network frontend myapi
$ docker network connect backend api
## 现在 api 容器既可以访问 frontend 中的 web,也可以访问 backend 中的 db
2.8、容器网络高级特性
Overlay 是 Docker 用来解决"跨宿主机容器怎么通信"的方案
它要解决什么问题?
你有多台宿主机,每台上面跑容器:
bash
宿主机A (172.16.0.1) 宿主机B (172.16.0.2)
├─ 容器A: 192.168.0.2 ├─ 容器B: 192.168.0.3
└─ docker0 网桥 └─ docker0 网桥
容器A 想和 容器B 通信,有两个麻烦:
- IP 冲突:每台宿主机都独立分配 172.17.0.x,两台机器上的容器 IP 很容易重复,你没法直接用 IP 找对方
- 路由不通:容器 IP 是内网的,中间物理网络(路由器)不认识,不会帮你转发
Overlay 的思路:在两个宿主机之间打一条"虚拟隧道",让两台机器上的容器看起来像在同一个局域网里。
核心原理:VXLAN 隧道封装
关键一步:VXLAN 封装。Docker 把这个容器包整个塞进一个 UDP 数据包里:
bash
┌─────────────────────────────────────┐
│ 外层UDP包: 源172.16.0.1 → 目标172.16.0.2:4789 │ ← 物理网络看得懂的地址
│ ┌───────────────────────────────┐ │
│ │ 内层: 源192.168.0.2 → 目标192.168.0.3:80 │ │ ← 真正的容器通信内容
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
这就像寄快递再套一个快递箱:外层箱子写着宿主机之间的地址(物理网络负责送),内层才是容器A给容器B的信。
物理网络转发:中间的交换机/路由器只看到外层 UDP 包 172.16.0.1 → 172.16.0.2,正常转发,完全不知道里面藏着容器流量。
2.9、容器 DNS 解析机制
bash
【容器内部】
应用: "我要访问 baidu.com,先查 DNS"
↓
查看容器自己的 /etc/resolv.conf
→ "去问 127.0.0.11"
↓
127.0.0.11 = 容器内部的 Docker 内嵌 DNS(就在容器里,不是外面的服务器)
↓
内嵌 DNS 查表:"baidu.com 是容器名吗?" → 不是
↓ "那我问上游去"
【离开容器,来到宿主机/网络层】
转发给上游 DNS(宿主机的 DNS 或你 --dns 指定的 8.8.8.8)
↓
上游 DNS 逐级查询,返回 110.242.68.66
↓
答案沿原路返回给容器里的应用
【容器内部】
应用拿到 IP,开始访问 baidu.com
三、Docker Compose
3.1、解决什么问题
Compose 把"管理一堆容器"的复杂性(启动顺序、网络、挂载、配置、命令记忆)压缩成一个声明式 YAML 文件 + 一条命令,让多容器应用的本地开发和测试从"手工拼装"变成"一键启停"。
3.2、组成部分
- 服务 (service):一个应用容器,实际上可以运行多个相同镜像的实例。
- 项目(project):由一组关联的应用容器组成的一个完整业务单元。
服务(Service)= 一个"工种"
服务是运行相同镜像的一组容器。关键点:它强调的往往不是"一个容器",而是"这个角色/工种"。
bash
services:
web: # 这是一个服务:跑 Flask 的
build: .
redis: # 这是一个服务:跑缓存的
image: redis:alpine
这里有 2 个服务:web 和 redis。
项目(Project)= 一"桌菜"
项目是一组关联服务组成的完整业务单元------也就是你的整个应用。
继续上面的例子:web + redis 这两个服务合起来,就是一个项目(比如叫 "counter-app")。一条命令管理的就是这个项目:
bash
docker compose up # 启动整个项目(web 和 redis 一起起来)
docker compose down # 停掉整个项目
项目在 Docker 里体现为一组资源:网络名、容器名都会自动加上项目名前缀来隔离:
bash
项目名 counter-app:
├─ 网络: counter-app_default
├─ 容器: counter-app-web-1
└─ 容器: counter-app-redis-1
两者的关系
bash
项目 (Project) ← 整个应用,如 counter-app
├── 服务 web ← 工种1:处理请求
│ └── 容器实例 ×1
└── 服务 redis ← 工种2:存计数
└── 容器实例 ×1
3.3、Compose 模板文件
镜像与构建(服务从哪来)
| 指令 | 作用 |
|---|---|
image |
直接用现成镜像,如 image: redis:alpine |
build |
从 Dockerfile 构建。可简写 build: .,也可展开指定路径/替换 Dockerfile/传参: |
bash
build:
context: ./dir # 构建上下文
dockerfile: Dockerfile-alternate
args:
buildno: 1
容器运行行为
| 指令 | 作用 |
|---|---|
command |
覆盖镜像默认 CMD,如 command: echo "hello world" |
entrypoint |
覆盖入口 |
working_dir |
工作目录 |
user |
以哪个用户运行 |
restart |
重启策略,如 restart: always |
container_name |
固定容器名(默认自动生成) |
privileged |
特权模式(谨慎使用) |
cap_add / cap_drop |
增删 Linux capabilities |
read_only |
容器文件系统只读 |
stdin_open / tty |
交互模式(配合 docker compose exec 用) |
网络与端口
bash
ports:
- "3000" # 随机映射宿主机端口
- "8000:8000" # 宿主机8000 → 容器8000
- "127.0.0.1:8001:8001" # 只绑本地回环
- expose:只向同网络容器暴露,不映射到宿主机
- networks:接入哪些网络(默认自动建一个)
- network_mode:host/none/bridge 等
- dns / dns_search / extra_hosts:DNS 配置(之前学过)
存储
bash
volumes:
- /var/lib/mysql # 匿名卷
- cache/:/tmp/cache # bind mount(相对路径)
- ~/configs:/etc/configs/:ro # 只读挂载
- mysql_data:/var/lib/mysql # 命名卷(需在顶层 volumes: 声明)
配置与依赖
bash
depends_on:
db:
condition: service_healthy # 等 db 健康检查通过才启动,而不只是"起了就行"
- environment:直接写环境变量(注意 yes/no/on/off 会被 YAML 解析成布尔值,需加引号)
- env_file:从.env 文件批量导入
总结
Compose 模板本质就是 docker run 所有参数 + 多服务编排 的声明式写法。用的时候对照查即可,日常高频的就十来项:image/build、ports、volumes、environment、depends_on、networks、healthcheck、restart。