如何在Docker容器中一个接一个地运行多个入口点脚本?

时间:2018-12-12 12:38:10

标签: shell docker unix sh entry-point

我正在尝试将主机UID与容器UID进行如下匹配。

Dockerfile

RUN addgroup -g 1000 deploy \
&& adduser -D -u 1000 -G deploy -s /bin/sh deploy

USER deploy

COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
CMD ["php-fpm7","-F"]

entrypoint.sh

whoami # it outputs `deploy`  

# Change UID of 'deploy' as per host user UID
HOST_CURRENT_USER_ID=$(stat -c "%u" /var/www/${PROJECT_NAME})
if [ ${HOST_CURRENT_USER_ID} -ne 0 ]; then
    gosu root usermod -u ${HOST_CURRENT_USER_ID} deploy
    gosu root groupmod -g ${HOST_CURRENT_USER_ID} deploy
fi

whoami  # It outputs as unknown user id 1000. 

请注意上面whoami的输出。即使我将deploy的UID更改为主机uid,入口点脚本过程也不会更改,因为入口点shell已被UID 1000调用。

因此,我提出了一个解决方案,使两个入口点脚本一个是更改UID,另一个是用于容器的引导过程,该过程将在更改部署的UID后在单独的外壳中运行。因此,如何使两个入口点一个接一个地运行。例如类似

ENTRYPOINT ["/fix-uid.sh && /entrypoint.sh"]

2 个答案:

答案 0 :(得分:1)

您似乎正在设计与我创建的解决方案非常相似的解决方案。正如ErikMD提到的,不要使用gosu从用户切换到root,而是要从root切换到用户。否则,您的容器内将有一个开放的安全漏洞,任何用户都无法成为root用户,从而破坏了以不同的用户ID运行容器的目的。


对于我提出的解决方案,无论容器是作为没有卷安装的用户在生产中运行,还是通过最初以根用户身份启动容器而在具有卷安装的开发环境中运行,我都可以使用。您可以有一个相同的Dockerfile,然后更改入口点,使其具有以下含义:

#!/bin/sh

if [ "$(id -u)" = "0" ]; then
  fix-perms -r -u deploy -g deploy /var/www/${PROJECT_NAME}
  exec gosu deploy "$@"
else
  exec "$@"
fi

上面的fix-perms脚本是from my base image,并包含以下代码:

# update the uid
if [ -n "$opt_u" ]; then
  OLD_UID=`getent passwd "${opt_u}" | cut -f3 -d:`
  NEW_UID=`ls -nd "$1" | awk '{print $3}'`
  if [ "$OLD_UID" != "$NEW_UID" ]; then
    echo "Changing UID of $opt_u from $OLD_UID to $NEW_UID"
    usermod -u "$NEW_UID" -o "$opt_u"
    if [ -n "$opt_r" ]; then
      find / -xdev -user "$OLD_UID" -exec chown -h "$opt_u" {} \;
    fi
  fi
fi

(请注意,我非常喜欢您对stat -c的使用,并且很可能会更新我的fix-perms脚本,以在我现在使用的ls命令中利用它。)

最重要的部分是运行容器。当您需要运行fix-perms代码时(对我来说这仅是在开发中),我以超级用户身份启动容器。撰写文件中可以是docker run -u root:root ...user: "root:root"。这首先将容器启动为root用户,这会在运行fix-perms的入口点触发if / else的前半部分,然后运行gosu deploy从root删除以进行部署,然后调用“ $ @”,是您的命令(CMD)。最终结果是容器中的pid 1现在以部署用户身份运行命令。


顺便说一句,如果您真的想以一种更容易扩展子图像的方式来运行多个入口点片段的简便方法,那么我会使用一个entrypoint.d文件夹,该文件夹由基本图像中的入口点脚本处理。编写代码以实现该逻辑很简单:

for ep in /etc/entrypoint.d/*.sh; do
  if [ -x "${ep}" ]; then
    echo "Running: ${ep}"
    "${ep}"
  fi
done

所有这些以及使用nginx的示例都可以在https://github.com/sudo-bmitch/docker-base

上看到。

答案 1 :(得分:0)

您观察到的行为似乎很正常:在入口点脚本中,您更改了与用户名deploy关联的UID,但是两个whoami命令仍以同一用户运行(由首先是UID,而不是用户名。

有关Docker上下文中的UID和GID的更多信息,请参见例如that reference

还请注意,使用gosu重新成为root并不是标准做法(尤其是在上游文档中,warning如此)。

对于您的用例,我建议删除USER deploy命令并最终通过调整入口点脚本来最终切换用户:

  

Dockerfile

(…)
RUN addgroup -g 1000 deploy \
&& adduser -D -u 1000 -G deploy -s /bin/sh deploy

COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
CMD ["php-fpm7","-F"]
  

entrypoint.sh

#!/bin/sh
whoami # it outputs `root`

# Change UID of 'deploy' as per host user UID
HOST_CURRENT_USER_ID=$(stat -c "%u" /var/www/${PROJECT_NAME})
if [ ${HOST_CURRENT_USER_ID} -ne 0 ]; then
    usermod -u ${HOST_CURRENT_USER_ID} deploy
    groupmod -g ${HOST_CURRENT_USER_ID} deploy
fi

# don't forget the "exec" builtin
exec gosu ${HOST_CURRENT_USER_ID}:${HOST_CURRENT_USER_ID} "$@"
  

这可以使用id进行测试,例如:

$ docker build -t test-gosu .
$ docker run --rm -it test-gosu /bin/sh
  $ id