Docker 容器默认是无状态的——容器删除后数据即消失。生产环境必须使用持久化存储。本文详解 Docker 的 Volume、Bind Mount、网络模式及生产级存储策略,并深入底层 UnionFS 实现、内核 bind mount 原理、CSI 标准、Swarm Overlay 网络加密、Kubernetes PV/PVC 编排以及容器存储性能基准测试与选型决策矩阵。
一、数据持久化三种方案
| 方案 | 说明 | 数据位置 | 适用场景 |
|---|---|---|---|
| Volume | Docker 管理 named volume | /var/lib/docker/volumes/ | 生产首选,跨容器共享 |
| Bind Mount | 映射宿主机路径 | 任意目录 | 开发环境代码热重载 |
| tmpfs | 内存挂载 | 宿主机内存 | 敏感数据,不写入磁盘 |
底层实现原理
Docker 容器的文件系统基于 UnionFS(联合文件系统) 构建。UnionFS 将多个只读层与一个可写顶层叠加,使得容器镜像层共享只读数据,而容器的写操作全部落在上层的可写层(copy-on-write)。当容器被删除时,这个可写层随之销毁,这也是容器默认无状态的根本原因。
Volume 原理:Docker Daemon 通过内核的 mount 系统调用,在 /var/lib/docker/volumes/<name>/_data 处创建一个独立的目录,然后在容器启动时以 bind mount 方式挂载到容器指定路径。由于该目录完全独立于容器的可写层,因此容器删除后数据仍然保留。Docker 通过 Volume Driver 插件机制扩展存储后端。
Bind Mount 原理:直接利用 Linux 内核的 bind mount 特性,将宿主机现有目录或文件挂载到容器的文件系统中。bind mount 本质上是在内核 VFS(Virtual File System)层面创建了一个新的挂载点指向同一个 inode,不经过 Docker 的存储管理层。
tmpfs 原理:基于 Linux tmpfs 文件系统,数据完全驻留在内存和 swap 中,不写入任何持久化块设备。适合存放会话密钥、临时缓存等敏感数据。
# Volume(Docker 管理)
docker volume create my-data
docker run -v my-data:/app/data my-app
# Bind Mount(宿主机目录)
docker run -v /host/data:/app/data:ro my-app # 只读
docker run -v /host/data:/app/data:Z my-app # SELinux 私有标签
docker run -v /host/data:/app/data:z my-app # SELinux 共享标签
# tmpfs(内存,限制 100MB,禁止执行)
docker run --tmpfs /app/cache:size=100m,noexec my-app
性能对比
| 指标 | Volume | Bind Mount | tmpfs |
|---|---|---|---|
| 写入延迟 | 中(Docker 管理开销) | 低(直通宿主机 FS) | 极低(内存操作) |
| 跨容器共享 | 原生支持 | 需手动管理路径 | 不支持 |
| 备份迁移 | docker volume backup 插件 | rsync/cp | 不可持久化 |
| 权限隔离 | 良好(命名空间隔离) | 依赖宿主机权限 | 良好 |
Volume Driver 扩展
Docker 支持通过 Volume Driver 对接外部存储系统:
# 使用 NFS Volume Driver
docker volume create --driver local \
--opt type=nfs \
--opt o=addr=192.168.1.100,rw \
--opt device=:/exports/data \
nfs-data
# 使用 Ceph RBD Driver(通过 rexray/rbd 插件)
docker volume create --driver rexray/rbd \
--opt size=20 ceph-volume
二、网络模式
| 模式 | 说明 | 适用 |
|---|---|---|
| bridge | 默认,容器间通过内部 IP 通信 | 单机多容器 |
| host | 容器直接使用宿主机网络 | 性能敏感 |
| overlay | 跨主机容器通信 | Swarm/K8s |
| none | 无网络 | 完全隔离 |
自定义 Bridge 网络详解
默认的 bridge(docker0)网络不附带 DNS 解析能力,容器间只能通过 IP 通信。创建用户自定义 bridge 网络后,Docker 内置的嵌入式 DNS 服务器(监听 127.0.0.11:53)会为同网络容器提供基于容器名的 DNS 发现。
# 创建自定义 bridge 网络,指定子网和网关
docker network create \
--driver bridge \
--subnet=172.28.0.0/16 \
--gateway=172.28.0.1 \
--opt com.docker.network.bridge.name=br_custom \
my-net
# 在网络中启动容器
docker run -d --name db --network my-net --ip 172.28.0.10 postgres:15
docker run -d --name api --network my-net my-api
# api 容器内可直接访问 db:5432,底层由 Docker DNS 解析
网络隔离:不同自定义 bridge 网络之间默认二层隔离,容器无法直接通信。如需跨网络通信,需通过端口映射或显式路由。
macvlan / ipvlan 模式
macvlan 允许为容器分配独立的 MAC 地址和 IP,使其直接暴露在物理二层网络中,如同一台真实物理机。适用于需要与物理网络直接集成的场景(如传统监控系统、遗留中间件)。
# 创建 macvlan 网络
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
macvlan-net
# 启动容器获取独立 IP
docker run --net macvlan-net --ip 192.168.1.100 -d nginx
ipvlan 与 macvlan 类似,但容器共享宿主机的 MAC 地址,通过不同 IP 区分,更适合 MAC 地址受限的数据中心环境。
container 模式
新容器共享另一个容器的网络命名空间(Network Namespace),实现 localhost 直连。
# sidecar 模式:日志采集容器共享应用容器网络
docker run -d --name app my-app
docker run -d --name log-collector --network container:app fluentd
网络排查命令
# 查看网络详情及连接容器
docker network inspect my-net
# 进入容器抓包
docker run --net container:app nicolaka/netshoot tcpdump -i eth0 -w /tmp/capture.pcap
# 验证 DNS 解析
docker run --rm --network my-net nicolaka/netshoot dig db
# 查看容器 iptables 规则
docker run --rm --privileged nicolaka/netshoot iptables -t nat -L -n -v
三、生产存储策略
| 方案 | 说明 |
|---|---|
| 本地 Volume | 单宿主机,最简单 |
| NFS Volume | 跨主机共享存储 |
| 云盘挂载 | AWS EBS / 阿里云 ESSD |
| 分布式存储 | Ceph / Portworx |
CSI 标准(Container Storage Interface)
CSI 是由 Kubernetes 社区主导的存储插件标准,旨在解耦容器编排系统与存储后端实现。Docker 本身不原生使用 CSI,但在 Swarm 和通过外部卷插件可对接 CSI 驱动。在 Kubernetes 生态中,CSI 已成为持久化存储的唯一标准接口。
CSI 核心组件包括:
- CSI Controller:负责卷的创建、删除、附着(Attach)、快照管理等控制面操作。
- CSI Node:运行在每个工作节点上,负责卷的挂载(Mount)、格式化等数据面操作。
云厂商 CSI 插件对比
| 云厂商 | CSI 驱动 | 支持卷类型 | 特性 |
|---|---|---|---|
| AWS | ebs.csi.aws.com | gp3/io1/io2 | IOPS 可配置、加密、快照 |
| AWS | efs.csi.aws.com | EFS | 多可用区挂载、NFS 协议 |
| Azure | disk.csi.azure.com | Standard/Premium SSD | 磁盘加密集、共享磁盘 |
| Azure | file.csi.azure.com | Azure Files | SMB/NFS 双协议 |
| 阿里云 | diskplugin.csi.alibabacloud.com | ESSD/SSD/高效云盘 | 多可用区、加密 |
分布式存储方案
Portworx:专为容器设计的分布式块存储,支持多云环境、细粒度 IO 控制、跨 AZ 复制。
Longhorn: Rancher 开源的轻量级分布式块存储,基于微服务架构,每个卷对应一个独立的控制器和副本集。
Rook:基于 Ceph 的 Kubernetes 云原生存存编排器,通过 Operator 自动化部署 Ceph 集群,提供块存储(RBD)、文件系统(CephFS)和对象存储(RGW)三种接口。
四、Docker Compose 网络与存储编排实战
Docker Compose 通过声明式 YAML 定义多容器应用的网络拓扑和存储依赖。
多网络隔离
version: "3.9"
services:
frontend:
image: nginx
networks:
- public-net
api:
image: my-api
networks:
- public-net
- backend-net
db:
image: postgres:15
networks:
- backend-net
volumes:
- db-data:/var/lib/postgresql/data
networks:
public-net:
driver: bridge
backend-net:
driver: bridge
internal: true # 无外网访问,仅容器间通信
volumes:
db-data:
driver: local
Named Volumes 外部化配置
volumes:
shared-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/nas/shared
cache:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
o: size=500m,uid=1000
五、Swarm Overlay 网络:加密、负载均衡与服务发现
Docker Swarm 的 Overlay 网络基于 Linux VXLAN 技术,在宿主机之间构建虚拟二层网络,使跨主机容器获得同网段通信能力。
创建加密 Overlay 网络
docker network create \
--driver overlay \
--subnet 10.0.9.0/24 \
--opt encrypted \
--attachable \
secure-overlay
--opt encrypted 启用 IPSec 加密,防止跨主机流量被窃听。Swarm 使用 Gossip 协议在各节点间同步网络拓扑信息。
服务发现与负载均衡
Swarm 内置基于虚拟 IP(VIP)的四层负载均衡。创建服务时,Swarm 为该服务分配一个虚拟 IP,并通过 IPVS 在内核层将流量分发到各任务副本。
# 部署三副本 Web 服务
docker service create \
--name web \
--network secure-overlay \
--replicas 3 \
--publish published=8080,target=80 \
nginx
# 查看 VIP 和任务分布
docker service inspect web --format='{{json .Endpoint.VirtualIPs}}'
默认负载均衡策略为轮询(Round Robin)。如需会保持,需通过外部反向代理(如 Traefik、Nginx Ingress)实现七层会话粘性。
六、Kubernetes StorageClass / PersistentVolume / PVC 完整 YAML
Kubernetes 将存储抽象为三个层次:StorageClass(存储类,定义后端类型和参数)、PersistentVolume(持久卷,管理员预置或动态供给的存储资源)、PersistentVolumeClaim(持久卷声明,用户申请存储的接口)。
StorageClass 定义
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # CSI 驱动名称
parameters:
type: gp3
iops: "10000"
throughput: "500"
encrypted: "true"
kmsKeyId: alias/aws/ebs
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
mountOptions:
- debug
PersistentVolume(静态供给)
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-01
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
nfs:
server: 192.168.1.100
path: /exports/data
readOnly: false
PersistentVolumeClaim(动态供给)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 50Gi
Pod 挂载 PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data-pvc
七、容器存储性能基准测试与选型决策矩阵
存储选型需综合考量 IOPS、吞吐量、延迟、可用性、成本和运维复杂度。
fio 基准测试脚本
# 顺序写吞吐测试
docker run --rm -v test-vol:/data \n williamyeh/fio --directory=/data \
--name=seq-write --rw=write --bs=1M --size=1G --numjobs=4 --direct=1
# 随机读 IOPS 测试
docker run --rm -v test-vol:/data \n williamyeh/fio --directory=/data \
--name=rand-read --rw=randread --bs=4k --size=1G --numjobs=16 --direct=1 \
--ioengine=libaio --iodepth=64 --runtime=60
典型场景选型矩阵
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 开发环境代码热重载 | Bind Mount | 实时同步,零延迟 |
| 数据库(MySQL/PostgreSQL) | 本地 SSD Volume / 云盘 | 低延迟、高 IOPS、数据一致性 |
| 海量日志/文件存储 | NFS / CephFS | 多节点共享读写、大容量扩展 |
| AI 训练大模型数据集 | 对象存储(S3/OSS)+ 本地缓存 | 低成本、高吞吐、按需加载 |
| 有状态微服务多 AZ 部署 | Portworx / Longhorn | 跨可用区复制、快照、灾难恢复 |
| 缓存/会话存储 | tmpfs / Redis | 内存级速度,允许数据丢失 |
延迟与 IOPS 参考值
| 存储类型 | 读延迟 | 写延迟 | 随机读 IOPS |
|---|---|---|---|
| tmpfs | < 1us | < 1us | > 1M |
| NVMe 本地盘 | ~50us | ~20us | ~500K |
| 云盘 gp3(AWS) | ~1ms | ~1ms | ~16K(基线) |
| NFS(千兆网) | ~5ms | ~10ms | ~10K |
| Ceph RBD(SSD) | ~2ms | ~3ms | ~50K |
八、网络与安全:iptables/docker-proxy 原理、用户自定义网桥、网络命名空间
docker-proxy 与 iptables
当使用 docker run -p 8080:80 进行端口映射时,Docker 启动一个 docker-proxy 进程监听宿主机 8080 端口,将流量转发到容器的 80 端口。在 Linux 环境下,Docker 同时通过 iptables 的 DNAT 规则实现内核级转发,效率高于用户态代理。
# 查看 Docker 自动添加的 NAT 规则
sudo iptables -t nat -L DOCKER -n -v
# 典型输出包含:
# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80
对于用户自定义网桥,Docker 在 FORWARD 链中添加规则控制容器间及容器到外部网络的流量,默认策略为允许。
网络命名空间(Network Namespace)
每个 Docker 容器拥有独立的网络命名空间,包含独立的网络接口、路由表、防火墙规则和套接字。容器eth0 通过 veth pair 与宿主机 vethxxx 连接,veth 的一端在容器命名空间,另一端接在 docker0 或自定义 bridge 上。
# 查看容器网络命名空间中的接口
sudo nsenter -t $(docker inspect -f '{{.State.Pid}}' <container>) -n ip addr
# 查看宿主机侧的 veth 对端
ip link show | grep veth
# 查看容器的路由表
sudo nsenter -t $(docker inspect -f '{{.State.Pid}}' <container>) -n ip route
安全加固建议
- 禁用默认 bridge:生产环境废弃 docker0,仅使用用户自定义 bridge 以启用 DNS 发现和网络策略。
- 限制容器出站访问:通过
--iptables=false配合自定义防火墙规则,或利用 Docker Network Policy(与 K8s NetworkPolicy 类似的外部项目)限制东西向流量。 - 启用用户命名空间 remapping:
dockerd --userns-remap=default将容器 root 映射到宿主机普通用户,降低容器逃逸后的权限。 - 加密 Overlay 网络:跨主机部署时始终使用
--opt encrypted。
# 用户命名空间 remapping 配置(/etc/docker/daemon.json)
{
"userns-remap": "default"
}
相关阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。