5.2 存储驱动与卷管理差异


5.2 存储驱动与卷管理差异

本节摘要:Podman 与 Docker 的存储差异集中在三处:镜像存储位置(用户家目录 与 系统目录的两套世界)、卷的物理形态(命名卷按用户隔离)、绑定挂载的权限语义(SELinux 标签与 UID 映射双重检查)。本节用三个故障案例把机制讲透,最后给出卷与绑定挂载的选型决策表。

三个真实故障开场

迁移用户最常提交的三类工单,先给现象再给答案:

故障一:挂载了配置文件,容器读得到但写不进,报 Permission denied——SELinux 标签问题。故障二:数据库容器把 bind mount 当数据目录,容器内进程(以非 root 用户运行)建不了文件——UID 映射错位。故障三:Docker 迁过来的脚本找不到镜像——两套存储目录互不相通(第 2.1 节与第 3.2 节讲过机制,这里是症状)。前两个是本节主角。

坑一:SELinux 标签,一个冒号的事

在启用了 SELinux 的发行版(RHEL、Fedora 系)上,bind mount 默认会被容器拒绝写入——不是权限位的问题,是标签的问题:宿主文件带着用户主目录的 SELinux 标签,容器进程带着容器专用标签(第 6.1 节展开机制),策略默认禁止后者读写前者。Podman 与 Docker 在此的分野在于:Podman 把解决方案做进了挂载语法:

# 用 :Z 重贴标签(私有挂载,该目录只给这个容器用) podman run --rm -v ./data:/data:Z alpine:3.19 \ sh -c 'echo ok > /data/a.txt && cat /data/a.txt' # ok # 用 :z 重贴共享标签(多个容器共享同目录时用) podman run --rm -v ./shared:/shared:z app-a:latest ... podman run --rm -v ./shared:/shared:z app-b:latest ...

一个冒号的大小写,语义差别值得背下来:大写 Z 重贴为"单容器专用"标签(container_file_t 加独有类别),小写 z 重贴为"共享"标签(container_file_t 通用类别)。选错方向各有代价:该用 Z 的场景用了 z,等于把目录开放给所有容器进程读写,隔离强度下降;该用 z 的场景用了 Z,第二个容器读不了。默认建议:私有优先,确需共享再降级到 z。另外注意:重贴标签修改的是宿主文件属性——对系统目录别乱用(把 /usr 贴成容器标签会让系统服务读不了它),仅对数据目录使用。

坑二:UID 映射错位,用第 2.2 节的工具解决

第 2.2 节建立的映射认知直接套用。典型场景:官方 PostgreSQL 镜像里的进程以容器内 UID 999(postgres 用户)运行,bind mount 的宿主目录属主是你(UID 1000)。容器内 999 落到宿主机是 100998,与 1000 无交集——写失败。三种修法按推荐顺序:

# 修法一(最常用):chown 到映射后的属主,再挂载 mkdir -p ~/pgdata podman unshare chown 999:999 ~/pgdata # unshare 内的 999 = 宿主机视角的 100998?不—— # unshare 内视角下 999 就是容器视角的 999, # 映射到宿主机是你的身份所在区间,此操作让容器内 # postgres 用户恰好拥有该目录 # 修法二:keep-id(容器内直接使用你的宿主 UID) podman run --userns=keep-id --rm -v ~/pgdata:/var/lib/postgresql/data:Z \ postgres:16-alpine ... # 适用:镜像允许以任意 UID 运行(镜像里有 entrypoint 适配) # 修法三:命名卷(让 Podman 管理属主) podman volume create pgdata podman run -v pgdata:/var/lib/postgresql/data:Z postgres:16-alpine ... # 命名卷由 Podman 初始化属主,官方镜配镜像的 volume 声明 # 时容器会按镜像要求自动 chown——最省心也最推荐

命名卷 与 绑定挂载:决策表

两种数据放置方式的对位比较,落在选型上:

维度 命名卷 绑定挂载
物理位置 用户级卷目录(rootless 在家目录下) 宿主任意路径
属主初始化 Podman 可按镜像声明自动处理 你自己负责
SELinux 挂载时默认处理妥当 需要显式 :Z 或 :z
备份可见性 需通过 podman volume 定位路径 路径直观,rsync 直用
适合 数据库、应用状态等"容器私有数据" 配置文件、源码、共享目录
与 Docker 语义 基本对齐(位置不同) 对齐(但权限检查更严)

经验法则一句话:数据归容器管的用卷,数据归你管的用 bind mount。数据库的数据文件属于前者(你不该直接翻它内部结构),nginx 的配置文件属于后者(你要用编辑器直接改它)。

05-02-fig01

一个完整案例:数据库容器的数据迁移

把坑串成流程。背景:把一个 Docker 上跑了两年的 PostgreSQL 容器迁到 rootless Podman,数据必须无损。步骤:先用 Docker 侧工具导出或直接停库拷贝 bind mount 目录;目标侧 podman volume create pgdata 建卷;podman unshare chown 999:999 卷路径 或依赖镜像的自动初始化;启动时挂载 pgdata:/var/lib/postgresql/data:Z;恢复数据后用 pg 内置工具校验。结果:容器内 postgres 用户读写正常,数据完整。解读:整个流程的关键动作只有两步——把"数据的所有权"从宿主机用户视角翻译到容器视角(chown),把"数据的访问权"从用户域开放给容器域(:Z)。变式:MySQL、Redis 等官方镜像同理,先查镜像内进程的 UID,再走同一套翻译动作。

本节要点回顾

  • 两套存储世界:rootless 与 rootful、Podman 与 Docker,目录互不相通,跨世界用归档过桥
  • :Z 与 :z 语义有大小写之分:私有与共享,默认用大写
  • UID 错位三修法:unshare chown、keep-id、命名卷,按场景选
  • 卷与 bind mount 的分界:数据归容器管用卷,归你管用挂载
  • 排障顺序:先 SELinux(看审计日志)再属主(看 unshare 输出)

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U