定制ENTRYPOINT自动修改Docker中volume的权限
2018-01-25 17:57:10 +08 字数:2124 标签: Dockervolume的权限问题 ¶
在Docker中,需要把host的目录挂载到container中作为volume使用时,往往会发生文件权限问题。 常见的现象是,container对该路径并无写权限,以致其中服务的各种千奇百怪的问题。
导致这类问题的原因,是container内外的UID不同。 比如,host当前使用docker的用户UID是1000(这是默认第一个用户的UID)。 如果container内的UID是2000,那么host创建的目录对container来说就并非owner,默认情况下不可写入。
此外还有一种情况,那就是挂载前,host上不存在被挂载的目录。 Docker会以root权限,先创建该目录,再挂载。 这就导致,即使host与container的UID都是1000,也会出现无写权限的情况。 这种现象,只会在初始化时出现,但也足够令新手困惑,令老手厌烦。
为什么在Dockerfile中不能把volume的权限配置好?
因为Dockerfile是对image的描述,而volume则是container的内容。
Dockerfile中做出的权限配置,对非volume来说是可以生效的,而对volume则不然。
本质上,host挂载到volume上的目录,是属于host的。
Dockerfile是在docker build
期间执行,而volume则是在docker run
的时候产生。
其实,Docker在自动创建volume路径时,应该再自动地把它修改为container内前台进程的user:group
。
然而Docker目前并无此类机制,俺们这些用户就只能另谋出路。
一般的临时方案,都是去手动修改权限。
要么通过chown
,把owner改成container内用户的UID;
要么通过chmod 777
,搞成所有用户通用。
这些当然不是什么好的长期方案,也违背了Docker方便部署的初衷。
目前看来,最好的方案,还是定制Dockerfile的ENTRYPOINT。
定制ENTRYPOINT ¶
默认情况下ENTRYPOINT相当于是/bin/sh
,只是准备简单的Shell运行环境。
定制ENTRYPOINT为一个复杂的脚本,就可以在每次container启动时,都执行一遍。
这个入口脚本,一般都是叫entrypoint.sh
或docker-entrypoint.sh
,并无强制约定。
COPY entrypoint.sh ./
ENTRYPOINT ["./entrypoint.sh"]
在volume产生时,是docker run
的准备阶段(create),而执行entrypoint.sh
则是在启动阶段(start)。
所以,在entrypoint.sh
中可以对volume做权限配置。
当然,权限配置需要root权限,entrypoint.sh
需要在root用户下启动。
当然,也可以在一般用户下启动,需要chown
时在提升至root权限。
不过这样会有安全隐患。
反之,以root执行entrypoint.sh
,在其中以普通用户启动真正的服务,这样在服务被黑的情况下,能确保仅当前服务受影响。
选择ENTRYPOINT的shell ¶
Docker基础镜像的发行版,通常是Debian或Alpine。 Debian包含了Bash和Dash,而Alpine则默认只有Ash。
Ash的全称是Almquist shell,由Kenneth Almquist在1989年5月30日发布。
它是一种轻量级的Unix shell,在多个发行版上都有开枝散叶,详见ash variants。
现在,各大发行版上能看到的/bin/sh
,基本都是Ash的一种。
Dash其实就是Debian上的一种Ash。
因此,用Ash来写ENTRYPOINT,会更具有通用性。 文件头可以使用以下形式的Shebang。
#!/bin/sh
当然,什么也不写其实也默认是这个。
写上后显得更专业,对自己也算是一个提示吧,以免把Bash特有的一些用法,如source
、if [[ ... ]]
等,用在脚本中。
样例 ¶
#!/bin/sh
mkdir -p "${DJANGO_DATABASE}" "${DJANGO_LOGS}" "${DJANGO_STATIC}"
python manage.py collectstatic --noinput
python -Wd manage.py migrate --fake-initial
chown -R "${DJANGO_USER}":999 "${DJANGO_SITE}"
if [ $# -gt 0 ]
then
su "${DJANGO_USER}" -c "exec $*"
else
su "${DJANGO_USER}" -c "exec uwsgi --ini uwsgi.ini --http=0.0.0.0:${DJANGO_PORT}"
fi
以上是一个Django网站镜像的entrypoint.sh
。
其中,和本文关系密切的是chown
那一行。
把相关volume的UID设为${DJANGO_USER}
,是镜像内的读写需要;
把GID设为999
,是因为在宿主机上,Docker安装后,默认的docker用户组的GID就是999
,便于宿主机进行操作。
最后的if
代码块,用${DJANGO_USER}
执行前台进程,提高了安全性(相当于Dockerfile里使用USER);
exec
相关的配置,给了一个CMD为空时的默认命令。
其它关于collectstatic
、migrate
等操作,是Django网站相关的有用配置,不在本文详解。
另一个复杂的参考,是postgres的官方镜像docker-entrypoint.sh。
其中做了很多事,但比较核心的,还是chown
、chmod
那几行。
参考 ¶
还有一种方案,利用--volumes-from
,可以指定一个Docker管控的非挂载数据卷。
这种数据卷没有权限问题,但宿主机的访问会有些麻烦。