No hace mucho me enfrenté a una tarea muy inusual: configurar el enrutamiento para MetalLB. Todo estaría bien, porque... Normalmente MetalLB no requiere ninguna acción adicional, pero en nuestro caso tenemos un cluster bastante grande con una configuración de red muy sencilla.
En este artículo, le diré cómo configurar el enrutamiento basado en fuentes y políticas para la red externa de su clúster.
No entraré en detalles sobre la instalación y configuración de MetalLB, ya que supongo que ya tienes algo de experiencia. Sugiero ir directo al grano, es decir, configurar el enrutamiento. Entonces tenemos cuatro casos:
Caso 1: cuando no se requiere configuración
Veamos un caso sencillo.
No se requiere configuración de enrutamiento adicional cuando las direcciones emitidas por MetalLB están en la misma subred que las direcciones de sus nodos.
Por ejemplo, tienes una subred 192.168.1.0/24
, tiene un enrutador 192.168.1.1
y sus nodos reciben direcciones: 192.168.1.10-30
, luego para MetalLB puedes ajustar el rango 192.168.1.100-120
y asegúrese de que funcionarán sin ninguna configuración adicional.
¿Porqué es eso? Porque tus nodos ya tienen rutas configuradas:
# ip route
default via 192.168.1.1 dev eth0 onlink
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10
Y las direcciones del mismo rango las reutilizarán sin ninguna acción adicional.
Caso 2: Cuando se requiere personalización adicional
Debe configurar rutas adicionales siempre que sus nodos no tengan una dirección IP configurada o una ruta a la subred para la cual MetalLB emite direcciones.
Te lo explicaré con un poco más de detalle. Siempre que MetalLB genera una dirección, se puede comparar con una asignación simple como:
ip addr add 10.9.8.7/32 dev lo
Presta atención a:
- a) La dirección se asigna con un prefijo.
/32
es decir, no se agregará automáticamente una ruta a la subred (es solo una dirección) - b) La dirección se adjunta a cualquier interfaz de nodo (por ejemplo, loopback). Vale la pena mencionar aquí las características de la pila de red de Linux. No importa a qué interfaz agregue la dirección, el kernel siempre procesará las solicitudes arp y enviará respuestas arp a cualquiera de ellas, este comportamiento se considera correcto y, además, se usa bastante en un entorno tan dinámico como Kubernetes.
Este comportamiento se puede personalizar, por ejemplo habilitando arp estricto:
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
En este caso, las respuestas arp solo se enviarán si la interfaz contiene explícitamente una dirección IP específica. Esta configuración es necesaria si planea utilizar MetalLB y su kube-proxy se ejecuta en modo IPVS.
Sin embargo, MetalLB no utiliza el kernel para procesar solicitudes arp, sino que lo hace él mismo en el espacio de usuario, por lo que esta opción no afectará el funcionamiento de MetalLB.
Volvamos a nuestra tarea. Si la ruta para las direcciones emitidas no existe en sus nodos, agréguela por adelantado a todos los nodos:
ip route add 10.9.8.0/24 dev eth1
Caso 3: cuando necesita enrutamiento basado en origen
Deberá configurar el enrutamiento basado en origen cuando reciba paquetes a través de una puerta de enlace independiente, no la configurada de forma predeterminada; por lo tanto, los paquetes de respuesta también deben pasar por la misma puerta de enlace.
Por ejemplo, tienes la misma subred 192.168.1.0/24
dedicado a sus nodos, pero desea emitir direcciones externas utilizando MetalLB. Supongamos que tiene varias direcciones de una subred 1.2.3.0/24
ubicado en la VLAN 100 y desea usarlos para acceder a los servicios de Kubernetes externamente.
Al contactar 1.2.3.4
realizará solicitudes desde una subred diferente a la 1.2.3.0/24
y espera una respuesta. El nodo que actualmente es el maestro de la dirección emitida por MetalLB. 1.2.3.4
, recibirá el paquete del enrutador 1.2.3.1
, pero la respuesta para él necesariamente debe ir por el mismo camino, a través de 1.2.3.1
.
Dado que nuestro nodo ya tiene una puerta de enlace predeterminada configurada 192.168.1.1
, entonces, de forma predeterminada, la respuesta irá a él y no a 1.2.3.1
, a través del cual recibimos el paquete.
¿Cómo afrontar esta situación?
En este caso, debe preparar todos sus nodos de tal manera que estén listos para atender direcciones externas sin configuración adicional. Es decir, para el ejemplo anterior, debe crear una interfaz VLAN en el nodo de antemano:
ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 up
Y luego agregue rutas:
ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100
Tenga en cuenta que agregamos rutas a una tabla de enrutamiento separada 100
contendrá sólo dos rutas necesarias para enviar un paquete de respuesta a través de la puerta de enlace 1.2.3.1
, ubicado detrás de la interfaz eth0.100
.
Ahora necesitamos agregar una regla simple:
ip rule add from 1.2.3.0/24 lookup 100
que dice explícitamente: si la dirección de origen del paquete está en 1.2.3.0/24
, entonces necesitas usar la tabla de enrutamiento 100
. En él ya hemos descrito la ruta que lo llevará a través 1.2.3.1
Caso 4: cuando necesita enrutamiento basado en políticas
La topología de la red es la misma que en el ejemplo anterior, pero digamos que también desea poder acceder a direcciones de grupos externos. 1.2.3.0/24
de tus vainas:
La peculiaridad es que al acceder a cualquier dirección en 1.2.3.0/24
, el paquete de respuesta llega al nodo y tiene una dirección de origen en el rango 1.2.3.0/24
será enviado obedientemente a eth0.100
, pero queremos que Kubernetes lo redirija a nuestro primer pod, que generó la solicitud original.
Resolver este problema resultó difícil, pero fue posible gracias al enrutamiento basado en políticas:
Para una mejor comprensión del proceso, aquí hay un diagrama de bloques de netfilter:
Primero, como en el ejemplo anterior, creemos una tabla de enrutamiento adicional:
ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100
Ahora agreguemos algunas reglas a iptables:
iptables -t mangle -A PREROUTING -i eth0.100 -j CONNMARK --set-mark 0x100
iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j RETURN
iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark
Estas reglas marcarán las conexiones entrantes a la interfaz. eth0.100
, marcando todos los paquetes con la etiqueta 0x100
, las respuestas dentro de la misma conexión también se marcarán con la misma etiqueta.
Ahora podemos agregar una regla de enrutamiento:
ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100
Es decir, todos los paquetes con una dirección de origen. 1.2.3.0/24
y etiquetar 0x100
debe enrutarse utilizando una tabla 100
.
Así, otros paquetes recibidos en otra interfaz no están sujetos a esta regla, lo que permitirá enrutarlos utilizando herramientas estándar de Kubernetes.
Hay una cosa más, en Linux existe el llamado filtro de ruta inversa, que lo estropea todo; realiza una verificación simple: para todos los paquetes entrantes, cambia la dirección de origen del paquete con la dirección del remitente y verifica si el paquete puede salir por la misma interfaz en la que fue recibido, de lo contrario lo filtrará.
El problema es que en nuestro caso no funcionará correctamente, pero podemos desactivarlo:
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filter
Tenga en cuenta que el primer comando controla el comportamiento global de rp_filter; si no está deshabilitado, el segundo comando no tendrá ningún efecto. Sin embargo, las interfaces restantes permanecerán con rp_filter habilitado.
Para no limitar completamente el funcionamiento del filtro, podemos utilizar la implementación rp_filter para netfilter. Usando rpfilter como módulo de iptables, puedes configurar reglas bastante flexibles, por ejemplo:
iptables -t raw -A PREROUTING -i eth0.100 -d 1.2.3.0/24 -j RETURN
iptables -t raw -A PREROUTING -i eth0.100 -m rpfilter --invert -j DROP
habilitar rp_filter en la interfaz eth0.100
para todas las direcciones excepto 1.2.3.0/24
.
Fuente: habr.com