Eseguire automaticamente il bootstrap dei nodi GKE con DaemonSet

Questo tutorial mostra come personalizzare i nodi di un cluster Google Kubernetes Engine (GKE) utilizzando DaemonSets. Un DaemonSet contribuisce a garantire che tutti i nodi (o quelli selezionati) eseguano una copia di un pod. Quando vengono aggiunti nuovi nodi a un cluster, viene eseguito anche un pod da DaemonSet.

Se gli strumenti e i sistemi che utilizzi per inizializzare i cluster sono diversi da quelli che utilizzi per eseguire i workload, aumenti l'impegno necessario per gestire l'ambiente. Ad esempio, se utilizzi uno strumento di gestione della configurazione per inizializzare i nodi del cluster, ti affidi a una procedura esterna all'ambiente di runtime in cui vengono eseguiti gli altri carichi di lavoro. L'utilizzo di un DaemonSet ti consente di utilizzare gli stessi strumenti per orchestrare i tuoi carichi di lavoro che utilizzi per modificare i nodi GKE.

Lo scopo di questo tutorial è aiutare gli amministratori di sistema, gli ingegneri di sistema o gli operatori dell'infrastruttura a semplificare l'inizializzazione dei cluster Kubernetes.

Prima di leggere questa pagina, assicurati di avere familiarità con:

In questo tutorial imparerai a utilizzare le incompatibilità e le tolleranze di Kubernetes per assicurarti che i nodi vengano configurati da un DaemonSet prima che i workload delle applicazioni possano essere pianificati su di essi.

Obiettivi

In questo tutorial imparerai a:

  • Esegui il provisioning di un cluster GKE.
  • Contamina un pool di nodi per impedire la pianificazione del carico di lavoro prima di applicare la configurazione del nodo.
  • Esegui il deployment di un DaemonSet che configura i nodi e rimuove il taint.
  • Verifica che i nodi del cluster siano configurati e che il taint sia stato rimosso.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il calcolatore prezzi.

I nuovi utenti di Google Cloud potrebbero avere diritto a una prova senza costi.

Al termine delle attività descritte in questo documento, puoi evitare l'addebito di ulteriori costi eliminando le risorse che hai creato. Per saperne di più, consulta Esegui la pulizia.

Prima di iniziare

  1. Accedi al tuo account Google Cloud . Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti senza costi per l'esecuzione, il test e il deployment dei workload.
  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.

Implicazioni per la sicurezza di DaemonSet con privilegi

L'utilizzo dell'impostazione securityContext: privileged: true in un DaemonSet (o in qualsiasi pod) è potente, ma comporta implicazioni di sicurezza significative perché disattiva la maggior parte dei limiti di isolamento dei container per quel pod. Devi essere consapevole delle seguenti restrizioni e rischi per la sicurezza che introduce:

  • Container escape o compromissione dell'host:una vulnerabilità all'interno dell'applicazione o dell'immagine del container con privilegi può portare direttamente all'accesso root sul nodo host.
  • Violazione del principio del privilegio minimo: la modalità privilegiata concede tutte le funzionalità, probabilmente molte di più di quelle necessarie per un'attività specifica. Questo ampio accesso aumenta il potenziale danno se il container viene compromesso.
  • Destabilizzazione dei nodi:comandi accidentali o dannosi potrebbero essere eseguiti all'interno del container con privilegi, ad esempio valori sysctl errati o comandi come rm -rf /host/boot. Questi tipi di comandi possono causare l'arresto anomalo o il danneggiamento del sistema operativo del nodo host.
  • Movimento laterale: la compromissione di un nodo tramite un DaemonSet privilegiato offre a un utente malintenzionato un punto d'appoggio solido per attaccare altri nodi, il piano di controllo di Kubernetes o i sistemi connessi.
  • Esposizione dei dati:l'accesso senza restrizioni al file system host (/) può esporre dati sensibili archiviati sul nodo, incluse credenziali, chiavi o dati appartenenti ad altri pod se utilizzano volumi hostPath.
  • Maggiore superficie di attacco:la modalità con privilegi espone un maggior numero di chiamate e funzionalità del sistema del kernel host a potenziali exploit dall'interno del container.

