documentation.suse.com / Documentación de SUSE Edge / Guías prácticas / MetalLB en K3s (con el modo de capa 2)

25 MetalLB en K3s (con el modo de capa 2)

MetalLB es una implementación de equilibrador de carga para clústeres de Kubernetes en bare metal que utiliza protocolos de enrutamiento estándar.

En esta guía se explica cómo desplegar MetalLB en el modo de capa 2 (L2).

25.1 Por qué usar este método

MetalLB es una opción conveniente para el equilibrio de carga en clústeres bare metal de Kubernetes por varias razones:

  1. Integración nativa con Kubernetes: MetalLB se integra a la perfección con Kubernetes, lo que facilita el despliegue y la gestión mediante las herramientas y prácticas habituales de Kubernetes.

  2. Compatibilidad bare metal: a diferencia de los equilibradores de carga basados en la nube, MetalLB se ha diseñado específicamente para despliegues locales en las que los sistemas de equilibrio de carga tradicionales podrían no estar disponibles o no ser viables.

  3. Compatibilidad con múltiples protocolos: MetalLB admite el modo de capa 2 y BGP (Border Gateway Protocol, protocolo de gateway de frontera), lo que proporciona flexibilidad para diferentes arquitecturas y requisitos de red.

  4. Alta disponibilidad: dado que distribuye el equilibrio de carga entre varios nodos, MetalLB garantiza una alta disponibilidad y fiabilidad para sus servicios.

  5. Escalabilidad: MetalLB puede manejar despliegues a gran escala, y se va adaptando con su clúster de Kubernetes para satisfacer la demanda creciente.

En el modo de capa 2 (L2), un nodo asume la responsabilidad de anunciar un servicio a la red local. Desde la perspectiva de la red, simplemente parece que esa máquina tiene varias direcciones IP asignadas a su interfaz de red.

La mayor ventaja del modo de capa 2 es su universalidad: funciona en cualquier red Ethernet sin necesidad de hardware especial, ni siquiera de routers sofisticados.

25.2 MetalLB en K3s (con L2)

En este inicio rápido, se utilizará el modo L2. Esto significa que no se necesita equipo de red especial, solo tres IP libres dentro del rango de red.

25.3 Requisitos previos

  • Un clúster K3s donde se vaya a desplegar MetalLB.

Aviso
Aviso

K3S incluye su propio equilibrador de carga de servicios llamado Klipper. Debe inhabilitarlo para ejecutar MetalLB. Para inhabilitar Klipper, hay que instalar K3s mediante el indicador --disable=servicelb.

  • Helm

  • Tres direcciones IP libres dentro del rango de red. En este ejemplo 192.168.122.10-192.168.122.12

Importante
Importante

Debe asegurarse de que esas direcciones IP no están asignadas. En un entorno DHCP esas direcciones no deben formar parte del pool DHCP para evitar dobles asignaciones.

25.4 Despliegue

Utilizaremos el chart de Helm de MetalLB publicado como parte de la solución SUSE Edge:

helm install \
  metallb oci://registry.suse.com/edge/charts/metallb \
  --namespace metallb-system \
  --create-namespace

while ! kubectl wait --for condition=ready -n metallb-system $(kubectl get\
 pods -n metallb-system -l app.kubernetes.io/component=controller -o name)\
 --timeout=10s; do
 sleep 2
done

25.5 Configuración

En este punto, la instalación ya está completa. Ahora hay que configurarla con nuestros valores de ejemplo:

cat <<-EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: ip-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.122.10/32
  - 192.168.122.11/32
  - 192.168.122.12/32
EOF
cat <<-EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: ip-pool-l2-adv
  namespace: metallb-system
spec:
  ipAddressPools:
  - ip-pool
EOF

Ahora, está lista para usarse. Es posible personalizar muchas cosas en el modo L2, por ejemplo:

Y mucho más para BGP.

25.5.1 Traefik y MetalLB

Traefik se despliega por defecto con K3s (se puede inhabilitar con --disable=traefik) y se expone por defecto como LoadBalancer (para usarse con Klipper). Sin embargo, como hay que inhabilitar Klipper, el servicio Traefik para Ingress sigue siendo de tipo LoadBalancer. Por lo tanto, en el momento de desplegar MetalLB, la primera IP se asignará automáticamente para Traefik Ingress.

# Before deploying MetalLB
kubectl get svc -n kube-system traefik
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
traefik   LoadBalancer   10.43.44.113   <pending>     80:31093/TCP,443:32095/TCP   28s
# After deploying MetalLB
kubectl get svc -n kube-system traefik
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                      AGE
traefik   LoadBalancer   10.43.44.113   192.168.122.10   80:31093/TCP,443:32095/TCP   3m10s

Esto se aplicará más tarde en el proceso (Sección 25.6.1, “Ingress con MetalLB”).

