feat: backup cron
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
# Daily Meal API
|
||||
|
||||
API Laravel de Daily Meal.
|
||||
|
||||
## Docker en production
|
||||
|
||||
Le fichier source du dépôt est `docker-compose.prod.yml`. Sur le serveur, il est
|
||||
déployé sous le nom `docker-compose.yml`. Les commandes de ce guide sont donc
|
||||
exécutées depuis le dossier de l'application sans option `-f`.
|
||||
|
||||
### Configuration
|
||||
|
||||
Le Compose charge la configuration applicative depuis le `.env` de production.
|
||||
Les variables Laravel ne sont pas répétées dans le fichier Compose afin de
|
||||
conserver une seule source de vérité.
|
||||
|
||||
Exemple des variables propres à l'infrastructure :
|
||||
|
||||
```dotenv
|
||||
IMAGE_TAG=2026-08-13.1
|
||||
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
LOG_CHANNEL=stderr
|
||||
LOG_LEVEL=warning
|
||||
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=bowli
|
||||
DB_USERNAME=bowli
|
||||
DB_PASSWORD=change-me
|
||||
|
||||
REDIS_HOST=redis
|
||||
QUEUE_CONNECTION=redis
|
||||
SCOUT_DRIVER=meilisearch
|
||||
MEILISEARCH_HOST=http://meilisearch:7700
|
||||
MEILISEARCH_KEY=change-me
|
||||
|
||||
FILESYSTEM_DISK=s3
|
||||
AWS_ACCESS_KEY_ID=change-me
|
||||
AWS_SECRET_ACCESS_KEY=change-me
|
||||
AWS_DEFAULT_REGION=eu-west-3
|
||||
```
|
||||
|
||||
`IMAGE_TAG` est obligatoire. Utiliser un tag de release immuable permet de savoir
|
||||
exactement quelle version est déployée et évite l'utilisation accidentelle de
|
||||
`latest`.
|
||||
|
||||
### Volumes et réseau externes
|
||||
|
||||
PostgreSQL, Redis, Meilisearch et les sauvegardes locales utilisent des volumes
|
||||
externes. Ils restent présents quand le projet Compose est recréé.
|
||||
|
||||
À effectuer une seule fois sur un nouveau serveur :
|
||||
|
||||
```bash
|
||||
docker volume create bemeal_pgsql_data
|
||||
docker volume create bemeal_redis_data
|
||||
docker volume create bemeal_meilisearch_data
|
||||
docker volume create bemeal_database_backups
|
||||
docker network create proxy
|
||||
```
|
||||
|
||||
Ces commandes sont idempotentes. Le réseau `proxy` peut déjà avoir été créé par
|
||||
le reverse proxy. Ne jamais supprimer `bemeal_pgsql_data` sans avoir validé une
|
||||
sauvegarde.
|
||||
|
||||
### Premier démarrage
|
||||
|
||||
```bash
|
||||
docker compose config -q
|
||||
docker compose build db-backup
|
||||
docker compose pull app postgres redis meilisearch
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
`docker compose build db-backup` construit l'image locale contenant `pg_dump`,
|
||||
`pg_restore`, AWS CLI, le cron et les scripts de `docker/database-backup`. Il faut
|
||||
l'exécuter au premier déploiement, après une modification de ces scripts ou après
|
||||
un changement de version de PostgreSQL.
|
||||
|
||||
Pour reconstruire et redémarrer uniquement ce service :
|
||||
|
||||
```bash
|
||||
docker compose up -d --build db-backup
|
||||
```
|
||||
|
||||
### Mettre à jour l'application
|
||||
|
||||
Après avoir changé `IMAGE_TAG` dans `.env` :
|
||||
|
||||
```bash
|
||||
docker compose config -q
|
||||
docker compose pull app
|
||||
docker compose up -d --remove-orphans
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Si les migrations ne sont pas déjà gérées par le pipeline ou l'image :
|
||||
|
||||
```bash
|
||||
docker compose exec app php artisan migrate --force
|
||||
```
|
||||
|
||||
## Sauvegardes PostgreSQL vers S3
|
||||
|
||||
Le service `db-backup` lance une sauvegarde au démarrage, puis chaque jour à
|
||||
03:00. Chaque archive est compressée, contrôlée avec `pg_restore`, accompagnée
|
||||
d'un checksum SHA-256 puis envoyée vers S3 avec le chiffrement AES-256. Les
|
||||
copies locales sont conservées 7 jours par défaut.
|
||||
|
||||
```dotenv
|
||||
BACKUP_CRON="0 3 * * *"
|
||||
BACKUP_RUN_ON_START=true
|
||||
BACKUP_LOCAL_RETENTION_DAYS=7
|
||||
BACKUP_S3_BUCKET=bowli-backup
|
||||
BACKUP_S3_PREFIX=database-backups/postgres
|
||||
BACKUP_S3_ENDPOINT=
|
||||
TZ=Europe/Paris
|
||||
```
|
||||
|
||||
Ces valeurs sont les valeurs par défaut et peuvent être omises du `.env`. Le
|
||||
bucket `bowli-backup` doit rester entièrement privé. `BACKUP_S3_ENDPOINT` reste
|
||||
vide avec AWS S3.
|
||||
|
||||
### Autorisations IAM minimales
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "ListDatabaseBackups",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": "arn:aws:s3:::bowli-backup",
|
||||
"Condition": {
|
||||
"StringLike": {
|
||||
"s3:prefix": [
|
||||
"database-backups/postgres",
|
||||
"database-backups/postgres/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sid": "ManageDatabaseBackupObjects",
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:PutObject", "s3:GetObject"],
|
||||
"Resource": "arn:aws:s3:::bowli-backup/database-backups/postgres/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Le conteneur ne supprime pas les sauvegardes distantes. Configurer une règle de
|
||||
cycle de vie S3 sur ce préfixe, par exemple après 30 ou 90 jours.
|
||||
|
||||
### Sauvegarde manuelle et contrôle
|
||||
|
||||
```bash
|
||||
docker compose exec db-backup /usr/local/bin/backup-database manual
|
||||
docker compose ps db-backup
|
||||
docker compose logs --tail=100 db-backup
|
||||
```
|
||||
|
||||
## Restaurer une sauvegarde
|
||||
|
||||
Pour restaurer la sauvegarde S3 la plus récente :
|
||||
|
||||
```bash
|
||||
./docker/database-backup/restore-production latest-s3
|
||||
```
|
||||
|
||||
Autres sources acceptées :
|
||||
|
||||
```bash
|
||||
# Dernière sauvegarde du volume local
|
||||
./docker/database-backup/restore-production latest-local
|
||||
|
||||
# Sauvegarde S3 précise
|
||||
./docker/database-backup/restore-production \
|
||||
s3://bowli-backup/database-backups/postgres/20260813T030000Z-bowli-scheduled.dump
|
||||
```
|
||||
|
||||
La restauration demande une confirmation explicite. Elle arrête l'application
|
||||
et les workers, crée une sauvegarde de sécurité, vérifie l'archive, restaure dans
|
||||
une base temporaire puis échange les bases. L'ancienne base est conservée sous
|
||||
un nom daté pour permettre un rollback.
|
||||
|
||||
Les services redémarrent uniquement si toute l'opération réussit. En cas
|
||||
d'échec, ils restent arrêtés afin d'éviter d'écrire dans une base potentiellement
|
||||
incomplète.
|
||||
|
||||
Pour utiliser exceptionnellement un autre nom de fichier Compose :
|
||||
|
||||
```bash
|
||||
COMPOSE_FILE=docker-compose.prod.yml \
|
||||
./docker/database-backup/restore-production latest-s3
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user