使用 DaemonSet 自動啟動 GKE 節點

本教學課程說明如何使用 DaemonSets 自訂 Google Kubernetes Engine (GKE) 叢集的節點。DaemonSet 可確保所有 (或選定的) 節點都執行 Pod 副本。在叢集中新增節點時,這些節點也會執行 DaemonSet 中的 Pod。

如果您用來初始化叢集的工具和系統,與用來執行工作負載的工具和系統不同,管理環境的難度就會提高。舉例來說,如果您使用設定管理工具初始化叢集節點,您會依賴程序,而該程序位於其餘工作負載執行的執行階段環境之外。使用 DaemonSet 時,您可以使用與修改 GKE 節點相同的工具來協調工作負載。

本教學課程旨在協助系統管理員、系統工程師或基礎架構營運人員簡化 Kubernetes 叢集的初始化作業。

閱讀本頁面前,請確認您已熟悉下列事項:

在本教學課程中,您將瞭解如何使用 Kubernetes taint 和容許條件,確保節點是由 DaemonSet 設定,應用程式工作負載才能排定在節點上。

目標

在本教學課程中,您將執行下列操作:

  • 佈建 GKE 叢集。
  • 在套用節點設定前,先汙染節點集區,防止工作負載排程。
  • 部署 DaemonSet,設定節點並移除汙點。
  • 確認叢集節點已設定,且污點已移除。

費用

在本文件中,您會使用下列 Google Cloud計費元件:

您可以使用 Pricing Calculator,根據預測用量估算費用。

初次使用 Google Cloud 的使用者可能符合免費試用期資格。

完成本文所述工作後,您可以刪除建立的資源,避免繼續計費,詳情請參閱「清除所用資源」。

事前準備

  1. 登入 Google Cloud 帳戶。如果您未曾使用過 Google Cloud,歡迎 建立帳戶,親自體驗實際使用我們產品的成效。新客戶還能獲得價值 $300 美元的免費抵免額,能用於執行、測試及部署工作負載。
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

具備權限的 DaemonSet 安全性影響

在 DaemonSet (或任何 Pod) 中使用 securityContext: privileged: true 設定非常實用,但會帶來重大安全性影響,因為這會為該 Pod 停用大多數的容器隔離界線。請注意以下安全性限制和風險:

  • 容器跳脫或主機遭入侵:特殊權限容器應用程式或映像檔中的安全漏洞,可能會直接導致主機節點的根存取權。
  • 違反最小權限原則:特權模式會授予「所有」功能,可能遠超過特定工作所需。如果容器遭到入侵,這種廣泛的存取權可能會造成更大的損害。
  • 節點不穩定:在具備權限的容器中,可能會執行意外或惡意指令,例如不正確的 sysctl 值或 rm -rf /host/boot 等指令。這類指令可能會導致主機節點作業系統當機或損毀。
  • 橫向移動:透過具備特殊權限的 DaemonSet 入侵一個節點,攻擊者就能取得強大的立足點,攻擊其他節點、Kubernetes 控制層或連結的系統。
  • 資料曝光:無限制存取主機檔案系統 (/) 可能會曝光儲存在節點上的機密資料,包括憑證、金鑰,或屬於其他 Pod 的資料 (如果這些 Pod 使用 hostPath 卷宗)。
  • 攻擊面擴大:特權模式會公開更多主機核心系統呼叫和功能,讓容器內可能出現安全漏洞。

為避免安全風險,請留意下列最佳做法和緩解措施:

  • 避免使用特殊權限模式:最安全的做法是完全避免使用 privileged: true 設定。
  • 使用 Linux 功能:如需提高權限,您可以在 securityContext.capabilities.add 欄位中授予特定 Linux 功能,例如 NET_ADMIN、SYS_ADMIN、SYS_MODULE,而非完整權限。這個方法遵循最小權限原則,比授予廣泛權限更為推薦。
  • 限制範圍:只在專用 (可能已汙損) 的節點集區上執行具備權限的 DaemonSet,以防容器遭到入侵時造成影響。
  • 強制執行政策:使用 Policy Controller 或 Gatekeeper 等工具建立政策,限制、稽核或要求提供部署具備權限的容器的正當理由。
  • 掃描並使用受信任的映像檔:使用二進位授權和嚴格的映像檔掃描,確保只有經過審查、受信任的容器映像檔,才能以進階權限執行。
  • 減少主機掛接:只掛接必要的特定主機路徑,並盡可能使用 readOnly: true。請避免掛接整個根檔案系統 (/)。
  • 定期執行稽核:定期檢查所有以 privileged: true 設定執行的工作負載。

啟動環境

在本節中,執行以下操作:

  1. 啟用必要的 Cloud API。
  2. 為 GKE 叢集中的節點佈建權限受限的服務帳戶。
  3. 準備 GKE 叢集。
  4. 授予使用者叢集管理員權限。

