feat: docker docmpose
This commit is contained in:
parent
dadd4e04b1
commit
e984ec04b5
|
|
@ -63,7 +63,7 @@ jobs:
|
||||||
# DEPLOY BLUE / GREEN (CADDY GÈRE LE ROUTING)
|
# DEPLOY BLUE / GREEN (CADDY GÈRE LE ROUTING)
|
||||||
# =========================
|
# =========================
|
||||||
deploy:
|
deploy:
|
||||||
name: Deploy Blue/Green
|
name: Deploy Blue/Green (Caddy routing)
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
|
@ -78,98 +78,66 @@ jobs:
|
||||||
username: ${{ secrets.SSH_USER }}
|
username: ${{ secrets.SSH_USER }}
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
source: docker-compose.prod.yml
|
source: docker-compose.prod.yml
|
||||||
target: ${{ secrets.DEPLOY_PATH || '/opt/bemeal' }}
|
target: /opt/bemeal
|
||||||
|
|
||||||
- name: Deploy via SSH
|
- name: Deploy via SSH
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
env:
|
env:
|
||||||
IMAGE_TAG: ${{ github.sha }}
|
IMAGE_TAG: ${{ github.sha }}
|
||||||
REGISTRY: ${{ env.REGISTRY }}
|
REGISTRY: gitea.leonmorival.com
|
||||||
IMAGE_NAME: ${{ env.IMAGE_NAME }}
|
IMAGE_NAME: daily_meal/bemeal-api
|
||||||
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
|
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.SSH_HOST }}
|
host: ${{ secrets.SSH_HOST }}
|
||||||
username: ${{ secrets.SSH_USER }}
|
username: ${{ secrets.SSH_USER }}
|
||||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
envs: REGISTRY,IMAGE_NAME,IMAGE_TAG,DEPLOY_PATH
|
envs: IMAGE_TAG,REGISTRY,IMAGE_NAME
|
||||||
script: |
|
script: |
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
APP_DIR="${DEPLOY_PATH:-/opt/bemeal}"
|
cd /opt/bemeal
|
||||||
COMPOSE_FILE="docker-compose.prod.yml"
|
|
||||||
|
|
||||||
cd "$APP_DIR"
|
|
||||||
|
|
||||||
COMPOSE="docker compose --env-file .env.prod -f $COMPOSE_FILE"
|
|
||||||
|
|
||||||
|
COMPOSE="docker compose --env-file .env.prod -f docker-compose.prod.yml"
|
||||||
IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG"
|
IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG"
|
||||||
|
|
||||||
echo "Pull image: $IMAGE"
|
|
||||||
docker pull "$IMAGE"
|
docker pull "$IMAGE"
|
||||||
|
|
||||||
echo "Start shared services"
|
echo "Deploy shared infra"
|
||||||
$COMPOSE up -d pgsql redis meilisearch
|
$COMPOSE up -d pgsql redis meilisearch
|
||||||
|
|
||||||
# =========================
|
# detect active
|
||||||
# BLUE / GREEN DETECTION
|
|
||||||
# =========================
|
|
||||||
ACTIVE=""
|
ACTIVE=""
|
||||||
|
if docker ps | grep -q bemeal-api-blue && \
|
||||||
if docker ps --format '{{.Names}}' | grep -q "bemeal-api-blue"; then
|
[ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-blue 2>/dev/null || true)" = "healthy" ]; then
|
||||||
if [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-blue 2>/dev/null || true)" = "healthy" ]; then
|
ACTIVE=blue
|
||||||
ACTIVE="blue"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if docker ps --format '{{.Names}}' | grep -q "bemeal-api-green"; then
|
if docker ps | grep -q bemeal-api-green && \
|
||||||
if [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-green 2>/dev/null || true)" = "healthy" ]; then
|
[ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-green 2>/dev/null || true)" = "healthy" ]; then
|
||||||
ACTIVE="green"
|
ACTIVE=green
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ACTIVE" = "blue" ]; then
|
if [ "$ACTIVE" = "blue" ]; then
|
||||||
TARGET="green"
|
TARGET=green
|
||||||
else
|
else
|
||||||
TARGET="blue"
|
TARGET=blue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TARGET_SERVICE="bemeal-api-$TARGET"
|
TARGET_SERVICE="bemeal-api-$TARGET"
|
||||||
|
|
||||||
echo "Deploying to $TARGET_SERVICE"
|
echo "Deploy $TARGET_SERVICE"
|
||||||
|
|
||||||
IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" \
|
IMAGE="$IMAGE" $COMPOSE up -d --no-deps --force-recreate "$TARGET_SERVICE"
|
||||||
$COMPOSE up -d --force-recreate --no-deps "$TARGET_SERVICE"
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# HEALTHCHECK
|
|
||||||
# =========================
|
|
||||||
echo "Waiting for healthcheck..."
|
|
||||||
|
|
||||||
|
# wait health
|
||||||
for i in $(seq 1 60); do
|
for i in $(seq 1 60); do
|
||||||
STATUS="$(docker inspect -f '{{.State.Health.Status}}' "$TARGET_SERVICE" 2>/dev/null || true)"
|
STATUS=$(docker inspect -f '{{.State.Health.Status}}' "$TARGET_SERVICE" || true)
|
||||||
|
[ "$STATUS" = "healthy" ] && break
|
||||||
if [ "$STATUS" = "healthy" ]; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$STATUS" != "healthy" ]; then
|
[ "$STATUS" != "healthy" ] && exit 1
|
||||||
echo "Healthcheck failed"
|
|
||||||
docker logs --tail=200 "$TARGET_SERVICE" || true
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
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"
|
docker exec caddy caddy reload
|
||||||
|
|
||||||
# =========================
|
|
||||||
# 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
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
name: bemeal
|
name: bemeal
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# IMAGE
|
||||||
|
# =========================
|
||||||
x-app-image: &app-image gitea.leonmorival.com/daily_meal/bemeal-api:${IMAGE_TAG:-latest}
|
x-app-image: &app-image gitea.leonmorival.com/daily_meal/bemeal-api:${IMAGE_TAG:-latest}
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# ENV
|
||||||
|
# =========================
|
||||||
x-app-environment: &app-environment
|
x-app-environment: &app-environment
|
||||||
APP_LOCALE: "fr"
|
APP_LOCALE: "fr"
|
||||||
APP_ENV: "production"
|
APP_ENV: "production"
|
||||||
|
|
@ -14,33 +20,30 @@ x-app-environment: &app-environment
|
||||||
FILESYSTEM_DISK: "public"
|
FILESYSTEM_DISK: "public"
|
||||||
|
|
||||||
DB_CONNECTION: "pgsql"
|
DB_CONNECTION: "pgsql"
|
||||||
DB_HOST: "pgsql"
|
DB_HOST: "bemeal-pgsql"
|
||||||
DB_PORT: "5432"
|
DB_PORT: "5432"
|
||||||
DB_DATABASE: "${DB_DATABASE:?DB_DATABASE is required}"
|
DB_DATABASE: "${DB_DATABASE:?DB_DATABASE is required}"
|
||||||
DB_USERNAME: "${DB_USERNAME:?DB_USERNAME is required}"
|
DB_USERNAME: "${DB_USERNAME:?DB_USERNAME is required}"
|
||||||
DB_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}"
|
DB_PASSWORD: "${DB_PASSWORD:?DB_PASSWORD is required}"
|
||||||
|
|
||||||
GEMINI_API_KEY: "${GEMINI_API_KEY:?GEMINI_API_KEY is required}"
|
REDIS_HOST: "bemeal-redis"
|
||||||
|
|
||||||
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_CLIENT: "predis"
|
REDIS_CLIENT: "predis"
|
||||||
QUEUE_CONNECTION: "redis"
|
QUEUE_CONNECTION: "redis"
|
||||||
|
|
||||||
SCOUT_DRIVER: "meilisearch"
|
SCOUT_DRIVER: "meilisearch"
|
||||||
MEILISEARCH_HOST: "http://meilisearch:7700"
|
MEILISEARCH_HOST: "http://bemeal-meilisearch:7700"
|
||||||
MEILISEARCH_KEY: "${MEILISEARCH_KEY:?MEILISEARCH_KEY is required}"
|
MEILISEARCH_KEY: "${MEILISEARCH_KEY:?MEILISEARCH_KEY is required}"
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# VOLUMES
|
||||||
|
# =========================
|
||||||
x-app-volumes: &app-volumes
|
x-app-volumes: &app-volumes
|
||||||
- storage-data:/var/www/html/storage/app
|
- storage-data:/var/www/html/storage/app
|
||||||
- storage-public-data:/var/www/html/storage/app/public
|
- storage-public-data:/var/www/html/storage/app/public
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# DEPENDS
|
||||||
|
# =========================
|
||||||
x-app-depends-on: &app-depends-on
|
x-app-depends-on: &app-depends-on
|
||||||
pgsql:
|
pgsql:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
@ -49,6 +52,9 @@ x-app-depends-on: &app-depends-on
|
||||||
meilisearch:
|
meilisearch:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# HEALTHCHECK
|
||||||
|
# =========================
|
||||||
x-app-healthcheck: &app-healthcheck
|
x-app-healthcheck: &app-healthcheck
|
||||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/api/health >/dev/null 2>&1"]
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/api/health >/dev/null 2>&1"]
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
@ -56,6 +62,9 @@ x-app-healthcheck: &app-healthcheck
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 12
|
retries: 12
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# APP BASE
|
||||||
|
# =========================
|
||||||
x-app-service: &app-service
|
x-app-service: &app-service
|
||||||
image: *app-image
|
image: *app-image
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
@ -67,7 +76,11 @@ x-app-service: &app-service
|
||||||
- public
|
- public
|
||||||
stop_grace_period: 30s
|
stop_grace_period: 30s
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# SERVICES
|
||||||
|
# =========================
|
||||||
services:
|
services:
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# BLUE
|
# BLUE
|
||||||
# =========================
|
# =========================
|
||||||
|
|
@ -75,10 +88,6 @@ services:
|
||||||
<<: *app-service
|
<<: *app-service
|
||||||
container_name: bemeal-api-blue
|
container_name: bemeal-api-blue
|
||||||
healthcheck: *app-healthcheck
|
healthcheck: *app-healthcheck
|
||||||
networks:
|
|
||||||
public:
|
|
||||||
aliases:
|
|
||||||
- bemeal-app
|
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# GREEN
|
# GREEN
|
||||||
|
|
@ -87,10 +96,6 @@ services:
|
||||||
<<: *app-service
|
<<: *app-service
|
||||||
container_name: bemeal-api-green
|
container_name: bemeal-api-green
|
||||||
healthcheck: *app-healthcheck
|
healthcheck: *app-healthcheck
|
||||||
networks:
|
|
||||||
public:
|
|
||||||
aliases:
|
|
||||||
- bemeal-app
|
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# DATABASE
|
# DATABASE
|
||||||
|
|
@ -114,6 +119,9 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# REDIS
|
||||||
|
# =========================
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine
|
image: redis:alpine
|
||||||
container_name: bemeal-redis
|
container_name: bemeal-redis
|
||||||
|
|
@ -123,6 +131,9 @@ services:
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# HORIZON
|
||||||
|
# =========================
|
||||||
horizon:
|
horizon:
|
||||||
image: *app-image
|
image: *app-image
|
||||||
container_name: bemeal-horizon
|
container_name: bemeal-horizon
|
||||||
|
|
@ -134,10 +145,13 @@ services:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
environment: *app-environment
|
environment: *app-environment
|
||||||
volumes: *app-volumes
|
volumes: *app-volumes
|
||||||
command: "php artisan horizon"
|
command: php artisan horizon
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# SCHEDULER
|
||||||
|
# =========================
|
||||||
scheduler:
|
scheduler:
|
||||||
image: *app-image
|
image: *app-image
|
||||||
container_name: bemeal-scheduler
|
container_name: bemeal-scheduler
|
||||||
|
|
@ -149,19 +163,25 @@ services:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
environment: *app-environment
|
environment: *app-environment
|
||||||
volumes: *app-volumes
|
volumes: *app-volumes
|
||||||
command: "php artisan schedule:work"
|
command: php artisan schedule:work
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# ADMINER
|
||||||
|
# =========================
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer:latest
|
image: adminer:latest
|
||||||
container_name: bemeal-adminer
|
container_name: bemeal-adminer
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
ADMINER_DEFAULT_SERVER: pgsql
|
ADMINER_DEFAULT_SERVER: bemeal-pgsql
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
|
|
||||||
|
# =========================
|
||||||
|
# MEILISEARCH
|
||||||
|
# =========================
|
||||||
meilisearch:
|
meilisearch:
|
||||||
image: getmeili/meilisearch:latest
|
image: getmeili/meilisearch:latest
|
||||||
container_name: bemeal-meilisearch
|
container_name: bemeal-meilisearch
|
||||||
|
|
@ -181,12 +201,16 @@ volumes:
|
||||||
pgsql-data:
|
pgsql-data:
|
||||||
external: true
|
external: true
|
||||||
name: bemeal_pgsql_data
|
name: bemeal_pgsql_data
|
||||||
|
|
||||||
redis-data:
|
redis-data:
|
||||||
name: bemeal_redis_data
|
name: bemeal_redis_data
|
||||||
|
|
||||||
storage-data:
|
storage-data:
|
||||||
name: bemeal_storage_data
|
name: bemeal_storage_data
|
||||||
|
|
||||||
storage-public-data:
|
storage-public-data:
|
||||||
name: bemeal_storage_public_data
|
name: bemeal_storage_public_data
|
||||||
|
|
||||||
meilisearch-data:
|
meilisearch-data:
|
||||||
name: bemeal_meilisearch_data
|
name: bemeal_meilisearch_data
|
||||||
|
|
||||||
|
|
@ -195,6 +219,7 @@ volumes:
|
||||||
# =========================
|
# =========================
|
||||||
networks:
|
networks:
|
||||||
internal:
|
internal:
|
||||||
|
name: bemeal_internal
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue