Kubernetes en premisa con MetalLB en modo BGP.
Una de las desventajas de tener un cluster de k8s en premisa es la falta de LoadBalancer. Gracias a MetalLB esto está resuelto de una manera fácil y elegante.
Cuando queremos publicar servicios en k8s, lo hacemos usando un Ingress Controller (nginx o Traefik por nombrar algunos). Este servicio se apoya de las direcciones IP de los hosts si no tenemos LoadBalancer. El tema se complica cuando queremos publicar servicios que no son HTTP o HTTPS, sé que nginx puede publicar otros protocolos. La ventaja de un IP via LoadBalancer es que podemos usarlos en varios servicios (pods) y publicar cualquier puerto en TCP o UDP.
MetalLB
Los requerimientos son los siguientes:
- Un cluster de k8s en la versión 1.9.0 o más reciente y que no tenga un tipo de LoadBalancer en funcionamiento, esto quiere decir que tendríamos problemas usando esta solución en GKE por ejemplo.
- Una configuración de cluster que pueda coexistir con MetalLB https://metallb.universe.tf/installation/network-addons/
- Direcciones IPv4 para asignar usando este servicio.
- Dependiendo del modo operativo, podríamos necesitar un router que soporte BGP.
- https://metallb.universe.tf/#requirements
En mis primeras pruebas, MetalLB fue configurado usando Layer2 (capa 2), es la forma más rápida de probar esta solución, después de varios días de desplegar aplicaciones que hacían uso de LoadBalancer me di cuenta de que algunas direcciones IP de momento no respondían a las peticiones ARP que son necesarias para alcanzar dicha dirección IP. Por esta razón ahora desplegare MetalLB usando BGP.
En modo BGP, la configuración es más extensa y cuanta con campos que tendrán sentido si se ha usado BGP anteriormente.
En mi caso, ya tengo algo de experiencia usando este protocolo y por eso decidí cambiar de Layer2 a BGP en lugar de buscar una solución al problema descrito anteriormente. Además, mi router de core soporte BGP.
Configuración en Mikrotik RouterOS.
Debemos preparar el router (o en su defecto un Switch con L3 & BGP) para aceptar sesiones BGP desde los nodos de k8s, MetalLB ejecutara un agente en todos los nodos de k8s y estos iniciaran una sesión BGP con nuestro router.
Configuración básica en RouterOS – CLI:
/routing bgp instance set default as=64635 redistribute-connected=yes redistribute-static=yes router-id=10.45.254.2 /routing bgp peer add multihop=no name=kube1 remote-address=172.22.35.25 remote-as=64636 ttl=default add multihop=no name=kube2 remote-address=172.22.35.26 remote-as=64636 ttl=default add multihop=no name=kube3 remote-address=172.22.35.27 remote-as=64636 ttl=default
Instalando MetalLB.
kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml
También se puede instalar usando Helm, para más información: https://metallb.universe.tf/installation/
Revisamos nuestro entorno k8s para validar que tenemos los componentes de MetalLB en un correcto estado.
¡Excelente! Tenemos tres speaker, uno en cada nodo.
Configurando MetalLB.
Necesitamos un kind tipo ConfigMap para aplicar la configuración deseada a MetalLB.
apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | peers: - peer-address: 172.22.35.1 peer-asn: 64635 my-asn: 64636 address-pools: - name: default protocol: bgp addresses: - 172.22.35.64/26 bgp-advertisements: - aggregation-length: 32 localpref: 100 communities: - name: public protocol: bgp addresses: - 200.1.154.64/26 auto-assign: false bgp-advertisements: - aggregation-length: 32 localpref: 100 communities:
Aplicamos el configMap:
Si todos nuestros parámetros son correctos, revisamos en el core router y debemos tener las sesiones BGP establecidas.
¿Qué tenemos?
En este punto deberemos contar con la opción de seleccionar una dirección IP, en mi caso podría ser del pool llamado default o del pool llamado public, se puede diferenciar que las IP del pool llamado public son ruteables y existen en la tabla de Internet (200.1.154.0/24).
Vamos a inicializar un pod que haga uso de una dirección del pool default. Para esto usare un contenedor con un servicio de SMTP el cual no necesita almacenamiento.
Deployment + Service
apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: smtp name: smtp spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: smtp strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 type: RollingUpdate template: metadata: creationTimestamp: null labels: app: smtp spec: containers: - env: - name: RELAY_NETWORKS value: 172.22.35.0/24:10.45.0.0/16:200.1.154.0/24 image: namshi/smtp imagePullPolicy: IfNotPresent name: smtp ports: - containerPort: 25 name: smtp-port --- apiVersion: v1 kind: Service metadata: creationTimestamp: null labels: app: smtp name: smtp annotations: metallb.universe.tf/allow-shared-ip: ekvm metallb.universe.tf/address-pool: default spec: externalTrafficPolicy: Local ports: - name: smtp nodePort: 25 port: 25 protocol: TCP targetport: smtp-port selector: app: smtp loadBalancerIP: 172.22.35.70 type: LoadBalancer</pre>
¡Excelente!
¡Nuestro servicio de SMTP responde en el IP asignado por MetalLB!
El próximo paso es el almacenamiento.