啟用 Cloud API

  1. 開啟 Cloud Shell。

    開啟 Cloud Shell

  2. 選取 Google Cloud 專案:

    gcloud config set project project-id
    

    將 project-id 替換為您為本教學課程建立或選取的Google Cloud 專案 ID。

  3. 啟用 Kubernetes Engine API:

    gcloud services enable container.googleapis.com
    

佈建服務帳戶來管理 GKE 叢集

在本節中,您將建立與叢集節點相關聯的服務帳戶。在本教學課程中,GKE 節點會使用這個服務帳戶,而非預設服務帳戶。最佳做法是只授予服務帳戶執行應用程式所需的角色和存取權限。

服務帳戶所需的角色如下:

  • Monitoring 檢視者角色 (roles/monitoring.viewer)。這個角色具備監控資料的唯讀存取權。
  • Monitoring 指標寫入者角色 (roles/monitoring.metricWriter)。這個角色允許寫入監控資料。
  • 記錄寫入者角色 (roles/logging.logWriter):這個角色具備寫入記錄的權限。

如要佈建服務帳戶,請按照下列步驟操作:

  1. 在 Cloud Shell 中,初始化儲存服務帳戶名稱的環境變數:

    GKE_SERVICE_ACCOUNT_NAME=ds-init-tutorial-gke
    
  2. 建立服務帳戶:

    gcloud iam service-accounts create "$GKE_SERVICE_ACCOUNT_NAME" \
      --display-name="$GKE_SERVICE_ACCOUNT_NAME"
    
  3. 初始化環境變數,儲存服務帳戶電子郵件帳戶名稱:

    GKE_SERVICE_ACCOUNT_EMAIL="$(gcloud iam service-accounts list \
        --format='value(email)' \
        --filter=displayName:"$GKE_SERVICE_ACCOUNT_NAME")"
    
  4. 將 Identity and Access Management (IAM) 角色繫結至服務帳戶:

    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/monitoring.viewer
    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/monitoring.metricWriter
    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/logging.logWriter
    

準備 GKE 叢集

在本節中,您將啟動 GKE 叢集、授予權限並完成叢集設定。

在本教學課程中,只要叢集有相對較少的節點,且節點屬於小型一般用途,就足以說明教學課程的概念。您建立的叢集有一個節點集區 (預設集區)。

  • 在 Cloud Shell 中,建立並啟動區域 GKE 叢集:

    gcloud container clusters create ds-init-tutorial \
        --enable-ip-alias \
        --machine-type=n1-standard-2 \
        --metadata disable-legacy-endpoints=true \
        --node-labels=app=default-init \
        --node-locations us-central1-a,us-central1-b,us-central1-c \
        --no-enable-basic-auth \
        --no-issue-client-certificate \
        --num-nodes=1 \
        --location us-central1 \
        --service-account="$GKE_SERVICE_ACCOUNT_EMAIL"
    

使用 DaemonSet 套用節點設定

在本節中,您將對節點集區套用污點,防止工作負載在設定完成前於節點上執行。接著,您會部署 DaemonSet,執行下列作業:

  1. 使用 taint 的容許條件,在設有 taint 的節點上排程 Pod。
  2. 執行具特殊權限的 init 容器,先使用 sysctl 套用節點設定,再使用 kubectl 移除節點的污點。移除汙點後,節點就能排定工作負載。
  3. 排定並執行暫停容器,該容器會保持閒置狀態且不消耗任何資源,防止 DaemonSet 重新排定用於設定的 Pod。

