Una referencia escaneable de patrones arquitectónicos que evalúa el examen CKA. Lee de arriba a abajo o salta a una sección.
Arquitectura, Instalación y Configuración del Clúster
Requisito para realizar una copia de seguridad de recuperación ante desastres del estado del clúster.
Utilice `etcdctl snapshot save` con los certificados TLS apropiados (`--cacert`, `--cert`, `--key`) y el endpoint.
Por qué: etcd almacena el estado completo del clúster. La creación directa de snapshots es la forma canónica de hacer una copia de seguridad. En un clúster kubeadm, TLS está habilitado, por lo que los certificados son obligatorios para que `etcdctl` se autentique.
Restaurar un clúster desde una copia de seguridad de recuperación ante desastres.
Utilice `etcdctl snapshot restore` en un nuevo directorio de datos. Luego, actualice el manifiesto de pod estático `etcd.yaml` para que su montaje de volumen `--data-dir` apunte a la nueva ubicación y reinicie el kubelet.
Por qué: La restauración crea un nuevo directorio de datos. El manifiesto del pod estático debe actualizarse para usar estos nuevos datos, de lo contrario etcd se iniciará con el directorio de datos antiguo (o vacío).
Realizar una actualización de versión para un clúster gestionado por kubeadm.
1. En el plano de control: actualice `kubeadm`, ejecute `kubeadm upgrade plan`, luego `kubeadm upgrade apply`. 2. En cada nodo worker: `kubectl drain`, actualice `kubelet`, reinicie el servicio kubelet, `kubectl uncordon`.
Por qué: El proceso es de varios pasos y secuencial. `kubeadm` solo actualiza los componentes del plano de control; `kubelet` debe actualizarse manualmente en cada nodo. Drenar los nodos asegura que las cargas de trabajo sean desalojadas de forma segura antes del mantenimiento.
Los certificados del clúster están a punto de caducar y necesitan ser verificados o renovados.
Utilice `kubeadm certs check-expiration` para ver las fechas de caducidad. Utilice `kubeadm certs renew all` (o para componentes específicos) para renovarlos. Reinicie los pods del plano de control después de la renovación.
Por qué: Los certificados generados por Kubeadm tienen una validez de 1 año. La renovación es una tarea de mantenimiento común. Los componentes del plano de control deben reiniciarse para cargar los nuevos certificados.
Un componente del plano de control (por ejemplo, el servidor API) necesita ser configurado o reiniciado.
Modifique el manifiesto del componente en `/etc/kubernetes/manifests/`. El kubelet en el nodo detectará automáticamente el cambio y reiniciará el pod.
Por qué: Los componentes del plano de control en kubeadm se ejecutan como pods estáticos, gestionados directamente por el kubelet, no por el servidor API. Toda la gestión se realiza a través de archivos de manifiesto en el directorio vigilado.
Definir controles de acceso para usuarios o aplicaciones.
Utilice un `Role` y `RoleBinding` para permisos con ámbito de namespace. Utilice un `ClusterRole` y `ClusterRoleBinding` para permisos a nivel de clúster.
Por qué: Esta es la separación fundamental en RBAC. Un Role siempre está vinculado a un namespace, mientras que un ClusterRole puede otorgar acceso a recursos no namespaced (como nodos) o a recursos en todos los namespaces.
Una service account necesita acceder a recursos en todos los namespaces.
Cree un `ClusterRole` que defina los permisos. Cree un `ClusterRoleBinding` para otorgar ese ClusterRole a la `ServiceAccount` específica.
Por qué: Aunque una ServiceAccount está en un namespace, un ClusterRoleBinding puede otorgarle permisos a nivel de clúster. Un `RoleBinding` solo otorgaría los permisos dentro del propio namespace del RoleBinding.
Servicios y Redes
Exponer una aplicación al tráfico externo sin un balanceador de carga en la nube.
Utilice un Service de `type: NodePort`. Esto expone el servicio en un puerto estático (rango predeterminado: 30000-32767) en la dirección IP de cada nodo.
Por qué: NodePort es una forma sencilla de obtener tráfico externo en el clúster. Es menos costoso y agnóstico de plataforma en comparación con `type: LoadBalancer`, pero requiere que los clientes conozcan una IP de nodo.
Exponer múltiples servicios HTTP/S bajo una única dirección IP, con enrutamiento basado en host o path.
Implemente un Ingress Controller (por ejemplo, NGINX). Cree recursos `Ingress` que definan reglas de enrutamiento desde hosts/paths a `Services` de backend.
Por qué: Ingress es el recurso estándar de Kubernetes para el enrutamiento L7. Requiere un controlador separado para implementar la lógica de enrutamiento. Esto desacopla las reglas de enrutamiento de la implementación del proxy.
Asegurar un namespace denegando todo el tráfico de entrada por defecto.
Cree una `NetworkPolicy` que seleccione todos los pods (`podSelector: {}`) y especifique una regla de entrada vacía (`ingress: []`).
Por qué: Una vez que un pod es seleccionado por cualquier NetworkPolicy, todo el tráfico no permitido explícitamente es denegado. Una política que selecciona todos los pods con una regla de entrada vacía crea efectivamente un firewall de "denegar todo" para el namespace.
Permitir que los pods en un namespace "frontend" accedan a pods en un namespace "backend".
En el namespace "backend", cree una NetworkPolicy. En la regla `ingress.from`, utilice un `namespaceSelector` para que coincida con las etiquetas en el recurso `Namespace` "frontend".
Por qué: `podSelector` solo funciona dentro del namespace de la política. Para permitir el tráfico desde otros namespaces, debe utilizar un `namespaceSelector`. Esto requiere etiquetar los propios objetos `Namespace`.
Una aplicación necesita conectarse a otro servicio dentro del clúster.
Utilice el nombre DNS interno del servicio: `<service-name>.<namespace>.svc.cluster.local`. Si está en el mismo namespace, `<service-name>` es suficiente.
Por qué: Kubernetes proporciona descubrimiento de servicios estable basado en DNS a través de CoreDNS. Esto desacopla las aplicaciones de las IPs específicas de los pods, que son efímeras.
Una aplicación con estado (por ejemplo, un conjunto de réplicas de base de datos) requiere identidad de red directa para cada pod.
Cree un `Service` sin cabeza (`clusterIP: None`) para el `StatefulSet`. Esto proporciona registros DNS A únicos para cada pod (por ejemplo, `pod-0.my-service.my-ns...`).
Por qué: Un servicio sin cabeza no realiza balanceo de carga. En cambio, proporciona registros DNS para cada pod, lo que permite a los clientes conectarse a instancias específicas, lo cual es crucial para la elección de líder o el descubrimiento de pares en sistemas con estado.
Un servicio orientado al exterior necesita ver la dirección IP original del cliente para el registro o el filtrado basado en IP.
Establezca `externalTrafficPolicy: Local` en el Service `NodePort` o `LoadBalancer`.
Por qué: La política `Cluster` predeterminada oculta la IP del cliente a través de SNAT. `Local` evita este salto de red adicional al enrutar el tráfico solo a los pods en el nodo que recibió el tráfico, preservando la IP de origen.
Cargas de Trabajo y Programación
Colocar o separar pods para rendimiento o alta disponibilidad.
Utilice `podAffinity` para programar pods en el mismo nodo/zona que otros pods específicos. Utilice `podAntiAffinity` para evitar programarlos juntos.
Por qué: Esto proporciona un control de programación más avanzado que la afinidad a nivel de nodo. La antiafinidad con `requiredDuringScheduling...` es fundamental para distribuir réplicas de un servicio en nodos o zonas para HA.
Dedicar nodos a cargas de trabajo específicas o evitar que ciertas cargas de trabajo se ejecuten en ellos.
Aplique un `taint` a un nodo (por ejemplo, `gpu=true:NoSchedule`). Añada una `toleration` que coincida a los pods que deberían poder ejecutarse en ese nodo.
Por qué: Los taints repelen los pods, mientras que las tolerations los permiten. Este es el mecanismo principal para dedicar nodos. El efecto `NoExecute` desalojará los pods ya en ejecución que no tengan la toleration.
Implementar un agente de monitoreo o registro en cada nodo del clúster.
Utilice un `DaemonSet`. Asegura que una copia del pod se ejecute en cada nodo que coincida con sus criterios de programación.
Por qué: DaemonSet está diseñado para este propósito exacto. Se despliega automáticamente en nuevos nodos y maneja la gestión de pods a nivel de nodo, lo cual sería difícil con un Deployment.
Ejecutar una tarea batch única o una tarea programada recurrente.
Utilice un `Job` para una tarea que se ejecuta una vez hasta su finalización. Utilice un `CronJob` para crear Jobs en una programación recurrente (por ejemplo, copias de seguridad nocturnas).
Por qué: Los Jobs aseguran que los pods se ejecuten hasta un número especificado de finalizaciones. CronJobs son un controlador de nivel superior que gestiona Jobs basándose en una programación cron.
Actualizar una aplicación a una nueva versión con tiempo de inactividad cero.
Utilice un `Deployment` con la estrategia `RollingUpdate` predeterminada. Configure `maxSurge` y `maxUnavailable` para controlar la velocidad de actualización y la disponibilidad.
Por qué: Las actualizaciones continuas (rolling updates) reemplazan gradualmente los pods antiguos por los nuevos, asegurando que el servicio permanezca disponible. `maxUnavailable` garantiza un número mínimo de pods en ejecución, mientras que `maxSurge` permite un aumento por encima del recuento de réplicas deseado para acelerar el despliegue.
Asegurar que los pods obtengan recursos garantizados y no consuman recursos excesivos en un nodo.
Establezca `resources.requests` (CPU/memoria) para garantizar un mínimo para la programación. Establezca `resources.limits` para evitar que un contenedor exceda una cierta cantidad.
Por qué: Las requests son utilizadas por el scheduler para la colocación y garantizan recursos. Los limits son impuestos por el kubelet y el runtime del contenedor; exceder el límite de memoria resulta en OOMKill.
Implementar una aplicación con estado que requiere identificadores de red estables y únicos, y almacenamiento persistente por réplica.
Utilice un `StatefulSet` con un `volumeClaimTemplate`. Esto crea un `PersistentVolumeClaim` único para cada pod, asegurando que los datos se vuelvan a adjuntar a la misma identidad de pod tras el reinicio.
Por qué: Los StatefulSets proporcionan nombres de pod estables (por ejemplo, `web-0`, `web-1`) y un PVC único y persistente para cada uno. Esto es esencial para aplicaciones que dependen de una identidad y almacenamiento estables.
Almacenamiento
Proporcionar almacenamiento persistente para una aplicación sin pre-aprovisionar volúmenes.
Cree una `StorageClass` que defina un aprovisionador de almacenamiento. Luego, cree un `PersistentVolumeClaim` (PVC) que solicite almacenamiento de esa clase. Un `PersistentVolume` (PV) será aprovisionado dinámicamente.
Por qué: Esto desacopla las aplicaciones de la infraestructura de almacenamiento subyacente. Los desarrolladores solicitan almacenamiento a través de PVCs, y el administrador del clúster define cómo se aprovisiona ese almacenamiento a través de StorageClasses.
Controlar qué sucede con un volumen persistente después de que se elimina su claim.
Establezca `persistentVolumeReclaimPolicy` en el PV o StorageClass. `Delete` elimina automáticamente el almacenamiento subyacente. `Retain` deja el volumen y los datos intactos, requiriendo una limpieza manual.
Por qué: `Retain` es la opción más segura para los datos de producción, ya que evita la pérdida accidental de datos. `Delete` es conveniente para entornos efímeros o de desarrollo. El valor predeterminado depende del aprovisionador.
Definir cómo un volumen puede ser montado por los pods.
Utilice `accessModes`: `ReadWriteOnce` (RWO) para lectura-escritura en un solo nodo, `ReadOnlyMany` (ROX) para solo lectura en varios nodos, `ReadWriteMany` (RWX) para lectura-escritura en varios nodos.
Por qué: El modo de acceso debe ser compatible con el proveedor de almacenamiento subyacente. La incompatibilidad de las necesidades de la aplicación (por ejemplo, necesitar RWX) con las capacidades de almacenamiento (solo soportar RWO) es una causa común de PVCs en estado Pending.
Inyectar archivos de configuración o datos sensibles en un pod.
Monte un `ConfigMap` o `Secret` como un volumen. Cada clave en el objeto de datos se convierte en un archivo en la ruta de montaje.
Por qué: Esta es la forma estándar de proporcionar configuración a los pods. Permite que la configuración se gestione como un objeto de Kubernetes y se actualice independientemente de la imagen del pod.
Una aplicación necesita más espacio de almacenamiento en su volumen persistente existente.
Asegúrese de que la `StorageClass` tenga `allowVolumeExpansion: true`. Edite el `PVC` para solicitar un tamaño mayor en `spec.resources.requests.storage`.
Por qué: La expansión de volumen es una característica opcional. La StorageClass debe permitirla explícitamente, y el controlador CSI subyacente debe soportarla. Puede ser necesario reiniciar el pod para que el sistema de archivos sea redimensionado.
Resolución de Problemas
Un pod se encuentra atascado en el estado `Pending` y no se está programando.
Ejecute `kubectl describe pod <nombre-del-pod>`. Revise la sección `Events` en busca de mensajes del scheduler.
Por qué: El comando `describe` es la herramienta principal para esto. Mostrará razones como "Insufficient cpu/memory", "node(s) had taints the pod didn't tolerate", o "didn't match node selector".
Un pod se inicia y falla repetidamente, con un estado `CrashLoopBackOff`.
1. `kubectl logs <nombre-del-pod> --previous` para ver los logs del contenedor que falló. 2. `kubectl describe pod <nombre-del-pod>` para verificar el código de salida y la razón.
Por qué: `CrashLoopBackOff` significa que la aplicación dentro del contenedor está saliendo. Los logs de la instancia anterior (`--previous`) son cruciales, ya que el contenedor actual podría no haber registrado nada útil aún. El código de salida también puede indicar el tipo de error.
Un pod no se inicia con estado `ImagePullBackOff` o `ErrImagePull`.
`kubectl describe pod <nombre-del-pod>` para ver el mensaje del evento. Verifique que el nombre y la etiqueta de la imagen sean correctos. Para registros privados, asegúrese de que `imagePullSecrets` esté configurado y que el secreto sea válido.
Por qué: Este es un problema del registro o del nombre de la imagen, no un problema de la aplicación. Las causas comunes son errores tipográficos, etiquetas incorrectas o fallos de autenticación con un registro privado.
Un nodo tiene un estado `NotReady`.
Conéctese por SSH al nodo afectado. Verifique el estado del servicio kubelet con `systemctl status kubelet`. Vea sus logs con `journalctl -u kubelet`.
Por qué: El `kubelet` es el agente responsable de informar sobre la salud del nodo. Si está caído o no puede comunicarse con el servidor API, el nodo se marcará como NotReady. Sus logs son el primer lugar donde buscar.
Existe un servicio, pero el tráfico no llega a los pods de backend.
1. `kubectl describe svc <nombre-del-servicio>` y verifique que el `Selector` coincida con las etiquetas de los pods. 2. `kubectl get endpoints <nombre-del-servicio>` y asegúrese de que liste las IPs correctas de los pods. Si no, las etiquetas no coinciden.
Por qué: El vínculo entre un Service y sus Pods es el selector de etiquetas. Si el selector es incorrecto o los pods no tienen las etiquetas correctas, el objeto Endpoints estará vacío y el servicio no tendrá a dónde enrutar el tráfico.
Los pods no pueden resolver nombres de servicio o nombres de host externos.
1. Verifique si los pods de CoreDNS se están ejecutando en `kube-system`. 2. Revise los logs de CoreDNS. 3. Ejecute un pod de depuración (por ejemplo, `busybox`) y use `nslookup` para probar la resolución desde dentro del clúster.
Por qué: DNS es una dependencia crítica del clúster. Las fallas generalmente se remontan a la implementación de CoreDNS en sí, su configuración (en un ConfigMap) o políticas de red que bloquean el tráfico DNS en el puerto UDP/TCP 53.
Un nodo debe ser puesto fuera de línea para mantenimiento.
Primero, `kubectl cordon <nombre-del-nodo>` para marcarlo como no programable. Luego, `kubectl drain <nombre-del-nodo> --ignore-daemonsets` para desalojar de forma segura todos los pods de usuario.
Por qué: `cordon` evita que se programen nuevos pods. `drain` respeta los PodDisruptionBudgets y desaloja los pods con gracia. Se necesita `--ignore-daemonsets` porque los pods de DaemonSet no pueden ser desalojados.
Identificar qué pods o nodos están consumiendo la mayor cantidad de CPU o memoria.
Utilice `kubectl top pods` y `kubectl top nodes`. Esto requiere que `metrics-server` esté implementado en el clúster.
Por qué: `kubectl top` proporciona una vista rápida y en tiempo real del consumo de recursos, esencial para identificar aplicaciones que consumen muchos recursos o presión de recursos en un nodo.
Un pod ha estado en estado `Terminating` durante mucho tiempo y no se elimina.
Fuerce la eliminación del pod con `kubectl delete pod <nombre-del-pod> --grace-period=0 --force`.
Por qué: Esto puede suceder si un finalizer está atascado o el kubelet no puede limpiar los recursos. La eliminación forzada elimina el pod del servidor API inmediatamente, pero debe usarse como último recurso ya que puede dejar recursos huérfanos en el nodo.