Introduction au MLOps
Le déploiement de modèles de machine learning en production présente des défis uniques : scalabilité, reproductibilité, monitoring et maintenance. Docker et Kubernetes forment une combinaison puissante pour résoudre ces problématiques en offrant containerisation et orchestration.
Prérequis
- Connaissances de base en Python et ML
- Docker installé sur votre machine
- Kubectl et accès à un cluster Kubernetes
- Un modèle ML entraîné (nous utiliserons scikit-learn)
Étape 1 : Créer l'Application ML
Commençons par créer une API simple avec Flask pour servir notre modèle :
Structure du Projet
ml-app/
├── app.py
├── model.py
├── requirements.txt
├── Dockerfile
├── model.pkl
└── k8s/
├── deployment.yaml
├── service.yaml
└── ingress.yaml
Code de l'Application (app.py)
from flask import Flask, request, jsonify
import joblib
import numpy as np
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
# Charger le modèle au démarrage
model = joblib.load('model.pkl')
@app.route('/health', methods=['GET'])
def health_check():
return jsonify({'status': 'healthy'}), 200
@app.route('/predict', methods=['POST'])
def predict():
try:
data = request.get_json()
features = np.array(data['features']).reshape(1, -1)
prediction = model.predict(features)[0]
probability = model.predict_proba(features)[0].max()
return jsonify({
'prediction': int(prediction),
'confidence': float(probability),
'status': 'success'
})
except Exception as e:
app.logger.error(f"Erreur de prédiction: {str(e)}")
return jsonify({'error': str(e)}), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Requirements (requirements.txt)
Flask==2.3.3
scikit-learn==1.3.0
joblib==1.3.2
numpy==1.24.3
gunicorn==21.2.0
Étape 2 : Containerisation avec Docker
Dockerfile Optimisé
# Utiliser une image Python légère
FROM python:3.9-slim
# Définir le répertoire de travail
WORKDIR /app
# Copier les requirements et installer les dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copier le code de l'application
COPY . .
# Créer un utilisateur non-root pour la sécurité
RUN useradd -m -u 1000 mluser && chown -R mluser:mluser /app
USER mluser
# Exposer le port
EXPOSE 5000
# Utiliser Gunicorn pour la production
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
Construction de l'Image
# Construire l'image
docker build -t ml-app:v1.0 .
# Tester localement
docker run -p 5000:5000 ml-app:v1.0
# Tester l'API
curl -X POST http://localhost:5000/predict \
-H "Content-Type: application/json" \
-d '{"features": [5.1, 3.5, 1.4, 0.2]}'
Étape 3 : Optimisation Docker
Multi-stage Build
# Build stage
FROM python:3.9-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Production stage
FROM python:3.9-slim
WORKDIR /app
# Copier les packages installés
COPY --from=builder /root/.local /root/.local
COPY . .
# Mettre à jour PATH
ENV PATH=/root/.local/bin:$PATH
RUN useradd -m -u 1000 mluser && chown -R mluser:mluser /app
USER mluser
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
Étape 4 : Déploiement Kubernetes
Deployment (k8s/deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: ml-app-deployment
labels:
app: ml-app
spec:
replicas: 3
selector:
matchLabels:
app: ml-app
template:
metadata:
labels:
app: ml-app
spec:
containers:
- name: ml-app
image: ml-app:v1.0
ports:
- containerPort: 5000
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: FLASK_ENV
value: "production"
Service (k8s/service.yaml)
apiVersion: v1
kind: Service
metadata:
name: ml-app-service
spec:
selector:
app: ml-app
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: ClusterIP
Ingress (k8s/ingress.yaml)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ml-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
rules:
- host: ml-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ml-app-service
port:
number: 80
Étape 5 : Déploiement et Gestion
Commandes de Déploiement
# Appliquer les configurations
kubectl apply -f k8s/
# Vérifier le déploiement
kubectl get deployments
kubectl get pods
kubectl get services
# Voir les logs
kubectl logs -f deployment/ml-app-deployment
# Mise à jour rolling
kubectl set image deployment/ml-app-deployment ml-app=ml-app:v1.1
Étape 6 : Monitoring et Observabilité
ConfigMap pour la Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: ml-app-config
data:
MODEL_VERSION: "v1.0"
LOG_LEVEL: "INFO"
MAX_REQUESTS_PER_MINUTE: "1000"
Métriques Prometheus
# Ajouter à app.py
from prometheus_client import Counter, Histogram, generate_latest
REQUEST_COUNT = Counter('ml_requests_total', 'Total ML requests')
REQUEST_LATENCY = Histogram('ml_request_duration_seconds', 'ML request latency')
@app.route('/metrics')
def metrics():
return generate_latest()
@REQUEST_LATENCY.time()
@app.route('/predict', methods=['POST'])
def predict():
REQUEST_COUNT.inc()
# ... reste du code
Étape 7 : Sécurité et Bonnes Pratiques
NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ml-app-netpol
spec:
podSelector:
matchLabels:
app: ml-app
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 5000
Secrets pour les Données Sensibles
# Créer un secret
kubectl create secret generic ml-app-secrets \
--from-literal=api-key=your-api-key \
--from-literal=db-password=your-password
# Utiliser dans le deployment
env:
- name: API_KEY
valueFrom:
secretKeyRef:
name: ml-app-secrets
key: api-key
Étape 8 : CI/CD Pipeline
GitHub Actions Workflow
name: ML App CI/CD
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Docker image
run: |
docker build -t ${{ secrets.REGISTRY }}/ml-app:${{ github.sha }} .
- name: Push to registry
run: |
echo ${{ secrets.REGISTRY_PASSWORD }} | docker login -u ${{ secrets.REGISTRY_USER }} --password-stdin
docker push ${{ secrets.REGISTRY }}/ml-app:${{ github.sha }}
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/ml-app-deployment ml-app=${{ secrets.REGISTRY }}/ml-app:${{ github.sha }}
Étape 9 : Scaling et Performance
Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ml-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ml-app-deployment
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Tests et Validation
Test de Charge
# Utiliser Apache Bench
ab -n 1000 -c 10 -p data.json -T application/json http://ml-app.example.com/predict
# Ou avec Python
import requests
import concurrent.futures
import time
def test_prediction():
response = requests.post('http://ml-app.example.com/predict',
json={'features': [5.1, 3.5, 1.4, 0.2]})
return response.status_code
# Test concurrent
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
futures = [executor.submit(test_prediction) for _ in range(1000)]
results = [future.result() for future in futures]
Troubleshooting
Commandes de Debug
# Vérifier les pods
kubectl describe pod
# Accéder à un pod
kubectl exec -it -- /bin/bash
# Voir les événements
kubectl get events --sort-by=.metadata.creationTimestamp
# Vérifier les ressources
kubectl top pods
kubectl top nodes
Conclusion
Ce tutoriel vous a guidé à travers le processus complet de déploiement d'une application ML avec Docker et Kubernetes. Cette approche offre :
- Scalabilité automatique selon la demande
- Haute disponibilité avec la réplication
- Déploiements sans interruption avec rolling updates
- Monitoring intégré pour la production
- Sécurité renforcée avec les bonnes pratiques K8s
Cette architecture MLOps robuste vous permettra de déployer et maintenir vos modèles ML en production avec confiance.
Besoin d'aide pour implémenter votre pipeline MLOps ? Contactez-moi pour un accompagnement personnalisé.