当前位置: 首页 > news >正文

Jenkins容器部署微服务时出现daemon socket、permission denied等问题(天机学堂)

前言:首先我们看到Docker daemon socketpermission denied/var/run/docker.sock这几个关键词时,就应优先从Socket 挂载和用户组权限两个方向排查。

一、问题背景

在天机学堂项目中,Jenkins 本身通过 Docker 容器运行。流水线需要执行以下操作:

  1. 获取已经构建好的微服务 JAR 包;
  2. 根据 Dockerfile 构建镜像;
  3. 删除旧容器;
  4. 启动新的微服务容器。

前面的 JAR 包和 Dockerfile 复制正常:

cp ../tjxt-dev-build/tj-user/target/tj-user.jar ./app.jar cp ../tjxt-dev-build/Dockerfile ./Dockerfile

但执行 Docker 命令时出现报错:

Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

最终 Jenkins 输出:

Stage "deploy" skipped due to earlier failure(s) ERROR: script returned exit code 1 Finished: FAILURE

这说明 JAR 包复制已经成功,真正失败的是:

Jenkins 容器中的jenkins用户没有权限访问宿主机的 Docker Daemon。


二、运行环境

本次问题的环境如下:

宿主机:CentOS 7 虚拟机 容器运行时:Docker Jenkins:运行在 Docker 容器中 Jenkins 容器名:jenkins 微服务容器名:tj-user Docker Socket:/var/run/docker.sock

通过下面的命令可以确认 Jenkins 是容器化部署:

docker ps -a | grep jenkins

输出类似:

ea613792a874 jenkins ... 18080->8080/tcp

Jenkins 工作目录也位于:

/var/jenkins_home/workspace/tj-user

这同样说明 Jenkins 运行在容器中。


三、Docker Socket 是什么

Docker 命令本身并不直接创建容器。

例如执行:

docker ps

Docker 客户端会通过下面的 Unix Socket 向 Docker Daemon 发送请求:

/var/run/docker.sock

调用关系可以理解为:

Jenkins 流水线 ↓ docker 命令 ↓ /var/run/docker.sock ↓ 宿主机 Docker Daemon ↓ 构建镜像、启动容器

因此,Jenkins 想在宿主机上构建镜像和启动容器,需要同时满足两个条件:

  1. Jenkins 容器内存在docker命令;
  2. Jenkins 用户有权限访问/var/run/docker.sock

本次环境中 Docker 命令已经存在,真正缺少的是第二个条件。


四、定位问题

1. 检查宿主机 Docker Socket 权限

在宿主机执行:

ls -ln /var/run/docker.sock

本次输出:

srw-rw----. 1 0 994 0 Jul 29 22:53 /var/run/docker.sock

这里使用了-n参数,因此用户和组显示为数字 ID。

各字段含义如下:

srw-rw---- │ │ │ │ │ └── 其他用户没有权限 │ └───── 所属组可以读写 └─────── 所有者可以读写

Socket 的所有者信息为:

UID = 0 GID = 994

还可以使用下面的命令进一步确认:

stat -c '权限=%a UID=%u GID=%g' /var/run/docker.sock

输出:

权限=660 UID=0 GID=994

其中:

660

表示:

  • 所有者具有读写权限;
  • 所属组具有读写权限;
  • 其他用户没有任何权限。

也就是说,只有以下两类用户能够操作 Docker:

root 用户 GID 为 994 的用户组成员

2. 检查 Jenkins 容器中的运行用户

执行:

docker exec jenkins sh -c 'whoami; id; ls -ln /var/run/docker.sock'

本次输出:

jenkins uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins) srw-rw----. 1 0 994 0 Jul 29 14:53 /var/run/docker.sock

可以得到以下信息:

Jenkins 用户 UID:1000 Jenkins 主组 GID:1000 Jenkins 所属组:只有 1000 Docker Socket GID:994

Jenkins 用户既不是root,也不属于 GID 为994的组。

因此,权限校验结果为:

