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

Running as a host user within a Docker container

在我的团队中,我们使用 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

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

在上面的示例中,应用程序最终 运行ning 作为 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 不存在 用户,但写入的任何文件到主机目录最终由 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'

有谁知道我有什么办法可以:

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

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

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 收到命令)

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

来自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]

仅当设置了以下环境变量中的 none 时才会调用 getpwuid()LOGNAMEUSERLNAMEUSERNAME

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

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

在我的例子中,对 getuser() 的调用似乎来自试图生成调试器 pin 代码的 Werkzeug 库。

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