在Docker容器中作为主机用户运行

时间:2017-08-23 09:47:07

标签: linux docker flask development-environment sudo

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

假设我正在使用app.py中的依赖项requirements.txt处理Flask应用,那么工作流程看起来大致如下:

# 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用户身份运行。除非应用程序将文件写回工作目录,否则这不是问题。如果确实如此,那么我们可能会在cache.sqlite拥有的工作目录中找到文件(例如debug.logroot)。这给我们团队中的用户带来了许多问题。

对于我们的其他应用程序,我们通过使用主机用户的 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不存在的用户运行,但写入主机目录的所有文件最终都由robin用户。这在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对用户名。

3 个答案:

答案 0 :(得分:21)

您可以共享主机的passwd文件:

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

(在docker收到命令之前,id -u和$ USER都在宿主shell中解析了)

答案 1 :(得分:1)

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

来自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库。

答案 2 :(得分:0)

如果你可以使用另一个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,那么在命令行上覆盖入口点并传递创建用户的脚本也可能适合您。