From e984ec04b5c6b6fbfa7a625f1df607a09d7a19f2 Mon Sep 17 00:00:00 2001 From: Leon Morival Date: Tue, 19 May 2026 15:52:29 +0200 Subject: [PATCH] feat: docker docmpose --- .gitea/workflows/deploy.yml | 84 ++++++++++++------------------------- docker-compose.prod.yml | 71 +++++++++++++++++++++---------- 2 files changed, 74 insertions(+), 81 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 14db132..da0cd0c 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -63,7 +63,7 @@ jobs: # DEPLOY BLUE / GREEN (CADDY GÈRE LE ROUTING) # ========================= deploy: - name: Deploy Blue/Green + name: Deploy Blue/Green (Caddy routing) needs: build runs-on: ubuntu-latest @@ -78,98 +78,66 @@ jobs: username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} source: docker-compose.prod.yml - target: ${{ secrets.DEPLOY_PATH || '/opt/bemeal' }} + target: /opt/bemeal - name: Deploy via SSH uses: appleboy/ssh-action@v1.0.3 env: IMAGE_TAG: ${{ github.sha }} - REGISTRY: ${{ env.REGISTRY }} - IMAGE_NAME: ${{ env.IMAGE_NAME }} - DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} + REGISTRY: gitea.leonmorival.com + IMAGE_NAME: daily_meal/bemeal-api with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} - envs: REGISTRY,IMAGE_NAME,IMAGE_TAG,DEPLOY_PATH + envs: IMAGE_TAG,REGISTRY,IMAGE_NAME script: | set -eu - APP_DIR="${DEPLOY_PATH:-/opt/bemeal}" - COMPOSE_FILE="docker-compose.prod.yml" - - cd "$APP_DIR" - - COMPOSE="docker compose --env-file .env.prod -f $COMPOSE_FILE" + cd /opt/bemeal + COMPOSE="docker compose --env-file .env.prod -f docker-compose.prod.yml" IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" - echo "Pull image: $IMAGE" docker pull "$IMAGE" - echo "Start shared services" + echo "Deploy shared infra" $COMPOSE up -d pgsql redis meilisearch - # ========================= - # BLUE / GREEN DETECTION - # ========================= + # detect active ACTIVE="" - - if docker ps --format '{{.Names}}' | grep -q "bemeal-api-blue"; then - if [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-blue 2>/dev/null || true)" = "healthy" ]; then - ACTIVE="blue" - fi + if docker ps | grep -q bemeal-api-blue && \ + [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-blue 2>/dev/null || true)" = "healthy" ]; then + ACTIVE=blue fi - if docker ps --format '{{.Names}}' | grep -q "bemeal-api-green"; then - if [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-green 2>/dev/null || true)" = "healthy" ]; then - ACTIVE="green" - fi + if docker ps | grep -q bemeal-api-green && \ + [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-green 2>/dev/null || true)" = "healthy" ]; then + ACTIVE=green fi if [ "$ACTIVE" = "blue" ]; then - TARGET="green" + TARGET=green else - TARGET="blue" + TARGET=blue fi TARGET_SERVICE="bemeal-api-$TARGET" - echo "Deploying to $TARGET_SERVICE" + echo "Deploy $TARGET_SERVICE" - IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" \ - $COMPOSE up -d --force-recreate --no-deps "$TARGET_SERVICE" - - # ========================= - # HEALTHCHECK - # ========================= - echo "Waiting for healthcheck..." + IMAGE="$IMAGE" $COMPOSE up -d --no-deps --force-recreate "$TARGET_SERVICE" + # wait health for i in $(seq 1 60); do - STATUS="$(docker inspect -f '{{.State.Health.Status}}' "$TARGET_SERVICE" 2>/dev/null || true)" - - if [ "$STATUS" = "healthy" ]; then - break - fi - + STATUS=$(docker inspect -f '{{.State.Health.Status}}' "$TARGET_SERVICE" || true) + [ "$STATUS" = "healthy" ] && break sleep 2 done - if [ "$STATUS" != "healthy" ]; then - echo "Healthcheck failed" - docker logs --tail=200 "$TARGET_SERVICE" || true - exit 1 - fi + [ "$STATUS" != "healthy" ] && exit 1 - docker exec "$TARGET_SERVICE" wget -qO- http://127.0.0.1/api/health >/dev/null || exit 1 + # 👉 SWITCH ULTRA SIMPLE + echo "bemeal-api-$TARGET" > /opt/bemeal/caddy/active - echo "Deployment successful" - - # ========================= - # CLEAN OLD CONTAINER - # ========================= - if [ -n "$ACTIVE" ] && [ "$ACTIVE" != "$TARGET" ]; then - echo "Stopping old $ACTIVE" - $COMPOSE stop "bemeal-api-$ACTIVE" || true - $COMPOSE rm -f "bemeal-api-$ACTIVE" || true - fi + docker exec caddy caddy reload diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 541d3ce..ce3f46a 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,7 +1,13 @@ name: bemeal +# ========================= +# IMAGE +# ========================= x-app-image: &app-image gitea.leonmorival.com/daily_meal/bemeal-api:${IMAGE_TAG:-latest} +# ========================= +# ENV +# ========================= x-app-environment: &app-environment APP_LOCALE: "fr" APP_ENV: "production" @@ -14,33 +20,30 @@ x-app-environment: &app-environment FILESYSTEM_DISK: "public" DB_CONNECTION: "pgsql" - DB_HOST: "pgsql" + DB_HOST: "bemeal-pgsql" DB_PORT: "5432" DB_DATABASE: "${DB_DATABASE:?DB_DATABASE is required}" DB_USERNAME: "${DB_USERNAME:?DB_USERNAME is required}" DB_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}" - GEMINI_API_KEY: "${GEMINI_API_KEY:?GEMINI_API_KEY is required}" - - AI_MEAL_USER_NAME: "${AI_MEAL_USER_NAME:-Lolo AI}" - AI_MEAL_USER_EMAIL: "${AI_MEAL_USER_EMAIL:-loloai@maily.com}" - AI_MEAL_IMAGE_PATH: "${AI_MEAL_IMAGE_PATH:-meal-posts/ai-generated}" - AI_MEAL_IMAGE_QUALITY: "${AI_MEAL_IMAGE_QUALITY:-medium}" - AI_MEAL_TEXT_TIMEOUT: "${AI_MEAL_TEXT_TIMEOUT:-90}" - AI_MEAL_IMAGE_TIMEOUT: "${AI_MEAL_IMAGE_TIMEOUT:-120}" - - REDIS_HOST: "redis" + REDIS_HOST: "bemeal-redis" REDIS_CLIENT: "predis" QUEUE_CONNECTION: "redis" SCOUT_DRIVER: "meilisearch" - MEILISEARCH_HOST: "http://meilisearch:7700" + MEILISEARCH_HOST: "http://bemeal-meilisearch:7700" MEILISEARCH_KEY: "${MEILISEARCH_KEY:?MEILISEARCH_KEY is required}" +# ========================= +# VOLUMES +# ========================= x-app-volumes: &app-volumes - storage-data:/var/www/html/storage/app - storage-public-data:/var/www/html/storage/app/public +# ========================= +# DEPENDS +# ========================= x-app-depends-on: &app-depends-on pgsql: condition: service_healthy @@ -49,6 +52,9 @@ x-app-depends-on: &app-depends-on meilisearch: condition: service_started +# ========================= +# HEALTHCHECK +# ========================= x-app-healthcheck: &app-healthcheck test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/api/health >/dev/null 2>&1"] start_period: 30s @@ -56,6 +62,9 @@ x-app-healthcheck: &app-healthcheck timeout: 5s retries: 12 +# ========================= +# APP BASE +# ========================= x-app-service: &app-service image: *app-image restart: unless-stopped @@ -67,7 +76,11 @@ x-app-service: &app-service - public stop_grace_period: 30s +# ========================= +# SERVICES +# ========================= services: + # ========================= # BLUE # ========================= @@ -75,10 +88,6 @@ services: <<: *app-service container_name: bemeal-api-blue healthcheck: *app-healthcheck - networks: - public: - aliases: - - bemeal-app # ========================= # GREEN @@ -87,10 +96,6 @@ services: <<: *app-service container_name: bemeal-api-green healthcheck: *app-healthcheck - networks: - public: - aliases: - - bemeal-app # ========================= # DATABASE @@ -114,6 +119,9 @@ services: networks: - internal + # ========================= + # REDIS + # ========================= redis: image: redis:alpine container_name: bemeal-redis @@ -123,6 +131,9 @@ services: networks: - internal + # ========================= + # HORIZON + # ========================= horizon: image: *app-image container_name: bemeal-horizon @@ -134,10 +145,13 @@ services: condition: service_started environment: *app-environment volumes: *app-volumes - command: "php artisan horizon" + command: php artisan horizon networks: - internal + # ========================= + # SCHEDULER + # ========================= scheduler: image: *app-image container_name: bemeal-scheduler @@ -149,19 +163,25 @@ services: condition: service_started environment: *app-environment volumes: *app-volumes - command: "php artisan schedule:work" + command: php artisan schedule:work networks: - internal + # ========================= + # ADMINER + # ========================= adminer: image: adminer:latest container_name: bemeal-adminer restart: unless-stopped environment: - ADMINER_DEFAULT_SERVER: pgsql + ADMINER_DEFAULT_SERVER: bemeal-pgsql networks: - internal + # ========================= + # MEILISEARCH + # ========================= meilisearch: image: getmeili/meilisearch:latest container_name: bemeal-meilisearch @@ -181,12 +201,16 @@ volumes: pgsql-data: external: true name: bemeal_pgsql_data + redis-data: name: bemeal_redis_data + storage-data: name: bemeal_storage_data + storage-public-data: name: bemeal_storage_public_data + meilisearch-data: name: bemeal_meilisearch_data @@ -195,6 +219,7 @@ volumes: # ========================= networks: internal: + name: bemeal_internal driver: bridge public: