速看要点:
本文重点:
存储架构图:
┌─────────────────────────────────────────┐
│ Docker存储层次结构 │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────┐ │
│ │ 容器可写层 (Container Layer) │ │
│ └─────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────┐ │
│ │ 镜像只读层 (Image Layers) │ │
│ │ ┌───────────────────────────┐ │ │
│ │ │ Layer 3: Application │ │ │
│ │ ├───────────────────────────┤ │ │
│ │ │ Layer 2: Runtime │ │ │
│ │ ├───────────────────────────┤ │ │
│ │ │ Layer 1: Base OS │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ 存储驱动 (Storage Driver) │ │
│ │ Overlay2 / AUFS / Btrfs ... │ │
│ └─────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ 主机文件系统 │ │
│ │ ext4 / xfs / btrfs ... │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
东巴文理解:
Docker存储 = 分层存储系统
就像:一本书的每一页都是独立的
存储组成:
├─ 镜像层(Image Layers): 只读,不可修改
├─ 容器层(Container Layer): 可写,存储修改
├─ 存储驱动(Storage Driver): 管理分层存储
└─ 主机文件系统: 底层存储介质
工作原理:
├─ 镜像由多个只读层组成
├─ 容器在镜像层之上添加可写层
├─ 修改文件时使用Copy-on-Write
└─ 存储驱动管理层的合并和访问
CoW原理:
Copy-on-Write(写时复制):
├─ 读取文件: 直接从镜像层读取
├─ 修改文件: 复制到容器层,然后修改
├─ 删除文件: 在容器层标记删除
└─ 节省空间,提高效率
示例:
┌─────────────────────────────────────┐
│ 容器层: /etc/config.conf (修改后) │
├─────────────────────────────────────┤
│ 镜像层: /etc/config.conf (原始) │
└─────────────────────────────────────┘
读取: 直接读取镜像层
修改: 复制到容器层 → 修改容器层副本
删除: 在容器层创建删除标记
驱动列表:
Overlay2(推荐):
├─ 性能优秀
├─ 稳定性好
├─ 支持主流文件系统
└─ Docker默认驱动
AUFS:
├─ 历史悠久
├─ 稳定性好
├─ 需要内核支持
└─ Ubuntu默认
Btrfs:
├─ 支持快照
├─ 支持压缩
├─ 需要Btrfs文件系统
└─ 适合高级用户
Device Mapper:
├─ 块级存储
├─ 性能一般
├─ RHEL/CentOS默认
└─ 生产环境不推荐
ZFS:
├─ 支持快照
├─ 支持压缩
├─ 需要ZFS文件系统
└─ 适合高级用户
VFS:
├─ 简单实现
├─ 性能较差
├─ 不支持CoW
└─ 仅用于测试
查看当前驱动:
# 查看Docker信息
docker info | grep "Storage Driver"
# 输出
Storage Driver: overlay2
# 查看详细信息
docker info | grep -A 10 "Storage Driver"
# 输出
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
修改存储驱动:
# 编辑Docker配置文件
vim /etc/docker/daemon.json
# 添加配置
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
# 重启Docker
systemctl restart docker
# 验证修改
docker info | grep "Storage Driver"
数据卷优势:
数据卷特点:
├─ 独立于容器生命周期
├─ 数据持久化存储
├─ 容器间共享数据
├─ 高效数据迁移
└─ 易于备份和恢复
与容器存储对比:
┌──────────────────┬──────────────┬──────────────┐
│ 特性 │ 容器存储 │ 数据卷 │
├──────────────────┼──────────────┼──────────────┤
│ 生命周期 │ 跟随容器 │ 独立管理 │
│ 数据持久化 │ 容器删除丢失 │ 永久保存 │
│ 容器间共享 │ 不支持 │ 支持 │
│ 性能 │ 较低 │ 较高 │
│ 备份恢复 │ 困难 │ 简单 │
└──────────────────┴──────────────┴──────────────┘
三种类型:
Volume(命名卷):
├─ Docker管理
├─ 存储在/var/lib/docker/volumes/
├─ 推荐使用
└─ 适合持久化数据
Bind Mount(绑定挂载):
├─ 主机目录挂载
├─ 可以挂载到任意位置
├─ 适合开发环境
└─ 配置文件、代码目录
tmpfs(临时文件系统):
├─ 存储在内存中
├─ 容器停止后消失
├─ 适合敏感数据
└─ 密钥、证书
基本语法:
docker volume COMMAND
COMMAND:
create 创建数据卷
inspect 查看数据卷详情
ls 列出数据卷
prune 删除未使用的数据卷
rm 删除数据卷
创建数据卷:
# 创建数据卷
docker volume create myvolume
# 创建数据卷并指定驱动
docker volume create --driver local myvolume
# 创建数据卷并指定选项
docker volume create \
--driver local \
--opt type=tmpfs \
--opt device=tmpfs \
--opt o=size=100m,uid=1000 \
myvolume
# 查看数据卷
docker volume ls
# 输出
DRIVER VOLUME NAME
local myvolume
查看数据卷:
# 查看数据卷详情
docker volume inspect myvolume
# 输出
[
{
"CreatedAt": "2024-01-01T00:00:00Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
"Name": "myvolume",
"Options": {},
"Scope": "local"
}
]
# 查看数据卷内容
ls /var/lib/docker/volumes/myvolume/_data
删除数据卷:
# 删除指定数据卷
docker volume rm myvolume
# 删除所有未使用的数据卷
docker volume prune
# 输出
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
myvolume
# 强制删除
docker volume prune -f
创建容器时挂载数据卷:
# 使用-v参数
docker run -d --name mynginx -v myvolume:/usr/share/nginx/html nginx:latest
# 使用--mount参数(推荐)
docker run -d --name mynginx \
--mount source=myvolume,target=/usr/share/nginx/html \
nginx:latest
# 查看挂载信息
docker inspect mynginx | grep -A 10 "Mounts"
# 输出
"Mounts": [
{
"Type": "volume",
"Name": "myvolume",
"Source": "/var/lib/docker/volumes/myvolume/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
]
挂载参数对比:
# -v 参数(简写)
-v myvolume:/usr/share/nginx/html:ro
格式: [卷名]:[容器路径]:[选项]
选项:
ro: 只读
rw: 读写(默认)
z: 共享SELinux标签
Z: 私有SELinux标签
# --mount 参数(详细)
--mount source=myvolume,target=/usr/share/nginx/html,readonly
格式: key=value,多个参数用逗号分隔
参数:
source: 数据卷名称
target: 容器内路径
readonly: 只读
volume-driver: 指定驱动
数据库数据持久化:
# MySQL数据持久化
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=root123 \
-v mysql_data:/var/lib/mysql \
mysql:5.7
# 即使删除容器,数据仍然保留
docker rm -f mysql
# 重新创建容器,挂载相同数据卷
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=root123 \
-v mysql_data:/var/lib/mysql \
mysql:5.7
# 数据仍然存在!
共享数据卷:
# 创建共享数据卷
docker volume create shared_data
# 创建容器1,写入数据
docker run -d --name app1 \
-v shared_data:/data \
alpine sh -c "echo 'Hello from app1' > /data/hello.txt && sleep 3600"
# 创建容器2,读取数据
docker run -d --name app2 \
-v shared_data:/data \
alpine cat /data/hello.txt
# 输出
Hello from app1
# 查看日志
docker logs app2
备份数据卷:
# 备份数据卷到tar文件
docker run --rm \
-v myvolume:/data \
-v $(pwd):/backup \
alpine tar czf /backup/myvolume_backup.tar.gz -C /data .
# 查看备份文件
ls -lh myvolume_backup.tar.gz
# 输出
-rw-r--r-- 1 root root 1.2K Jan 1 00:00 myvolume_backup.tar.gz
恢复数据卷:
# 创建新数据卷
docker volume create myvolume_restored
# 恢复数据
docker run --rm \
-v myvolume_restored:/data \
-v $(pwd):/backup \
alpine sh -c "cd /data && tar xzf /backup/myvolume_backup.tar.gz"
# 验证恢复
docker run --rm \
-v myvolume_restored:/data \
alpine ls -l /data
特点:
绑定挂载:
├─ 挂载主机目录到容器
├─ 主机目录必须存在
├─ 可以挂载到任意位置
├─ 性能优于数据卷
└─ 适合开发环境
优势:
✅ 性能优秀
✅ 配置简单
✅ 易于访问
✅ 适合开发调试
劣势:
❌ 依赖主机路径
❌ 可移植性差
❌ 安全风险
❌ 不推荐生产环境
挂载主机目录:
# 创建主机目录
mkdir -p /data/nginx/html
# 创建测试文件
echo "Hello Docker" > /data/nginx/html/index.html
# 使用-v参数挂载
docker run -d --name mynginx \
-v /data/nginx/html:/usr/share/nginx/html \
-p 8080:80 \
nginx:latest
# 使用--mount参数挂载(推荐)
docker run -d --name mynginx \
--mount type=bind,source=/data/nginx/html,target=/usr/share/nginx/html \
-p 8080:80 \
nginx:latest
# 访问测试
curl http://localhost:8080
# 输出
Hello Docker
只读挂载:
# 只读挂载
docker run -d --name mynginx \
--mount type=bind,source=/data/nginx/html,target=/usr/share/nginx/html,readonly \
-p 8080:80 \
nginx:latest
# 尝试修改
docker exec mynginx sh -c "echo 'test' > /usr/share/nginx/html/test.txt"
# 输出
sh: can't create /usr/share/nginx/html/test.txt: Read-only file system
开发环境:
# 挂载代码目录
docker run -d --name myapp \
-v /home/user/myapp:/app \
-p 3000:3000 \
node:14
# 挂载配置文件
docker run -d --name nginx \
-v /data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /data/nginx/html:/usr/share/nginx/html \
-p 80:80 \
nginx:latest
# 挂载日志目录
docker run -d --name app \
-v /var/log/app:/var/log/app \
myapp:latest
共享主机文件:
# 共享主机时间
docker run -d --name myapp \
-v /etc/localtime:/etc/localtime:ro \
myapp:latest
# 共享主机DNS配置
docker run -d --name myapp \
-v /etc/resolv.conf:/etc/resolv.conf:ro \
myapp:latest
# 共享主机证书
docker run -d --name myapp \
-v /etc/ssl/certs:/etc/ssl/certs:ro \
myapp:latest
特点:
tmpfs挂载:
├─ 存储在内存中
├─ 容器停止后数据消失
├─ 读写速度极快
├─ 适合临时数据
└─ 适合敏感数据
优势:
✅ 性能极高
✅ 安全性好
✅ 不占用磁盘
✅ 自动清理
劣势:
❌ 数据不持久
❌ 占用内存
❌ 容量有限
创建tmpfs挂载:
# 使用--tmpfs参数
docker run -d --name myapp \
--tmpfs /tmp \
myapp:latest
# 使用--mount参数(推荐)
docker run -d --name myapp \
--mount type=tmpfs,target=/tmp \
myapp:latest
# 指定大小和权限
docker run -d --name myapp \
--mount type=tmpfs,target=/tmp,tmpfs-size=100m,tmpfs-mode=1777 \
myapp:latest
临时文件存储:
# 存储临时文件
docker run -d --name myapp \
--tmpfs /tmp:rw,size=100m \
myapp:latest
# 存储会话数据
docker run -d --name php \
--tmpfs /var/lib/php/sessions \
php:7.4-fpm
敏感数据存储:
# 存储密钥
docker run -d --name myapp \
--tmpfs /run/secrets \
myapp:latest
# 存储密码
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=root123 \
--tmpfs /var/run/mysqld \
mysql:5.7
工作原理:
Overlay2存储结构:
┌─────────────────────────────────────┐
│ Merged View (容器视图) │
│ /usr /bin /etc /var /home ... │
└──────────────┬──────────────────────┘
│
┌───────┴───────┐
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Upper │ │ Lower │
│ (可写层) │ │ (只读层) │
│ 容器修改 │ │ 镜像层 │
└─────────────┘ └─────────────┘
Lower层: 镜像的只读层
Upper层: 容器的可写层
Merged: 合并后的视图
Work: 工作目录
文件操作:
读取文件:
├─ 在Upper层查找
├─ 如果存在,直接读取
└─ 如果不存在,从Lower层读取
修改文件:
├─ 从Lower层复制到Upper层
├─ 在Upper层修改
└─ Lower层保持不变
删除文件:
├─ 在Upper层创建whiteout文件
└─ 标记文件已删除
查看配置:
# 查看存储驱动信息
docker info | grep -A 10 "Storage Driver"
# 输出
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
优化配置:
# 编辑Docker配置
vim /etc/docker/daemon.json
# 添加配置
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.size=20G",
"overlay2.override_kernel_check=true"
]
}
# 重启Docker
systemctl restart docker
配置Device Mapper:
# 编辑Docker配置
vim /etc/docker/daemon.json
# 添加配置
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}
# 重启Docker
systemctl restart docker
配置Btrfs:
# 创建Btrfs文件系统
mkfs.btrfs -f /dev/sdb
# 挂载Btrfs
mount /dev/sdb /var/lib/docker
# 编辑Docker配置
vim /etc/docker/daemon.json
# 添加配置
{
"storage-driver": "btrfs"
}
# 重启Docker
systemctl restart docker
性能排序:
性能从高到低:
1. tmpfs (内存)
2. Bind Mount (主机文件系统)
3. Volume (Docker管理)
4. 容器存储 (Overlay2)
说明:
├─ tmpfs: 最快,但数据不持久
├─ Bind Mount: 性能优秀,适合开发
├─ Volume: 性能良好,推荐生产
└─ 容器存储: 性能一般,适合临时数据
Overlay2优化:
# 使用高性能文件系统
# 推荐: ext4, xfs
# 调整文件系统参数
tune2fs -O ^has_journal /dev/sda1
# 禁用文件系统访问时间
mount -o remount,noatime /var/lib/docker
# 配置Docker
vim /etc/docker/daemon.json
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
选择合适的存储类型:
应用场景选择:
├─ 数据库: Volume + 高性能磁盘
├─ 日志: Bind Mount + 日志轮转
├─ 配置文件: Bind Mount + 只读
├─ 临时数据: tmpfs
└─ 代码目录: Bind Mount
性能优化:
├─ 使用SSD存储
├─ 合理设置挂载选项
├─ 定期清理无用数据
└─ 监控存储使用情况
Volume(命名卷):
├─ Docker管理
├─ 数据持久化
├─ 容器间共享
├─ 易于备份
└─ 推荐生产环境
Bind Mount(绑定挂载):
├─ 主机目录挂载
├─ 性能优秀
├─ 配置简单
├─ 依赖主机路径
└─ 推荐开发环境
tmpfs(临时文件系统):
├─ 内存存储
├─ 性能极高
├─ 数据不持久
├─ 适合敏感数据
└─ 推荐临时数据
数据卷管理:
├─ docker volume create: 创建数据卷
├─ docker volume ls: 列出数据卷
├─ docker volume inspect: 查看数据卷详情
├─ docker volume rm: 删除数据卷
└─ docker volume prune: 删除未使用的数据卷
容器挂载:
├─ -v: 简写挂载参数
├─ --mount: 详细挂载参数
└─ --tmpfs: tmpfs挂载
生产环境:
✅ 使用Volume存储数据
✅ 使用高性能存储介质
✅ 定期备份重要数据
✅ 监控存储使用情况
开发环境:
✅ 使用Bind Mount挂载代码
✅ 使用Bind Mount挂载配置
✅ 方便实时修改和调试
安全建议:
✅ 敏感数据使用tmpfs
✅ 配置文件只读挂载
✅ 定期清理无用数据
✅ 设置存储配额
存储驱动:
✅ 使用Overlay2驱动
✅ 使用高性能文件系统
✅ 禁用文件系统访问时间
✅ 定期清理无用镜像和容器
数据卷:
✅ 使用SSD存储
✅ 合理设置挂载选项
✅ 避免大量小文件
✅ 定期清理无用数据卷
下一章: Docker Compose基础
将学习:
数据卷管理: 创建一个名为"mydata"的数据卷,查看其详情,然后删除它。
数据持久化: 创建一个MySQL容器,使用数据卷存储数据库文件,插入测试数据后删除容器,重新创建容器验证数据是否保留。
绑定挂载: 创建一个nginx容器,挂载主机目录到容器,修改主机文件后在浏览器中访问验证。
容器间数据共享: 创建两个容器,使用同一个数据卷共享数据,在一个容器中写入数据,在另一个容器中读取。
数据备份恢复: 创建一个包含重要数据的数据卷,将其备份到tar文件,然后恢复到新的数据卷中。
存储性能对比: 创建三个容器,分别使用Volume、Bind Mount和tmpfs存储数据,测试它们的读写性能差异。
完整应用存储方案: 为一个Web应用设计存储方案,要求:
存储监控脚本: 编写一个脚本,监控Docker存储使用情况,包括: