Date: 8 avril 2023 - Révision du 2025.12.05
Kubernetes ne m’offre pas de service de balance de charge (load balancer externe au réseau K8s) pour plusieurs applications qui partagent le même point d’entrée (une seule adresse IP/même port).
La raison étant que ces services sont habituellement intégrés aux systèmes des fournisseurs Infonuagique: Google Cloud, Azure, AWS, etc.
En labo, nous avons utilisé le tunnel de minikube ou bien celui de Docker Desktop/K8s comme solution externe.
Pour le déploiement natif d’un amas K8S, il faut se rabattre sur des solutions tierces.
Par exemple: metallb, traefik proxy, etc.
# Sur le cluster K8S suivant:
kubectl get nodes
# Résultat:
# NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP
# k8s01 Ready control-plane 155d v1.25.3 192.168.2.51 <none>
# k8s02 Ready worker 155d v1.25.3 192.168.2.52 <none>
# k8s03 Ready worker 155d v1.25.3 192.168.2.53 <none>
# k8s04 Ready worker 155d v1.25.3 192.168.2.54 <none>
# k8s05 Ready worker 155d v1.25.3 192.168.2.55 <none>
# ---------------------------------------------------------
# Installer metallb (LoadBalancer externe)
# NOTE: Ajuster le numéro de version. Référence: https://metallb.io/installation/
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.9/config/manifests/metallb-native.yaml
# Résultat:
# namespace/metallb-system created
# customresourcedefinition.apiextensions.k8s.io/addresspools.metallb.io created
# customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
# customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created
# customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created
# customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created
# customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
# customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
# serviceaccount/controller created
# serviceaccount/speaker created
# role.rbac.authorization.k8s.io/controller created
# role.rbac.authorization.k8s.io/pod-lister created
# clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
# clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
# rolebinding.rbac.authorization.k8s.io/controller created
# rolebinding.rbac.authorization.k8s.io/pod-lister created
# clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
# clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
# secret/webhook-server-cert created
# service/webhook-service created
# deployment.apps/controller created
# daemonset.apps/speaker created
# validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created
# ---------------------------------------------------------
# Afficher les ressources de l'espace de nom du LB
kubectl get all -n metallb-system -o wide
# Résultat:
# NAME READY STATUS RESTARTS AGE IP NODE
# pod/controller-844979dcdc-64nms 1/1 Running 0 5m46s 10.244.77.5 k8s04
# pod/speaker-7r8bw 1/1 Running 0 5m46s 192.168.2.55 k8s05
# pod/speaker-8n5x5 1/1 Running 0 5m46s 192.168.2.52 k8s02
# pod/speaker-md92s 1/1 Running 0 5m45s 192.168.2.53 k8s03
# pod/speaker-rb4wh 1/1 Running 0 5m45s 192.168.2.54 k8s04
# pod/speaker-sth9t 1/1 Running 0 5m46s 192.168.2.51 k8s01
#
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
# service/webhook-service ClusterIP 10.103.93.242 <none> 443/TCP 6m10s component=controller
#
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE CONTAINERS IMAGES SELECTOR
# daemonset.apps/speaker 5 5 5 5 5 kubernetes.io/os=linux 6m10s speaker quay.io/metallb/speaker:v0.13.9 app=metallb,component=speaker
#
# NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
# deployment.apps/controller 1/1 1 1 6m10s controller quay.io/metallb/controller:v0.13.9 app=metallb,component=controller
#
# NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
# replicaset.apps/controller-844979dcdc 1 1 1 5m46s controller quay.io/metallb/controller:v0.13.9 app=metallb,component=controller,pod-template-hash=844979dcdc
nano metallb-config.yaml
NOTE: Pour la plage d’adresses, il faut utiliser les IP disponibles dans le laboratoire.
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 192.168.2.60-192.168.2.69
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
kubectl apply -f Metallb-config.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.0/deploy/static/provider/cloud/deploy.yaml
# ---------------------------------------------------------
# Résultat:
# namespace/ingress-nginx created
# serviceaccount/ingress-nginx created
# serviceaccount/ingress-nginx-admission created
# role.rbac.authorization.k8s.io/ingress-nginx created
# role.rbac.authorization.k8s.io/ingress-nginx-admission created
# clusterrole.rbac.authorization.k8s.io/ingress-nginx created
# clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
# rolebinding.rbac.authorization.k8s.io/ingress-nginx created
# rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
# clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
# clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
# configmap/ingress-nginx-controller created
# service/ingress-nginx-controller created
# service/ingress-nginx-controller-admission created
# deployment.apps/ingress-nginx-controller created
# job.batch/ingress-nginx-admission-create created
# job.batch/ingress-nginx-admission-patch created
# ingressclass.networking.k8s.io/nginx created
# validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
# ---------------------------------------------------------
# Résultat:
kubectl get all -n ingress-nginx
# NAME READY STATUS RESTARTS AGE
# pod/ingress-nginx-admission-create-57dl4 0/1 Completed 0 6m38s
# pod/ingress-nginx-admission-patch-phpss 0/1 Completed 0 6m38s
# pod/ingress-nginx-controller-7d9674b7cf-2dbrg 1/1 Running 0 6m38s
#
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# service/ingress-nginx-controller LoadBalancer 10.109.153.164 192.168.2.61 80:32524/TCP,443:31844/TCP 6m39s
# service/ingress-nginx-controller-admission ClusterIP 10.99.155.93 <none> 443/TCP 6m39s
#
# NAME READY UP-TO-DATE AVAILABLE AGE
# deployment.apps/ingress-nginx-controller 1/1 1 1 6m39s
#
# NAME DESIRED CURRENT READY AGE
# replicaset.apps/ingress-nginx-controller-7d9674b7cf 1 1 1 6m38s
#
# NAME COMPLETIONS DURATION AGE
# job.batch/ingress-nginx-admission-create 1/1 5s 6m38s
# job.batch/ingress-nginx-admission-patch 1/1 5s 6m38s
# ---------------------------------------------------------
# Résultat:
kubectl get all -n kube-system
# NAME READY STATUS RESTARTS AGE
# pod/kube-proxy-486bn 1/1 Running 2 (26h ago) 156d
# pod/kube-proxy-49sgw 1/1 Running 2 (26h ago) 156d
# pod/kube-proxy-rvs7t 1/1 Running 2 (26h ago) 156d
# pod/kube-proxy-x6c6n 1/1 Running 2 (26h ago) 156d
#
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 156d
#
# NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
# daemonset.apps/kube-proxy 4 4 4 4 4 kubernetes.io/os=linux 156d
Il y a maintenant un service de type LoadBalancer qui roule et qui a obtenu une adresse IP (192.168.2.61) de MetalLB:
service/ingress-nginx-controller LoadBalancer 10.98.108.0 192.168.2.61 80:30517/TCP,443:31700/TCP
Il est maintenant possible d’avoir accès au réseau k8s via cette adresse IP:

