From caffb74cbf49f8f1e5a5bdf98397b8fa319eb66e Mon Sep 17 00:00:00 2001 From: tristan Date: Tue, 31 Mar 2026 06:38:38 +0000 Subject: [PATCH] =?UTF-8?q?[#INFRA-142]=20Revoir=20le=20syst=C3=A8me=20de?= =?UTF-8?q?=20d=C3=A9ploiement=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit | Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Co-authored-by: gitea-actions Reviewed-on: https://gitea.malio.fr/MALIO-DEV/SIRH/pulls/11 Co-authored-by: tristan Co-committed-by: tristan --- .dockerignore | 23 +++++ .gitea/workflows/build-docker.yml | 30 ++++++ .gitea/workflows/release-artefact.yml | 65 ------------- deploy/docker/.env.example | 25 +++++ deploy/docker/Dockerfile.prod | 77 +++++++++++++++ deploy/docker/deploy.sh | 28 ++++++ deploy/docker/docker-compose.prod.yml | 13 +++ deploy/docker/nginx.conf | 46 +++++++++ deploy/docker/supervisord.conf | 28 ++++++ deploy/nginx/sirh-docker.conf | 12 +++ doc/deployment-docker.md | 135 ++++++++++++++++++++++++++ 11 files changed, 417 insertions(+), 65 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/build-docker.yml delete mode 100644 .gitea/workflows/release-artefact.yml create mode 100644 deploy/docker/.env.example create mode 100644 deploy/docker/Dockerfile.prod create mode 100755 deploy/docker/deploy.sh create mode 100644 deploy/docker/docker-compose.prod.yml create mode 100644 deploy/docker/nginx.conf create mode 100644 deploy/docker/supervisord.conf create mode 100644 deploy/nginx/sirh-docker.conf create mode 100644 doc/deployment-docker.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b49f8eb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,23 @@ +.git +.gitea +.env.local +.env.test +docker/ +deploy/docker/docker-compose.prod.yml +deploy/docker/deploy.sh +deploy/docker/.env.example +frontend/node_modules +frontend/.nuxt +frontend/.output +var/ +LOG/ +docs/ +doc/ +tests/ +*.sql +*.xlsx +*.png +*.md +!composer.lock +!symfony.lock +!frontend/package-lock.json diff --git a/.gitea/workflows/build-docker.yml b/.gitea/workflows/build-docker.yml new file mode 100644 index 0000000..b00f8a2 --- /dev/null +++ b/.gitea/workflows/build-docker.yml @@ -0,0 +1,30 @@ +name: Build & Push Docker Image + +on: + push: + tags: + - "v*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to Gitea Registry + run: | + echo "${{ secrets.RELEASE_TOKEN }}" | docker login gitea.malio.fr -u "${{ gitea.repository_owner }}" --password-stdin + + - name: Build Docker image + run: | + docker build \ + -f deploy/docker/Dockerfile.prod \ + -t gitea.malio.fr/malio-dev/sirh:${{ github.ref_name }} \ + -t gitea.malio.fr/malio-dev/sirh:latest \ + . + + - name: Push Docker image + run: | + docker push gitea.malio.fr/malio-dev/sirh:${{ github.ref_name }} + docker push gitea.malio.fr/malio-dev/sirh:latest diff --git a/.gitea/workflows/release-artefact.yml b/.gitea/workflows/release-artefact.yml deleted file mode 100644 index e9a4c3d..0000000 --- a/.gitea/workflows/release-artefact.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Build Release Artefact - -on: - push: - tags: - - "v*" - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: "8.4" - extensions: mbstring, intl, pdo_pgsql, xml, curl, zip, gd - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - - name: Install backend deps (prod) - env: - APP_ENV: prod - APP_DEBUG: "0" - run: composer install --no-dev --optimize-autoloader --no-interaction --no-scripts - - - name: Build frontend (static) - run: | - cd frontend - npm ci - CI=1 NUXT_TELEMETRY_DISABLED=1 NUXT_PUBLIC_API_BASE=/api NUXT_PUBLIC_APP_BASE=/ npm run generate - test -f .output/public/index.html - - - name: Build artefact - shell: bash - run: | - set -euo pipefail - mkdir -p release - tar -czf "release/sirh-${GITHUB_REF_NAME}.tar.gz" \ - bin \ - config \ - migrations \ - public \ - src \ - templates \ - vendor \ - composer.json \ - composer.lock \ - symfony.lock \ - frontend/.output - - - name: Create Release - uses: softprops/action-gh-release@v2 - with: - files: release/sirh-${{ github.ref_name }}.tar.gz - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} diff --git a/deploy/docker/.env.example b/deploy/docker/.env.example new file mode 100644 index 0000000..bc2bdec --- /dev/null +++ b/deploy/docker/.env.example @@ -0,0 +1,25 @@ +# Symfony +APP_ENV=prod +APP_DEBUG=0 +APP_SECRET=change-me + +# Database (use host.docker.internal to reach bare-metal PostgreSQL) +DATABASE_URL="postgresql://sirh_user:password@host.docker.internal:5432/sirh?serverVersion=16&charset=utf8" + +# JWT +JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem +JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem +JWT_PASSPHRASE=change-me +JWT_COOKIE_SECURE=1 +JWT_COOKIE_SAMESITE=lax +JWT_TOKEN_TTL=86400 +JWT_COOKIE_TTL=86400 + +# CORS +CORS_ALLOW_ORIGIN='^https?://sirh\.malio-dev\.fr$' + +# App +DEFAULT_URI=https://sirh.malio-dev.fr +APP_SHARE_DIR=var/share +RTT_START_DATE=2026-02-23 +HOLIDAY_URL="https://calendrier.api.gouv.fr/jours-feries/" diff --git a/deploy/docker/Dockerfile.prod b/deploy/docker/Dockerfile.prod new file mode 100644 index 0000000..1d54694 --- /dev/null +++ b/deploy/docker/Dockerfile.prod @@ -0,0 +1,77 @@ +# --- Stage 1: Build backend --- +FROM php:8.4-cli AS backend-build + +RUN apt-get update && apt-get install -y \ + libicu-dev libpq-dev libpng-dev libzip-dev libxml2-dev \ + unzip curl git \ + && docker-php-ext-install -j$(nproc) intl pdo_pgsql zip gd opcache \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +WORKDIR /app +COPY composer.json composer.lock symfony.lock ./ +RUN APP_ENV=prod APP_DEBUG=0 composer install --no-dev --optimize-autoloader --no-scripts --no-interaction + +COPY bin bin/ +COPY config config/ +COPY migrations migrations/ +COPY public public/ +COPY src src/ +COPY templates templates/ + +# --- Stage 2: Build frontend --- +FROM node:lts-alpine AS frontend-build + +WORKDIR /app/frontend +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci + +COPY frontend/ ./ +ENV CI=1 \ + NUXT_TELEMETRY_DISABLED=1 \ + NUXT_PUBLIC_API_BASE=/api \ + NUXT_PUBLIC_APP_BASE=/ +RUN npm run generate + +# --- Stage 3: Production image --- +FROM php:8.4-fpm AS production + +RUN apt-get update && apt-get install -y \ + libicu-dev libpq-dev libpng-dev libzip-dev libxml2-dev \ + nginx supervisor \ + && docker-php-ext-install -j$(nproc) intl pdo_pgsql zip gd opcache \ + && rm -rf /var/lib/apt/lists/* + +# PHP production config +RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" + +# PHP-FPM: forward worker output to stderr for docker logs +RUN echo "catch_workers_output = yes" >> /usr/local/etc/php-fpm.d/www.conf \ + && echo "decorate_workers_output = no" >> /usr/local/etc/php-fpm.d/www.conf + +# Nginx: log to stdout/stderr +RUN ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log + +# Remove default nginx site +RUN rm -f /etc/nginx/sites-enabled/default + +# Configs +COPY deploy/docker/supervisord.conf /etc/supervisor/conf.d/app.conf +COPY deploy/docker/nginx.conf /etc/nginx/sites-enabled/sirh.conf + +# Backend from stage 1 +COPY --from=backend-build /app /var/www/html + +# Frontend from stage 2 +COPY --from=frontend-build /app/frontend/.output/public /var/www/html/frontend/.output/public + +# Permissions +RUN mkdir -p /var/www/html/var \ + && chown -R www-data:www-data /var/www/html/var + +WORKDIR /var/www/html +EXPOSE 80 + +CMD ["supervisord", "-n", "-c", "/etc/supervisor/conf.d/app.conf"] diff --git a/deploy/docker/deploy.sh b/deploy/docker/deploy.sh new file mode 100755 index 0000000..db2f765 --- /dev/null +++ b/deploy/docker/deploy.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +TAG="${1:-latest}" +export SIRH_IMAGE_TAG="$TAG" + +echo "==> Deploying sirh:${TAG}..." + +echo "==> Pulling image..." +docker compose pull + +echo "==> Starting container..." +docker compose up -d + +echo "==> Waiting for container to be ready..." +sleep 3 + +echo "==> Running migrations..." +docker compose exec -T app php bin/console doctrine:migrations:migrate --no-interaction + +echo "==> Clearing cache..." +docker compose exec -T app php bin/console cache:clear --env=prod +docker compose exec -T app php bin/console cache:warmup --env=prod + +VERSION=$(docker compose exec -T app cat config/version.yaml | grep 'app.version' | awk -F"'" '{print $2}') +echo "==> Deployed v${VERSION}" diff --git a/deploy/docker/docker-compose.prod.yml b/deploy/docker/docker-compose.prod.yml new file mode 100644 index 0000000..6000ed8 --- /dev/null +++ b/deploy/docker/docker-compose.prod.yml @@ -0,0 +1,13 @@ +services: + app: + image: gitea.malio.fr/malio-dev/sirh:${SIRH_IMAGE_TAG:-latest} + container_name: sirh-app + env_file: .env + ports: + - "8080:80" + volumes: + - ./config/jwt:/var/www/html/config/jwt:ro + - ./uploads:/var/www/html/var/uploads + extra_hosts: + - "host.docker.internal:host-gateway" + restart: unless-stopped diff --git a/deploy/docker/nginx.conf b/deploy/docker/nginx.conf new file mode 100644 index 0000000..dcfb549 --- /dev/null +++ b/deploy/docker/nginx.conf @@ -0,0 +1,46 @@ +server { + listen 80; + server_name _; + + root /var/www/html/frontend/.output/public; + index index.html; + + access_log /dev/stdout; + error_log /dev/stderr; + + location ^~ /api/ { + root /var/www/html/public; + try_files $uri /index.php?$query_string; + } + + location ^~ /bundles/ { + root /var/www/html/public; + try_files $uri =404; + } + + location = /api/login_check { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php; + fastcgi_param DOCUMENT_ROOT /var/www/html/public; + fastcgi_param SCRIPT_NAME /index.php; + fastcgi_param PATH_INFO /login_check; + fastcgi_param REQUEST_URI /login_check; + fastcgi_pass 127.0.0.1:9000; + } + + location ~ ^/index\.php(/|$) { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php; + fastcgi_param DOCUMENT_ROOT /var/www/html/public; + fastcgi_pass 127.0.0.1:9000; + internal; + } + + location ~ \.php$ { + return 404; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/deploy/docker/supervisord.conf b/deploy/docker/supervisord.conf new file mode 100644 index 0000000..dafb36a --- /dev/null +++ b/deploy/docker/supervisord.conf @@ -0,0 +1,28 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/var/run/supervisord.pid + +[program:php-fpm] +command=php-fpm -F +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stopasgroup=true +stopsignal=QUIT + +[program:nginx] +command=nginx -g "daemon off;" +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stopasgroup=true +stopsignal=QUIT diff --git a/deploy/nginx/sirh-docker.conf b/deploy/nginx/sirh-docker.conf new file mode 100644 index 0000000..7006208 --- /dev/null +++ b/deploy/nginx/sirh-docker.conf @@ -0,0 +1,12 @@ +server { + listen 80; + server_name sirh.malio-dev.fr; + + location / { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/doc/deployment-docker.md b/doc/deployment-docker.md new file mode 100644 index 0000000..25a6c8d --- /dev/null +++ b/doc/deployment-docker.md @@ -0,0 +1,135 @@ +# Deploiement Docker — SIRH + +## Pre-requis + +Installer Docker et Docker Compose sur la machine : + +```bash +# Ubuntu +sudo apt update +sudo apt install -y ca-certificates curl gnupg +sudo install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt update +sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin +sudo usermod -aG docker $USER +``` + +Se deconnecter/reconnecter pour que le groupe `docker` prenne effet. + +## Premier deploiement + +### 1. Creer le dossier de deploiement + +```bash +sudo mkdir -p /var/www/sirh +sudo chown -R $(whoami):$(whoami) /var/www/sirh +``` + +### 2. Copier les fichiers depuis le repo + +```bash +cp deploy/docker/docker-compose.prod.yml /var/www/sirh/docker-compose.yml +cp deploy/docker/deploy.sh /var/www/sirh/deploy.sh +cp deploy/docker/.env.example /var/www/sirh/.env +chmod +x /var/www/sirh/deploy.sh +``` + +### 3. Configurer l'environnement + +Editer `/var/www/sirh/.env` avec les vraies valeurs : +- `APP_SECRET` : generer avec `openssl rand -hex 32` +- `DATABASE_URL` : `postgresql://user:pass@host.docker.internal:5432/sirh?serverVersion=16&charset=utf8` +- `JWT_PASSPHRASE` : generer avec `openssl rand -hex 32` + +### 4. Generer les cles JWT + +```bash +cd /var/www/sirh +mkdir -p config/jwt +docker run --rm -v $(pwd)/config/jwt:/jwt php:8.4-cli bash -c \ + "apt-get update -qq && apt-get install -y -qq openssl > /dev/null && \ + openssl genpkey -algorithm RSA -out /jwt/private.pem -pkeyopt rsa_keygen_bits:4096 && \ + openssl rsa -pubout -in /jwt/private.pem -out /jwt/public.pem" +``` + +### 5. Creer le dossier uploads + +```bash +mkdir -p /var/www/sirh/uploads +``` + +### 6. Configurer le login au registry Gitea + +```bash +docker login gitea.malio.fr +# Username: ton user Gitea +# Password: ton token Gitea +``` + +### 7. Configurer Nginx systeme + +```bash +sudo cp deploy/nginx/sirh-docker.conf /etc/nginx/sites-available/sirh.conf +sudo ln -sf /etc/nginx/sites-available/sirh.conf /etc/nginx/sites-enabled/sirh.conf +sudo nginx -t && sudo systemctl reload nginx +``` + +### 8. Deployer + +```bash +cd /var/www/sirh +./deploy.sh +``` + +## Deployer une release + +```bash +cd /var/www/sirh +./deploy.sh # deploie la derniere version (latest) +./deploy.sh v0.1.61 # deploie une version specifique +``` + +## Rollback + +### Image seule (pas de changement de schema BDD) + +```bash +./deploy.sh v0.1.60 +``` + +### Avec rollback de migration + +```bash +# 1. Rollback schema (pendant que la version actuelle tourne encore) +docker compose exec -T app php bin/console doctrine:migrations:migrate prev --no-interaction +# 2. Deployer l'ancienne version +./deploy.sh v0.1.60 +``` + +## Voir les logs + +```bash +cd /var/www/sirh +docker compose logs -f # tous les logs +docker compose logs -f --tail=100 # 100 dernieres lignes +``` + +## Migration depuis l'ancien deploiement (tar.gz) + +Si l'application tourne deja en bare metal : + +1. Installer Docker (voir pre-requis) +2. Creer le dossier `/var/www/sirh-docker/` (ne pas ecraser l'ancien) +3. Copier les fichiers : + ```bash + cp /var/www/sirh/.env /var/www/sirh-docker/.env + cp -a /var/www/sirh/config/jwt /var/www/sirh-docker/config/jwt + cp -a /var/www/sirh/var/uploads /var/www/sirh-docker/uploads + ``` +4. Editer `/var/www/sirh-docker/.env` : changer `DATABASE_URL` pour utiliser `host.docker.internal` au lieu de `127.0.0.1` +5. Mettre a jour Nginx systeme : remplacer la conf par `deploy/nginx/sirh-docker.conf` +6. Arreter l'ancien PHP-FPM : `sudo systemctl stop php8.4-fpm` +7. Deployer : `cd /var/www/sirh-docker && ./deploy.sh` +8. Verifier que tout marche, puis supprimer l'ancien dossier