Inicia automáticamente los nodos de GKE con DaemonSets.

En este instructivo, se muestra cómo personalizar los nodos de un clúster de Google Kubernetes Engine (GKE) mediante DaemonSets. Un DaemonSet ayuda a garantizar que todos los nodos (o los nodos seleccionados) ejecuten una copia de un Pod. Cuando se agregan nodos nuevos a un clúster, también ejecutan un Pod del DaemonSet.

Si las herramientas y los sistemas que usas cuando inicializas los clústeres son diferentes a las herramientas y a los sistemas que usas a fin de ejecutar las cargas de trabajo, aumenta el esfuerzo que se necesita para administrar el entorno. Por ejemplo, si usas una herramienta de administración de la configuración para inicializar los nodos del clúster, dependes de un procedimiento que está fuera del entorno de ejecución en el que se ejecuta el resto de las cargas de trabajo. El uso de un DaemonSet te permite usar las mismas herramientas para organizar tus cargas de trabajo que usas para modificar tus nodos de GKE.

El objetivo de este instructivo es ayudar a los administradores de sistemas, los ingenieros de sistemas o los operadores de infraestructura a optimizar la inicialización de los clústeres de Kubernetes.

Antes de leer esta página, asegúrate de estar familiarizado con los siguientes temas:

En este instructivo, aprenderás a usar taints y tolerancias de Kubernetes para garantizar que un DaemonSet configure los nodos antes de que se puedan programar cargas de trabajo de la aplicación en ellos.

Objetivos

En este instructivo, harás lo que se indica a continuación:

  • Aprovisiona un clúster de GKE.
  • Aplica un taint a un grupo de nodos para evitar la programación de cargas de trabajo antes de aplicar la configuración del nodo.
  • Implementa un DaemonSet que configure los nodos y quite el taint.
  • Verifica que los nodos del clúster estén configurados y que se haya quitado el aislamiento.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios.

Es posible que los usuarios de Google Cloud nuevos cumplan con los requisitos para acceder a una prueba gratuita.

Cuando completes las tareas que se describen en este documento, podrás borrar los recursos que creaste para evitar que se te siga facturando. Para obtener más información, consulta Realiza una limpieza.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud . Si es la primera vez que usas Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  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.

Implicaciones de seguridad de DaemonSet con privilegios

Usar el parámetro de configuración securityContext: privileged: true en un DaemonSet (o cualquier Pod) es potente, pero tiene implicaciones de seguridad significativas porque inhabilita la mayoría de los límites de aislamiento del contenedor para ese Pod. Debes tener en cuenta las siguientes restricciones de seguridad y los riesgos que introduce:

  • Escape del contenedor o vulneración del host: Una vulnerabilidad dentro de la aplicación o imagen del contenedor con privilegios puede llevar directamente al acceso raíz en el nodo host.
  • Incumplimiento del principio de privilegio mínimo: El modo con privilegios otorga todas las capacidades, probablemente muchas más de las necesarias para una tarea específica. Este acceso amplio aumenta el daño potencial si se vulnera el contenedor.
  • Desestabilización del nodo: Se podrían ejecutar comandos accidentales o maliciosos dentro del contenedor privilegiado, por ejemplo, valores de sysctl incorrectos o comandos como rm -rf /host/boot. Estos tipos de comandos pueden provocar fallas o dañar el sistema operativo del nodo host.
  • Movimiento lateral: Comprometer un nodo a través de un DaemonSet con privilegios le da al atacante una base sólida para atacar otros nodos, el plano de control de Kubernetes o los sistemas conectados.
  • Exposición de datos: El acceso sin restricciones al sistema de archivos del host (/) puede exponer datos sensibles almacenados en el nodo, incluidas credenciales, claves o datos que pertenecen a otros Pods si usan volúmenes hostPath.
  • Mayor superficie de ataque: El modo privilegiado expone más llamadas y funciones del sistema del kernel del host a posibles vulnerabilidades desde el contenedor.

