【问题标题】:Exec not replacing bash shell in Docker entrypoint scriptExec没有替换Docker入口点脚本中的bash shell
【发布时间】:2020-01-03 22:16:19
【问题描述】:

我正在尝试从 Docker 容器运行 UWSGI 服务器。我已经取得了成功,但是我遇到了一个问题,因为我的入口点脚本在容器启动后仍将以 PID 1 的 root 身份运行,而我宁愿将初始的 /bin/bash 进程替换为 UWSGI 进程:

bash-4.4# ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 {docker-entrypoi} /bin/bash /usr/local/bin/docker-entrypoint.sh
   19 myuser    0:00 uwsgi --ini /opt/mysite/uwsgi.ini
   21 myuser    0:00 uwsgi --ini /opt/mysite/uwsgi.ini
   22 myuser    0:00 uwsgi --ini /opt/mysite/uwsgi.ini
   24 myuser    0:02 python3 ./manage.py qcluster
   28 myuser    0:00 python3 ./manage.py qcluster
   29 myuser    0:00 python3 ./manage.py qcluster
   30 myuser    0:00 python3 ./manage.py qcluster
   31 myuser    0:00 python3 ./manage.py qcluster
   32 myuser    0:00 python3 ./manage.py qcluster
   33 myuser    0:00 python3 ./manage.py qcluster
   34 myuser    0:00 python3 ./manage.py qcluster

我已经尝试了execsu-exec 的一些变化,但我仍然遇到上述问题。我需要在启动时向 UWSGI 提供我的 PEM 密码,所以我一直在使用如下语法:

echo $PEM_PASSPHRASE | exec uwsgi --ini /opt/mysite/uwsgi.ini

这可以正常启动和运行,但我仍然让 PID 1 /bin/bash 进程运行,UWSGI 进程作为下面的子进程。我觉得我遗漏了一些明显的细节,无法通过exec 将 bash 进程替换为 UWSGI 进程。

不管怎样,我只在 Dockerfile 中使用 ENTRYPOINT,而不是 CMD:

ENTRYPOINT ["docker-entrypoint.sh"]

任何正确方向的指针将不胜感激。

【问题讨论】:

    标签: bash docker uwsgi docker-entrypoint


    【解决方案1】:

    管道中的Shell 命令——包括exec——在子shell 中运行。因此,echo ... | exec uwsgi ... 创建了两个子进程,一个运行 echo(实际上可能是一个运行内置 shell 的子shell),另一个子shell 立即将自己替换为 uwsgi

    我没有在 docker 中测试过这个,但是以下任何一个都应该可以工作:

    exec uwsgi --ini /opt/mysite/uwsgi.ini <<<"$PEM_PASSPHRASE"
    exec uwsgi --ini /opt/mysite/uwsgi.ini < <(echo "$PEM_PASSPHRASE")
    

    写完后,我突然想到,在 bash v4.3 及更高版本中,它实际上更容易,因为lastpipe shell 选项将告诉 bash 在当前 shell 中运行管道的最后一个元素,而不是子壳:

    shopt -s lastpipe
    echo "$PEM_PASSPHRASE" | exec uwsgi --ini /opt/mysite/uwsgi.ini
    

    但是,由于这是我们正在讨论的密码,因此存在有关这些密码可能会如何暴露密码的安全考虑。第一个选项(“here-string”)创建一个临时文件(在磁盘上!)存储密码,打开它进行输入,然后立即取消链接。这意味着它在任何正常的文件路径下都无法访问,但它会无限期地存储在磁盘上(并且不会被安全地删除)。可以物理访问计算机的人(或在某些系统上,直接通过 /proc 读取)。所以不太好。

    (这里的文档会做同样的事情。)

    第二个(从“进程替换”重定向)和第三个(lastpipe)可能会更好......或者更糟。在 bash 中,echo 是内置的,因此进程替换(&lt;( ) 部分)会创建一个子 shell,将 echo 内置运行到管道中......然后退出。哪个更好。但是在没有echo 内置的shell 中,它将运行一个单独的echo 进程,并且它的参数列表(即密码)实际上是公共信息(例如,通过ps 命令)。这可能更糟。

    所以我的建议是使用第二个或第三个,并确保你在 bash 下运行它。

    【讨论】:

    • 嘿,成功了!太感谢了!我用exec uwsgi --ini /opt/mysite/uwsgi.ini &lt; &lt;(echo "$PEM_PASSPHRASE") 替换了我的最后一个电话,这就成功了。现在 PID 1 是 uwsgi 进程之一,现在在正确的用户而不是 root 下运行。我永远不会把它放在一起来解决这个问题。非常感谢背景知识;正是我想要的。干杯!
    • 我很清楚:是不是在我的旧版本下,单个 echo 调用只是保留了一个 bash 子进程,所以它从未放弃 PID 被替换为exec 打电话?这是我在处理 Dockerfile/入口点脚本的不同排列时的印象。
    • @AaronBley 在旧版本中,PID 1 不是echo,它只是一个几乎无用的父shell,它分叉出两个子进程(一个运行echo,另一个运行exec uwsgiecho 子shell 执行其位并退出,但父shell 等待直到两个子进程完成。管道将强制其中的命令作为子进程运行,无论它们是什么。
    • @AaronBley 另外,我习惯了macOS,它带有bash v3.2;你可能有一个更新的版本,所以lastpipe shell 选项将可用......并且使修复这个微不足道。查看更新。
    • 非常感谢您提供的详细信息。这很有帮助!
    【解决方案2】:

    那么你的那个命令应该被放置为ENTRYPOINT,因为它的命令将是第一个进程。 (我假设您是从 docker-entrypoint.sh 内部调用它)

    ENTRYPOINT ["echo $PEM_PASSPHRASE | exec uwsgi --ini /opt/mysite/uwsgi.ini"]

    【讨论】:

    • 没错,但是在exec 调用之前,我在docker-entrypoint.sh 中发生了许多其他命令,所以我认为将它放在最后就足够了?我是否需要按照这些思路对其进行返工? Dockerfile:ENTRYPOINT ["docker-entrypoint.sh"]CMD "echo $PEM_PASSPHRASE | exec uwsgi --ini /opt/mysite/uwsgi.ini"docker-entrypoint.sh:其他逻辑,那么……exec "$@"
    • 第一个命令是主进程。我们不创造奇迹。将入口点设为 pid=1 有什么问题?
    • 我的印象是 PID 1 负责正确处理 SIGINT/SIGTERM/等。并确保这些信号传递给子进程。我宁愿让我正在运行的 UWSGI 进程来处理这个问题,而不是在容器启动时运行一次的一次性 bash 脚本。我想我总是可以尝试将 Tini 附加到容器上。无论如何,我尝试在没有echo 的情况下对其进行修改,只是为了看看我会得到什么,而ps 仍然将它们的入口点脚本显示为 PID 1。我将尝试添加 Tini。
    • 好的。但是(1)您可以在 bash 脚本中处理信号,并且(2)如果您发送一个信号来终止您的wannabe-main-process,它将退出并且入口点也将退出,因为这是它的最后一个命令。该进程还将向子进程发送其信号。所以,无论哪种方式,你都明白,对吧?
    猜你喜欢
    • 1970-01-01
    • 2020-04-28
    • 2015-11-22
    • 2016-12-29
    • 1970-01-01
    • 2015-11-27
    • 1970-01-01
    • 2011-05-27
    • 2017-07-13
    相关资源
    最近更新 更多