Per evitare rischi per la sicurezza, tieni presente le seguenti best practice e mitigazioni:

  • Evita di utilizzare la modalità con privilegi:l'approccio più sicuro è evitare completamente l'impostazione privileged: true.
  • Utilizza le funzionalità di Linux:se sono necessari diritti elevati, puoi concedere funzionalità Linux specifiche come NET_ADMIN, SYS_ADMIN, SYS_MODULE nel campo securityContext.capabilities.add anziché privilegi completi. Questo approccio segue il principio del privilegio minimo, che consigliamo rispetto alla concessione di autorizzazioni generiche.
  • Limita l'ambito:esegui DaemonSet privilegiati solo su pool di nodi dedicati, possibilmente con taint, per contenere il potenziale impatto se un container viene compromesso.
  • Applica le policy:utilizza strumenti come Policy Controller o Gatekeeper per creare policy che limitano, controllano o richiedono una giustificazione per il deployment di container privilegiati.
  • Scansiona e utilizza immagini attendibili:utilizza Autorizzazione binaria e una rigorosa scansione delle immagini per garantire che vengano eseguite con privilegi elevati solo le immagini container verificate e attendibili.
  • Riduci al minimo i montaggi dell'host:monta solo i percorsi host specifici necessari e utilizza readOnly: true, se possibile. Evita di montare l'intero file system root (/).
  • Esegui controlli regolari:rivedi periodicamente tutti i carichi di lavoro in esecuzione con l'impostazione privileged: true.

Esegui il bootstrap dell'ambiente

In questa sezione, imparerai a:

  1. Abilita le API Cloud necessarie.
  2. Esegui il provisioning di un service account con privilegi limitati per i nodi nel cluster GKE.
  3. Prepara il cluster GKE.
  4. Concedi all'utente i privilegi di amministrazione del cluster.

Abilita Cloud APIs

  1. Apri Cloud Shell.

    APRI Cloud Shell

  2. Seleziona il progetto Google Cloud :

    gcloud config set project project-id
    

    Sostituisci project-id con l'ID del progettoGoogle Cloud che hai creato o selezionato per questo tutorial.

  3. Abilita l'API Kubernetes Engine:

    gcloud services enable container.googleapis.com
    

Provisiona un account di servizio per gestire i cluster GKE

In questa sezione crei un service account associato ai nodi del cluster. In questo tutorial, i nodi GKE utilizzano questo account di servizio anziché il service account predefinito. Come best practice, concedi al account di servizio solo i ruoli e le autorizzazioni di accesso necessari per eseguire l'applicazione.

I ruoli richiesti per il account di servizio sono i seguenti:

  • Ruolo Visualizzatore monitoraggio (roles/monitoring.viewer). Questo ruolo concede accesso di sola lettura ai dati di monitoraggio.
  • Ruolo Monitoring Metric Writer (roles/monitoring.metricWriter). Questo ruolo consente di scrivere dati di monitoraggio.
  • Ruolo Writer log (roles/logging.logWriter). Questo ruolo concede le autorizzazioni per scrivere i log.

Per eseguire il provisioning di un account di servizio:

  1. In Cloud Shell, inizializza una variabile di ambiente che memorizza il nome delaccount di serviziot:

    GKE_SERVICE_ACCOUNT_NAME=ds-init-tutorial-gke
    
  2. Crea un service account:

    gcloud iam service-accounts create "$GKE_SERVICE_ACCOUNT_NAME" \
      --display-name="$GKE_SERVICE_ACCOUNT_NAME"
    
  3. Inizializza una variabile di ambiente che memorizza il nome dell'account email del account di servizio:

    GKE_SERVICE_ACCOUNT_EMAIL="$(gcloud iam service-accounts list \
        --format='value(email)' \
        --filter=displayName:"$GKE_SERVICE_ACCOUNT_NAME")"
    
  4. Associa i ruoli Identity and Access Management (IAM) al account di servizio:

    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
    

Prepara il cluster GKE

In questa sezione, avvii il cluster GKE, concedi le autorizzazioni e completi la configurazione del cluster.

