1. 为什么容器里存不了数据——从“删掉容器就丢数据”说起你刚用docker run -d nginx起了一个 Web 服务往/usr/share/nginx/html/里放了自己写的index.html刷新浏览器看到页面正常可一执行docker rm -f 容器ID再docker run -d nginx新起一个页面又变回默认的欢迎页。不是代码没改对是根本没存住——这事儿我第一次遇到时在公司测试环境反复折腾了三小时最后抓着运维同事问“Docker 是不是故意不让我存文件”他笑着递给我一杯咖啡说“不是它不让你存是你没告诉它‘这儿得留着’。”这就是Persistent storage持久化存储在 Docker 里的核心矛盾容器天生是临时的它的文件系统UnionFS 层随启随生、随停即焚而业务数据——比如数据库的.ibd文件、用户上传的图片、日志归档、配置快照——必须跨容器生命周期存在。标题里这个 “A guide to Persistent storage in Docker”说白了就是教你怎么在“一次性的沙盒”里安全、可靠、高效地建出一块“永远不消失的硬盘”。关键词里没有具体技术名词但“Persistent storage”本身已是精准锚点——它直指 Docker 生态中最常被新手忽略、却被生产环境反复拷打的核心能力。它不只关乎“能不能存”更决定“存得稳不稳”数据一致性、“读得快不快”I/O 性能、“管得顺不顺”备份/迁移/权限控制。适合三类人直接抄作业刚学完docker run想搭个人博客却卡在“文章一重启就消失”的开发者正把老旧单体应用容器化、发现 MySQL 数据库目录一删全丢的运维工程师还有正在设计 CI/CD 流水线、需要缓存构建中间产物避免重复下载的 DevOps 实践者。这篇文章不讲抽象概念只拆解真实场景下每一种持久化方案的选型逻辑、实操命令、参数陷阱和线上踩坑记录——就像当年那位递咖啡的同事手把手带你把数据真正“钉”在磁盘上。2. Docker 存储机制的本质为什么默认不持久2.1 容器层叠文件系统Layered Filesystem的真相很多人以为docker commit能把修改固化成镜像就等于“数据持久化”了。错。这是对 Docker 存储模型最典型的误解。我们来拆开看一个容器启动时的真实文件系统结构# 启动一个基础容器并查看其挂载点 $ docker run -it --rm alpine sh -c mount | grep overlay overlay on / type overlay (rw,relatime,lowerdir/var/lib/docker/overlay2/l/ABC...:/var/lib/docker/overlay2/l/DEF...,upperdir/var/lib/docker/overlay2/XYZ.../diff,workdir/var/lib/docker/overlay2/XYZ.../work)关键三个目录lowerdir只读层对应镜像的所有历史层base OS、安装的软件包、COPY 进去的文件等多个容器可共享upperdir可写层容器运行时所有新增、修改、删除操作都发生在这里workdir工作目录overlayfs 内部管理用不用管。提示upperdir就是那个“一删容器就清空”的地方。它本质是/var/lib/docker/overlay2/下某个随机命名的子目录路径由 Docker daemon 自动分配用户不可控、不可预测、也不该直接访问。所以当你echo hello /data/test.txt这个文件实际写入的是upperdir下的data/test.txt一旦容器停止并被rmDocker 会自动清理整个upperdir目录及其父级目录包括workdir物理磁盘上的那块空间就被标记为“可回收”。这不是 Bug是设计使然——容器的轻量、快速启停、不可变基础设施Immutable Infrastructure理念全部依赖于这种“干净出生、彻底死亡”的特性。2.2 三种持久化路径的底层定位与适用边界Docker 提供了三种官方支持的持久化机制它们不是并列选项而是针对不同场景的分层解决方案方式底层实现生命周期归属典型用途是否跨主机Bind Mounts主机目录直接挂载完全由主机文件系统管理开发调试、配置文件热更新、日志收集否路径强依赖主机VolumeDocker 管理的独立存储卷由docker volume命令创建/管理数据库存储、应用数据、CI 缓存否但可通过插件扩展tmpfs Mount主机内存挂载容器生命周期内存在敏感临时密钥、会话缓存绝对不落盘否注意--volumes-from是旧版容器间卷复用方式已不推荐Storage Drivers如overlay2,btrfs是镜像层管理机制与用户数据持久化无关本文不展开。这三者的根本区别在于谁拥有数据所有权、谁负责生命周期管理、谁承担 I/O 路径风险。比如 Bind Mounts 把/home/user/app/data挂给容器那/home/user/app/data的磁盘满了容器就直接报No space left on device而 Volume 由 Docker daemon 统一管理它知道/var/lib/docker/volumes/下哪个卷占了多少空间还能配合docker system df做容量分析。选错方式轻则开发环境跑不通重则生产数据库因挂载点权限错误导致无法启动。2.3 为什么不能只用 Bind Mounts——一个血泪教训去年我帮一家做 SaaS 的客户做容器化改造他们坚持用 Bind Mounts 存 MySQL 数据理由是“路径看得见备份脚本好写”。结果上线第三天凌晨两点监控报警MySQL 主从同步中断。登录一看主库容器日志疯狂刷[ERROR] InnoDB: The Auto-extending data file ./ibdata1 is of a different size 768 pages than specified in the .cnf file 0 pages!排查发现他们用docker run -v /data/mysql:/var/lib/mysql ...启动但/data/mysql目录在宿主机上被另一个定时清理脚本误删了部分子目录只留了空壳MySQL 容器启动时检测到/var/lib/mysql非空但结构异常拒绝初始化直接 crashloop。而因为没配健康检查探针K8s 一直认为容器“活着”流量持续打入最终导致从库数据严重滞后。根本原因Bind Mounts 把宿主机文件系统的脆弱性权限、路径存在性、磁盘满、误操作完全暴露给了容器。Volume 则不同——docker volume create mysql-data创建后Docker 会在/var/lib/docker/volumes/下生成完整隔离的目录结构并确保其权限、属主符合容器内进程需求如 MySQL 默认以mysql:mysql用户运行Volume 会自动 chown。这才是生产环境该有的容错水位。3. VolumeDocker 原生推荐的持久化主力方案3.1 创建、管理与生命周期控制Volume 是 Docker 官方文档明确标注为“Production-Ready”的持久化方式。它的核心价值在于解耦容器与存储的生命周期交由 Docker daemon 统一治理。创建一个 Volume 只需一条命令$ docker volume create myapp-data myapp-data这条命令做了什么在/var/lib/docker/volumes/下创建名为myapp-data的子目录初始化一个metadata.db文件记录该 Volume 的创建时间、驱动类型默认local、标签label等元数据设置目录权限为0755属主为root:root后续挂载时由容器进程自动适配。实操心得永远用docker volume create显式创建不要依赖docker run -v myvol:/path的隐式创建。后者在 Swarm 或 Compose 中可能因节点调度失败导致 Volume 创建在错误机器上且无法通过docker volume ls统一管理。查看所有 Volume$ docker volume ls DRIVER VOLUME NAME local myapp-data local mysql-data local redis-cache删除 Volume 必须显式执行且有保护机制# 删除前会检查是否被容器使用 $ docker volume rm myapp-data Error response from daemon: remove myapp-data: volume is in use - [abc123] # 强制删除慎用 $ docker volume rm -f myapp-data提示Volume 的生命周期独立于容器。即使所有使用它的容器都已停止Volume 依然存在数据完好无损。这是它与 Bind Mounts 最本质的区别。3.2 在容器中挂载 Volume 的完整语法与参数解析挂载 Volume 到容器标准语法是$ docker run -d \ --name myapp \ -v myapp-data:/app/data:rw \ -v /host/config:/app/config:ro \ nginx:alpine这里-v参数的三段式结构VOLUME_NAME:CONTAINER_PATH:MODE必须吃透第一段myapp-dataVolume 名称必须已存在或由 Docker 隐式创建第二段/app/data容器内挂载路径必须是绝对路径且容器内该路径需存在或可被自动创建第三段rw或ro挂载模式read-write / read-only默认是rw。注意ro模式下容器内进程无法修改挂载点下的任何文件但可以读取。这对配置文件、证书等只读资源非常关键——避免应用意外覆盖关键配置。更精细的控制需用--mount语法推荐用于生产$ docker run -d \ --name myapp \ --mount sourcemyapp-data,target/app/data,typevolume,consistencycached \ --mount sourcenginx-conf,target/etc/nginx/conf.d,typebind,read_onlytrue \ nginx:alpine--mount的优势在于语义清晰source/target/type关键字一目了然不易混淆参数丰富支持consistencymacOS 上的文件一致性策略、driver_opts指定驱动参数等高级选项未来兼容Docker 官方明确表示--mount是未来方向-v会逐渐弱化。3.3 Volume 驱动Driver的实战选型不止localdocker volume create默认使用local驱动即数据存储在本地磁盘。但在集群或云环境中这远远不够。Docker 支持插件化存储驱动通过--driver参数指定驱动名称适用场景关键能力典型厂商local单机开发/测试本地文件系统零配置Docker 内置nvidia-dockerGPU 计算容器挂载 GPU 设备与驱动库NVIDIArexray云平台持久化对接 AWS EBS、Azure Disk、vSphereDell EMCportworx企业级容器存储跨节点复制、快照、加密、QoSPortworxnetshareNFS/SMB 共享存储挂载网络文件系统多容器共享Open Source举个真实案例某金融客户要求数据库容器必须支持跨 AZ可用区高可用。我们放弃local选用portworx驱动# 创建带复制因子的 Volume $ docker volume create \ --driver pwx \ --opt size100 \ --opt repl3 \ --opt sharedtrue \ mysql-prod-volumerepl3表示数据在集群中保存 3 份副本sharedtrue允许多个容器如主库从库备份任务同时读写同一 Volume。当某个节点宕机Portworx 自动将 IO 重定向到其他副本节点应用无感知。这比自己写脚本 rsync 同步数据可靠性高出两个数量级。实操心得驱动选型不是技术炫技而是对 SLA服务等级协议的承诺。如果你的业务要求 RPO0零数据丢失就必须用支持同步复制的驱动如果只是日志归档local 定时rsync到 NAS 就足够。4. Bind Mounts开发调试的利器与生产环境的雷区4.1 为什么开发阶段离不开 Bind Mounts想象你在写一个 Python Flask 应用代码在~/projects/myflask/app.py想边改边看效果。如果每次改完都docker build打镜像再run10 分钟改 3 行代码效率归零。Bind Mounts 就是为此而生$ docker run -d \ --name flask-dev \ -p 5000:5000 \ -v ~/projects/myflask:/app:rw \ -w /app \ -e FLASK_ENVdevelopment \ python:3.9-slim \ flask run --host0.0.0.0:5000这里-v ~/projects/myflask:/app把本地代码目录实时映射进容器/app。你用 VS Code 在宿主机改app.py容器内flask run进程立刻检测到文件变化自动 reload——这就是开发体验的“灵魂”。注意-w /app指定工作目录否则容器启动后默认在/Python 解释器找不到app.py。这是新手常踩的坑。Bind Mounts 的另一大优势是配置热更新。比如 Nginx 配置$ docker run -d \ --name nginx-proxy \ -p 80:80 \ -v ~/configs/nginx.conf:/etc/nginx/nginx.conf:ro \ -v ~/www:/usr/share/nginx/html:ro \ nginx:alpine修改~/configs/nginx.conf后只需docker kill -s HUP nginx-container-id发送SIGHUP信号Nginx 就会重新加载配置无需重启容器。这对灰度发布、A/B 测试至关重要。4.2 生产环境禁用 Bind Mounts 的五大硬性理由尽管开发友好但将 Bind Mounts 用于生产数据存储是 Docker 社区公认的反模式。以下是五个无法绕过的硬伤路径强依赖宿主机docker run -v /data/mysql:/var/lib/mysql要求所有节点的/data/mysql目录必须存在、权限正确、磁盘充足。在 Kubernetes 集群中Pod 可能被调度到任意节点你无法保证每个节点都有/data/mysql。而 Volume 由 Docker daemon 统一管理路径对用户透明。权限地狱Permission Hell容器内进程如mysql用户 UID999尝试写入宿主机目录时若该目录属主是root:root就会报Permission denied。解决方法要么chown 999:999 /data/mysql破坏宿主机权限体系要么在容器内用--user root启动安全风险。Volume 会自动处理 UID/GID 映射。SELinux/AppArmor 冲突在启用 SELinux 的 CentOS/RHEL 系统上Bind Mounts 默认被标记为unconfined_u:object_r:default_t:s0而容器进程运行在system_u:system_r:svirt_lxc_net_t:s0:c12,c34上下文导致Operation not permitted。Volume 会自动添加z共享或Z私有标签解决。备份与迁移成本高备份 Bind Mounts 数据需先docker stop容器再tar -czf backup.tgz /data/mysql最后docker start。期间服务中断。Volume 可直接docker run --rm -v myvol:/volume -v $(pwd):/backup alpine tar -czf /backup/myvol.tgz -C /volume .在线备份无停机。无法跨平台移植Windows/macOS 的 Docker Desktop 使用 Linux VM-v C:\data:/data实际映射到 VM 内部路径性能差且路径语义混乱。Volume 在所有平台行为一致。提示唯一可接受的生产级 Bind Mounts 场景是只读配置挂载如-v /etc/ssl/certs:/etc/ssl/certs:ro因为它不涉及写操作规避了权限、SELinux、路径依赖等所有风险。4.3 安全加固Bind Mounts 的最小权限实践如果因历史原因必须用 Bind Mounts如遗留系统改造请严格遵循以下加固步骤创建专用用户与组# 在宿主机创建专用组和用户 $ sudo groupadd -g 1001 appdata $ sudo useradd -u 1001 -g 1001 -d /data/app appuser $ sudo chown -R appuser:appdata /data/app容器内以该 UID 启动$ docker run -d \ --name legacy-app \ --user 1001:1001 \ -v /data/app:/app:rw \ legacy-image:1.0挂载时显式声明:z或:Z仅限 SELinux 环境# :z 表示多个容器共享此目录如主从数据库 -v /data/mysql:/var/lib/mysql:z # :Z 表示此目录仅供当前容器独占如日志 -v /data/logs:/var/log/app:Z禁止挂载根目录或敏感路径绝对禁止-v /:/host或-v /etc:/etc这等于把宿主机 root 权限交给容器。5. tmpfs Mount内存中的“一次性保险柜”5.1 什么场景下你需要一个“不落地”的存储tmpfs Mount 的核心价值是提供一种绝对不写入磁盘、纯内存驻留、容器销毁即清空的存储空间。它不是为了“存数据”而是为了“保安全”。典型场景有三类敏感凭据临时存放数据库密码、API Token、TLS 私钥。这些信息绝不能以明文形式落在磁盘上哪怕是在 Volume 里否则磁盘快照、备份、物理机维修都可能泄露。高频临时缓存Session 数据、计算中间结果。内存读写速度是 SSD 的 10~100 倍且无需考虑磁盘 I/O 竞争。防篡改只读环境某些合规审计要求应用运行时的临时文件必须在内存中杜绝任何落盘可能。例如启动一个 PostgreSQL 容器把密码文件放在 tmpfs$ docker run -d \ --name pg-secure \ --tmpfs /run/secrets:rw,size1M,mode0400 \ -e POSTGRES_PASSWORD_FILE/run/secrets/pg_pass \ -v /host/pg-data:/var/lib/postgresql/data:rw \ postgres:14这里--tmpfs /run/secrets:rw,size1M,mode0400创建了一个 1MB 大小、权限为0400仅所有者可读的内存文件系统挂载到容器内/run/secrets。然后通过-e POSTGRES_PASSWORD_FILE...告诉 PostgreSQL 从这个内存路径读取密码。容器停止后这块内存被释放密码彻底消失连shred命令都不用。5.2 tmpfs 参数详解与性能调优--tmpfs语法为--tmpfs container-path:options常用选项选项说明示例size内存大小上限单位b/k/m/gsize512mmode挂载点权限八进制mode0755uid,gid挂载点属主/属组 IDuid1001,gid1001注意size是硬限制。如果容器向 tmpfs 写入超过该值的数据会触发No space left on device错误应用需自行处理。因此必须预估峰值内存占用。性能调优关键点不要盲目设大tmpfs 占用的是主机物理内存RAM不是 swap。设size10g但实际只用 100MB等于浪费 9.9GB 内存可能导致主机 OOMOut of Memory被 kill。监控内存压力用docker stats container观察MEM USAGE / LIMIT或在宿主机用free -h查看ShmemShared Memory列。结合--memory限制容器总内存防止 tmpfs 应用堆内存超限。$ docker run -d \ --memory2g \ --tmpfs /tmp:rw,size512m \ myapp:latest5.3 tmpfs 与 Volume/Bind Mounts 的组合战术单一存储方式难以覆盖所有需求生产环境往往需要组合使用。一个典型的安全架构是$ docker run -d \ --name secure-app \ # 1. tmpfs 存敏感凭据内存不落盘 --tmpfs /run/secrets:rw,size1m,mode0400 \ # 2. Volume 存业务数据持久可备份 -v app-data:/app/data:rw \ # 3. Bind Mounts 存只读配置开发友好生产可控 -v /etc/app/config.yaml:/app/config.yaml:ro \ # 4. tmpfs 存临时缓存高速易清理 --tmpfs /app/cache:rw,size256m,mode0755 \ secure-app:2.1这个组合实现了安全分层凭据tmpfs与数据Volume物理隔离性能分级缓存走内存tmpfs数据走 SSDVolume运维友好配置Bind Mounts可由 Ansible 统一推送无需重建镜像。实操心得我在某支付网关项目中用此组合将 PCI DSS 合规审计通过时间从 3 周缩短到 2 天。审计员只关心“凭据是否落盘”我们直接展示df -h输出中/run/secrets的tmpfs类型以及ls -l /run/secrets的0400权限一击通过。6. 实战为 MySQL 容器构建企业级持久化方案6.1 需求分析不只是“让数据库不丢数据”客户提出的需求是“把 MySQL 5.7 迁到 Docker要求数据不丢、主从同步稳定、备份自动化、扩容方便。” 这看似简单实则包含四层技术诉求层级技术目标对应存储方案风险点L1数据不丢容器重启/崩溃后数据完整Volume 正确挂载点挂载点权限错误导致 MySQL 启动失败L2主从稳定Binlog、Relay Log 不因 IO 延迟中断Volume innodb_flush_methodO_DIRECT默认fsync在 Volume 上性能差L3备份可靠每日全量 每小时增量RPO≤5分钟Volume mysqldumpxtrabackup备份时未加--single-transaction导致锁表L4扩容灵活业务增长时可无缝升级磁盘Volume 云存储驱动如rexraylocalVolume 无法在线扩容忽略任一层都会在生产中引发故障。下面逐层实现。6.2 Volume 创建与挂载从零开始搭建数据基石第一步创建专用 Volume# 创建带标签的 Volume便于后续识别和清理 $ docker volume create \ --label com.mycompany.appmysql \ --label com.mycompany.envprod \ mysql-prod-data第二步编写docker run命令重点参数解析$ docker run -d \ --name mysql-prod \ --restartunless-stopped \ # 核心挂载 Volume 到 MySQL 数据目录 -v mysql-prod-data:/var/lib/mysql:rw \ # 挂载配置文件只读避免容器内修改 -v /etc/my.cnf:/etc/my.cnf:ro \ # 挂载 tmpfs 存临时表和排序缓冲提升性能 --tmpfs /tmp:rw,size512m,mode1777 \ # 内存限制防 OOM --memory4g \ # CPU 限制防 IO 饥饿 --cpus2 \ # 网络隔离 --networkbackend-net \ # 关键设置 MySQL 用户 UID匹配 Volume 权限 --user 999:999 \ # 环境变量 -e MYSQL_ROOT_PASSWORDStrongPass123! \ -e MYSQL_DATABASEmyapp \ -e MYSQL_USERappuser \ -e MYSQL_PASSWORDAppPass456 \ # 暴露端口生产环境建议用 host 网络或 service mesh -p 3306:3306 \ # MySQL 5.7 镜像 mysql:5.7注意--user 999:999是 MySQL 官方镜像默认的mysql用户 UID/GID。如果不指定容器以root启动会以root:root权限写入 Volume导致后续非 root 容器如备份任务无法读取。验证挂载是否成功$ docker exec -it mysql-prod ls -ld /var/lib/mysql drwxr-xr-x 5 mysql mysql 4096 Jun 15 08:22 /var/lib/mysql $ docker exec -it mysql-prod mount | grep mysql-prod-data /dev/sda1 on /var/lib/mysql type ext4 (rw,relatime,errorsremount-ro)看到on /var/lib/mysql type ext4证明 Volume 已正确挂载为独立文件系统。6.3 MySQL 配置优化让 Volume 发挥最大性能默认 MySQL 配置在 Volume 上性能不佳需针对性优化。关键参数如下写入/etc/my.cnf[mysqld] # 数据目录必须与挂载点一致 datadir /var/lib/mysql # 关键绕过文件系统缓存直接写磁盘对 SSD 友好 innodb_flush_method O_DIRECT # 减少 fsync 次数提升写入吞吐牺牲少量安全性RPO≈1秒 innodb_flush_log_at_trx_commit 2 # 日志文件单独挂载可选进一步隔离 IO innodb_log_group_home_dir /var/lib/mysql/logs # Buffer Pool 大小设为内存的 70%4G 内存 → 2.8G innodb_buffer_pool_size 2800M # 启用慢查询日志挂载到 Volume便于分析 slow_query_log ON slow_query_log_file /var/lib/mysql/slow.log long_query_time 2提示innodb_flush_methodO_DIRECT是 Volume 性能提升的关键。它让 InnoDB 绕过 Linux Page Cache直接与磁盘交互避免双重缓存InnoDB Buffer Pool Page Cache导致的内存浪费和延迟抖动。实测在 1000TPS 场景下平均响应时间从 120ms 降至 45ms。6.4 自动化备份脚本Volume 在线备份不中断业务备份脚本必须满足不锁表、不中断服务、压缩传输、保留 7 天。使用mysqldumpgzipcron组合#!/bin/bash # backup-mysql.sh VOLUME_NAMEmysql-prod-data BACKUP_DIR/backups/mysql DATE$(date %Y%m%d_%H%M%S) HOSTNAME$(hostname) # 创建备份目录 mkdir -p $BACKUP_DIR # 使用 mysqldump 在线备份--single-transaction 确保一致性 docker exec mysql-prod \ mysqldump -uroot -pStrongPass123! \ --all-databases \ --single-transaction \ --routines \ --triggers \ --events \ | gzip $BACKUP_DIR/full_${HOSTNAME}_${DATE}.sql.gz # 清理 7 天前的备份 find $BACKUP_DIR -name full_*_${DATE:0:8}* -mtime 7 -delete echo Backup completed: $BACKUP_DIR/full_${HOSTNAME}_${DATE}.sql.gz部署到宿主机 crontab# 每天凌晨 2 点执行 0 2 * * * /usr/local/bin/backup-mysql.sh /var/log/mysql-backup.log 21实操心得曾有个客户用--lock-all-tables备份导致备份期间所有写请求阻塞订单系统超时率飙升至 40%。--single-transaction基于 MVCC对 InnoDB 表完全无锁这才是生产环境的正确姿势。7. 常见问题与排查技巧实录7.1 Volume 挂载后容器内目录为空——权限与初始化陷阱现象docker run -v myvol:/data nginx:alpine启动后docker exec -it container ls /data返回空但docker volume inspect myvol显示CreatedAt正确。排查思路检查 Volume 是否真的为空sudo ls -la /var/lib/docker/volumes/myvol/_data检查容器内挂载点权限docker exec -it container ls -ld /data检查容器进程 UIDdocker exec -it container id根本原因Volume 第一次挂载时Docker 会以容器内进程的 UID 创建_data目录。如果容器以root启动_data属主是root:root但若应用进程如nginx以nginx用户UID101运行它就没有权限在/data下创建文件。解决方案方案 A推荐启动时指定用户让 Volume 初始化匹配docker run -d --user 101:101 -v myvol:/data nginx:alpine方案 B手动初始化 Volumedocker run --rm -v myvol:/data alpine chown -R 101:101 /data注意chown必须在 Volume 创建后、首次挂载前执行否则容器内进程已创建文件chown无效。7.2 “device or resource busy” 删除 Volume 失败——容器残留与挂载点泄漏现象docker volume rm myvol报错Error response from daemon: remove myvol: volume is in use但docker ps显示无容器运行。排查命令# 查看所有容器含已退出的 $ docker ps -a | grep myvol # 查看 Volume 被哪些容器引用Docker 20.10 $ docker volume inspect myvol | jq .[] | .UsageData # 检查宿主机挂载点可能被其他进程占用 $ mount | grep myvol $ lsof D /var/lib/docker/volumes/myvol/_data常见原因与修复原因1容器已退出但未清理docker rm exited-container-id彻底删除。原因2Volume 被其他容器的--volumes-from引用docker ps --format {{.Names}} {{.Mounts}} | grep myvol找出容器docker rm -f。原因3宿主机进程直接访问 Volume 目录如vim /var/lib/docker/volumes/myvol/_data/file.txtlsof找出进程 PIDkill -9 pid。提示定期执行docker system prune -a -f清理所有未