Article 4 : Déploiement Kubernetes avec GitOps

Déploiement de mon infrastructure Kubernetes versionnée avec GitOps, avec application automatisée via une pipeline dédiée et exposition publique de mon portfolio.
Après avoir mis en place le build et le push de mon image Docker, il est temps d’aller plus loin : versionner et automatiser le déploiement de mon infrastructure Kubernetes.
Séparation des responsabilités : code vs infra
Pour garder une architecture propre et maintenir une logique GitOps, j’ai séparé l’infrastructure du code applicatif dans deux dépôts Git différents :
Wooulf/forkfolio
: contient le code source de mon site (Next.js), leDockerfile
et une pipeline CI pour builder + déployer l’image Docker.Wooulf/infra-k8s-terraform
: contient tous les fichiers de configuration Kubernetes (deployment.yaml
,service.yaml
, etc.), et une autre pipeline CI/CD dédiée à leur application sur le cluster.
🎯 Ce découpage permet de découpler le code applicatif de l'infrastructure, ce qui facilite la maintenance, les revues de code ciblées, et l’évolution des deux parties de manière indépendante.
Déploiement de l'application sur MicroK8s
Pour que mon application tourne sur le cluster et soit exposée proprement au public, j’ai mis en place les éléments suivants :
- Un Deployment : qui gère le cycle de vie du pod (mises à jour, résilience…)
- Un Service ClusterIP : pour stabiliser la communication interne vers le pod
- Un Ingress : pour router les requêtes HTTP externes vers la bonne application
- Un Service NodePort : pour exposer l’Ingress Controller au monde extérieur
Une requête, un chemin
L'ensemble de ces composants permet de faire transiter une requête HTTP entrante à travers le cluster, comme ceci :
🌐
Client
→VPS
(port 80) →NodePort
→Ingress
→ClusterIP
→Pod
Voici un schéma clair et visuel du fonctionnement :
Fichiers de définition Kubernetes
Voici les fichiers versionnés dans le repo d’infra :
🧱 Deployment
Gère le déploiement du conteneur, les mises à jour, la redondance.
apiVersion: apps/v1
kind: Deployment
metadata:
name: portfolio
spec:
replicas: 1
selector:
matchLabels:
app: portfolio
template:
metadata:
labels:
app: portfolio
spec:
containers:
- name: portfolio
image: woulf/portfolio:latest
ports:
- containerPort: 3000
🌐 Service ClusterIP
Expose le pod à l’intérieur du cluster via une IP stable.
apiVersion: v1
kind: Service
metadata:
name: portfolio
spec:
selector:
app: portfolio
ports:
- port: 80
targetPort: 3000
🌍 Ingress
Fait le lien entre un nom de domaine (woulf.fr
) et le bon service interne.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: woulf-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: woulf.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: portfolio
port:
number: 3000
ingressClassName: nginx
🛣️ Service NodePort pour exposer l’Ingress Controller
Ce Service expose le pod NGINX de l’Ingress Controller vers l’extérieur, via un port ouvert sur le VPS. Cela permet aux requêtes HTTP/HTTPS d’atteindre le cluster depuis l’extérieur.
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress-microk8s-controller
namespace: ingress
spec:
type: NodePort
externalIPs:
- 185.216.27.229
selector:
name: nginx-ingress-microk8s
ports:
- port: 80
targetPort: 80
nodePort: 32180
💡 externalIPs permet d’exposer manuellement un service sur l’IP publique d’un VPS. C’est une approche fonctionnelle en environnement auto-hébergé, mais dans un contexte cloud, on privilégiera les Service de type LoadBalancer, qui s’intègrent directement avec l’infrastructure réseau du fournisseur. Pour les clusters sans cloud provider, une solution comme MetalLB permet de simuler ce comportement.
Pipeline GitHub Actions dans le repo infra
Une fois les fichiers versionnés, je les applique automatiquement sur mon cluster grâce à une deuxième pipeline CI/CD dans le dépôt infra-k8s-terraform
.
Elle s’exécute dès qu’un fichier est modifié dans le dossier k8s/
:
on:
push:
paths:
- 'k8s/**'
jobs:
apply_k8s_configs:
steps:
- uses: actions/checkout@v4
- uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
- run: kubectl apply -f k8s/
- run: kubectl get all
✅ Résultat : à chaque commit de config, le cluster se synchronise automatiquement.
Et la suite ?
Je pourrais aller encore plus loin avec un outil GitOps complet comme ArgoCD ou Flux. Ces outils se chargeraient de surveiller le dépôt Git en continu et de mettre à jour le cluster sans passer par une pipeline manuelle.
⚠️ Ce setup reste minimaliste : il n'inclut pas de haute disponibilité ni de gestion dynamique du trafic. Il est cependant suffisant pour un portfolio auto-hébergé, dans une logique de MVP.
🔄 Ce setup me permet d’avoir une boucle de feedback rapide entre mes commits et le résultat en production, tout en gardant un code d’infra proprement versionné. Ce n’est pas encore du GitOps “as a Service”, mais on s’en approche.
💡 Dans le prochain article, je parlerai de la gestion des secrets, du futur passage en HTTPS, des idées de monitoring, et des prochaines évolutions possibles de mon infrastructure.