【问题标题】:Running as a host user within a Docker container在 Docker 容器中以主机用户身份运行
【发布时间】:2017-08-23 09:47:06
【问题描述】:

在我的团队中,我们使用 Docker 容器在本地运行我们的网站应用程序,同时对它们进行开发。

假设我在 app.py 开发一个 Flask 应用程序,并且依赖于 requirements.txt,工作流程大致如下所示:

# I am "robin" and I am in the docker group
$ whoami
robin
$ groups
robin docker

# Install dependencies into a docker volume
$ docker run -ti -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local python:3-slim pip install -r requirements.txt
Collecting Flask==0.12.2 (from -r requirements.txt (line 1))
# ... etc.

# Run the app using the same docker volume
$ docker run -ti -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -e FLASK_APP=app.py -e FLASK_DEBUG=true -p 5000:5000 python:3-slim flask run -h 0.0.0.0
 * Serving Flask app "app"
 * Forcing debug mode on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 251-131-649

现在我们有一个本地服务器运行我们的应用程序,我们可以对本地文件进行更改,服务器将根据需要刷新。

在上面的示例中,应用程序最终以root 用户身份运行。除非应用程序将文件写回工作目录,否则这不是问题。如果是这样,那么我们最终可能会在root 拥有的工作目录中找到文件(例如cache.sqlitedebug.log)。这给我们团队的用户带来了许多问题。

对于我们的其他应用程序,我们通过使用 主机用户的 UID 和 GID 运行应用程序解决了这个问题 - 例如对于 Django 应用程序:

$ docker run -ti -u `id -u`:`id -g` -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -p 8000:8000 python:3-slim ./manage.py runserver

在这种情况下,应用程序将在容器内以 ID 1000不存在用户身份运行,但写入主机目录的任何文件最终都归 @987654330 正确拥有@用户。这在 Django 中运行良好。

但是,Flask 拒绝以不存在的用户身份运行(在调试模式下):

$ docker run -ti -u `id -u`:`id -g` -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -e FLASK_APP=app.py -e FLASK_DEBUG=true -p 5000:5000 python:3-slim flask run -h 0.0.0.0
 * Serving Flask app "app"
 * Forcing debug mode on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
Traceback (most recent call last):
...
  File "/usr/local/lib/python3.6/getpass.py", line 169, in getuser
    return pwd.getpwuid(os.getuid())[0]
KeyError: 'getpwuid(): uid not found: 1000'

有没有人知道我有什么办法:

  • 让 Flask 不用担心未分配的用户 ID,或者
  • 以某种方式在运行时将用户 ID 动态分配给用户名,或者
  • 否则是否允许 docker 应用程序以主机用户身份在主机上创建文件?

我现在唯一能想到的解决方案(超级hacky)是将docker镜像中/etc/passwd的权限更改为全局可写,然后在运行时向该文件添加新行以分配新的UID /GID 对到用户名。

【问题讨论】:

    标签: linux docker flask development-environment sudo


    【解决方案1】:

    您可以共享主机的密码文件:

    docker run -ti -v /etc/passwd:/etc/passwd -u `id -u`:`id -g` -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -p 8000:8000 python:3-slim ./manage.py runserver
    

    或者,将用户添加到带有useradd 的图像中,使用/etc 作为音量,就像使用/usr/local 一样:

    docker run -v etcvol:/etc python..... useradd -u `id -u` $USER
    

    id -u 和 $USER 都在主机 shell 中解析,在 docker 接收命令之前)

    【讨论】:

    • 我会在 13 小时内奖励你赏金
    • 实际上我想我会使用你的第二个建议,因为它更便携 - 例如将适用于 macOS 系统。
    • 很高兴知道它很有用!
    • 建议的技巧假定数字用户 ID(图像使用的用户 ID 和用于运行图像的用户 ID)与主机 /etc/passwd 中的用户 ID 一致。如果他们不这样做(正如我通常所期望的那样),那么解决方法通常也不安全。我们应该了解作为同一用户运行的吸引力,以及如何通过抽象出用户名来内化这种冲动。
    • 忽略用户名是从它们中抽象出来的良好的第一步:docker run -v "${HOME}:/root" -w "/root" SERVER/IMAGE:VERSION env HOME="/root" COMMAND。但是,这需要在运行后进行清理,因为 docker 守护进程会将root 写入${HOME}
    【解决方案2】:

    刚刚遇到这个问题并找到了不同的解决方法。

    来自getpass.py

    def getuser():
        """Get the username from the environment or password database.
    
        First try various environment variables, then the password
        database.  This works on Windows as long as USERNAME is set.
    
        """
    
    
        for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
            user = os.environ.get(name)
            if user:
                return user
    
    
        # If this fails, the exception will "explain" why
        import pwd
        return pwd.getpwuid(os.getuid())[0]
    

    只有在没有设置以下环境变量时才会调用getpwuid()LOGNAMEUSERLNAMEUSERNAME

    设置其中任何一个都应该允许容器启动。

    $ docker run -ti -e USER=someuser ...
    

    就我而言,对getuser() 的调用似乎来自Werkzeug 库,试图生成调试器pin 代码。

    【讨论】:

    • 谢谢。这几乎可以肯定是最“正确”的回应。尤其是当您查看 Werkzeug 实际使用用户名的用途时,这是极其微不足道的,并且完全不依赖于“正确”的值。读者应避免绑定挂载/etc/passwd 或在 Docker 容器内创建用户。这些都是黑客解决方案。
    【解决方案3】:

    如果您可以使用另一个 Python 包来启动您的容器,也许您想使用我的 Python 包https://github.com/boon-code/docker-inside,它会覆盖入口点并在运行中在容器中创建您的用户...

    docker-inside -v `pwd`:`pwd` -w `pwd` -v pydeps:/usr/local -e FLASK_APP=app.py -e FLASK_DEBUG=true -p 5000:5000 python:3-slim -- flask run -h 0.0.0.0
    

    如果您想坚持使用 Docker CLI,在命令行上覆盖入口点并传递创建用户的脚本也可能对您没问题。

    【讨论】:

      猜你喜欢
      • 2019-04-26
      • 2018-06-18
      • 1970-01-01
      • 2019-06-24
      • 2018-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-14
      相关资源
      最近更新 更多