forked from baron/baron-sso
production 푸시 초안
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# =================================================================
|
||||
# Baron SSO 인스턴스 자동 생성 스크립트 (Full Infrastructure)
|
||||
# Baron SSO 인스턴스 자동 생성 스크립트 (전체 인프라 포함)
|
||||
# =================================================================
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
@@ -12,7 +13,8 @@ fi
|
||||
INSTANCE_NAME=$1
|
||||
PORT_PREFIX=$2
|
||||
BASE_DIR=$(cd $(dirname $0); pwd)
|
||||
TARGET_DIR="${BASE_DIR}/../instances/${INSTANCE_NAME}"
|
||||
REPO_ROOT=$(cd "${BASE_DIR}/.." && pwd)
|
||||
TARGET_DIR="${TARGET_DIR:-${BASE_DIR}/../instances/${INSTANCE_NAME}}"
|
||||
|
||||
echo "🚀 Creating instance: ${INSTANCE_NAME} (Port Prefix: ${PORT_PREFIX}xxx)"
|
||||
|
||||
@@ -32,6 +34,22 @@ mkdir -p "${TARGET_DIR}/orgfront"
|
||||
# 2. .env 생성 및 변수 로드
|
||||
sed "s/{{INSTANCE_NAME}}/${INSTANCE_NAME}/g; s/{{PORT_PREFIX}}/${PORT_PREFIX}/g" \
|
||||
"${BASE_DIR}/templates/.env.template" > "${TARGET_DIR}/.env"
|
||||
SOURCE_ROOT_ESCAPED=$(printf '%s\n' "$REPO_ROOT" | sed 's/[\/&]/\\&/g')
|
||||
sed -i "s/^SOURCE_ROOT=.*/SOURCE_ROOT=${SOURCE_ROOT_ESCAPED}/" "${TARGET_DIR}/.env"
|
||||
|
||||
# 생성된 compose 파일을 실행하기 전에 Traefik public network가 있어야 합니다.
|
||||
TRAEFIK_PUBLIC_NETWORK=$(grep "^TRAEFIK_PUBLIC_NETWORK=" "${TARGET_DIR}/.env" | cut -d'=' -f2 | tr -d '\r')
|
||||
TRAEFIK_PUBLIC_NETWORK=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}
|
||||
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
||||
if ! docker network inspect "$TRAEFIK_PUBLIC_NETWORK" >/dev/null 2>&1; then
|
||||
echo "Creating Docker network ${TRAEFIK_PUBLIC_NETWORK}..."
|
||||
docker network create "$TRAEFIK_PUBLIC_NETWORK"
|
||||
else
|
||||
echo "Docker network ${TRAEFIK_PUBLIC_NETWORK} already exists."
|
||||
fi
|
||||
else
|
||||
echo "⚠️ docker command is unavailable or not accessible; skipping Traefik public network check."
|
||||
fi
|
||||
|
||||
# 포트 계산 (단순 치환)
|
||||
BACKEND_PORT="${PORT_PREFIX}000"
|
||||
@@ -44,21 +62,21 @@ ORGFRONT_DOMAIN="${INSTANCE_NAME}-org.${DOMAIN_SUFFIX}"
|
||||
# 3. Docker Compose & Config 복사 및 치환
|
||||
cp "${BASE_DIR}/templates/docker-compose.yaml" "${TARGET_DIR}/"
|
||||
|
||||
# Gateway & UserFront Nginx
|
||||
# Gateway 및 UserFront Nginx
|
||||
sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" "${BASE_DIR}/templates/gateway/nginx.conf" > "${TARGET_DIR}/gateway/nginx.conf"
|
||||
sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" "${BASE_DIR}/templates/userfront/nginx.conf" > "${TARGET_DIR}/userfront/nginx.conf"
|
||||
|
||||
# Oathkeeper Rules template
|
||||
# Oathkeeper 규칙 템플릿
|
||||
sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" "${BASE_DIR}/templates/ory/oathkeeper/rules.json" > "${TARGET_DIR}/ory/templates/oathkeeper/rules.json"
|
||||
cp "${TARGET_DIR}/ory/templates/oathkeeper/rules.json" "${TARGET_DIR}/ory/templates/oathkeeper/rules.stage.json"
|
||||
cp "${TARGET_DIR}/ory/templates/oathkeeper/rules.json" "${TARGET_DIR}/ory/templates/oathkeeper/rules.prod.json"
|
||||
cp "${TARGET_DIR}/ory/templates/oathkeeper/rules.json" "${TARGET_DIR}/ory/templates/oathkeeper/rules.active.json"
|
||||
|
||||
# Kratos Config template
|
||||
# Kratos 설정 템플릿
|
||||
sed "s/{{BACKEND_PORT}}/${BACKEND_PORT}/g; s/{{USERFRONT_PORT}}/${USERFRONT_PORT}/g" \
|
||||
"${BASE_DIR}/templates/ory/kratos/kratos.yml.template" > "${TARGET_DIR}/ory/templates/kratos/kratos.yml.template"
|
||||
|
||||
# Vite Configs
|
||||
# Vite 설정
|
||||
sed "s/{{ADMINFRONT_DOMAIN}}/${ADMINFRONT_DOMAIN}/g; s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" \
|
||||
"${BASE_DIR}/templates/adminfront/vite.config.ts" > "${TARGET_DIR}/adminfront/vite.config.ts"
|
||||
sed "s/{{DEVFRONT_DOMAIN}}/${DEVFRONT_DOMAIN}/g; s/{{BACKEND_PORT}}/${BACKEND_PORT}/g" \
|
||||
@@ -88,7 +106,7 @@ ORY_CONFIG_OUTPUT_DIR="${TARGET_DIR}/config/.generated/ory" \
|
||||
bash "${BASE_DIR}/../scripts/render_ory_config.sh"
|
||||
|
||||
# 6. 마무리
|
||||
chmod +x "${TARGET_DIR}/.env"
|
||||
chmod 600 "${TARGET_DIR}/.env"
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
echo "✅ Success! ALL files (Infra/Ory/Apps/FrontConfigs) are ready."
|
||||
|
||||
@@ -43,12 +43,64 @@ cd instances/test3
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
프로덕션처럼 고정 경로에 바로 생성해야 하면 `TARGET_DIR`를 지정합니다.
|
||||
|
||||
```bash
|
||||
cd deploy
|
||||
TARGET_DIR=/home/user/prod.baron-sso ./create-instance.sh prod 30
|
||||
cd /home/user/prod.baron-sso
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## 4. 도메인 및 URL 규칙
|
||||
스크립트 실행 시 인스턴스 이름에 따라 다음 도메인이 자동으로 설정 파일에 주입됩니다. (`DOMAIN_SUFFIX`는 `.env`에서 수정 가능)
|
||||
- **SSO/UserFront**: `https://[이름]-sso.hmac.kr`
|
||||
- **AdminFront**: `https://[이름]-admin.hmac.kr`
|
||||
- **DevFront**: `https://[이름]-dev.hmac.kr`
|
||||
|
||||
프로덕션 도메인이 `app.brsw.kr`처럼 인스턴스 기본 규칙과 다르면 생성 후 `.env`에서 다음 값을 운영 도메인으로 고정합니다.
|
||||
|
||||
```dotenv
|
||||
USERFRONT_URL=https://app.brsw.kr
|
||||
PUBLIC_HOST=app.brsw.kr
|
||||
HYDRA_PUBLIC_URL=https://app.brsw.kr/oidc
|
||||
VITE_OIDC_AUTHORITY=https://app.brsw.kr/oidc
|
||||
```
|
||||
|
||||
## 4.1 Traefik 연동
|
||||
|
||||
생성된 `docker-compose.yaml`은 외부 진입 서비스에 Traefik docker provider label을 포함합니다.
|
||||
|
||||
- `gateway`: `${PUBLIC_HOST}`로 라우팅하며 `app.brsw.kr` 같은 SSO 메인 도메인을 담당합니다.
|
||||
- `adminfront`: `${ADMINFRONT_HOST}`로 라우팅합니다.
|
||||
- `devfront`: `${DEVFRONT_HOST}`로 라우팅합니다.
|
||||
- `orgfront`: `${ORGFRONT_HOST}`로 라우팅합니다.
|
||||
|
||||
`deploy/create-instance.sh`는 `.env`의 `TRAEFIK_PUBLIC_NETWORK` 값을 읽어 기본 `traefik-public` external network가 없으면 생성합니다. Traefik은 별도 경로(`/home/user/traefik`)에서 먼저 실행되어 있어야 하며, Baron SSO compose는 public-facing 서비스만 이 external network에 붙입니다.
|
||||
|
||||
## 4.2 프로덕션 이미지 버전 규칙
|
||||
|
||||
프로덕션 후보 이미지는 `.gitea/workflows/production_image_publish.yml`에서 `dev` 브랜치를 checkout해 빌드하고 공용 저장소에 push합니다. 운영자가 입력하는 값은 `version_prefix`이며 형식은 `vX.YYMM`입니다.
|
||||
|
||||
최종 이미지 태그는 워크플로우가 checkout된 커밋의 4자리 short SHA를 마지막 자리로 붙여 자동 생성합니다.
|
||||
|
||||
```text
|
||||
v1.2606.ab12
|
||||
```
|
||||
|
||||
배포는 먼저 `.gitea/workflows/staging_image_deploy.yml`에서 최종 `image_tag`를 입력해 staging에 적용하고, 같은 `image_tag`를 `.gitea/workflows/production_image_deploy.yml`에 입력해 production에 적용합니다. 두 deploy workflow는 모두 `vX.YYMM.<커밋해시4자리>` 형식만 허용하고, 해당 태그의 이미지를 pull한 뒤 각 환경의 `APP_ENV`로 실행합니다.
|
||||
|
||||
같은 이미지를 사용하기 위한 계약은 다음과 같습니다.
|
||||
|
||||
```text
|
||||
dev 브랜치 검증
|
||||
-> production_image_publish.yml에서 vX.YYMM.<commit4> 이미지 build/push
|
||||
-> staging_image_deploy.yml에서 같은 image_tag pull/up
|
||||
-> production_image_deploy.yml에서 같은 image_tag pull/up
|
||||
```
|
||||
|
||||
WORKS Drive archive를 켜면 publish workflow가 registry에 push한 이미지를 다시 pull한 뒤 `scripts/docker-image/upload_works_drive.sh`로 `docker-build-image/<repository-path>/<tag>/` 아래에 `image.tar.zst`, `image.tar.zst.sha256`, `manifest.json`을 저장합니다. 이 archive도 staging/production과 같은 `image_tag` 기준입니다.
|
||||
|
||||
## 5. 주요 설정 파일 관리
|
||||
- **Nginx (Gateway & UserFront)**: 각 인스턴스의 백엔드 포트를 자동으로 감지하여 리버스 프록시를 수행합니다.
|
||||
- **Ory Kratos**: `allowed_origins`, `allowed_return_urls`, `webhook` 주소 등이 인스턴스 포트에 맞게 자동 치환됩니다.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
INSTANCE_NAME={{INSTANCE_NAME}}
|
||||
COMPOSE_PROJECT_NAME=baron-sso-{{INSTANCE_NAME}}
|
||||
APP_ENV=production
|
||||
SOURCE_ROOT=../..
|
||||
|
||||
# === [2] 포트 Prefix 설정 (예: 23 입력 시 23000, 23432 등 생성) ===
|
||||
P={{PORT_PREFIX}}
|
||||
@@ -27,6 +28,13 @@ USERFRONT_URL=https://{{INSTANCE_NAME}}-sso.${DOMAIN_SUFFIX}
|
||||
ADMINFRONT_URL=https://{{INSTANCE_NAME}}-admin.${DOMAIN_SUFFIX}
|
||||
DEVFRONT_URL=https://{{INSTANCE_NAME}}-dev.${DOMAIN_SUFFIX}
|
||||
ORGFRONT_URL=https://{{INSTANCE_NAME}}-org.${DOMAIN_SUFFIX}
|
||||
PUBLIC_HOST={{INSTANCE_NAME}}-sso.${DOMAIN_SUFFIX}
|
||||
ADMINFRONT_HOST={{INSTANCE_NAME}}-admin.${DOMAIN_SUFFIX}
|
||||
DEVFRONT_HOST={{INSTANCE_NAME}}-dev.${DOMAIN_SUFFIX}
|
||||
ORGFRONT_HOST={{INSTANCE_NAME}}-org.${DOMAIN_SUFFIX}
|
||||
TRAEFIK_PUBLIC_NETWORK=traefik-public
|
||||
TRAEFIK_ENTRYPOINT=websecure
|
||||
TRAEFIK_CERT_RESOLVER=myresolver
|
||||
|
||||
# OIDC/Auth URL
|
||||
VITE_OIDC_AUTHORITY=${USERFRONT_URL}/oidc
|
||||
|
||||
@@ -6,6 +6,7 @@ export default defineConfig({
|
||||
envPrefix: ["VITE_", "USERFRONT_"],
|
||||
server: {
|
||||
host: "127.0.0.1",
|
||||
port: 5174,
|
||||
// 인스턴스별 도메인을 자동으로 허용
|
||||
allowedHosts: ["{{DEVFRONT_DOMAIN}}", "localhost", "127.0.0.1"],
|
||||
proxy: {
|
||||
@@ -17,7 +18,7 @@ export default defineConfig({
|
||||
},
|
||||
preview: {
|
||||
host: "127.0.0.1",
|
||||
port: 5173,
|
||||
port: 5174,
|
||||
allowedHosts: ["{{DEVFRONT_DOMAIN}}", "localhost", "127.0.0.1"],
|
||||
proxy: {
|
||||
"/api": {
|
||||
|
||||
392
deploy/templates/docker-compose.images.yaml
Normal file
392
deploy/templates/docker-compose.images.yaml
Normal file
@@ -0,0 +1,392 @@
|
||||
name: ${COMPOSE_PROJECT_NAME}
|
||||
|
||||
services:
|
||||
# --- Infrastructure ---
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_db
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||
ports:
|
||||
- "${DB_PORT}:5432"
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
networks: [app_net]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_redis
|
||||
ports:
|
||||
- "${REDIS_PORT}:6379"
|
||||
networks: [app_net]
|
||||
|
||||
clickhouse:
|
||||
image: clickhouse/clickhouse-server:latest
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_clickhouse
|
||||
environment:
|
||||
- CLICKHOUSE_USER=baron
|
||||
- CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD}
|
||||
ports:
|
||||
- "${CLICKHOUSE_PORT_HTTP}:8123"
|
||||
- "${CLICKHOUSE_PORT_NATIVE}:9000"
|
||||
volumes:
|
||||
- clickhouse_data:/var/lib/clickhouse
|
||||
networks: [app_net]
|
||||
|
||||
# --- Ory Stack ---
|
||||
postgres_ory:
|
||||
image: postgres:${ORY_POSTGRES_TAG:-17-alpine}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_ory_db
|
||||
environment:
|
||||
- POSTGRES_USER=${ORY_POSTGRES_USER:-ory}
|
||||
- POSTGRES_PASSWORD=${ORY_POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${ORY_POSTGRES_DB:-ory}
|
||||
volumes:
|
||||
- ory_db_data:/var/lib/postgresql/data
|
||||
- ./ory/init-db:/docker-entrypoint-initdb.d:ro
|
||||
networks: [app_net]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${ORY_POSTGRES_USER:-ory} -d ${KRATOS_DB:-ory_kratos}"]
|
||||
interval: 5s
|
||||
|
||||
kratos-migrate:
|
||||
image: oryd/kratos:${KRATOS_VERSION:-v26.2.0}
|
||||
env_file: .env
|
||||
environment:
|
||||
- DSN=postgres://${ORY_POSTGRES_USER:-ory}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB:-ory_kratos}?sslmode=disable&max_conns=20
|
||||
- KRATOS_SERVE_PUBLIC_BASE_URL=${KRATOS_BROWSER_URL}
|
||||
- KRATOS_SERVE_ADMIN_BASE_URL=${KRATOS_ADMIN_URL:-http://kratos:4434}
|
||||
- KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL}
|
||||
- KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS=${KRATOS_ALLOWED_RETURN_URLS_JSON:-["${KRATOS_UI_URL}","${KRATOS_UI_URL}/","${USERFRONT_URL}","${USERFRONT_URL}/","${USERFRONT_URL}/ko","${USERFRONT_URL}/ko/","${USERFRONT_URL}/en","${USERFRONT_URL}/en/","${USERFRONT_URL}/auth/callback","${USERFRONT_URL}/ko/auth/callback","${USERFRONT_URL}/en/auth/callback","${ADMINFRONT_URL}/auth/callback","${DEVFRONT_URL}/auth/callback","${ORGFRONT_URL}/auth/callback"]}
|
||||
- KRATOS_SELFSERVICE_FLOWS_ERROR_UI_URL=${KRATOS_UI_URL}/error
|
||||
- KRATOS_SELFSERVICE_FLOWS_SETTINGS_UI_URL=${KRATOS_UI_URL}/error?error=settings_disabled
|
||||
- KRATOS_SELFSERVICE_FLOWS_RECOVERY_UI_URL=${KRATOS_UI_URL}/recovery
|
||||
- KRATOS_SELFSERVICE_FLOWS_VERIFICATION_UI_URL=${KRATOS_UI_URL}/verification
|
||||
- KRATOS_SELFSERVICE_FLOWS_LOGIN_UI_URL=${KRATOS_UI_URL}/login
|
||||
- KRATOS_SELFSERVICE_FLOWS_REGISTRATION_UI_URL=${KRATOS_UI_URL}/registration
|
||||
- KRATOS_SELFSERVICE_FLOWS_LOGOUT_AFTER_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL}/login
|
||||
volumes:
|
||||
- ./config/.generated/ory/kratos:/etc/config/kratos:ro
|
||||
command: migrate sql up -e -c /etc/config/kratos/kratos.yml --yes
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
postgres_ory: { condition: service_healthy }
|
||||
|
||||
kratos:
|
||||
image: oryd/kratos:${KRATOS_VERSION:-v26.2.0}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_kratos
|
||||
env_file: .env
|
||||
environment:
|
||||
- DSN=postgres://${ORY_POSTGRES_USER:-ory}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB:-ory_kratos}?sslmode=disable&max_conns=20
|
||||
- COOKIE_SECRET=${COOKIE_SECRET}
|
||||
- KRATOS_SERVE_PUBLIC_BASE_URL=${KRATOS_BROWSER_URL}
|
||||
- KRATOS_SERVE_ADMIN_BASE_URL=${KRATOS_ADMIN_URL:-http://kratos:4434}
|
||||
- KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL}
|
||||
- KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS=${KRATOS_ALLOWED_RETURN_URLS_JSON:-["${KRATOS_UI_URL}","${KRATOS_UI_URL}/","${USERFRONT_URL}","${USERFRONT_URL}/","${USERFRONT_URL}/ko","${USERFRONT_URL}/ko/","${USERFRONT_URL}/en","${USERFRONT_URL}/en/","${USERFRONT_URL}/auth/callback","${USERFRONT_URL}/ko/auth/callback","${USERFRONT_URL}/en/auth/callback","${ADMINFRONT_URL}/auth/callback","${DEVFRONT_URL}/auth/callback","${ORGFRONT_URL}/auth/callback"]}
|
||||
- KRATOS_SELFSERVICE_FLOWS_ERROR_UI_URL=${KRATOS_UI_URL}/error
|
||||
- KRATOS_SELFSERVICE_FLOWS_SETTINGS_UI_URL=${KRATOS_UI_URL}/error?error=settings_disabled
|
||||
- KRATOS_SELFSERVICE_FLOWS_RECOVERY_UI_URL=${KRATOS_UI_URL}/recovery
|
||||
- KRATOS_SELFSERVICE_FLOWS_VERIFICATION_UI_URL=${KRATOS_UI_URL}/verification
|
||||
- KRATOS_SELFSERVICE_FLOWS_LOGIN_UI_URL=${KRATOS_UI_URL}/login
|
||||
- KRATOS_SELFSERVICE_FLOWS_REGISTRATION_UI_URL=${KRATOS_UI_URL}/registration
|
||||
- KRATOS_SELFSERVICE_FLOWS_LOGOUT_AFTER_DEFAULT_BROWSER_RETURN_URL=${KRATOS_UI_URL}/login
|
||||
volumes:
|
||||
- ./config/.generated/ory/kratos:/etc/config/kratos:ro
|
||||
command: serve -c /etc/config/kratos/kratos.yml --dev --watch-courier
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
kratos-migrate: { condition: service_completed_successfully }
|
||||
|
||||
hydra-migrate:
|
||||
image: oryd/hydra:${HYDRA_VERSION:-v26.2.0}
|
||||
env_file: .env
|
||||
environment:
|
||||
- DSN=postgres://${ORY_POSTGRES_USER:-ory}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${HYDRA_DB:-ory_hydra}?sslmode=disable&max_conns=20
|
||||
command: migrate sql up -e --yes
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
postgres_ory: { condition: service_healthy }
|
||||
|
||||
hydra:
|
||||
image: oryd/hydra:${HYDRA_VERSION:-v26.2.0}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_hydra
|
||||
env_file: .env
|
||||
environment:
|
||||
- DSN=postgres://${ORY_POSTGRES_USER:-ory}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${HYDRA_DB:-ory_hydra}?sslmode=disable&max_conns=20
|
||||
- URLS_SELF_ISSUER=${HYDRA_PUBLIC_URL}
|
||||
- URLS_LOGIN=${HYDRA_LOGIN_URL:-${USERFRONT_URL}/login}
|
||||
- URLS_CONSENT=${HYDRA_CONSENT_URL:-${USERFRONT_URL}/consent}
|
||||
- URLS_ERROR=${HYDRA_ERROR_URL:-${USERFRONT_URL}/error}
|
||||
- SECRETS_SYSTEM=${ORY_POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- ./config/.generated/ory/hydra:/etc/config/hydra:ro
|
||||
command: serve -c /etc/config/hydra/hydra.yml all --dev
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
hydra-migrate: { condition: service_completed_successfully }
|
||||
|
||||
keto-migrate:
|
||||
image: oryd/keto:${KETO_VERSION:-v26.2.0}
|
||||
env_file: .env
|
||||
environment:
|
||||
- DSN=postgres://${ORY_POSTGRES_USER:-ory}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KETO_DB:-ory_keto}?sslmode=disable&max_conns=20
|
||||
volumes:
|
||||
- ./config/.generated/ory/keto:/etc/config/keto:ro
|
||||
command: ["migrate", "up", "-c", "/etc/config/keto/keto.yml", "--yes"]
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
postgres_ory: { condition: service_healthy }
|
||||
|
||||
keto:
|
||||
image: oryd/keto:${KETO_VERSION:-v26.2.0}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_keto
|
||||
env_file: .env
|
||||
environment:
|
||||
- DSN=postgres://${ORY_POSTGRES_USER:-ory}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KETO_DB:-ory_keto}?sslmode=disable&max_conns=20
|
||||
volumes:
|
||||
- ./config/.generated/ory/keto:/etc/config/keto:ro
|
||||
command: serve -c /etc/config/keto/keto.yml
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
keto-migrate: { condition: service_completed_successfully }
|
||||
|
||||
oathkeeper_logs_init:
|
||||
image: alpine:latest
|
||||
command: ["sh", "-c", "mkdir -p /var/log/oathkeeper && chown -R ${OATHKEEPER_UID:-1001}:${OATHKEEPER_GID:-1001} /var/log/oathkeeper"]
|
||||
volumes:
|
||||
- oathkeeper_logs:/var/log/oathkeeper
|
||||
networks: [app_net]
|
||||
|
||||
oathkeeper:
|
||||
image: oryd/oathkeeper:${OATHKEEPER_VERSION:-v26.2.0}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_oathkeeper
|
||||
env_file: .env
|
||||
user: "${OATHKEEPER_UID:-1001}:${OATHKEEPER_GID:-1001}"
|
||||
ports:
|
||||
- "${OATHKEEPER_PROXY_PORT}:4455"
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- LOG_LEVEL=debug
|
||||
- OATHKEEPER_INTROSPECT_CLIENT_ID=${OATHKEEPER_INTROSPECT_CLIENT_ID:-oathkeeper-introspect}
|
||||
- OATHKEEPER_INTROSPECT_CLIENT_SECRET=${OATHKEEPER_INTROSPECT_CLIENT_SECRET:-oathkeeper-secret}
|
||||
volumes:
|
||||
- ./config/.generated/ory/oathkeeper:/etc/config/oathkeeper:ro
|
||||
- oathkeeper_logs:/var/log/oathkeeper
|
||||
entrypoint: ["/etc/config/oathkeeper/entrypoint.sh"]
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
oathkeeper_logs_init: { condition: service_completed_successfully }
|
||||
kratos: { condition: service_started }
|
||||
hydra: { condition: service_started }
|
||||
|
||||
ory_stack_check:
|
||||
image: alpine:latest
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_ory_stack_check
|
||||
command: >
|
||||
/bin/sh -c "
|
||||
apk add --no-cache curl;
|
||||
echo 'Wait for Ory services...';
|
||||
check_ready() {
|
||||
name=\"$$1\";
|
||||
url=\"$$2\";
|
||||
max=\"$${ORY_STACK_CHECK_MAX_ATTEMPTS:-60}\";
|
||||
i=1;
|
||||
while [ \"$$i\" -le \"$$max\" ]; do
|
||||
if curl --connect-timeout 2 --max-time 3 -fsS \"$$url\" >/dev/null; then
|
||||
echo \"Ory service ready: $$name\";
|
||||
return 0;
|
||||
fi;
|
||||
echo \"Waiting for Ory service: $$name ($$i/$$max)\";
|
||||
i=$$((i + 1));
|
||||
sleep 1;
|
||||
done;
|
||||
echo \"ERROR: Ory service not ready: $$name after $$max attempts ($$url)\" >&2;
|
||||
echo \"ERROR: Check service logs: docker logs $${COMPOSE_PROJECT_NAME}_$$name\" >&2;
|
||||
return 1;
|
||||
};
|
||||
check_ready kratos http://kratos:4433/health/ready || exit 1;
|
||||
check_ready hydra http://hydra:4444/health/ready || exit 1;
|
||||
check_ready keto http://keto:4466/health/ready || exit 1;
|
||||
echo 'Ory stack is ready.';"
|
||||
depends_on:
|
||||
- kratos
|
||||
- hydra
|
||||
- keto
|
||||
networks: [app_net]
|
||||
|
||||
init-rp:
|
||||
image: oryd/hydra:${HYDRA_CLI_VERSION:-v26.2.0}
|
||||
env_file: .env
|
||||
entrypoint: ["/bin/sh", "-ec"]
|
||||
command:
|
||||
- |
|
||||
upsert_client() {
|
||||
ID=$$1
|
||||
shift
|
||||
if hydra get oauth2-client --endpoint "$${HYDRA_ADMIN_URL:-http://hydra:4445}" "$$ID" >/dev/null 2>&1; then
|
||||
hydra update oauth2-client --endpoint "$${HYDRA_ADMIN_URL:-http://hydra:4445}" "$$ID" "$$@"
|
||||
else
|
||||
hydra create oauth2-client --endpoint "$${HYDRA_ADMIN_URL:-http://hydra:4445}" --id "$$ID" "$$@"
|
||||
fi
|
||||
}
|
||||
|
||||
upsert_client "adminfront" \
|
||||
--name "AdminFront" \
|
||||
--grant-type authorization_code,refresh_token \
|
||||
--response-type code \
|
||||
--scope openid,offline_access,profile,email \
|
||||
--token-endpoint-auth-method none \
|
||||
--redirect-uri "$${ADMINFRONT_CALLBACK_URLS:-$${ADMINFRONT_URL}/auth/callback}"
|
||||
|
||||
upsert_client "devfront" \
|
||||
--name "DevFront" \
|
||||
--grant-type authorization_code,refresh_token \
|
||||
--response-type code \
|
||||
--scope openid,offline_access,profile,email \
|
||||
--token-endpoint-auth-method none \
|
||||
--redirect-uri "$${DEVFRONT_CALLBACK_URLS:-$${DEVFRONT_URL}/auth/callback}"
|
||||
|
||||
upsert_client "orgfront" \
|
||||
--name "OrgFront" \
|
||||
--grant-type authorization_code,refresh_token \
|
||||
--response-type code \
|
||||
--scope openid,offline_access,profile,email \
|
||||
--token-endpoint-auth-method none \
|
||||
--redirect-uri "$${ORGFRONT_CALLBACK_URLS:-$${ORGFRONT_URL}/auth/callback}"
|
||||
|
||||
upsert_client "$${OATHKEEPER_INTROSPECT_CLIENT_ID:-oathkeeper-introspect}" \
|
||||
--secret "$${OATHKEEPER_INTROSPECT_CLIENT_SECRET:-oathkeeper-secret}" \
|
||||
--grant-type client_credentials \
|
||||
--response-type token \
|
||||
--scope openid,offline_access,profile,email
|
||||
depends_on:
|
||||
ory_stack_check: { condition: service_completed_successfully }
|
||||
networks: [app_net]
|
||||
|
||||
# --- Application Services ---
|
||||
backend:
|
||||
image: ${BACKEND_IMAGE_NAME}:${IMAGE_TAG}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_backend
|
||||
env_file: .env
|
||||
environment:
|
||||
- PORT=${BACKEND_PORT}
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- IDP_PROVIDER=${IDP_PROVIDER:-ory}
|
||||
- USERFRONT_URL=${USERFRONT_URL}
|
||||
- KRATOS_ADMIN_URL=${KRATOS_ADMIN_URL:-http://kratos:4434}
|
||||
- HYDRA_ADMIN_URL=${HYDRA_ADMIN_URL:-http://hydra:4445}
|
||||
- HYDRA_PUBLIC_URL=${HYDRA_PUBLIC_URL}
|
||||
- KETO_READ_URL=${KETO_READ_URL:-http://keto:4466}
|
||||
- KETO_WRITE_URL=${KETO_WRITE_URL:-http://keto:4467}
|
||||
- DB_HOST=postgres
|
||||
- REDIS_ADDR=redis:6379
|
||||
- CLICKHOUSE_HOST=clickhouse
|
||||
- SEED_TENANT_CSV_PATH=/app/seed-tenant.csv
|
||||
ports:
|
||||
- "${BACKEND_PORT}:${BACKEND_PORT}"
|
||||
volumes:
|
||||
- ./adminfront/seed-tenant.csv:/app/seed-tenant.csv:ro
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
postgres: { condition: service_healthy }
|
||||
redis: { condition: service_started }
|
||||
oathkeeper: { condition: service_started }
|
||||
|
||||
gateway:
|
||||
image: ${USERFRONT_IMAGE_NAME}:${IMAGE_TAG}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_gateway
|
||||
ports:
|
||||
- "${USERFRONT_PORT}:80"
|
||||
volumes:
|
||||
- ./gateway/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.rule=Host(`${PUBLIC_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-gateway.loadbalancer.server.port=80"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
adminfront:
|
||||
image: ${ADMINFRONT_IMAGE_NAME}:${IMAGE_TAG}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_adminfront
|
||||
env_file: .env
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- API_PROXY_TARGET=http://backend:${BACKEND_PORT}
|
||||
ports:
|
||||
- "${ADMINFRONT_PORT}:5173"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-adminfront.rule=Host(`${ADMINFRONT_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-adminfront.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-adminfront.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-adminfront.loadbalancer.server.port=5173"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
devfront:
|
||||
image: ${DEVFRONT_IMAGE_NAME}:${IMAGE_TAG}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_devfront
|
||||
env_file: .env
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- API_PROXY_TARGET=http://backend:${BACKEND_PORT}
|
||||
ports:
|
||||
- "${DEVFRONT_PORT}:5174"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-devfront.rule=Host(`${DEVFRONT_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-devfront.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-devfront.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-devfront.loadbalancer.server.port=5174"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
orgfront:
|
||||
image: ${ORGFRONT_IMAGE_NAME}:${IMAGE_TAG}
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_orgfront
|
||||
env_file: .env
|
||||
environment:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- API_PROXY_TARGET=http://backend:${BACKEND_PORT}
|
||||
- USERFRONT_URL=${USERFRONT_URL}
|
||||
ports:
|
||||
- "${ORGFRONT_PORT}:5175"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-orgfront.rule=Host(`${ORGFRONT_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-orgfront.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-orgfront.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-orgfront.loadbalancer.server.port=5175"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
networks:
|
||||
app_net:
|
||||
name: ${COMPOSE_PROJECT_NAME}_net
|
||||
traefik_public:
|
||||
external: true
|
||||
name: ${TRAEFIK_PUBLIC_NETWORK:-traefik-public}
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
name: db_data_${INSTANCE_NAME}
|
||||
ory_db_data:
|
||||
name: ory_db_data_${INSTANCE_NAME}
|
||||
clickhouse_data:
|
||||
name: clickhouse_data_${INSTANCE_NAME}
|
||||
oathkeeper_logs:
|
||||
name: oathkeeper_logs_${INSTANCE_NAME}
|
||||
@@ -288,7 +288,7 @@ services:
|
||||
ports:
|
||||
- "${BACKEND_PORT}:${BACKEND_PORT}"
|
||||
volumes:
|
||||
- ../../adminfront/seed-tenant.csv:/app/seed-tenant.csv:ro
|
||||
- ${SOURCE_ROOT:-../..}/adminfront/seed-tenant.csv:/app/seed-tenant.csv:ro
|
||||
networks: [app_net]
|
||||
depends_on:
|
||||
postgres: { condition: service_healthy }
|
||||
@@ -297,7 +297,7 @@ services:
|
||||
|
||||
gateway:
|
||||
build:
|
||||
context: ../..
|
||||
context: ${SOURCE_ROOT:-../..}
|
||||
dockerfile: ./userfront/Dockerfile
|
||||
target: production
|
||||
container_name: ${COMPOSE_PROJECT_NAME}_gateway
|
||||
@@ -305,11 +305,20 @@ services:
|
||||
- "${USERFRONT_PORT}:80"
|
||||
volumes:
|
||||
- ./gateway/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
networks: [app_net]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.rule=Host(`${PUBLIC_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-gateway.loadbalancer.server.port=80"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
adminfront:
|
||||
build:
|
||||
context: ../..
|
||||
context: ${SOURCE_ROOT:-../..}
|
||||
dockerfile: ./adminfront/Dockerfile
|
||||
args:
|
||||
VITE_ADMIN_PUBLIC_URL: ${ADMINFRONT_URL}
|
||||
@@ -323,11 +332,20 @@ services:
|
||||
- API_PROXY_TARGET=http://backend:${BACKEND_PORT}
|
||||
ports:
|
||||
- "${ADMINFRONT_PORT}:5173"
|
||||
networks: [app_net]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-adminfront.rule=Host(`${ADMINFRONT_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-adminfront.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-adminfront.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-adminfront.loadbalancer.server.port=5173"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
devfront:
|
||||
build:
|
||||
context: ../..
|
||||
context: ${SOURCE_ROOT:-../..}
|
||||
dockerfile: ./devfront/Dockerfile
|
||||
args:
|
||||
VITE_DEVFRONT_PUBLIC_URL: ${DEVFRONT_URL}
|
||||
@@ -339,12 +357,21 @@ services:
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- API_PROXY_TARGET=http://backend:${BACKEND_PORT}
|
||||
ports:
|
||||
- "${DEVFRONT_PORT}:5173"
|
||||
networks: [app_net]
|
||||
- "${DEVFRONT_PORT}:5174"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-devfront.rule=Host(`${DEVFRONT_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-devfront.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-devfront.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-devfront.loadbalancer.server.port=5174"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
orgfront:
|
||||
build:
|
||||
context: ../..
|
||||
context: ${SOURCE_ROOT:-../..}
|
||||
dockerfile: ./orgfront/Dockerfile
|
||||
args:
|
||||
VITE_ORGFRONT_PUBLIC_URL: ${ORGFRONT_URL}
|
||||
@@ -358,11 +385,23 @@ services:
|
||||
- USERFRONT_URL=${USERFRONT_URL}
|
||||
ports:
|
||||
- "${ORGFRONT_PORT}:5175"
|
||||
networks: [app_net]
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-orgfront.rule=Host(`${ORGFRONT_HOST}`)"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-orgfront.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}"
|
||||
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}-orgfront.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}"
|
||||
- "traefik.http.services.${COMPOSE_PROJECT_NAME}-orgfront.loadbalancer.server.port=5175"
|
||||
networks:
|
||||
- app_net
|
||||
- traefik_public
|
||||
|
||||
networks:
|
||||
app_net:
|
||||
name: ${COMPOSE_PROJECT_NAME}_net
|
||||
traefik_public:
|
||||
external: true
|
||||
name: ${TRAEFIK_PUBLIC_NETWORK:-traefik-public}
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
|
||||
Reference in New Issue
Block a user