线上有个服务迁移到 Docker,日志目录挂载进去,结果容器一起来就报 Permission denied。第一反应是宿主机目录权限有问题,后来发现坑不止这一个。
把这一路遇到的情况和解决方法放在这,下次碰到就不用重新排查了。
调整目录权限是多数人最先想到的,直接在宿主机上给挂载点来个 chmod -R 777 /path/to/mounted/directory。开发环境这么干问题不大,但生产环境尽量别开 777。
更稳妥的方式是弄清楚容器内跑的是哪个 uid/gid,然后宿主机目录 chown 给那个用户,或者至少加到对应的组里。如果实在搞不清容器里是谁,至少可以改成 755 或 770 按需给。
如果不想动宿主机目录权限,可以从容器这头下手。很多镜像默认用 root 跑,但挂进去的目录如果只允许普通用户访问就炸了。可以在 Dockerfile 里用 USER 指定一个非 root 用户,或者跑容器的时候直接 -u 指定,比如 docker run -u appuser -v /host/path:/container/path image_name。appuser 需要是镜像里已经建好的用户,或者启动时 --user 也支持 uid:gid 的形式。注意,如果镜像里根本不存在这个用户,容器可能起不来,或者进程跑着跑着就崩了,需要提前确认。
再一种情况是跑在开了 SELinux 或 AppArmor 的系统上,典型的就是 CentOS/RHEL 默认开着 SELinux。
容器访问挂载目录的权限会被安全策略拦下来,日志里可能会看到 avc denied 这类信息。临时关掉 SELinux 可以 setenforce 0 测试一下是不是这个问题,确认是的话别永久关,而是配置合适的策略,比如用 chcon 或 semanage fcontext 把目录打上 container_file_t 标签。具体命令会因发行版和安全策略不同有差别,但思路都是让容器进程有权访问这些路径。
来此加密支持IP证书申请,有效期7天,完美解决纯IP访问场景下的HTTPS需求。无论是内部测试环境、物联网设备,还是没有域名的服务接口,都能快速获取受信任的SSL证书,保障通信安全。
使用命名卷很多时候比直接挂载宿主机目录省事。
直接 -v /host:/container 这种绑挂,权限跟着宿主机走,容易打架。换成 docker volume create 出来的命名卷,Docker 自己管理权限,容器内读写基本不会出权限问题。缺点是宿主机上看数据没那么直观,但日常开发用 docker cp 或者进容器里操作也够用了。
还有一点容易忽略:Docker 守护进程本身的权限。如果 Docker daemon 没有访问某个宿主机目录的权限,容器里挂载这个目录时也会挂掉,尤其在 daemon 不是以 root 跑、或者挂载的是 NFS 目录之类的情况下,表现出的症状跟前面的问题很像。确认 daemon 对路径有 rwx 权限,或者临时用 root 启动 daemon 试试能不能复现。
最后一种做法就是重新挂载,同时显式指定读写模式:docker run -u appuser -v /host/path:/container/path:rw image_name。
其实 :rw 是默认的,但有些场景下显式声明能避免一些奇怪的行为,比如某些存储驱动的权限下发问题。如果在挂载时还可以指定更细的传播属性,但常规排查到这就够用了。
上面这些方法都试一遍还没解决的话,翻一下 Docker 日志和系统日志,看看有没有更细的报错,比如 mount 阶段的错误信息。有时候问题不在权限,而是路径本身不可访问或者被其他进程锁住,顺着日志再找会更靠谱。