Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
這是神馬情況?
我們檢查一下之前啟動(dòng)方式的”/var/jenkins_home”目錄權(quán)限,查看Jenkins容器的當(dāng)前用戶: 當(dāng)前用戶是”jenkins”而且”/var/jenkins_home”目錄是屬于jenkins用戶擁有的
docker@default:~$ docker run -ti --rm --entrypoint="/bin/bash" jenkins -c "whoami && id" jenkins uid=1000(jenkins) gid=1000(jenkins) groups=1000(jenkins) docker@default:~$ docker run -ti --rm --entrypoint="/bin/bash" jenkins -c "ls -la /var/jenkins_home" total 20 drwxr-xr-x 2 jenkins jenkins 4096 Jun 5 08:39 . drwxr-xr-x 28 root root 4096 May 24 16:43 .. -rw-r--r-- 1 jenkins jenkins 220 Nov 12 2014 .bash_logout -rw-r--r-- 1 jenkins jenkins 3515 Nov 12 2014 .bashrc -rw-r--r-- 1 jenkins jenkins 675 Nov 12 2014 .profile
而當(dāng)映射本地?cái)?shù)據(jù)卷時(shí),/var/jenkins_home目錄的擁有者變成了root用戶
docker run -ti --rm -v $(pwd)/data:/var/jenkins_home --entrypoint="/bin/bash" jenkins -c "ls -la /var/jenkins_home" total 4 drwxr-sr-x 2 root staff 40 Jun 5 08:32 . drwxr-xr-x 28 root root 4096 May 24 16:43 ..
這就解釋了為什么當(dāng)”jenkins”用戶的進(jìn)程訪問”/var/jenkins_home”目錄時(shí),會(huì)出現(xiàn) Permission denied 的問題
我們再檢查一下宿主機(jī)上的數(shù)據(jù)卷目錄,當(dāng)前路徑下”data”目錄的擁有者是”root”,這是因?yàn)檫@個(gè)目錄是Docker進(jìn)程缺省創(chuàng)建出來的。
docker@default:~$ ls -la data total 0 drwxr-sr-x 2 root staff 40 Jun 5 08:32 ./ drwxr-sr-x 5 docker staff 160 Jun 5 08:32 ../
發(fā)現(xiàn)問題之后,相應(yīng)的解決方法也很簡單:把當(dāng)前目錄的擁有者賦值給uid 1000,再啟動(dòng)”jenkins”容器就一切正常了。
sudo chown -R 1000 data docker start jenkins
這時(shí)利用瀏覽器訪問 “http://192.168.99.100:8080/” 就可以看到Jenkins的Web界面了。注:如無法訪問,可能需要通過docker-machine ip命令獲得當(dāng)前Docker宿主機(jī)的IP地址。
當(dāng)我們再進(jìn)入容器內(nèi)部查看”/var/jenkins_home”目錄的權(quán)限,其擁有者已經(jīng)變成 “jenkins”
docker@default:~$ docker exec jenkins ls -la /var/jenkins_home total 24 drwxr-sr-x 11 jenkins staff 340 Jun 5 09:00 . drwxr-xr-x 28 root root 4096 May 24 16:43 .. drwxr-sr-x 3 jenkins staff 60 Jun 5 08:59 .java -rw-r--r-- 1 jenkins staff 289 Jun 5 08:59 copy_reference_file.log ...
而有趣的是在宿主機(jī)上我們看到的 “data”目錄的擁有者是”docker”,這是因?yàn)?#8221;docker”用戶在”boot2docker”宿主機(jī)上的uid也是”1000″。
docker@default:~$ ls -la data total 20 drwxr-sr-x 2 docker staff 40 Jun 5 11:55 ./ drwxr-sr-x 6 docker staff 180 Jun 5 11:55 ../ ...
這時(shí)我們已經(jīng)可以知道:容器的本地?cái)?shù)據(jù)卷中文件/目錄的權(quán)限是和宿主機(jī)上一致的,只是uid/gid在docker容器和宿主機(jī)中可能映射為不同的用戶/組名稱。
在上文,我們使用了一個(gè)常見的技巧,即在宿主機(jī)上執(zhí)行chown命令時(shí)采用了uid而不是具體的用戶名,這樣就可以保證設(shè)置正確的擁有者。
問題雖然解決了,但思考并沒有結(jié)束。因?yàn)楫?dāng)使用本地?cái)?shù)據(jù)卷時(shí),Jenkins容器會(huì)依賴宿主機(jī)目錄權(quán)限的正確性,這會(huì)給自動(dòng)化部署帶來額外的工作。有沒有方法讓Jenkins容器為數(shù)據(jù)卷自動(dòng)地設(shè)置正確的權(quán)限呢?這個(gè)問題對(duì)很多以non-root方式運(yùn)行的應(yīng)用也都有借鑒意義。
為non-root應(yīng)用正確地掛載本地?cái)?shù)據(jù)卷
我們可以從萬能的stackoverflow.com找到很多相關(guān)的討論,其中一個(gè)非常有借鑒意義問答如下
http://stackoverflow.com/questions/23544282/what-is-the-best-way-to-manage-permissions-for-docker-shared-volumes
其中的基本思路有兩個(gè):
一個(gè)是利用Data Container的方法在容器間共享數(shù)據(jù)卷。這樣就規(guī)避了解決宿主機(jī)上數(shù)據(jù)卷的權(quán)限問題。由于在1.9版本之后,Docker提供了named volume來取代純數(shù)據(jù)容器,我們還需要真正地解決這個(gè)問題。
另外一個(gè)思路就是讓容器中以root用戶啟動(dòng),在容器啟動(dòng)腳本中利用”chown”命令來修正數(shù)據(jù)卷文件權(quán)限,之后切換到non-root用戶來執(zhí)行程序
我們來參照第二個(gè)思路來解決這個(gè)問題
下面是一個(gè)基于Jenkins鏡像的Dockerfile:它會(huì)切換到”root”用戶并在鏡像中添加”gosu”命令,和新的入口點(diǎn)”/entrypoint.sh”
FROM jenkins:latest USER root RUN GOSU_SHA=5ec5d23079e94aea5f7ed92ee8a1a34bbf64c2d4053dadf383992908a2f9dc8a \ && curl -sSL -o /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.9/gosu-$(dpkg --print-architecture)" \ && chmod +x /usr/local/bin/gosu \ && echo "$GOSU_SHA /usr/local/bin/gosu" | sha256sum -c - COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]
注釋:gosu 是經(jīng)常出現(xiàn)在官方Docker鏡像中的一個(gè)小工具。它是”su”和”sudo”命令的輕量級(jí)替代品,并解決了它們在tty和信號(hào)傳遞中的一些問題。
新入口點(diǎn)的”entrypoint.sh”的內(nèi)容如下:它會(huì)為”JENKINS_HOME”目錄設(shè)置”jenkins”的擁有權(quán)限,并且再利用”gosu”命令切換到”jenkins”用戶來執(zhí)行”jenkins”應(yīng)用。
#! /bin/bash set -e chown -R 1000 "$JENKINS_HOME" exec gosu jenkins /bin/tini -- /usr/local/bin/jenkins.sh
您可以直接從 https://github.com/denverdino/docker-jenkins 獲得相關(guān)代碼,并構(gòu)建自己的Jenkins鏡像。執(zhí)行命令如下:
git clone https://github.com/AliyunContainerService/docker-jenkins cd docker-jenkins/jenkins docker build -t denverdino/jenkins .
然后基于新鏡像啟動(dòng)Jenkins容器
docker rm -f jenkins docker run -d -p 8080:8080 -p 50000:50000 -v $(pwd)/data:/var/jenkins_home --name jenkins denverdino/jenkins