ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker数据卷实战:从持久化到MySQL容器存储全解析

Docker数据卷实战:从持久化到MySQL容器存储全解析 1. 为什么容器里的数据会“丢”理解容器文件系统的本质开始聊数据卷之前我先把一个很多人踩过的坑摆出来你敢不敢在没做任何持久化处理的情况下直接跑一个 MySQL 容器然后往里写业务数据我当年第一次干这事容器运行得好好的数据也查得到结果某天手一抖执行了docker rm -f mysql再重新启动一个新容器所有表、数据、配置全部灰飞烟灭那一刻真是汗流浃背。这个问题的根源得从容器文件系统的镜像分层机制说起。Docker 镜像本质上是一堆只读层的堆叠容器运行时在只读层之上加一层可写层。你对容器文件系统做的所有修改包括写入文件、安装软件、修改配置都会被记录在这个可写层里。听着好像没问题但一旦容器被删除这一层可写层也会跟着销毁数据自然就没了。这还不算完哪怕容器还在只是被重启有些写入如果不落盘也会因为各种原因丢失。所以数据卷要解决的核心问题说白了就三个持久化容器删了、重建了数据还得在。数据库、配置文件、日志这类东西必须脱离容器的生命周期存在。共享多个容器需要读写同一份数据比如 Nginx 和 PHP-FPM 两个容器要协同处理同一个项目目录里的文件。性能容器可写层在部分存储驱动下性能一般而数据卷是直接挂载宿主机目录或专用存储卷读写效率更稳。换句话说数据卷是容器世界里“数据不随容器陪葬”的根本保障。谁要是觉得“容器里的文件只要还在容器里就算安全”那迟早会被生产事故教育。让我用一个生活类比帮你建立直觉容器就像一间临时租来的房子镜像就是精装修的样板间。你住进去之后添置的家具、写的文件、改的装修全在房子内部。某一天房东Docker说房子到期拆了重建你屋里所有私人物品全部没了。数据卷呢就相当于你在外面租了一个仓库把贵重东西统统放进去房子的墙、地板和仓库是连通的房子拆了重建一百遍仓库里的东西纹丝不动。2. 三种持久化方案怎么选Volume、Bind Mount、tmpfs 的区别与实战Docker 官方提供三种数据挂载方式很多人一上来就懵到底用-v还是-mount到底该挂目录还是该建卷我先把三者的本质区别摊开讲清楚再给你一套可以“抄作业”的选型逻辑。2.1 数据卷VolumeDocker 替你管理的那块地数据卷是 Docker 官方最推荐的方式。它的特点是由 Docker 守护进程在宿主机上创建一个专门目录默认在/var/lib/docker/volumes/下然后把容器里的路径挂到这个目录上。你不需要关心这个目录具体在哪Docker 自己管。用-v创建具名卷并挂载的写法docker volume create mydata docker run -d --name nginx-volume -p 8080:80 -v mydata:/usr/share/nginx/html nginx:latest也可以不提前建卷直接写卷名Docker 会自动创建docker run -d -v mydata:/usr/share/nginx/html nginx:latestVolume 的核心优势有三个跨平台迁移方便用docker volume系列命令可以统一管理。不用担心宿主机目录权限问题Docker 会配合容器内用户处理。配合docker volume backup之类的工具或第三方插件可以做更精细的备份策略。缺点是如果你需要直接到宿主机上改文件还得去翻 Docker 的存储目录路径比较深操作起来不够直观。2.2 绑定挂载Bind Mount直接指路宿主机目录绑定挂载是把宿主机上的任意目录直接映射到容器里。这个方式最直观也最适合开发环境比如你本地代码在/home/user/project想直接在容器里跑服务并实时看到代码修改那就 bind mountdocker run -d --name web-dev -p 3000:3000 -v /home/user/project:/app node:18注意-v后面只要带绝对路径Docker 就认为是 bind mount不带路径只有名字就认为是 volume。这个区分很容易绕晕其实记住一句口诀就行——“带斜杠的是宿主机路径不带斜杠的是卷名”。Bind mount 的灵活性高但也有个大坑宿主机的目录权限会直接覆盖容器内目录权限。比如宿主机目录是 root 所有、权限 755容器内进程以普通用户运行那容器里就是没权限写入。2.3 tmpfs不落盘的内存挂载tmpfs 直接挂在内存里不写宿主机磁盘适合放密码、临时文件、session 这类重启就无所谓的数据。性能极好但数据没了就是真没了。用法docker run -d --name temp-test --tmpfs /tmp nginx:latest这个方式在生产里用得不多主要在开发调试或跑短任务时用。2.4 选型实战Nginx 静态站点挂载我拿一个最常见的需求来演示把宿主机上的静态网站目录挂到 Nginx 容器里。假设目录是/data/www里面有index.html。两个等价命令方式一-vdocker run -d --name website -p 8080:80 \ -v /data/www:/usr/share/nginx/html:ro nginx:latest方式二--mountdocker run -d --name website -p 8080:80 \ --mount typebind,src/data/www,dst/usr/share/nginx/html,readonly nginx:latest--mount语法比-v啰嗦但语义更清晰适合写进脚本或 compose 文件里让人一眼看懂。ro只读挂载是个好习惯静态文件本来就不该被容器改写加个只读能防止容器出问题的时候把宿主机文件弄乱。我给一张三种方式的对照表方便你存档特性VolumeBind Mounttmpfs宿主机位置Docker 管理目录自定义任意路径内存适合场景数据库数据、容器间共享、生产持久化代码开发、配置注入临时文件、密钥持久化是是否性能好取决于宿主机磁盘极快管理难度低中低跨主机迁移容易较麻烦不适用3. 从零规划持久化存储MySQL 容器完整实战聊了这么多理论该上手干点真东西了。我用 MySQL 容器做持久化实战因为它是数据持久化需求最典型、最不能出岔子的场景。你在热词列表里应该也看到了大量“docker安装mysql失败”“docker安装mysql8.0并使用”“访问docker容器内的mysql”这类搜索说明这块确实是新手重灾区。3.1 创建数据卷并挂载 MySQL第一步先建立专用的数据卷。有人喜欢让 Docker 自动建但我的习惯是显式创建并命名因为后续做备份、迁移、查看元数据时具名卷比匿名卷好找得多。docker volume create mysql-data docker volume create mysql-config然后启动 MySQL 8.0 容器docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDMyStrongPass123 \ -e MYSQL_DATABASEappdb \ -e MYSQL_USERappuser \ -e MYSQL_PASSWORDAppUserPass123 \ -v mysql-data:/var/lib/mysql \ -v mysql-config:/etc/mysql/conf.d \ mysql:8.0这里有两个细节需要展开讲。第一个细节是挂载路径的选择。/var/lib/mysql是 MySQL 的数据目录这个必须挂/etc/mysql/conf.d是配置文件目录挂它主要是为了日后调整配置不用进容器。很多教程只告诉你挂数据目录这是对的但生产环境你往往还需要改字符集、改max_connections这些配置散落在容器里如果不挂出来每次改配置都得docker exec进容器用 vi 改效率极低而且容器一重建配置又没了。第二个细节是权限。网上大量“docker安装mysql失败”的案例其实就是卡在权限上。MySQL 容器内的 mysql 用户 UID 是 999它要求数据目录的属主必须是这个 UID。如果你在宿主机上预先创建了目录并手动改了权限成 root容器起来就直接报Permission denied。所以用 Docker 管理的具名卷反而是最省事的——Docker 初始化卷的时候会自动处理好属主和 SELinux 上下文。跑起来之后先测试一下数据是否真的写入了docker exec -it mysql8 mysql -uroot -pMyStrongPass123 -e CREATE DATABASE testdb; USE testdb; CREATE TABLE t1(id INT); INSERT INTO t1 VALUES (1); SELECT * FROM t1;看到查询结果后我们模拟最惊险的场景删库跑路式操作——直接删除容器。docker stop mysql8 docker rm mysql8然后重新用同样的卷创建新容器docker run -d \ --name mysql8-new \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDMyStrongPass123 \ -v mysql-data:/var/lib/mysql \ -v mysql-config:/etc/mysql/conf.d \ mysql:8.0再查一次刚才那张表docker exec -it mysql8-new mysql -uroot -pMyStrongPass123 -e SELECT * FROM testdb.t1;你会发现数据安安静静地躺在那里。这就是数据卷最核心的价值容器可以随时推倒重来数据永远锚定在卷里。3.2 数据卷的备份、恢复与迁移持久化的另一面是备份和迁移。如果你只做了持久化但不会备份那跟裸奔没区别。手动备份数据卷的标准姿势是用一个临时容器把卷打包docker run --rm \ -v mysql-data:/source \ -v /data/backup:/backup \ ubuntu:22.04 \ tar czf /backup/mysql-data-$(date %F).tar.gz -C /source .解释一下这条命令的逻辑--rm表示容器运行完立刻删除不会残留垃圾“-v mysql-data:/source” 把数据卷挂到容器里的 /source“-v /data/backup:/backup” 把宿主机备份目录挂进去容器里执行的命令是把 /source 内容打包到 /backup。整个操作完全不碰 MySQL 本身纯粹是文件级备份非常干净。恢复备份的思路反过来docker run --rm \ -v mysql-data:/target \ -v /data/backup:/backup \ ubuntu:22.04 \ tar xzf /backup/mysql-data-2025-01-01.tar.gz -C /target注意恢复前最好先把目标卷里现有的数据清掉不然可能出现新旧文件混杂。清空卷可以用docker volume rm或临时容器执行find /target -type f -delete。迁移到另一台机器也不难思路就是“打包卷文件 → 传到新机器 → 恢复到同名卷”。生产环境里我一般结合rsync做增量同步隔一段时间把卷目录直接同步过去配合主从复制基本能做到分钟级 RPO。3.3 绑定挂载方式实战配置文件管理有些情况下你不想用 Docker 管理的卷而是想直接维护宿主机上的配置文件。比如 Redis它的配置经常需要调整用 bind mount 最直观mkdir -p /data/redis cp /path/to/redis.conf /data/redis/redis.conf docker run -d \ --name redis-server \ -p 6379:6379 \ -v /data/redis/redis.conf:/etc/redis/redis.conf \ -v /data/redis/data:/data \ redis:7.0 \ redis-server /etc/redis/redis.conf这里有一个血泪教训如果你 bind mount 了一个宿主机上不存在的文件Docker 会自动帮你创建一个空目录而不是空文件。也就是说如果/data/redis/redis.conf不小心被我写成了/data/redis/redis.conf/多了个斜杠容器里看到的就不是配置文件而是一个目录Redis 直接启动失败。这个坑在搭建 Redis 主从时经常遇到因为每台机器的配置文件名稍有差异就容易被路径问题绊倒。绑定挂载还有个隐患宿主机上的配置文件权限如果不对容器内的程序可能读不到。我踩过一次把宿主机上 root 所有的 redis.conf 挂进容器Redis 进程以 redis 用户运行结果启动时报Cant open the log file: Permission denied。解决方案很简单把配置文件的属主改成容器内用户或者给文件开 644 权限。4. 多容器数据共享你要知道的几种姿势与坑持久化只是数据卷的一半功能另一半是共享。多个容器要访问同一份数据这个需求在微服务、前后端分离、定时任务等架构里太常见了。4.1 同卷共享最简单的多容器读写同一份数据假设你有两个容器一个写日志一个读日志做分析。两个容器共享同一个卷docker volume create shared-logs docker run -d --name producer -v shared-logs:/logs busybox \ sh -c while true; do echo \$(date)\ /logs/app.log; sleep 2; done docker run -d --name consumer -v shared-logs:/logs ubuntu:22.04 \ tail -f /logs/app.log这个做法的优点是简单直接缺点是并发写的时候可能出现锁竞争尤其是数据库类应用共享卷时的并发一致性。所以生产上共享卷更多用于只读场景或追加写场景比如配置文件分发、静态资源供给、日志聚合。4.2--volumes-from数据卷继承--volumes-from是 Docker 里非常有年代感但依然好用的功能。它的作用是新容器直接继承另一个容器的所有挂载点。比如你启动了一个配置初始化容器里面有各种配置文件卷后续容器可以直接这样继承docker run -d --name config-generator -v /data/configs:/configs alpine \ sh -c echo server: {port: 8080} /configs/app.yml sleep 3600 docker run -d --name web-app --volumes-from config-generator -p 8080:8080 myapp:latestweb-app 容器里直接就能在/configs下看到生成的配置。这个功能在你需要“一个容器负责初始化数据另一个容器负责跑业务”的架构里特别顺手不需要关心数据卷的名字到底是什么只要知道哪个容器挂了卷就行。但是--volumes-from有个容易被忽视的坑它继承的是“挂载关系”而不是快照。如果源容器被删了挂载关系依然存在底层卷还在但如果你源容器用的是匿名卷源容器删了之后匿名卷不会被自动删除却会变成“孤儿卷”时间长了占用磁盘。所以用这个功能时建议源容器要么用具名卷要么一直保持运行状态。4.3 容器与宿主机之间的文件交换很多新手操作容器文件都用docker cpdocker cp ./local-file.txt container-name:/app/ docker cp container-name:/app/output.txt ./local-output.txtdocker cp适合临时传小文件正式场景还是要用挂载来做。开发环境里最常见的是把当前目录直接挂进去docker run -d --name frontend-dev -v $(pwd):/app -p 3000:3000 node:18 npm run dev这样宿主机上改代码容器里的服务自动热更新。这种工作流能成立的核心原因是 bind mount 是实时同步的不是启动时复制一次。这里要特别提醒一个坑挂载目录会遮蔽容器内原有内容。比如镜像里/app目录有预置代码你 bind mount 宿主机空目录到/app那容器里原本的代码就被“遮住”了。这不是数据被清空而是挂载点指向了宿主机目录容器镜像里的原文件还在但你访问不到。想要保留镜像里的内容要么启动时做一次拷贝要么把挂载点改成子目录。4.4 容器间共享时的权限与安全策略数据共享最让人头疼的问题就是权限。最好从一开始就规划好 UID/GID 策略不然等到容器都跑起来才发现互相读写不了再逐个docker exec chown会把人逼疯。我的建议是在创建容器的阶段就固定 UID。例如运行 Nginx 和 PHP-FPM 两个容器共享/app目录两个镜像内部的运行用户 UID 很可能不一样Nginx 是 nginx UID 101PHP-FPM 是 www-data UID 33处理不好就是 PHP 写入的文件 Nginx 读不了Nginx 创建的缓存 PHP 删不掉。方案一是改容器镜像里的用户 ID让它们一致方案二是在宿主机上把共享目录设成 ACL允许多个 UID 读写。方案一更彻底我用的是 Dockerfile 里显式指定FROM php:8.2-fpm RUN usermod -u 1000 www-data groupmod -g 1000 www-data然后用这个镜像跑容器宿主机目录属主就设成 UID 1000两个容器加宿主机三方都能读写。这套思路可以延伸到任意需要跨容器共享的组合里。5. 数据卷避坑指南权限、孤儿卷、挂载遮蔽与性能说实话数据卷本身不难真正让人头秃的都是边缘情况。我把这几年实战中遇到的高频问题和对应的排查思路整理成速查表遇到问题直接对照着查。5.1 高频问题速查表现象可能原因解决办法容器启动报Permission denied卷或宿主目录属主与容器内用户不匹配用具名卷或 chown 目录属主为容器内 UID容器里看到的是空目录原本镜像里的文件不见了挂载点遮蔽了镜像内目录不要挂整个目录改挂子目录或挂载后拷贝删容器之后发现磁盘没释放匿名卷变成孤儿卷docker volume ls -f danglingtrue找出孤儿卷确认无用后删除挂载的配置文件没生效挂载路径写错或者 Docker 把文件当目录建了检查路径确保宿主机文件存在不要带尾斜杠容器重启后数据丢失挂载的是容器层路径没绑卷检查docker inspect中的 Mounts 列表多个共享卷的容器同时写文件时数据错乱并发写无锁机制改架构只让一个容器写其他容器只读或改用分布式存储5.2 孤儿卷和匿名卷的处理长期使用 Docker 的人机器上或多或少会积累一些无名无姓的卷。它们是容器删除时没带-v参数导致残留的匿名卷不占内存但占磁盘。查看方式docker volume ls -f danglingtrue清理前一定确认卷里没有需要的数据我见过有人直接docker volume prune把生产环境的备份卷全干掉的惨案。安全做法是先docker volume inspect看卷挂载点再到对应目录里确认内容最后再删。另外有一个提法是 Docker 新版本在docker rm的时候有个行为差异直接docker rm container不会删除容器使用的匿名卷而docker rm -v container会连带删除匿名卷。强烈建议不要养成带-v删容器的习惯万一某个看起来像匿名卷的卷其实有其他容器还在用或者里面存了你想保留的数据那删掉就真没了。5.3 挂载遮蔽问题的正确解法前面提过挂载遮蔽这里给一个完整的例子加深印象。假设镜像里/var/www/html已经放了应用代码你想通过 bind mount 把宿主机新代码放进去运行但又不想丢镜像里的默认页面。这时候不能直接挂目录而是先启动容器再手动引入新代码比如docker run -d --name myapp -p 8080:80 myapp-image:latest docker cp ./new-code/. myapp:/var/www/html/或者直接在镜像构建时就规划好镜像里把代码放在/opt/app启动容器时把/opt/app挂到宿主机目录不要挂/var/www/html这种镜像默认根目录。如果确实需要启动时从镜像初始化数据可以加一个 entrypoint 脚本在容器启动时判断挂载目录是否为空为空就把镜像内目录内容拷贝过去再启动主进程。这是很多官方镜像比如某些 MySQL 变种的做法也是最稳妥的“镜像自带种子数据 挂载持久化”方案。5.4 性能优化别把数据卷当普通磁盘用数据卷的读写性能理论上接近宿主机磁盘但实际使用中很多细节会影响最终表现。如果你的应用对 IOPS 敏感比如数据库、消息队列需要注意几点避免在 macOS/Windows 上把整个项目目录 bind mount 进容器。这类系统的文件共享机制gRPC-FUSE 或 virtiofs本身有性能损耗尤其是大量小文件读写时和 Linux 原生环境差距明显。开发场景图方便用 bind mount 没毛病生产环境请直接用 volume 或直接在 Linux 服务器上跑。数据库数据卷最好放在 SSD 盘上。这似乎是一句废话但很多人迁移 Docker 时图省事把/var/lib/docker放在了机械盘分区MySQL 跑起来慢得令人发指排查半天才发现是磁盘问题。谨慎使用:delegated挂载选项macOS 下。这个选项可以显著提升 bind mount 的写入性能代价是容器内的写入不会立刻同步到宿主机。如果用在数据库上断电场景下数据丢失风险会变大。写代码的热更新项目可以用数据库类容器千万别碰。文件描述符数量。容器内跑高并发应用时挂载目录里的文件句柄占用受宿主机fs.file-max和容器 ulimit 限制。超过限制会报Too many open files这时候不是数据卷的问题是系统参数要调。6. 用 Docker Compose 管理数据卷生产级配置解析到这一步单纯的docker run已经不够看了。实际项目大多用 Docker Compose 来编排多容器应用数据卷的定义和管理方式跟命令行有差异这里单独拎出来讲。6.1 Compose 文件中定义数据卷一个典型的带数据卷的 Compose 文件结构version: 3.8 services: mysql: image: mysql:8.0 container_name: compose-mysql ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: MyStrongPass123 MYSQL_DATABASE: appdb volumes: - mysql_data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d:ro networks: - backend app: image: myapp:latest ports: - 8080:8080 volumes: - ./app/code:/app:ro - shared_uploads:/app/uploads depends_on: - mysql networks: - backend volumes: mysql_data: name: app-mysql-data shared_uploads: driver: local networks: backend: driver: bridge注意这里的层级关系volumes:顶层定义的是具名卷services.name.volumes配置的是挂载关系。我习惯给顶层卷显式指定name这样docker volume ls看到的是app-mysql-data而不是默认的compose_mysql_data迁移和备份时更容易匹配环境。6.2 通过 Compose 实现数据初始化Compose 里还有个常用技巧利用挂载 entrypoint 做数据初始化。比如你有一个初始化 SQL 脚本希望 MySQL 首次启动时自动导入mkdir -p ./mysql/init cat ./mysql/init/init.sql EOF CREATE DATABASE IF NOT EXISTS bizdb; USE bizdb; CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, name VARCHAR(64)); INSERT INTO users VALUES (1, admin); EOF然后 Compose 里把./mysql/init挂载到/docker-entrypoint-initdb.dservices: mysql: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql - ./mysql/init:/docker-entrypoint-initdb.d:ro这个目录很特殊MySQL 官方镜像在首次初始化数据目录时会自动按文件名顺序执行里面的.sql、.sh等文件。注意是只有在数据目录为空时才执行如果mysql_data卷里已经有数据重新挂载 init 脚本是不会再跑的。这个机制非常适合做“一键拉起新环境”。6.3 Compose 数据卷备份脚本化有了 Compose 之后备份也可以脚本化。我一般在项目仓库里放一个backup.sh内容类似#!/bin/bash set -euo pipefail BACKUP_DIR/data/backups STAMP$(date %Y%m%d-%H%M%S) VOLUMESapp-mysql-data app-shared-uploads for vol in $VOLUMES; do docker run --rm \ -v ${vol}:/source \ -v ${BACKUP_DIR}:/backup \ ubuntu:22.04 \ tar czf /backup/${vol}-${STAMP}.tar.gz -C /source . done echo Backup completed: ${BACKUP_DIR}用cron每天凌晨跑一次配合find $BACKUP_DIR -mtime 7 -delete做保留周期管理一套最简单的备份机制就落地了。别看它简陋很多小团队的生产环境就是这么撑过来的关键在“有”而不在“全”。7. 几个我踩过的坑写出来给你避雷最后分享一些常规文档里找不到的经验都是我交了“学费”才明白的。第一个坑宿主机目录权限被容器改掉。如果你 bind mount 某个目录进容器容器内进程以 root 跑写入的文件属主就是 root。就算你宿主机上平时用普通用户开发一旦容器写入过这个目录之后你在宿主机上想删改这些文件就经常碰到权限拒绝。血的教训是能用具名卷就别用 bind mount必须用 bind mount 时尽量在容器里指定非 root 用户。第二个坑数据库容器千万别用 bind mount 挂宿主机普通目录。数据库对文件属主、权限、目录结构非常敏感。宿主机目录的 SELinux 上下文、属主、挂载选项稍微不对MySQL 启动时很可能直接初始化失败或者启动一半崩溃。用 Docker 管理的具名卷Docker 会在创建时把目录属主和权限一次性设置成容器需要的状态省掉大半麻烦。我在热词列表里看到大量“docker安装mysql失败”的搜索估计八成都是栽在这类权限问题上。第三个坑docker run -v自动创建目录的规则很迷惑。如果宿主机路径不存在Docker 会以 root 身份自动创建一个目录。这个目录权限默认 755如果容器内用户是非 root写入照样失败。更骚的是如果宿主机路径写错成“文件路径带了个斜杠”Docker 会创建一个目录而不是报错让你排查半天。第四个坑卷的备份要停服务吗很多人备份 MySQL 数据卷之前纠结要不要先停容器。如果你用的是 tar 直接打包没有数据库层的在线备份机制不停服务打包出来的数据可能处于不一致状态。我的建议是MySQL 这类数据库要么用官方备份工具mysqldump、备份工具做逻辑备份要么干脆停容器做冷备。前者对数据一致性没风险后者简单粗暴但需要停机窗口。直接 tar 正在运行中的 MySQL 数据目录我吃过亏不推荐。第五个坑不要轻易执行docker system prune -a --volumes。这条命令会清掉所有未使用的卷。听起来很干净但它不区分“没用”和“我还有用但没挂载到运行中容器”。有一次我在清理磁盘时顺手执行了结果把一个离线备份用的卷删了那份数据彻底找不回来。此后我的默认操作是docker system prune不带--volumes卷手动逐个检查绝不批量删除。在我自己的日常操作习惯里现在几乎已经形成了一套固定模板凡是数据库或有状态服务一律使用具名卷凡是开发环境热更新代码一律 bind mount凡是临时容器能加--rm就加凡是删容器默认不带-v。这套模板本质上就是在反复踩坑之后沉淀下来的条件反射。数据卷管理这东西一开始觉得是 Docker 的一个小功能用到后来你会发现容器的越界能力全靠它兜底。哪怕你对 Docker 的其他部分还不太熟只要把这个模块玩明白已经能应付绝大多数业务场景了。
返回列表