Para evitar riesgos de seguridad, debes tener en cuenta las siguientes prácticas recomendadas y mitigaciones:

  • Evita usar el modo privilegiado: El enfoque más seguro es evitar por completo el parámetro de configuración privileged: true.
  • Usa las capacidades de Linux: Si se necesitan derechos elevados, puedes otorgar capacidades específicas de Linux, como NET_ADMIN, SYS_ADMIN y SYS_MODULE, en el campo securityContext.capabilities.add en lugar de privilegios completos. Este enfoque sigue el principio de privilegio mínimo, que recomendamos en lugar de otorgar permisos amplios.
  • Limita el alcance: Ejecuta DaemonSets con privilegios solo en grupos de nodos dedicados, posiblemente contaminados, para contener el impacto potencial si se vulnera un contenedor.
  • Aplica políticas: Usa herramientas como Policy Controller o Gatekeeper para crear políticas que restrinjan, auditen o requieran justificación para implementar contenedores con privilegios.
  • Analiza y usa imágenes de confianza: Usa la Autorización Binaria y el análisis riguroso de imágenes para garantizar que solo se ejecuten imágenes de contenedor verificadas y de confianza con privilegios elevados.
  • Minimiza las activaciones de host: Activa solo las rutas de acceso de host específicas que se necesitan y usa readOnly: true siempre que sea posible. Evita activar todo el sistema de archivos raíz (/).
  • Realiza auditorías periódicas: Revisa periódicamente todas las cargas de trabajo que se ejecutan con el parámetro de configuración privileged: true.

Inicia el entorno

En esta sección, harás lo siguiente:

  1. Habilita las API de Cloud necesarias.
  2. Aprovisiona una cuenta de servicio con privilegios limitados para los nodos en el clúster de GKE.
  3. Prepara el clúster de GKE.
  4. Otorga al usuario privilegios de administrador de clústeres.

Habilita las API de Cloud

  1. Abre Cloud Shell

    ABRIR Cloud Shell

  2. Selecciona el proyecto Google Cloud :

    gcloud config set project project-id
    

    Reemplaza project-id por el ID del proyectoGoogle Cloud que creaste o seleccionaste para este instructivo.

  3. Habilita la API de Kubernetes Engine:

    gcloud services enable container.googleapis.com
    

Aprovisiona una cuenta de servicio para administrar clústeres de GKE

En esta sección, crearás una cuenta de servicio asociada a los nodos del clúster. En este instructivo, los nodos de GKE usan esta cuenta de servicio en lugar de la cuenta de servicio predeterminada. Como práctica recomendada, otorga a la cuenta de servicio solo los roles y los permisos de acceso necesarios para ejecutar la aplicación.

Las funciones que se necesitan para la cuenta de servicio son las que se describen a continuación:

  • Rol de visualizador de Monitoring (roles/monitoring.viewer): Este rol otorga acceso de solo lectura a los datos de supervisión.
  • Función de escritor de métricas de Monitoring (roles/monitoring.metricWriter). Esta función permite escribir datos de supervisión.
  • Rol de escritor de registros (roles/logging.logWriter): Este rol otorga permisos para escribir registros.

Para aprovisionar una cuenta de servicio, sigue estos pasos:

  1. En Cloud Shell, inicializa una variable de entorno que almacene el nombre de la cuenta de servicio:

    GKE_SERVICE_ACCOUNT_NAME=ds-init-tutorial-gke
    
  2. Crea una cuenta de servicio:

    gcloud iam service-accounts create "$GKE_SERVICE_ACCOUNT_NAME" \
      --display-name="$GKE_SERVICE_ACCOUNT_NAME"
    
  3. Inicializa una variable de entorno que almacene el nombre de la cuenta de correo electrónico de la cuenta de servicio:

    GKE_SERVICE_ACCOUNT_EMAIL="$(gcloud iam service-accounts list \
        --format='value(email)' \
        --filter=displayName:"$GKE_SERVICE_ACCOUNT_NAME")"
    
  4. Vincula las funciones de administración de identidades y accesos (IAM) a la cuenta de servicio:

    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 el clúster de GKE

