重啟一次就“掉權限”?Jenkins + Docker 隱形陷阱 GID 不一致的問題,你一定也踩過
下午例會還沒開完,群里就炸了鍋:“Jenkins 又掛了,任務都跑一半呢!”
趕緊一查,CPU 和內存都飆紅。
圖片
幾個并發任務把服務器搞宕了。 無奈只能重啟。
服務恢復得很快,我剛在群里發完 “Jenkins 正常了” 的消息,誰知隔壁負責上線的小伙伴剛點了發布,控制臺立馬飄紅:
圖片
permission denied while trying to connect to the Docker daemon socket
我心里一咯噔,肌肉記憶立馬啟動:
chown 1000:1000 /var/run/docker.sock
構建秒恢復——但我也清楚,這只是臨時搶救,堪稱打針續命。但這種操作終究不是長久之計。
當時雖然救火成功,但我隱隱覺得這個問題藏得不淺。
初步排查:權限都配了,為什么還是失敗?
從錯誤信息看:
ERROR: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
這說明 Jenkins 容器內的構建進程無法訪問 /var/run/docker.sock
。我們確實已經把 socket 文件掛載進去了:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
那為啥還會沒權限?
先看看宿主機上的 socket 權限:
ls -l /var/run/docker.sock
# srw-rw---- 1 root docker 0 Jun 5 16:14 /var/run/docker.sock
組是 docker
,權限 660
,意味著只有 docker
組成員才有權訪問。
再看宿主機當前用戶的身份:
id ubuntu
# uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),...,999(docker)
用戶確實在 docker
組里,說明宿主機訪問沒問題。
那 Jenkins 容器里的用戶呢?
docker exec -it jenkins bash
id jenkins
# uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),995(docker)
表面上也有個 docker
組,那為什么訪問不了?
真相揭曉:同名不同組,GID 才是硬通貨
關鍵在于:雖然組名都是 docker
,但 GID 不一樣!
- 宿主機上的
docker
組 GID 是999
- 容器內的
docker
組 GID 是995
從操作系統的角度看,權限是由 GID 決定的,名字只是標簽而已。
這就像兩個同名群聊,一個聊技術一個聊八卦,重名但互不相干。 Jenkins 所在容器里雖然有個叫 docker
的組,但那不是宿主機 socket 所屬的組,當然就沒有權限訪問。
??chown 其實只是改門鎖
當我們執行:
sudo chown 1000:1000 /var/run/docker.sock
實際上是把 socket 文件的屬主和屬組改成 Jenkins 容器內的 UID 和 GID。
這樣 Jenkins 用戶就獲得了訪問權限。但這個權限是易失的! 因為 /var/run/docker.sock
是 Docker daemon 啟動時自動創建的,每次重啟就會恢復為默認的 root:docker
,前功盡棄。
?? 根治方案:group_add 精準加組
我們要做的不是
改門鎖
,而是讓 Jenkins 容器的用戶成為合法持鑰匙的人
。
? 步驟一:查出宿主機 docker 組的 GID
$ getent group docker
docker:x:999:ubuntu
確認好 GID 是 999
? 步驟二:在 Jenkins 的 docker-compose 中使用 group_add
指定 GID
version: "3.8"
services:
jenkinsci-master:
image: jenkins/jenkins:lts
...
group_add:
- 999 #添加宿主機docker組的GID
group_add
是個冷門但強力的選項,這表示:運行這個容器時,自動把它的用戶加入宿主機 GID 為 999 的組中。
這樣 Jenkins 用戶就擁有了訪問權限,即使重啟服務器,也不會丟失。
加強配置:鎖定sock屬組,防止 GID 漂移
有些環境中 GID 可能會變化,我們可以在 Docker 啟動參數中明確指定 docker.sock 的屬組:
#查看docker.service位置
systemctl cat docker.service
編輯 /lib/systemd/system/docker.service
,找到 ExecStart
,追加參數:
ExecStart=/usr/bin/dockerd ... --group=docker
然后重啟:
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl restart docker
這樣可以確保宿主機的 docker.sock 永遠由 docker
組控制,不會亂跳。
最終驗證:權限恢復,構建通過
再次重啟宿主機后,檢查 Jenkins 容器內的組信息:
$ docker exec -it jenkins-jenkinsci-master-1 id
uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins),995(docker),999(systemd-journal)
雖然顯示為 systemd-journal
,但 GID=999
就是宿主機的 docker 組,名字只是容器內部的映射,無需糾結。
$ docker exec -it jenkins-jenkinsci-master-1 getent group 999
systemd-journal:x:999:
$ docker exec -it jenkins-jenkinsci-master-1 cat /etc/group|grep 999
systemd-journal:x:999:
接下來執行命令驗證下:
$ docker exec -it jenkins-jenkinsci-master-1 docker ps -aq
72b0282caa3d
6fed992a55ef
2ab4d1e5e397
?? 一切正常!執行構建任務,也恢復正常:
圖片
16:59:42 {"message":"發布信息上報完成!","status":200}
16:59:42 [Pipeline] }
16:59:42 [Pipeline] // withEnv
16:59:42 [Pipeline] }
16:59:42 Deleting 1 temporary files
16:59:42 [Pipeline] // configFileProvider
16:59:42 [Pipeline] echo
16:59:42 ? 部署成功完成!
總結回顧
問題 | 原因分析 | 推薦做法 |
無法訪問 docker.sock | 容器內用戶未加入宿主機 docker 組 | 使用 |
每次重啟都失效 | socket 屬組每次重啟被還原 | 固化屬組:修改 |
容器內組名對不上 | 名字不重要,GID 才是關鍵 | 用 |