0% ont trouvé ce document utile (0 vote)
12 vues17 pages

Rapport Technique Exhaustif Du Projet Devops: Sommaire

Transféré par

abouzawbaahack
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
12 vues17 pages

Rapport Technique Exhaustif Du Projet Devops: Sommaire

Transféré par

abouzawbaahack
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Rapport Technique Exhaustif

du Projet DevOps

Sommaire Tout Plier/Déplier

Objectifs Généraux Schéma du Pipeline Étape 1: Préparation

Étape 2: Dockerisation Étape 3: Déploiement Kubernetes Étape 4: Pipeline CI/CD

Conclusion

🎯 Objectifs Généraux du Projet


Construire une application web simple et la **conteneuriser** avec un Dockerfile
pour la rendre portable et isolée.
Utiliser Docker Compose pour orchestrer un service multi-conteneurs en local (3
répliques + Nginx), simulant une architecture de production.
Déployer l'application sur un cluster Kubernetes (via Minikube) pour gérer la
mise à l'échelle et la résilience.
**Automatiser** l'intégralité du processus (tests, build, publication d'image) via
une pipeline **CI/CD** (Intégration et Déploiement Continus) avec GitHub
Actions.

Schéma Complet de l'Architecture et du Pipeline


CI/CD
GitHub Actions Docker Hub
git push Trigger 1. Test (pytest) (Registry)
GitHub
Développeur 2. Build Image
(Code Source)
3. Push Image
4. Deploy (kubectl)

Image Pull
kubectl apply

Cluster Kubernetes (Minikube)

Service Nginx
(NodePort: 30080)

Pod Nginx

Requête HTTP
Service Flask
Nginx Local Proxy Pass vers localhost:30080 (ClusterIP)
Client / Utilisateur
(Serveur Hôte)

Pod Flask 1 Pod Flask 2 Pod Flask 3

Connexion DB
Service MySQL
(ClusterIP)

Pod MySQL Persistent Volume / PVC

Étape 1: Initialisation et Préparation de



l'Environnement

🎯 Objectif de l'étape :

Mettre en place un environnement de développement fonctionnel sur une machine


Ubuntu vierge en installant tous les outils nécessaires : Git pour le versioning, Docker
pour la conteneurisation, et Minikube/Kubectl pour l'orchestration.

💻 Commandes d'installation :

Commande Explication détaillée

Mise à jour du système : Cette commande en deux parties


est cruciale. apt update rafraîchit la liste des paquets
yassine@DEVSECOPS:~$ sudo apt
disponibles. apt upgrade -y met à niveau tous les paquets
update && sudo apt upgrade -y
installés vers leurs dernières versions, le -y répondant
automatiquement "oui" à toutes les confirmations.
Installation des outils : Installe les logiciels essentiels en une
yassine@DEVSECOPS:~$ sudo apt seule ligne : git (gestion de code source), docker.io (moteur
install git docker.io curl -y de conteneurisation Docker), et curl (outil pour transférer des
données, utilisé ici pour télécharger Minikube/Kubectl).

Gestion des permissions Docker : Par défaut, Docker ne peut


être exécuté qu'en tant que root (avec sudo ). Cette
yassine@DEVSECOPS:~$ sudo
commande ajoute l'utilisateur courant ( $USER ) au groupe
usermod -aG docker $USER &&
docker , lui donnant le droit d'exécuter des commandes
newgrp docker
Docker sans sudo . newgrp docker applique ce
changement à la session de terminal actuelle.

Installation de Kubectl & Minikube : Ces commandes


