Comment déployer une application simple sur Kubernetes

Pour moi, c’est ce blog même, qui tourne déjà sur Kubernetes sur mon Raspberry Pi au moment où j'écris ces lignes. J'ai essayé de le faire de la manière la plus professionnelle possible. Non seulement pour apprendre mais aussi pour partager avec vous ; laissez-moi vous expliquer comment j'ai déployé ce blog sur Kubernetes.
Comprendre tout le processus (de la création de l'image au DNS et au HTTPS) est un défi de taille. C'est pourquoi nous allons d'abord nous concentrer sur le déploiement d'un site web très simple sur votre réseau local.
Étape 1 : L'application la plus simple possible
Pour simplifier ce tutoriel, créons l'application la plus basique possible : un simple fichier HTML servi par Nginx et construit avec un Dockerfile.

Le Dockerfile ressemble à ceci :
FROM nginx:alpine
COPY app/ /usr/share/nginx/html/Et pour faire encore plus simple, le fichier HTML n'est même pas du "vrai" HTML, mais les navigateurs s'en fichent. Il contient juste ceci :
<h1>Hello from the simplest possible app!</h1>L'objectif ici est d'apprendre le déploiement, pas le développement. Vous pouvez construire et tester l'image localement avec les commandes suivantes :
docker build -t simple-app .Et pour tester localement :
docker run -p 80:80 simple-appUne fois lancé, vous pouvez accéder à votre site en visitant http://localhost dans votre navigateur.
Étape 2 : Comment envoyer votre image sur GitHub Container Registry
Super, nous avons une application : il est temps de la rendre accessible à notre cluster. Pour ce faire, Kubernetes doit pouvoir récupérer l'image depuis un registre. Il y a deux options : soit vous optez pour l'auto-hébergement complet (avec Docker Registry ou Zot), soit vous utilisez GitHub Container Registry (GHCR). Pour que ce tutoriel soit plus accessible à tous, utilisons GitHub, mais n'hésitez pas à essayer d'auto-héberger vos conteneurs également !
D'abord, vous aurez besoin d'un Personal Access Token (PAT). Vous pouvez en créer un sur GitHub en vous rendant dans Settings → Developer settings → Personal access tokens (Tokens classic).
Cochez ces permissions :
write:packagesread:packages
(Petit conseil : les tokens PAT ont une date d'expiration. S'il expire, Kubernetes finira par perdre la capacité de récupérer vos images. Je vous conseille vivement de vous mettre un rappel dans votre calendrier quelques jours avant l'échéance pour éviter de vous retrouver avec un déploiement bloqué).
Stockez-le dans une variable d'environnement :
export GHCR_TOKEN=ghp_xxxxxxxxxxxxxEnsuite, connectez-vous à GHCR :
echo $GHCR_TOKEN | docker login ghcr.io -u <VOTRE_NOM_UTILISATEUR_GITHUB> --password-stdinMaintenant, taguez et envoyez votre image (remplacez <VOTRE_NOM_UTILISATEUR_GITHUB> par votre pseudo).
Notez l'argument --platform linux/arm64 : le Raspberry Pi utilise une architecture ARM, alors que la plupart des PC et ordinateurs portables utilisent du x86. Ce sont des types de processeurs différents, et comme une image x86 ne fonctionnera pas sur un processeur ARM, cette option est indispensable pour construire vers le Pi.
docker build --platform linux/arm64 -t ghcr.io/<VOTRE_NOM_UTILISATEUR_GITHUB>/simple-app:latest .
docker push ghcr.io/<VOTRE_NOM_UTILISATEUR_GITHUB>/simple-app:latestVotre image devrait maintenant apparaître dans l'onglet Packages de votre profil GitHub.

Étape 3 : Comment configurer Kubernetes pour récupérer l'image
Maintenant que l'image est en ligne, Kubernetes doit pouvoir la récupérer pour la déployer. Pour cela, nous devons indiquer à K3s quelles informations d'identification utiliser pour ghcr.io. Sur votre nœud Pi, modifiez le fichier de configuration :
sudo nano /etc/rancher/k3s/registries.yamlAjoutez vos identifiants :
configs:
"ghcr.io":
auth:
username: "<VOTRE_NOM_UTILISATEUR_GITHUB>"
password: "<VOTRE_TOKEN_PAT_GITHUB>"Important : Pour que K3s prenne en compte ces changements, vous devrez redémarrer le service :
sudo systemctl restart k3sÉtape 4 : Comment rédiger une configuration YAML de base
Nous sommes maintenant prêts à déployer. Pour y parvenir, nous devrons écrire un peu de YAML pour définir l'état souhaité de notre application. Regardons d'un peu plus près les différents blocs nécessaires au déploiement d'un service ou d'un site web de base.
1. Le Namespace
C'est la "boîte" qui contient et organise vos applications. En définissant kind: Namespace avec le nom website-namespace, nous créons une frontière logique. Toutes les autres ressources de notre fichier utilisent ensuite metadata.namespace: website-namespace pour se rattacher à cette boîte.
Plus généralement, cette configuration, comme toutes les autres, partage quelques champs communs : apiVersion (la version de l'API), kind (ce que nous créons) et metadata (le nom de la ressource et son namespace).
Voici comment le namespace est défini :
apiVersion: v1
kind: Namespace
metadata:
name: website-namespacePour en savoir plus sur les namespaces, consultez la documentation officielle.
2. Le déploiement (Deployment)
Le déploiement définit l'état souhaité de votre application. Comme toute autre ressource, il possède un nom défini dans ses métadonnées. Pour des applications plus vastes, vous pourriez avoir plusieurs déploiements dans le même namespace, chacun gérant une partie différente de votre système.
Voici comment le déploiement est défini :
apiVersion: apps/v1
kind: Deployment
metadata:
name: website
namespace: website-namespace
spec:
replicas: 1
selector:
matchLabels:
app: website-pod
template:
metadata:
labels:
app: website-pod
spec:
containers:
- name: website-app
image: ghcr.io/<VOTRE_NOM_UTILISATEUR_GITHUB>/simple-app:latestConteneurs : Cette section définit le conteneur à exécuter dans le pod et l'image à récupérer sur GHCR. Il possède un nom car un pod peut techniquement exécuter plusieurs conteneurs "étroitement liés" (ex: votre application principale accompagnée d'un "sidecar" pour les logs ou les métriques).
Labels (Étiquettes) : Nous marquons nos pods avec spec.template.metadata.labels (ex : app : website-pod). Voyez cela comme des étiquettes pour identifier vos ressources.
Sélecteurs (Selectors) : Le déploiement utilise spec.selector pour savoir de quels pods il est responsable en cherchant ces étiquettes. Dans notre cas simple, le sélecteur et les étiquettes correspondent exactement, ce qui peut sembler redondant, mais cette séparation permet un ciblage très flexible dans des installations plus grandes.
Répliques (Replicas) : Enfin, nous précisons combien de pods exécuter (fixé à 1 ici).
Pour en savoir plus sur les déploiements, consultez la documentation officielle.
3. Le service (Service)
Comme les pods sont éphémères et que leurs IPs changent, nous avons besoin d'un point de contact stable pour atteindre notre application. Le service (Service) possède son propre nom unique et agit comme un routeur qui reçoit les requêtes de l'Ingress et les transmet au bon pod. Il fait également office de répartiteur de charge (Load Balancer) en distribuant le trafic vers n'importe quel pod correspondant à son sélecteur. Dans des configurations plus complexes, vous aurez souvent plusieurs services gérant différentes communications internes.
Voici comment le service est défini :
apiVersion: v1
kind: Service
metadata:
name: website-service
namespace: website-namespace
spec:
selector:
app: website-pod
ports:
- port: 80
targetPort: 80Mappage de ports : Nous faisons correspondre le port externe (80) au targetPort du conteneur (80).
Cela peut paraître étrange de mapper le port 80 vers 80, mais cette traduction est très utile dans certaines situations. Sous Linux, se lier à des ports inférieurs à 1024 (comme 80 ou 443) nécessite généralement des privilèges root. L'utilisation d'un Service nous permet d'accepter du trafic sur le port 80 et de le renvoyer vers un port sûr et non privilégié (comme 8080) à l'intérieur du conteneur si nécessaire. Pour en savoir plus sur les services, consultez la documentation officielle.
4. L'Ingress
L'Ingress est un routeur HTTP (un sous-module sous networking.k8s.io/v1) qui définit quels noms de domaine mènent à quels services. Comme les autres ressources, il possède un nom spécifique, et vous pouvez en définir plusieurs pour gérer différents domaines ou points d'entrée.
Voici comment l'ingress est défini :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: website-ingress
namespace: website-namespace
spec:
rules:
- host: simple-website.home.arpa
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: website-service
port:
number: 80Règles : Nous définissons que l'hôte simple-website.home.arpa mène à notre website-service.
Chemins (Paths) : Le chemin / avec pathType: Prefix signifie que tout ce qui se trouve sous ce domaine sera routé vers le même service. Cela inclut simple-website.home.arpa/about ou simple-website.home.arpa/blog/article-1.
Pour en savoir plus sur les ingresses, consultez la documentation officielle.
Voici un schéma montrant comment ces différents composants interagissent :

La configuration complète
En concaténant tous ces blocs à l'aide du séparateur ---, nous obtenons notre fichier de configuration final. Bien que vous puissiez les garder dans des fichiers séparés, les combiner dans un seul config.yaml est souvent plus simple pour les petits projets. À mesure que les choses se complexifient, vous finirez peut-être par vouloir les diviser à nouveau en fichiers individuels.
Enregistrez le contenu suivant dans un fichier nommé config.yaml (n'oubliez pas de mettre à jour le chemin de l'image avec votre nom d'utilisateur) :
apiVersion: v1
kind: Namespace
metadata:
name: website-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: website
namespace: website-namespace
spec:
replicas: 1
selector:
matchLabels:
app: website-pod
template:
metadata:
labels:
app: website-pod
spec:
containers:
- name: website-app
image: ghcr.io/<VOTRE_NOM_UTILISATEUR_GITHUB>/simple-app:latest
---
apiVersion: v1
kind: Service
metadata:
name: website-service
namespace: website-namespace
spec:
selector:
app: website-pod
ports:
- port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: website-ingress
namespace: website-namespace
spec:
rules:
- host: simple-website.home.arpa
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: website-service
port:
number: 80Étape 5 : Comment déployer votre application sur Kubernetes
Une fois votre image envoyée et K3s configuré, appliquez la configuration :
kubectl apply -f config.yamlVous pouvez ensuite vérifier que tout tourne correctement en listant les ressources de notre namespace. Puisque nous avons utilisé website-namespace, nous devons le préciser avec l'argument -n :
# Vérifier toutes les ressources (pods, services, déploiements)
kubectl get all -n website-namespace
# Vérifier spécifiquement l'ingress
kubectl get ingress -n website-namespaceAttendez quelques secondes que le pod passe en statut "Running". Si vous voyez une erreur ImagePullBackOff, vérifiez bien vos identifiants du registre à l'étape 3 !
Si vous avez suivi l'article précédent sur Pi-Hole et redirigé les domaines *.home.arpa vers votre Pi, votre site devrait être accessible à l'adresse simple-website.home.arpa.

Vous trouverez le code complet ici : github.com/Local-pie/simple-app
Dans le prochain article, nous verrons comment ouvrir votre cluster sur Internet en toute sécurité.

Judicael Poumay (Ph.D.)
Suivez-moi sur LinkedIn pour du contenu hebdomadaire Judicaël Poumay
En tant que chercheur/développeur IA indépendant spécialisé en Traitement du Langage Naturel (NLP), j'ai une expertise complète dans le développement et l'intégration de systèmes d'IA, ainsi que l'analyse de données.
Votre entreprise cherche à intégrer des solutions IA, analyser des données ou renforcer son développement back-end ? Contactez-moi !
Articles Similaires

GitHub Actions pour le CI/CD : Un Guide Complet
Un guide complet pour construire des pipelines d'intégration et de déploiement continus avec GitHub Actions. Apprenez à configurer des workflows, à créer des images de conteneurs et à automatiser en toute sécurité les déploiements sur Kubernetes.

Comment auto-héberger un registre de conteneurs sur K3s avec Zot
Apprenez à auto-héberger un registre de conteneurs privé et prêt pour la production sur K3s en utilisant Zot. Ce guide couvre l'installation avec Helm, l'intégration CI/CD, la signature d'images avec Cosign et l'analyse de vulnérabilités avec Trivy.

Comment installer Kubernetes K3s sur un Raspberry Pi 5
Apprenez à installer et configurer K3s, une distribution Kubernetes légère, sur un Raspberry Pi 5. Ce guide couvre l'activation des cgroups, la mise en place du cluster et la configuration de l'accès à distance avec kubectl.