参加你附近的 ,了解 VS Code 中的 AI 辅助开发。

将非 root 用户添加到容器

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

  • 适用于 Mac 的 Docker Desktop:在容器内部,任何挂载的文件/文件夹都将表现为由你指定的容器用户拥有。在本地,所有文件系统操作都将使用你的本地用户的权限。

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

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

为 VS Code 指定用户

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

"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 都将包含一个 UID/GID 为 1000 的非 root 用户(通常称为 vscodenode),但许多基础镜像和 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