Docker 学习 5

一、数据管理

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。

相关推荐
A黄俊辉A1 小时前
两分钟上手docker
运维·docker·容器
程序员老赵3 小时前
Docker 部署 go2rtc:轻松搭建摄像头多协议流媒体平台
前端·docker·直播
努力努力再努力wz4 小时前
【Docker入门系列】:从架构演进到容器化:一文建立 Docker、虚拟化与 Namespace 的底层心智模型
运维·开发语言·数据结构·c++·docker·容器·架构
吴佳浩 Alben4 小时前
多智能体系统的通信风暴与死锁治理:生产级降级与容灾方案
人工智能·docker·ai·容器·架构
玉&心5 小时前
在dockers desktop上面部署k8s的前端和后端
云原生·容器·kubernetes
wdfk_prog5 小时前
Docker 29 与 containerd image store:镜像为什么会保存两种形态
运维·docker·容器
张洛闻Eren5 小时前
云原生k8s【第七课】:集群监控与可观测性
linux·运维·docker·云原生·容器·kubernetes·k8s
Y38153266216 小时前
Docker Compose 服务依赖与健康检查:healthcheck 让容器按正确顺序启动
运维·docker·容器
2601_9652354817 小时前
快速了解docker
docker·容器·dubbo