Cerrábamos el módulo 3 señalando que la red de Rutas Norte es completamente plana: cualquier pod de rutas-norte-pro puede abrir una conexión a postgres-reservas:5432, y ningún cliente de internet puede llegar todavía a www.rutasnorte.example. Antes de poner puertas (las políticas de red de 04-06) y ventanas (el Ingress de 04-04), hay que entender cómo está construida la casa. Esta lección es la que explica qué hay realmente debajo de esa 10.244.x.x que ves en kubectl get pods -o wide, quién crea esa interfaz de red, por qué la IP de un Service no aparece en ninguna tarjeta de red de ninguna máquina, y cómo un paquete que sale de api-reservas acaba entrando en postgres-reservas. Es la lección más "de fontanería" del curso, y también la que hace que todo lo demás deje de parecer magia.
Contenido
- El modelo de red de Kubernetes y sus cuatro reglas
- Por qué ese modelo simplifica todo lo demás
- Los cuatro planos de comunicación
- CNI: la interfaz que conecta el pod a la red
- Los plugins: Flannel, Calico, Cilium y Weave Net
- Superposición (VXLAN), enrutamiento nativo (BGP) y eBPF
- Los rangos de direcciones del clúster
- Cómo se implementa de verdad un ClusterIP: kube-proxy
- Recorrido completo de un paquete en Rutas Norte
- Comprobación práctica en minikube
- El modelo de red de Kubernetes y sus cuatro reglas
Kubernetes no implementa la red. Lo que hace es imponer un contrato: define un modelo que cualquier implementación de red debe cumplir, y deja que otros lo cumplan como quieran. El contrato tiene cuatro reglas.
| # | Regla | Qué significa en la práctica |
|---|---|---|
| 1 | Cada pod tiene su propia dirección IP | No se comparte la IP del nodo ni se hace mapeo de puertos. api-reservas escucha en el 3000 dentro de su propia IP, aunque haya cinco réplicas en el mismo nodo |
| 2 | Todo pod puede hablar con todo pod sin NAT | Da igual si están en el mismo nodo o en nodos distintos: la conexión es directa, sin traducción de direcciones |
| 3 | Los agentes del nodo alcanzan a los pods de ese nodo | El kubelet, y por tanto las sondas de salud de 07-01, pueden conectar con los pods sin trucos |
| 4 | La IP que el pod ve de sí mismo es la que ven los demás | Si dentro del contenedor hostname -i dice 10.244.1.37, los demás pods lo ven exactamente como 10.244.1.37 |
La cuarta regla parece redundante, pero es la que prohíbe explícitamente el modelo de Docker clásico. En un docker run -p 8080:80, el contenedor cree que es 172.17.0.4:80 mientras el resto del mundo lo ve como 192.168.1.10:8080. Esa discrepancia rompe cualquier protocolo que anuncie su propia dirección: bases de datos en clúster, sistemas de descubrimiento, registros de servicio. Kubernetes la elimina de raíz.
Fíjate en lo que no dice el contrato: nada sobre cómo se enrutan los paquetes, si hay encapsulación, qué rango de IPs se usa o si hay cortafuegos. Todo eso es libertad de implementación.
- Por qué ese modelo simplifica todo lo demás
El modelo se resume en una frase: "IP por pod, red plana". Sus consecuencias son enormes.
- Los puertos dejan de ser un recurso escaso. En un mundo de mapeo de puertos, desplegar tres réplicas de
api-reservasen el mismo nodo obliga a asignarles 3000, 3001 y 3002, y a que alguien lleve la cuenta. Con IP por pod, las tres escuchan en el 3000. El manifiesto no cambia según dónde caiga el pod. - Las aplicaciones no necesitan saber que están en Kubernetes. Una aplicación que funciona en una máquina virtual funciona en un pod: abre su puerto y conecta a
host:puerto. Esto es lo que permitió migrar tanto software sin reescribirlo. - El descubrimiento se vuelve trivial. Como no hay NAT, la IP que el
EndpointSlicede 02-05 apunta es directamente conectable. Sin esa garantía, cada Service tendría que traducir direcciones y puertos por nodo. - La migración de pods es transparente. Un pod muere y otro nace con otra IP, pero la mecánica de conexión es idéntica.
El precio es que por defecto no hay ningún aislamiento. La regla 2 dice "todo pod puede hablar con todo pod", y eso es exactamente lo que significa: el pod más insignificante de rutas-norte-pro puede intentar conectar con postgres-reservas. Las NetworkPolicies de 04-06 existen precisamente para recortar ese permiso universal.
- Los cuatro planos de comunicación
Cuando alguien dice "la red de Kubernetes" suele estar mezclando cuatro problemas distintos, con soluciones distintas.
flowchart TB
subgraph N1["Nodo 1"]
subgraph P1["Pod api-reservas 10.244.1.5"]
C1["contenedor api"]
C2["sidecar de logs"]
end
P2["Pod redis-cache<br/>10.244.1.9"]
end
subgraph N2["Nodo 2"]
P3["Pod postgres-reservas<br/>10.244.2.4"]
end
EXT["Cliente de internet"]
SVC["Service ClusterIP<br/>10.96.31.72"]
C1 -.->|"1. localhost"| C2
P1 -->|"2. pod a pod, sin NAT"| P3
P1 -->|"3. pod a Service"| SVC
SVC --> P2
EXT -->|"4. exterior a Service"| SVC
| Plano | Cómo se resuelve | Dónde se estudia |
|---|---|---|
| 1. Contenedor ↔ contenedor en el mismo pod | Comparten el network namespace: se ven por localhost y compiten por los mismos puertos |
Ya visto en 02-01; patrones en 06-04 |
| 2. Pod ↔ pod | Lo implementa el plugin CNI. Es el tema central de esta lección | Aquí, apartados 4 a 7 |
| 3. Pod ↔ Service | Lo implementa kube-proxy (o el CNI, si sustituye a kube-proxy) reescribiendo destinos | Aquí, apartado 8 |
| 4. Exterior ↔ Service | Tipos de Service NodePort/LoadBalancer e Ingress |
04-02 y 04-04 |
Un error de diagnóstico muy común es atacar el plano equivocado. Si api-reservas no llega a postgres-reservas por IP de pod, el problema es del CNI. Si llega por IP de pod pero no por el nombre del Service, el problema es de kube-proxy o del DNS de 04-03. Separar los planos ahorra horas.
- CNI: la interfaz que conecta el pod a la red
CNI (Container Network Interface) es una especificación de la CNCF, muy corta y deliberadamente aburrida: define cómo un runtime de contenedores le pide a un programa externo que conecte o desconecte un contenedor de una red. No es un producto: es un contrato entre dos partes.
La cadena de invocación al crear un pod es esta:
sequenceDiagram
participant K as kubelet
participant CR as containerd
participant CNI as Plugin CNI
participant IPAM as IPAM
K->>CR: crear sandbox del pod
CR->>CR: crear network namespace vacio
CR->>CNI: ADD (namespace, ID del pod)
CNI->>IPAM: dame una IP del podCIDR del nodo
IPAM-->>CNI: 10.244.1.37/24
CNI->>CNI: crear veth, mover un extremo al ns
CNI->>CNI: asignar IP, ruta por defecto
CNI-->>CR: OK, IP 10.244.1.37
CR-->>K: sandbox listo
K->>CR: arrancar los contenedores del pod
Los puntos que conviene fijar:
- Quien invoca al plugin es el kubelet a través del runtime (containerd, en nuestro clúster). El apiserver no toca la red.
- El plugin se instala como un binario en
/opt/cni/biny se configura con un fichero JSON en/etc/cni/net.d. Por eso los plugins se despliegan casi siempre como un DaemonSet (06-02): un pod por nodo que copia el binario y el fichero de configuración al arrancar y luego se queda ejecutando su agente. - Las operaciones son
ADD,DEL,CHECKyVERSION. Al borrar el pod se invocaDELy la IP vuelve al pool. - IPAM (IP Address Management) es un subcomponente: decide qué IP concreta se asigna dentro del rango que le toca al nodo.
Lo que hace el plugin en un ADD típico, traducido a comandos que ya conoces de Linux:
- Crea un par veth (dos interfaces virtuales unidas como un cable).
- Deja un extremo en el network namespace del host (con nombre tipo
veth3a7f2c1@if3) y mueve el otro dentro del namespace del pod, donde se llamaeth0. - Pide una IP al IPAM y se la asigna a
eth0. - Añade la ruta por defecto del pod, apuntando al puente o a la puerta de enlace del nodo.
- En el host, conecta su extremo al puente (
cni0,docker0,cbr0) o crea la ruta correspondiente. - Devuelve al runtime la IP asignada, que acaba en
status.podIPdel pod.
Si el CNI falla o no está instalado, verás el síntoma clásico:
NAME READY STATUS RESTARTS AGE
api-reservas-7d9f5c8b4-x2kp9 0/1 ContainerCreating 0 3m
Events:
Warning FailedCreatePodSandBox kubelet Failed to create pod sandbox:
plugin type="bridge" failed (add): failed to set bridge addr: could not add IPPods eternamente en ContainerCreating con errores de sandbox casi siempre son problema de red del nodo, no de tu manifiesto.
- Los plugins: Flannel, Calico, Cilium y Weave Net
Elegir plugin es una de las decisiones más duraderas de un clúster: cambiarlo con carga en producción es una operación delicada. Estos son los cuatro históricamente más usados.
| Flannel | Calico | Cilium | Weave Net | |
|---|---|---|---|---|
| Modelo de datos | Superposición VXLAN (o host-gw) |
Enrutamiento nativo L3 con BGP; opción VXLAN/IPIP | eBPF en el kernel; opcional VXLAN/geneve o nativo | Superposición propia (VXLAN "fast datapath") |
| NetworkPolicy | No la implementa | Sí, y además políticas propias más ricas | Sí, incluidas políticas L7 (HTTP, Kafka, DNS) | Sí (soporte básico) |
| Rendimiento | Bueno; penalización por encapsulación | Muy bueno en modo nativo (sin encapsular) | El mejor: puede sustituir a kube-proxy | Aceptable; el menos rápido de los cuatro |
| Complejidad | Mínima | Media (BGP requiere entender la red física) | Alta; requiere kernel moderno | Baja |
| Cifrado en tránsito | No | WireGuard | WireGuard o IPsec | Sí, integrado |
| Observabilidad | Ninguna | Métricas Prometheus | Hubble: flujos, mapas de servicio, L7 | Básica |
| Cuándo elegirlo | Aprender, laboratorio, clúster sin requisitos de aislamiento | Producción con NetworkPolicy y red bajo tu control | Producción exigente, observabilidad, políticas L7, malla sin sidecars | Instalaciones sencillas donde ya está en uso |
Consecuencia práctica para Rutas Norte, y hay que subrayarla: si el clúster usa Flannel, las NetworkPolicies que escribas en 04-06 se aplicarán sin error y no harán absolutamente nada. El objeto se crea, kubectl get networkpolicy lo lista, y el tráfico sigue pasando. Es el fallo silencioso más peligroso de esta parte del curso, y por eso lo verificaremos explícitamente.
Los clústeres gestionados de 10-06 traen su propio plugin (Amazon VPC CNI, Azure CNI, el de GKE), que suele asignar a los pods IPs de la red real de la nube. Eso hace que un pod sea directamente enrutable desde otras máquinas de la VPC, con la contrapartida de consumir direcciones del espacio corporativo.
- Superposición (VXLAN), enrutamiento nativo (BGP) y eBPF
El problema que resuelven todos es el mismo: el pod 10.244.1.5 está en el nodo A y quiere hablar con 10.244.2.4, que está en el nodo B. La red física entre nodos no sabe nada de 10.244.0.0/16: si le entregas ese paquete tal cual, lo descarta.
Red superpuesta (VXLAN)
Se construye una red virtual encima de la física. El nodo origen mete el paquete original entero dentro de un paquete UDP dirigido a la IP real del nodo destino (puerto 8472 en VXLAN), que lo desencapsula y lo entrega al pod.
[ IP nodoA -> IP nodoB | UDP 8472 | VXLAN | IP 10.244.1.5 -> 10.244.2.4 | TCP 5432 | datos ]
\_______________ sobre exterior _______________/ \____________ paquete original ______/- Ventaja decisiva: funciona sobre cualquier red, sin tocar routers ni pedir nada al equipo de redes. Por eso es el modo por defecto de tanta instalación.
- Coste: la cabecera añade unos 50 bytes, lo que reduce la MTU efectiva (de 1500 a 1450 típicamente) y provoca fragmentación si algo la ignora; hay trabajo extra de encapsular y desencapsular en cada paquete; y el tráfico es opaco para los cortafuegos y analizadores de la red física, que solo ven UDP entre nodos.
Enrutamiento nativo (BGP)
No se encapsula nada. Cada nodo anuncia por BGP a los routers de la red física: "el rango 10.244.2.0/24 se alcanza a través de mí". La red física aprende las rutas y entrega los paquetes de pod directamente.
- Ventaja: sin sobrecoste, MTU íntegra, el tráfico de pods es visible y filtrable con las herramientas de red existentes.
- Requisito: la red debe permitirlo. En un centro de datos propio con routers que hablen BGP, es lo ideal. En muchas nubes o en redes corporativas cerradas, no es posible y se cae de vuelta a encapsulación.
eBPF
eBPF permite cargar programas verificados dentro del kernel de Linux y engancharlos a puntos concretos de la pila de red. Cilium lo usa para tomar decisiones de enrutamiento, balanceo y política antes de que el paquete recorra toda la maquinaria de iptables.
- Puede sustituir por completo a kube-proxy, eliminando las cadenas de
iptablesque veremos en el apartado 8. - Escala mucho mejor: donde
iptablesdegrada con miles de Services, eBPF usa tablas hash de coste constante. - Habilita políticas con conocimiento de HTTP (por ruta y método) e identidad de carga de trabajo.
- A cambio, exige un kernel razonablemente reciente y sube el listón de conocimiento para depurar.
| Criterio | VXLAN | BGP nativo | eBPF |
|---|---|---|---|
| Sobrecoste por paquete | Alto (~50 B + encapsulado) | Nulo | Nulo o negativo |
| Requisitos de la red física | Ninguno | Debe enrutar/hablar BGP | Ninguno |
| Visibilidad desde la red física | Baja | Alta | Media |
| Escalado con muchos Services | Depende de kube-proxy | Depende de kube-proxy | Excelente |
- Los rangos de direcciones del clúster
En un clúster conviven tres espacios de direcciones que no hay que confundir nunca.
| Rango | Qué direcciona | Ejemplo en minikube | ¿Enrutable fuera? |
|---|---|---|---|
| Red de nodos | Las máquinas físicas o virtuales | 192.168.49.0/24 |
Sí, es la red real |
podCIDR / cluster CIDR |
Las IPs de los pods | 10.244.0.0/16, troceado en /24 por nodo |
Solo dentro del clúster |
serviceCIDR |
Las IPs virtuales de los Services | 10.96.0.0/12 |
No existe en ninguna interfaz |
El cluster CIDR global se reparte: el kube-controller-manager, con --allocate-node-cidrs, asigna a cada nodo una porción (por defecto un /24, es decir 254 pods útiles por nodo) y la escribe en spec.podCIDR del objeto Node. El IPAM del plugin CNI reparte IPs dentro de esa porción. Así se garantiza que dos nodos nunca asignen la misma IP.
Dónde se configuran:
# Al crear el cluster con kubeadm (leccion 10-02)
kubeadm init --pod-network-cidr=10.244.0.0/16 --service-cidr=10.96.0.0/12
# En minikube, al crear el perfil
minikube start -p rutas-norte --extra-config=kubeadm.pod-network-cidr=10.244.0.0/16Tres avisos que ahorran incidentes:
- No se pueden cambiar en caliente. Cambiar el
serviceCIDRde un clúster vivo no es una operación soportada; se planifica antes de crearlo. - No deben solaparse con la red corporativa. Si tu empresa usa
10.96.0.0/16para servidores, los pods de Rutas Norte no podrán hablar con ellos: el nodo creerá que esas IPs son Services del clúster. - El
/24por nodo es un límite real. Un nodo grande con 300 pods se queda sin direcciones aunque le sobre CPU y memoria.
- Cómo se implementa de verdad un ClusterIP: kube-proxy
Aquí está la revelación de la lección. Cuando en 02-05 creaste el Service postgres-reservas con clusterIP: 10.96.140.22, esa dirección no se asignó a ninguna interfaz de ninguna máquina. Es una ficción. No responde a ping. No hay ningún proceso escuchando en ella.
Lo que existe son reglas de reescritura de destino instaladas en cada nodo por kube-proxy, un DaemonSet del namespace kube-system que observa la API y, cada vez que cambia un Service o un EndpointSlice, actualiza esas reglas.
flowchart LR
API["kube-apiserver<br/>Services + EndpointSlices"] -->|watch| KP["kube-proxy<br/>(un pod por nodo)"]
KP -->|programa reglas| DP["iptables / IPVS<br/>del kernel del nodo"]
POD["Pod origen"] -->|"conecta a 10.96.140.22:5432"| DP
DP -->|"DNAT a 10.244.2.4:5432"| DEST["Pod postgres-reservas"]
Modo iptables (el más habitual)
Para cada Service, kube-proxy crea una cadena KUBE-SERVICES que casa por IP y puerto de destino, salta a una cadena KUBE-SVC-XXXX propia del Service, y de ahí reparte entre las cadenas KUBE-SEP-YYYY, una por endpoint, cada una con su regla DNAT.
# Cadena de entrada: captura el trafico hacia la IP del Service
-A KUBE-SERVICES -d 10.96.140.22/32 -p tcp --dport 5432
-j KUBE-SVC-QW3RTY5432ABCD
# Reparto entre 2 endpoints con probabilidad estadistica
-A KUBE-SVC-QW3RTY5432ABCD -m statistic --mode random --probability 0.50000
-j KUBE-SEP-AAA111
-A KUBE-SVC-QW3RTY5432ABCD -j KUBE-SEP-BBB222
# Cada endpoint: traduccion de destino a la IP real del pod
-A KUBE-SEP-AAA111 -p tcp -j DNAT --to-destination 10.244.2.4:5432
-A KUBE-SEP-BBB222 -p tcp -j DNAT --to-destination 10.244.3.7:5432De aquí salen tres hechos importantes:
- El balanceo es aleatorio por conexión, no por petición. Una conexión TCP larga (una sesión de PostgreSQL, un WebSocket) queda pegada a un pod hasta que se cierra. Por eso una API que multiplexa peticiones sobre pocas conexiones puede repartir mal la carga.
- La probabilidad es en cascada: con 3 endpoints, las reglas llevan 1/3, luego 1/2, luego el resto. Así sale uniforme.
- El coste es lineal:
iptablesevalúa reglas en orden. Con decenas de miles de Services, cada actualización obliga a reprogramar tablas enormes y aparecen latencias de sincronización. Es el motivo de que existan IPVS y eBPF.
Modo IPVS
IPVS es el balanceador de carga L4 del kernel de Linux, con tablas hash en lugar de listas de reglas.
iptables |
IPVS |
|
|---|---|---|
| Estructura | Lista de reglas evaluada en orden | Tabla hash, coste constante |
| Escalado | Degrada con miles de Services | Estable con decenas de miles |
| Algoritmos | Solo aleatorio | rr, lc, dh, sh, sed, nq |
| Depuración | iptables-save |
ipvsadm -Ln |
| Requisitos | Ninguno | Módulos ip_vs* cargados |
En modo IPVS sí aparece una interfaz kube-ipvs0 en el nodo con las IPs de los Services asignadas, pero es una interfaz dummy: sirve para que el kernel acepte los paquetes, no para responder.
La regla mental que hay que llevarse: el Service es una regla, no una máquina. Si un ClusterIP no responde, no busques un proceso caído: busca endpoints vacíos (selector que no casa, como viste en 02-05) o un kube-proxy que no está sincronizando.
- Recorrido completo de un paquete en Rutas Norte
Sigamos una conexión real: el pod api-reservas-7d9f5c8b4-x2kp9 (10.244.1.5, nodo 1) abre una conexión a postgres-reservas:5432 (ClusterIP 10.96.140.22), cuyo único endpoint es 10.244.2.4 en el nodo 2. El clúster usa VXLAN.
sequenceDiagram
participant APP as api-reservas (10.244.1.5)
participant DNS as CoreDNS
participant NS1 as Kernel nodo 1
participant NET as Red fisica
participant NS2 as Kernel nodo 2
participant PG as postgres-reservas (10.244.2.4)
APP->>DNS: postgres-reservas.rutas-norte-pro.svc.cluster.local?
DNS-->>APP: 10.96.140.22
APP->>NS1: SYN a 10.96.140.22:5432 (sale por eth0/veth)
NS1->>NS1: KUBE-SERVICES -> KUBE-SVC -> KUBE-SEP<br/>DNAT destino = 10.244.2.4:5432
NS1->>NS1: ruta: 10.244.2.0/24 esta via VXLAN en nodo2
NS1->>NET: encapsula UDP 8472 nodo1 -> nodo2
NET->>NS2: entrega el paquete exterior
NS2->>NS2: desencapsula: IP 10.244.1.5 -> 10.244.2.4
NS2->>PG: SYN por el veth del pod
PG-->>NS2: SYN-ACK a 10.244.1.5
NS2->>NET: vuelta encapsulada
NET->>NS1: entrega
NS1->>NS1: conntrack deshace el DNAT:<br/>origen pasa a 10.96.140.22:5432
NS1-->>APP: SYN-ACK aparentemente del ClusterIP
Los detalles que importan:
- La resolución del nombre ocurre primero y es independiente de todo lo demás (04-03).
- El DNAT ocurre en el nodo de origen, antes de enrutar. El nodo 2 nunca ve la IP del Service.
postgres-reservasve como origen10.244.1.5, la IP real del pod cliente, no la del nodo. Esto es la regla 2 (sin NAT de origen) y es lo que permite que las NetworkPolicies de 04-06 puedan identificar al emisor por sus etiquetas. Ojo: esto cambia cuando el tráfico entra desde fuera conexternalTrafficPolicy: Cluster(04-02).conntrackes imprescindible: el kernel recuerda la traducción para deshacerla en la respuesta. Una tablaconntrackllena provoca conexiones que se pierden aleatoriamente bajo carga, un síntoma clásico en los picos de puentes de Rutas Norte.
- Comprobación práctica en minikube
Todo lo anterior se puede tocar con las manos en el perfil rutas-norte.
Los rangos del clúster
# podCIDR asignado a cada nodo
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'# El serviceCIDR no se expone como campo; se deduce provocando un error
kubectl create svc clusterip prueba-cidr --tcp=80:80 --dry-run=server \
-o yaml --clusterip=1.2.3.4The Service "prueba-cidr" is invalid: spec.clusterIPs[0]:
Invalid value: []string{"1.2.3.4"}: failed to allocate IP 1.2.3.4:
the provided IP (1.2.3.4) is not in the valid range. The range of valid IPs is 10.96.0.0/12Es un truco muy útil: el mensaje de error te revela el rango exacto.
Las IPs reales de la plataforma
NAME READY STATUS IP NODE
api-reservas-7d9f5c8b4-x2kp9 1/1 Running 10.244.0.31 rutas-norte
postgres-reservas-5c9d7f-4nm2q 1/1 Running 10.244.0.14 rutas-norte
NAME TYPE CLUSTER-IP PORT(S)
postgres-reservas ClusterIP 10.96.140.22 5432/TCPLas IPs de pod están en 10.244.0.x (el podCIDR del único nodo) y la del Service en 10.96.x.x. Rangos distintos, naturalezas distintas.
Las reglas que no existen y las que sí
# La IP del Service no esta en ninguna interfaz del nodo
minikube -p rutas-norte ssh -- ip -4 addr show | grep -E "inet " inet 127.0.0.1/8 scope host lo
inet 192.168.49.2/24 brd 192.168.49.255 scope global eth0
inet 10.244.0.1/24 brd 10.244.0.255 scope global bridgeNi rastro de 10.96.140.22. Ahora las reglas:
-A KUBE-SERVICES -d 10.96.140.22/32 -p tcp -m comment
--comment "rutas-norte-pro/postgres-reservas cluster IP" -m tcp --dport 5432
-j KUBE-SVC-6GHJ2KLM4NOPQRSTEl comentario incluye <namespace>/<servicio>: es la forma más rápida de localizar las reglas de un Service concreto.
Un pod efímero con herramientas de red
La imagen nicolaka/netshoot trae dig, curl, nc, tcpdump, traceroute e ipvsadm. Es la navaja suiza para depurar redes en Kubernetes.
kubectl run netshoot --rm -it --restart=Never \
-n rutas-norte-pro --image=nicolaka/netshoot -- bashDentro del pod:
# 1. Mi propia IP: debe estar en el podCIDR del nodo
ip -4 addr show eth0 | grep inet
# inet 10.244.0.42/24 scope global eth0
# 2. Conectividad pod a pod DIRECTA (plano 2, salta el Service)
nc -zv 10.244.0.14 5432
# Connection to 10.244.0.14 5432 port [tcp/postgresql] succeeded!
# 3. Conectividad pod a Service (plano 3)
nc -zv postgres-reservas 5432
# Connection to postgres-reservas 5432 port [tcp/postgresql] succeeded!
# 4. Lo que demuestra el aviso del modulo 3: un pod cualquiera,
# sin ninguna relacion con la plataforma, llega a la base de datos
# con los datos personales de los clientes. Eso lo arregla 04-06.Si el paso 2 funciona y el 3 no, el CNI está bien y el problema es de Service o DNS. Si el 2 tampoco funciona, es el CNI o una política de red. Esa bifurcación es el 80 % del diagnóstico de red en Kubernetes.
Errores Comunes y Consejos
- Confundir los tres rangos. Ver
10.96.x.xy buscar en qué nodo está ese pod es un clásico. Regla:10.244.x.xes un pod (existe),10.96.x.xes un Service (es una regla),192.168.x.xes un nodo. - Hacer ping a un ClusterIP. No responde y eso es normal: las reglas de kube-proxy solo casan TCP/UDP en el puerto declarado, no ICMP. Un ping fallido a un Service no prueba nada. Usa
nc -zvocurl. - Solapar el
podCIDRo elserviceCIDRcon la red corporativa. Provoca fallos incomprensibles al hablar con servicios externos, como la pasarela de pagos. Compruébalo antes de crear el clúster, no después. - Ignorar la MTU con VXLAN. Conexiones que abren bien pero se cuelgan al transferir bloques grandes (una restauración de PostgreSQL, una respuesta JSON de 2 MB) suelen ser MTU mal ajustada. Diagnóstico:
ping -M do -s 1400 <ip>. - Elegir Flannel y escribir NetworkPolicies. Se aplican sin error y no filtran nada. Antes de confiar en una política, verifica que el CNI la implementa con una prueba real.
- Culpar al CNI de todo. Antes de eso, comprueba los planos por orden:
localhostdentro del pod, IP de pod, ClusterIP, nombre DNS. El primero que falle señala la capa culpable. - Consejo: guarda un alias para el pod de depuración.
alias kshoot='kubectl run netshoot-$RANDOM --rm -it --restart=Never --image=nicolaka/netshoot -- bash'. Con--rmno dejas basura en el clúster. - Consejo: en producción, revisa las métricas de
conntrack(nf_conntrack_countfrente anf_conntrack_max). En los picos de puentes de Rutas Norte, la tabla llena se manifiesta como errores de conexión intermitentes sin ningún pod caído.
Ejercicios
Ejercicio 1: Cartografiar la red del clúster
Sobre el perfil rutas-norte, documenta: el podCIDR del nodo, el serviceCIDR, la IP del nodo, y las IPs de los pods y Services de rutas-norte-pro. Clasifica cada dirección en su rango y explica cuáles existen físicamente y cuáles no.
Ejercicio 2: Demostrar que el ClusterIP es una ficción
Elige el Service redis-cache. Demuestra con tres comprobaciones que su IP no existe en ninguna interfaz, que sí existen reglas de iptables para ella, y que aun así se puede conectar. Explica quién hace la traducción y en qué nodo ocurre.
Ejercicio 3: Separar los planos ante una avería
Un compañero dice que api-reservas "no ve la base de datos". Diseña una secuencia de comprobaciones, de la capa más baja a la más alta, que permita decidir si el problema es del CNI, del Service, del DNS o de la propia aplicación. Ejecútala con netshoot y anota qué salida esperarías en cada caso de fallo.
Soluciones
Ejercicio 1
kubectl get nodes -o custom-columns=NODO:.metadata.name,\
POD_CIDR:.spec.podCIDR,IP:.status.addresses[0].address
kubectl create svc clusterip x --tcp=80:80 --clusterip=1.2.3.4 --dry-run=server 2>&1 | tail -1
kubectl get pods -n rutas-norte-pro -o custom-columns=POD:.metadata.name,IP:.status.podIP
kubectl get svc -n rutas-norte-pro -o custom-columns=SVC:.metadata.name,CLUSTERIP:.spec.clusterIPClasificación esperada:
| Dirección | Rango | ¿Existe? |
|---|---|---|
192.168.49.2 |
Red de nodos | Sí, es eth0 del nodo |
10.244.0.31 |
podCIDR |
Sí, es eth0 dentro del pod |
10.96.140.22 |
serviceCIDR |
No: solo reglas en el kernel |
10.244.0.1 |
podCIDR |
Sí, es el puente del nodo (puerta de enlace de los pods) |
Ejercicio 2
IP=$(kubectl get svc redis-cache -n rutas-norte-pro -o jsonpath='{.spec.clusterIP}')
# 1. No esta en ninguna interfaz
minikube -p rutas-norte ssh -- "ip -4 addr | grep $IP || echo 'NO APARECE: correcto'"
# 2. Si hay reglas
minikube -p rutas-norte ssh -- "sudo iptables-save -t nat | grep $IP"
# 3. Y sin embargo conecta
kubectl run t --rm -it --restart=Never -n rutas-norte-pro \
--image=nicolaka/netshoot -- nc -zv redis-cache 6379La traducción (DNAT) la hace el kernel del nodo donde corre el pod cliente, usando las reglas que kube-proxy programó a partir del EndpointSlice. El paquete sale del nodo ya con la IP real del pod destino.
Ejercicio 3
Secuencia de menor a mayor nivel, con la conclusión de cada fallo:
kubectl exec -it deploy/api-reservas -n rutas-norte-pro -- sh
# Plano 1: el proceso local escucha?
nc -zv localhost 3000 # falla -> la app no arranco: no es red
# Plano 2: IP de pod directa
PGIP=$(kubectl get pod -l app=postgres-reservas -n rutas-norte-pro \
-o jsonpath='{.items[0].status.podIP}')
nc -zv $PGIP 5432 # falla -> CNI o NetworkPolicy
# Plano 3: ClusterIP
nc -zv 10.96.140.22 5432 # falla (y el anterior OK) -> kube-proxy o endpoints vacios
kubectl get endpointslices -n rutas-norte-pro -l kubernetes.io/service-name=postgres-reservas
# Plano DNS
nslookup postgres-reservas # falla (y el anterior OK) -> CoreDNS o resolv.conf
# Plano aplicacion
psql -h postgres-reservas -U reservas -c 'select 1' # falla -> credenciales o base de datosCada escalón que funciona descarta una capa entera. El primero que falla nombra al culpable.
Conclusión
Ya sabes qué hay debajo de la red de Kubernetes. El modelo se apoya en cuatro reglas obligatorias —IP por pod, comunicación pod a pod sin NAT, los agentes del nodo alcanzan a sus pods, y la IP que el pod ve de sí mismo es la que ven los demás— y es esa uniformidad la que permite que las aplicaciones no sepan que están en un clúster y que los puertos dejen de ser un recurso a administrar. Distingues los cuatro planos de comunicación y sabes cuál diagnosticar primero cuando algo falla.
Has visto que Kubernetes delega la implementación en un plugin CNI, que el kubelet invoca a través del runtime al crear cada pod, y que hace un trabajo muy concreto: crear un par veth, pedir una IP al IPAM dentro del podCIDR del nodo y programar las rutas. Conoces las diferencias reales entre Flannel, Calico, Cilium y Weave Net, y en particular que Flannel no implementa NetworkPolicy, un detalle que condicionará la lección 04-06. Entiendes el compromiso entre encapsular con VXLAN (funciona en cualquier sitio, cuesta 50 bytes y MTU) y enrutar de forma nativa con BGP (gratis, pero exige colaboración de la red física), y por qué eBPF está desplazando a ambos enfoques en los clústeres exigentes.
Y sobre todo, has desmontado la ficción más útil de Kubernetes: el ClusterIP no existe. No está en ninguna interfaz, no responde a ping y no hay ningún proceso detrás. Solo hay reglas de iptables o entradas de IPVS que kube-proxy mantiene sincronizadas con los EndpointSlices, haciendo DNAT en el nodo de origen y deshaciéndolo en la respuesta gracias a conntrack. Has seguido un paquete de api-reservas a postgres-reservas de principio a fin y lo has comprobado en tu propio minikube.
Con la fontanería entendida, toca subir un piso. En 04-02 veremos que ClusterIP es solo uno de los cuatro tipos de Service, que cada uno se construye sobre el anterior, y cómo NodePort, LoadBalancer y ExternalName empiezan a abrir la plataforma al exterior: el primer paso para que un cliente que quiere comprar un billete de autobús pueda, por fin, llegar a tienda-web.
Curso de Kubernetes
Módulo 1: Introducción a Kubernetes
- ¿Qué es Kubernetes?
- Arquitectura de Kubernetes
- Conceptos y Terminología Clave
- Configuración de un Clúster de Kubernetes
- La CLI de Kubernetes: kubectl
- Objetos, Manifiestos YAML y el Modelo Declarativo
- El Proyecto del Curso: la Plataforma Rutas Norte
Módulo 2: Componentes Principales de Kubernetes
- Pods
- ReplicaSets
- Deployments
- Actualizaciones, Rollbacks y Estrategias de Despliegue
- Servicios
- Namespaces
- Etiquetas, Selectores y Anotaciones
Módulo 3: Gestión de Configuración y Secretos
- ConfigMaps
- Secrets
- Variables de Entorno
- Cuotas y Límites de Recursos
- LimitRanges y Clases de Calidad de Servicio (QoS)
- ServiceAccounts y Acceso a la API desde los Pods
Módulo 4: Redes en Kubernetes
- Redes de Clúster
- Tipos de Servicios
- DNS Interno y Descubrimiento de Servicios
- Controladores de Ingress
- TLS y Gestión de Certificados con cert-manager
- Políticas de Red
Módulo 5: Almacenamiento en Kubernetes
- Volúmenes
- Volúmenes Persistentes
- Reclamaciones de Volúmenes Persistentes
- Clases de Almacenamiento
- Aprovisionamiento Dinámico, Expansión y Snapshots
- Copias de Seguridad y Restauración de Datos
Módulo 6: Conceptos Avanzados de Kubernetes
- StatefulSets
- DaemonSets
- Trabajos y CronJobs
- Init Containers, Sidecars y Patrones Multi-Contenedor
- Planificación: Afinidad, Taints y Tolerations
- Definiciones de Recursos Personalizados (CRDs)
- Operadores y el Patrón Controlador
Módulo 7: Monitoreo y Registro
- Verificaciones de Salud y Sondas
- Servidor de Métricas y kubectl top
- Monitoreo con Prometheus
- Visualización y Alertas con Grafana y Alertmanager
- Registro Centralizado con Elasticsearch, Fluentd y Kibana (EFK)
- Depuración de Aplicaciones y Eventos del Clúster
Módulo 8: Seguridad en Kubernetes
- Control de Acceso Basado en Roles (RBAC)
- Contextos de Seguridad y Endurecimiento del Contenedor
- Políticas de Seguridad de Pods y Pod Security Standards
- Seguridad de Red
- Seguridad de Imágenes
- Auditoría, Escaneo y Gestión de Vulnerabilidades
Módulo 9: Escalado y Rendimiento
- Autoescalado Horizontal de Pods
- Autoescalado Vertical de Pods
- Autoescalado de Clúster
- Escalado por Eventos y Métricas Personalizadas con KEDA
- Alta Disponibilidad: PodDisruptionBudgets y Topología
- Ajuste de Rendimiento
Módulo 10: Ecosistema y Herramientas de Kubernetes
- Minikube y Entornos Locales con kind
- Kubeadm
- Helm
- Kustomize
- GitOps con Argo CD y Flux
- Kubernetes Gestionado: EKS, AKS y GKE
Módulo 11: Estudios de Caso y Aplicaciones del Mundo Real
- Despliegue de una Aplicación Web
- Ejecución de Aplicaciones con Estado
- CI/CD con Kubernetes
- Estrategias de Despliegue: Blue-Green y Canary
- Gestión Multi-Clúster
- Operación en Producción: Incidencias, Runbooks y Costes
