🚀 在 VS Code 中

在容器中新增非 root 使用者

許多 Docker 映像檔使用 root 作為預設使用者,但在某些情況下,您可能偏好改用非 root 使用者。如果您這樣做,有些關於本機檔案系統 (綁定) 掛載的 怪異之處 您應該了解。具體而言

  • Docker Desktop for Mac:在容器內,任何掛載的檔案/資料夾都會表現得像是它們由您指定的容器使用者所擁有。在本機端,所有檔案系統操作都將改為使用您本機使用者的權限。

  • Docker Desktop for Windows:在容器內,任何掛載的檔案/資料夾都會顯示得像是它們由 root 擁有,但您指定的使用者仍然能夠讀取/寫入它們,且所有檔案都將是可執行的。在本機端,所有檔案系統操作都將改為使用您本機使用者的權限。這是因為從根本上來說,沒有辦法直接將 Windows 樣式的檔案權限對應到 Linux。

  • Linux 上的 Docker CE/EE:在容器內,任何掛載的檔案/資料夾都將擁有與容器外部完全相同的權限 - 包括擁有者使用者 ID (UID) 和群組 ID (GID)。因此,您的容器使用者需要擁有相同的 UID,或屬於具有相同 GID 的群組。使用者/群組的實際名稱並不重要。機器上的第一個使用者通常會取得 UID 1000,因此大多數容器都使用此 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 都會包含 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 使用者,您可以直接加以利用。

在任一種情況下,如果您已經建置容器並連線到它,請從命令選 palette (F1) 執行 Dev Containers: Rebuild Container 以套用變更。否則,請執行 Dev Containers: Open Folder in Container... 以連線到容器。

變更現有容器使用者的 UID/GID

雖然在使用 Dockerfile 或映像檔 時,remoteUser 屬性會嘗試在 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