Docker Socket 要求:root 或 GID 994 Jenkins 当前身份:UID 1000、GID 1000 结果:无权访问

这就是 Jenkins 流水线报错的根本原因。


五、解决步骤

步骤一:查看容器内是否已经存在 GID 994 的组

执行:

-v /var/run/docker.sock:/var/run/docker.sock
docker exec -u root jenkins getent group 994

本次输出:

dockerhost:x:994:jenkins

字段含义为:

dockerhost : 用户组名称 x : 组密码占位符 994 : 用户组 GID jenkins : 该组中的成员

说明容器中已经存在一个名为dockerhost的组,其 GID 正好为994


步骤二:将 Jenkins 用户加入对应用户组

假如查询结果中没有jenkins用户,可以执行:

docker exec -u root jenkins usermod -aG dockerhost jenkins

参数解释:

usermod

用于修改用户属性。

-a

表示追加用户组,而不是覆盖 Jenkins 原有的用户组。

-G dockerhost

表示将用户加入附加组dockerhost

jenkins

表示需要修改的用户。

这里必须使用:

-aG

不建议只使用:

-G

因为单独使用-G可能覆盖用户原有的附加组。

如果容器内没有 GID 为994的组,则需要先创建:

docker exec -u root jenkins groupadd -g 994 dockerhost

然后再添加 Jenkins 用户:

docker exec -u root jenkins usermod -aG dockerhost jenkins

其中:

groupadd -g 994 dockerhost

表示创建一个名为dockerhost、GID 为994的用户组。

这里关键的不是组名,而是数字 GID 必须和宿主机 Docker Socket 的 GID 一致。


步骤三:重启 Jenkins 容器

修改用户组后,需要重启 Jenkins 容器:

docker restart jenkins

原因是:

已经运行的 Jenkins Java 进程不会自动重新加载修改后的附加用户组。

如果不重启,即使/etc/group中已经出现 Jenkins 用户,原 Jenkins 进程仍可能继续使用旧权限。

重启后等待 Jenkins 完成启动:

docker ps | grep jenkins

也可以查看日志:

docker logs --tail 100 jenkins

步骤四:验证 Jenkins 用户组

执行:

docker exec jenkins id

正常情况下应该看到类似结果:

uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),994(dockerhost)

重点检查是否出现:

994(dockerhost)

出现该内容说明 Jenkins 用户已经成功加入 Docker Socket 对应的用户组。


步骤五:验证 Jenkins 是否能够操作 Docker

执行:

docker exec -u jenkins jenkins docker ps

该命令表示:

以 jenkins 用户身份 在 jenkins 容器中 执行 docker ps

如果能够正常列出宿主机中的容器,例如:

seata nacos xxljob gogs mq jenkins mysql nginx es redis

并且不再出现:

permission denied

就说明权限问题已经解决。

最后回到 Jenkins 页面,重新执行tj-user流水线即可。


六、完整修复命令

本次环境中,Docker Socket 的 GID 为994,完整操作如下:

# 1. 查看宿主机 Docker Socket 的权限和 GID ls -ln /var/run/docker.sock stat -c '权限=%a UID=%u GID=%g' /var/run/docker.sock # 2. 查看 Jenkins 用户和 Socket 权限 docker exec jenkins sh -c 'whoami; id; ls -ln /var/run/docker.sock' # 3. 检查 Jenkins 容器内是否存在 GID 994 docker exec -u root jenkins getent group 994 # 4. 如果 dockerhost 组已经存在,将 Jenkins 加入该组 docker exec -u root jenkins usermod -aG dockerhost jenkins # 5. 如果组不存在,先创建,再添加用户 docker exec -u root jenkins groupadd -g 994 dockerhost docker exec -u root jenkins usermod -aG dockerhost jenkins # 6. 重启 Jenkins 容器 docker restart jenkins # 7. 验证用户组 docker exec jenkins id # 8. 验证 Docker 操作权限 docker exec -u jenkins jenkins docker ps

