Docker Volume 之權限管理(轉)

Volume數據卷是Docker的一個重要概念。數據卷是可供一個或多個容器使用的特殊目錄,能夠爲容器應用存儲提供有價值的特性:java

持久化數據與容器的生命週期解耦:在容器刪除以後數據卷中的內容能夠保持。Docker 1.9以後引進的named volume(命名文件卷)能夠更加方便地管理數據卷的生命週期;數據卷能夠被獨立地建立和刪除。
數據卷能夠用於實現容器之間的數據共享
能夠支持不一樣類型的數據存儲實現

Docker缺省提供了對宿主機本地文件卷的支持,能夠將宿主機的目錄掛載到容器之中。因爲沒有容器分層文件系統帶來的性能損失,本地文件卷很是適合一些須要高性能數據訪問的場景,好比MySQL的數據庫文件的存儲。同時Docker支持經過volume plugin實現不一樣類型的數據卷,能夠更加靈活解決不一樣應用負載的存儲需求。好比在阿里雲容器服務中能夠爲容器提供基於雲盤的塊存儲、基於OSSFS和NAS/NFS的共享文件存儲。git

然而Docker數據卷的權限管理常常是很是使人困惑的。本文將結合實例給你們介紹Docker數據卷權限管理中的常見問題和解決方法。
從Jenkins掛載本地數據卷錯誤談起github

最近的一個同事在利用容器運行Jenkins時遇到一個問題,其復現步驟以下:docker

注:若是是Windows/Mac須要登陸到Boot2docker虛擬機之上,而Linux無需如此。
注:若是是Windows/Mac須要登陸到Boot2docker虛擬機之上,而Linux無需如此。數據庫

docker-machine ssh default瀏覽器

啓動Jenkins官方鏡像,並檢查日誌bash

docker run -d -p 8080:8080 -p 50000:50000 --name jenkins jenkins
docker logs jenkins

咱們能夠發現"jenkins"容器日誌顯示結果一切正常ssh

然而爲了持久化Jenkins配置數據,當咱們把宿主機當前目錄下的data文件夾掛載到容器中的目錄"/var/jenkins_home"的時候,問題出現了:curl

docker rm -f jenkins
docker run -d -p 8080:8080 -p 50000:50000 -v $(pwd)/data:/var/jenkins_home --name jenkins jenkins
docker logs jenkins工具

錯誤日誌以下

touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

這是神馬狀況?

咱們檢查一下以前啓動方式的"/var/jenkins_home"目錄權限,查看Jenkins容器的當前用戶: 當前用戶是"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

而當映射本地數據卷時,/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 ..

這就解釋了爲何當"jenkins"用戶的進程訪問"/var/jenkins_home"目錄時,會出現 Permission denied 的問題

咱們再檢查一下宿主機上的數據卷目錄,當前路徑下"data"目錄的擁有者是"root",這是由於這個目錄是Docker進程缺省建立出來的。

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 ../

發現問題以後,相應的解決方法也很簡單:把當前目錄的擁有者賦值給uid 1000,再啓動"jenkins"容器就一切正常了。

sudo chown -R 1000 data
docker start jenkins

這時利用瀏覽器訪問 "http://192.168.99.100:8080/" 就能夠看到Jenkins的Web界面了。注:如沒法訪問,可能須要經過docker-machine ip命令得到當前Docker宿主機的IP地址。

當咱們再進入容器內部查看"/var/jenkins_home"目錄的權限,其擁有者已經變成 "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
...

而有趣的是在宿主機上咱們看到的 "data"目錄的擁有者是"docker",這是由於"docker"用戶在"boot2docker"宿主機上的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 ../
...

這時咱們已經能夠知道:容器的本地數據卷中文件/目錄的權限是和宿主機上一致的,只是uid/gid在Docker容器和宿主機中可能映射爲不一樣的用戶/組名稱。

在上文,咱們使用了一個常見的技巧,即在宿主機上執行chown命令時採用了uid而不是具體的用戶名,這樣就能夠保證設置正確的擁有者。

問題雖然解決了,但思考並無結束。由於當使用本地數據卷時,Jenkins容器會依賴宿主機目錄權限的正確性,這會給自動化部署帶來額外的工做。有沒有方法讓Jenkins容器爲數據卷自動地設置正確的權限呢?這個問題對不少以non-root方式運行的應用也都有借鑑意義。

爲non-root應用正確地掛載本地數據卷
咱們能夠從萬能的stackoverflow.com找到不少相關的討論,其中一個很是有借鑑意義問答以下

http://stackoverflow.com/ques...

其中的基本思路有兩個:

一個是利用Data Container的方法在容器間共享數據卷。這樣就規避了解決宿主機上數據卷的權限問題。因爲在1.9版本以後,Docker提供了named volume來取代純數據容器,咱們還須要真正地解決這個問題。
另一個思路就是讓容器中以root用戶啓動,在容器啓動腳本中利用"chown"命令來修正數據卷文件權限,以後切換到non-root用戶來執行程序
咱們來參照第二個思路來解決這個問題

下面是一個基於Jenkins鏡像的Dockerfile:它會切換到"root"用戶並在鏡像中添加"gosu"命令,和新的入口點"/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 是常常出如今官方Docker鏡像中的一個小工具。它是"su"和"sudo"命令的輕量級替代品,並解決了它們在tty和信號傳遞中的一些問題。

新入口點的"entrypoint.sh"的內容以下:它會爲"JENKINS_HOME"目錄設置"jenkins"的擁有權限,而且再利用"gosu"命令切換到"jenkins"用戶來執行"jenkins"應用。

#! /bin/bash
set -e
chown -R 1000 "$JENKINS_HOME"
exec gosu jenkins /bin/tini -- /usr/local/bin/jenkins.sh

您能夠直接從 https://github.com/denverdino... 得到相關代碼,並構建本身的Jenkins鏡像。執行命令以下:

git clone https://github.com/AliyunContainerService/docker-jenkins
cd docker-jenkins/jenkins
docker build -t denverdino/jenkins .

而後基於新鏡像啓動Jenkins容器

docker rm -f jenkins
docker run -d -p 8080:8080 -p 50000:50000 -v $(pwd)/data:/var/jenkins_home --name jenkins denverdino/jenkins

總結

本文介紹了Docker數據卷的基本概念。針對non-root進程訪問本地數據卷出現的權限問題,咱們給出了一個解決方案。咱們計劃在將來爲你們繼續總結在Docker數據捲上遇到的一些其餘問題,

相關文章
相關標籤/搜索