fix prod ddeployment
This commit is contained in:
@@ -1,26 +1,26 @@
|
||||
# ── Traefik domains — used in docker-compose labels ──────────────────────────
|
||||
ACME_EMAIL=rentaldrivego@gmail.com
|
||||
API_DOMAIN=api.rentaldrivego.ma
|
||||
PUBLIC_SITE_DOMAIN=rentaldrivego.ma
|
||||
PGMANAGE_DOMAIN=pgmanage.rentaldrivego.ma
|
||||
PORTAINER_DOMAIN=portainer.rentaldrivego.ma
|
||||
ACME_EMAIL=ops@example.com
|
||||
API_DOMAIN=api.example.com
|
||||
PUBLIC_SITE_DOMAIN=example.com
|
||||
PGMANAGE_DOMAIN=pgmanage.example.com
|
||||
PORTAINER_DOMAIN=portainer.example.com
|
||||
|
||||
# ── Optional prebuilt image source ────────────────────────────────────────────
|
||||
# Used for pull-based deployments. CI injects APP_IMAGE and APP_VERSION
|
||||
# automatically during registry-backed deploys, so these can stay commented out.
|
||||
# APP_IMAGE=gitlab.rentaldrivego.ma:5050/rental_car/car_management_system
|
||||
# APP_IMAGE=registry.example.com/rentaldrivego/car_management_system
|
||||
# APP_VERSION=latest
|
||||
|
||||
# ── Database ──────────────────────────────────────────────────────────────────
|
||||
# Full connection URL (used when DATABASE_URL_FROM_POSTGRES=false)
|
||||
DATABASE_URL=postgresql://postgres:24DY&1u5FLCkNMeiO_p@postgres:5432/rentaldrivego
|
||||
DATABASE_URL=postgresql://postgres:change-me@postgres:5432/rentaldrivego
|
||||
# Build the URL from individual vars (set to true to use POSTGRES_* vars below)
|
||||
DATABASE_URL_FROM_POSTGRES=true
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=rentaldrivego
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=24DY&1u5FLCkNMeiO_p
|
||||
POSTGRES_PASSWORD=change-me
|
||||
|
||||
# ── Cache ─────────────────────────────────────────────────────────────────────
|
||||
REDIS_URL=redis://redis:6379
|
||||
@@ -30,44 +30,44 @@ API_PORT=4000
|
||||
# SSR server-side calls go via the internal Docker network
|
||||
API_INTERNAL_URL=http://api:4000/api/v1
|
||||
# Public-facing API URL visible to browsers — MUST be your real domain, not localhost
|
||||
API_URL=https://api.rentaldrivego.ma
|
||||
API_URL=https://api.example.com
|
||||
# Baked into Next.js bundles at build time — MUST be set before running `npm run build`
|
||||
NEXT_PUBLIC_API_URL=https://api.rentaldrivego.ma/api/v1
|
||||
NEXT_PUBLIC_API_URL=https://api.example.com/api/v1
|
||||
|
||||
# ── Frontend public URLs ──────────────────────────────────────────────────────
|
||||
# Marketplace sits at the domain root (Traefik routes /dashboard and /admin to their apps)
|
||||
NEXT_PUBLIC_MARKETING_URL=https://rentaldrivego.ma
|
||||
NEXT_PUBLIC_MARKETPLACE_URL=https://rentaldrivego.ma
|
||||
NEXT_PUBLIC_DASHBOARD_URL=https://rentaldrivego.ma/dashboard
|
||||
NEXT_PUBLIC_ADMIN_URL=https://rentaldrivego.ma/admin
|
||||
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=rentaldrivego.ma
|
||||
DASHBOARD_URL=https://rentaldrivego.ma/dashboard
|
||||
ADMIN_URL=https://rentaldrivego.ma/admin
|
||||
NEXT_PUBLIC_MARKETING_URL=https://example.com
|
||||
NEXT_PUBLIC_MARKETPLACE_URL=https://example.com
|
||||
NEXT_PUBLIC_DASHBOARD_URL=https://example.com/dashboard
|
||||
NEXT_PUBLIC_ADMIN_URL=https://example.com/admin
|
||||
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=example.com
|
||||
DASHBOARD_URL=https://example.com/dashboard
|
||||
ADMIN_URL=https://example.com/admin
|
||||
|
||||
# ── CORS ──────────────────────────────────────────────────────────────────────
|
||||
# Comma-separated list of allowed browser origins. REQUIRED in production.
|
||||
CORS_ORIGINS=https://rentaldrivego.ma
|
||||
CORS_ORIGINS=https://example.com,https://www.example.com
|
||||
|
||||
# ── Auth ──────────────────────────────────────────────────────────────────────
|
||||
JWT_SECRET=PMPS5k0D7rUeJOk0NkhI5bRtoGjkUqjK
|
||||
JWT_SECRET=replace-with-a-long-random-secret
|
||||
JWT_EXPIRY=8h
|
||||
RENTER_JWT_EXPIRY=7d
|
||||
NODE_ENV=production
|
||||
|
||||
# ── Email ─────────────────────────────────────────────────────────────────────
|
||||
EMAIL_FROM=rentaldrivego@gmail.com
|
||||
EMAIL_FROM=noreply@example.com
|
||||
EMAIL_FROM_NAME=RentalDriveGo
|
||||
# Option A — Resend
|
||||
#RESEND_API_KEY=C8qPDuFwsv5l@KsGhL/V
|
||||
#RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx
|
||||
# Option B — SMTP (Gmail)
|
||||
MAIL_HOST=smtp.gmail.com
|
||||
MAIL_PORT=587
|
||||
MAIL_SCHEME=smtp
|
||||
MAIL_USERNAME=rentaldrivego@gmail.com
|
||||
MAIL_PASSWORD="xmhg ibqy muoc rntc"
|
||||
MAIL_FROM_ADDRESS=rentaldrivego@gmail.com
|
||||
MAIL_USERNAME=your-smtp-user@example.com
|
||||
MAIL_PASSWORD=replace-with-an-app-password
|
||||
MAIL_FROM_ADDRESS=noreply@example.com
|
||||
MAIL_FROM_NAME=RentalDriveGo
|
||||
MAIL_REPLY_TO_ADDRESS=rentaldrivego@gmail.com
|
||||
MAIL_REPLY_TO_ADDRESS=support@example.com
|
||||
MAIL_REPLY_TO_NAME=RentalDriveGo
|
||||
|
||||
# ── Firebase push notifications (optional) ────────────────────────────────────
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Traefik domains
|
||||
ACME_EMAIL=rentaldrivego@gmail.com
|
||||
API_DOMAIN=api.rentaldrivego.ma
|
||||
PUBLIC_SITE_DOMAIN=rentaldrivego.ma
|
||||
PGMANAGE_DOMAIN=pgmanage.rentaldrivego.ma
|
||||
PORTAINER_DOMAIN=portainer.rentaldrivego.ma
|
||||
ACME_EMAIL=ops@example.com
|
||||
API_DOMAIN=api.example.com
|
||||
PUBLIC_SITE_DOMAIN=example.com
|
||||
PGMANAGE_DOMAIN=pgmanage.example.com
|
||||
PORTAINER_DOMAIN=portainer.example.com
|
||||
|
||||
# Database
|
||||
DATABASE_URL_FROM_POSTGRES=true
|
||||
@@ -11,8 +11,8 @@ POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=rentaldrivego
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=24DY&1u5FLCkNMeiO_p
|
||||
DATABASE_URL=postgresql://postgres:24DY&1u5FLCkNMeiO_p@postgres:5432/rentaldrivego
|
||||
POSTGRES_PASSWORD=change-me
|
||||
DATABASE_URL=postgresql://postgres:change-me@postgres:5432/rentaldrivego
|
||||
|
||||
# Cache
|
||||
REDIS_URL=redis://redis:6379
|
||||
@@ -20,23 +20,23 @@ REDIS_URL=redis://redis:6379
|
||||
# API
|
||||
API_PORT=4000
|
||||
API_INTERNAL_URL=http://api:4000/api/v1
|
||||
API_URL=https://api.rentaldrivego.ma
|
||||
NEXT_PUBLIC_API_URL=https://api.rentaldrivego.ma/api/v1
|
||||
API_URL=https://api.example.com
|
||||
NEXT_PUBLIC_API_URL=https://api.example.com/api/v1
|
||||
|
||||
# Frontend public URLs
|
||||
NEXT_PUBLIC_MARKETING_URL=https://rentaldrivego.ma
|
||||
NEXT_PUBLIC_MARKETPLACE_URL=https://rentaldrivego.ma
|
||||
NEXT_PUBLIC_DASHBOARD_URL=https://rentaldrivego.ma/dashboard
|
||||
NEXT_PUBLIC_ADMIN_URL=https://rentaldrivego.ma/admin
|
||||
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=rentaldrivego.ma
|
||||
DASHBOARD_URL=https://rentaldrivego.ma/dashboard
|
||||
ADMIN_URL=https://rentaldrivego.ma/admin
|
||||
NEXT_PUBLIC_MARKETING_URL=https://example.com
|
||||
NEXT_PUBLIC_MARKETPLACE_URL=https://example.com
|
||||
NEXT_PUBLIC_DASHBOARD_URL=https://example.com/dashboard
|
||||
NEXT_PUBLIC_ADMIN_URL=https://example.com/admin
|
||||
NEXT_PUBLIC_PUBLIC_SITE_DOMAIN=example.com
|
||||
DASHBOARD_URL=https://example.com/dashboard
|
||||
ADMIN_URL=https://example.com/admin
|
||||
|
||||
# CORS
|
||||
CORS_ORIGINS=https://rentaldrivego.ma,https://www.rentaldrivego.ma
|
||||
CORS_ORIGINS=https://example.com,https://www.example.com
|
||||
|
||||
# Auth
|
||||
JWT_SECRET=PMPS5k0D7rUeJOk0NkhI5bRtoGjkUqjK
|
||||
JWT_SECRET=replace-with-a-long-random-secret
|
||||
JWT_EXPIRY=8h
|
||||
RENTER_JWT_EXPIRY=7d
|
||||
NODE_ENV=production
|
||||
@@ -46,17 +46,17 @@ FILE_STORAGE_ROOT=/var/lib/rentaldrivego/storage
|
||||
|
||||
# PgManage
|
||||
PGMANAGE_DEFAULT_USERNAME=admin
|
||||
PGMANAGE_DEFAULT_PASSWORD=24DY&1u5FLCkNMeiO_p
|
||||
PGMANAGE_DEFAULT_PASSWORD=change-me
|
||||
|
||||
# Email
|
||||
EMAIL_FROM=rentaldrivego@gmail.com
|
||||
EMAIL_FROM=noreply@example.com
|
||||
EMAIL_FROM_NAME=RentalDriveGo
|
||||
MAIL_HOST=smtp.gmail.com
|
||||
MAIL_PORT=587
|
||||
MAIL_SCHEME=smtp
|
||||
MAIL_USERNAME=rentaldrivego@gmail.com
|
||||
MAIL_PASSWORD=xmhg ibqy muoc rntc
|
||||
MAIL_FROM_ADDRESS=rentaldrivego@gmail.com
|
||||
MAIL_USERNAME=your-smtp-user@example.com
|
||||
MAIL_PASSWORD=replace-with-an-app-password
|
||||
MAIL_FROM_ADDRESS=noreply@example.com
|
||||
MAIL_FROM_NAME=RentalDriveGo
|
||||
MAIL_REPLY_TO_ADDRESS=rentaldrivego@gmail.com
|
||||
MAIL_REPLY_TO_ADDRESS=support@example.com
|
||||
MAIL_REPLY_TO_NAME=RentalDriveGo
|
||||
|
||||
+19
-26
@@ -5,6 +5,7 @@ stages:
|
||||
|
||||
variables:
|
||||
DOCKERFILE_PATH: Dockerfile.production
|
||||
DEPLOY_ROOT: /opt/rentaldrivego
|
||||
# Required: disable TLS so the docker client can reach the dind daemon
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
@@ -141,35 +142,27 @@ deploy_to_vps:
|
||||
script:
|
||||
- echo "--- Deploying $DOCKER_IMAGE to VPS ---"
|
||||
- |
|
||||
REGISTRY_PASSWORD_B64="$(printf '%s' "$REGISTRY_PASSWORD" | base64 | tr -d '\n')"
|
||||
|
||||
echo "-- Syncing deployment assets --"
|
||||
ssh $VPS_USER@$VPS_IP "
|
||||
set -e
|
||||
cd /opt/rentaldrivego
|
||||
mkdir -p '$DEPLOY_ROOT/scripts' '$DEPLOY_ROOT/docker/pgmanage'
|
||||
"
|
||||
scp docker-compose.production.yml docker-compose.portainer.production.yml traefik.yaml $VPS_USER@$VPS_IP:$DEPLOY_ROOT/
|
||||
scp scripts/docker-prod-common.sh scripts/docker-prod-deploy.sh $VPS_USER@$VPS_IP:$DEPLOY_ROOT/scripts/
|
||||
scp docker/pgmanage/override.py $VPS_USER@$VPS_IP:$DEPLOY_ROOT/docker/pgmanage/
|
||||
|
||||
echo '-- Pulling latest code --'
|
||||
git pull
|
||||
|
||||
echo '-- Logging into registry --'
|
||||
test -n '$REGISTRY_HOST' || { echo 'Registry host is not set.'; exit 1; }
|
||||
test -n '$REGISTRY_USER' || { echo 'Registry user is not set.'; exit 1; }
|
||||
test -n '$REGISTRY_PASSWORD' || { echo 'Registry password is not set.'; exit 1; }
|
||||
echo '$REGISTRY_PASSWORD' | docker login '$REGISTRY_HOST' -u '$REGISTRY_USER' --password-stdin
|
||||
|
||||
echo '-- Pulling pre-built image --'
|
||||
docker pull $DOCKER_IMAGE
|
||||
|
||||
echo '-- Ensuring uploads volume exists --'
|
||||
docker volume create rentaldrivego-prod_api_uploads >/dev/null
|
||||
|
||||
echo '-- Restarting services --'
|
||||
APP_IMAGE=$IMAGE_REPOSITORY \
|
||||
APP_VERSION=$CI_COMMIT_SHORT_SHA \
|
||||
docker compose -p rentaldrivego-prod \
|
||||
--env-file .env.docker.production \
|
||||
-f docker-compose.production.yml \
|
||||
up -d --no-build
|
||||
|
||||
echo '-- Pruning old images --'
|
||||
docker image prune -f
|
||||
echo "-- Running deploy script on VPS --"
|
||||
ssh $VPS_USER@$VPS_IP "
|
||||
set -e
|
||||
cd '$DEPLOY_ROOT'
|
||||
APP_IMAGE='$IMAGE_REPOSITORY' \
|
||||
APP_VERSION='$CI_COMMIT_SHORT_SHA' \
|
||||
REGISTRY_HOST='$REGISTRY_HOST' \
|
||||
REGISTRY_USER='$REGISTRY_USER' \
|
||||
REGISTRY_PASSWORD=\$(printf '%s' '$REGISTRY_PASSWORD_B64' | base64 -d) \
|
||||
bash scripts/docker-prod-deploy.sh
|
||||
"
|
||||
rules:
|
||||
# Deploy only when both registry access and VPS credentials are available.
|
||||
|
||||
@@ -26,6 +26,32 @@ services:
|
||||
volumes:
|
||||
- redis_prod_data:/data
|
||||
|
||||
migrate:
|
||||
image: ${APP_IMAGE:-rentaldrivego-prod-app}:${APP_VERSION:-latest}
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.production
|
||||
args:
|
||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
||||
NEXT_PUBLIC_MARKETPLACE_URL: ${NEXT_PUBLIC_MARKETPLACE_URL}
|
||||
NEXT_PUBLIC_DASHBOARD_URL: ${NEXT_PUBLIC_DASHBOARD_URL}
|
||||
NEXT_PUBLIC_ADMIN_URL: ${NEXT_PUBLIC_ADMIN_URL}
|
||||
command: ["npm", "run", "db:deploy"]
|
||||
networks:
|
||||
- internal
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env.docker.production
|
||||
environment:
|
||||
DATABASE_URL_FROM_POSTGRES: ${DATABASE_URL_FROM_POSTGRES:-true}
|
||||
POSTGRES_HOST: ${POSTGRES_HOST:-postgres}
|
||||
POSTGRES_PORT: ${POSTGRES_PORT:-5432}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-rentaldrivego}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
restart: "no"
|
||||
|
||||
api:
|
||||
image: ${APP_IMAGE:-rentaldrivego-prod-app}:${APP_VERSION:-latest}
|
||||
build:
|
||||
@@ -37,12 +63,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
npm run db:deploy &&
|
||||
npm run start --workspace @rentaldrivego/api
|
||||
command: ["npm", "run", "start", "--workspace", "@rentaldrivego/api"]
|
||||
networks:
|
||||
- internal
|
||||
- traefik-proxy
|
||||
@@ -92,7 +113,8 @@ services:
|
||||
- internal
|
||||
- traefik-proxy
|
||||
depends_on:
|
||||
- api
|
||||
api:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env.docker.production
|
||||
restart: unless-stopped
|
||||
@@ -127,7 +149,8 @@ services:
|
||||
- internal
|
||||
- traefik-proxy
|
||||
depends_on:
|
||||
- api
|
||||
api:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env.docker.production
|
||||
restart: unless-stopped
|
||||
@@ -162,7 +185,8 @@ services:
|
||||
- internal
|
||||
- traefik-proxy
|
||||
depends_on:
|
||||
- api
|
||||
api:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env.docker.production
|
||||
restart: unless-stopped
|
||||
|
||||
+29
-18
@@ -182,27 +182,27 @@ REGISTRY_USER=<deploy-user-or-token-name>
|
||||
REGISTRY_PASSWORD=<personal-access-token-or-deploy-token>
|
||||
```
|
||||
|
||||
The production compose file now reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The GitLab deploy job injects those values automatically. If you want to pull manually on the server, you can also set `APP_IMAGE` in `.env.docker.production`.
|
||||
The production compose file reads `APP_IMAGE` and `APP_VERSION` for pull-based deploys. The GitLab deploy job injects those values automatically and now syncs the deployment assets to the VPS before running the server-side deploy script. Production no longer depends on `git pull` during release.
|
||||
|
||||
The CI pipeline publishes and deploys from the GitLab default branch (`$CI_DEFAULT_BRANCH`). If you change your release branch, update the repository default branch in GitLab instead of hard-coding branch names in `.gitlab-ci.yml`.
|
||||
|
||||
If you use `docker:dind` on a self-hosted GitLab runner, the runner's Docker executor must be started with `privileged = true`. Without that, `docker:dind` often logs AppArmor or `/sys/kernel/security` mount errors and can become unreliable even when the job container still starts.
|
||||
|
||||
#### 5. Start Traefik
|
||||
#### 5. Bootstrap the server once
|
||||
|
||||
Traefik must be running before the app stack so it can wire up routes at startup.
|
||||
Traefik must be running before the app stack so it can wire up routes at startup. This bootstrap path is for the first server setup or for deliberate source-based rebuilds. Normal production releases should go through GitLab CI.
|
||||
|
||||
```bash
|
||||
docker compose -f traefik.yaml up -d
|
||||
bash scripts/docker-prod-up-traefik.sh
|
||||
```
|
||||
|
||||
#### 6. Build and start the app stack
|
||||
If you intentionally want to build from source on the server:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:up
|
||||
```
|
||||
|
||||
Or use the new helper scripts if you want to start one production container at a time:
|
||||
Or use the helper scripts if you want to start one production container at a time:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:start:traefik
|
||||
@@ -220,11 +220,22 @@ npm run docker:prod:start:api && npm run docker:prod:start:frontends
|
||||
```
|
||||
|
||||
Docker will:
|
||||
1. Build the monorepo image
|
||||
1. Build the monorepo image on the server
|
||||
2. Start all app services (`api`, `marketplace`, `dashboard`, `admin`, `pgmanage`)
|
||||
|
||||
Traefik automatically picks up the containers and provisions TLS certificates. Services are live at their `https://` URLs within ~30 seconds.
|
||||
|
||||
#### 6. Standard release flow
|
||||
|
||||
Normal production releases should use GitLab CI:
|
||||
|
||||
1. CI builds and pushes a single versioned image.
|
||||
2. CI copies the current deployment assets (`docker-compose.production.yml`, Traefik config, deploy scripts, pgManage override) to the VPS.
|
||||
3. CI runs `bash scripts/docker-prod-deploy.sh` on the VPS with `APP_IMAGE` and `APP_VERSION` pinned to the commit being released.
|
||||
4. The deploy script pulls the release image, starts Postgres and Redis, runs `npm run db:deploy` as a one-shot migration job, then starts the API and frontends and waits for their health checks.
|
||||
|
||||
This avoids the old drift where the VPS checkout changed independently through `git pull`, or where API startup silently mixed migrations with application boot.
|
||||
|
||||
#### 6a. Deploy Portainer
|
||||
|
||||
Portainer is deployed separately from the main app stack and reuses the shared `traefik-proxy` network managed by Traefik.
|
||||
@@ -245,25 +256,25 @@ The production helper scripts and the GitLab deploy job now explicitly create th
|
||||
|
||||
#### Updating after a code change
|
||||
|
||||
Pull the latest code and rebuild only the changed service:
|
||||
Push to the GitLab default branch and let the pipeline deploy the pinned image. That is the supported production release path.
|
||||
|
||||
If CI is unavailable and you need to redeploy the image already published to the registry from the server:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d --no-deps <service>
|
||||
# e.g. to redeploy only the API:
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d --no-deps api
|
||||
APP_IMAGE=registry.example.com/rentaldrivego/car_management_system \
|
||||
APP_VERSION=<gitlab-commit-sha> \
|
||||
REGISTRY_HOST=registry.example.com \
|
||||
REGISTRY_USER=<registry-user> \
|
||||
REGISTRY_PASSWORD=<registry-password> \
|
||||
bash scripts/docker-prod-deploy.sh
|
||||
```
|
||||
|
||||
To rebuild everything:
|
||||
|
||||
```bash
|
||||
npm run docker:prod:up
|
||||
```
|
||||
Only use `npm run docker:prod:up` when you intentionally want a source build on the server.
|
||||
|
||||
#### Apply database migrations without downtime
|
||||
|
||||
```bash
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml run --rm api npm run db:deploy
|
||||
docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml run --rm migrate
|
||||
```
|
||||
|
||||
#### Create the first production admin
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@
|
||||
"docker:prod:start:frontends": "bash scripts/docker-prod-up-frontends.sh",
|
||||
"docker:prod:start:pgmanage": "bash scripts/docker-prod-up-pgmanage.sh",
|
||||
"docker:prod:start:portainer": "bash scripts/docker-prod-up-portainer.sh",
|
||||
"docker:prod:up": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml up --build -d",
|
||||
"docker:prod:deploy": "bash scripts/docker-prod-deploy.sh",
|
||||
"docker:prod:up": "bash scripts/docker-prod-up-all.sh",
|
||||
"docker:prod:down": "docker compose -p rentaldrivego-prod --env-file .env.docker.production -f docker-compose.production.yml down",
|
||||
"docker:prod:backup": "bash scripts/docker-prod-backup.sh",
|
||||
"docker:prod:restore": "bash scripts/docker-prod-restore.sh",
|
||||
|
||||
@@ -50,9 +50,63 @@ traefik_compose() {
|
||||
start_prod_services() {
|
||||
ensure_env_file
|
||||
ensure_api_uploads_volume
|
||||
|
||||
local services=("$@")
|
||||
local needs_migrations=0
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
case "${service}" in
|
||||
api|marketplace|dashboard|admin)
|
||||
needs_migrations=1
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${needs_migrations} -eq 1 ]]; then
|
||||
prod_compose up --build -d postgres redis
|
||||
wait_for_healthy postgres 120
|
||||
prod_compose run --rm --build migrate
|
||||
fi
|
||||
|
||||
prod_compose up --build -d "$@"
|
||||
}
|
||||
|
||||
require_release_image() {
|
||||
if [[ -z "${APP_IMAGE:-}" || -z "${APP_VERSION:-}" ]]; then
|
||||
echo "APP_IMAGE and APP_VERSION must be set for pull-based production deploys." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
login_registry_if_configured() {
|
||||
local configured=0
|
||||
|
||||
[[ -n "${REGISTRY_HOST:-}" ]] && configured=$((configured + 1))
|
||||
[[ -n "${REGISTRY_USER:-}" ]] && configured=$((configured + 1))
|
||||
[[ -n "${REGISTRY_PASSWORD:-}" ]] && configured=$((configured + 1))
|
||||
|
||||
if [[ ${configured} -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ${configured} -ne 3 ]]; then
|
||||
echo "REGISTRY_HOST, REGISTRY_USER, and REGISTRY_PASSWORD must either all be set or all be unset." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "${REGISTRY_PASSWORD}" | docker login "${REGISTRY_HOST}" -u "${REGISTRY_USER}" --password-stdin
|
||||
}
|
||||
|
||||
pull_prod_release_images() {
|
||||
require_release_image
|
||||
prod_compose pull migrate api marketplace dashboard admin
|
||||
}
|
||||
|
||||
run_prod_migrations() {
|
||||
prod_compose run --rm migrate
|
||||
}
|
||||
|
||||
wait_for_healthy() {
|
||||
local service="$1"
|
||||
local timeout="${2:-120}"
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||
|
||||
echo "Deploying production release ${APP_IMAGE:-<missing>}:${APP_VERSION:-<missing>}"
|
||||
|
||||
ensure_env_file
|
||||
ensure_traefik_network
|
||||
ensure_api_uploads_volume
|
||||
require_release_image
|
||||
login_registry_if_configured
|
||||
|
||||
echo "Pulling release images"
|
||||
pull_prod_release_images
|
||||
|
||||
echo "Starting shared infrastructure"
|
||||
start_traefik
|
||||
prod_compose up -d postgres redis
|
||||
wait_for_healthy postgres 120
|
||||
|
||||
echo "Running database migrations"
|
||||
run_prod_migrations
|
||||
|
||||
echo "Starting application services"
|
||||
prod_compose up -d api marketplace dashboard admin pgmanage
|
||||
wait_for_healthy api 180
|
||||
wait_for_healthy marketplace 180
|
||||
wait_for_healthy dashboard 180
|
||||
wait_for_healthy admin 180
|
||||
|
||||
echo "Pruning old images"
|
||||
docker image prune -f >/dev/null
|
||||
|
||||
echo "Production release ${APP_IMAGE}:${APP_VERSION} is up and healthy."
|
||||
@@ -6,7 +6,7 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
source "${ROOT_DIR}/scripts/docker-prod-common.sh"
|
||||
|
||||
start_traefik
|
||||
start_prod_services
|
||||
start_prod_services postgres redis api marketplace dashboard admin pgmanage
|
||||
wait_for_healthy api
|
||||
wait_for_healthy marketplace
|
||||
wait_for_healthy dashboard
|
||||
|
||||
Reference in New Issue
Block a user