Dans le dernier article de notre série sur Kubernetes, nous avons vu comment déployer un cluster Kubernetes en utilisant K3s
. Aujourd’hui, on va changer d’application à déployer, et on va installer un serveur Pi-hole sur notre cluster.
Bien entendu, notre Pi-hole sera distribué sur les différents nœuds de notre cluster, pour une haute disponibilité. On va donc parler de LoadBalancer, de Deployment… Bref, on va s’amuser !
Mais pourquoi faire ça ? Eh bien, l’idée, c’est d’avoir un Pi-hole qui ne tombe jamais en rade. Si un serveur lâche, Kubernetes prend le relais et Pi-hole continue de tourner. C’est ça, la magie du HA (Haute Disponibilité) ! Et puis, ça me permet de bidouiller Kubernetes, c’est toujours bon pour apprendre.
Pi-hole : Qu’est-ce que c’est ?
Avant de commencer quoi que ce soit, il faut comprendre ce qu’est Pi-hole. Pi-hole, c’est un serveur DNS, mais pas n’importe lequel. Il est hébergé sur votre réseau local, et il sert à bloquer les pubs et les trackers. En gros, il est là pour protéger votre vie privée et vous éviter les publicités intempestives. C’est un peu comme un AdBlocker, mais au niveau de tout votre réseau !
J’avais déjà un Pi-hole dans le passé, qui marchait très bien. Mais j’ai décidé de le déployer sur mon cluster Kubernetes, pour en apprendre un peu plus sur Kubernetes et pour voir tous les problèmes que je pourrais rencontrer. Et je peux vous dire, il y en a eu !
Architecture
Pour rappel, notre cluster Kubernetes est composé de 3 nœuds : un master et deux workers. Voilà à quoi ressemble notre architecture :
Une petite explication s’impose, je pense.
L’architecture est composée de différents éléments :
- Internet : Donne accès au monde extérieur
- PC Mohamad : Mon PC, c’est le pont (routeur) entre mon home lab et internet
- Switch : Permet de connecter les différents éléments de mon home lab
- Serveur Proxmox 1/2 : Serveurs physiques qui hébergent mes VMs
- Kubernetes Master Node : Le nœud master de mon cluster Kubernetes
- Kubernetes Worker 1/2 : Les nœuds workers de mon cluster Kubernetes
- Pi-Hole 1/2 : Les deux instances de Pi-hole que nous allons déployer sur notre cluster
- MetalLB Load Balancer : Le Load Balancer qui va distribuer le trafic vers nos Pi-hole.
Pour travailler dans le confort de mon PC, j’ai copié les fichiers de configuration de mon cluster Kubernetes sur mon PC. Cela me permet d’utiliser la commande kubectl
directement depuis mon PC, grâce à la commande :
scp [email protected]:/etc/rancher/k3s/k3s.yaml config
Avant de commencer
Avant de commencer, il y a une multitude de choses à faire. On sait que notre objectif est de déployer Pi-hole sur notre cluster Kubernetes. Mais avant de le faire, il faut comprendre que notre Pi-hole aura besoin de stockage persistant.
Pourquoi ? Parce que Pi-hole a besoin de stocker des données, comme les listes de blocage, la configuration DNS et les statistiques. Si jamais un Pod est détruit, les données seraient perdues. De plus, notre objectif est que Pi-hole soit sur plusieurs nœuds, donc il faut que les données soient partagées entre les différents nœuds.
Pour ça, on va utiliser Longhorn, un système de stockage persistant pour Kubernetes. Longhorn, en gros, ça prend le stockage des nœuds et ça le transforme en un stockage distribué et redondant. C’est parfait pour notre cas d’utilisation. Il va créer des volumes persistants (PersistentVolumes - PV) et nous, on va créer des requêtes de volume persistant (PersistentVolumeClaims - PVC) pour dire à Kubernetes qu’on a besoin d’un volume pour stocker nos données Pi-hole.
Pour l’installer, il existe différentes façons de procéder. La plus difficile est de le faire manuellement, donc écrire les différents fichiers de configuration, les appliquer, etc. La plus simple est d’utiliser Helm, un gestionnaire de packages pour Kubernetes. C’est un peu comme un apt pour Kubernetes. On va donc utiliser Helm pour installer Longhorn.
Installation de Longhorn
On va donc installer Longhorn en utilisant Helm. Pour ça, je vais utiliser un outil qui va me permettre d’organiser tous les différents helm charts qu’on va utiliser pour ce projet. Cet outil s’appelle Helmfile.
Pour installer cet outil, il faudra simplement télécharger le binaire, et le mettre dans votre PATH. Voici le GITHUB du projet.
Pour Linux, il suffira de déplacer le binaire dans le path
/usr/bin
ou/usr/local/bin
par exemple.
Pourquoi Helmfile ? Eh bien, Helmfile permet de gérer plusieurs charts Helm dans un seul fichier. Ça simplifie la gestion de tes applications Kubernetes, surtout quand on a plusieurs dépendances.
Il faut également installer le binaire de Helm. Pour ça, il suffit de télécharger le binaire depuis le site officiel, et de le déplacer dans votre PATH.
Maintenant, avant de faire quoi que ce soit, il faut connaître la dernière version de Longhorn. Pour ça, on va utiliser la commande suivante :
mohamad@debian:~$ helm search repo longhorn
NAME CHART VERSION APP VERSION DESCRIPTION
longhorn/longhorn 1.8.0 v1.8.0 Longhorn is a distributed block storage system ...
Donc la dernière version de Longhorn est la 1.8.0. On va donc créer un fichier helmfile.yaml
qui va contenir les différents helm charts qu’on va utiliser pour ce projet, en commençant bien entendu par Longhorn.
repositories:
- name: longhorn
url: https://charts.longhorn.io
---
releases:
- name: longhorn
namespace: longhorn-system
chart: longhorn/longhorn
version: 1.8.0
Rien de bien compliqué. On définit le repository de Longhorn, et on définit la release de Longhorn. Maintenant, pour que helmfile puisse marcher correctement, il faut lui faire installer certains plugins. Pour ça, on va lancer la commande suivante :
helmfile init
Il va ensuite vous demander si vous voulez installer tel ou tel plugin, il suffit de répondre y
à chaque fois, comme dans l’image ci-dessous :
Maintenant, lançons la commande suivante pour installer Longhorn :
helmfile apply
Et vous devriez voir quelque chose comme ça :
Et voilà, Longhorn est installé. Bon, pas tout à fait… Si on regarde l’état des pods, on verra que Longhorn n’est pas encore installé correctement et qu’il est même en train de crasher en boucle.
mohamad@debian:~$ kubectl get pods -n longhorn-system
NAME READY STATUS RESTARTS AGE
longhorn-driver-deployer-7fc7ffcb7f-qkgnz 0/1 Init:0/1 0 109s
longhorn-manager-89n8c 1/2 CrashLoopBackOff 3 (37s ago) 109s
longhorn-manager-bhsfq 1/2 CrashLoopBackOff 3 (48s ago) 109s
longhorn-manager-vjlgk 1/2 CrashLoopBackOff 4 (12s ago) 109s
longhorn-ui-5fcc4bcfc7-c49t4 1/1 Running 0 109s
longhorn-ui-5fcc4bcfc7-qbrwl 1/1 Running 0 109s
Pourquoi ? Regardons les logs du pod longhorn-manager-bhsfq
:
mohamad@debian:~$ kubectl logs longhorn-manager-bhsfq -n longhorn-system
Defaulted container "longhorn-manager" out of: longhorn-manager, pre-pull-share-manager-image
time="2025-03-01T13:43:57Z" level=fatal msg="Error starting manager: failed to check environment, please make sure you have iscsiadm/open-iscsi installed on the host: failed to execute: /usr/bin/nsenter [nsenter --mount=/host/proc/10422/ns/mnt --net=/host/proc/10422/ns/net iscsiadm --version], output , stderr nsenter: failed to execute iscsiadm: No such file or directory\n: exit status 127" func=main.main.DaemonCmd.func3 file="daemon.go:105"
Le message est clair, il manque le package iscsiadm
sur le host. Pour l’installer, il suffit de lancer la commande suivante sur chaque nœud de votre cluster :
sudo apt install open-iscsi
Mais bon, aller à la main sur chaque nœud nous enlève cet esprit “infrastructure as code”. Donc on va utiliser Ansible pour automatiser cette tâche. Pour ça, on va créer un playbook Ansible qui va installer le package open-iscsi
sur chaque nœud de notre cluster. Voici le playbook :
---
- hosts: all
become: true
tasks:
- name: Install open-iscsi
apt:
name: open-iscsi
state: present
Et une fois lancé, on aura ça comme résultat :
Ce n’est pas mieux comme ça ? Maintenant, juste au cas où, on va supprimer le namespace longhorn-system
et on va relancer l’installation de Longhorn :
kubectl delete namespace longhorn-system
helmfile apply
Puis, on va vérifier que tout est bien installé :
mohamad@debian:~$ kubectl get pods -n longhorn-system
NAME READY STATUS RESTARTS AGE
csi-attacher-79866cdcf8-66xd9 1/1 Running 0 13s
csi-attacher-79866cdcf8-hpd9g 1/1 Running 0 13s
csi-attacher-79866cdcf8-jbhhl 1/1 Running 0 13s
csi-provisioner-664cb5bdd5-hxlxr 1/1 Running 0 13s
csi-provisioner-664cb5bdd5-kt7hh 1/1 Running 0 13s
csi-provisioner-664cb5bdd5-sm4pl 1/1 Running 0 13s
csi-resizer-64f6fb4459-drptz 1/1 Running 0 13s
csi-resizer-64f6fb4459-jgwz8 1/1 Running 0 13s
...
...
longhorn-manager-nvl4n 2/2 Running 0 71s
longhorn-manager-v5c24 2/2 Running 1 (62s ago) 71s
longhorn-ui-5fcc4bcfc7-9wrth 1/1 Running 0 71s
longhorn-ui-5fcc4bcfc7-g6wds 1/1 Running 0 71s
...
...
Donc Longhorn est installé correctement. On peut maintenant passer à l’installation de Pi-hole.
Maintenant qu’on a notre stockage persistant, il faut qu’on puisse avoir une seule adresse IP pour accéder à notre Pi-hole. Pour ça, on va utiliser un LoadBalancer. On va utiliser MetalLB, un LoadBalancer pour Kubernetes. En gros, MetalLB va prendre une IP de notre réseau et la donner au service Pi-hole. Comme ça, on aura toujours la même IP pour accéder à Pi-hole, même si les pods changent de nœud. Pour l’installer, on ne va pas changer la méthode qui gagne, on va utiliser Helm. Voici le contenu de notre fichier helmfile.yaml
:
repositories:
- name: longhorn
url: https://charts.longhorn.io
- name: metallb
url: https://metallb.github.io/metallb
---
releases:
- name: longhorn
namespace: longhorn-system
chart: longhorn/longhorn
version: 1.8.0
- name: metallb
chart: metallb/metallb
version: 0.14.9
namespace: metallb-system
Maintenant, on va lancer la commande suivante pour installer MetalLB :
helmfile apply
Et voilà, MetalLB est installé. Pour vérifier que tout est bien installé, on va lancer la commande suivante :
mohamad@debian:~$ kubectl get pods -n metallb-system
NAME READY STATUS RESTARTS AGE
metallb-controller-dc88974b6-k7wc8 1/1 Running 0 2m55s
metallb-speaker-5jllp 4/4 Running 0 2m55s
metallb-speaker-cx2j6 4/4 Running 0 2m55s
metallb-speaker-zt86w 4/4 Running 0 2m55s
Il faudra attendre quelques minutes pour que MetalLB soit installé correctement, et passe son étape d’init.
Maintenant, on doit configurer MetalLB pour qu’il puisse fonctionner correctement, comme par exemple lui donner le range d’adresses IP qu’il pourra utiliser. Pour faire ça, il existe différentes façons. Pour configurer correctement MetalLB, je vais utiliser Kustomize.
Kustomize va nous permettre de créer un fichier de configuration pour MetalLB et de le déployer sur notre cluster Kubernetes. On peux considérer Kustomize comme un gestionnaire de configuration pour Kubernetes. C’est super pratique pour éviter de répéter des configs et pour gérer facilement les différences entre les environnements.
Tout d’abord, il faudra créer un nouveau fichier, le mien je l’ai nommé kustomization.yaml
et il contient le contenu suivant :
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app.kubernetes.io/managed-by: Kustomize
resources:
- ./metallb/pool.yaml
Ensuite, on va créer la config en soi, donc créer un dossier metallb
et dedans créer un fichier pool.yaml
qui contient le contenu suivant :
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pool
namespace: metallb-system
spec:
addresses:
- 192.168.100.32/27
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: pool
namespace: metallb-system
spec:
ipAddressPools:
- pool
Le range dans addresses
est le range d’adresses IP que MetalLB pourra utiliser. Dans mon cas, je suis dans un réseau 192.168.100.0/24 et j’ai choisi le range 192.168.100.32/27, pour que je puisse avoir les adresses utilisables :
- 192.168.100.33 à 192.168.100.62
Également, j’ai fait une deuxième ressource de type L2Advertisement
qui permet à MetalLB de faire de la pub pour les adresses IP qu’il a à disposition. Donc couche 2 qui est la couche mac. MetalLB utilise le protocole ARP (Address Resolution Protocol) pour annoncer ces adresses au réseau local. C’est comme ça que notre PC sait que l’IP 192.168.100.250 est accessible via MetalLB.
Une fois qu’on a fait, rien de plus compliqué que de lancer la commande suivante :
kubectl apply -k .
Il faut être dans le même dossier que le fichier
kustomization.yaml
:)
Et normalement, vous verrez quelque chose comme ça :
Et voilà, MetalLB est configuré correctement. On peut maintenant passer à l’installation de Pi-hole.
Installation de Pi-hole
Pour l’installation de notre Pi-hole, on va continuer sur notre lancée, et on va utiliser Helm. Pour ça, on va modifier notre fichier helmfile.yaml
pour ajouter la release de Pi-hole :
Voilà à quoi va ressembler notre fichier helmfile.yaml
:
repositories:
- name: longhorn
url: https://charts.longhorn.io
- name: metallb
url: https://metallb.github.io/metallb
- name: mojo2600
url: https://mojo2600.github.io/pihole-kubernetes/
---
releases:
- name: longhorn
namespace: longhorn-system
chart: longhorn/longhorn
version: 1.8.0
- name: metallb
chart: metallb/metallb
version: 0.14.9
namespace: metallb-system
- name: pihole
namespace: pihole-system
chart: mojo2600/pihole
version: 2.27.0
values:
- ./values/pihole.values.yaml
Également, j’ai dû ajouter un fichier values.yaml
qui contient certains paramètres qui seront utilisés pour lancer Pi-hole. Voici le contenu de ce fichier :
---
persistenVolumeClaim:
enabled: true
serviceWeb:
loadBalancerIP: 192.168.100.250
annotations:
metallb.universe.tf/allow-shared-ip: pihole-svc
type: LoadBalancer
serviceDns:
loadBalancerIP: 192.168.100.250
annotations:
metallb.universe.tf/allow-shared-ip: pihole-svc
type: LoadBalancer
replicaCount: 1
Donc rien de très compliqué, on dit simplement qu’on veut qu’il y ait du persistent storage. On définit l’adresse IP qu’on veut utiliser pour le LoadBalancer. L’annotation metallb.universe.tf/allow-shared-ip: pihole-svc
est importante. Elle permet à MetalLB de partager la même IP entre plusieurs services. Ici, on l’utilise pour partager l’IP entre le service web (pour l’interface d’administration) et le service DNS.
Maintenant, on va lancer la commande suivante pour installer Pi-hole :
helmfile apply
Et voilà, Pi-hole est installé. Pour vérifier que tout est bien installé, on va lancer la commande suivante :
mohamad@debian:~$ kubectl get svc -n pihole-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
pihole-dhcp NodePort 10.43.227.177 <none> 67:30868/UDP 7m59s
pihole-dns-tcp LoadBalancer 10.43.57.10 192.168.100.250 53:30898/TCP 7m59s
pihole-dns-udp LoadBalancer 10.43.92.96 192.168.100.250 53:31650/UDP 7m59s
pihole-web LoadBalancer 10.43.110.72 192.168.100.250 80:31936/TCP,443:32195/TCP 7m59s
Si tu vois une adresse IP dans la colonne EXTERNAL-IP
, c’est que MetalLB a bien fait son boulot !
Conclusion
Voilà, Pi-hole est maintenant déployé sur notre cluster Kubernetes ! C’est pas de la magie, mais presque. On a utilisé Longhorn pour le stockage persistant, MetalLB pour l’équilibrage de charge et Helm pour simplifier l’installation. Maintenant, on peux profiter d’un Pi-hole haute disponibilité et d’une vie privée un peu mieux protégée.
Si jamais vous voulez un autre post ou j’explique comment configurer Pi-hole, n’hésitez pas à me le dire par message Linkedin !