Per questo tutorial, un cluster con un numero relativamente basso di nodi piccoli e di uso generale è sufficiente per dimostrare il concetto. Crea un cluster con un node pool (quello predefinito).

  • In Cloud Shell, crea e avvia un cluster GKE regionale:

    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"
    

Applica le configurazioni dei nodi utilizzando un DaemonSet

In questa sezione, impedisci l'esecuzione dei carichi di lavoro sui nodi prima del completamento della configurazione applicando un taint al pool di nodi. Poi esegui il deployment di un DaemonSet che esegue le seguenti operazioni:

  1. Pianifica i pod sui nodi con incompatibilità utilizzando una tolleranza per l'incompatibilità.
  2. Esegue un container init con privilegi che applica prima la configurazione del nodo utilizzando sysctl, quindi rimuove il taint dal nodo utilizzando kubectl. La rimozione del taint rende il nodo pianificabile per i carichi di lavoro.
  3. Pianifica ed esegue un container di pausa che rimane inattivo e non consuma risorse per impedire a DaemonSet di riprogrammare il pod utilizzato per la configurazione.

Questo tutorial applica il parametro del kernel vm.max_map_count=262144 come configurazione di esempio.

  1. Applica un taint al pool di nodi predefinito:

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

    Con questa incompatibilità, solo i pod che la tollerano, come il pod DaemonSet, possono essere pianificati in questo pool di nodi.

  2. Verifica che il taint sia applicato:

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

    Lo stato del nodo dovrebbe mostrare node.config.status/stage=configuring:NoSchedule.

  3. Salva il seguente manifest come 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
    

    Questo manifest crea un service account, un ClusterRole e un ClusterRoleBinding per concedere al DaemonSet l'autorizzazione per rimuovere i taint dai nodi. DaemonSet esegue il deployment di un pod su ogni nodo che tollera l'incompatibilità configuring:NoSchedule. Questo pod esegue un container init con privilegi che applica la configurazione sysctl (vm.max_map_count=262144) e rimuove l'incompatibilità del nodo, rendendolo pianificabile. Viene quindi avviato un container di pausa per mantenere in esecuzione il pod.

    Il container init viene eseguito in modalità con privilegi, il che ha implicazioni per la sicurezza. Per maggiori dettagli, vedi Compromessi e limitazioni di sicurezza di Privileged DaemonSet.

  4. Applica il manifest:

    kubectl apply -f auto-untaint-daemonset.yaml
    
  5. Verifica che i pod DaemonSet siano stati creati e attendi che raggiungano lo stato Running:

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

    Lo stato Running indica che il container init è stato completato correttamente. Prendi nota del nome del pod in modo da poterlo utilizzare per verificare l'inizializzazione nella sezione successiva.

Convalida e verifica la procedura di inizializzazione

Una volta completata la configurazione del nodo, puoi verificare i risultati controllando i log.

  1. Controlla i log del container init di uno dei pod per visualizzarne l'output:

    kubectl logs POD_NAME -c configure-and-untaint
    

    Sostituisci POD_NAME con il nome del tuo pod.

    Dovresti visualizzare un output che indica la riuscita della configurazione e l'eliminazione del taint del nodo.

  2. Verifica che l'incompatibilità sia stata rimossa:

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

    Lo stato del nodo deve mostrare Taints: <none> o i taint con la chiave node.config.status/stage.

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, puoi eliminare il progetto che hai creato per il tutorial. Se hai creato un progetto dedicato a questo tutorial, puoi eliminarlo completamente. Se hai utilizzato un progetto esistente ma non vuoi eliminarlo, segui questi passaggi per pulire il progetto.

Liberare spazio nel progetto

Per liberare spazio in un progetto senza eliminarlo, devi rimuovere le risorse che hai creato in questo tutorial.

  1. In Cloud Shell, elimina il cluster GKE:

    gcloud container clusters delete ds-init-tutorial --quiet --region us-central1
    
  2. Elimina il account di servizio:

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

Elimina il progetto

Il modo più semplice per eliminare la fatturazione è eliminare il progetto creato per il tutorial.

  1. Nella console Google Cloud , vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona quello che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Passaggi successivi