摘要: 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. MySQL 5.7 新特性之初始化

    1. 把二进制安装包下载放在/opt 目录下并解压 2. 创建软连接, 并添加运行环境 ln -s /usr/local/mysql /opt/mysql-5.7.18-linux-glibc2.5- ...

  2. C#在使用Assembly加载程序集时失败

    错误现象: 进行插件读取时出现错误:"尝试从一个网络位置加载程序集,在早期版本的 .NET Framework 中,这会导致对该程序集进行沙盒处理.此发行版的 .NET Framework ...

  3. svg 五花 元辅音 助读器

    如图,使用svg 创建5个圆,中间 辅音字母,外圈 元音字母 以及 示例单词. 可以使用  提交 按钮 进行更新

  4. SpringMVC 自定义类型转换器

    先准备一个JavaBean(Employee) 一个Handler(SpringMVCTest) 一个converters(EmployeeConverter) 要实现的输入一个字符串转换成一个emp ...

  5. 【Python3.6+Django2.0+Xadmin2.0系列教程之二】学生信息管理系统(入门篇)

    上一篇我们已经创建好了一个Xadmin的基础项目,现在我们将在此基础上构建一个同样很基础的学生信息管理系统. 一.创建模型 模型是表示我们的数据库表或集合类,并且其中所述类的每个属性是表或集合的字段, ...

  6. Flask博客开发——Tinymce编辑器

    之前Flask博客的文本编辑器比较简陋,这里为博客添加个优雅易用的Tinymce文本编辑器. 1.项目中添加Tinymce 下载好Tinymce包以及语言包,并添加到项目中.添加到项目的方法,参考了这 ...

  7. [BZOJ 5093]图的价值

    Description 题库链接 一个带标号的图的价值定义为每个点度数的 \(k\) 次方的和.给定 \(n\) 和 \(k\) ,请计算所有 \(n\) 个点的带标号的简单无向图的价值之和.对 \( ...

  8. 【PowerOJ1738】最小路径覆盖

    Description 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个 顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶 ...

  9. hdu 3016 dp+线段树

    Man Down Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  10. python,for,while循环控制

    1.for循环 for循环 for i in range(0,5): for j in range(0,5): print('#'*5) 2.while 循环 import random #get n ...