使用 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 功能:如果需要提升權限,您可以授予特定 Linux 功能 (例如 NET_ADMINSYS_ADMINSYS_MODULE) 的權限,而不必授予完整權限。請在 securityContext.capabilities.add 欄位中輸入這些功能。這個方法遵循最小權限原則,我們建議您採用這個方法,而非授予廣泛權限。
  • 限制範圍:只在專用 (可能已汙損) 的節點集區上執行具備權限的 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 的容許條件,在遭汙染的節點上排定 Pod。
  2. 執行具備權限的 init 容器,先使用 sysctl 套用節點設定,然後使用 kubectl 從節點移除汙點。移除汙點後,節點就能排定工作負載。
  3. 排定並執行閒置的暫停容器,不會耗用任何資源,避免 DaemonSet 重新排定用於設定的 Pod。

本教學課程會套用 vm.max_map_count=262144 核心參數做為設定範例。

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

    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 taint 的每個節點。這個 Pod 會執行特殊權限 init 容器,套用 sysctl 設定 (vm.max_map_count=262144) 並移除節點 taint,讓節點可供排程。接著,暫停容器會啟動,讓 Pod 保持運作。

    初始化容器會在特權模式中執行,這會造成安全疑慮。 詳情請參閱「Privileged DaemonSet tradeoffs and security restrictions」。

  4. 套用資訊清單:

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

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

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

驗證並確認初始化程序

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

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

    kubectl logs POD_NAME -c configure-and-untaint
    

    POD_NAME 替換為 Pod 名稱。

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

  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] (關閉) 以刪除專案。

後續步驟