diff --git a/.gitea/workflows/staging_code_pull.yml b/.gitea/workflows/staging_code_pull.yml index 5ee15377..52035a8b 100644 --- a/.gitea/workflows/staging_code_pull.yml +++ b/.gitea/workflows/staging_code_pull.yml @@ -2,6 +2,11 @@ name: Release Baron SSO to Staging on: workflow_dispatch: + inputs: + target_branch: + description: "Branch to deploy" + required: true + default: "dev" jobs: deploy-staging: @@ -15,21 +20,23 @@ jobs: with: ssh-private-key: ${{ secrets.STAGE_SSH_PRIVATE_KEY }} - - name: Deploy to Staging + - name: Deploy to Staging by git pull env: DEPLOY_PATH: ${{ vars.STAGE_DEPLOY_PATH }} STAGE_HOST: ${{ vars.STAGE_HOST }} STAGE_USER: ${{ vars.STAGE_USER }} + TARGET_BRANCH: ${{ inputs.target_branch }} run: | set -euo pipefail echo "DEBUG: STAGE_USER='${STAGE_USER}'" echo "DEBUG: STAGE_HOST='${STAGE_HOST}'" echo "DEBUG: DEPLOY_PATH='${DEPLOY_PATH}'" + echo "DEBUG: TARGET_BRANCH='${TARGET_BRANCH}'" # Sanity check - if [ -z "${STAGE_USER}" ] || [ -z "${STAGE_HOST}" ] || [ -z "${DEPLOY_PATH}" ]; then - echo "::error::Missing required vars (STAGE_USER/STAGE_HOST/DEPLOY_PATH)." + if [ -z "${STAGE_USER}" ] || [ -z "${STAGE_HOST}" ] || [ -z "${DEPLOY_PATH}" ] || [ -z "${TARGET_BRANCH}" ]; then + echo "::error::Missing required vars (STAGE_USER/STAGE_HOST/DEPLOY_PATH/TARGET_BRANCH)." exit 1 fi @@ -122,40 +129,39 @@ jobs: else git remote set-url origin ssh://git@172.16.10.175:222/baron/baron-sso.git fi - git fetch origin main && \ - git checkout -B main origin/main && \ - git pull --ff-only" + git fetch --depth 1 origin '${TARGET_BRANCH}' && \ + git checkout -B '${TARGET_BRANCH}' FETCH_HEAD" # .env 파일 복사 scp .env "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/" # 배포 실행 - echo "ssh "${STAGE_USER}@${STAGE_HOST}" \ - "export DEPLOY_PATH='${DEPLOY_PATH}'; \ - cd \"\${DEPLOY_PATH}\"; \ - set -a; . ./.env; set +a; \ + ssh "${STAGE_USER}@${STAGE_HOST}" "DEPLOY_PATH='${DEPLOY_PATH}' bash -s" <<'EOSSH' + set -euo pipefail + cd "${DEPLOY_PATH}" + set -a; . ./.env; set +a; - # 네트워크 생성 - for net in baron_net public_net ory-net hydranet kratosnet; do - docker network inspect \"\$net\" >/dev/null 2>&1 || docker network create \"\$net\" - done + # 네트워크 생성 + for net in baron_net public_net ory-net hydranet kratosnet; do + docker network inspect "${net}" >/dev/null 2>&1 || docker network create "${net}" + done - envsubst < docker/docker-compose.staging.template.yaml > docker-compose.yml; \ - # [중요] 설정 파일 권한 문제 해결 (Ory 이미지는 root가 아닌 사용자로 실행됨) - chmod -R 777 docker/ory + # [중요] 설정 파일 권한 문제 해결 (Ory 이미지는 root가 아닌 사용자로 실행됨) + chmod -R 777 docker/ory - docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml pull; \ + docker compose -f docker/staging_pull_compose.template.yaml pull - # [주의] DB 초기화 스크립트는 '새로운 볼륨'에서만 실행됨. - docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml down || true + # [주의] DB 초기화 스크립트는 '새로운 볼륨'에서만 실행됨. + docker compose -f docker/staging_pull_compose.template.yaml down || true - docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml up -d --remove-orphans; \ + docker compose -f docker/staging_pull_compose.template.yaml up -d --remove-orphans - # 배포 후 상태 확인 (실패 시 로그 출력을 위함) - sleep 10; \ - if [ \$(docker inspect -f '{{.State.ExitCode}}' baron-sso-staging-kratos-migrate-1) -ne 0 ]; then \ - echo 'Kratos Migrate Failed. Logs:'; \ - docker logs baron-sso-staging-kratos-migrate-1; \ - exit 1; \ - fi" + # 배포 후 상태 확인 (실패 시 로그 출력을 위함) + sleep 10 + if [ "$(docker inspect -f '{{.State.ExitCode}}' baron-sso-staging-kratos-migrate-1)" -ne 0 ]; then + echo 'Kratos Migrate Failed. Logs:' + docker logs baron-sso-staging-kratos-migrate-1 + exit 1 + fi + EOSSH diff --git a/docker/staging_pull_compose.template.yaml b/docker/staging_pull_compose.template.yaml new file mode 100644 index 00000000..911a129b --- /dev/null +++ b/docker/staging_pull_compose.template.yaml @@ -0,0 +1,321 @@ +services: + postgres: + image: postgres:17-alpine + container_name: baron_postgres + environment: + POSTGRES_USER: "${DB_USER:-baron}" + POSTGRES_PASSWORD: "${DB_PASSWORD:-password}" + POSTGRES_DB: "${DB_NAME:-baron_sso}" + ports: + - "${DB_PORT:-5432}:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + - ./docker/init-metadata:/docker-entrypoint-initdb.d + networks: + - baron_net + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready -U ${DB_USER:-baron} -d ${DB_NAME:-baron_sso}", + ] + interval: 5s + timeout: 5s + retries: 5 + restart: always + + clickhouse: + image: clickhouse/clickhouse-server:latest + container_name: baron_clickhouse + restart: always + volumes: + - clickhouse_data:/var/lib/clickhouse + environment: + CLICKHOUSE_USER: "${CLICKHOUSE_USER:-baron}" + CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD:-password}" + networks: + - baron_net + + redis: + image: redis:7-alpine + container_name: baron_redis + restart: always + command: redis-server --port 6389 + ports: + - "6389:6389" + volumes: + - redis_data:/data + networks: + - baron_net + + gateway: + image: nginx:alpine + container_name: baron_gateway + restart: always + ports: + - "${USERFRONT_PORT:-5000}:5000" + volumes: + - ./gateway/nginx.conf:/etc/nginx/conf.d/default.conf:ro + networks: + - baron_net + - public_net + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5000/"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + + postgres_ory: + image: postgres:${ORY_POSTGRES_TAG:-17-alpine} + container_name: ory_postgres + environment: + - POSTGRES_USER=${ORY_POSTGRES_USER:-ory} + - POSTGRES_PASSWORD=${ORY_POSTGRES_PASSWORD:-secret} + - POSTGRES_DB=${ORY_POSTGRES_DB:-ory} + volumes: + - ./docker/ory/init-db:/docker-entrypoint-initdb.d + - ory_postgres_data:/var/lib/postgresql/data + networks: + - ory-net + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready -U ${ORY_POSTGRES_USER:-ory} -d ${KRATOS_DB:-ory_kratos}", + ] + interval: 5s + timeout: 5s + retries: 5 + + kratos-migrate: + image: oryd/kratos:${KRATOS_VERSION:-v25.4.0} + environment: + - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB:-ory_kratos}?sslmode=disable&max_conns=20 + - KRATOS_SERVE_PUBLIC_BASE_URL="${KRATOS_BROWSER_URL:-http://localhost:4433}" + - KRATOS_SERVE_ADMIN_BASE_URL="${KRATOS_ADMIN_URL:-http://kratos:4434}" + - KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL="${KRATOS_UI_URL:-http://localhost:5000}" + - KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS='["${KRATOS_UI_URL:-http://localhost:5000}","${USERFRONT_URL:-http://localhost:5000}"]' + volumes: + - ./docker/ory/kratos:/etc/config/kratos + command: migrate sql up -e -c /etc/config/kratos/kratos.yml --yes + depends_on: + postgres_ory: + condition: service_healthy + networks: + - ory-net + + kratos: + image: oryd/kratos:${KRATOS_VERSION:-v25.4.0} + container_name: ory_kratos + environment: + - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${KRATOS_DB:-ory_kratos}?sslmode=disable&max_conns=20 + - COOKIE_SECRET="${COOKIE_SECRET:-localcookie123}" + - KRATOS_SERVE_PUBLIC_BASE_URL="${KRATOS_BROWSER_URL:-http://localhost:4433}" + - KRATOS_SERVE_ADMIN_BASE_URL="${KRATOS_ADMIN_URL:-http://kratos:4434}" + - KRATOS_SELFSERVICE_DEFAULT_BROWSER_RETURN_URL="${KRATOS_UI_URL:-http://localhost:5000}" + - KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS='["${KRATOS_UI_URL:-http://localhost:5000}","${USERFRONT_URL:-http://localhost:5000}"]' + volumes: + - ./docker/ory/kratos:/etc/config/kratos + command: serve -c /etc/config/kratos/kratos.yml + depends_on: + kratos-migrate: + condition: service_completed_successfully + networks: + - ory-net + - kratosnet + + hydra-migrate: + image: oryd/hydra:${HYDRA_VERSION:-v25.4.0} + environment: + - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${HYDRA_DB:-ory_hydra}?sslmode=disable&max_conns=20 + command: migrate sql up -e --yes + depends_on: + postgres_ory: + condition: service_healthy + networks: + - ory-net + + hydra: + image: oryd/hydra:${HYDRA_VERSION:-v25.4.0} + container_name: ory_hydra + environment: + - DSN=postgres://${ORY_POSTGRES_USER}:${ORY_POSTGRES_PASSWORD}@postgres_ory:5432/${HYDRA_DB:-ory_hydra}?sslmode=disable&max_conns=20 + - URLS_SELF_ISSUER=${USERFRONT_URL:-http://localhost:5000}/oidc + - URLS_LOGIN=${USERFRONT_URL:-http://localhost:5000}/login + - URLS_CONSENT=${USERFRONT_URL:-http://localhost:5000}/consent + - SECRETS_SYSTEM=${ORY_POSTGRES_PASSWORD} + volumes: + - ./docker/ory/hydra:/etc/config/hydra + command: serve -c /etc/config/hydra/hydra.yml all --dev + depends_on: + hydra-migrate: + condition: service_completed_successfully + networks: + - ory-net + - hydranet + + oathkeeper: + image: oryd/oathkeeper:${OATHKEEPER_VERSION:-v0.40.6} + container_name: oathkeeper + restart: unless-stopped + depends_on: + kratos: + condition: service_started + environment: + - LOG_LEVEL=debug + command: serve proxy --config /etc/config/oathkeeper/oathkeeper.yml + volumes: + - ./docker/ory/oathkeeper:/etc/config/oathkeeper + networks: + - ory-net + - baron_net + - public_net + ports: + - "4455:4455" + - "4456:4456" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:4456/health/ready"] + interval: 5s + timeout: 5s + retries: 5 + + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: baron_backend + env_file: + - .env + environment: + - APP_ENV=${APP_ENV:-development} + - GO_ENV=${APP_ENV:-development} + - COOKIE_SECRET=${COOKIE_SECRET} + - JWT_SECRET=${JWT_SECRET} + - DESCOPE_PROJECT_ID=${DESCOPE_PROJECT_ID} + - DESCOPE_MANAGEMENT_KEY=${DESCOPE_MANAGEMENT_KEY} + - NAVER_CLOUD_ACCESS_KEY=${NAVER_CLOUD_ACCESS_KEY} + - NAVER_CLOUD_SECRET_KEY=${NAVER_CLOUD_SECRET_KEY} + - NAVER_CLOUD_SERVICE_ID=${NAVER_CLOUD_SERVICE_ID} + - NAVER_SENDER_PHONE_NUMBER=${NAVER_SENDER_PHONE_NUMBER} + - USERFRONT_URL=${USERFRONT_URL} + - REDIS_ADDR=${REDIS_ADDR} + - IDP_PROVIDER=${IDP_PROVIDER:-ory} + - KRATOS_ADMIN_URL=${KRATOS_ADMIN_URL:-http://kratos:4434} + - HYDRA_ADMIN_URL=${HYDRA_ADMIN_URL:-http://hydra:4445} + - HYDRA_PUBLIC_URL=${HYDRA_PUBLIC_URL:-http://hydra:4444} + - DB_HOST=postgres + - CLICKHOUSE_HOST=clickhouse + - CLICKHOUSE_PORT=${CLICKHOUSE_PORT_NATIVE:-9000} + - CLICKHOUSE_USER=${CLICKHOUSE_USER:-baron} + - CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-password} + depends_on: + - infra_check + networks: + - baron_net + - ory-net + volumes: + - ./backend:/app + command: ["go", "run", "./cmd/server"] + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + + adminfront: + build: + context: ./adminfront + dockerfile: Dockerfile + container_name: baron_adminfront + env_file: + - .env + environment: + - APP_ENV=${APP_ENV:-development} + - API_PROXY_TARGET=http://baron_backend:3000 + ports: + - "${ADMIN_PORT:-5173}:5173" + volumes: + - ./adminfront:/app + - /app/node_modules + networks: + - baron_net + + devfront: + build: + context: ./devfront + dockerfile: Dockerfile + container_name: baron_devfront + env_file: + - .env + environment: + - APP_ENV=${APP_ENV:-development} + - API_PROXY_TARGET=http://baron_backend:3000 + ports: + - "${DEVFRONT_PORT:-5174}:5173" + volumes: + - ./devfront:/app + - /app/node_modules + networks: + - baron_net + + userfront: + build: + context: ./userfront + dockerfile: Dockerfile + container_name: baron_userfront + env_file: + - .env + environment: + - BACKEND_URL=${BACKEND_URL} + - USERFRONT_URL=${USERFRONT_URL} + - APP_ENV=${APP_ENV} + networks: + - baron_net + - ory-net + depends_on: + backend: + condition: service_healthy + command: > + /bin/sh -c "mkdir -p /usr/share/nginx/html/assets && + echo \"BACKEND_URL=$${BACKEND_URL}\" >> /usr/share/nginx/html/assets/.env && + echo \"USERFRONT_URL=$${USERFRONT_URL}\" >> /usr/share/nginx/html/assets/.env && + echo \"APP_ENV=$${APP_ENV}\" >> /usr/share/nginx/html/assets/.env && + cp /usr/share/nginx/html/assets/.env /usr/share/nginx/html/.env && + nginx -g 'daemon off;'" + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5000/"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 10s + + infra_check: + image: alpine + command: ["echo", "Infrastructure assumed running"] + networks: + - baron_net + +volumes: + postgres_data: + clickhouse_data: + redis_data: + ory_postgres_data: + +networks: + baron_net: + external: true + name: baron_net + public_net: + external: true + name: public_net + ory-net: + external: true + name: ory-net + hydranet: + external: true + name: hydranet + kratosnet: + external: true + name: kratosnet