Docker correctly configured

This commit is contained in:
Grégoire Mureau 2025-07-07 15:13:01 +02:00
parent eebe89587b
commit 31c2a49304
10 changed files with 118 additions and 31 deletions

32
.github/workflows/workflow.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Build and Deploy API
on:
push:
branches: [main]
jobs:
build-and-run:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: ./deployment/api
push: false
tags: sentiment_api:latest
- name: Run docker-compose
run: docker-compose up -d

View file

@ -0,0 +1,14 @@
# Ignorer les caches Python
__pycache__/
*.pyc
# Ignorer les artefacts temporaires
downloaded_artifacts/
mlruns/
logs/
# Ignorer les tests unitaires
tests/
# Ignorer les variables d'environnement. Elles seront plutôt chargées dans la commande docker run `docker run --env-file ./deployment/api/.env -p 8000:8000 sentiment-api` en développement local, et via des variables définies dans le yaml
*.env

21
deployment/api/Dockerfile Normal file
View file

@ -0,0 +1,21 @@
# Utiliser une image légère Python 3.12
FROM python:3.12-slim
# Définir le dossier de travail
WORKDIR /app
# Copier uniquement requirements.txt d'abord (pour optimiser le cache Docker)
COPY requirements.txt .
# Installer les dépendances
RUN pip install --no-cache-dir -r requirements.txt
# Copier le reste du code source
COPY . .
# Exposer les ports 8000 (fastAPI) et 8501 (streamlit)
EXPOSE 8000 8501
RUN chmod +x /app/start.sh
CMD ["/app/start.sh"]

View file

@ -0,0 +1,31 @@
version: "3.9"
services:
api:
build:
context: ./deployment/api
dockerfile: Dockerfile
container_name: sentiment_api
ports:
- "8000:8000" # API FastAPI
- "8501:8501" # Streamlit
environment:
MLFLOW_TRACKING_URI: "${MLFLOW_TRACKING_URI}" # ex: http://mlflow:5000
MLFLOW_S3_ENDPOINT_URL: "${MLFLOW_S3_ENDPOINT_URL}" # ex: url MinIO ou S3
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}" # clé ID S3
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}" # clé secrète S3
SMTP_SERVER: "${SMTP_SERVER}" # serveur SMTP
SMTP_PORT: "${SMTP_PORT}" # port SMTP (ex: 587)
SMTP_EMAIL: "${SMTP_EMAIL}" # email expéditeur
SMTP_PASSWORD: "${SMTP_PASSWORD}" # mot de passe SMTP
SMTP_FROM_ALIAS: "${SMTP_FROM_ALIAS}" # alias (optionnel)
ADMIN_EMAIL: "${ADMIN_EMAIL}" # email admin destinataire
# only if you have a reverse proxy server
# networks:
# - swag
restart: always
# only if you have a reverse proxy server
# networks:
# swag:
# external: true

View file

@ -11,10 +11,10 @@ def send_error_report_email(error_reports):
"""
smtp_server = os.getenv("SMTP_SERVER")
smtp_port = int(os.getenv("SMTP_PORT", 587))
smtp_email = os.getenv("SMTP_EMAIL")
smtp_email = os.getenv("SMTP_EMAIL") # main email address. If smt_from_alias is not, it will also be the mail sender
smtp_password = os.getenv("SMTP_PASSWORD")
smtp_from_alias = os.getenv("SMTP_FROM_ALIAS", smtp_email)
admin_email = os.getenv("ADMIN_EMAIL")
smtp_from_alias = os.getenv("SMTP_FROM_ALIAS", smtp_email) # mail sender if not none (alias)
admin_email = os.getenv("ADMIN_EMAIL") # this one will receive the mail
if not all([smtp_server, smtp_email, smtp_password, admin_email]):
print("❌ Configuration email manquante dans .env")
@ -23,7 +23,11 @@ def send_error_report_email(error_reports):
try:
# Créer le message
msg = MIMEMultipart()
msg['From'] = f"Air Paradis Monitor <{smtp_from_alias}>"
if smtp_from_alias is not None:
smtp_from = smtp_from_alias
else:
smtp_from = smtp_email
msg['From'] = f"Air Paradis Monitor <{smtp_from}>"
msg['To'] = admin_email
msg['Subject'] = f"🚨 Rapport d'erreurs - Prédictions sentiments ({datetime.now().strftime('%d/%m/%Y %H:%M')})"
@ -86,7 +90,7 @@ Un nouveau rapport d'erreurs a été généré pour l'API de prédiction de sent
---
Cordialement,
🤖 API Air Paradis - Système de monitoring automatique
📧 Envoyé depuis : """ + smtp_from_alias
📧 Envoyé depuis : """ + smtp_from
# Ajouter le corps du message
msg.attach(MIMEText(body, 'plain', 'utf-8'))
@ -97,7 +101,7 @@ Cordialement,
server.login(smtp_email, smtp_password)
server.send_message(msg)
print(f"✅ Email de rapport envoyé avec succès depuis {smtp_from_alias}")
print(f"✅ Email de rapport envoyé avec succès depuis {smtp_from}")
print(f"📊 JSON intégré dans le corps du message")
return True

View file

@ -14,7 +14,7 @@ from tensorflow.keras.preprocessing.sequence import pad_sequences
PARENT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
sys.path.append(PARENT_DIR)
# 🔐 Charger les variables d'environnement (.env à deux niveaux au-dessus)
load_dotenv(dotenv_path=os.path.join(PARENT_DIR, ".env"))
load_dotenv()
# 🔁 Import de la fonction de prétraitement (LOCAL)
@ -69,6 +69,7 @@ def load_model_and_artifacts(model_name=MODEL_NAME, stage=STAGE):
# Déterminer les chemins des artefacts
local_dir = "./downloaded_artifacts"
os.makedirs(local_dir, exist_ok=True)
print("Contenu de downloaded_artifacts :", os.listdir(local_dir))
base_path = "local_artifacts"
tokenizer_file = "tokenizer.pickle"

View file

@ -9,6 +9,7 @@ requests==2.32.4
# === Machine Learning ===
tensorflow==2.19.0
mlflow==3.1.1
boto3==1.39.2
numpy==2.1.3
pandas==2.3.0
# === Traitement de texte ===

7
deployment/api/start.sh Normal file
View file

@ -0,0 +1,7 @@
#!/bin/bash
# Lancer Streamlit en arrière-plan
streamlit run app.py --server.port 8501 --server.address 0.0.0.0 &
# Lancer Uvicorn en premier plan (Docker "suit" ce processus)
uvicorn main:app --host 0.0.0.0 --port 8000

View file

@ -6,30 +6,6 @@ Created on Sat Jul 5 15:03:24 2025
@author: madfuckinman
"""
# # test_alias_email.py
# import os
# from dotenv import load_dotenv
# from email_service import send_error_report_email
# load_dotenv()
# print("🔧 Configuration Email:")
# print(f"Serveur SMTP: {os.getenv('SMTP_SERVER')}")
# print(f"Authentification: {os.getenv('SMTP_EMAIL')}")
# print(f"Expéditeur (alias): {os.getenv('SMTP_FROM_ALIAS')}")
# print(f"Destinataire: {os.getenv('ADMIN_EMAIL')}")
# # Test
# test_reports = {
# "Service client décevant": "positif",
# "J'adore cette compagnie": "negatif",
# "Vol annulé sans prévenir": "positif"
# }
# print("\n📧 Test d'envoi depuis l'alias...")
# result = send_error_report_email(test_reports)
# print(f"Résultat : {'✅ Succès' if result else '❌ Échec'}")
import os
import sys
from dotenv import load_dotenv