使用 DaemonSet 自動啟動 GKE 節點

本教學課程說明如何使用 DaemonSets 自訂 Google Kubernetes Engine (GKE) 叢集的節點。DaemonSet 可確保所有 (或選定) 節點都執行 Pod 副本。這種做法可讓您使用相同的工具來協調工作負載,就像修改 GKE 節點一樣。

如果您用來初始化叢集的工具和系統,與用來執行工作負載的工具和系統不同,管理環境所需的工作量就會增加。舉例來說,如果您使用設定管理工具初始化叢集節點,您會依賴程序,而該程序位於執行其餘工作負載的執行階段環境之外。

本教學課程的目標是協助系統管理員、系統工程師或基礎架構操作員,簡化 Kubernetes 叢集的初始化作業。

閱讀本頁面之前,請先熟悉下列概念:

在本教學課程中,您將瞭解如何使用 Kubernetes 標籤和選取器,根據套用至節點的標籤選擇要執行的初始化程序。在這些步驟中,您會部署 DaemonSet,只在套用 default-init 標籤的節點上執行。不過,為了展示這項機制的彈性,您可以建立另一個節點集區,並將 alternative-init 標籤套用至這個新集區中的節點。然後在叢集中部署另一個 DaemonSet,並設定為只在具有 alternative-init 標籤的節點上執行。

此外,您可以在每個節點上執行多個初始化程序,而不只是單一程序。您可以運用這項機制,更妥善地建構初始化程序,清楚區分各程序的關注事項。

在本教學課程中,初始化程序會對標示 default-init 標籤的每個節點執行下列動作:

  1. 將額外磁碟連接至節點。
  2. 使用節點的作業系統套件管理員安裝一組套件和程式庫。
  3. 載入一組 Linux 核心模組。

啟動環境

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

  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. 啟用 Google Kubernetes Engine API:

    gcloud services enable container.googleapis.com
    

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

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

服務帳戶必須擁有以下角色:

  • Monitoring 檢視者角色 (roles/monitoring.viewer)。這個角色提供 Cloud Monitoring 主控台和 API 的唯讀存取權。
  • Monitoring 指標寫入者角色 (roles/monitoring.metricWriter)。這個角色允許寫入監控資料。
  • 記錄寫入者角色 (roles/logging.logWriter)。這個角色只會授予寫入記錄的足夠權限。
  • 服務帳戶使用者角色 (roles/iam.serviceAccountUser)。這個角色可授予專案中服務帳戶的存取權。在本教學課程中,初始化程序會模擬服務帳戶,以執行具備權限的操作。
  • Compute 管理員角色 (roles/compute.admin)。這個角色提供所有 Compute Engine 資源的完整控制權。在本教學課程中,服務帳戶需要這個角色,才能將額外磁碟附加至叢集節點。

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

  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/compute.admin
    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
    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/iam.serviceAccountUser
    

準備 GKE 叢集

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

在本教學課程中,叢集只要有相對較少的節點,且節點屬於小型一般用途,就足以說明本教學課程的概念。您建立的叢集會包含一個節點集區 (預設節點集區)。然後使用 default-init 標籤,為預設節點集區中的所有節點加上標籤。

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

    gcloud container clusters create ds-init-tutorial \
        --enable-ip-alias \
        --image-type=ubuntu_containerd \
        --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

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

  1. 建立儲存初始化程序的 ConfigMap
  2. 部署 DaemonSet,排定並執行初始化程序。

DaemonSet 會執行下列作業:

  1. 設定磁碟區,讓 DaemonSet 處理的容器可存取 ConfigMap 的內容。
  2. 設定基礎叢集節點的受限檔案系統區域磁碟區。這些區域可讓 DaemonSet 排程的容器直接與執行這些容器的節點互動。
  3. 排定及執行初始化容器,該容器會執行初始化程序,完成後即終止。
  4. 排定及執行閒置容器,不會耗用任何資源。

閒置容器可確保節點只會初始化一次。DaemonSet 的設計宗旨是讓所有符合資格的節點執行 Pod 副本。如果您使用一般容器,該容器會執行初始化程序,完成後就會終止。根據設計,DaemonSet 會重新排定 Pod 的時間。為避免「持續重新排程」,DaemonSet 會先在 init 容器中執行初始化程序,然後讓容器保持執行狀態。

下列初始化程序包含具備和不具備權限的操作。使用 chroot 時,您可以執行指令,就像直接在節點上執行指令一樣,而不只是在容器內執行。

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: entrypoint
  labels:
    app: default-init
