Un pod del namespace shop llama a http://api y la petición agota el tiempo de espera. El pod está en marcha, el Service existe y su EndpointSlice lista dos backends listos. El fallo puede estar en el DNS, en las reglas que kube-proxy escribió en el nodo, en el overlay entre nodos o en una NetworkPolicy que alguien aplicó la semana pasada. Cada una es una capa distinta, gestionada por un componente distinto.
Este artículo recorre esas capas con un diagrama: el modelo de red, el tráfico de pod a pod, los plugins CNI, los Services, el DNS, NetworkPolicy y el tráfico de entrada y salida del clúster.
El modelo de red de Kubernetes
El modelo de red de Kubernetes tiene tres reglas. Cada pod recibe su propia dirección IP en todo el clúster, compartida por sus contenedores. Cada pod puede alcanzar a cualquier otro pod, en cualquier nodo, sin proxies ni NAT. Y los agentes de nodo, como el kubelet, pueden alcanzar todos los pods de su nodo.
Kubernetes define las API, y los componentes que instalas hacen el trabajo:
| Capa | Qué te da | Implementada por |
|---|---|---|
| Red de pods | Una IP por pod, alcanzabilidad de pod a pod | Un plugin CNI |
| Services | Una IP virtual estable y un nombre para un conjunto de pods | kube-proxy o un reemplazo eBPF |
| DNS | Nombres como api.shop.svc.cluster.local |
CoreDNS |
| NetworkPolicy | Reglas de permiso para el tráfico de pods | El plugin CNI |
| Ingress, Gateway API | Enrutamiento desde fuera del clúster | Un controlador que instalas |
Esa división explica muchas sorpresas, como una NetworkPolicy que el servidor de API acepta pero que tu plugin ignora en silencio.
De pod a pod en el mismo nodo
Cuando el runtime de contenedores arranca un pod, llama al plugin CNI con ADD. La mayoría de los plugins crean un par veth, un cable virtual con eth0 dentro del pod y el otro extremo en el host, y asignan la IP. Flannel conecta el extremo del host a un bridge de Linux, cni0; Calico prescinde del bridge: cada pod recibe una ruta /32 y el host actúa como su router. En ambos casos, el tráfico dentro del mismo nodo no necesita encapsulación ni NAT.
De pod a pod entre nodos: overlay o enrutamiento nativo
Cada nodo suele poseer una porción del rango de pods. Con el valor por defecto de --node-cidr-mask-size de kube-controller-manager, que es 24, un rango 10.244.0.0/16 da un /24 a cada uno de 256 nodos, es decir, 256 direcciones para los 110 pods que admite el kubelet por defecto. Entre nodos, el plugin tiene dos opciones:
| Overlay (VXLAN, Geneve, IP-in-IP) | Enrutamiento nativo (BGP, rutas del cloud, IP de VPC) | |
|---|---|---|
| La red entre nodos ve | Solo IP de nodos | IP de pods |
| Requiere | Alcanzabilidad IP entre nodos | Una red que enrute los rangos de pods, o direcciones de VPC para los pods |
| Sobrecarga por paquete | 50 bytes para VXLAN con IPv4, 20 para IP-in-IP | Ninguna |
| Ejemplos | Valores por defecto de Flannel y Cilium, Calico VXLAN | Calico con BGP, AWS VPC CNI, Azure CNI |
La sobrecarga es un problema de MTU: en una red de 1500 bytes, los pods detrás de VXLAN con IPv4 necesitan una MTU de 1450, según la guía de MTU de Calico. Si la configuras mal, los paquetes grandes desaparecen, lo que parece un cuelgue aleatorio.
Comparativa de plugins CNI
| Plugin | Tráfico entre nodos | NetworkPolicy | Notas |
|---|---|---|---|
| Flannel | VXLAN por defecto (UDP 8472); host-gw en una red compartida de capa 2 |
Flannel no la aplica; añade kube-network-policies o Calico | Pequeño y simple |
| Calico | BGP, u overlay VXLAN (UDP 4789) o IP-in-IP | Completa, más las políticas de Calico por namespace y globales | Planos de datos iptables, nftables, eBPF, VPP y Windows |
| Cilium | VXLAN por defecto (UDP 8472), Geneve o enrutamiento nativo | Completa, más reglas de capa 7 y basadas en DNS | Ruta de datos eBPF |
| AWS VPC CNI | Sin overlay; los pods reciben direcciones de VPC | Desde la v1.14, con enableNetworkPolicy |
Pods por nodo limitados por el tipo de instancia |
El límite de AWS aprieta pronto: el máximo por defecto es ENI × (IP por ENI − 1) + 2, así que una m5.large con 3 interfaces de 10 direcciones ejecuta 3 × 9 + 2 = 29 pods. La delegación de prefijos lo eleva.
Services: una IP estable delante de pods que cambian
Las IP de los pods cambian cada vez que se reemplaza un pod. Un Service da a un conjunto de pods una IP virtual estable, la ClusterIP, y un nombre DNS; los EndpointSlices siguen la pista de los pods que hay detrás.
| Tipo | Qué obtienes |
|---|---|
| ClusterIP (por defecto) | Una IP virtual dentro del clúster |
| NodePort | Además, un puerto en cada nodo, 30000–32767 por defecto |
| LoadBalancer | Además, un balanceador de carga del cloud o de un add-on |
| ExternalName | Un alias DNS (CNAME), sin proxy |
Headless (clusterIP: None) |
Sin IP virtual; el DNS devuelve las IP de los pods |
Ninguna máquina posee una ClusterIP. En cada nodo, kube-proxy programa el kernel para que un paquete dirigido a 10.96.48.20:80 se reescriba (NAT de destino) a una dirección de pod como 10.244.2.22:8080 antes de salir del nodo del cliente. El Service es un paso lógico, no un servidor. Las reglas dependen del modo de proxy:
| Modo | Estado en Kubernetes 1.37 | Cómo funciona |
|---|---|---|
| iptables | Por defecto en Linux | Reglas por Service y endpoint; backend aleatorio |
| nftables | GA desde la 1.33; el futuro modo por defecto previsto | Actualizaciones más rápidas a gran escala; requiere kernel 5.13 o posterior |
| ipvs | Obsoleto desde la 1.35; desactivado por defecto en la 1.40, eliminado en la 1.43 | Tablas IPVS del kernel, más algoritmos de balanceo |
| eBPF (Cilium, Calico) | Sustituye a kube-proxy | Balanceo de carga en programas eBPF |
Define el modo de forma explícita para que una actualización no pueda cambiarlo, y planifica la salida de IPVS. En modo nftables, los NodePorts escuchan por defecto solo en las direcciones principales del nodo.
DNS con CoreDNS
CoreDNS es el DNS de clúster por defecto, y kubeadm lo ejecuta detrás de un Service que sigue llamándose kube-dns. Cada Service recibe un nombre, <service>.<namespace>.svc.cluster.local, que se resuelve a su ClusterIP, o a las IP de los pods en un Service headless. El kubelet escribe el /etc/resolv.conf de cada pod; en shop en un clúster kubeadm suele decir:
nameserver 10.96.0.10
search shop.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
El problema es ndots:5: un nombre con menos de cinco puntos, como api.stripe.com, se prueba primero con cada dominio de búsqueda, de modo que una consulta externa puede costar tres consultas fallidas. Añade un punto final a los nombres más consultados, baja ndots en el dnsConfig del pod o ejecuta NodeLocal DNSCache. Los nombres externos siguen entonces la ruta que se muestra en cómo funciona el DNS.
NetworkPolicy: del permiso por defecto al mínimo privilegio
Por defecto, cada pod acepta tráfico de cualquier origen. En cuanto cualquier NetworkPolicy selecciona un pod para ingress (o egress), solo pasa lo que alguna política permite; las respuestas se permiten automáticamente. Las políticas solo añaden reglas de permiso, así que el orden nunca importa. Empieza cada namespace con un denegar por defecto:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
Luego abre lo que necesites, aquí TCP 8080 de web a api:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-web
namespace: shop
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
Cuatro detalles que pillan a la gente:
- El puerto es el del pod, 8080, no el puerto 80 del Service.
- El denegar por defecto también aísla los pods web, así que el Gateway necesita su propia regla para alcanzarlos.
- Un guion cambia el significado. Un
namespaceSelectory unpodSelectoren una misma entradafromdeben coincidir ambos; como dos entradas, basta con uno. - El denegar por defecto en egress bloquea el DNS, así que permite primero el puerto 53 hacia
kube-dns.
NetworkPolicy no tiene reglas de denegación, ni ámbito de todo el clúster, ni nada para TLS; para TLS mutuo entre workloads, consulta el diagrama de arquitectura de service mesh.
Tráfico de entrada y salida: Ingress, Gateway API y egress
Ingress es estable pero está congelado, y el proyecto Kubernetes recomienda en su lugar la Gateway API. El controlador comunitario Ingress NGINX se retiró en marzo de 2026 y ya no recibe correcciones de seguridad, pero las instalaciones existentes siguen funcionando sin que nadie lo note.
La Gateway API reparte el trabajo por roles: los responsables de la plataforma gestionan el Gateway, con sus listeners y certificados, y los equipos de aplicación enlazan HTTPRoutes desde sus propios namespaces. Llegó a GA en octubre de 2023, y las versiones de 2026 también hicieron estándar TLSRoute, TCPRoute y UDPRoute. El diagrama de ingress de Kubernetes sigue ese camino salto a salto.
El egress es la dirección olvidada. El tráfico que sale del clúster suele traducirse a la IP del nodo, como hace AWS VPC CNI para los destinos fuera de la VPC. Límitalo con reglas de egress, que solo ven los destinos externos como rangos de IP, con reglas que entienden DNS como toFQDNs de Cilium, o con un egress gateway cuando un socio necesita una IP de origen fija.
El diagrama de red de Kubernetes
Contenido del diagrama
- worker-1 · pods 10.244.1.0/24
- worker-2 · pods 10.244.2.0/24
- Users
- public-gw
- web · 10.244.1.12
- coredns · 10.244.1.5
- api · 10.244.2.21
- api · 10.244.2.22
- api · 10.96.48.20
- api-allow-web
- HTTPS 443
- HTTPRoute
- DNS: api.shop.svc.cluster.local via kube-dns 10.96.0.10
- TCP 10.96.48.20:80
- DNAT 10.244.2.21:8080
- DNAT 10.244.2.22:8080
- VXLAN overlay over UDP · node IP to node IP
Cómo leerlo:
- Dos nodos worker, cada uno etiquetado con su rango de pods /24.
- El azul es norte-sur: los usuarios llegan a
public-gwpor HTTPS 443, y una HTTPRoute los envía al pod web. - El ámbar es DNS: web pregunta a CoreDNS, a través de
kube-dnsen 10.96.0.10, porapi.shop.svc.cluster.localy recibe 10.96.48.20. - El verde azulado es la ruta del Service: web se conecta a 10.96.48.20:80, y las reglas de kube-proxy en worker-1 lo reescriben a un pod api en el puerto 8080.
- El gris es el overlay: el paquete cruza a worker-2 dentro de VXLAN, entre las IP de los nodos.
- El rojo es la política:
api-allow-websolo deja que los podsapp=webalcancen los pods api. - El bloque Markdown lista los valores por defecto detrás de los números.
Cómo dibujar con claridad diagramas de red de Kubernetes
- Etiqueta los nodos con los rangos de pods y los pods con sus IP, los detalles que buscas en los logs.
- Dibuja el Service como un paso lógico y anota dónde ocurre la reescritura.
- Etiqueta las aristas con dirección, puerto y protocolo, pasando al puerto del pod después del Service.
- Colorea distinto el norte-sur y el este-oeste, y dibuja el overlay una sola vez, entre los nodos.
- Conecta cada política a los pods que selecciona, con el origen permitido.
- Deja fuera el kubelet, el servidor de API y las réplicas adicionales; el diagrama de arquitectura de Kubernetes cubre el plano de control.
- Errores comunes: kube-proxy en la ruta de los paquetes (escribe reglas, el kernel reenvía), no incluir DNS y una política sobre un plugin que la ignora.
Constrúyelo en Archboard
- Inicia sesión con Google o GitHub y crea un diagrama. La herramienta gratuita Kubernetes YAML a diagrama convierte manifests existentes en un primer borrador.
- Añade dos nodos Marco del pack Básico para worker-1 y worker-2, etiquetados con sus rangos de pods.
- Del pack Kubernetes, añade cuatro nodos Pod con sus IP: web y coredns en worker-1, dos pods api en worker-2.
- Añade un Service (ClusterIP, puerto 80 a 8080) entre los marcos, con aristas etiquetadas desde web y hacia los dos pods api.
- Añade una NetworkPolicy que seleccione
app=api, con aristas punteadas hacia los pods api. - Añade un nodo Internet del pack Red y un Gateway con un listener HTTPS 443, y encadénalos con el pod web.
- Dibuja una arista DNS discontinua de web a coredns y una arista VXLAN gruesa y bidireccional entre los marcos.
- Añade un bloque Markdown del pack Básico con los valores por defecto, y notas breves de Texto.
La página del caso de uso de Kubernetes lista más objetos. El ejemplo usa 11 nodos de diagrama, dentro del plan Free.
Preguntas frecuentes
¿Cómo se comunican los pods entre nodos en Kubernetes? A través del plugin CNI: un overlay envuelve los paquetes de los pods entre las IP de los nodos, o el enrutamiento nativo transporta directamente las direcciones de los pods. En ambos casos, los pods ven las IP reales unos de otros.
¿Cuál es la diferencia entre una IP de pod y una ClusterIP? Una IP de pod pertenece a un solo pod y cambia cuando el pod se reemplaza. Una ClusterIP es la IP virtual estable de un Service, que kube-proxy o un reemplazo eBPF reescribe a una IP de pod actual.
¿Qué modo de kube-proxy debería usar? En Kubernetes 1.37, iptables es el modo por defecto, nftables es GA desde la 1.33 y será el modo por defecto, e IPVS está obsoleto. Con kernel 5.13 o posterior, nftables es la mejor opción a largo plazo.
¿Funcionan las NetworkPolicies con todos los plugins CNI? No. La API las acepta en todas partes, pero solo las aplican los plugins que implementan NetworkPolicy: Calico, Cilium y AWS VPC CNI con la política de red activada lo hacen; Flannel por sí solo, no.
¿Debería usar Ingress o la Gateway API? La Gateway API para lo nuevo: Kubernetes la recomienda, e Ingress está congelado. Los recursos Ingress existentes siguen funcionando, pero planifica la salida de Ingress NGINX, que ya no recibe correcciones de seguridad.