feat(db): bootstrap PostgreSQL et extension TimescaleDB

Service `db` du docker-compose racine sur timescale/timescaledb-ha:pg17,
volume nomme et cibles Makefile db-up / db-down / db-reset / db-logs / db-psql.

- db/init/100-extensions.sql declare l'extension attendue, db/init/110 cree
  la base enervision_test utilisee par la suite de tests du backend.
- Numerotation a partir de 100 : l'image depose ses propres scripts 000, 001
  et 010, et un prefixe a deux chiffres se trie avant 010 en locale C.
- Volume monte sur /home/postgres/pgdata/data, PGDATA de cette image. Monte
  au chemin habituel de l'image postgres, il ne retiendrait rien sans erreur.
- Port publie 5433 par defaut, 5432 etant souvent deja pris sur un poste.
This commit is contained in:
Johan LEROY
2026-09-14 14:19:25 +02:00
parent 91f4f007d3
commit 351e928309
7 changed files with 119 additions and 3 deletions
+15
View File
@@ -0,0 +1,15 @@
# Variables lues par docker-compose.yml a la racine.
# Le backend lance hors conteneur (`make dev`) lit apps/backend/.env, pas ce fichier.
POSTGRES_USER=enervision
POSTGRES_PASSWORD=change_me
POSTGRES_DB=enervision
# 5432 est souvent deja pris par une autre base du poste.
POSTGRES_PORT=5433
APP_ENV=local
APP_DEBUG=true
APP_LOG_LEVEL=INFO
APP_SECRET_KEY=change_me
APP_CORS_ORIGINS=http://localhost:4200
BACKEND_PORT=8000
+24 -2
View File
@@ -1,7 +1,8 @@
BACKEND := apps/backend
.DEFAULT_GOAL := help
.PHONY: help install dev lint format typecheck test check docker-build
.PHONY: help install dev lint format typecheck test test-integration check docker-build \
db-up db-down db-reset db-logs db-psql migrate
help: ## Liste les cibles disponibles
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'
@@ -21,10 +22,31 @@ format: ## Formate et corrige le backend
typecheck: ## Verifie le typage du backend
cd $(BACKEND) && uv run mypy app
test: ## Execute les tests backend
test: ## Execute les tests backend ne demandant pas de base
cd $(BACKEND) && uv run pytest
test-integration: ## Execute les tests exigeant une base joignable
cd $(BACKEND) && uv run pytest -m integration
check: lint typecheck test ## Chaine de verification complete
docker-build: ## Construit l'image du backend
docker build -t enervision-backend:local $(BACKEND)
db-up: ## Demarre la base PostgreSQL TimescaleDB
docker compose up -d db
db-down: ## Arrete la base en conservant ses donnees
docker compose stop db
db-reset: ## Detruit la base et rejoue db/init
docker compose down -v && docker compose up -d db
db-logs: ## Suit les journaux de la base
docker compose logs -f db
db-psql: ## Ouvre une session psql sur la base applicative
docker compose exec db psql -U $${POSTGRES_USER:-enervision} -d $${POSTGRES_DB:-enervision}
migrate: ## Applique les migrations Alembic
cd $(BACKEND) && uv run alembic upgrade head
+25 -1
View File
@@ -1,6 +1,7 @@
# Base de donnees
PostgreSQL avec l'extension TimescaleDB. Non initialise, voir le ticket dedie.
PostgreSQL 17 avec l'extension TimescaleDB, servie en local par le service `db` du
`docker-compose.yml` racine (image `timescale/timescaledb-ha:pg17`).
- `init` : scripts de bootstrap joues au premier demarrage du conteneur.
- `migrations` : migrations SQL versionnees.
@@ -8,3 +9,26 @@ PostgreSQL avec l'extension TimescaleDB. Non initialise, voir le ticket dedie.
Les migrations du schema applicatif expose par l'API vivent dans
`apps/backend/alembic`, pas ici.
## `init` ne rejoue jamais
Le dossier est monte sur `/docker-entrypoint-initdb.d`, dont PostgreSQL ne joue le
contenu qu'a la toute premiere initialisation, quand `PGDATA` est vide. Modifier ou
ajouter un script ensuite reste sans effet sur une base existante :
```bash
docker compose down -v && docker compose up -d db
```
L'image joue d'abord ses propres scripts (`000_`, `001_`, `010_`), dont un
`CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit CASCADE` qui installe `timescaledb`
au passage dans `postgres`, `template1` et la base applicative. Nos fichiers sont
numerotes a partir de `100` pour passer apres, quelle que soit la locale de tri.
| Script | Role |
|---|---|
| `100-extensions.sql` | Declare explicitement les extensions attendues. |
| `110-test-database.sql` | Cree `enervision_test`, attendue par la suite de tests du backend. |
Comme un bootstrap peut toujours avoir ete saute, c'est `/api/v1/health/ready` qui fait
foi : la sonde refuse de repondre 200 si l'extension n'est pas chargee.
View File
+4
View File
@@ -0,0 +1,4 @@
-- Piege : ce script ne rejoue qu'a la premiere initialisation, quand PGDATA est vide.
-- Le modifier ensuite reste sans effet tant que le volume n'est pas detruit.
CREATE EXTENSION IF NOT EXISTS timescaledb;
+8
View File
@@ -0,0 +1,8 @@
-- Contrainte : le nom de cette base est code en dur dans apps/backend/tests/conftest.py.
-- Elle sert la suite de tests de la stack locale, pas un deploiement.
CREATE DATABASE enervision_test;
\connect enervision_test
CREATE EXTENSION IF NOT EXISTS timescaledb;
+43
View File
@@ -0,0 +1,43 @@
# Piege : PGDATA de l'image timescaledb-ha vaut /home/postgres/pgdata/data, pas le chemin
# habituel de l'image postgres. Monte ailleurs, le volume ne retient rien, sans erreur.
name: enervision
services:
db:
image: timescale/timescaledb-ha:pg17
environment:
POSTGRES_USER: ${POSTGRES_USER:?}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?}
POSTGRES_DB: ${POSTGRES_DB:?}
ports:
- "${POSTGRES_PORT:-5433}:5432"
volumes:
- pgdata:/home/postgres/pgdata/data
- ./db/init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 12
start_period: 40s
restart: unless-stopped
backend:
build: ./apps/backend
depends_on:
db:
condition: service_healthy
environment:
APP_ENV: ${APP_ENV:-local}
APP_DEBUG: ${APP_DEBUG:-false}
APP_LOG_LEVEL: ${APP_LOG_LEVEL:-INFO}
APP_SECRET_KEY: ${APP_SECRET_KEY:?}
APP_CORS_ORIGINS: ${APP_CORS_ORIGINS:-http://localhost:4200}
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
ports:
- "${BACKEND_PORT:-8000}:8000"
restart: unless-stopped
volumes:
pgdata: