多栈 Docker 环境:数据迁移后的卷盘点与安全清理流程

背景 在多 Compose 栈并存的环境里,迁移完成后常见现象是: 目标是:先盘点,再确认引用关系,最后最小化删除,避免误删关键数据。 核心原则 1) 先确认“新数据落点”是否稳定 迁移成功的强信号通常是:关键服务容器的持久化目录全部使用 bind mount,且映射到统一的宿主机工作目录结构。 2) named volume 的风险等级更高 在多栈环境里,named volume 很可能承载关键数据,例如: 结论:只有当卷确认“零引用”且业务已稳定使用 bind mount 时,才进入删除阶段。 盘点步骤 Step A:列出所有卷(建立资产清单) 只列出“无引用候选”(只做名单,不删除): Step B:列出所有容器(包含已停止) 目的: Step C:从容器侧确认挂载类型(关键确认) 对“目标业务容器”查看挂载: 关注点: 若目标业务容器已完全为 bind mount,说明迁移落点基本完成。 识别“迁移搬运容器” 迁移时常用方式是: 这类容器特征: 验证方式:列出某个卷当前被哪些容器引用(最重要的安全检查): 安全清理顺序 ① 先删“迁移搬运容器” 前提:确认这些容器不再需要、并且目标业务已稳定运行。 ② 再做“卷引用归零”验证 对每个旧卷执行: 判定规则: ③ 最后删除确认无引用的旧卷 …

OnlyOffice Docker 栈内统一与匿名卷迁移记录

背景 在使用 onlyoffice/documentserver 单容器部署 OnlyOffice 的过程中,虽然已经将常见目录(logs / data / cache / public / fonts)通过 bind mount 挂载到了用户目录,但在实际检查容器挂载点时,仍然发现 /var/lib/docker/volumes/ 下存在多组随机字符串命名的 volume。 这类 volume 并非人为创建,而是镜像在 Dockerfile 中通过 VOLUME 指令声明后,由 Docker 自动生成的匿名卷。如果不显式覆盖挂载,这些卷会默默承载真实数据,从而破坏“栈内统一、目录可迁移”的目标。 本文记录一次完整的排查与修正过程,将 OnlyOffice 的 全部持久化数据 统一收敛到单一项目目录下,并清理匿名卷依赖。 问题确认:匿名卷从何而来 通过以下命令检查容器挂载点: 可以观察到: 这些匿名卷对应的典型路径包括: 结论:OnlyOffice 内部依赖的 PostgreSQL / RabbitMQ / Redis 等组件,其数据仍然落在 Docker 管理区,而非项目目录。 目标 现有目录结构(示例) …

Docker 中应用栈数据卷重构与迁移记录

一、问题背景(抽象化) 在一台长期运行的 Linux 主机上,使用 Docker Compose 部署了多个应用栈。随着时间推移,出现了以下典型问题: 目标不是改变 Docker 的存储机制,而是: 在不破坏 Docker 设计的前提下,让数据卷“可读、可控、可迁移”。 二、总体设计原则(通用) 1. 不强行迁移 Docker 根目录 2. 明确区分三类数据 类型 处理方式 Compose / 配置文件 保留在用户可读目录 运行期业务数据 使用 命名 volume 临时 / 缓存数据 允许 Docker 自动管理 3. 通过“命名 volume”解决可读性问题 三、问题定位方法(通用步骤) 1. 列出系统中所有 volume 2. 反向确认 volume 与容器的挂载关系 通过该方式,可以明确: 四、数据迁移的通用流程 步骤 …

Docker 中拆分 Ollama 与 Open-WebUI 为独立栈的实践记录

背景 在单机环境中使用 Docker Compose 同时运行 Ollama(推理核心)与 Open-WebUI(交互界面)是一个常见做法。初期将两者放在同一个 compose 文件中管理较为方便,但随着系统逐步演进,这种方式会带来以下问题: 因此,本次实践的目标是: 在不丢失任何既有数据的前提下,将 Ollama 与 Open-WebUI 拆分为两个独立的 Docker Compose 栈,并通过一个核心网络进行连接。 设计原则 原始状态分析 在最初的一体化部署中,Docker 会自动为服务创建数据卷。拆分为多个栈后,如果未显式指定旧数据卷名称,Docker 会为新栈创建新的空卷,从而导致“历史数据消失”的错觉。 需要明确的是: 数据通常并未被删除,而是新容器未挂载到正确的旧数据卷。 因此,拆分过程中最关键的工作并不是服务启动顺序,而是数据卷的精确绑定。 拆分思路概述 整体架构被拆分为两类栈: 所有服务间通信均通过 Docker 内部网络完成,核心服务不直接对外暴露。 核心栈配置要点(示意) 核心栈的职责包括: 配置示意(省略非关键字段): 外围栈配置要点(示意) 外围栈的职责包括: 配置示意(省略非关键字段): 常见问题与判断方法 为什么拆分后看起来“数据没了”? 如何判断旧数据卷是否仍然存在? 可通过 Docker 的卷列表与磁盘占用信息进行判断。通常: 最终状态 在完成拆分与修正后,系统应达到以下状态: 总结 本次实践的核心经验可以归纳为一句话: Docker 不会替用户判断应当使用哪个历史数据卷,所有关键数据都必须显式绑定。 通过明确: …

