Shell 如何在docker容器中逐个运行多个入口点脚本?

Shell 如何在docker容器中逐个运行多个入口点脚本?,shell,docker,unix,sh,entry-point,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 `dep

我正在尝试将主机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更改为HostUID,entrypoint脚本进程也不会更改,因为UID1000调用了entrypoint shell

所以我想出了一个解决方案,制作了两个入口点脚本,一个是更改UID,另一个是容器的引导过程,在我更改deploy的UID后,它将在一个单独的shell中运行。那么我怎样才能让两个入口点一个接一个地运行呢。例如


ENTRYPOINT[“/fix-uid.sh&&/ENTRYPOINT.sh”]
您观察到的行为似乎相当正常:在ENTRYPOINT脚本中,您更改了与用户名
deploy
关联的uid,但两个
whoami
命令仍然使用同一用户运行(首先由uid标识,而不是用户名)

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

还请注意,使用
gosu
重新成为root用户不是一种标准做法(请特别参阅上游文档)

对于您的用例,我建议删除
USER deploy
命令,并在最后切换USER,方法是调整您的入口点脚本,如下所示:

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. 
这可以使用
id
进行测试,例如:


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


对于我所提出的解决方案,无论容器是在生产环境中作为没有卷装载的用户运行,还是在开发环境中通过最初以root用户身份启动卷装载来运行,我都能做到这一点。您可以拥有相同的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
脚本是,包括以下代码位:

# 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
,并且很可能会更新我的
fixperms
脚本,以便在我现在使用的
ls
命令上利用它。)

其中最重要的部分是运行容器。当您需要运行
fixperms
代码时(对我来说,这只是在开发中),我将以root用户身份启动容器。这可以是编写文件中的
docker运行-u root:root…
用户:“root:root”
。它首先以root用户的身份启动容器,触发入口点中运行
fix perms
的if/else的前半部分,然后运行
gosu deploy
从root用户拖放到deploy用户,然后调用“$@”,这是您的命令(CMD)。最终结果是容器中的pid 1现在以部署用户的身份运行您的命令


另一方面,如果您真的希望以一种更容易扩展子映像的方式运行多个入口点片段,我将使用一个由基本映像中的入口点脚本处理的
entrypoint.d
文件夹。编写代码以实现该逻辑非常简单:

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


所有这些都可以看到,还有一个使用nginx的示例,位于:

感谢您的注释。正如我在问题中提到的,我使用gosu,并且在我的入口点脚本的末尾,我撤销了gosu的许可,比如
,哪个gosu和&gosu根chmod o-rwx/usr/bin/gosu
。现在安全了吗?@SkyRar为什么不明确一点,在构建时使用
chmod 700/usr/bin/gosu
删除粘性位,这样在运行entrypoint chmod命令之前,entrypoint生成的任何进程都不能提升权限?谢谢您的提示。正如我在问题中提到的,我使用gosu,并且在我的入口点脚本的末尾,我撤销了gosu的许可,就像gosu和gosu根chmod o-rwx/usr/bin/gosu一样。现在安全吗?
for ep in /etc/entrypoint.d/*.sh; do
  if [ -x "${ep}" ]; then
    echo "Running: ${ep}"
    "${ep}"
  fi
done