本教學課程會以 vm.max_map_count=262144 核心參數為例,說明如何設定。

  1. 將汙點套用至預設節點集區:

    gcloud container node-pools update default-pool \
      --cluster=ds-init-tutorial \
      --node-taints=node.config.status/stage=configuring:NoSchedule \
      --region=us-central1
    

    有了這個 taint,只有容許該 taint 的 Pod (例如 DaemonSet Pod) 才能排定在這個節點集區中。

  2. 確認已套用污點:

    kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep Taints
    

    節點狀態應會顯示 node.config.status/stage=configuring:NoSchedule。

  3. 將下列資訊清單儲存為 auto-untaint-daemonset.yaml:

    # WARNING: This DaemonSet runs as privileged, which has significant
    # security implications. Only use this on clusters where you have
    # strict controls over what is deployed.
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: node-config-sa
      namespace: default
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: node-patcher-role
    rules:
    - apiGroups: [""]
      resources: ["nodes"]
      # Permissions needed to read and remove a taint from the node.
      verbs: ["get", "patch", "update"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: node-config-binding
    subjects:
    - kind: ServiceAccount
      name: node-config-sa
      namespace: default
    roleRef:
      kind: ClusterRole
      name: node-patcher-role
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: auto-untaint-daemonset
      labels:
        app: auto-untaint-configurator
    spec:
      selector:
        matchLabels:
          app: auto-untaint-configurator
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            app: auto-untaint-configurator
        spec:
          serviceAccountName: node-config-sa
          hostPID: true
          # Toleration now matches the taint on your node.
          tolerations:
          - key: "node.config.status/stage"
            operator: "Equal"
            value: "configuring"
            effect: "NoSchedule"
          volumes:
          - name: host-root-fs
            hostPath:
              path: /
          initContainers:
          - name: configure-and-untaint
            image: ubuntu:22.04 # Using a standard container image.
            securityContext:
              privileged: true # Required for chroot and sysctl.
            env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            volumeMounts:
            - name: host-root-fs
              mountPath: /host
            command: ["/bin/bash", "-c"]
            args:
            - |
              # Using explicit error checking for each critical command.
    
              # Define the configuration and taint details.
              SYSCTL_PARAM="vm.max_map_count"
              SYSCTL_VALUE="262144"
              TAINT_KEY="node.config.status/stage"
    
              echo "Running configuration on node: ${NODE_NAME}"
    
              # 1. APPLY CONFIGURATION
              echo "--> Applying ${SYSCTL_PARAM}=${SYSCTL_VALUE}..."
              if ! chroot /host sysctl -w "${SYSCTL_PARAM}=${SYSCTL_VALUE}"; then
                echo "ERROR: Failed to apply sysctl parameter." >&2
                exit 1
              fi
              echo "--> Configuration applied successfully."
    
              # 2. UNTAINT THE NODE
              # This command removes the taint from the node this Pod is running on.
              echo "--> Untainting node ${NODE_NAME} by removing taint ${TAINT_KEY}..."
              if ! /host/home/kubernetes/bin/kubectl taint node "${NODE_NAME}" "${TAINT_KEY}:NoSchedule-"; then
                echo "ERROR: Failed to untaint the node." >&2
                exit 1
              fi
              echo "--> Node has been untainted and is now schedulable."
          # The main container is minimal; it just keeps the Pod running.
          containers:
          - name: pause-container
            image: registry.k8s.io/pause:3.9
    

    這個資訊清單會建立 ServiceAccount、ClusterRole 和 ClusterRoleBinding,授予 DaemonSet 權限,以便從節點移除污點。DaemonSet 會將 Pod 部署至可容許 configuring:NoSchedule 污點的每個節點。這個 Pod 會執行特殊權限 init 容器,套用 sysctl 設定 (vm.max_map_count=262144) 並移除節點污點,讓節點可排程。接著,暫停容器會啟動,讓 Pod 保持運作。

    init 容器會以特殊權限模式執行,這會影響安全性。詳情請參閱「Privileged DaemonSet tradeoffs and security restrictions」(Privileged DaemonSet 的取捨和安全性限制)。

  4. 套用資訊清單:

    kubectl apply -f auto-untaint-daemonset.yaml
    
  5. 確認 DaemonSet Pod 已建立,並等待 Pod 達到 Running 狀態:

    kubectl get pods -l app=auto-untaint-configurator -o wide
    

    Running 狀態表示 init 容器已成功完成。請記下 Pod 名稱,以便在下一節中驗證初始化作業。

驗證及確認初始化程序

節點設定完成後,您可以查看記錄來驗證結果。

  1. 檢查其中一個 Pod 的 init 容器記錄,查看輸出內容:

    kubectl logs POD_NAME -c configure-and-untaint
    

    將 POD_NAME 替換為 Pod 名稱。

    輸出內容應該會指出設定成功,且節點已取消 taint。

  2. 確認污點已移除:

    kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep Taints
    

    節點狀態應會顯示 Taints: <none>,或顯示具有鍵 node.config.status/stage 的汙點。

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程所用資源的費用,請刪除您為本教學課程建立的專案。如果您是為了這個教學課程建立專案,可以完全刪除專案。如果您使用的是現有專案,但不想刪除專案,請按照下列步驟清除專案。

清理專案

如要清除專案而不刪除,請移除您在本教學課程中建立的資源。

  1. 在 Cloud Shell 中刪除 GKE 叢集:

    gcloud container clusters delete ds-init-tutorial --quiet --region us-central1
    
  2. 刪除服務帳戶:

    gcloud iam service-accounts delete "$GKE_SERVICE_ACCOUNT_EMAIL" --quiet
    

刪除專案

如要避免系統向您收費,最簡單的方法就是刪除您在教學課程中建立的專案。

  1. 前往 Google Cloud 控制台的「Manage resources」(管理資源) 頁面。

    前往「Manage resources」(管理資源)

  2. 在專案清單中選取要刪除的專案,然後點選「Delete」(刪除)。
  3. 在對話方塊中輸入專案 ID,然後按一下 [Shut down] (關閉) 以刪除專案。

後續步驟