388 lines
13 KiB
Markdown
388 lines
13 KiB
Markdown
|
|
# AUDIT COMPLET: veza-backend-api Broken Pipe & Fichier .env Manquant dans Incus
|
||
|
|
|
||
|
|
**Date**: 2025-01-27
|
||
|
|
**Problème**: veza-backend-api broken pipe. can't find .env file inside the dedicated incus container for the incus deploy
|
||
|
|
**Priorité**: P0 - CRITIQUE (bloque le déploiement)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. RÉSUMÉ EXÉCUTIF
|
||
|
|
|
||
|
|
### 1.1 Problème Principal
|
||
|
|
Le service `veza-backend-api` déployé dans un conteneur Incus rencontre deux problèmes critiques:
|
||
|
|
1. **Broken Pipe Error**: Erreurs de "broken pipe" lors de l'écriture des logs
|
||
|
|
2. **Fichier .env Manquant**: L'application ne trouve pas le fichier `.env` dans le conteneur
|
||
|
|
|
||
|
|
### 1.2 Impact
|
||
|
|
- ❌ Le service ne démarre pas correctement
|
||
|
|
- ❌ Les logs ne sont pas capturés par systemd/journald
|
||
|
|
- ❌ L'application ne peut pas charger les variables d'environnement
|
||
|
|
- ❌ Le déploiement Incus échoue silencieusement
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. ANALYSE DÉTAILLÉE
|
||
|
|
|
||
|
|
### 2.1 Architecture Actuelle du Déploiement
|
||
|
|
|
||
|
|
#### 2.1.1 Fichiers de Configuration
|
||
|
|
```
|
||
|
|
config/incus/env/backend-api.env # Source (sur l'hôte)
|
||
|
|
↓ (copié via incus file push)
|
||
|
|
/etc/veza/backend-api.env # Destination (dans le conteneur)
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2.1.2 Service Systemd
|
||
|
|
```ini
|
||
|
|
[Service]
|
||
|
|
WorkingDirectory=/opt/veza/backend-api
|
||
|
|
ExecStart=/opt/veza/backend-api/start-backend-api.sh
|
||
|
|
EnvironmentFile=/etc/veza/backend-api.env
|
||
|
|
StandardOutput=journal
|
||
|
|
StandardError=journal
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2.1.3 Script Wrapper
|
||
|
|
```bash
|
||
|
|
#!/bin/bash
|
||
|
|
# start-backend-api.sh
|
||
|
|
source /etc/veza/backend-api.env
|
||
|
|
exec /usr/local/bin/veza-backend-api
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2.1.4 Application Go
|
||
|
|
```go
|
||
|
|
// main.go
|
||
|
|
_ = godotenv.Load() // Cherche .env dans le working directory
|
||
|
|
|
||
|
|
// config.NewConfig()
|
||
|
|
LoadEnvFiles(env) // Cherche .env.{env} et .env dans le working directory
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.2 Problèmes Identifiés
|
||
|
|
|
||
|
|
#### 2.2.1 Problème #1: Incohérence des Chemins de Fichiers .env
|
||
|
|
|
||
|
|
**Symptôme**: L'application Go cherche un fichier `.env` dans `/opt/veza/backend-api/` mais le fichier est à `/etc/veza/backend-api.env`
|
||
|
|
|
||
|
|
**Cause Racine**:
|
||
|
|
- Le script de déploiement copie le fichier à `/etc/veza/backend-api.env`
|
||
|
|
- Systemd charge les variables via `EnvironmentFile=/etc/veza/backend-api.env`
|
||
|
|
- Le wrapper script charge les variables via `source /etc/veza/backend-api.env`
|
||
|
|
- **MAIS** l'application Go fait `godotenv.Load()` qui cherche `.env` dans le working directory (`/opt/veza/backend-api/`)
|
||
|
|
- L'application Go fait aussi `LoadEnvFiles(env)` qui cherche `.env.production` et `.env` dans le working directory
|
||
|
|
|
||
|
|
**Impact**:
|
||
|
|
- Si les variables sont déjà chargées par systemd/wrapper, `godotenv.Load()` échoue silencieusement (pas d'erreur si fichier absent)
|
||
|
|
- Si les variables ne sont PAS chargées par systemd/wrapper, l'application ne trouve pas le fichier `.env` et échoue
|
||
|
|
|
||
|
|
**Preuve**:
|
||
|
|
```go
|
||
|
|
// veza-backend-api/cmd/api/main.go:50
|
||
|
|
_ = godotenv.Load() // Cherche .env dans le répertoire courant
|
||
|
|
|
||
|
|
// veza-backend-api/internal/config/env_loader.go:24
|
||
|
|
_ = godotenv.Load() // Cherche .env dans le répertoire courant
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2.2.2 Problème #2: Broken Pipe Error
|
||
|
|
|
||
|
|
**Symptôme**: Erreurs "broken pipe" lors de l'écriture des logs
|
||
|
|
|
||
|
|
**Cause Racine**:
|
||
|
|
- Systemd/journald peut fermer le pipe stdout/stderr si le service démarre trop vite
|
||
|
|
- Le wrapper script peut écrire sur stdout/stderr avant que systemd ne soit prêt
|
||
|
|
- L'application Go peut essayer d'écrire sur stdout/stderr après que journald ait fermé le pipe
|
||
|
|
|
||
|
|
**Impact**:
|
||
|
|
- Les logs ne sont pas capturés correctement
|
||
|
|
- L'application peut crasher si les erreurs de broken pipe ne sont pas gérées
|
||
|
|
|
||
|
|
**Preuve**:
|
||
|
|
- Le code Go a déjà des mécanismes pour gérer les broken pipe (dans `logging/logger.go`)
|
||
|
|
- Mais le wrapper script et les premières lignes de `main.go` peuvent écrire sur stdout/stderr avant que le logger ne soit initialisé
|
||
|
|
|
||
|
|
#### 2.2.3 Problème #3: Vérification Insuffisante du Fichier .env
|
||
|
|
|
||
|
|
**Symptôme**: Le script de déploiement vérifie que le fichier existe mais ne vérifie pas qu'il est accessible par l'application
|
||
|
|
|
||
|
|
**Cause Racine**:
|
||
|
|
- Le script `deploy-service-native.sh` vérifie que le fichier existe (`test -f`)
|
||
|
|
- Le script vérifie que le fichier est lisible (`test -r`)
|
||
|
|
- **MAIS** le script ne vérifie pas que le fichier est accessible depuis le working directory de l'application
|
||
|
|
- Le script ne crée pas de lien symbolique ou copie vers le working directory
|
||
|
|
|
||
|
|
**Impact**:
|
||
|
|
- L'application Go ne peut pas charger le fichier `.env` même si les variables sont chargées par systemd
|
||
|
|
|
||
|
|
#### 2.2.4 Problème #4: Ordre de Chargement des Variables d'Environnement
|
||
|
|
|
||
|
|
**Symptôme**: Les variables peuvent être chargées dans le mauvais ordre
|
||
|
|
|
||
|
|
**Ordre Actuel**:
|
||
|
|
1. Systemd charge `/etc/veza/backend-api.env` (via `EnvironmentFile`)
|
||
|
|
2. Wrapper script charge `/etc/veza/backend-api.env` (via `source`)
|
||
|
|
3. Application Go charge `.env` dans working directory (via `godotenv.Load()`)
|
||
|
|
4. Application Go charge `.env.{env}` et `.env` (via `LoadEnvFiles()`)
|
||
|
|
|
||
|
|
**Problème**:
|
||
|
|
- Si le fichier `.env` n'existe pas dans le working directory, `godotenv.Load()` échoue silencieusement
|
||
|
|
- Si les variables ne sont pas chargées par systemd/wrapper, l'application échoue
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. CAUSES RACINES
|
||
|
|
|
||
|
|
### 3.1 Cause Racine #1: Architecture Incohérente
|
||
|
|
- **Problème**: Mélange de deux approches (systemd EnvironmentFile + godotenv.Load())
|
||
|
|
- **Solution**: Choisir une seule approche et l'appliquer de manière cohérente
|
||
|
|
|
||
|
|
### 3.2 Cause Racine #2: Broken Pipe Non Géré au Démarrage
|
||
|
|
- **Problème**: Écriture sur stdout/stderr avant que journald ne soit prêt
|
||
|
|
- **Solution**: Rediriger stdout/stderr vers journald dès le début ou utiliser un logger qui gère les broken pipe
|
||
|
|
|
||
|
|
### 3.3 Cause Racine #3: Vérification Incomplète
|
||
|
|
- **Problème**: Le script de déploiement ne vérifie pas que l'application peut accéder au fichier .env
|
||
|
|
- **Solution**: Créer un fichier `.env` dans le working directory ou modifier l'application pour chercher le fichier au bon endroit
|
||
|
|
|
||
|
|
### 3.4 Cause Racine #4: Documentation Manquante
|
||
|
|
- **Problème**: Pas de documentation claire sur où le fichier .env doit être et comment il est chargé
|
||
|
|
- **Solution**: Documenter clairement l'architecture de chargement des variables d'environnement
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. SCÉNARIOS D'ÉCHEC
|
||
|
|
|
||
|
|
### 4.1 Scénario 1: Fichier .env Non Copié
|
||
|
|
```
|
||
|
|
1. Script de déploiement échoue silencieusement lors de la copie
|
||
|
|
2. Fichier /etc/veza/backend-api.env n'existe pas
|
||
|
|
3. Systemd ne charge pas les variables (mais ne signale pas d'erreur si EnvironmentFile est optionnel)
|
||
|
|
4. Wrapper script échoue avec "No such file or directory"
|
||
|
|
5. Application ne démarre pas
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.2 Scénario 2: Fichier .env Non Lisible
|
||
|
|
```
|
||
|
|
1. Fichier copié mais permissions incorrectes
|
||
|
|
2. Systemd peut charger les variables (root)
|
||
|
|
3. Wrapper script peut charger les variables (root)
|
||
|
|
4. Application Go cherche .env dans working directory (n'existe pas)
|
||
|
|
5. Application démarre mais avec variables manquantes
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.3 Scénario 3: Broken Pipe au Démarrage
|
||
|
|
```
|
||
|
|
1. Systemd démarre le service
|
||
|
|
2. Wrapper script écrit sur stdout/stderr
|
||
|
|
3. Journald n'est pas encore prêt → broken pipe
|
||
|
|
4. Application Go essaie d'écrire sur stdout/stderr → broken pipe
|
||
|
|
5. Application crash ou logs perdus
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.4 Scénario 4: Variables Non Chargées
|
||
|
|
```
|
||
|
|
1. Fichier .env copié correctement
|
||
|
|
2. Systemd charge les variables
|
||
|
|
3. Wrapper script charge les variables
|
||
|
|
4. Application Go cherche .env dans working directory (n'existe pas)
|
||
|
|
5. Si variables critiques manquantes → application échoue
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. POINTS DE VÉRIFICATION
|
||
|
|
|
||
|
|
### 5.1 Vérifications à Effectuer
|
||
|
|
|
||
|
|
#### 5.1.1 Dans le Conteneur
|
||
|
|
```bash
|
||
|
|
# Vérifier que le fichier existe
|
||
|
|
incus exec veza-backend-api -- test -f /etc/veza/backend-api.env && echo "OK" || echo "FAIL"
|
||
|
|
|
||
|
|
# Vérifier les permissions
|
||
|
|
incus exec veza-backend-api -- ls -la /etc/veza/backend-api.env
|
||
|
|
|
||
|
|
# Vérifier le contenu
|
||
|
|
incus exec veza-backend-api -- cat /etc/veza/backend-api.env
|
||
|
|
|
||
|
|
# Vérifier que systemd peut le charger
|
||
|
|
incus exec veza-backend-api -- systemctl show veza-backend-api --property=EnvironmentFiles
|
||
|
|
|
||
|
|
# Vérifier le working directory
|
||
|
|
incus exec veza-backend-api -- pwd
|
||
|
|
incus exec veza-backend-api -- ls -la /opt/veza/backend-api/
|
||
|
|
|
||
|
|
# Vérifier les variables d'environnement chargées
|
||
|
|
incus exec veza-backend-api -- systemctl show veza-backend-api --property=Environment
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 5.1.2 Logs Systemd
|
||
|
|
```bash
|
||
|
|
# Vérifier les logs du service
|
||
|
|
incus exec veza-backend-api -- journalctl -u veza-backend-api -n 50
|
||
|
|
|
||
|
|
# Vérifier les erreurs
|
||
|
|
incus exec veza-backend-api -- journalctl -u veza-backend-api -p err
|
||
|
|
|
||
|
|
# Vérifier les broken pipe
|
||
|
|
incus exec veza-backend-api -- journalctl -u veza-backend-api | grep -i "broken pipe"
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 5.1.3 Status du Service
|
||
|
|
```bash
|
||
|
|
# Vérifier le status
|
||
|
|
incus exec veza-backend-api -- systemctl status veza-backend-api
|
||
|
|
|
||
|
|
# Vérifier si le service est actif
|
||
|
|
incus exec veza-backend-api -- systemctl is-active veza-backend-api
|
||
|
|
|
||
|
|
# Vérifier les erreurs de démarrage
|
||
|
|
incus exec veza-backend-api -- systemctl status veza-backend-api | grep -i error
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. SOLUTIONS PROPOSÉES
|
||
|
|
|
||
|
|
### 6.1 Solution 1: Créer un Fichier .env dans le Working Directory (RECOMMANDÉ)
|
||
|
|
|
||
|
|
**Avantages**:
|
||
|
|
- Compatible avec l'application Go existante
|
||
|
|
- Pas de modification du code Go
|
||
|
|
- Simple à implémenter
|
||
|
|
|
||
|
|
**Inconvénients**:
|
||
|
|
- Duplication du fichier (mais acceptable pour la cohérence)
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
1. Modifier `deploy-service-native.sh` pour copier aussi le fichier vers `/opt/veza/backend-api/.env`
|
||
|
|
2. Vérifier que le fichier est créé et lisible
|
||
|
|
|
||
|
|
### 6.2 Solution 2: Modifier l'Application Go pour Chercher le Fichier au Bon Endroit
|
||
|
|
|
||
|
|
**Avantages**:
|
||
|
|
- Pas de duplication
|
||
|
|
- Architecture plus propre
|
||
|
|
|
||
|
|
**Inconvénients**:
|
||
|
|
- Modification du code Go
|
||
|
|
- Plus complexe à maintenir
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
1. Modifier `env_loader.go` pour chercher le fichier à `/etc/veza/backend-api.env` en premier
|
||
|
|
2. Fallback vers `.env` dans le working directory
|
||
|
|
|
||
|
|
### 6.3 Solution 3: Utiliser Uniquement Systemd EnvironmentFile
|
||
|
|
|
||
|
|
**Avantages**:
|
||
|
|
- Architecture simple
|
||
|
|
- Pas de duplication
|
||
|
|
- Variables chargées avant le démarrage de l'application
|
||
|
|
|
||
|
|
**Inconvénients**:
|
||
|
|
- Nécessite de supprimer `godotenv.Load()` du code Go
|
||
|
|
- Peut casser le développement local
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
1. Supprimer `godotenv.Load()` de `main.go`
|
||
|
|
2. Supprimer `LoadEnvFiles()` de `config.NewConfig()`
|
||
|
|
3. S'assurer que systemd charge toutes les variables nécessaires
|
||
|
|
|
||
|
|
### 6.4 Solution 4: Gérer les Broken Pipe au Démarrage
|
||
|
|
|
||
|
|
**Avantages**:
|
||
|
|
- Résout le problème des broken pipe
|
||
|
|
- Améliore la robustesse
|
||
|
|
|
||
|
|
**Implémentation**:
|
||
|
|
1. Rediriger stdout/stderr vers journald dès le début du wrapper script
|
||
|
|
2. S'assurer que le logger Go gère les broken pipe (déjà fait)
|
||
|
|
3. Éviter d'écrire sur stdout/stderr avant que le logger ne soit initialisé
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. RECOMMANDATION FINALE
|
||
|
|
|
||
|
|
**Solution Recommandée**: Combinaison de Solution 1 + Solution 4
|
||
|
|
|
||
|
|
1. **Créer un fichier `.env` dans le working directory** pour compatibilité avec l'application Go
|
||
|
|
2. **Gérer les broken pipe** en redirigeant stdout/stderr vers journald dès le début
|
||
|
|
3. **Améliorer les vérifications** dans le script de déploiement
|
||
|
|
4. **Documenter** l'architecture de chargement des variables d'environnement
|
||
|
|
|
||
|
|
**Raison**:
|
||
|
|
- Compatible avec l'application Go existante
|
||
|
|
- Résout les deux problèmes (broken pipe + fichier .env)
|
||
|
|
- Minimal impact sur le code existant
|
||
|
|
- Facile à maintenir
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. RISQUES ET MITIGATION
|
||
|
|
|
||
|
|
### 8.1 Risque 1: Duplication du Fichier .env
|
||
|
|
**Mitigation**: Le fichier est petit et la duplication est acceptable pour la cohérence
|
||
|
|
|
||
|
|
### 8.2 Risque 2: Modification du Script de Déploiement
|
||
|
|
**Mitigation**: Tester le script sur un conteneur de test avant production
|
||
|
|
|
||
|
|
### 8.3 Risque 3: Régression sur le Développement Local
|
||
|
|
**Mitigation**: S'assurer que les modifications n'affectent pas le développement local
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. TESTS DE VALIDATION
|
||
|
|
|
||
|
|
### 9.1 Tests à Effectuer
|
||
|
|
|
||
|
|
1. **Test 1: Déploiement Complet**
|
||
|
|
```bash
|
||
|
|
./config/incus/deploy-service-native.sh backend-api
|
||
|
|
./config/incus/verify-deployment.sh
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Test 2: Vérification du Fichier .env**
|
||
|
|
```bash
|
||
|
|
incus exec veza-backend-api -- test -f /etc/veza/backend-api.env
|
||
|
|
incus exec veza-backend-api -- test -f /opt/veza/backend-api/.env
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Test 3: Vérification des Variables**
|
||
|
|
```bash
|
||
|
|
incus exec veza-backend-api -- env | grep DATABASE_URL
|
||
|
|
incus exec veza-backend-api -- env | grep JWT_SECRET
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Test 4: Vérification des Logs**
|
||
|
|
```bash
|
||
|
|
incus exec veza-backend-api -- journalctl -u veza-backend-api -n 20
|
||
|
|
incus exec veza-backend-api -- journalctl -u veza-backend-api | grep -i "broken pipe"
|
||
|
|
```
|
||
|
|
|
||
|
|
5. **Test 5: Test de Santé**
|
||
|
|
```bash
|
||
|
|
incus exec veza-backend-api -- curl -f http://localhost:8080/health
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. RÉFÉRENCES
|
||
|
|
|
||
|
|
### 10.1 Fichiers Concernés
|
||
|
|
- `config/incus/deploy-service-native.sh` (lignes 315-358)
|
||
|
|
- `config/incus/systemd/veza-backend-api.service`
|
||
|
|
- `config/incus/scripts/start-backend-api.sh`
|
||
|
|
- `veza-backend-api/cmd/api/main.go` (ligne 50)
|
||
|
|
- `veza-backend-api/internal/config/env_loader.go`
|
||
|
|
- `veza-backend-api/internal/config/config.go` (ligne 210)
|
||
|
|
|
||
|
|
### 10.2 Documentation
|
||
|
|
- Systemd EnvironmentFile: https://www.freedesktop.org/software/systemd/man/systemd.exec.html#EnvironmentFile=
|
||
|
|
- godotenv: https://github.com/joho/godotenv
|
||
|
|
- Incus file push: https://linuxcontainers.org/incus/docs/main/instances/#file-management
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Fin de l'Audit**
|