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 desde el 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 para 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 las 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 aplicaciones 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 nodos y quite el taint.
  • Verifica que los nodos del clúster estén configurados y que se quite el taint.

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 Google Cloud cuenta de. Si eres nuevo en 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 de contenedores para ese Pod. Debes tener en cuenta las siguientes restricciones y riesgos de seguridad que introduce:

  • Escape de contenedor o compromiso del host: Una vulnerabilidad dentro de la aplicación o imagen de contenedor con privilegios puede generar acceso raíz directo en el nodo host.
  • Violación del principio de privilegio mínimo: El modo privilegiado otorga todas las capacidades, probablemente mucho más de lo necesario para una tarea específica. Este acceso amplio aumenta el daño potencial si el contenedor se ve comprometido.
  • Desestabilización del nodo: Los comandos accidentales o maliciosos se pueden ejecutar dentro del contenedor con privilegios, por ejemplo, valores sysctl incorrectos o comandos como rm -rf /host/boot. Estos tipos de comandos pueden fallar o dañar el sistema operativo del nodo host.
  • Movimiento lateral: Comprometer un nodo a través de un DaemonSet con privilegios le da a un 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 las credenciales, las claves o los datos que pertenecen a otros Pods si usan volúmenes hostPath.
  • Mayor superficie de ataque: El modo privilegiado expone más de las 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 capacidades de Linux: Si se necesitan derechos elevados, puedes otorgar capacidades específicas de Linux, como NET_ADMIN, SYS_ADMIN, 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 con taints, para contener el impacto potencial si un contenedor se ve comprometido.
  • 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 los montajes de host: Solo monta las rutas de acceso de host específicas necesarias y usa readOnly: true siempre que sea posible. Evita montar 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 Google Cloud proyecto:

    gcloud config set project project-id
    

    Reemplaza project-id por el ID del Google Cloud proyecto 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:

  • Función de visualizador de Monitoring (roles/monitoring.viewer). Esta función proporciona 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.
  • Función de escritor de registros (roles/logging.logWriter). Esta función 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 regional de clúster de 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"
    

Aplica configuraciones de nodos con un DaemonSet

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

  1. Programa Pods en nodos con taints mediante 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 taint del nodo con kubectl. Quitar el taint hace que el nodo se pueda programar para las 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 configuración de ejemplo.

  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 toleran, como el Pod de DaemonSet, se pueden programar en este grupo de nodos.

  2. Verifica que se aplique el taint:

    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 una ServiceAccount, ClusterRole y 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 init con privilegios que aplica la configuración sysctl (vm.max_map_count=262144) y quita el taint del nodo, lo que hace que el nodo se pueda programar. 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 Intercambios de DaemonSet con privilegios y seguridad restricciones.

  4. Aplica el manifiesto

    kubectl apply -f auto-untaint-daemonset.yaml
    
  5. Verifica que se creen los Pods de 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 init se completó correctamente. Toma nota del nombre del Pod para que puedas usarlo para 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 consultando los registros.

  1. Verifica los registros del contenedor init 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 del taint del nodo se realizaron correctamente.

  2. Verifica que se quite el taint:

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

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

Limpia

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

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 tú 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?