注意:第四步和第五步根据实际查询结果二选一,不要重复创建相同 GID 的用户组。


七、为什么不能只在宿主机执行usermod

有些教程会直接执行:

usermod -aG docker jenkins

这种方式只适用于 Jenkins 直接安装在宿主机上的情况。

本次 Jenkins 运行在 Docker 容器中:

宿主机用户系统 和 Jenkins 容器用户系统

属于两个不同的用户空间。

因此,在宿主机修改jenkins用户组,通常不会直接影响 Jenkins 容器内部的用户。

正确做法是:

docker exec -u root jenkins ...

进入 Jenkins 容器内部修改用户组。


八、为什么用户组名称不同也可以访问

宿主机中的 GID 994 可能对应:

docker

而 Jenkins 容器中的 GID 994 可能对应:

dockerhost

例如:

宿主机:docker:x:994 容器内:dockerhost:x:994:jenkins

虽然组名不同,但 Linux 做权限判断时主要比较的是数字 GID,而不是组名。

因此,只要满足:

Socket 所属 GID = Jenkins 附加组 GID

就可以获得访问权限。

本次环境中:

Socket GID = 994 dockerhost GID = 994

两者一致,所以 Jenkins 可以访问 Docker Socket。


九、不推荐的临时解决方案

网上常见的解决方法是:

chmod 666 /var/run/docker.sock

该命令会将 Socket 权限修改为:

所有用户都可以读写

虽然可能立即解决问题,但不推荐长期使用。

主要原因有两个。

第一,权限范围过大。任何能够访问该 Socket 的用户都可能操作宿主机 Docker,包括:

启动特权容器 挂载宿主机目录 删除容器 删除镜像 读取宿主机文件

第二,Docker 服务重启后,Socket 可能重新创建,手动修改的权限可能失效。

因此,更合理的方案是:

保持 Docker Socket 权限为 660 让 Jenkins 用户加入对应 GID 的用户组

十、需要同时检查 Docker Socket 是否挂载

Jenkins 容器想控制宿主机 Docker,还必须挂载 Docker Socket。

可以执行:

docker inspect jenkins \ --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

正常应该出现:

/var/run/docker.sock -> /var/run/docker.sock

如果没有该挂载,即使用户组权限正确,Jenkins 容器也无法访问宿主机 Docker。

创建 Jenkins 容器时通常需要添加:

-v /var/run/docker.sock:/var/run/docker.sock

例如:

docker run -d \ --name jenkins \ -p 18080:8080 \ -v jenkins-data:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:lts

其中:

-v /var/run/docker.sock:/var/run/docker.sock

表示把宿主机 Docker Socket 映射到 Jenkins 容器中的相同位置。


十一、需要注意容器重建问题

通过下面的命令修改用户组:

docker exec -u root jenkins usermod -aG dockerhost jenkins

修改的是当前 Jenkins 容器内部的文件系统。

如果以后执行:

docker rm jenkins

并重新创建 Jenkins 容器,那么本次用户组修改可能丢失。

更稳定的方式是在创建 Jenkins 容器时添加附加组。

先查询宿主机 Docker Socket 的 GID:

stat -c '%g' /var/run/docker.sock

假设结果为:

994

创建容器时可以添加:

--group-add 994

示例:

docker run -d \ --name jenkins \ -p 18080:8080 \ --group-add 994 \ -v jenkins-data:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:lts

这样 Jenkins 容器启动时,就会直接获得 GID 994 的附加组权限。

如果使用 Docker Compose,可以配置:

services: jenkins: image: jenkins/jenkins:lts container_name: jenkins ports: - "18080:8080" volumes: - jenkins-data:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock group_add: - "994"

这种方式比进入容器手动修改更加持久。


十二、问题总结

本次故障不是以下问题导致的:

JAR 包构建失败 Dockerfile 编写错误 Jenkinsfile 语法错误 微服务代码错误 Docker 服务没有启动

真正原因是:

