Kubernetes en premisa con MetalLB en modo BGP.

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.

Autor: Ariel Antigua

Automation guy with a love for Containers!