Kubernetes LoadBalancer et Octavia
Lorsque vous exposez une application Kubernetes sur Internet, vous utilisez généralement un Service de type: LoadBalancer. Sur notre plateforme, cela crée automatiquement un véritable Octavia Load Balancer dans votre projet OpenStack. Comprendre cette intégration est essentiel pour diagnostiquer les problèmes réseau et gérer le quota.
Fonctionnement
Lorsque vous créez un Service avec type: LoadBalancer dans votre shoot cluster, le cloud-controller-manager, qui s'exécute dans le control plane du shoot sur le Seed, provisionne un load balancer Octavia dans OpenStack et le relie à vos worker nodes.
%%{init: {'themeVariables': {'textColor': '#333333', 'edgeLabelBackground': '#ffffff', 'clusterBkg': 'transparent', 'clusterBorder': '#999999'}}}%%
flowchart LR
Internet["Internet"]
FIP["Floating IP<br>(public network)"]
subgraph OS ["OpenStack"]
LB["Octavia Load Balancer<br>Listener · Pool · Health Monitor"]
end
subgraph K8s ["Kubernetes Shoot Cluster"]
W1["Worker Node 1"]
W2["Worker Node 2"]
Pod1["Pod: nginx"]
Pod2["Pod: nginx"]
end
Internet --> FIP
FIP --> LB
LB --> W1
LB --> W2
W1 --> Pod1
W2 --> Pod2
classDef ext fill:#fce4ec,stroke:#c62828,color:#b71c1c;
classDef fip fill:#e3f2fd,stroke:#1565c0,color:#0d47a1;
classDef lb fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20;
classDef worker fill:#fff3e0,stroke:#e65100,color:#bf360c;
classDef pod fill:#f3e5f5,stroke:#6a1b9a,color:#4a148c;
class Internet ext;
class FIP fip;
class LB lb;
class W1,W2 worker;
class Pod1,Pod2 pod;
linkStyle 0,1 stroke:#c62828,stroke-width:2px;
linkStyle 2,3 stroke:#2e7d32,stroke-width:2px;
linkStyle 4,5 stroke:#e65100,stroke-width:2px;
%%{init: {'themeVariables': {'textColor': '#e0e0e0', 'edgeLabelBackground': '#2a2a2a', 'clusterBkg': 'transparent', 'clusterBorder': '#666666'}}}%%
flowchart LR
Internet["Internet"]
FIP["Floating IP<br>(public network)"]
subgraph OS ["OpenStack"]
LB["Octavia Load Balancer<br>Listener · Pool · Health Monitor"]
end
subgraph K8s ["Kubernetes Shoot Cluster"]
W1["Worker Node 1"]
W2["Worker Node 2"]
Pod1["Pod: nginx"]
Pod2["Pod: nginx"]
end
Internet --> FIP
FIP --> LB
LB --> W1
LB --> W2
W1 --> Pod1
W2 --> Pod2
classDef ext fill:#3e1e1e,stroke:#ef5350,color:#ffcdd2;
classDef fip fill:#1a3a5c,stroke:#64b5f6,color:#e3f2fd;
classDef lb fill:#1b3a2a,stroke:#66bb6a,color:#c8e6c9;
classDef worker fill:#3e2723,stroke:#ff9800,color:#ffe0b2;
classDef pod fill:#2a1a3a,stroke:#ab47bc,color:#e1bee7;
class Internet ext;
class FIP fip;
class LB lb;
class W1,W2 worker;
class Pod1,Pod2 pod;
linkStyle 0,1 stroke:#ef5350,stroke-width:2px;
linkStyle 2,3 stroke:#66bb6a,stroke-width:2px;
linkStyle 4,5 stroke:#ff9800,stroke-width:2px;
Le flux de provisionnement
- Vous créez un
Serviceavectype: LoadBalancerdans votre shoot cluster. - Le cloud-controller-manager (CCM) détecte le nouveau service.
- Le CCM appelle l'API OpenStack Octavia pour provisionner :
- Un load balancer avec une VIP sur le réseau des worker nodes.
- Un listener pour le port spécifié dans le Service K8s.
- Un pool avec les worker nodes comme membres.
- Un health monitor qui vérifie chaque worker node.
- Une floating IP est créée sur le réseau public et associée à la VIP du load balancer.
- Le CCM réécrit la floating IP dans le statut du Service en tant que
EXTERNAL-IP. kubectl get svcaffiche désormais l'IP externe : votre application est accessible depuis Internet.
Ce qui apparaît dans OpenStack
Le load balancer Octavia est une véritable ressource OpenStack dans votre projet. Vous pouvez le voir dans :
- OpenStack Horizon → Network → Load Balancers
- OpenStack CLI :
openstack loadbalancer list
Chaque service K8s LoadBalancer crée :
| Ressource Octavia | Rôle |
|---|---|
| Load Balancer | L'objet LB de niveau supérieur avec une VIP sur le réseau des workers |
| Listener | Écoute sur le port spécifié dans le Service K8s |
| Pool | Contient les worker nodes comme membres |
| Members | Un par worker node, redirige vers le NodePort |
| Health Monitor | Vérifie l'état de santé des worker nodes via le NodePort |
Exemple pratique
Déployez nginx avec un service LoadBalancer :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: nginx
Appliquez et vérifiez le service :
kubectl apply -f nginx.yaml
kubectl get svc nginx
Pendant le provisionnement par Octavia, l'IP externe s'affiche comme <pending> :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.97.246.12 <pending> 80:31234/TCP 5s
Une fois Octavia prêt (généralement 30 à 60 secondes) :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.97.246.12 192.0.2.50 80:31234/TCP 45s
Le EXTERNAL-IP est la floating IP attribuée par Octavia. Votre application est désormais accessible à l'adresse http://192.0.2.50.
Vérifier dans OpenStack
Le load balancer et la floating IP sont visibles dans OpenStack :
openstack loadbalancer list
openstack floating ip list
Cycle de vie des floating IP
La floating IP appartient toujours au service LoadBalancer, et non à l'objet Ingress. Il en résulte le comportement suivant :
| Action | Ce qui arrive à la floating IP |
|---|---|
Supprimer le service LoadBalancer |
La floating IP est supprimée et quitte votre projet |
Supprimer uniquement l'objet Ingress |
La floating IP est conservée : elle appartient au service du contrôleur Ingress |
Désinstaller le contrôleur Ingress (par ex. helm uninstall) |
La floating IP est supprimée, car le service du contrôleur l'est aussi |
Supprimer puis recréer un service LoadBalancer |
Une nouvelle floating IP avec une adresse différente : les adresses IP ne sont pas persistantes |
Conserver une floating IP de façon permanente
Avec l'annotation loadbalancer.openstack.org/keep-floatingip: "true", la floating IP reste dans votre projet lorsque le service est supprimé. Elle est alors dissociée (openstack floating ip list l'affiche sans port) et peut être réutilisée plus tard :
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
loadbalancer.openstack.org/keep-floatingip: "true"
spec:
type: LoadBalancer
loadBalancerIP: 192.0.2.50 # free floating IP from your project
selector:
app: nginx
ports:
- port: 80
targetPort: 80
La réutilisation passe par spec.loadBalancerIP : le nouveau service adopte l'adresse existante dès qu'elle est libre (dissociée).
Définir l'annotation sur chaque service
L'annotation s'applique par service et n'est pas liée à l'adresse IP elle-même. Un nouveau service qui adopte une floating IP existante via spec.loadBalancerIP doit aussi définir l'annotation, sinon l'IP est supprimée en même temps que ce service.
Les adresses réservées ne peuvent pas être récupérées
Si vous devez conserver une adresse IP précise (par ex. pour des enregistrements DNS ou des listes d'autorisation de pare-feu sur des systèmes distants), la réserver avec l'annotation est le seul moyen fiable. Une floating IP supprimée ne peut pas être réobtenue sous la même adresse.
NodePort vs LoadBalancer vs Ingress
| NodePort | LoadBalancer | Ingress | |
|---|---|---|---|
| Expose | Un seul service sur un port élevé (30000 à 32767) | Un seul service sur un port standard | Plusieurs services via routage HTTP(S) |
| IP externe | IP du worker node + port du node | Floating IP dédiée | Dépend du type de service du contrôleur Ingress |
| LB Octavia | Non | Oui, un par service | Uniquement si le contrôleur Ingress utilise LoadBalancer |
| Terminaison TLS | Non | Possible avec des annotations | Oui (le contrôleur Ingress gère le TLS) |
| Quota | Aucun | 1 LB Octavia + 1 floating IP par service | 1 LB Octavia (pour le contrôleur Ingress) |
| Idéal pour | Accès interne / débogage | Un seul service exposé sur Internet | Plusieurs services HTTP(S) sur une seule IP |
Recommandation
Pour plusieurs services HTTP(S), utilisez un contrôleur Ingress (par ex. nginx-ingress) avec un seul service LoadBalancer. Cela ne consomme qu'un seul LB Octavia et vous offre la terminaison TLS et le routage basé sur le chemin.
Considérations sur le quota
Chaque Service de type LoadBalancer crée un load balancer Octavia dans votre projet OpenStack. Cela consomme :
- Load balancers Octavia : vérifiez le quota de votre projet.
- Floating IP : une par service LoadBalancer.
- Ports : sur le réseau des worker nodes.
Si vous atteignez la limite de quota, les nouveaux services LoadBalancer restent en état <pending>. Vérifiez votre quota dans le Cloud Services Portal ou avec :
openstack quota show
Nettoyage
Lorsque vous supprimez un Service Kubernetes de type LoadBalancer, le cloud-controller-manager supprime automatiquement le load balancer Octavia correspondant et libère la floating IP :
Si vous souhaitez conserver la floating IP (par ex. pour un futur service ou des enregistrements DNS stables), définissez l'annotation loadbalancer.openstack.org/keep-floatingip: "true" avant la suppression. Voir Cycle de vie des floating IP.
kubectl delete svc nginx
Warning
Supprimez toujours d'abord le Service K8s. Si vous supprimez manuellement le load balancer Octavia dans OpenStack, le CCM risque de ne pas pouvoir nettoyer correctement, laissant des ressources orphelines.
Load balancers Octavia manuels
Vous pouvez également créer des load balancers Octavia manuellement pour des charges de travail non Kubernetes (par ex. des VM OpenStack). Consultez le guide des load balancers OpenStack pour des exemples CLI, y compris HTTP, la terminaison HTTPS et les health monitors.
Étape suivante
Apprenez à créer des comptes de service persistants ou revenez à la gestion des clusters.