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.
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
- 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.
-
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 theresourcemanager.projects.createpermission. Learn how to grant roles.
-
Verify that billing is enabled for your Google Cloud project.
-
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 theresourcemanager.projects.createpermission. Learn how to grant roles.
-
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
sysctlincorrectos o comandos comorm -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_ADMINySYS_MODULE, en el camposecurityContext.capabilities.adden 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: truesiempre 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:
- Habilita las API de Cloud necesarias.
- Aprovisiona una cuenta de servicio con privilegios limitados para los nodos en el clúster de GKE.
- Prepara el clúster de GKE.
- Otorga al usuario privilegios de administrador de clústeres.
Habilita las API de Cloud
Abre Cloud Shell
Selecciona el proyecto Google Cloud :
gcloud config set project project-id
Reemplaza
project-idpor el ID del proyectoGoogle Cloud que creaste o seleccionaste para este instructivo.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:
En Cloud Shell, inicializa una variable de entorno que almacene el nombre de la cuenta de servicio:
GKE_SERVICE_ACCOUNT_NAME=ds-init-tutorial-gkeCrea una cuenta de servicio:
gcloud iam service-accounts create "$GKE_SERVICE_ACCOUNT_NAME" \ --display-name="$GKE_SERVICE_ACCOUNT_NAME"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")"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:
- Programa Pods en nodos con taint usando una tolerancia para el taint.
- Ejecuta un contenedor init con privilegios que primero aplica la configuración del nodo con
sysctly, luego, quita el aislamiento del nodo conkubectl. Quitar el taint hace que el nodo sea apto para la programación de cargas de trabajo. - 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.
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-central1Con este taint, solo los Pods que lo toleren, como el Pod de DaemonSet, se pueden programar en este grupo de nodos.
Verifica que se haya aplicado el aislamiento:
kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep TaintsEl estado del nodo debe mostrar
node.config.status/stage=configuring:NoSchedule.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.9Este 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 desysctl(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.
Aplica el manifiesto
kubectl apply -f auto-untaint-daemonset.yamlVerifica 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 wideEl estado
Runningindica 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.
Verifica los registros del contenedor de inicialización de uno de los Pods para ver su resultado:
kubectl logs POD_NAME -c configure-and-untaintReemplaza
POD_NAMEpor 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.
Verifica que se haya quitado la taint:
kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep TaintsEl estado del nodo debe mostrar
Taints: <none>o mostrar marcas que tengan la clavenode.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.
En Cloud Shell, borra el clúster de GKE:
gcloud container clusters delete ds-init-tutorial --quiet --region us-central1Borra 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.
- En la Google Cloud consola, ve a la página Administrar recursos.
- En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
- En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.
¿Qué sigue?
- Obtén más información sobre GKE.
- Implementa una cadena de suministro de software segura.
- Obtén más información sobre cómo endurecer la seguridad del clúster de GKE.
- Explora arquitecturas de referencia, diagramas y prácticas recomendadas sobre Google Cloud. Consulta nuestro Cloud Architecture Center.