适用:使用 systemd 的 Linux 服务器、常规 rootful Docker Engine;Compose 示例使用
docker composeV2。本文为教学故障场景,错误文本和结果为示意,具体输出以现场为准。
一、遭遇情况:宿主机能写,容器却创建不了文件
应用把日志写到 /data/app.log 时出现 Permission denied。宿主机上运维账号在对应目录执行 touch 却成功。有人准备递归 chmod 777,或者让整个容器改为 root 运行。
本篇案例设定为容器应用用 UID 10001 运行,挂载目录却只允许宿主机目录拥有者写入。宿主机运维账号有权限,不代表容器进程的数字身份也有权限。
二、原因分析:挂载不会自动把权限变得兼容
常规 rootful、未启用用户命名空间重映射的场景下,文件系统按数字 UID/GID 判断身份。容器内用户名与宿主机用户名相同,不保证编号相同;名字不同而编号相同,也可能访问相同文件权限。
此外,挂载可能是只读,或者被 SELinux 等策略拒绝。把这些原因都称为“权限问题”,很容易用错误方法扩大权限。

三、排查思路:先确认进程,再确认它写到哪里
假设容器名为 app:
docker inspect app --format 'ConfiguredUser={{.Config.User}}'
docker inspect app --format '{{json .Mounts}}'
docker exec app id
docker exec app ls -ldn /data
ConfiguredUser 为空通常表示没有显式设置镜像级用户,但入口脚本仍可能切换应用身份。docker exec app id 也不保证与经过降权的业务进程完全相同,需要结合镜像说明、应用启动方式或进程信息确认。
在 Mounts 里核对 Source、Destination 和 RW。若 RW=false 或应用报 Read-only file system,先确认该路径本来是否应该可写;不要通过改宿主机权限来修复只读挂载。
宿主机检查目标目录和父目录时,可使用 ls -ldn,有 namei 的系统也可用 namei -l /实际路径。目录执行权限影响路径穿越,不能只看最终文件的写权限。
四、实践:用数字身份复现并修正
以下只创建一个独立临时目录,使用常规 rootful Docker。rootless 或 userns-remap 环境的 UID 映射不同,不直接套用数字归属修复。
lab_dir=$(mktemp -d /tmp/dt12.XXXXXX)
ls -ldn "$lab_dir"
docker run --rm --user 10001:10001 --mount "type=bind,src=$lab_dir,dst=/data" alpine:3.21 sh -c 'id; touch /data/probe'
mktemp -d 常创建只允许当前拥有者访问的目录,容器里的 10001 通常无权写,预期出现拒绝。若宿主机当前账号本来就是 UID 10001,应换一个已确认不同的实验 UID,不能机械期待失败。
确认这是新建实验目录后,仅修改该目录的归属和权限:
sudo chown 10001:10001 "$lab_dir"
sudo chmod 0750 "$lab_dir"
docker run --rm --user 10001:10001 --mount "type=bind,src=$lab_dir,dst=/data" alpine:3.21 sh -c 'touch /data/probe; ls -ln /data/probe'
在未被其他策略阻止时,应成功创建文件。生产修复则要根据真实应用 UID、共享组和目录用途选择,不能对整个数据库数据目录盲目递归改属主。
五、SELinux 系统还要看标签
如果数字权限正确、挂载可写,但仍然拒绝,在启用 SELinux 的发行版上检查:
getenforce
ls -Zd /实际业务专用目录
sudo ausearch -m AVC -ts recent
ausearch 需要相应审计工具及权限。AVC 记录与访问时间、目标路径对上,才形成标签相关证据;不能只因为系统是某个发行版就一律归因 SELinux。
对专门用于本实验的目录,可以按 Docker 文档使用私有标签挂载:
docker run --rm --user 10001:10001 -v "${lab_dir}:/data:Z" alpine:3.21 sh -c 'touch /data/probe-selinux; ls -ln /data/probe-selinux'
:Z 用于私有、不共享的标签,:z 用于适当的共享场景。重标记会影响宿主机目录,不要把系统目录或多人共用目录随意加上 :Z。这些写法针对普通容器挂载,不能直接推定 Swarm service 有相同支持。
六、验证与总结
用真实应用身份,在实际挂载目标创建并读取一个专门的测试文件,再检查应用原来的写入操作。确认文件位置、所属身份和持久化都符合预期,随后只清理本次测试文件。
权限修复的顺序是:实际进程身份 → 实际挂载路径 → 读写属性 → 数字权限 → 安全策略。目录 777、全局关闭 SELinux 或整个服务改 root,通常都跳过了真正的原因定位。