[{"data":1,"prerenderedAt":1162},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fdocker\u002Fusage\u002Fdocker-persistence":158,"\u002Fdocs\u002Fdocker\u002Fusage\u002Fdocker-persistence-description":1152},[4],{"title":5,"path":6,"stem":7,"children":8,"page":36},"Docs","\u002Fdocs","docs",[9,37,45,65,148],{"title":10,"description":11,"icon":12,"path":13,"stem":14,"children":15,"page":36},"Redis","Redis是一个开源的高性能键值存储数据库，它提供了多种数据结构来存储数据，如字符串、哈希、列表、集合、有序集合等。Redis将数据存储在内存中，以提供快速的读写访问速度，并且能够通过异步的方式将数据持久化到磁盘上。它支持复制、Lua脚本、事务处理、不同级别的持久化选项以及多种客户端语言的接口。Redis广泛用于缓存、消息队列、短时数据存储和高性能的应用场景中。","skill-icons:redis-light","\u002Fdocs\u002Fredis","docs\u002F1.redis",[16],{"title":17,"path":18,"stem":19,"children":20,"description":34,"icon":35},"install","\u002Fdocs\u002Fredis\u002Finstall","docs\u002F1.redis\u002F1.install\u002F1.index",[21,24,29],{"title":22,"path":18,"stem":19,"description":23},"Introduction","The powerful Git-based CMS designed specifically for Nuxt developers.",{"title":25,"path":26,"stem":27,"description":28},"Installation","\u002Fdocs\u002Fredis\u002Finstall\u002Finstallation","docs\u002F1.redis\u002F1.install\u002F2.installation","Get started with Nuxt Content v3 in your Nuxt application.",{"title":30,"path":31,"stem":32,"description":33},"Configuration","\u002Fdocs\u002Fredis\u002Finstall\u002Fconfiguration","docs\u002F1.redis\u002F1.install\u002F3.configuration","Nuxt Content is configured with sensible defaults.",null,"icon-park-outline:install",false,{"title":38,"path":39,"stem":40,"children":41,"description":43,"icon":44},"SpringBoot","\u002Fdocs\u002Fspringboot","docs\u002F2.springboot\u002F1.index",[42],{"title":22,"path":39,"stem":40,"description":23},"Spring Boot 是一个基于 Spring 框架的‌开箱即用型 Java 开发框架‌，由 Pivotal 团队于 2014 年推出，旨在彻底简化 Spring 应用的初始搭建、配置和部署流程。其核心理念是 ‌\"约定优于配置\"‌，通过自动化机制减少开发者的样板代码工作，显著提升效率。","simple-icons:springboot",{"title":46,"path":47,"stem":48,"children":49,"description":63,"icon":64},"Rocky Linux","\u002Fdocs\u002Frocky","docs\u002F3.rocky\u002F1.index",[50,53,58],{"title":51,"path":47,"stem":48,"description":52},"1 Rocky Linux 概述","Rocky Linux 是一个开源、免费的企业级 Linux 操作系统。它的核心目标是与 Red Hat Enterprise Linux (RHEL) 100% 完全兼容，可以看作是原本广受欢迎的 CentOS 的“替代者”和“精神继承者.",{"title":54,"path":55,"stem":56,"description":57},"2 VMware安装Rocky Linux","\u002Fdocs\u002Frocky\u002Finstall","docs\u002F3.rocky\u002F2.install","本文主要介绍了VMware Workstation 17.x 虚拟机如何快速安装Rocky Linux 10.x 版本的Linux操作系统。",{"title":59,"path":60,"stem":61,"description":62},"3 Rocky Linux 10安装Docker","\u002Fdocs\u002Frocky\u002Fdocker","docs\u002F3.rocky\u002F3.docker","在 Rocky Linux 10 上安装 Docker 的推荐方式是使用其官方仓库。下面是完整的安装步骤。","Rocky Linux 是一个开源、免费的企业级 Linux 操作系统。它的核心目标是与 Red Hat Enterprise Linux (RHEL) 100% 完全兼容，可以看作是原本广受欢迎的 CentOS 的“替代者”和“精神继承者”","logos:rocky-linux-icon",{"title":66,"description":67,"icon":68,"path":69,"stem":70,"children":71,"page":36},"Docker","Docker是一个开源的应用容器引擎，可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中，然后发布到Docker容器中运行，也可以实现虚拟化。","devicon:docker","\u002Fdocs\u002Fdocker","docs\u002F4.docker",[72,96,111,137],{"title":73,"path":74,"stem":75,"children":76,"description":34,"icon":95},"Docker 基础","\u002Fdocs\u002Fdocker\u002Fbasic","docs\u002F4.docker\u002F1.basic\u002F1.index",[77,80,85,90],{"title":78,"path":74,"stem":75,"description":79},"Docker基础概念","Docker是一个基于Go语言开发并遵从 Apache2.0 协议开源的应用容器引擎，可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中，然后发布到Docker环境中运行，容器彼此隔离，但可以通过特定的通道相互传递信息。保证了在任何环境下运行的应用程序具有相同的行为，这极大地减少了环境差异导致的“在我机器上可以运行”的问题。",{"title":81,"path":82,"stem":83,"description":84},"Docker核心架构","\u002Fdocs\u002Fdocker\u002Fbasic\u002Fdocker-architecture","docs\u002F4.docker\u002F1.basic\u002F2.docker-architecture","Docker 采用 C\u002FS 架构，客户端通过 REST API 将指令下达给常驻后台的守护进程（dockerd）；守护进程则依靠 Linux 内核的 Namespace 实现环境隔离、Cgroups 实现资源配额，并利用 UnionFS 的分层存储机制构建镜像和容器，从而在单台宿主机上实现了轻量级、高性能的应用沙箱。",{"title":86,"path":87,"stem":88,"description":89},"Docker镜像加速","\u002Fdocs\u002Fdocker\u002Fbasic\u002Fdocker-mirror-acceleration","docs\u002F4.docker\u002F1.basic\u002F3.docker-mirror-acceleration","Docker安装完成后默认的官方镜像仓库是[Docker Hub‌](https:\u002F\u002Fhub.docker.com\u002F){target=blank}，这是Docker官方维护的公共镜像注册中心。但对于国内Docker用户而言，在没有魔法的情况下是无法访问Docker Hub网站的，也无法从Docker Hub仓库拉取镜像。目前国内常用的2种方法：**Docker镜像加速源** 和 **配置Docker代理**。",{"title":91,"path":92,"stem":93,"description":94},"Docker Context","\u002Fdocs\u002Fdocker\u002Fbasic\u002Fdocker-context","docs\u002F4.docker\u002F1.basic\u002F4.docker-context","Docker Context 是 Docker 19.03 版本引入的一项功能，它为 Docker 客户端（CLI）提供了一套标准化的“连接配置”机制。通过 Context，你可以轻松地在本地开发环境、远程服务器、云服务等多个 Docker 环境之间快速切换，极大地提升了多环境管理的效率。","mdi:information-box-outline",{"title":97,"description":34,"icon":35,"path":98,"stem":99,"children":100,"page":36},"Docker 安装","\u002Fdocs\u002Fdocker\u002Finstall","docs\u002F4.docker\u002F2.install",[101,106],{"title":102,"path":103,"stem":104,"description":105},"CentOS安装Docker","\u002Fdocs\u002Fdocker\u002Finstall\u002Fcentos-docker-install","docs\u002F4.docker\u002F2.install\u002F1.centos-docker-install","在 CentOS 上安装 Docker，准确的说是安装 Docker Engine, 本文主要介绍如何在CentOS上安装Docker。",{"title":107,"path":108,"stem":109,"description":110},"Rocky安装Docker","\u002Fdocs\u002Fdocker\u002Finstall\u002Frocky-docker-install","docs\u002F4.docker\u002F2.install\u002F2.rocky-docker-install","在 Rocky Linux 10 上安装 Docker 和 Centos 安装 Docker 步骤基本类似的，推荐方式是使用其官方仓库。下面是完整的安装步骤。",{"title":112,"description":34,"icon":113,"path":114,"stem":115,"children":116,"page":36},"Docker 使用","fluent:data-usage-24-regular","\u002Fdocs\u002Fdocker\u002Fusage","docs\u002F4.docker\u002F3.usage",[117,122,127,132],{"title":118,"path":119,"stem":120,"description":121},"Docker持久化","\u002Fdocs\u002Fdocker\u002Fusage\u002Fdocker-persistence","docs\u002F4.docker\u002F3.usage\u002F1.docker-persistence","在 Docker 中，容器默认是无状态的，容器删除后内部数据会直接丢失，Docker 官方提供了三类核心数据持久化方案，分别适配不同的使用场景：卷（Volumes）、绑定挂载（Bind Mounts） 和 tmpfs 挂载。",{"title":123,"path":124,"stem":125,"description":126},"Docker容器自启动","\u002Fdocs\u002Fdocker\u002Fusage\u002Fcontainer-restart","docs\u002F4.docker\u002F3.usage\u002F2.container-restart","Docker容器自启动策略是指Docker服务运行期间，特定容器在退出、崩溃或宿主机重启后，容器是否自动重新运行。",{"title":128,"path":129,"stem":130,"description":131},"Docker Dockerfile","\u002Fdocs\u002Fdocker\u002Fusage\u002Fdocker-dockerfile","docs\u002F4.docker\u002F3.usage\u002F3.docker-dockerfile","本文旨在探讨Dockerfile的功能定位与指令体系，并在此基础上，给出基于Dockerfile实现自定义镜像构建的具体方法。",{"title":133,"path":134,"stem":135,"description":136},"连接远程Docker服务","\u002Fdocs\u002Fdocker\u002Fusage\u002Fconnect-remote-docker","docs\u002F4.docker\u002F3.usage\u002F4.connect-remote-docker","在日常开发或测试中，我们经常需要在本地开发环境中直接操作远程 Docker 服务——执行镜像构建、容器启停等命令，效果与直接在远程服务器上操作完全一致。这能极大提升调试与部署效率，同时避免频繁登录远程服务器的繁琐。",{"title":138,"description":34,"icon":139,"path":140,"stem":141,"children":142,"page":36},"Docker 参考手册","material-symbols-light:docs-outline","\u002Fdocs\u002Fdocker\u002Fresources","docs\u002F4.docker\u002F5.resources",[143],{"title":144,"path":145,"stem":146,"description":147},"Docker 命令大全","\u002Fdocs\u002Fdocker\u002Fresources\u002Fdocker-command","docs\u002F4.docker\u002F5.resources\u002F1.docker-command","下面介绍Docker常见的基础命令。",{"title":149,"path":150,"stem":151,"children":152,"description":156,"icon":157},"Linux","\u002Fdocs\u002Flinux","docs\u002F5.Linux\u002F1.index",[153],{"title":154,"path":150,"stem":151,"description":155},"1.dnf和yum区别","一句话概括：dnf 是 yum 的下一代升级版，你可以把 dnf 看作\"优化重构后的yum\"","Linux是免费开源、多用户多任务、高稳定性与安全性、支持多平台硬件的Unix操作系统。文档整理了Linux相关的命令和常见问题处理方法。","logos:linux-tux",{"id":159,"title":160,"body":161,"createDate":1143,"description":121,"extension":1144,"links":34,"meta":1145,"navigation":1146,"path":119,"readingTime":1147,"seo":1148,"sitemap":1149,"stem":120,"updateDate":34,"wordCount":1150,"__hash__":1151},"docs\u002Fdocs\u002F4.docker\u002F3.usage\u002F1.docker-persistence.md","Docker数据持久化方案",{"type":162,"value":163,"toc":1134},"minimark",[164,169,174,419,423,451,455,477,481,577,581,1130],[165,166,168],"h2",{"id":167},"_1-核心持久化方案详解","1. 核心持久化方案详解",[170,171,173],"h3",{"id":172},"_11-数据卷volumes","1.1 ‌数据卷(Volumes)",[175,176,177,185,191,197,203,209,215],"ul",{},[178,179,180,184],"li",{},[181,182,183],"strong",{},"管理主体‌："," 由 Docker 守护进程全权管理，数据默认存放在宿主机 \u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002F 目录下。",[178,186,187,190],{},[181,188,189],{},"‌核心优势‌："," 跨操作系统可移植、支持多容器共享、权限自动管控，容器删除后数据卷依然保留。",[178,192,193,196],{},[181,194,195],{},"‌适用场景‌："," 生产环境的数据库存储、核心配置文件、需要定期备份的重要数据，是生产环境的首选方案，也是Docker官方推荐的持久化方案。",[178,198,199,202],{},[181,200,201],{},"‌基础用法‌："," docker volume create my_data && docker run -v my_data:\u002Fcontainer\u002Fpath nginx。",[178,204,205,208],{},[181,206,207],{},"默认位置："," 默认位置：在Linux系统上，Docker默认将所有的卷（Volumes）数据保存在宿主机的 \u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002F 目录下。每个卷会在这个目录下有一个以自己名字命名的文件夹，其中的 _data 子目录就是存放实际数据的地方。",[178,210,211,214],{},[181,212,213],{},"工作原理："," 当你创建一个卷（例如 my_volume）并将它挂载到容器的 \u002Fapp\u002Fdata 目录时，容器内对 \u002Fapp\u002Fdata 的任何读写操作，实际上都是在操作宿主机上 \u002Fvar\u002Flib\u002Fdocker\u002Fvolumes\u002Fmy_volume\u002F_data 这个目录里的文件。这就是为什么即使容器被删除，数据依然存在。",[178,216,217,220,221,224,225,233,262,270,285,294,328,337,358,366,382,391,409],{},[181,218,219],{},"更改Volumes的默认存储位置","：",[222,223],"br",{},"既然默认位置可以修改，当系统盘空间不足或出于性能考虑时，可以将其迁移到其他位置。操作方法如下：",[226,227,228],"ol",{},[178,229,230],{},[181,231,232],{},"停止 Docker 服务",[234,235,240],"pre",{"className":236,"code":237,"language":238,"meta":239,"style":239},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","sudo systemctl stop docker\n","bash","",[241,242,243],"code",{"__ignoreMap":239},[244,245,248,252,256,259],"span",{"class":246,"line":247},"line",1,[244,249,251],{"class":250},"sBMFI","sudo",[244,253,255],{"class":254},"sfazB"," systemctl",[244,257,258],{"class":254}," stop",[244,260,261],{"class":254}," docker\n",[226,263,265],{"start":264},2,[178,266,267],{},[181,268,269],{},"编辑 Docker 配置文件：",[234,271,273],{"className":236,"code":272,"language":238,"meta":239,"style":239},"sudo vi \u002Fetc\u002Fdocker\u002Fdaemon.json\n",[241,274,275],{"__ignoreMap":239},[244,276,277,279,282],{"class":246,"line":247},[244,278,251],{"class":250},[244,280,281],{"class":254}," vi",[244,283,284],{"class":254}," \u002Fetc\u002Fdocker\u002Fdaemon.json\n",[226,286,288],{"start":287},3,[178,289,290,293],{},[181,291,292],{},"添加或修改 data-root 配置："," 在文件中添加或修改 data-root 字段，指定你希望存储所有Docker数据（包括镜像、容器和卷）的新目录",[234,295,297],{"className":236,"code":296,"language":238,"meta":239,"style":239},"{\n   \"data-root\": \"\u002Fpath\u002Fto\u002Fyour\u002Fnew\u002Fdocker-data\"\n}\n",[241,298,299,305,323],{"__ignoreMap":239},[244,300,301],{"class":246,"line":247},[244,302,304],{"class":303},"sMK4o","{\n",[244,306,307,310,314,317,320],{"class":246,"line":264},[244,308,309],{"class":250},"   \"data-root\"",[244,311,313],{"class":312},"s2Zo4",":",[244,315,316],{"class":303}," \"",[244,318,319],{"class":254},"\u002Fpath\u002Fto\u002Fyour\u002Fnew\u002Fdocker-data",[244,321,322],{"class":303},"\"\n",[244,324,325],{"class":246,"line":287},[244,326,327],{"class":303},"}\n",[226,329,331],{"start":330},4,[178,332,333,336],{},[181,334,335],{},"迁移现有数据:"," 如果你已有Docker数据需要保留，需要将它们从旧目录移动到新目录。",[234,338,340],{"className":236,"code":339,"language":238,"meta":239,"style":239},"sudo rsync -aP \u002Fvar\u002Flib\u002Fdocker\u002F \u002Fpath\u002Fto\u002Fyour\u002Fnew\u002Fdocker-data\n",[241,341,342],{"__ignoreMap":239},[244,343,344,346,349,352,355],{"class":246,"line":247},[244,345,251],{"class":250},[244,347,348],{"class":254}," rsync",[244,350,351],{"class":254}," -aP",[244,353,354],{"class":254}," \u002Fvar\u002Flib\u002Fdocker\u002F",[244,356,357],{"class":254}," \u002Fpath\u002Fto\u002Fyour\u002Fnew\u002Fdocker-data\n",[226,359,361],{"start":360},5,[178,362,363],{},[181,364,365],{},"重启 Docker 服务：",[234,367,369],{"className":236,"code":368,"language":238,"meta":239,"style":239},"sudo systemctl start docker\n",[241,370,371],{"__ignoreMap":239},[244,372,373,375,377,380],{"class":246,"line":247},[244,374,251],{"class":250},[244,376,255],{"class":254},[244,378,379],{"class":254}," start",[244,381,261],{"class":254},[226,383,385],{"start":384},6,[178,386,387,390],{},[181,388,389],{},"验证并清理（可选）：","  确认一切运行正常后，可以删除旧的Docker数据目录以释放空间。",[234,392,394],{"className":236,"code":393,"language":238,"meta":239,"style":239},"sudo rm -rf \u002Fvar\u002Flib\u002Fdocker.old\n",[241,395,396],{"__ignoreMap":239},[244,397,398,400,403,406],{"class":246,"line":247},[244,399,251],{"class":250},[244,401,402],{"class":254}," rm",[244,404,405],{"class":254}," -rf",[244,407,408],{"class":254}," \u002Fvar\u002Flib\u002Fdocker.old\n",[410,411,412],"warning",{},[413,414,415,416,418],"p",{},"特别提醒：",[222,417],{},"\ndata-root 配置会更改 所有Docker数据（包括镜像、容器、卷等）的存储根目录。这是一个全局性的更改，操作前请务必备份重要数据。",[170,420,422],{"id":421},"_12-绑定挂载bind-mounts","1.2 绑定挂载(Bind Mounts)",[175,424,425,431,436,441,446],{},[178,426,427,430],{},[181,428,429],{},"‌管理主体‌："," 由用户直接指定宿主机的任意绝对路径，映射到容器内部目录。",[178,432,433,435],{},[181,434,189],{}," 宿主机与容器文件实时同步，可直接用宿主机工具编辑、查看挂载目录内容。",[178,437,438,440],{},[181,439,195],{}," 本地开发调试（实现代码热加载）、挂载宿主机系统配置文件（如 \u002Fetc\u002Flocaltime）、需要直接在宿主机管理的日志目录。",[178,442,443,445],{},[181,444,201],{}," docker run -v \u002Fhost\u002Fabsolute\u002Fpath:\u002Fcontainer\u002Fpath nginx",[178,447,448,450],{},[181,449,213],{}," 当你对一个容器设置了绑定挂载目录后，容器中只是映射了(引用)宿主机的挂载目录，而非数据复制，容器内并没有独立的一份数据副本。容器内的挂载点直接指向宿主机的物理路径，两者共享同一份底层存储数据。宿主机和容器无论哪一方修改数据，都是实时同步可见的。不存在数据同步延迟或者不一致问题。删除容器后，宿主机上的数据依然保留；删除宿主机上的文件，容器内对应文件即刻消失。",[170,452,454],{"id":453},"_13-tmpfs内存挂载","1.3 ‌tmpfs内存挂载",[175,456,457,462,467,472],{},[178,458,459,461],{},[181,460,429],{}," 数据完全存放在宿主机内存中，不会写入磁盘。",[178,463,464,466],{},[181,465,189],{}," 读写性能远高于磁盘，数据不会落盘，安全性高，容器停止后数据自动清空。",[178,468,469,471],{},[181,470,195],{}," 临时存放 OAuth Token、密钥等敏感临时信息，仅用于非持久化的高速缓存场景。",[178,473,474,476],{},[181,475,201],{}," docker run --tmpfs \u002Fcontainer\u002Ftmp\u002Fpath nginx",[165,478,480],{"id":479},"_2-方案核心差异对比","2. 方案核心差异对比",[482,483,484,503],"table",{},[485,486,487],"thead",{},[488,489,490,494,497,500],"tr",{},[491,492,493],"th",{},"对比维度",[491,495,496],{},"数据卷(Volumes)",[491,498,499],{},"绑定挂载(Bind Mount)",[491,501,502],{},"tmpfs 内存挂载",[504,505,506,521,535,549,563],"tbody",{},[488,507,508,512,515,518],{},[509,510,511],"td",{},"持久化能力",[509,513,514],{},"完全持久化，容器删除数据保留",[509,516,517],{},"完全持久化，依赖宿主机目录",[509,519,520],{},"不持久化，容器销毁数据丢失",[488,522,523,526,529,532],{},[509,524,525],{},"跨主机迁移",[509,527,528],{},"简单，不受宿主机路径约束",[509,530,531],{},"困难，强依赖宿主机指定路径",[509,533,534],{},"不支持迁移",[488,536,537,540,543,546],{},[509,538,539],{},"权限管理",[509,541,542],{},"Docker 自动管控，不易出错",[509,544,545],{},"需手动配置宿主机目录 UID\u002FGID",[509,547,548],{},"由内核自动管理",[488,550,551,554,557,560],{},[509,552,553],{},"性能表现",[509,555,556],{},"中等",[509,558,559],{},"读写延迟最低",[509,561,562],{},"性能最高，是 SSD 的 10 倍以上",[488,564,565,568,571,574],{},[509,566,567],{},"生产环境适配",[509,569,570],{},"首选方案",[509,572,573],{},"仅用于开发\u002F特殊配置场景",[509,575,576],{},"仅用于临时敏感数据场景",[165,578,580],{"id":579},"_3-关键注意事项","3. 关键注意事项",[582,583,585,692,748,796,837,868],"accordion",{"type":584},"multiple",[586,587,590,593,662,668,681,684],"accordion-item",{"icon":588,"label":589},"i-lucide-circle-help","最常见的文件权限问题",[413,591,592],{},"容器内的用户（通常是 root 或 uid=1000）创建的文件，在宿主机上显示为 root 或奇怪的 UID，导致宿主机无法编辑；反过来，宿主机创建的文件，容器内进程（如 Nginx、MySQL）又没有权限读取。",[175,594,595,601],{},[178,596,597,600],{},[181,598,599],{},"典型现象："," Permission denied，或容器启动后报错退出了。",[178,602,603,606,614,648],{},[181,604,605],{},"解决方案：",[175,607,608],{},[178,609,610,613],{},[181,611,612],{},"指定用户 ID（推荐）："," 启动容器时，用 --user 参数强制指定与宿主机当前用户相同的 UID 和 GID",[234,615,617],{"className":236,"code":616,"language":238,"meta":239,"style":239},"# 假设宿主机用户 UID=1000, GID=1000\ndocker run --user 1000:1000 -v \u002Fhost\u002Fdata:\u002Fapp\u002Fdata my_image\n",[241,618,619,625],{"__ignoreMap":239},[244,620,621],{"class":246,"line":247},[244,622,624],{"class":623},"sHwdD","# 假设宿主机用户 UID=1000, GID=1000\n",[244,626,627,630,633,636,639,642,645],{"class":246,"line":264},[244,628,629],{"class":250},"docker",[244,631,632],{"class":254}," run",[244,634,635],{"class":254}," --user",[244,637,638],{"class":254}," 1000:1000",[244,640,641],{"class":254}," -v",[244,643,644],{"class":254}," \u002Fhost\u002Fdata:\u002Fapp\u002Fdata",[244,646,647],{"class":254}," my_image\n",[175,649,650,656],{},[178,651,652,655],{},[181,653,654],{},"容器内调整："," 在 Dockerfile 中创建与宿主机相同 UID 的用户，并在 chown 挂载目录。",[178,657,658,661],{},[181,659,660],{},"简单粗暴（仅限开发）："," 直接 chmod -R 777 \u002Fhost\u002Fdata，但生产环境绝对不要用。",[413,663,664,667],{},[181,665,666],{},"注意："," 使用id + 用户名可以查询用户名的UID和GID。",[234,669,671],{"className":236,"code":670,"language":238,"meta":239,"style":239},"id anyfork\n",[241,672,673],{"__ignoreMap":239},[244,674,675,678],{"class":246,"line":247},[244,676,677],{"class":250},"id",[244,679,680],{"class":254}," anyfork\n",[413,682,683],{},"输出结果如下：",[234,685,690],{"className":686,"code":688,"language":689,"meta":239},[687],"language-text","uid=1000(anyfork) gid=1000(anyfork) 组=1000(anyfork),10(wheel),995(docker)\n","text",[241,691,688],{"__ignoreMap":239},[586,693,695,698],{"icon":588,"label":694},"容器内非空目录被'隐藏'?",[413,696,697],{},"当你把一个空的宿主机目录（Bind Mount）挂载到一个非空的容器目录时，容器目录里的原有文件会被“遮住”。容器看起来是空的，导致程序因找不到默认配置文件而启动失败。",[175,699,700,705],{},[178,701,702,704],{},[181,703,599],{}," 镜像里明明有 \u002Fapp\u002Fconf\u002Fconfig.yaml，挂载后程序报错“配置文件不存在”。",[178,706,707,709,714,743],{},[181,708,605],{},[175,710,711],{},[178,712,713],{},"方案一：先启动一个临时容器，把默认文件拷贝到宿主机目录，再正式挂载。",[234,715,717],{"className":236,"code":716,"language":238,"meta":239,"style":239},"docker run --rm my_image cat \u002Fapp\u002Fconf\u002Fconfig.yaml > .\u002Fhost\u002Fconfig.yaml\n",[241,718,719],{"__ignoreMap":239},[244,720,721,723,725,728,731,734,737,740],{"class":246,"line":247},[244,722,629],{"class":250},[244,724,632],{"class":254},[244,726,727],{"class":254}," --rm",[244,729,730],{"class":254}," my_image",[244,732,733],{"class":254}," cat",[244,735,736],{"class":254}," \u002Fapp\u002Fconf\u002Fconfig.yaml",[244,738,739],{"class":303}," >",[244,741,742],{"class":254}," .\u002Fhost\u002Fconfig.yaml\n",[175,744,745],{},[178,746,747],{},"方案二：在 Dockerfile 中把配置文件路径和动态数据路径分开（例如 \u002Fapp\u002Fconfig 放固定配置，\u002Fapp\u002Fdata 放持久化数据），只挂载数据目录。",[586,749,751,754],{"icon":588,"label":750},"并发读写导致数据损坏(尤其数据库)",[413,752,753],{},"多个容器同时挂载同一个卷或宿主机目录进行写入，会导致数据错乱或文件锁冲突，数据库文件（如 SQLite、MySQL 的表空间）极易损坏。",[175,755,756,759],{},[178,757,758],{},"典型现象：database is locked（SQLite）或 MySQL 启动失败，日志显示 corrupted。",[178,760,761,762,767,791],{},"解决方案：\n",[175,763,764],{},[178,765,766],{},"严格遵守 1 对 1 写入原则：一个数据卷\u002F目录，只允许一个容器拥有写入权限，其他容器仅以只读（ro）方式挂载。",[234,768,770],{"className":236,"code":769,"language":238,"meta":239,"style":239},"# :ro只读挂载\ndocker run -v my_data:\u002Fdata:ro my_reader\n",[241,771,772,777],{"__ignoreMap":239},[244,773,774],{"class":246,"line":247},[244,775,776],{"class":623},"# :ro只读挂载\n",[244,778,779,781,783,785,788],{"class":246,"line":264},[244,780,629],{"class":250},[244,782,632],{"class":254},[244,784,641],{"class":254},[244,786,787],{"class":254}," my_data:\u002Fdata:ro",[244,789,790],{"class":254}," my_reader\n",[175,792,793],{},[178,794,795],{},"使用网络数据库（如独立部署的 MySQL\u002FPostgreSQL），通过 TCP 协议访问，而非直接挂载数据文件。",[586,797,799],{"icon":588,"label":798},"匿名卷和 Dockerfile 中的 VOLUME 陷阱",[175,800,801,807,813],{},[178,802,803,806],{},[181,804,805],{},"匿名卷（Anonymous Volumes）："," 当你用 docker run -v \u002Fsome\u002Fpath（不指定名字）时，Docker 会创建一个哈希值命名的卷。容器删除时，如果没加 -v 参数，这些卷会遗留在宿主机上，长此以往占用大量磁盘空间。",[178,808,809,812],{},[181,810,811],{},"Dockerfile 里的 VOLUME 指令："," 如果在 Dockerfile 中写了 VOLUME \u002Fdata，后续在 Dockerfile 中执行 RUN 或 COPY 试图往 \u002Fdata 写文件，会失败（因为该目录已被标记为挂载点）。此外，这种声明会强制产生匿名卷，后续维护不方便。",[178,814,815,817],{},[181,816,605],{},[175,818,819,825,831],{},[178,820,821,824],{},[181,822,823],{},"务必使用命名卷："," docker volume create my_named_volume 或 docker run -v my_named_volume:\u002Fdata。",[178,826,827,830],{},[181,828,829],{},"清理垃圾卷："," 定期执行 docker volume prune 删除未使用的卷。",[178,832,833,836],{},[181,834,835],{},"尽量避免在 Dockerfile 中写"," VOLUME，将挂载行为完全交给启动命令 docker run 或 docker-compose.yml 控制，更加灵活。",[586,838,840,843],{"icon":588,"label":839},"误删容器导致数据丢失(混淆了可写层与卷)",[413,841,842],{},"有人以为容器里的所有文件都在卷里，直接用 docker rm -f container 删了容器，结果发现数据没了。这是因为数据根本没放进卷里，而是写在了容器的“可写层”。",[175,844,845,850],{},[178,846,847,849],{},[181,848,599],{}," 运行容器并写入数据，未执行 -v 挂载，删除容器后数据消失。",[178,851,852,854],{},[181,853,605],{},[175,855,856,862],{},[178,857,858,861],{},[181,859,860],{},"黄金法则："," 任何需要持久化的数据，必须显式地用 -v 或 --mount 挂载出来。 不要依赖容器内部的可写层。",[178,863,864,867],{},[181,865,866],{},"建立启动模板（docker-compose.yml）","，确保每个有状态的服务（数据库、存储中间件）都配置了 volumes 字段。",[586,869,871,874,882,968,982,990,1050,1055,1058,1063,1078,1083,1094,1099,1110,1115,1120,1127],{"icon":588,"label":870},"标准数据卷备份迁移方案",[413,872,873],{},"数据卷备份可通过临时容器挂载源卷和宿主机备份目录，直接打包数据实现快速迁移。利用 --rm 临时容器，同时挂载“源数据卷”和“宿主机备份目录”，然后执行 tar 打包。这个方法不仅不污染宿主机环境，而且能保证文件权限（UID\u002FGID）被完整保留，非常适合跨机器迁移。",[226,875,876],{},[178,877,878,881],{},[181,879,880],{},"备份数据卷（打包）："," 假设你要备份名为 my_app_data 的卷：",[234,883,885],{"className":236,"code":884,"language":238,"meta":239,"style":239},"docker run --rm \\\n  -v my_app_data:\u002Fsource \\          # 挂载源数据卷到容器的 \u002Fsource\n  -v \u002Fhost\u002Fbackup:\u002Fbackup \\         # 挂载宿主机备份目录到容器的 \u002Fbackup\n  alpine \\\n  tar czf \u002Fbackup\u002Fmy_data_$(date +%Y%m%d).tar.gz -C \u002Fsource .\n",[241,886,887,899,913,926,933],{"__ignoreMap":239},[244,888,889,891,893,895],{"class":246,"line":247},[244,890,629],{"class":250},[244,892,632],{"class":254},[244,894,727],{"class":254},[244,896,898],{"class":897},"sTEyZ"," \\\n",[244,900,901,904,907,910],{"class":246,"line":264},[244,902,903],{"class":254},"  -v",[244,905,906],{"class":254}," my_app_data:\u002Fsource",[244,908,909],{"class":897}," \\          ",[244,911,912],{"class":623},"# 挂载源数据卷到容器的 \u002Fsource\n",[244,914,915,917,920,923],{"class":246,"line":287},[244,916,903],{"class":250},[244,918,919],{"class":254}," \u002Fhost\u002Fbackup:\u002Fbackup",[244,921,922],{"class":897}," \\         ",[244,924,925],{"class":623},"# 挂载宿主机备份目录到容器的 \u002Fbackup\n",[244,927,928,931],{"class":246,"line":330},[244,929,930],{"class":250},"  alpine",[244,932,898],{"class":897},[244,934,935,938,941,944,947,950,953,956,959,962,965],{"class":246,"line":360},[244,936,937],{"class":254},"  tar",[244,939,940],{"class":254}," czf",[244,942,943],{"class":254}," \u002Fbackup\u002Fmy_data_",[244,945,946],{"class":303},"$(",[244,948,949],{"class":250},"date",[244,951,952],{"class":254}," +%Y%m%d",[244,954,955],{"class":303},")",[244,957,958],{"class":254},".tar.gz",[244,960,961],{"class":254}," -C",[244,963,964],{"class":254}," \u002Fsource",[244,966,967],{"class":254}," .\n",[175,969,970,976],{},[178,971,972,975],{},[181,973,974],{},"--rm："," 容器打包完成后自动删除，不留痕迹。",[178,977,978,981],{},[181,979,980],{},"-C \u002Fsource .："," 表示切换到 \u002Fsource 目录，再打包“当前目录（.）”下的所有内容。这样解压时不会多出一层 \u002Fsource 文件夹。",[226,983,984],{"start":264},[178,985,986,989],{},[181,987,988],{},"恢复数据卷（迁移）",": 将备份包恢复到另一个卷（或同一个卷）中：",[234,991,993],{"className":236,"code":992,"language":238,"meta":239,"style":239},"docker run --rm \\\n  -v my_new_app_data:\u002Ftarget \\      # 挂载目标数据卷到容器的 \u002Ftarget\n  -v \u002Fhost\u002Fbackup:\u002Fbackup \\         # 挂载存放备份包的宿主机目录\n  alpine \\\n  tar xzf \u002Fbackup\u002Fmy_data_20260708.tar.gz -C \u002Ftarget\n",[241,994,995,1005,1018,1029,1035],{"__ignoreMap":239},[244,996,997,999,1001,1003],{"class":246,"line":247},[244,998,629],{"class":250},[244,1000,632],{"class":254},[244,1002,727],{"class":254},[244,1004,898],{"class":897},[244,1006,1007,1009,1012,1015],{"class":246,"line":264},[244,1008,903],{"class":254},[244,1010,1011],{"class":254}," my_new_app_data:\u002Ftarget",[244,1013,1014],{"class":897}," \\      ",[244,1016,1017],{"class":623},"# 挂载目标数据卷到容器的 \u002Ftarget\n",[244,1019,1020,1022,1024,1026],{"class":246,"line":287},[244,1021,903],{"class":250},[244,1023,919],{"class":254},[244,1025,922],{"class":897},[244,1027,1028],{"class":623},"# 挂载存放备份包的宿主机目录\n",[244,1030,1031,1033],{"class":246,"line":330},[244,1032,930],{"class":250},[244,1034,898],{"class":897},[244,1036,1037,1039,1042,1045,1047],{"class":246,"line":360},[244,1038,937],{"class":254},[244,1040,1041],{"class":254}," xzf",[244,1043,1044],{"class":254}," \u002Fbackup\u002Fmy_data_20260708.tar.gz",[244,1046,961],{"class":254},[244,1048,1049],{"class":254}," \u002Ftarget\n",[175,1051,1052],{},[178,1053,1054],{},"tar xzf 会自动解压并把所有文件释放到 \u002Ftarget 目录下。",[413,1056,1057],{},"虽然这个方案很经典，但有几个极易出错的细节需要特别注意：",[226,1059,1060],{},[178,1061,1062],{},"数据库一致性（最重要）：",[175,1064,1065,1068],{},[178,1066,1067],{},"如果 my_app_data 是 MySQL、Postgres 等正在运行的数据库数据目录，直接打包很可能导致备份文件损坏，因为数据库还在写内存缓冲。",[178,1069,1070,1073,1074],{},[181,1071,1072],{},"正确做法："," 备份前先暂停容器写入（docker pause ",[1075,1076,1077],"container",{},"），或者先执行 docker exec 在容器内跑 mysqldump 导出逻辑备份，而不是直接打包物理文件。如果是纯静态文件（如图片、配置），则无所谓。",[226,1079,1080],{"start":264},[178,1081,1082],{},"压缩效率与资源消耗：",[175,1084,1085,1088],{},[178,1086,1087],{},"如果数据量极大（几十 GB），使用 alpine 默认的 tar 压缩会很慢且 CPU 飙升。",[178,1089,1090,1093],{},[181,1091,1092],{},"优化："," 可以在 tar 中去掉压缩（不加 z），只打包成 .tar，然后在宿主机上用 pigz（多线程 gzip）并行压缩，速度能快几倍。",[226,1095,1096],{"start":287},[178,1097,1098],{},"文件权限（UID\u002FGID）",[175,1100,1101,1104],{},[178,1102,1103],{},"tar 默认会保留权限。如果目标机器上容器的运行用户不同（例如 A 机器 UID=1000，B 机器 UID=1001），解压后容器可能无法读写文件。",[178,1105,1106,1109],{},[181,1107,1108],{},"对策："," 解压时可用 --owner=1000 --group=1000 参数强制修改所有者，或者在 docker run 启动目标容器时用 --user 参数来适配。",[226,1111,1112],{"start":330},[178,1113,1114],{},"增量备份怎么办？",[175,1116,1117],{},[178,1118,1119],{},"这种 tar 打包是全量备份。如果每天都要备份且数据量很大，建议改用 rsync 命令（同样挂载临时容器），只同步变更的文件，而不是每次全量打包。",[226,1121,1122],{"start":287},[178,1123,1124],{},[181,1125,1126],{},"更现代的替代方案（Docker 官方插件）",[413,1128,1129],{},"如果你的 Docker 版本较新（20.10+）且数据卷需要频繁备份到远端，可以考虑使用 Docker Volume Plugins（如 Rclone 或 阿里云 OSS Volume 插件），它们支持直接将卷数据增量同步到云存储，不需要手动在宿主机保留 .tar 文件。",[1131,1132,1133],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":239,"searchDepth":287,"depth":287,"links":1135},[1136,1141,1142],{"id":167,"depth":264,"text":168,"children":1137},[1138,1139,1140],{"id":172,"depth":287,"text":173},{"id":421,"depth":287,"text":422},{"id":453,"depth":287,"text":454},{"id":479,"depth":264,"text":480},{"id":579,"depth":264,"text":580},"2026-07-08 16:06:00","md",{},{"title":118},16.5,{"title":160,"description":121},{"loc":119},2973,"owAZBNh9XJ9Bv0BZ3mpCJimCKWh4qGMxzt-pTs-uCms",{"data":1153,"body":1154},{},{"type":1155,"children":1156},"root",[1157],{"type":1158,"tag":413,"props":1159,"children":1160},"element",{},[1161],{"type":689,"value":121},1787625812668]