Note: Nous obtenons une erreur 404 car il n’y a pas encore de routes de définies. Par contre, cette réponse nous indique qu’il y a un service HTTP à l’entrée.
# Modifié pour la demo ingress
apiVersion: apps/v1
kind: Deployment
metadata:
name: superminou
labels:
app: un-superminou
spec:
replicas: 5
selector:
matchLabels:
mon-app: un-superminou
template:
metadata:
labels:
mon-app: un-superminou
spec:
containers:
- name: nginx
image: alainboudreault/superminou:latest
imagePullPolicy: Always
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: superminou-service
spec:
selector:
mon-app: un-superminou
# type: par defaut = ClusterIP, ce qui est requis pour le service ingress
ports:
- protocol: TCP
port: 80
targetPort: 80
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
mon-app: un-superpitou
name: super-pitou
spec:
replicas: 3
selector:
matchLabels:
mon-app: un-superpitou
template:
metadata:
labels:
mon-app: un-superpitou
spec:
volumes:
- name: webdata
emptyDir: {}
initContainers:
- name: web-content
image: busybox
volumeMounts:
- name: webdata
mountPath: "/webdata"
command: ["/bin/sh", "-c", 'echo "<h1>Je suis un super <font color=blue>PITOU</font></h1><hr/><h2>Servi par: <?php echo gethostname(); ?></h2>" > /webdata/index.php']
containers:
- image: php:8.0.3-apache-buster
name: php-apache
volumeMounts:
- name: webdata
mountPath: "/var/www/html"
---
apiVersion: v1
kind: Service
metadata:
name: superpitou-service
spec:
selector:
mon-app: un-superpitou
# type: par defaut = ClusterIP, ce qui est requis pour le service ingress
ports:
- protocol: TCP
port: 80
targetPort: 80
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-demo01
spec:
ingressClassName: nginx
rules:
- host: superminou.demo
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superminou-service
port:
number: 80
- host: superpitou.demo
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: superpitou-service
port:
number: 80
kubectl apply -f ingress-demo.yml
# -----------------------------------------------------------
kubectl get ingress
# NAME CLASS HOSTS ADDRESS PORTS AGE
# ingress-demo01 nginx superminou.demo,superpitou.demo 192.168.2.61 80 106m
# -----------------------------------------------------------
kubectl describe ingress ingress-demo01
# Résultat:
# Name: ingress-demo01
# Labels: <none>
# Namespace: default
# Address: 192.168.2.61
# Ingress Class: nginx
# Default backend: <default>
# Rules:
# Host Path Backends
# ---- ---- --------
# superminou.demo
# / superminou-service:80 (10.244.235.154:80,10.244.235.157:80,10.244.236.150:80 + 2 more...)
# superpitou.demo
# / superpitou-service:80 (10.244.235.149:80,10.244.236.152:80,10.244.77.42:80)
# Annotations: <none>
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Normal Sync 13m (x7 over 107m) nginx-ingress-controller Scheduled for sync
192.168.2.61 superminou.demo superpitou.demo


Document rédigé par Alain Boudreault © 2021-2026
Version 2025.12.03.1
Site par ve2cuy