3 changed files with 222 additions and 0 deletions
@ -0,0 +1,15 @@ |
|||||||
|
# Copy to .env.prod, fill in, then: set -a && source .env.prod && set +a |
||||||
|
# Or use: export $(grep -v '^#' .env.prod | xargs) |
||||||
|
|
||||||
|
POSTGRES_HOST=postgres |
||||||
|
POSTGRES_DB=gc_index_relay_prod |
||||||
|
POSTGRES_USER=postgres |
||||||
|
POSTGRES_PASSWORD=change-me |
||||||
|
POSTGRES_RUNTIME_USER=gc_index_relay |
||||||
|
POSTGRES_RUNTIME_PASSWORD=change-me-runtime |
||||||
|
|
||||||
|
SECRET_KEY_BASE=generate-with-mix-phx-gen-secret |
||||||
|
PHX_HOST=gc-http-relay.imwald.eu |
||||||
|
|
||||||
|
# Optional: pin release image (default latest) |
||||||
|
# TAG=0.2.0 |
||||||
@ -0,0 +1,118 @@ |
|||||||
|
# Production stack: Apache (on the host) terminates TLS and proxies to 127.0.0.1:4000. |
||||||
|
# |
||||||
|
# Helper script: ./scripts/deploy_prod.sh --help |
||||||
|
# |
||||||
|
# --- Local: build and push --- |
||||||
|
# cp .env.prod.example .env.prod && edit secrets |
||||||
|
# export TAG=0.2.0 # optional; relay/migrator use :latest if unset |
||||||
|
# docker login |
||||||
|
# ./scripts/deploy_prod.sh build-push |
||||||
|
# |
||||||
|
# --- Remote: pull and run --- |
||||||
|
# ./scripts/deploy_prod.sh deploy |
||||||
|
# |
||||||
|
# Images (repository: silberengel/gc-http-relay): |
||||||
|
# :${TAG} — Phoenix release (relay + migrator) |
||||||
|
# :setup — one-shot DB user bootstrap (tag is literal "setup") |
||||||
|
|
||||||
|
services: |
||||||
|
postgres: |
||||||
|
image: docker.io/apache/age:release_PG17_1.6.0 |
||||||
|
restart: unless-stopped |
||||||
|
user: 1000:1000 |
||||||
|
volumes: |
||||||
|
- pgdata:/var/lib/postgresql/data |
||||||
|
environment: |
||||||
|
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env.prod} |
||||||
|
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env.prod} |
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env.prod} |
||||||
|
command: > |
||||||
|
postgres |
||||||
|
healthcheck: |
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] |
||||||
|
interval: 10s |
||||||
|
timeout: 5s |
||||||
|
retries: 5 |
||||||
|
deploy: |
||||||
|
resources: |
||||||
|
limits: |
||||||
|
cpus: "1.00" |
||||||
|
memory: 1G |
||||||
|
reservations: |
||||||
|
cpus: "0.50" |
||||||
|
memory: 512M |
||||||
|
networks: |
||||||
|
- internal |
||||||
|
|
||||||
|
setup: |
||||||
|
image: docker.io/silberengel/gc-http-relay:setup |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: ./docker/setup.Dockerfile |
||||||
|
command: ["/usr/local/bin/usersetup.sh"] |
||||||
|
restart: "no" |
||||||
|
depends_on: |
||||||
|
postgres: |
||||||
|
condition: service_healthy |
||||||
|
environment: |
||||||
|
POSTGRES_HOST: ${POSTGRES_HOST:-postgres} |
||||||
|
POSTGRES_USER: ${POSTGRES_USER:?set POSTGRES_USER in .env.prod} |
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env.prod} |
||||||
|
POSTGRES_DB: ${POSTGRES_DB:?set POSTGRES_DB in .env.prod} |
||||||
|
POSTGRES_RUNTIME_USER: ${POSTGRES_RUNTIME_USER:?set POSTGRES_RUNTIME_USER in .env.prod} |
||||||
|
POSTGRES_RUNTIME_PASSWORD: ${POSTGRES_RUNTIME_PASSWORD:?set POSTGRES_RUNTIME_PASSWORD in .env.prod} |
||||||
|
networks: |
||||||
|
- internal |
||||||
|
|
||||||
|
migrator: |
||||||
|
image: docker.io/silberengel/gc-http-relay:${TAG:-latest} |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: ./docker/server.Dockerfile |
||||||
|
command: ["/app/bin/migrate"] |
||||||
|
restart: "no" |
||||||
|
depends_on: |
||||||
|
postgres: |
||||||
|
condition: service_healthy |
||||||
|
setup: |
||||||
|
condition: service_completed_successfully |
||||||
|
environment: |
||||||
|
DATABASE_URL: "ecto://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}/${POSTGRES_DB}" |
||||||
|
SECRET_KEY_BASE: ${SECRET_KEY_BASE:?set SECRET_KEY_BASE in .env.prod} |
||||||
|
networks: |
||||||
|
- internal |
||||||
|
|
||||||
|
relay: |
||||||
|
image: docker.io/silberengel/gc-http-relay:${TAG:-latest} |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: ./docker/server.Dockerfile |
||||||
|
command: ["/app/bin/server"] |
||||||
|
restart: unless-stopped |
||||||
|
deploy: |
||||||
|
resources: |
||||||
|
limits: |
||||||
|
cpus: "1.00" |
||||||
|
memory: 1G |
||||||
|
reservations: |
||||||
|
cpus: "0.50" |
||||||
|
memory: 512M |
||||||
|
depends_on: |
||||||
|
postgres: |
||||||
|
condition: service_healthy |
||||||
|
migrator: |
||||||
|
condition: service_completed_successfully |
||||||
|
ports: |
||||||
|
- "127.0.0.1:4000:4000" |
||||||
|
environment: |
||||||
|
DATABASE_URL: "ecto://${POSTGRES_RUNTIME_USER}:${POSTGRES_RUNTIME_PASSWORD}@${POSTGRES_HOST:-postgres}/${POSTGRES_DB}" |
||||||
|
SECRET_KEY_BASE: ${SECRET_KEY_BASE:?set SECRET_KEY_BASE in .env.prod} |
||||||
|
PHX_HOST: ${PHX_HOST:?set PHX_HOST in .env.prod (public hostname, no scheme)} |
||||||
|
networks: |
||||||
|
- internal |
||||||
|
|
||||||
|
networks: |
||||||
|
internal: |
||||||
|
|
||||||
|
volumes: |
||||||
|
pgdata: |
||||||
@ -0,0 +1,89 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
# Production Docker Compose helper — run ./scripts/deploy_prod.sh --help |
||||||
|
|
||||||
|
set -euo pipefail |
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" |
||||||
|
cd "$ROOT" |
||||||
|
|
||||||
|
COMPOSE="${COMPOSE:-docker compose}" |
||||||
|
COMPOSE_FILE="${COMPOSE_FILE:-compose.prod.yml}" |
||||||
|
ENV_FILE="${ENV_FILE:-.env.prod}" |
||||||
|
|
||||||
|
APP_IMAGES=(setup relay migrator) |
||||||
|
|
||||||
|
usage() { |
||||||
|
cat <<'EOF' |
||||||
|
Production Docker Compose helper (compose.prod.yml). |
||||||
|
|
||||||
|
./scripts/deploy_prod.sh build build app images (setup, relay, migrator) |
||||||
|
./scripts/deploy_prod.sh push push those images (run docker login first) |
||||||
|
./scripts/deploy_prod.sh build-push build then push |
||||||
|
./scripts/deploy_prod.sh pull pull images |
||||||
|
./scripts/deploy_prod.sh up start stack (detached) |
||||||
|
./scripts/deploy_prod.sh deploy pull then up -d (typical on server) |
||||||
|
./scripts/deploy_prod.sh down stop stack |
||||||
|
./scripts/deploy_prod.sh ps docker compose ps |
||||||
|
./scripts/deploy_prod.sh logs [svc] follow logs (default: all services) |
||||||
|
|
||||||
|
Env: ENV_FILE (default .env.prod), COMPOSE_FILE, TAG, COMPOSE (default "docker compose") |
||||||
|
EOF |
||||||
|
exit "${1:-0}" |
||||||
|
} |
||||||
|
|
||||||
|
require_env_file() { |
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then |
||||||
|
echo "error: missing env file: $ENV_FILE" >&2 |
||||||
|
echo " cp .env.prod.example .env.prod && edit, or set ENV_FILE=..." >&2 |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
compose() { |
||||||
|
require_env_file |
||||||
|
$COMPOSE --env-file "$ENV_FILE" -f "$COMPOSE_FILE" "$@" |
||||||
|
} |
||||||
|
|
||||||
|
cmd="${1:-}" |
||||||
|
[[ -z "$cmd" ]] && usage 1 |
||||||
|
[[ "$cmd" == "-h" || "$cmd" == "--help" ]] && usage 0 |
||||||
|
shift || true |
||||||
|
|
||||||
|
case "$cmd" in |
||||||
|
help) |
||||||
|
usage 0 |
||||||
|
;; |
||||||
|
build) |
||||||
|
compose build "${APP_IMAGES[@]}" |
||||||
|
;; |
||||||
|
push) |
||||||
|
compose push "${APP_IMAGES[@]}" |
||||||
|
;; |
||||||
|
build-push) |
||||||
|
compose build "${APP_IMAGES[@]}" |
||||||
|
compose push "${APP_IMAGES[@]}" |
||||||
|
;; |
||||||
|
pull) |
||||||
|
compose pull |
||||||
|
;; |
||||||
|
up) |
||||||
|
compose up -d |
||||||
|
;; |
||||||
|
deploy) |
||||||
|
compose pull |
||||||
|
compose up -d |
||||||
|
;; |
||||||
|
down) |
||||||
|
compose down |
||||||
|
;; |
||||||
|
ps) |
||||||
|
compose ps "$@" |
||||||
|
;; |
||||||
|
logs) |
||||||
|
compose logs -f "$@" |
||||||
|
;; |
||||||
|
*) |
||||||
|
echo "error: unknown command: $cmd" >&2 |
||||||
|
usage 1 |
||||||
|
;; |
||||||
|
esac |
||||||
Loading…
Reference in new issue