摘要: Volume数据卷是Docker的一个重要概念。数据卷是可供一个或多个容器使用的特殊目录,可以为容器应用存储提供有价值的特性。然而Docker数据卷的权限管理经常是非常令人困惑的。本文将结合实例给大家介绍Docker数据卷权限管理中的常见问题和解决方法。

Volume数据卷是Docker的一个重要概念。数据卷是可供一个或多个容器使用的特殊目录,可以为容器应用存储提供有价值的特性:

  • 持久化数据与容器的生命周期解耦:在容器删除之后数据卷中的内容可以保持。Docker 1.9之后引进的named volume(命名文件卷)可以更加方便地管理数据卷的生命周期;数据卷可以被独立地创建和删除。
  • 数据卷可以用于实现容器之间的数据共享
  • 可以支持不同类型的数据存储实现

Docker缺省提供了对宿主机本地文件卷的支持,可以将宿主机的目录挂载到容器之中。由于没有容器分层文件系统带来的性能损失,本地文件卷非常适合一些需要高性能数据访问的场景,比如MySQL的数据库文件的存储。同时Docker支持通过volume plugin实现不同类型的数据卷,可以更加灵活解决不同应用负载的存储需求。比如在阿里云容器服务中可以为容器提供基于云盘的块存储、基于OSSFS和NAS/NFS的共享文件存储。

然而Docker数据卷的权限管理经常是非常令人困惑的。本文将结合实例给大家介绍Docker数据卷权限管理中的常见问题和解决方法。

从Jenkins挂载本地数据卷错误谈起

最近的一个同事在利用容器运行Jenkins时遇到一个问题,其复现步骤如下:

注:如果是Windows/Mac需要登录到Boot2docker虚拟机之上,而Linux无需如此。

docker-machine ssh default

启动Jenkins官方镜像,并检查日志

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

我们可以发现"jenkins"容器日志显示结果一切正常

然而为了持久化Jenkins配置数据,当我们把宿主机当前目录下的data文件夹挂载到容器中的目录"/var/jenkins_home"的时候,问题出现了:

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/questions/23544282/what-is-the-best-way-to-manage-permissions-for-docker-shared-volumes

其中的基本思路有两个:

  • 一个是利用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/docker-jenkins 获得相关代码,并构建自己的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数据卷上遇到的一些其他问题,

Docker Volume 之权限管理(一)的更多相关文章

  1. Docker Volume 之权限管理(转)

    Volume数据卷是Docker的一个重要概念.数据卷是可供一个或多个容器使用的特殊目录,可以为容器应用存储提供有价值的特性: 持久化数据与容器的生命周期解耦:在容器删除之后数据卷中的内容可以保持.D ...

  2. 存储-docker volume 生命周期管理(14)

    volume 生命周期管理 - 每天5分钟玩转 Docker 容器技术(44) Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要.前面我们主要关注的是 volu ...

  3. HADOOP docker(七):hive权限管理

    1. hive权限简介1.1 hive中的用户与组1.2 使用场景1.3 权限模型1.3 hive的超级用户2. 授权管理2.1 开启权限管理2.2 实现超级用户2.3 实现hiveserver2用户 ...

  4. Docker容器中用户权限管理

    在Linux系统中有一部分知识非常重要,就是关于权限的管理控制:Linux系统的权限管理是由uid和gid负责,Linux系统会检查创建进程的uid和gid,以确定它是否有足够的权限修改文件,而非是通 ...

  5. volume 生命周期管理 - 每天5分钟玩转 Docker 容器技术(44)

    Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要.前面我们主要关注的是 volume 的创建.共享和使用,本节将讨论如何备份.恢复.迁移和销毁 volume. ...

  6. Docker volume权限导致的几个问题

    挂宿主目录的权限问题 由于容器和宿主机共用了一套内核,因此同一个uid对应的容器用户和宿主机用户(哪怕用户名不同)对于内核权限控制而言都是同一个用户.而默认情况下,如果未做特殊配置,容器里的进程默认是 ...

  7. rabbitMQ安装docker版 /权限管理命令

    1.进入docker hub镜像仓库地址:https://hub.docker.com/ 2.搜素rabbitMQ 查询镜像,可以看到多种类型,选择带有web页面的(managment) 3.拉取镜像 ...

  8. docker volume创建、备份、nfs存储

    docker存储volume #环境 centos7.4 , Docker version 17.12.0-ce docker volume创建.备份.nfs存储 #docker volume 数据存 ...

  9. 理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

随机推荐

  1. html5之表格元素

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. MySQL之SQL语句的优化

    仅供自己学习 结论写在前面: 1.尽量避免进行全表扫描,可以给where和order by涉及的列上建立索引 2.尽量在where子句中使用 !=或<>操作符,因为这样会导致引擎放弃索引而 ...

  3. 机器学习技法:07 Blending and Bagging

    Roadmap Motivation of Aggregation Uniform Blending Linear and Any Blending Bagging (Bootstrap Aggreg ...

  4. 伊布(ib)

    [问题描述]ib 被困在了一个美术馆里,她需要收集美术馆内的每种颜料才能获得逃出美术馆的钥匙美术馆由 n*m 的房间构成,每个房间里有一种颜料,解锁进入后就可以收集.有的房间不能解锁,如果解锁的话会直 ...

  5. UVA - 11997:K Smallest Sums

    多路归并 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> ...

  6. UVALive - 3882:And Then There Was One

    约瑟夫环 f[i]表示有i个人先处理第k个人,最后被处理的人是谁 #include<cstdio> #include<cstdlib> #include<algorith ...

  7. hihocoder 1391 树状数组

    #1391 : Countries 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are two antagonistic countries, countr ...

  8. hdu 5314 动态树

    Happy King Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Tot ...

  9. 凸包(BZOJ1069)

    顶点一定在凸包上,我们枚举对角线,观察到固定一个点后,随着另一个点的增加,剩下两个点的最优位置一定是单调的,于是就得到了一个优秀的O(n^2)做法. #include <cstdio> # ...

  10. Python Django的分页,Form验证,中间件

    本节内容 Django的分页 Form 中间件 1 Django 分页 1.1 Django自带的分页 1.首先来看下我的测试数据环境 ############ models.py ######### ...