En esta sección, iniciarás el clúster de GKE, otorgarás permisos y finalizarás la configuración del clúster.

Un clúster con una cantidad relativamente baja de nodos pequeños de uso general es suficiente para demostrar el concepto de este instructivo. Debes crear un clúster con un grupo de nodos (el predeterminado).

  • En Cloud Shell, crea e inicia un clúster de GKE regional:

    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"
    

Aplica parámetros de configuración de nodos con un DaemonSet

En esta sección, evitarás que las cargas de trabajo se ejecuten en los nodos antes de que se complete la configuración. Para ello, aplicarás un taint al grupo de nodos. Luego, implementa un DaemonSet que hace lo siguiente:

  1. Programa Pods en nodos con taint usando una tolerancia para el taint.
  2. Ejecuta un contenedor init con privilegios que primero aplica la configuración del nodo con sysctl y, luego, quita el aislamiento del nodo con kubectl. Quitar el taint hace que el nodo sea apto para la programación de cargas de trabajo.
  3. Programa y ejecuta un contenedor de pausa que permanece inactivo y no consume recursos para evitar que el DaemonSet reprograme el Pod que se usa para la configuración.

En este instructivo, se aplica el parámetro del kernel vm.max_map_count=262144 como ejemplo de configuración.

  1. Aplica un taint al grupo de nodos predeterminado:

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

    Con este taint, solo los Pods que lo toleren, como el Pod de DaemonSet, se pueden programar en este grupo de nodos.

  2. Verifica que se haya aplicado el aislamiento:

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

    El estado del nodo debe mostrar node.config.status/stage=configuring:NoSchedule.

  3. Guarda el siguiente manifiesto como 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
    

    Este manifiesto crea un ServiceAccount, un ClusterRole y un ClusterRoleBinding para otorgar al DaemonSet permiso para quitar taints de los nodos. El DaemonSet implementa un Pod en cada nodo que tolera el taint configuring:NoSchedule. Este Pod ejecuta un contenedor de inicialización con privilegios que aplica la configuración de sysctl (vm.max_map_count=262144) y quita el taint del nodo, lo que hace que el nodo sea apto para la programación. Luego, se inicia un contenedor de pausa para mantener el Pod en ejecución.

    El contenedor init se ejecuta en modo privilegiado, lo que tiene implicaciones de seguridad. Para obtener más detalles, consulta Compensaciones y restricciones de seguridad de DaemonSet con privilegios.

  4. Aplica el manifiesto

    kubectl apply -f auto-untaint-daemonset.yaml
    
  5. Verifica que se hayan creado los Pods del DaemonSet y espera a que alcancen el estado Running:

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

    El estado Running indica que el contenedor de inicialización se completó correctamente. Toma nota del nombre del Pod para que puedas usarlo y verificar la inicialización en la siguiente sección.

Valida y verifica el procedimiento de inicialización

Una vez que se complete la configuración del nodo, puedes verificar los resultados revisando los registros.

  1. Verifica los registros del contenedor de inicialización de uno de los Pods para ver su resultado:

    kubectl logs POD_NAME -c configure-and-untaint
    

    Reemplaza POD_NAME por el nombre del Pod.

    Deberías ver un resultado que indica que la configuración y la eliminación de la contaminación del nodo se realizaron correctamente.

  2. Verifica que se haya quitado la taint:

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

    El estado del nodo debe mostrar Taints: <none> o mostrar marcas que tengan la clave node.config.status/stage.

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en este instructivo, puedes borrar el proyecto que creaste para este instructivo. Si creaste un proyecto dedicado a este instructivo, puedes borrarlo por completo. Si usaste un proyecto existente, pero no quieres borrarlo, sigue estos pasos para limpiarlo.

Limpia el proyecto

Para limpiar un proyecto sin borrarlo, debes quitar los recursos que creaste en este instructivo.

  1. En Cloud Shell, borra el clúster de GKE:

    gcloud container clusters delete ds-init-tutorial --quiet --region us-central1
    
  2. Borra la cuenta de servicio:

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

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

  1. En la Google Cloud consola, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

¿Qué sigue?