宿主机 Docker Socket 的 GID 为 994 Jenkins 容器用户只属于 GID 1000 Jenkins 无权读写 /var/run/docker.sock

最终解决链路为:

查看 Docker Socket 权限 ↓ 确认 Socket GID 为 994 ↓ 查看 Jenkins 用户所属组 ↓ 发现 Jenkins 不属于 GID 994 ↓ 在容器内创建或使用 GID 994 的用户组 ↓ 将 Jenkins 用户加入该组 ↓ 重启 Jenkins 容器 ↓ 使用 Jenkins 用户执行 docker ps 验证 ↓ 重新运行流水线

最终 Jenkins 流水线恢复执行:

docker ps docker images docker build docker run

deploy阶段也可以继续运行。


十三、核心排查思路

遇到同类报错:

permission denied while trying to connect to the Docker daemon socket

不要立即修改 Jenkinsfile,也不要直接重装 Jenkins。

优先检查下面三项:

# Docker Socket 属于哪个 GID stat -c '%g' /var/run/docker.sock # Jenkins 当前属于哪些组 docker exec jenkins id # Jenkins 容器是否挂载 Docker Socket docker inspect jenkins \ --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

只要保证:

Docker Socket 已正确挂载 Jenkins 容器内存在 docker 命令 Jenkins 用户所属组包含 Socket 的 GID

Jenkins 就能够正常调用宿主机 Docker。

http://www.jsqmd.com/news/1302873/

相关文章:

  • 3步掌握Umi-OCR:免费离线文字识别软件的完整使用指南
  • 3分钟上手LLaMA-Factory:从模型微调优化到高效推理部署
  • 蓝耘 MaaS 的「思考成本」怎么样?我写了个剖析器,把 6 个模型扒了个底朝天
  • 电子课本一键离线化:tchMaterial-parser智能解析工具使用指南
  • hekate USB传输功能:告别SD卡拔插的终极文件管理方案
  • cppm题库怎么领取? - 众智商学院官方
  • AI教材写作:低查重率实战技巧与工具链优化
  • # 贴标机送标轴为什么常用 PLF060-5 配 400W 伺服:从辊速、惯量到同步误差分析
  • Mermaid Live Editor:免费在线图表工具终极指南,5分钟创建专业流程图
  • WinSetView:Windows文件夹视图全局设置的终极解决方案
  • 怎么有效降低英文AI率?别硬改!这样做才能降成功
  • 如何5分钟掌握付费墙突破神器:13ft Ladder完整使用指南
  • 蓝耘 MaaS:我把推理层重构成多模型路由,本以为能省 70%,实测只省了 9%——但学到了更值钱的三件事
  • 10分钟掌握LLaMA-Factory批量处理:大规模数据集并行加载全攻略
  • BilibiliDown完整指南:3步轻松下载B站视频和音频
  • PrimeVue-Tailwind与Nuxt项目集成教程:构建现代化Vue应用的最佳实践
  • 英文AI率居高不下?吃透Turnitin检测逻辑!实测有效降AI方法+工具分享
  • 广州大型企业高管经济犯罪辩护律师哪个专业:【法纳刑辩】实力强 - 晚香时候
  • Termux:Float新手入门:3分钟学会移动和调整悬浮终端窗口大小
  • LLaMA-Factory数据集处理指南:从JSON到高效微调数据
  • 2026年全国路沿石厂家售后水平排行榜单一览 - 起跑123
  • 日记第21天——好友相聚
  • 终极音乐管理指南:foobox-cn如何让foobar2000成为你的专属音乐中心
  • 开发者的文档翻译工作流:PDF翻译+格式校验+质量对比的一站式方案
  • AI辅助学术写作全流程工具链与效率提升
  • 企业资源包是什么
  • 《天道》笔记04 | 芮小丹表白那段,我反复看了三遍
  • xR与VP技术在庆典内容创作中的创新应用
  • 如何用AI实现视频智能剪辑:FunClip完全指南
  • 2026实测!超实用英文降AI技巧+多款工具测评