Docker 串口报 Permission denied:检查设备 GID 与进程附加组
串口已映射但应用 Permission denied,核对设备数字 GID、业务进程附加组和入口身份,通过最小 group-add 与设备映射处理,保留隔离与硬件验收限制。
串口已经通过 --device 映射进 Docker,应用仍报 Permission denied。Dockerfile 中已经把用户加进 dialout,id appuser 也列出了这个组,但当前进程的 id 输出没有它。先检查进程真正持有的附加组,以及设备节点的数字 GID;用户数据库里的成员关系不能代替运行进程的权限。
下面针对 Linux 上普通 rootful Docker、没有用户命名空间重映射的串口设备访问。Rootless、userns-remap、Docker Desktop 虚拟机和特殊设备驱动还有映射或宿主访问限制,不能只照搬一个 GID 就认为已经获得设备权限。
设备名相同,组号未必相同
先在 Docker daemon 所在宿主机查看实际设备。以下路径是示例;远程 Docker 的设备在服务端,不在运行 CLI 的笔记本上:
stat -Lc 'uid=%u gid=%g mode=%a path=%n' /dev/serial0
这里是 Linux stat 的写法,-L 读取符号链接的目标。树莓派的 /dev/serial0 可能指向另一个设备节点;先确认目标确实是需要的串口。设备根本不存在时,应检查宿主设备和映射路径,增加用户组不会创建它。
容器有相应工具时,再查看映射后的设备和默认执行用户:
docker exec serial-app id
docker exec serial-app stat -Lc 'uid=%u gid=%g mode=%a path=%n' /dev/serial0
serial-app 是示例容器名称。这里的 id 用来核对 exec 默认身份,应用入口若还会降权或切换用户,应继续检查应用进程自身的 UID、GID 和附加组,例如其 /proc/进程号/status 中的 Uid、Gid、Groups;不能用一份 root shell 的输出代表业务进程。
容器中的 dialout 与宿主机中的 dialout 即使名称相同,数字 GID 也可能不同。文件权限按数字身份判断。重点比较设备所属的数字组、组权限是否允许应用需要的读写,以及进程是否持有这个组。
把进程状态和账户记录分开看
id appuser 从账户资料查询用户的组;不带用户名的 id 显示当前进程的身份。两者不同,说明不能只凭构建时 useradd 成功判断应用已经取得附加组。
Docker 默认会为指定用户查找附加组,并不是所有容器天生都丢掉它们。还需核对最终镜像的账户资料、创建时的 user 设置,以及入口脚本怎样启动应用。显式指定主组、入口降权、镜像中同名组号不同等情况,应分别按实际进程确认,不从这条症状推断唯一原因。
已经启动的进程不会因为后来修改 /etc/group 就自动增加附加组。临时进入容器修改用户资料,也不是下一次镜像部署的稳定配置;确认处理路径后,应把所需设置放回镜像或部署入口。
只增加需要的设备组
设备确实存在、普通组权限满足要求,而应用缺少那个 GID 时,可以在创建容器时通过 --group-add 增加它。以下只示意相关参数,20 是占位示例,必须替换为实际设备 GID:
docker run --group-add 20 --device=/dev/serial0:/dev/serial0:rw IMAGE
保留服务原来的用户、挂载、网络、环境和入口,不能用这行简化命令覆盖整个部署。IMAGE 需换成已审定镜像;设备的 rw 只适合需要读写、不需要创建设备节点的程序。Compose 部署则在目标服务配置中使用对应的 group_add 和 devices,先核对最终解析结果。
--group-add 增加的是进程可用的附加组,不是给整个目录改属组。如果入口之后再次切换身份并清空组列表,仍要修入口的身份传递,不能不断往创建参数中堆组号。
不要用 --privileged 或把设备改为 666 来代替这项检查。前者扩大容器对宿主机的访问,后者允许更多本机用户访问串口;它们还不能证明原来缺的是哪一项权限。SELinux/AppArmor 拒绝、设备映射和应用组权限也是不同的检查层次,组号正确仍失败时按实际拒绝记录继续处理。
重建后验证真实的串口操作
创建参数改变后,单纯 restart 不能替现有容器加入新的创建配置。修改前保存部署配置、原镜像、设备映射和业务状态;安排允许中断的窗口,通过原部署流程重建目标容器,避免两个实例同时控制同一个串口。
重建后核对业务进程持有正确附加 GID,设备节点与预期一致,并让应用在获准的范围完成原先失败的串口操作。串口可能控制实际硬件,验收不能随便发送测试字节。容器能够启动,或 root 能打开设备,都不足以证明原应用权限已恢复。
需要回退时恢复原创建配置和镜像,再按相同流程替换容器。若还改过宿主设备权限或 udev 规则,按保存的设置单独恢复;回退容器配置不会撤销已经发给设备的指令。


