目录架构图说明6.1 为什么要用数据卷Docker 分层文件系统的问题Docker 数据卷的优势Docker 提供的两种卷类型6.2 bind mount 数据卷核心概念准备工作基础示例权限控制示例6.3 docker managed 数据卷核心概念镜像内置卷示例命名卷操作清理未使用的 Docker 数据卷只读命名卷示例6.4 数据卷容器Data Volume Container核心概念建立数据卷容器使用数据卷容器6.5 bind mount 数据卷和 docker managed 数据卷的对比相同点不同点​编辑关键实验验证6.6 备份与迁移数据卷数据备份数据恢复总结架构图说明Host ┌─────────────────────────────────────────────────────────┐ │ Filesystem │ Memory │ │ ┌─────────────┐ │ ┌─────────────┐ │ │ │ bind mount │ │ │ tmpfs mount │ │ │ │ volume mount│ │ └─────────────┘ │ │ └─────────────┘ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Docker area │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────────────────────┐ │ Container │ └─────────────────────────────────────────────────────────┘生活类比Docker 容器就像一个临时租的酒店房间你在房间里放的东西数据退房删除容器时都会被酒店收走。数据卷就是你在酒店楼下租的一个永久储物柜不管你换哪个房间容器储物柜里的东西都不会丢还能同时给多个房间容器共用。核心作用Docker 数据卷是一个可供容器使用的特殊目录它绕过了容器的分层文件系统直接将数据存储在宿主机上实现三个核心目标数据持久化即使容器被删除或重新创建数据卷中的数据仍然存在不会丢失数据共享多个容器可以同时挂载同一个数据卷实现数据的共享和交互生命周期独立数据卷的生命周期独立于容器不受容器的启动、停止和删除的影响6.1 为什么要用数据卷Docker 分层文件系统的问题性能差分层文件系统需要多层叠加读写IO 性能比直接读写宿主机磁盘低 30%-50%生命周期与容器绑定容器删除时所有写入容器文件系统的数据都会永久丢失Docker 数据卷的优势直接 mount 到宿主机文件系统完全绕开分层文件系统读写性能与宿主机本地磁盘完全一致容器删除后数据卷中的数据依然保留局限性仅限本地磁盘存储不能随容器自动跨主机迁移Docker 提供的两种卷类型bind mount绑定挂载docker managed volumeDocker 管理卷6.2 bind mount 数据卷核心概念将宿主机上的任意目录或文件直接挂载到容器内部的指定路径。这是最直观、最灵活的数据卷类型。语法格式-v 宿主机路径:容器内路径[:权限]如果宿主机路径不存在Docker 会自动创建该目录注意只会创建目录不会创建文件默认权限为读写rw可显式指定为只读ro准备工作# 安装必要工具SS client, Xserver and networking tools # 启用Web控制台 systemctl enable --now cockpit.socket # 注册系统到Red Hat Insights rhc connect代码解释systemctl enable --now cockpit.socket底层触发两个动作① 将 cockpit.socket 服务设置为开机自启② 立即启动该 socket 服务监听 9090 端口提供 Web 管理界面rhc connect将当前 RHEL 系统注册到 Red Hat Insights 平台用于接收安全补丁、性能分析和故障诊断服务坑Gotchas如果宿主机路径是一个不存在的文件Docker 会错误地创建一个同名目录导致挂载失败挂载单个文件时如果宿主机上的文件被删除后重新创建容器内的挂载点会失效需要重新挂载容器基础示例[rootdocker-nodel ~]# ls /data/ database job_logs redis registry secret [rootdocker-nodel ~]# date 2026年03月22日星期日09:55:40 CST [rootdocker-nodel ~]# ls /data/ [rootdocker-nodel ~]# rm -fr /data/* [rootdocker-nodel ~]# docker run lss -it --rm --name test -v /data:/data busybox:latest sh: lss: not found / # ls bin data dev etc home lib lib64 proc root sys tmp usr var / # touch data/file{1..5} / # rm -fr /data/* bin file{1..5} # ls data/ data dev etc home lib lib64 proc root sys tmp usr var / # touch /data/file1 /data/file2逐行解析Line-by-Line Breakdownrm -fr /data/*强制删除宿主机 /data 目录下的所有内容为测试做准备docker run lss -it --rm --name test -v /data:/data busybox:latest底层错误lss是无效命令应该是--rm前面的参数写错了正确命令docker run -it --rm --name test -v /data:/data busybox:latest-it分配交互式终端并保持标准输入打开--rm容器退出后自动删除容器本身不影响数据卷--name test给容器命名为 test-v /data:/data将宿主机的 /data 目录挂载到容器的 /data 目录读写权限busybox:latest使用最新的 busybox 镜像启动容器touch data/file{1..5}在容器内的 /data 目录创建 file1 到 file5 五个文件这些文件会立即同步到宿主机的 /data 目录rm -fr /data/* bin file{1..5}这是一个错误命令会尝试删除 /bin 目录和当前目录下的 file1-file5正确应该是rm -fr /data/*坑Gotchas命令参数顺序错误docker run的选项必须放在镜像名之前镜像名之后的内容会被当作容器内执行的命令大括号扩展file{1..5}是 bash 的特性在某些精简镜像如 alpine中可能不支持权限控制示例bash运行[rootdocker ~]# docker run -it --rm \ -v /tmp/data1:/data1 \ -v /tmp/data1:/data2:ro \ -v /etc/passwd:/data/passwd:ro busybox / # tail -n 3 /data/passwd lee:x:1000:1000:lee:/home/lee:/bin/bash apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin nginx:x:1001:1001::/home/nginx:/sbin/nologin / # touch /data1/leefile1 / # touch /data2/leefile1 touch: /data2/leefile1: Read-only file system逐行解析-v /tmp/data1:/data1将宿主机 /tmp/data1 挂载到容器 /data1读写权限-v /tmp/data1:/data2:ro将同一个宿主机目录挂载到容器 /data2只读权限-v /etc/passwd:/data/passwd:ro将宿主机的 /etc/passwd 文件挂载到容器内的 /data/passwd只读权限touch /data1/leefile1成功创建文件因为 /data1 是读写权限touch /data2/leefile1失败因为 /data2 是只读权限底层会触发 EACCES权限被拒绝错误企业级生产应用配置文件挂载将 Nginx、MySQL 等服务的配置文件从宿主机挂载到容器无需重新构建镜像即可修改配置日志收集将容器内的日志目录挂载到宿主机方便使用 ELK、Promtail 等工具统一收集和分析静态文件服务将前端静态资源挂载到 Nginx 容器实现热更新进阶优化使用--mount语法替代-v语法更清晰支持更多高级选项对于频繁读写的目录使用tmpfs挂载到内存提升 IO 性能使用 SELinux 标签:z或:Z解决 CentOS/RHEL 系统下的权限问题课后防宕机指南错误 1touch: cannot touch /data/file: Permission denied排查思路① 检查宿主机目录的权限UID/GID 是否与容器内用户匹配② 检查是否挂载了只读卷③ 检查 SELinux 上下文错误 2docker: Error response from daemon: create ./data: ./data includes invalid characters for a local volume name, only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed.排查思路bind mount 必须使用绝对路径不能使用相对路径相对路径会被 Docker 当作卷名处理6.3 docker managed 数据卷核心概念bind mount必须指定host文件系统路径限制了移植性docker managed volume不需要指定mount源也不能指定docker自动为容器创建数据卷目录默认创建的数据卷目录都在/var/lib/docker/volumes中如果挂载时指向容器内已有的目录原有数据会被复制到volume中原来的目录还可以看见Docker 管理卷是由 Docker daemon 自动创建和管理的数据卷不需要用户指定宿主机路径。所有 Docker 管理卷默认存储在/var/lib/docker/volumes/目录下。关键特性如果挂载时指向容器内已有的目录容器内该目录的原有数据会被自动复制到数据卷中容器内原来的目录仍然可以访问但写入操作会被重定向到数据卷可移植性强不需要关心宿主机的具体目录结构镜像内置卷示例[rootdocker-nodel ~]# docker inspect mysql:8.0 { ExposedPorts: { 3306/tcp: {}, 33060/tcp: {} }, Volumes: { /var/lib/mysql: {} }, WorkingDir: / } [rootdocker volumes]# docker run -d --name mysql -e MYSQL_ROOT_PASSWORDlee mysql:5.7 [rootdocker volumes]# ls -l /var/lib/docker/volumes 总用量 0 drwx-----x 3 root root 19 8月 20 16:34 ad74662b8d6bb6fdcc6e82925ae9942b94bac5f9da4bd52b0a14ac451ae9ef75 [rootdocker volumes]# touch ad74662b8d6bb6fdcc6e82925ae9942b94bac5f9da4bd52b0a14ac451ae9ef75/_data/leefile [rootdocker volumes]# docker exec -it mysql bash bash-4.2# cd /var/lib/mysql bash-4.2# ls auto.cnf client-cert.pem ib_logfile0 ibtmp1 mysql.sock public_key.pem sys ca-key.pem client-key.pem ib_logfile1 leefile performance_schema server-cert.pem ca.pem ib_buffer_pool ibdata1 mysql private_key.pem server-key.pem逐行解析docker inspect mysql:8.0查看 mysql 镜像的详细信息发现镜像内置了/var/lib/mysql卷docker run -d --name mysql -e MYSQL_ROOT_PASSWORDlee mysql:5.7-d后台运行容器-e MYSQL_ROOT_PASSWORDlee设置 MySQL root 用户的密码为 lee由于镜像内置了/var/lib/mysql卷Docker 会自动创建一个匿名卷并挂载到该路径ls -l /var/lib/docker/volumes查看 Docker 自动创建的匿名卷卷名是一个随机的 64 位哈希值touch .../_data/leefile在宿主机的卷目录下创建 leefile 文件docker exec -it mysql bash进入运行中的 mysql 容器ls /var/lib/mysql可以看到宿主机上创建的 leefile 文件已经同步到容器内坑Gotchas匿名卷在容器删除后不会自动删除会产生大量 孤儿卷占用磁盘空间如果使用docker run -v /var/lib/mysql mysqlDocker 会创建一个新的匿名卷而不是复用之前的卷命名卷操作bash运行# 建立数据卷 [rootdocker ~]# docker volume create leevol1 leevol1 # 查看卷 [rootdocker ~]# docker volume ls DRIVER VOLUME NAME local leevol1 # 查看卷的详细信息 [rootdocker ~]# docker volume inspect leevol1 [ { CreatedAt: 2026-03-22T10:00:0008:00, Driver: local, Labels: {}, Mountpoint: /var/lib/docker/volumes/leevol1/_data, Name: leevol1, Options: {}, Scope: local } ] # 使用建立的数据卷 [rootdocker _data]# docker run -d --name web1 -p 80:80 -v leevol1:/usr/share/nginx/html nginx e76706848323d6c329c41c4140903f8cc441458daf1459d9016bd1ed0ab3360a [rootdocker _data]# cd /var/lib/docker/volumes/leevol1/_data [rootdocker _data]# ls 50x.html index.html [rootdocker _data]# echo leevol1 index.html [rootdocker _data]# curl 172.25.254.100 leevol1逐行解析docker volume create leevol1创建一个名为 leevol1 的命名卷Docker 会在/var/lib/docker/volumes/下创建 leevol1 目录docker volume ls列出所有 Docker 管理卷docker run -d --name web1 -p 80:80 -v leevol1:/usr/share/nginx/html nginx-p 80:80将宿主机的 80 端口映射到容器的 80 端口-v leevol1:/usr/share/nginx/html将命名卷 leevol1 挂载到容器的 /usr/share/nginx/html 目录由于容器内该目录已有 Nginx 的默认页面Docker 会将这些文件自动复制到 leevol1 卷中echo leevol1 index.html在宿主机上修改卷中的 index.html 文件curl 172.25.254.100访问 Nginx 服务可以看到修改后的内容清理未使用的 Docker 数据卷bash运行[rootdocker ~]# 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: ad74662b8d6bb6fdcc6e82925ae9942b94bac5f9da4bd52b0a14ac451ae9ef75 Total reclaimed space: 256MB⚠️ 重要提示该操作是不可逆的一旦删除数据将无法恢复执行前请确保所有重要数据已经备份该命令只会删除没有被任何容器使用的卷只读命名卷示例bash运行[rootdocker-node1 ~]# docker volume create timinglee timinglee [rootdocker-node1 ~]# docker volume ls DRIVER VOLUME NAME local timinglee [rootdocker-node1 volumes]# touch timinglee/_data/file [rootdocker-node1 ~]# docker run -it --rm -v timinglee:/data:ro busybox:latest / # ls bin data dev etc home lib lib64 proc root sys tmp usr var / # touch data/file touch: data/file: Read-only file system / # ls data/ file [rootdocker-node1 ~]# docker volume rm timinglee timinglee [rootdocker-node1 ~]# docker volume ls DRIVER VOLUME NAME企业级生产应用数据库存储MySQL、PostgreSQL 等有状态服务的持久化存储是生产环境最常用的方式共享配置多个容器共享同一份配置文件如微服务架构中的公共配置CI/CD 流水线在构建阶段将编译产物存储到数据卷供后续测试和部署阶段使用进阶优化使用命名卷而非匿名卷便于管理和迁移定期执行docker volume prune清理未使用的卷释放磁盘空间对于需要跨主机共享的卷使用 NFS、GlusterFS 等分布式存储驱动使用docker volume create --opt typetmpfs创建内存卷提升临时数据的读写性能课后防宕机指南错误 1docker: Error response from daemon: volume leevol1 already exists.排查思路卷名冲突使用docker volume rm leevol1删除已存在的卷或者使用不同的卷名错误 2容器启动后数据丢失排查思路① 检查是否正确挂载了数据卷② 检查容器内的写入路径是否与挂载点一致③ 检查是否使用了--rm选项但忘记挂载数据卷6.4 数据卷容器Data Volume Container核心概念数据卷容器是一种专门用于管理数据卷的特殊容器它不运行任何业务进程只负责提供数据卷供其他容器挂载。通过--volumes-from选项其他容器可以直接继承数据卷容器的所有挂载点。核心优势统一管理多个数据卷的挂载点和权限简化多个容器之间的数据共享配置数据卷容器的生命周期与数据卷解耦建立数据卷容器bash运行[rootdocker ~]# docker run -d --name datavol \ -v /tmp/data1:/data1:rw \ -v /tmp/data2:/data2:ro \ -v /etc/resolv.conf:/etc/hosts busybox逐行解析docker run -d --name datavol后台运行一个名为 datavol 的容器-v /tmp/data1:/data1:rw挂载宿主机 /tmp/data1 到容器 /data1读写权限-v /tmp/data2:/data2:ro挂载宿主机 /tmp/data2 到容器 /data2只读权限-v /etc/resolv.conf:/etc/hosts挂载宿主机 /etc/resolv.conf 到容器 /etc/hosts默认读写权限busybox使用 busybox 镜像容器启动后会执行默认的 sleep 命令如果没有指定命令busybox 会立即退出需要加上sleep infinity保持运行坑Gotchas如果数据卷容器退出其他已经通过--volumes-from挂载的容器不会受到影响但新容器无法再挂载数据卷容器本身不需要一直运行只要它没有被删除就可以被其他容器引用使用数据卷容器bash运行[rootdocker ~]# docker run -it --name test --rm --volumes-from datavol busybox / # ls bin data1 data2 dev etc home lib lib64 proc root sys tmp usr var / # cat /etc/resolv.conf # Generated by Docker Engine. # This file can be edited; Docker Engine will not make further changes once it # has been modified. nameserver 114.114.114.114 search timinglee.org # Based on host file: /etc/resolv.conf (legacy) # Overrides: [] / # touch data1/leefile1 / # touch /data2/leefile1 touch: /data2/leefile1: Read-only file system逐行解析--volumes-from datavol继承 datavol 容器的所有挂载点包括权限设置ls可以看到 data1、data2 目录和 /etc/hosts 文件已经自动挂载cat /etc/resolv.conf显示的是宿主机 /etc/resolv.conf 的内容因为我们将它挂载到了容器的 /etc/hoststouch data1/leefile1成功因为 data1 是读写权限touch /data2/leefile1失败因为 data2 是只读权限权限设置会被完整继承企业级生产应用多容器共享数据如 Web 服务器集群共享静态资源应用服务器共享配置文件数据备份与迁移通过数据卷容器统一管理所有数据卷简化备份和迁移流程开发环境一致性在开发团队中共享数据卷容器确保所有开发人员使用相同的测试数据进阶优化使用--volumes-from可以同时继承多个数据卷容器的挂载点数据卷容器可以使用--entrypoint /bin/true来避免运行不必要的进程使用 Docker Compose 定义数据卷容器实现基础设施即代码课后防宕机指南错误 1docker: Error response from daemon: No such container: datavol.排查思路数据卷容器不存在或已被删除需要重新创建数据卷容器错误 2继承的挂载点权限不正确排查思路检查数据卷容器的挂载权限设置--volumes-from会完整继承所有权限包括只读权限6.5 bind mount 数据卷和 docker managed 数据卷的对比相同点两者都是宿主机文件系统中的真实路径都支持读写和只读权限都可以被多个容器同时挂载不同点对比项bind mountdocker managed volume卷位置可任意指定宿主机路径Docker 自动管理默认在/var/lib/docker/volumes/对已有 mount point 的影响容器内原有数据会被隐藏宿主机目录内容会覆盖容器内目录容器内原有数据会被复制到数据卷中移植性差依赖宿主机的目录结构好不需要关心宿主机的具体路径控制性强可以精确控制挂载的文件和目录弱只能控制挂载点无法控制宿主机路径支持单个文件挂载是否只能挂载目录managed:和mysql一样但数据不会移植过去Bind:控制性强managed:可移植性强关键实验验证# bind mount会隐藏容器内原有数据 [rootdocker-nodel ~]# echo timinglee /data/index.html [rootdocker-nodel ~]# docker run -d --name webserver -p 80:80 -v /data/:/usr/share/nginx/html nginx [rootdocker-nodel ~]# curl 172.25.254.100 timinglee # docker managed volume会复制容器内原有数据 [rootdocker-nodel ~]# docker volume create webhtml webhtml [rootdocker-nodel ~]# docker run -d --name webserver -p 80:80 -v webhtml:/usr/share/nginx/html nginx [rootdocker-nodel ~]# ls /var/lib/docker/volumes/webhtml/_data/ 50x.html index.html实验结论bind mount宿主机目录内容会完全覆盖容器内目录容器内原有数据被隐藏docker managed volume容器内原有数据会被复制到数据卷中不会丢失6.6 备份与迁移数据卷数据备份# 启动Web服务器并挂载数据卷 [rootdocker-node1 ~]# docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html nginx:1.23 # 进入容器创建测试文件 [rootdocker-node1 ~]# docker exec -it webserver bash root23951ce13871:/# cd /usr/share/nginx/html/ root23951ce13871:/usr/share/nginx/html# ls index.html timinglee root23951ce13871:/usr/share/nginx/html# touch timinglee{1..10} root23951ce13871:/usr/share/nginx/html# ls index.html timinglee1 timinglee2 timinglee4 timinglee6 timinglee8 timinglee timinglee10 timinglee3 timinglee5 timinglee7 timinglee9 root23951ce13871:/usr/share/nginx/html# exit # 备份数据卷 [rootdocker-node1 ~]# docker run -it --rm --volumes-from webserver -v $(pwd):/backup busybox:latest / # ls backup bin dev etc home lib lib64 proc root sys tmp usr var / # ls /usr/share/nginx/html/ index.html timinglee1 timinglee2 timinglee4 timinglee6 timinglee8 timinglee timinglee10 timinglee3 timinglee5 timinglee7 timinglee9 / # tar zcf /backup/html.tar.gz /usr/share/nginx/ tar: removing leading / from member names / # exit # 查看备份文件 [rootdocker-node1 ~]# ls busybox-latest.tar.gz docker mysql-8.0.tar mysql-8.0.tar busyboxplus.tar nginx-1.23.tar.gz debian11.tar.gz html.tar.gz phpmyadmin-latest.tar.gz busyboxplus.tar harbor-offline-installer-v2.14.0.tgz nginx-1.23.tar.gz逐行解析docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html nginx:1.23启动 Nginx 容器将宿主机 /data 目录挂载到容器的 /usr/share/nginx/htmldocker exec -it webserver bash进入运行中的容器touch timinglee{1..10}创建 10 个测试文件docker run -it --rm --volumes-from webserver -v $(pwd):/backup busybox:latest--volumes-from webserver继承 webserver 容器的所有挂载点-v $(pwd):/backup将宿主机当前目录挂载到容器的 /backup 目录这样容器内就可以同时访问到 webserver 的数据卷和宿主机的备份目录tar zcf /backup/html.tar.gz /usr/share/nginx/将 /usr/share/nginx/ 目录打包压缩到 /backup/html.tar.gz也就是宿主机当前目录下的 html.tar.gztar: removing leading / from member namestar 命令的正常提示表示会去掉路径开头的 /解压时会相对当前目录解压数据恢复# 清空原有数据 [rootdocker-node1 ~]# rm -fr /data/* [rootdocker-node1 ~]# docker exec -it webserver bash root23951ce13871:/# cd /usr/share/nginx/html/ root23951ce13871:/usr/share/nginx/html# ls root23951ce13871:/usr/share/nginx/html# exit # 重新启动容器并挂载备份目录 [rootdocker-node1 ~]# docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html -v $(pwd):/backup nginx:1.23 83a26edb472ecda951e241dc207847111cd4a2712cb349c52205c3d0e2727238 # 恢复数据 [rootdocker-node1 ~]# docker exec -it webserver bash root83a26edb472e:/# tar zxf /backup/html.tar.gz -C / root83a26edb472e:/# ls /usr/share/nginx/html/ index.html timinglee1 timinglee2 timinglee4 timinglee6 timinglee8 timinglee timinglee10 timinglee3 timinglee5 timinglee7 timinglee9逐行解析rm -fr /data/*清空宿主机 /data 目录下的所有数据模拟数据丢失docker run -d --name webserver -p 80:80 -v /data:/usr/share/nginx/html -v $(pwd):/backup nginx:1.23重新启动容器同时挂载数据卷和备份目录tar zxf /backup/html.tar.gz -C /将备份文件解压到根目录由于备份时去掉了开头的 /解压后会自动创建 usr/share/nginx/html 目录结构ls /usr/share/nginx/html/可以看到所有数据已经成功恢复通用备份命令模板# 备份任意数据卷容器 docker run --rm --volumes-from 数据卷容器名 -v $(pwd):/backup busybox tar zcf /backup/备份文件名.tar.gz 容器内要备份的路径 # 恢复数据到任意数据卷 docker run --rm -v 目标卷名:容器内路径 -v $(pwd):/backup busybox tar zxf /backup/备份文件名.tar.gz -C /企业级生产应用灾难恢复定期备份数据库数据卷在发生数据丢失时快速恢复环境迁移将数据卷从开发环境迁移到测试环境或生产环境版本控制对重要数据卷进行版本化备份支持回滚到任意历史版本进阶优化使用--exclude选项排除不需要备份的文件如日志文件、临时文件使用增量备份工具如 rsync替代 tar减少备份时间和存储空间将备份文件自动上传到对象存储如 S3、OSS实现异地备份使用定时任务如 cron自动执行备份脚本课后防宕机指南错误 1tar: /backup/html.tar.gz: Cannot open: Permission denied排查思路① 检查宿主机当前目录的权限② 检查容器内用户是否有写入 /backup 目录的权限③ 尝试使用 root 用户执行备份错误 2恢复后数据不完整排查思路① 检查备份文件是否完整使用tar tvf查看② 检查解压路径是否正确③ 检查是否有文件被排除在备份之外总结Docker 数据卷是容器化应用中实现数据持久化和共享的核心技术。bind mount 适合需要精确控制宿主机路径的场景如配置文件挂载和开发环境docker managed volume 适合生产环境中的有状态服务如数据库存储。数据卷容器提供了一种统一管理多个数据卷的方式简化了多容器数据共享的配置。在生产环境中我们应该优先使用命名卷而非匿名卷定期备份重要数据卷及时清理未使用的卷释放磁盘空间根据业务需求选择合适的数据卷类型