data:
  entrypoint.sh: |
    #!/usr/bin/env bash

    set -euo pipefail

    DEBIAN_FRONTEND=noninteractive
    ROOT_MOUNT_DIR="${ROOT_MOUNT_DIR:-/root}"

    echo "Installing dependencies"
    apt-get update
    apt-get install -y apt-transport-https curl gnupg lsb-release

    echo "Installing gcloud SDK"
    export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)"
    echo "deb https://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
    apt-get update
    apt-get install -y google-cloud-sdk

    echo "Getting node metadata"
    NODE_NAME="$(curl -sS http://metadata.google.internal/computeMetadata/v1/instance/name -H 'Metadata-Flavor: Google')"
    ZONE="$(curl -sS http://metadata.google.internal/computeMetadata/v1/instance/zone -H 'Metadata-Flavor: Google' | awk -F  "/" '{print $4}')"

    echo "Setting up disks"
    DISK_NAME="$NODE_NAME-additional"

    if ! gcloud compute disks list --filter="name:$DISK_NAME" | grep "$DISK_NAME" > /dev/null; then
        echo "Creating $DISK_NAME"
        gcloud compute disks create "$DISK_NAME" --size=1024 --zone="$ZONE"
    else
        echo "$DISK_NAME already exists"
    fi

    if ! gcloud compute instances describe "$NODE_NAME" --zone "$ZONE" --format '(disks[].source)' | grep "$DISK_NAME" > /dev/null; then
        echo "Attaching $DISK_NAME to $NODE_NAME"
        gcloud compute instances attach-disk "$NODE_NAME" --device-name=sdb --disk "$DISK_NAME" --zone "$ZONE"
    else
        echo "$DISK_NAME is already attached to $NODE_NAME"
    fi

    # We use chroot to run the following commands in the host root (mounted as the /root volume in the container)
    echo "Installing nano"
    chroot "${ROOT_MOUNT_DIR}" apt-get update
    chroot "${ROOT_MOUNT_DIR}" apt-get install -y nano

    echo "Loading Kernel modules"
    # Load the bridge kernel module as an example
    chroot "${ROOT_MOUNT_DIR}" modprobe bridge
...

建議您仔細檢查每個初始化程序,因為程序可能會變更叢集節點的狀態。只有少數人有權修改這些程序,因為這些程序會大幅影響叢集的可用性和安全性。

保護這個專屬命名空間中的資源。

如要部署 ConfigMap 和 DaemonSet,請執行下列步驟:

  1. 在 Cloud Shell 中,將工作目錄變更為 $HOME 目錄:

    cd "$HOME"
    
  2. 複製 Git 存放區,當中包含用於部署及設定初始化程序的指令碼和資訊清單檔案:

    git clone https://github.com/GoogleCloudPlatform/solutions-gke-init-daemonsets-tutorial
    
  3. 將工作目錄變更為新複製的存放區目錄:

    cd "$HOME"/solutions-gke-init-daemonsets-tutorial
    
  4. 建立 ConfigMap 以保存節點初始化指令碼:

    kubectl apply -f cm-entrypoint.yaml
    
  5. 部署 DaemonSet:

    kubectl apply -f daemon-set.yaml
    
  6. 確認已完成節點初始化:

    kubectl get ds --watch
    

    等待 DaemonSet 已備妥且為最新版本的通知,其輸出如下:

    NAME              DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
    node-initializer   3         3         3         3            3          <none>  2h
    

驗證並確認初始化程序

標示 default-init 標籤的叢集節點執行初始化程序後,您就可以驗證結果。

驗證程序會檢查每個節點是否符合下列條件:

  1. 已連接其他磁碟,隨時可供使用。
  2. 節點的作業系統套件管理員安裝的套件和程式庫。
  3. 已載入核心模組。

執行驗證程序:

  • 在 Cloud Shell 中執行驗證指令碼:

    kubectl get nodes -o=jsonpath='{range .items[?(@.metadata.labels.app=="default-init")]}{.metadata.name}{" "}{.metadata.labels.failure-domain\.beta\.kubernetes\.io/zone}{"\n"}{end}' | while IFS= read -r line ; do ./verify-init.sh $line < /dev/null; done
    

    等待指令碼執行完畢,並確認每個節點都已正確初始化,如以下輸出內容所示:

    Verifying gke-ds-init-tutorial-default-pool-5464b7e3-nzjm (us-central1-c) configuration
    Disk configured successfully on gke-ds-init-tutorial-default-pool-5464b7e3-nzjm (us-central1-c)
    Packages installed successfully in gke-ds-init-tutorial-default-pool-5464b7e3-nzjm (us-central1-c)
    Kernel modules loaded successfully on gke-ds-init-tutorial-default-pool-5464b7e3-nzjm (us-central1-c)
    Verifying gke-ds-init-tutorial-default-pool-65baf745-0gwt (us-central1-a) configuration
    Disk configured successfully on gke-ds-init-tutorial-default-pool-65baf745-0gwt (us-central1-a)
    Packages installed successfully in gke-ds-init-tutorial-default-pool-65baf745-0gwt (us-central1-a)
    Kernel modules loaded successfully on gke-ds-init-tutorial-default-pool-65baf745-0gwt (us-central1-a)
    Verifying gke-ds-init-tutorial-default-pool-6b125c50-3xvl (us-central1-b) configuration
    Disk configured successfully on gke-ds-init-tutorial-default-pool-6b125c50-3xvl (us-central1-b)
    Packages installed successfully in gke-ds-init-tutorial-default-pool-6b125c50-3xvl (us-central1-b)
    Kernel modules loaded successfully on gke-ds-init-tutorial-default-pool-6b125c50-3xvl (us-central1-b)