Docker 中未被任何容器使用的卷识别与清理实录

背景 在长期运行的 Docker 环境中,随着容器的反复创建、删除与重建,系统中可能残留未被任何容器使用的卷(volume)。这些卷如果不加区分地清理,存在误删有效数据的风险;如果完全不清理,又会造成系统状态不透明。 本文记录一次严格、可验证的 Docker 卷清理过程,目标是: 一、初步筛选:dangling volume Docker 提供了对“悬空卷(dangling volume)”的定义: 未被任何容器引用的卷 使用以下命令列出: 输出结果为两个卷 ID: 该步骤只能说明: 这些卷当前没有被容器引用 但仍需要进一步验证它们的空间占用与引用状态。 二、空间与引用状态交叉验证 通过 docker system df -v 查看本地卷的使用情况,并重点关注两项信息: 关键结果如下(节选): 可以确认: 三、判断结论 综合两次验证结果: 卷 ID LINKS SIZE 状态 14ab1c74859f… 0 7.7 kB 未被使用 ebd2fa33478… 0 90 kB 未被使用 结论: 四、安全删除方式 明确目标卷后,采用精确删除而非批量清理: 该方式的优点是: 五、实践原则总结 …

Docker 镜像与容器清理实践:以“栈视角”进行安全管理

背景 在长期运行的 Docker 服务器环境中,常见两类资产同时存在: 如果仅依据“容器是否在运行”来判断镜像是否可删除,极易误删冷备镜像,给后续恢复带来额外成本。因此,有必要建立一套以服务栈(stack)为核心的镜像与容器管理方法。 本文记录一次完整的排查与整理过程,目标不是“尽可能删除”,而是确保不误删任何未来仍需使用的镜像。 一、基础原则 在该环境中采用如下原则: 二、列出所有镜像(事实基线) 使用带 digest 的镜像列表作为“真实库存”: 该输出用于确认: 三、列出所有容器(不区分运行/停止) 容器列表是“镜像引用关系”的唯一事实来源: 在该环境中,当前不存在 exited 容器,所有服务均处于运行状态。但这并不改变后续判断逻辑。 四、用“栈(Compose Project)视角”理解容器结构 相比单个容器视角,“栈”更符合真实服务结构。通过 com.docker.compose.project 标签,可将容器按栈分组。 栈视图生成命令 五、当前运行中的服务栈清单(严格脱敏) 为避免暴露具体应用指纹,本节仅以抽象栈类别记录容器结构与数量,不出现任何可被直接识别的服务名称、镜像名或项目名。 该视图仅用于说明“以栈为单位进行管理”的方法论,而非展示具体部署细节。

Portainer 中删除 Docker 镜像失败的原因分析与处理

问题现象 在使用 Portainer(Community Edition)管理 Docker 环境时,尝试删除标记为 Unused 的镜像失败,界面提示类似以下错误: 即使所选镜像体积较大、状态显示为未使用,删除操作仍无法在 UI 中完成。 初步误判与澄清 误判一:镜像体积过大导致无法删除 该判断并不成立。 Docker 删除镜像的核心操作并非“拷贝或移动大文件”,而是: 镜像体积大小只影响删除耗时,不会导致“无法删除”。 正确原因分析 1. Portainer 报错的本质 504 Gateway Time-out 属于 前端或反向代理超时,而非 Docker Engine 返回的逻辑错误。 Portainer 的工作路径为: 当后端操作耗时过长时: 2. 根本原因:磁盘 I/O 性能不足 在以下场景中,该问题尤为明显: Docker 删除镜像时会触发: 这类操作 高度依赖随机 I/O 性能,在慢盘上可能持续数分钟。 3. 为什么 CLI 删除可以成功 使用命令行执行: 与 Portainer …