25.6 Uso

Vamos a crear un despliegue de ejemplo:

cat <<- EOF | kubectl apply -f -
---
apiVersion: v1
kind: Namespace
metadata:
  name: hello-kubernetes
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: hello-kubernetes
  namespace: hello-kubernetes
  labels:
    app.kubernetes.io/name: hello-kubernetes
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-kubernetes
  namespace: hello-kubernetes
  labels:
    app.kubernetes.io/name: hello-kubernetes
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: hello-kubernetes
  template:
    metadata:
      labels:
        app.kubernetes.io/name: hello-kubernetes
    spec:
      serviceAccountName: hello-kubernetes
      containers:
        - name: hello-kubernetes
          image: "paulbouwer/hello-kubernetes:1.10"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          env:
          - name: HANDLER_PATH_PREFIX
            value: ""
          - name: RENDER_PATH_PREFIX
            value: ""
          - name: KUBERNETES_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: KUBERNETES_POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: KUBERNETES_NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: CONTAINER_IMAGE
            value: "paulbouwer/hello-kubernetes:1.10"
EOF

Y, por último, el servicio:

cat <<- EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: hello-kubernetes
  namespace: hello-kubernetes
  labels:
    app.kubernetes.io/name: hello-kubernetes
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: hello-kubernetes
EOF

Veámoslo en acción:

kubectl get svc -n hello-kubernetes
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
hello-kubernetes   LoadBalancer   10.43.127.75   192.168.122.11   80:31461/TCP   8s

curl http://192.168.122.11
<!DOCTYPE html>
<html>
<head>
    <title>Hello Kubernetes!</title>
    <link rel="stylesheet" type="text/css" href="/css/main.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu:300" >
</head>
<body>

  <div class="main">
    <img src="/images/kubernetes.png"/>
    <div class="content">
      <div id="message">
  Hello world!
</div>
<div id="info">
  <table>
    <tr>
      <th>namespace:</th>
      <td>hello-kubernetes</td>
    </tr>
    <tr>
      <th>pod:</th>
      <td>hello-kubernetes-7c8575c848-2c6ps</td>
    </tr>
    <tr>
      <th>node:</th>
      <td>allinone (Linux 5.14.21-150400.24.46-default)</td>
    </tr>
  </table>
</div>
<div id="footer">
  paulbouwer/hello-kubernetes:1.10 (linux/amd64)
</div>
    </div>
  </div>

</body>
</html>

25.6.1 Ingress con MetalLB

Como Traefik ya sirve como controlador de Ingress, podemos exponer todo el tráfico HTTP/HTTPS a través de un objeto Ingress como:

IP=$(kubectl get svc -n kube-system traefik -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
cat <<- EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-kubernetes-ingress
  namespace: hello-kubernetes
spec:
  rules:
  - host: hellok3s.${IP}.sslip.io
    http:
      paths:
        - path: "/"
          pathType: Prefix
          backend:
            service:
              name: hello-kubernetes
              port:
                name: http
EOF

Y, a continuación:

curl http://hellok3s.${IP}.sslip.io
<!DOCTYPE html>
<html>
<head>
    <title>Hello Kubernetes!</title>
    <link rel="stylesheet" type="text/css" href="/css/main.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu:300" >
</head>
<body>

  <div class="main">
    <img src="/images/kubernetes.png"/>
    <div class="content">
      <div id="message">
  Hello world!
</div>
<div id="info">
  <table>
    <tr>
      <th>namespace:</th>
      <td>hello-kubernetes</td>
    </tr>
    <tr>
      <th>pod:</th>
      <td>hello-kubernetes-7c8575c848-fvqm2</td>
    </tr>
    <tr>
      <th>node:</th>
      <td>allinone (Linux 5.14.21-150400.24.46-default)</td>
    </tr>
  </table>
</div>
<div id="footer">
  paulbouwer/hello-kubernetes:1.10 (linux/amd64)
</div>
    </div>
  </div>

</body>
</html>

Verifique que MetalLB funciona correctamente:

% arping hellok3s.${IP}.sslip.io

ARPING 192.168.64.210
60 bytes from 92:12:36:00:d3:58 (192.168.64.210): index=0 time=1.169 msec
60 bytes from 92:12:36:00:d3:58 (192.168.64.210): index=1 time=2.992 msec
60 bytes from 92:12:36:00:d3:58 (192.168.64.210): index=2 time=2.884 msec

En el ejemplo anterior, el flujo de tráfico es el siguiente:

  1. hellok3s.${IP}.sslip.io se resuelve en la IP real.

  2. Después, el tráfico se gestiona con el pod metallb-speaker.

  3. metallb-speaker redirige el tráfico al controlador traefik.

  4. Por último, Traefik reenvía la petición al servicio hello-kubernetes.

Documentation survey