现已推出!阅读 10 月份的新功能和修复。

在容器中添加非root用户

许多 Docker 镜像使用 root 作为默认用户,但在某些情况下,您可能更喜欢使用非root用户。如果您这样做,则需要了解有关 **本地文件系统(绑定)挂载** 的一些 **特殊情况**。具体来说:

  • **Docker Desktop for Mac**:在容器内部,任何挂载的文件/文件夹都将以您指定的容器用户的方式进行操作。在本地,所有文件系统操作都将使用您本地用户的权限。

  • **Docker Desktop for Windows**:在容器内部,任何挂载的文件/文件夹都将显示为由 `root` 拥有,但您指定的 用户仍然可以读取/写入它们,并且所有文件都将可执行。在本地,所有文件系统操作都将使用您本地用户的权限。这是因为从根本上讲,无法将 Windows 式的文件权限直接映射到 Linux。

  • **Docker CE/EE on Linux**:在容器内部,任何挂载的文件/文件夹将具有与容器外部完全相同的权限 - 包括所有者用户 ID (UID) 和组 ID (GID)。因此,您的容器用户需要拥有相同的 UID 或属于具有相同 GID 的组。用户的实际名称/组名称并不重要。机器上的第一个用户通常会获得 1000 的 UID,因此大多数容器使用它作为用户的 ID 以避免此问题。

指定 VS Code 用户

如果您使用的镜像或 Dockerfile **已经提供了可选的非root用户**(例如 `node` 镜像),但仍默认为 root,您可以选择让 Visual Studio Code(服务器)和任何子进程(终端、任务、调试)使用它,方法是在 `devcontainer.json` 中指定 `remoteUser` 属性。

"remoteUser": "user-name-goes-here"

在 Linux 上,如果您在 `devcontainer.json` 中引用 **Dockerfile、镜像或 Docker Compose**,这也会自动将容器用户的 UID/GID 更新为与您的本地用户匹配,以避免此环境中存在的绑定挂载权限问题(除非您设置 `“updateRemoteUserUID”:false`)。

由于此设置仅影响 VS Code 和相关的子进程,因此需要重新启动 VS Code(或重新加载窗口)才能生效。但是,UID/GID 更新仅在创建容器时应用,并且需要重建才能更改。

指定默认容器用户

在某些情况下,您可能需要容器中的所有进程都以不同的用户运行(例如,由于启动要求),而不仅仅是 VS Code。如何执行此操作略有不同,具体取决于您是否正在使用 Docker Compose。

  • **Dockerfile 和镜像**:将 `containerUser` 属性添加到同一个文件。

    "containerUser": "user-name-goes-here"
    

    在 Linux 上,与 `remoteUser` 一样,这也会自动将容器用户的 UID/GID 更新为与您的本地用户匹配,以避免此环境中存在的绑定挂载权限问题(除非您设置 `“updateRemoteUserUID”:false`)。

  • **Docker Compose**:更新(或 扩展)您的 `docker-compose.yml`,为相应的服务添加以下内容

    user: user-name-or-UID-goes-here
    

创建非root用户

虽然来自 Dev Containers 扩展的任何镜像或 Dockerfile 都将包含一个具有 1000 的 UID/GID 的非root用户(通常称为 `vscode` 或 `node`),但许多基本镜像和 Dockerfile 却没有。幸运的是,您可以更新或创建 Dockerfile 将非root用户添加到您的容器中。

即使在生产环境中,也建议将您的应用程序作为非root用户运行(因为这更安全),所以即使您要重用现有的 Dockerfile,这也是一个好主意。例如,这个用于 Debian/Ubuntu 容器的代码段将创建一个名为 `user-name-goes-here` 的用户,赋予它使用 `sudo` 的权限,并将其设置为默认用户

ARG USERNAME=user-name-goes-here
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Create the user
RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
    #
    # [Optional] Add sudo support. Omit if you don't need to install software after connecting.
    && apt-get update \
    && apt-get install -y sudo \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME

# ********************************************************
# * Anything else you want to do like clean up goes here *
# ********************************************************

# [Optional] Set the default user. Omit if you want to keep the default as root.
USER $USERNAME

**提示:**如果您在构建时遇到有关 GID 或 UID 已经存在的错误,那么您选择的镜像可能已经有一个非root用户,您可以直接利用它。

无论哪种情况,如果您已经构建了容器并连接到它,请从命令面板 (F1) 中运行 **Dev Containers: Rebuild Container** 以获取更改。否则,运行 **Dev Containers: Open Folder in Container...** 以连接到容器。

更改现有容器用户的 UID/GID

虽然 `remoteUser` 属性在使用 **Dockerfile 或镜像** 时会尝试在 Linux 上自动更新 UID/GID,但您可以在 Dockerfile 中使用此代码段手动更改用户的 UID/GID。根据需要更新 `ARG` 值。

ARG USERNAME=user-name-goes-here
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN groupmod --gid $USER_GID $USERNAME \
    && usermod --uid $USER_UID --gid $USER_GID $USERNAME \
    && chown -R $USER_UID:$USER_GID /home/$USERNAME

请注意,在 Alpine Linux 上,您需要先安装 `shadow` 包。

RUN apk add --no-cache shadow