yassine@DEVSECOPS:~$ curl -LO téléchargent les binaires de kubectl (l'outil en ligne de
"https://dl.k8s.io/..." # commande pour interagir avec un cluster Kubernetes) et de
etc. Minikube (un outil qui crée un cluster Kubernetes à nœud
unique sur une machine locale).

❌ Premier Obstacle : Démarrage de Minikube

yassine@DEVSECOPS:~$ minikube start --driver=docker


❌ Exiting due to DRV_AS_ROOT: The "docker" driver should not be used with roo
...
permission denied while trying to connect to the Docker daemon socket...

💡 Diagnostic : L'erreur de permission indique que, même si la commande `usermod` a


été exécutée, la session de terminal actuelle n'a pas encore pris en compte les nouveaux
droits d'appartenance au groupe `docker`.

✅ Solution : La solution la plus simple et la plus fiable est de **fermer le terminal et


d'en ouvrir un nouveau**. Cela force le système à recharger les appartenances aux
groupes de l'utilisateur.

yassine@DEVSECOPS:~$ minikube start


🏄 Done! kubectl is now configured to use "minikube" cluster...
− Étape 2: Dockerisation et Orchestration Locale

🎯 Objectif de l'étape :

Développer le code source de l'application, la rendre portable via un Dockerfile, et


simuler un déploiement multi-instances avec répartition de charge grâce à Docker
Compose.

Contenu des fichiers du projet

📁 app/app.py

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def home():
return jsonify(message="Hello World!")

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

📁 app/requirements.txt

flask
pytest
requests

📁 app/tests/test_app.py
import pytest
from app import app

def test_home(client):
response = client.get('/')
assert response.status_code == 200
json_data = response.get_json()
assert json_data['message'] == "Hello World!"

📁 Dockerfile

# Utiliser une image Python officielle comme image de base


FROM python:3.11-slim

# Définir le répertoire de travail dans le conteneur


WORKDIR /app

# Copier le fichier des dépendances


COPY requirements.txt .

# Installer les dépendances


RUN pip install --no-cache-dir -r requirements.txt

# Copier le reste du code de l'application


COPY . .

# Exposer le port sur lequel l'application s'exécute


EXPOSE 5000

# Commande pour lancer l'application


CMD ["python", "app.py"]

📁 docker-compose.yml

version: "3.8"
services:
app1:
build: ./app
ports:
- "5001:5000"
app2:
build: ./app
ports:
- "5002:5000"
app3:
build: ./app
ports:
- "5003:5000"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app1
- app2
- app3

📁 nginx/nginx.conf

events {}

http {
upstream backend {
server app1:5000;
server app2:5000;
server app3:5000;
}

server {
listen 80;

location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}

❌ Second Obstacle : Docker Compose manquant

yassine@DEVSECOPS:~/Hightech$ docker-compose up --build -d


Command 'docker-compose' not found...

💡 Diagnostic : La commande docker-compose n'est plus incluse par défaut avec Docker.
C'est un plugin ou un paquet séparé à installer.
✅ Solution : Installer le paquet manquant.

yassine@DEVSECOPS:~/Hightech$ sudo apt install docker-compose -y

Test de validation local

Après installation, le lancement a fonctionné et le test de l'application en local, via le


répartiteur de charge Nginx, a été un succès :

yassine@DEVSECOPS:~/Hightech$ curl http://localhost:8080


{"message":"Hello World!"}

Étape 3: Déploiement Kubernetes et ses



multiples échecs

🎯 Objectif de l'étape :

Prendre notre application conteneurisée et la déployer sur un véritable orchestrateur


(Minikube). Cette étape est cruciale et révèle souvent des problèmes de configuration,
d'image, de code et de réseau qui n'apparaissent pas en local.
Manifestes Kubernetes (fichiers YAML)

📁 k8s/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
name: flask-app-namespace

📁 k8s/mysql-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: flask-app-namespace
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi

📁 k8s/mysql-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: flask-app-namespace
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpassword"
- name: MYSQL_DATABASE
value: "flaskdb"
- name: MYSQL_USER
value: "flaskuser"
- name: MYSQL_PASSWORD
value: "flaskpassword"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc

📁 k8s/mysql-service.yaml

apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: flask-app-namespace
spec:
selector:
app: mysql
ports:
- port: 3306

📁 k8s/flask-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-app
namespace: flask-app-namespace
spec:
replicas: 3
selector:
matchLabels:
app: flask-app
template:
metadata:
labels:
app: flask-app
spec:
containers:
- name: flask-app
image: your_dockerhub_user/flask-app:latest
ports:
- containerPort: 5000
env:
- name: MYSQL_HOST
value: mysql
- name: MYSQL_USER
value: flaskuser
- name: MYSQL_PASSWORD
value: flaskpassword
- name: MYSQL_DB
value: flaskdb

📁 k8s/flask-service.yaml

apiVersion: v1
kind: Service
metadata:
name: flask-service
namespace: flask-app-namespace
spec:
selector:
app: flask-app
ports:
- protocol: TCP
port: 5000
targetPort: 5000

📁 k8s/nginx-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: flask-app-namespace
data:
nginx.conf: |
events {}
http {
upstream flask_backend {
server flask-service:5000;
}
server {
listen 80;
location / {
proxy_pass http://flask_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}

📁 k8s/nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: flask-app-namespace
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: nginx-conf
configMap:
name: nginx-config

📁 k8s/nginx-service.yaml

apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: flask-app-namespace
spec:
selector:
app: nginx
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080

❌ Obstacle 1 : `ImagePullBackOff`
yassine@DEVSECOPS:~/Hightech$ kubectl apply -f k8s/
yassine@DEVSECOPS:~/Hightech$ kubectl get pods
NAME READY STATUS RESTARTS AGE
flask-app-deployment-xxxx 0/1 ImagePullBackOff 0 2m

💡 Diagnostic : C'est une erreur classique. Minikube exécute son propre environnement
Docker, isolé de celui de la machine hôte. Le cluster Kubernetes ne peut donc pas trouver
l'image hightech_app1 que nous avons construite localement.

✅ Solution : Stratégie "Build and Load" : La méthode la plus robuste est de


construire l'image sur la machine hôte puis de la charger manuellement dans
l'environnement Docker interne de Minikube.

Commande Explication détaillée

Chargement de l'image : C'est la commande clé. Elle prend une


minikube image load image Docker de l'hôte et la copie directement dans le registre
hightech_app1:latest d'images interne du cluster Minikube, la rendant accessible aux
pods.

❌ Obstacle 2 : `CrashLoopBackOff`

yassine@DEVSECOPS:~/Hightech$ kubectl get pods


NAME READY STATUS RESTARTS AGE
flask-app-deployment-xxxx 0/1 CrashLoopBackOff 5 5m

💡 Diagnostic : L'image est maintenant bien disponible, mais le conteneur démarre,


plante immédiatement, et Kubernetes essaie de le redémarrer en boucle. C'est presque
toujours un **bug dans le code de l'application elle-même**. Une inspection du fichier
app/app.py (via kubectl logs <pod-name>) a révélé une faute de frappe fatale : if
_name__ == "__main__": au lieu de if __name__ == "__main__":.

❌ Obstacle 3 : Réseau et Permissions (`Connection refused`)


Même avec les pods enfin au statut `Running` après correction du code et
reconstruction/chargement de l'image, l'accès externe via `minikube tunnel` échouait. Cela
indiquait un problème plus profond avec l'état du cluster, probablement corrompu après de
multiples essais et erreurs.

✅ Solution : La Réinitialisation Nucléaire


Quand un cluster de développement devient instable, la solution la plus rapide et la
plus sûre est de repartir sur des bases saines.

Commande Explication détaillée

minikube Destruction du cluster : Supprime complètement la machine virtuelle et toutes


delete les configurations associées à Minikube.

minikube
Nouveau départ : Crée un cluster Kubernetes propre et fonctionnel.
start

− Étape 4: La longue route de la pipeline CI/CD

🎯 Objectif de l'étape :

Créer une automatisation complète où chaque git push sur la branche principale de
GitHub déclenche automatiquement : 1. Les tests unitaires. 2. La construction d'une
nouvelle image Docker. 3. La publication de cette image sur Docker Hub.

📁 .github/workflows/ci-cd.yml

name: CI/CD Pipeline

on:
push:
branches:
- main
jobs:
build-test-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python


uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install dependencies


run: |
pip install -r app/requirements.txt

- name: Run tests


run: |
pytest app/tests

- name: Log in to Docker Hub


uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build Docker image


run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/flask-app:latest

- name: Push Docker image


run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/flask-app:latest

- name: Setup kubectl


uses: azure/setup-kubectl@v3
with:
version: 'latest'

- name: Deploy to Kubernetes


env:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
run: |
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml

- name: Reload Nginx


run: sudo systemctl reload nginx

Chronologie du Débogage du Workflow GitHub Actions :

❌ Échec n°1 : Le workflow ne se lance pas.


Cause : Le fichier ci-cd.yml était créé en local mais n'avait pas été ajouté, commité,
puis poussé sur GitHub.
Solution : Un simple git add/commit/push du fichier de workflow.

❌ Échecs n°2, 3, 4 : Échec des tests avec ModuleNotFoundError.


Cause : Dans l'environnement d'exécution de GitHub Actions, Python ne savait pas où
trouver les modules de l'application pour les tests.
Solution : Forcer l'ajout du répertoire racine au chemin de recherche de Python avec la
variable d'environnement PYTHONPATH. La commande devient : run: PYTHONPATH=.
pytest.

❌ Échecs n°5, 6, 7 : Échec de connexion à Docker Hub (Username and password


required).
Cause : Plusieurs erreurs humaines successives : une faute de frappe dans le nom du
secret dans le fichier YAML (DOCKERHUB_TOKEN vs DOCKER_TOKEN), puis un copier-coller
invalide du token.
Solution : Correction minutieuse du nom du secret dans le fichier ci-cd.yml et
régénération/réenregistrement des secrets dans les paramètres du repository GitHub.

❌ Échec n°8 (Le Bug Final et le plus instructif) : Après la première réussite de la
pipeline, les pods Kubernetes retournent en CrashLoopBackOff !
Cause : C'est un piège classique du DevOps. La correction du bug dans app/app.py (le
__name__) avait été faite **en local uniquement** et n'avait jamais été envoyée sur
GitHub. La pipeline, enfin fonctionnelle, a donc pris le code buggé depuis GitHub, a
construit une image défectueuse, et l'a publiée. Kubernetes, en tirant la nouvelle image
"latest", a récupéré la version buggée.
✅ Solution Ultime et Définitive :

Synchroniser l'état du code local avec le dépôt distant. Ce push final, contenant le code
corrigé, a déclenché la pipeline une dernière fois, qui a construit et publié l'image 100%
fonctionnelle.

Commande Explication détaillée

git commit -m "FIX:


Commit de la correction : Enregistre la version corrigée du
Final correction for
fichier app.py dans l'historique Git local.
app.py"

Déclenchement du pipeline : Envoie le commit sur GitHub,


git push ce qui déclenche le workflow GitHub Actions pour
reconstruire et republier l'image correcte.

🎉 SUCCÈS COMPLET ! 🎉
Après la réussite de la dernière pipeline, une simple mise à jour du déploiement
Kubernetes a permis de tirer l'image correcte depuis Docker Hub. Les pods sont
passés au statut `Running` de manière stable. Le test final, prouvant le
fonctionnement de l'application à l'intérieur du cluster via son IP interne, a été un
succès.

# Test final réussi depuis la VM Minikube


yassine@DEVSECOPS:~$ minikube ssh -- curl http://$(kubectl get serv
{"message":"Hello World!"}

Vous aimerez peut-être aussi