使用 Docker 自建 RustDesk 服务器:host 网络模式下的完整实践

摘要 本文记录了一次 RustDesk 自托管服务器 的完整部署实践,采用 Docker + host 网络模式,并结合 宿主机防火墙 与 边界路由器端口转发,在家庭网络环境中实现了稳定可用的远程桌面服务。 出于安全考虑,本文 对所有本地路径、端口号与网络细节进行抽象处理,仅保留架构思路、配置原则与验证方法,确保内容可理解、可复现,但不暴露任何可被直接利用的信息。 一、整体架构思路 RustDesk 官方在 Docker 场景中推荐使用 host 网络模式运行服务,其核心特征包括: 因此,网络安全边界主要由两部分构成: 二、Docker 服务的统一目录管理(路径脱敏) 所有 Docker 服务均集中放置在一个 统一的服务根目录 下,每个应用使用独立子目录管理自身配置与数据,例如: 这种结构的优点在于: 三、Docker Compose 配置(原则级描述) RustDesk 由两个核心组件组成: 二者均以 Docker 容器形式运行,并共享数据目录。 配置原则如下: 示意结构(非可直接复制配置): 上述示例为结构示意,刻意省略所有具体参数与数值。 四、端口设计原则(不公开端口号) RustDesk 服务需要若干 固定职责端口,但在公开文档中应避免直接暴露数值。 其功能层级可抽象为: 核心通信端口 可选功能端口 在实际部署中应遵循: 五、宿主机防火墙配置原则(以 …

Docker Compose 中 stop、start 与系统重启行为的正确理解

在使用 Docker Compose 管理服务器服务时,一个非常常见但经常被误解的问题是: 执行 docker compose stop 之后,如果系统重启,这些容器会不会自动启动? 这个问题如果理解错误,可能导致服务器重启后关键服务无法恢复,或者相反,一些本应“冷静”的服务又被意外启动。 本文对 Docker Compose 的停止机制与自动重启行为进行系统性说明。 1. docker compose stop 的真实含义 执行: 其行为是: 这是一个逻辑层面的“人为停止”,不是故障,不是崩溃,而是一个明确的运维决策。 2. 系统重启后是否会自动启动? 答案是:不会。 如果某个容器曾被 docker compose stop 停止,那么: 都不会自动将其启动。 该容器会保持 exited 状态,直到手动执行: 3. restart: unless-stopped 为什么不起作用? 很多人会在 docker-compose.yml 中写: 并以为这样就一定会自动启动。 但实际上: unless-stopped 的语义是“只要不是被人为停止,就在 Docker 启动时自动恢复” 而 docker compose …

Docker 容器环境中的镜像与卷清理指南(基于 Ubuntu)

在日常使用 Docker 的过程中,随着容器的频繁构建与销毁,系统中往往会积累大量未使用的镜像和卷,占据宝贵的磁盘空间。为了保持系统的整洁和高效运行,有必要定期清理这些无用资源。文章基于一次实际操作,系统地介绍了如何安全识别并清理未使用的 Docker 镜像与卷。 一、环境信息 二、目标 只清理未被任何容器使用的镜像和卷,避免影响正在运行的服务或数据。 三、查看资源使用情况 使用以下命令快速掌握当前 Docker 系统资源分布: 示例输出: 该输出显示存在大量未使用镜像和卷,具有明显的清理空间。 四、查找未使用的镜像和卷 1. 未使用镜像 利用命令查看未被任何容器使用的镜像: 或查看可以清除的镜像: 基于实际运行,当前环境中存在与容器无关的镜像,总共见于 11 GB左右,经确认后可以使用以下命令完成清除: 此操作将删除全部未被容器使用的镜像,安全无影响环境中正在运行的服务。 2. 未使用卷 查看未被挂载的 volume: 执行清理: 五、清理结果对比与核查 执行清理命令后,再次查看资源状态: 清理前: 清理后: 选中的卷由 Docker 输出为 “dangling=true”,但部分卷属于 Docker Stack 管理或被隐性引用,因此被留下。 六、构建缓存清理(可选) Docker 构建过程会产生中间层缓存,占用磁盘。如果不再重构已有镜像,可安全删除: 七、安全注意事项 八、最终系统状态示例 九、总结 通过定期清理 Docker 环境中未使用的镜像和卷,可以释放大量磁盘空间,保持系统高效运行。建议将以下命令放入脚本,配合 cron 实现定期维护: …