9

定制ENTRYPOINT自动修改Docker中volume的权限

 4 years ago
source link: https://note.qidong.name/2018/01/docker-volume-permission/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

定制ENTRYPOINT自动修改Docker中volume的权限

2018-01-25 17:57:10 +08  字数:2124  标签: Docker

volume的权限问题

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.shdocker-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基础镜像的发行版,通常是DebianAlpineDebian包含了BashDash,而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特有的一些用法,如sourceif [[ ... ]]等,用在脚本中。

样例

#!/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为空时的默认命令。

其它关于collectstaticmigrate等操作,是Django网站相关的有用配置,不在本文详解。

另一个复杂的参考,是postgres的官方镜像docker-entrypoint.sh。 其中做了很多事,但比较核心的,还是chownchmod那几行。

参考

还有一种方案,利用--volumes-from,可以指定一个Docker管控的非挂载数据卷。 这种数据卷没有权限问题,但宿主机的访问会有些麻烦。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK