From 8040dddbcba7b0296ff82b7a77815a7ba1f1cd3c Mon Sep 17 00:00:00 2001 From: 23MinL Date: Fri, 11 Jul 2025 23:44:51 +0900 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20CI/CD=EC=99=80=20Docker=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=97=90=EC=84=9C=20Config=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=ED=97=AC=EC=8A=A4=EC=B2=B4=ED=81=AC=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 36 ++++++++++++------ .github/workflows/docker-health-check.yml | 46 +++++++++++++++++++++-- docker-compose.yml | 10 +++++ 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index c877b2c..5585896 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -111,24 +111,30 @@ jobs: - name: 코드 체크아웃 uses: actions/checkout@v4 - - name: 개발 환경 배포 설정 + - name: Config 레포지토리 체크아웃 + uses: actions/checkout@v4 + with: + repository: UruruLab/Ururu-AI-Config + path: config + token: ${{ secrets.GITHUB_TOKEN }} + + - name: 개발 환경 Config 파일 복사 run: | - printf 'SPRING_BOOT_BASE_URL=%s\n' "${{ secrets.DEV_SPRING_BACKEND_URL }}" >> .env.development - printf 'REDIS_URL=%s\n' "${{ secrets.DEV_REDIS_URL }}" >> .env.development + cp config/.env.development .env.development + echo "✅ 개발환경 Config 파일 복사 완료" - name: Docker Compose 설정 검증 run: | echo "개발 환경 설정 파일 검증" echo "docker-compose.development.yml 구문 검증" - docker compose -f docker-compose.development.yml config --quiet + ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet echo "개발 환경 배포 준비 완료" - name: 배포 시뮬레이션 (실제 EC2 배포 없음) run: | echo "개발 환경 배포 시뮬레이션" echo "- Docker 이미지: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop" - echo "- Spring Boot URL: [MASKED]" - echo "- Redis URL: [MASKED]" + echo "- Config 파일: .env.development (Config 레포지토리에서 가져옴)" echo "개발 환경 배포 설정 완료" deploy-production: @@ -141,24 +147,30 @@ jobs: - name: 코드 체크아웃 uses: actions/checkout@v4 - - name: 운영 환경 배포 설정 + - name: Config 레포지토리 체크아웃 + uses: actions/checkout@v4 + with: + repository: UruruLab/Ururu-AI-Config + path: config + token: ${{ secrets.GITHUB_TOKEN }} + + - name: 운영 환경 Config 파일 복사 run: | - printf 'SPRING_BOOT_BASE_URL=%s\n' "${{ secrets.PROD_SPRING_BACKEND_URL }}" >> .env.production - printf 'REDIS_URL=%s\n' "${{ secrets.PROD_REDIS_URL }}" >> .env.production + cp config/.env.production .env.production + echo "✅ 운영환경 Config 파일 복사 완료" - name: Docker Compose 설정 검증 run: | echo "운영 환경 설정 파일 검증" echo "docker-compose.production.yml 구문 검증" - docker compose -f docker-compose.production.yml config --quiet + ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet echo "운영 환경 배포 준비 완료" - name: 배포 알림 준비 run: | echo "운영 환경 배포 준비 완료" echo "- Docker 이미지: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" - echo "- Spring Boot URL: [MASKED]" - echo "- Redis URL: [MASKED]" + echo "- Config 파일: .env.production (Config 레포지토리에서 가져옴)" echo "실제 EC2 배포는 별도 프로세스에서 진행" - name: 배포 완료 알림 diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index c0c29f0..c7aa2cc 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -4,6 +4,14 @@ on: schedule: - cron: '0 */6 * * *' workflow_dispatch: + push: + branches: + - main + - 'fix/*' + pull_request: + branches: + - main + - 'fix/*' jobs: health-check: @@ -13,14 +21,42 @@ jobs: - name: 코드 체크아웃 uses: actions/checkout@v4 + - name: Config 레포지토리 체크아웃 + uses: actions/checkout@v4 + with: + repository: UruruLab/Ururu-AI-Config + path: config + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Config 파일 복사 (.env 파일들) + run: | + mkdir -p ./ + cp config/.env* ./ + echo "✅ Config 파일 복사 완료" + + - name: 환경 파일 확인 + run: | + echo "📁 복사된 환경 파일 확인" + ls -la .env* + if [ -f ".env.development" ]; then + echo "✅ .env.development 파일 존재" + else + echo "❌ .env.development 파일 없음" + fi + if [ -f ".env.production" ]; then + echo "✅ .env.production 파일 존재" + else + echo "❌ .env.production 파일 없음" + fi + - name: Docker Compose 설정 검증 run: | echo "✅ Docker Compose 파일 구문 검증" docker compose config --quiet - echo "✅ 개발환경 설정 검증" - docker compose -f docker-compose.development.yml config --quiet - echo "✅ 운영환경 설정 검증" - docker compose -f docker-compose.production.yml config --quiet + echo "✅ 개발환경 설정 검증 (복사된 Config 파일 사용)" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet + echo "✅ 운영환경 설정 검증 (복사된 Config 파일 사용)" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet - name: 헬스체크 시뮬레이션 run: | @@ -42,6 +78,8 @@ jobs: run: | echo "📊 헬스체크 완료 보고서" echo "✅ Docker Compose 설정 파일 정상" + echo "✅ Config 레포지토리 연동 정상" + echo "✅ 환경별 설정 파일 검증 완료" echo "✅ 워크플로우 설정 정상" echo "⚠️ 실제 서비스 상태는 EC2에서 별도 확인 필요" echo "💡 EC2에서 실행: docker compose ps && docker compose logs" diff --git a/docker-compose.yml b/docker-compose.yml index 85a5a50..4ef619c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,12 @@ services: - ENVIRONMENT=${ENVIRONMENT:-development} command: /scripts/fetch-config.sh restart: "no" + healthcheck: + test: ["CMD", "test", "-f", "/config/.env.${ENVIRONMENT:-development}"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 30s # FastAPI AI 추천 서비스 (Spring Boot 서버와 통신) ururu-ai: @@ -35,6 +41,10 @@ services: - HTTP_RETRY_COUNT=${HTTP_RETRY_COUNT:-3} - HTTP_RETRY_DELAY=${HTTP_RETRY_DELAY:-1} - GUNICORN_WORKERS=${GUNICORN_WORKERS:-4} + - REDIS_URL=${REDIS_URL:-redis://3.37.116.89:6379} + - REDIS_HOST=${REDIS_HOST:-3.37.116.89} + - REDIS_PORT=${REDIS_PORT:-6379} + - REDIS_PASSWORD=${REDIS_PASSWORD:-} env_file: - ./config/.env.${ENVIRONMENT:-development} depends_on: From e306512fa504f3245416a4a367b95e8581f55f47 Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 00:07:41 +0900 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20Config=20=EB=A0=88=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=A0=91=EA=B7=BC=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20PRIVATE=5FREPO=5FTOKEN=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 4 ++-- .github/workflows/docker-health-check.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 5585896..2863383 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -116,7 +116,7 @@ jobs: with: repository: UruruLab/Ururu-AI-Config path: config - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.PRIVATE_REPO_TOKEN }} - name: 개발 환경 Config 파일 복사 run: | @@ -152,7 +152,7 @@ jobs: with: repository: UruruLab/Ururu-AI-Config path: config - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.PRIVATE_REPO_TOKEN }} - name: 운영 환경 Config 파일 복사 run: | diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index c7aa2cc..0753263 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -26,7 +26,7 @@ jobs: with: repository: UruruLab/Ururu-AI-Config path: config - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.PRIVATE_REPO_TOKEN }} - name: Config 파일 복사 (.env 파일들) run: | From 68b9f873da85b3301b9090c315906f1ce6dd8470 Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 00:07:54 +0900 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20Redis=20=EC=84=A4=EC=A0=95=EC=9D=84?= =?UTF-8?q?=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=EB=A1=9C=EB=A7=8C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=B4?= =?UTF-8?q?=EC=95=88=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4ef619c..28483c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,9 +41,9 @@ services: - HTTP_RETRY_COUNT=${HTTP_RETRY_COUNT:-3} - HTTP_RETRY_DELAY=${HTTP_RETRY_DELAY:-1} - GUNICORN_WORKERS=${GUNICORN_WORKERS:-4} - - REDIS_URL=${REDIS_URL:-redis://3.37.116.89:6379} - - REDIS_HOST=${REDIS_HOST:-3.37.116.89} - - REDIS_PORT=${REDIS_PORT:-6379} + - REDIS_URL=${REDIS_URL:-} + - REDIS_HOST=${REDIS_HOST:-} + - REDIS_PORT=${REDIS_PORT:-} - REDIS_PASSWORD=${REDIS_PASSWORD:-} env_file: - ./config/.env.${ENVIRONMENT:-development} From dfb6438f77119cbc74adf1f718ca9aabd2c91914 Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 01:09:18 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20Docker=20Compose=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=20=EC=84=A4=EC=A0=95=20=EC=B5=9C=EC=A0=81=ED=99=94=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EC=98=81=EC=96=B4=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Docker Compose 기본 환경을 production으로 변경 - 환경변수 바인딩 중복 제거 및 최적화 - 컨테이너-호스트 HTTP 통신을 위한 host.docker.internal 설정 - 환경별 Spring Boot 연동 설정 분리 (개발: 비활성화, 운영: 필수) - 워크플로우 step name 및 메시지 영어 변환으로 한글 깨짐 방지 - 환경별 실행 스크립트 추가 (run-dev.sh, run-prod.sh) - 환경변수 바인딩 검증 단계 추가 --- .github/workflows/ci-cd.yml | 94 ++++++++++++----------- .github/workflows/docker-health-check.yml | 73 ++++++++++-------- .github/workflows/performance-test.yml | 50 ++++++------ docker-compose.development.yml | 26 ++++++- docker-compose.production.yml | 23 ++++-- docker-compose.yml | 25 ++---- scripts/run-dev.sh | 15 ++++ scripts/run-prod.sh | 25 ++++++ 8 files changed, 199 insertions(+), 132 deletions(-) create mode 100644 scripts/run-dev.sh create mode 100644 scripts/run-prod.sh diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 2863383..b86f59f 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -22,15 +22,15 @@ jobs: GITHUB_ACTIONS: true steps: - - name: 코드 체크아웃 + - name: Checkout Code uses: actions/checkout@v4 - - name: Python 환경 설정 + - name: Setup Python Environment uses: actions/setup-python@v5 with: python-version: '3.11' - - name: 의존성 캐시 + - name: Cache Dependencies uses: actions/cache@v4 with: path: ~/.cache/pip @@ -38,21 +38,21 @@ jobs: restore-keys: | ${{ runner.os }}-pip- - - name: 의존성 설치 + - name: Install Dependencies run: | python -m pip install --upgrade pip - # 먼저 테스트 도구들 설치 (pytest-env 추가) + # Install test tools first (including pytest-env) pip install pytest==8.3.4 pytest-asyncio==0.25.0 pytest-env==1.1.5 flake8==7.1.1 black==25.1.0 isort==5.13.2 - # 그 다음 프로젝트 의존성 설치 + # Then install project dependencies pip install -r requirements.txt - - name: 코드 품질 검사 + - name: Code Quality Check run: | - # 중요한 구문 오류만 체크 + # Check only critical syntax errors flake8 app/ --count --select=E9,F63,F7,F82 --show-source --statistics --exit-zero - echo "코드 품질 검사 완료" + echo "Code quality check completed" - - name: 테스트 실행 + - name: Run Tests run: | export APP_NAME="Ururu AI Recommendation System" export EMBEDDING_MODEL_NAME="sentence-transformers/all-MiniLM-L6-v2" @@ -66,20 +66,20 @@ jobs: if: github.event_name == 'push' steps: - - name: 코드 체크아웃 + - name: Checkout Code uses: actions/checkout@v4 - - name: Docker Buildx 설정 + - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 - - name: 컨테이너 레지스트리 로그인 + - name: Login to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.GHCR_USERNAME }} password: ${{ secrets.GHCR_TOKEN }} - - name: 메타데이터 추출 + - name: Extract Metadata id: meta uses: docker/metadata-action@v5 with: @@ -90,7 +90,7 @@ jobs: type=sha,prefix=sha- type=raw,value=latest,enable={{is_default_branch}} - - name: Docker 이미지 빌드 및 푸시 + - name: Build and Push Docker Image uses: docker/build-push-action@v5 with: context: . @@ -108,34 +108,36 @@ jobs: environment: development steps: - - name: 코드 체크아웃 + - name: Checkout Code uses: actions/checkout@v4 - - name: Config 레포지토리 체크아웃 + - name: Checkout Config Repository uses: actions/checkout@v4 with: repository: UruruLab/Ururu-AI-Config path: config token: ${{ secrets.PRIVATE_REPO_TOKEN }} - - name: 개발 환경 Config 파일 복사 + - name: Copy Development Environment Config Files run: | cp config/.env.development .env.development - echo "✅ 개발환경 Config 파일 복사 완료" + echo "✅ Development environment config files copied successfully" - - name: Docker Compose 설정 검증 + - name: Validate Docker Compose Configuration run: | - echo "개발 환경 설정 파일 검증" - echo "docker-compose.development.yml 구문 검증" + echo "Validating development environment configuration files" + echo "Validating docker-compose.development.yml syntax" ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet - echo "개발 환경 배포 준비 완료" + echo "Verifying environment variable bindings" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 10 "environment:" || true + echo "Development environment deployment preparation completed" - - name: 배포 시뮬레이션 (실제 EC2 배포 없음) + - name: Simulate Deployment (No actual EC2 deployment) run: | - echo "개발 환경 배포 시뮬레이션" - echo "- Docker 이미지: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop" - echo "- Config 파일: .env.development (Config 레포지토리에서 가져옴)" - echo "개발 환경 배포 설정 완료" + echo "Development environment deployment simulation" + echo "- Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop" + echo "- Config File: .env.development (fetched from Config repository)" + echo "Development environment deployment configuration completed" deploy-production: needs: build-and-push @@ -144,38 +146,40 @@ jobs: environment: production steps: - - name: 코드 체크아웃 + - name: Checkout Code uses: actions/checkout@v4 - - name: Config 레포지토리 체크아웃 + - name: Checkout Config Repository uses: actions/checkout@v4 with: repository: UruruLab/Ururu-AI-Config path: config token: ${{ secrets.PRIVATE_REPO_TOKEN }} - - name: 운영 환경 Config 파일 복사 + - name: Copy Production Environment Config Files run: | cp config/.env.production .env.production - echo "✅ 운영환경 Config 파일 복사 완료" + echo "✅ Production environment config files copied successfully" - - name: Docker Compose 설정 검증 + - name: Validate Docker Compose Configuration run: | - echo "운영 환경 설정 파일 검증" - echo "docker-compose.production.yml 구문 검증" + echo "Validating production environment configuration files" + echo "Validating docker-compose.production.yml syntax" ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet - echo "운영 환경 배포 준비 완료" + echo "Verifying environment variable bindings" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 10 "environment:" || true + echo "Production environment deployment preparation completed" - - name: 배포 알림 준비 + - name: Prepare Deployment Notification run: | - echo "운영 환경 배포 준비 완료" - echo "- Docker 이미지: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" - echo "- Config 파일: .env.production (Config 레포지토리에서 가져옴)" - echo "실제 EC2 배포는 별도 프로세스에서 진행" + echo "Production environment deployment preparation completed" + echo "- Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" + echo "- Config File: .env.production (fetched from Config repository)" + echo "Actual EC2 deployment will be handled by separate process" - - name: 배포 완료 알림 + - name: Deployment Completion Notification if: success() run: | - echo "GitHub Actions 배포 파이프라인이 성공적으로 완료되었습니다." - echo "Docker 이미지가 GitHub Container Registry에 푸시되었습니다." - echo "EC2 서버에서 수동으로 docker compose pull && docker compose up -d 실행이 필요합니다." + echo "GitHub Actions deployment pipeline completed successfully." + echo "Docker image has been pushed to GitHub Container Registry." + echo "Manual execution required on EC2 server: docker compose pull && docker compose up -d" diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index 0753263..cbca236 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -18,68 +18,77 @@ jobs: runs-on: ubuntu-latest steps: - - name: 코드 체크아웃 + - name: Checkout Code uses: actions/checkout@v4 - - name: Config 레포지토리 체크아웃 + - name: Checkout Config Repository uses: actions/checkout@v4 with: repository: UruruLab/Ururu-AI-Config path: config token: ${{ secrets.PRIVATE_REPO_TOKEN }} - - name: Config 파일 복사 (.env 파일들) + - name: Copy Config Files (.env files) run: | mkdir -p ./ cp config/.env* ./ - echo "✅ Config 파일 복사 완료" + echo "✅ Config files copied successfully" - - name: 환경 파일 확인 + - name: Verify Environment Files run: | - echo "📁 복사된 환경 파일 확인" + echo "📁 Checking copied environment files" ls -la .env* if [ -f ".env.development" ]; then - echo "✅ .env.development 파일 존재" + echo "✅ .env.development file exists" else - echo "❌ .env.development 파일 없음" + echo "❌ .env.development file missing" fi if [ -f ".env.production" ]; then - echo "✅ .env.production 파일 존재" + echo "✅ .env.production file exists" else - echo "❌ .env.production 파일 없음" + echo "❌ .env.production file missing" fi - - name: Docker Compose 설정 검증 + - name: Validate Docker Compose Configuration run: | - echo "✅ Docker Compose 파일 구문 검증" + echo "✅ Validating Docker Compose file syntax" docker compose config --quiet - echo "✅ 개발환경 설정 검증 (복사된 Config 파일 사용)" + echo "✅ Validating development environment (using copied config files)" ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet - echo "✅ 운영환경 설정 검증 (복사된 Config 파일 사용)" + echo "✅ Validating production environment (using copied config files)" ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet - - name: 헬스체크 시뮬레이션 + - name: Verify Environment Variable Bindings run: | - echo "🔍 AI 서비스 헬스체크 시뮬레이션" - echo "- 체크 대상: http://localhost:8000/health" - echo "- 예상 응답: {\"status\": \"healthy\", \"service\": \"ururu-ai-recommendation\"}" + echo "🔍 Verifying environment variable bindings" + echo "Development environment key variables:" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 20 "environment:" | head -15 || true + echo "" + echo "Production environment key variables:" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 20 "environment:" | head -15 || true - echo "🔍 Spring Backend 연결 테스트 시뮬레이션" - echo "- 체크 대상: http://localhost:8080/health" - echo "- 실제 연결은 EC2 환경에서만 가능" + - name: Simulate Health Check + run: | + echo "🔍 AI Service Health Check Simulation" + echo "- Target: http://localhost:8000/health" + echo "- Expected Response: {\"status\": \"healthy\", \"service\": \"ururu-ai-recommendation\"}" + + echo "🔍 Spring Backend Connection Test Simulation" + echo "- Target: http://localhost:8080/health" + echo "- Actual connection only available in EC2 environment" - - name: GitHub Container Registry 이미지 확인 + - name: Check GitHub Container Registry Images run: | - echo "📦 최신 Docker 이미지 확인" + echo "📦 Checking latest Docker images" echo "- Registry: ghcr.io/${{ github.repository }}" - echo "- 최신 태그: latest, main, develop" + echo "- Latest tags: latest, main, develop" - - name: 헬스체크 보고서 + - name: Generate Health Check Report run: | - echo "📊 헬스체크 완료 보고서" - echo "✅ Docker Compose 설정 파일 정상" - echo "✅ Config 레포지토리 연동 정상" - echo "✅ 환경별 설정 파일 검증 완료" - echo "✅ 워크플로우 설정 정상" - echo "⚠️ 실제 서비스 상태는 EC2에서 별도 확인 필요" - echo "💡 EC2에서 실행: docker compose ps && docker compose logs" + echo "📊 Health Check Completion Report" + echo "✅ Docker Compose configuration files validated" + echo "✅ Config repository integration working" + echo "✅ Environment-specific configuration files verified" + echo "✅ Workflow configuration validated" + echo "️ Actual service status needs separate verification on EC2" + echo "Run on EC2: docker compose ps && docker compose logs" diff --git a/.github/workflows/performance-test.yml b/.github/workflows/performance-test.yml index 7c5a178..c1157c7 100644 --- a/.github/workflows/performance-test.yml +++ b/.github/workflows/performance-test.yml @@ -4,12 +4,12 @@ on: workflow_dispatch: inputs: test_duration: - description: '테스트 실행 시간 (초)' + description: 'Test execution time (seconds)' required: true default: '60' type: string concurrent_users: - description: '동시 사용자 수' + description: 'Number of concurrent users' required: true default: '5' type: string @@ -80,7 +80,7 @@ jobs: sleep 2 done - - name: Locust 성능 테스트 파일 생성 + - name: Create Locust Performance Test File run: | cat > locustfile.py << 'EOF' from locust import HttpUser, task, between @@ -95,7 +95,7 @@ jobs: @task(3) def get_recommendations(self): payload = { - "user_diagnosis": "건성 피부로 수분이 부족해요", + "user_diagnosis": "I have dry skin and lack moisture", "top_k": 10, "max_price": 50000 } @@ -109,7 +109,7 @@ jobs: if response.status_code == 200: response.success() else: - response.failure(f"추천 API 실패: {response.status_code}") + response.failure(f"Recommendation API failed: {response.status_code}") @task(1) def health_check(self): @@ -117,12 +117,12 @@ jobs: if response.status_code == 200: response.success() else: - response.failure(f"헬스체크 실패: {response.status_code}") + response.failure(f"Health check failed: {response.status_code}") EOF - - name: 성능 테스트 실행 + - name: Run Performance Test run: | - echo "🚀 성능 테스트 시작 (사용자: ${{ github.event.inputs.concurrent_users }}, 시간: ${{ github.event.inputs.test_duration }}초)" + echo "🚀 Starting performance test (Users: ${{ github.event.inputs.concurrent_users }}, Time: ${{ github.event.inputs.test_duration }}s)" locust \ --host=http://localhost:8000 \ --users=${{ github.event.inputs.concurrent_users }} \ @@ -130,29 +130,29 @@ jobs: --run-time=${{ github.event.inputs.test_duration }}s \ --headless \ --csv=performance_results \ - --html=performance_report.html || echo "성능 테스트 완료" + --html=performance_report.html || echo "Performance test completed" - - name: 성능 테스트 결과 분석 + - name: Analyze Performance Test Results run: | - echo "=== 성능 테스트 결과 요약 ===" + echo "=== Performance Test Results Summary ===" if [ -f performance_results_stats.csv ]; then - echo "📊 요청 통계:" + echo "📊 Request Statistics:" cat performance_results_stats.csv | head -5 echo "" - echo "❌ 실패 통계:" - cat performance_results_failures.csv 2>/dev/null || echo "실패 없음" + echo "❌ Failure Statistics:" + cat performance_results_failures.csv 2>/dev/null || echo "No failures" else - echo "⚠️ 성능 테스트 결과 파일을 찾을 수 없습니다." + echo "⚠️ Performance test result files not found." fi echo "" - echo "=== 테스트 환경 정보 ===" - echo "- 동시 사용자 수: ${{ github.event.inputs.concurrent_users }}" - echo "- 테스트 시간: ${{ github.event.inputs.test_duration }}초" - echo "- 서버 환경: GitHub Actions (ubuntu-latest)" - echo "- 테스트 대상: Mock FastAPI 서버" + echo "=== Test Environment Information ===" + echo "- Concurrent Users: ${{ github.event.inputs.concurrent_users }}" + echo "- Test Duration: ${{ github.event.inputs.test_duration }}s" + echo "- Server Environment: GitHub Actions (ubuntu-latest)" + echo "- Test Target: Mock FastAPI Server" - - name: 결과 파일 업로드 + - name: Upload Result Files uses: actions/upload-artifact@v4 if: always() with: @@ -161,9 +161,9 @@ jobs: performance_results*.csv performance_report.html - - name: 테스트 완료 정리 + - name: Cleanup Test Environment if: always() run: | - echo "🧹 테스트 환경 정리" - pkill -f "uvicorn" || echo "서버 프로세스 정리 완료" - echo "✅ 성능 테스트가 완료되었습니다!" + echo "🧹 Cleaning up test environment" + pkill -f "uvicorn" || echo "Server process cleanup completed" + echo "✅ Performance test completed!" diff --git a/docker-compose.development.yml b/docker-compose.development.yml index 8a5df5c..678109a 100644 --- a/docker-compose.development.yml +++ b/docker-compose.development.yml @@ -1,12 +1,15 @@ -version: '3.8' - services: ururu-ai: build: target: development environment: - - DEBUG=true - - LOG_LEVEL=DEBUG + - ENVIRONMENT=development + - REDIS_HOST=redis # 개발환경에서는 Redis 컨테이너와 연결 + - REDIS_URL=redis://redis:6379 + - SPRING_BOOT_INTEGRATION_ENABLED=false # 개발환경에서는 Spring Boot 연동 비활성화 + - USE_MOCK_DATA=true # Mock 데이터 사용 + env_file: + - .env.development volumes: - .:/app # 컨테이너 내부 venv 디렉토리 보호 @@ -15,6 +18,16 @@ services: # 개발환경에서만 디버깅용 포트 노출 (선택적) ports: - "${AI_PORT:-8001}:8000" # 개발용으로만 8001 포트 노출 + networks: + - ururu-network + depends_on: + - redis + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s # 개발용 Redis (캐싱 및 세션 저장용) redis: @@ -28,6 +41,11 @@ services: networks: - ururu-network +networks: + ururu-network: + driver: bridge + name: ururu-network + volumes: redis_data: driver: local diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 2e092ed..bcf705b 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -1,17 +1,23 @@ -version: '3.8' - services: ururu-ai: build: target: production environment: - - DEBUG=false - - LOG_LEVEL=INFO - # 운영환경에서는 Spring Boot 서버 IP/도메인으로 설정 - - SPRING_BOOT_BASE_URL=${SPRING_BOOT_BASE_URL:-http://api.ururu.shop} - - GUNICORN_WORKERS=${GUNICORN_WORKERS:-4} + - ENVIRONMENT=production + - SPRING_BOOT_INTEGRATION_ENABLED=true # 운영환경에서는 Spring Boot 연동 필수 + - USE_MOCK_DATA=false # 실제 데이터 사용 + env_file: + - .env.production ports: - "${AI_PORT:-8000}:8000" # Spring Boot 서버에서 접근 가능 + networks: + - ururu-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s deploy: replicas: 2 resources: @@ -39,4 +45,5 @@ services: networks: ururu-network: - driver: bridge \ No newline at end of file + driver: bridge + name: ururu-network \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 28483c6..aaa8360 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: # Private Config 가져오기 서비스 config-fetcher: @@ -10,11 +8,11 @@ services: environment: - GITHUB_TOKEN=${GITHUB_TOKEN} - CONFIG_REPO_URL=https://github.com/UruruLab/Ururu-AI-Config.git - - ENVIRONMENT=${ENVIRONMENT:-development} + - ENVIRONMENT=${ENVIRONMENT:-production} command: /scripts/fetch-config.sh restart: "no" healthcheck: - test: ["CMD", "test", "-f", "/config/.env.${ENVIRONMENT:-development}"] + test: ["CMD", "test", "-f", "/config/.env.${ENVIRONMENT:-production}"] interval: 10s timeout: 5s retries: 3 @@ -25,7 +23,7 @@ services: build: context: . dockerfile: Dockerfile - target: ${BUILD_TARGET:-development} + target: ${BUILD_TARGET:-production} container_name: ururu-ai-service ports: - "${AI_PORT:-8000}:8000" # Spring Boot 서버에서 접근용 @@ -33,20 +31,11 @@ services: - ./logs:/app/logs - ./config:/app/config:ro environment: - - ENVIRONMENT=${ENVIRONMENT:-development} - - DEBUG=${DEBUG:-true} - - LOG_LEVEL=${LOG_LEVEL:-INFO} + - ENVIRONMENT=${ENVIRONMENT:-production} + # 컨테이너에서 호스트의 Spring Boot에 접근하기 위한 설정 - SPRING_BOOT_BASE_URL=${SPRING_BOOT_BASE_URL:-http://host.docker.internal:8080} - - HTTP_TIMEOUT=${HTTP_TIMEOUT:-30} - - HTTP_RETRY_COUNT=${HTTP_RETRY_COUNT:-3} - - HTTP_RETRY_DELAY=${HTTP_RETRY_DELAY:-1} - - GUNICORN_WORKERS=${GUNICORN_WORKERS:-4} - - REDIS_URL=${REDIS_URL:-} - - REDIS_HOST=${REDIS_HOST:-} - - REDIS_PORT=${REDIS_PORT:-} - - REDIS_PASSWORD=${REDIS_PASSWORD:-} env_file: - - ./config/.env.${ENVIRONMENT:-development} + - ./config/.env.${ENVIRONMENT:-production} depends_on: config-fetcher: condition: service_healthy @@ -60,7 +49,7 @@ services: networks: - ururu-network extra_hosts: - - "host.docker.internal:host-gateway" + - "host.docker.internal:host-gateway" # Docker Desktop에서 호스트 접근 가능 logging: driver: "json-file" options: diff --git a/scripts/run-dev.sh b/scripts/run-dev.sh new file mode 100644 index 0000000..7a33cfd --- /dev/null +++ b/scripts/run-dev.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +echo "🚀 개발환경에서 Ururu AI 서비스 시작" +echo "환경: Development" +echo "Spring Boot 연동: 비활성화" +echo "데이터: Mock 데이터 사용" + +export ENVIRONMENT=development +export BUILD_TARGET=development + +docker compose -f docker-compose.development.yml up --build + +echo "✅ 개발환경 서비스가 시작되었습니다." +echo "🌐 AI 서비스 접근: http://localhost:8001" +echo "📚 API 문서: http://localhost:8001/docs" diff --git a/scripts/run-prod.sh b/scripts/run-prod.sh new file mode 100644 index 0000000..09bbda2 --- /dev/null +++ b/scripts/run-prod.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +echo "🚀 운영환경에서 Ururu AI 서비스 시작" +echo "환경: Production" +echo "Spring Boot 연동: 활성화" +echo "데이터: 실제 데이터베이스 사용" + +export ENVIRONMENT=production +export BUILD_TARGET=production + +# Spring Boot 연결 확인 +echo "🔍 Spring Boot 서버 연결 확인 중..." +if curl -f --connect-timeout 5 http://localhost:8080/health 2>/dev/null; then + echo "✅ Spring Boot 서버 연결 확인됨" +else + echo "⚠️ Spring Boot 서버에 연결할 수 없습니다." + echo " Docker 환경에서는 host.docker.internal:8080으로 연결을 시도합니다." +fi + +docker compose up --build + +echo "✅ 운영환경 서비스가 시작되었습니다." +echo "🌐 AI 서비스 접근: http://localhost:8000" +echo "📚 API 문서: http://localhost:8000/docs" +echo "🔗 Spring Boot 연동: 활성화됨" From 1c80bd8e57e0afaef612cdefb74db56822273267 Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 01:20:12 +0900 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20Config=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CI/CD 워크플로우에서 config 파일 존재 여부 확인 후 복사 - 파일이 없을 경우 명확한 오류 메시지와 함께 실패 처리 - 실행 스크립트에서도 환경 파일 존재 여부 사전 검증 - 에러 발생 시 사용자에게 Config 레포지토리 확인 안내 --- .github/workflows/ci-cd.yml | 18 ++++++++++++++---- .github/workflows/docker-health-check.yml | 9 +++++++-- scripts/run-dev.sh | 7 +++++++ scripts/run-prod.sh | 7 +++++++ 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b86f59f..b18afa8 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -120,8 +120,13 @@ jobs: - name: Copy Development Environment Config Files run: | - cp config/.env.development .env.development - echo "✅ Development environment config files copied successfully" + if [ -f "config/.env.development" ]; then + cp config/.env.development .env.development + echo "✅ Development environment config files copied successfully" + else + echo "❌ .env.development not found in config repository" + exit 1 + fi - name: Validate Docker Compose Configuration run: | @@ -158,8 +163,13 @@ jobs: - name: Copy Production Environment Config Files run: | - cp config/.env.production .env.production - echo "✅ Production environment config files copied successfully" + if [ -f "config/.env.production" ]; then + cp config/.env.production .env.production + echo "✅ Production environment config files copied successfully" + else + echo "❌ .env.production not found in config repository" + exit 1 + fi - name: Validate Docker Compose Configuration run: | diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index cbca236..7d27ec3 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -31,8 +31,13 @@ jobs: - name: Copy Config Files (.env files) run: | mkdir -p ./ - cp config/.env* ./ - echo "✅ Config files copied successfully" + if [ -f "config/.env" ]; then + cp config/.env* ./ + echo "✅ Config files copied successfully" + else + echo "❌ Config files not found in config repository" + exit 1 + fi - name: Verify Environment Files run: | diff --git a/scripts/run-dev.sh b/scripts/run-dev.sh index 7a33cfd..f7b07ae 100644 --- a/scripts/run-dev.sh +++ b/scripts/run-dev.sh @@ -8,6 +8,13 @@ echo "데이터: Mock 데이터 사용" export ENVIRONMENT=development export BUILD_TARGET=development +# 환경 파일 존재 확인 +if [ ! -f ".env.development" ]; then + echo "❌ .env.development 파일을 찾을 수 없습니다." + echo "💡 Config 레포지토리에서 환경 파일을 가져와주세요." + exit 1 +fi + docker compose -f docker-compose.development.yml up --build echo "✅ 개발환경 서비스가 시작되었습니다." diff --git a/scripts/run-prod.sh b/scripts/run-prod.sh index 09bbda2..0ab555e 100644 --- a/scripts/run-prod.sh +++ b/scripts/run-prod.sh @@ -8,6 +8,13 @@ echo "데이터: 실제 데이터베이스 사용" export ENVIRONMENT=production export BUILD_TARGET=production +# 환경 파일 존재 확인 +if [ ! -f ".env.production" ]; then + echo "❌ .env.production 파일을 찾을 수 없습니다." + echo "💡 Config 레포지토리에서 환경 파일을 가져와주세요." + exit 1 +fi + # Spring Boot 연결 확인 echo "🔍 Spring Boot 서버 연결 확인 중..." if curl -f --connect-timeout 5 http://localhost:8080/health 2>/dev/null; then From ffee6286628c7794104d13c7ccab5f92d010cf65 Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 01:29:59 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=A0=88=EB=B9=97?= =?UTF-8?q?=20=EC=A0=9C=EC=95=88=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81=20?= =?UTF-8?q?-=20CI/CD=20=ED=92=88=EC=A7=88=20=EA=B2=80=EC=A6=9D=20=EA=B0=95?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - flake8 --exit-zero 옵션 제거로 fail-fast 동작 활성화 - compgen -G 'config/.env*' 사용해서 모든 환경 파일 존재 여부 확인 - 중요한 구문 오류 발생 시 즉시 빌드 실패로 품질 보장 - Config 파일 검증 로직 개선으로 런타임 오류 방지 --- .github/workflows/ci-cd.yml | 28 +++++++++++++++-------- .github/workflows/docker-health-check.yml | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b18afa8..b8ba4f4 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -49,7 +49,7 @@ jobs: - name: Code Quality Check run: | # Check only critical syntax errors - flake8 app/ --count --select=E9,F63,F7,F82 --show-source --statistics --exit-zero + flake8 app/ --count --select=E9,F63,F7,F82 --show-source --statistics echo "Code quality check completed" - name: Run Tests @@ -120,11 +120,16 @@ jobs: - name: Copy Development Environment Config Files run: | - if [ -f "config/.env.development" ]; then - cp config/.env.development .env.development - echo "✅ Development environment config files copied successfully" + if compgen -G "config/.env*" > /dev/null; then + if [ -f "config/.env.development" ]; then + cp config/.env.development .env.development + echo "✅ Development environment config files copied successfully" + else + echo "❌ .env.development not found in config repository" + exit 1 + fi else - echo "❌ .env.development not found in config repository" + echo "❌ No config files found in config repository" exit 1 fi @@ -163,11 +168,16 @@ jobs: - name: Copy Production Environment Config Files run: | - if [ -f "config/.env.production" ]; then - cp config/.env.production .env.production - echo "✅ Production environment config files copied successfully" + if compgen -G "config/.env*" > /dev/null; then + if [ -f "config/.env.production" ]; then + cp config/.env.production .env.production + echo "✅ Production environment config files copied successfully" + else + echo "❌ .env.production not found in config repository" + exit 1 + fi else - echo "❌ .env.production not found in config repository" + echo "❌ No config files found in config repository" exit 1 fi diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index 7d27ec3..e0c3d9d 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -31,7 +31,7 @@ jobs: - name: Copy Config Files (.env files) run: | mkdir -p ./ - if [ -f "config/.env" ]; then + if compgen -G "config/.env*" > /dev/null; then cp config/.env* ./ echo "✅ Config files copied successfully" else From 4cd6b26092e4ddb6f1aac0acb8d5be2409b8c4ce Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 01:46:06 +0900 Subject: [PATCH 7/8] =?UTF-8?q?GitHub=20Actions=20=EC=9B=8C=ED=81=AC?= =?UTF-8?q?=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EA=B0=9C=EC=84=A0=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-cd.yml | 302 +++++++++++----------- .github/workflows/docker-health-check.yml | 160 ++++++------ .github/workflows/performance-test.yml | 290 ++++++++++----------- 3 files changed, 377 insertions(+), 375 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b8ba4f4..0849507 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -22,43 +22,43 @@ jobs: GITHUB_ACTIONS: true steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Setup Python Environment - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Cache Dependencies - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - # Install test tools first (including pytest-env) - pip install pytest==8.3.4 pytest-asyncio==0.25.0 pytest-env==1.1.5 flake8==7.1.1 black==25.1.0 isort==5.13.2 - # Then install project dependencies - pip install -r requirements.txt - - - name: Code Quality Check - run: | - # Check only critical syntax errors - flake8 app/ --count --select=E9,F63,F7,F82 --show-source --statistics - echo "Code quality check completed" - - - name: Run Tests - run: | - export APP_NAME="Ururu AI Recommendation System" - export EMBEDDING_MODEL_NAME="sentence-transformers/all-MiniLM-L6-v2" - export EMBEDDING_DIMENSION="384" - python -m pytest tests/ -v --tb=short - continue-on-error: false + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Python Environment + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Cache Dependencies + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + # Install test tools first (including pytest-env) + pip install pytest==8.3.4 pytest-asyncio==0.25.0 pytest-env==1.1.5 flake8==7.1.1 black==25.1.0 isort==5.13.2 + # Then install project dependencies + pip install -r requirements.txt + + - name: Code Quality Check + run: | + # Check only critical syntax errors + flake8 app/ --count --select=E9,F63,F7,F82 --show-source --statistics + echo "Code quality check completed" + + - name: Run Tests + run: | + export APP_NAME="Ururu AI Recommendation System" + export EMBEDDING_MODEL_NAME="sentence-transformers/all-MiniLM-L6-v2" + export EMBEDDING_DIMENSION="384" + python -m pytest tests/ -v --tb=short + continue-on-error: false build-and-push: needs: test @@ -66,40 +66,40 @@ jobs: if: github.event_name == 'push' steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ secrets.GHCR_USERNAME }} - password: ${{ secrets.GHCR_TOKEN }} - - - name: Extract Metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=sha,prefix=sha- - type=raw,value=latest,enable={{is_default_branch}} - - - name: Build and Push Docker Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} + + - name: Extract Metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix=sha- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and Push Docker Image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max deploy-development: needs: build-and-push @@ -108,46 +108,46 @@ jobs: environment: development steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Checkout Config Repository - uses: actions/checkout@v4 - with: - repository: UruruLab/Ururu-AI-Config - path: config - token: ${{ secrets.PRIVATE_REPO_TOKEN }} - - - name: Copy Development Environment Config Files - run: | - if compgen -G "config/.env*" > /dev/null; then - if [ -f "config/.env.development" ]; then - cp config/.env.development .env.development - echo "✅ Development environment config files copied successfully" + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Checkout Config Repository + uses: actions/checkout@v4 + with: + repository: UruruLab/Ururu-AI-Config + path: config + token: ${{ secrets.PRIVATE_REPO_TOKEN }} + + - name: Copy Development Environment Config Files + run: | + if compgen -G "config/.env*" > /dev/null; then + if [ -f "config/.env.development" ]; then + cp config/.env.development .env.development + echo "✅ Development environment config files copied successfully" + else + echo "❌ .env.development not found in config repository" + exit 1 + fi else - echo "❌ .env.development not found in config repository" + echo "❌ No config files found in config repository" exit 1 fi - else - echo "❌ No config files found in config repository" - exit 1 - fi - - - name: Validate Docker Compose Configuration - run: | - echo "Validating development environment configuration files" - echo "Validating docker-compose.development.yml syntax" - ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet - echo "Verifying environment variable bindings" - ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 10 "environment:" || true - echo "Development environment deployment preparation completed" - - - name: Simulate Deployment (No actual EC2 deployment) - run: | - echo "Development environment deployment simulation" - echo "- Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop" - echo "- Config File: .env.development (fetched from Config repository)" - echo "Development environment deployment configuration completed" + + - name: Validate Docker Compose Configuration + run: | + echo "Validating development environment configuration files" + echo "Validating docker-compose.development.yml syntax" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet + echo "Verifying environment variable bindings" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 10 "environment:" || true + echo "Development environment deployment preparation completed" + + - name: Simulate Deployment (No actual EC2 deployment) + run: | + echo "Development environment deployment simulation" + echo "- Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop" + echo "- Config File: .env.development (fetched from Config repository)" + echo "Development environment deployment configuration completed" deploy-production: needs: build-and-push @@ -156,50 +156,50 @@ jobs: environment: production steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Checkout Config Repository - uses: actions/checkout@v4 - with: - repository: UruruLab/Ururu-AI-Config - path: config - token: ${{ secrets.PRIVATE_REPO_TOKEN }} - - - name: Copy Production Environment Config Files - run: | - if compgen -G "config/.env*" > /dev/null; then - if [ -f "config/.env.production" ]; then - cp config/.env.production .env.production - echo "✅ Production environment config files copied successfully" + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Checkout Config Repository + uses: actions/checkout@v4 + with: + repository: UruruLab/Ururu-AI-Config + path: config + token: ${{ secrets.PRIVATE_REPO_TOKEN }} + + - name: Copy Production Environment Config Files + run: | + if compgen -G "config/.env*" > /dev/null; then + if [ -f "config/.env.production" ]; then + cp config/.env.production .env.production + echo "✅ Production environment config files copied successfully" + else + echo "❌ .env.production not found in config repository" + exit 1 + fi else - echo "❌ .env.production not found in config repository" + echo "❌ No config files found in config repository" exit 1 fi - else - echo "❌ No config files found in config repository" - exit 1 - fi - - name: Validate Docker Compose Configuration - run: | - echo "Validating production environment configuration files" - echo "Validating docker-compose.production.yml syntax" - ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet - echo "Verifying environment variable bindings" - ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 10 "environment:" || true - echo "Production environment deployment preparation completed" - - - name: Prepare Deployment Notification - run: | - echo "Production environment deployment preparation completed" - echo "- Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" - echo "- Config File: .env.production (fetched from Config repository)" - echo "Actual EC2 deployment will be handled by separate process" - - - name: Deployment Completion Notification - if: success() - run: | - echo "GitHub Actions deployment pipeline completed successfully." - echo "Docker image has been pushed to GitHub Container Registry." - echo "Manual execution required on EC2 server: docker compose pull && docker compose up -d" + - name: Validate Docker Compose Configuration + run: | + echo "Validating production environment configuration files" + echo "Validating docker-compose.production.yml syntax" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet + echo "Verifying environment variable bindings" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 10 "environment:" || true + echo "Production environment deployment preparation completed" + + - name: Prepare Deployment Notification + run: | + echo "Production environment deployment preparation completed" + echo "- Docker Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" + echo "- Config File: .env.production (fetched from Config repository)" + echo "Actual EC2 deployment will be handled by separate process" + + - name: Deployment Completion Notification + if: success() + run: | + echo "GitHub Actions deployment pipeline completed successfully." + echo "Docker image has been pushed to GitHub Container Registry." + echo "Manual execution required on EC2 server: docker compose pull && docker compose up -d" diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index e0c3d9d..1906686 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -18,82 +18,84 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Checkout Config Repository - uses: actions/checkout@v4 - with: - repository: UruruLab/Ururu-AI-Config - path: config - token: ${{ secrets.PRIVATE_REPO_TOKEN }} - - - name: Copy Config Files (.env files) - run: | - mkdir -p ./ - if compgen -G "config/.env*" > /dev/null; then - cp config/.env* ./ - echo "✅ Config files copied successfully" - else - echo "❌ Config files not found in config repository" - exit 1 - fi - - - name: Verify Environment Files - run: | - echo "📁 Checking copied environment files" - ls -la .env* - if [ -f ".env.development" ]; then - echo "✅ .env.development file exists" - else - echo "❌ .env.development file missing" - fi - if [ -f ".env.production" ]; then - echo "✅ .env.production file exists" - else - echo "❌ .env.production file missing" - fi - - - name: Validate Docker Compose Configuration - run: | - echo "✅ Validating Docker Compose file syntax" - docker compose config --quiet - echo "✅ Validating development environment (using copied config files)" - ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet - echo "✅ Validating production environment (using copied config files)" - ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet - - - name: Verify Environment Variable Bindings - run: | - echo "🔍 Verifying environment variable bindings" - echo "Development environment key variables:" - ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 20 "environment:" | head -15 || true - echo "" - echo "Production environment key variables:" - ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 20 "environment:" | head -15 || true - - - name: Simulate Health Check - run: | - echo "🔍 AI Service Health Check Simulation" - echo "- Target: http://localhost:8000/health" - echo "- Expected Response: {\"status\": \"healthy\", \"service\": \"ururu-ai-recommendation\"}" - - echo "🔍 Spring Backend Connection Test Simulation" - echo "- Target: http://localhost:8080/health" - echo "- Actual connection only available in EC2 environment" - - - name: Check GitHub Container Registry Images - run: | - echo "📦 Checking latest Docker images" - echo "- Registry: ghcr.io/${{ github.repository }}" - echo "- Latest tags: latest, main, develop" - - - name: Generate Health Check Report - run: | - echo "📊 Health Check Completion Report" - echo "✅ Docker Compose configuration files validated" - echo "✅ Config repository integration working" - echo "✅ Environment-specific configuration files verified" - echo "✅ Workflow configuration validated" - echo "️ Actual service status needs separate verification on EC2" - echo "Run on EC2: docker compose ps && docker compose logs" + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Checkout Config Repository + uses: actions/checkout@v4 + with: + repository: UruruLab/Ururu-AI-Config + path: config + token: ${{ secrets.PRIVATE_REPO_TOKEN }} + + - name: Copy Config Files (.env files) + run: | + mkdir -p ./ + if compgen -G "config/.env*" > /dev/null; then + cp config/.env* ./ + echo "✅ Config files copied successfully" + else + echo "❌ Config files not found in config repository" + exit 1 + fi + + - name: Verify Environment Files + run: | + echo "📁 Checking copied environment files" + ls -la .env* + if [ -f ".env.development" ]; then + echo "✅ .env.development file exists" + else + echo "❌ .env.development file missing" + exit 1 + fi + if [ -f ".env.production" ]; then + echo "✅ .env.production file exists" + else + echo "❌ .env.production file missing" + exit 1 + fi + + - name: Validate Docker Compose Configuration + run: | + echo "✅ Validating Docker Compose file syntax" + docker compose config --quiet + echo "✅ Validating development environment (using copied config files)" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet + echo "✅ Validating production environment (using copied config files)" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet + + - name: Verify Environment Variable Bindings + run: | + echo "🔍 Verifying environment variable bindings" + echo "Development environment key variables:" + ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 20 "environment:" | head -15 || true + echo "" + echo "Production environment key variables:" + ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 20 "environment:" | head -15 || true + + - name: Simulate Health Check + run: | + echo "🔍 AI Service Health Check Simulation" + echo "- Target: http://localhost:8000/health" + echo "- Expected Response: {\"status\": \"healthy\", \"service\": \"ururu-ai-recommendation\"}" + + echo "🔍 Spring Backend Connection Test Simulation" + echo "- Target: http://localhost:8080/health" + echo "- Actual connection only available in EC2 environment" + + - name: Check GitHub Container Registry Images + run: | + echo "📦 Checking latest Docker images" + echo "- Registry: ghcr.io/${{ github.repository }}" + echo "- Latest tags: latest, main, develop" + + - name: Generate Health Check Report + run: | + echo "📊 Health Check Completion Report" + echo "✅ Docker Compose configuration files validated" + echo "✅ Config repository integration working" + echo "✅ Environment-specific configuration files verified" + echo "✅ Workflow configuration validated" + echo "️ Actual service status needs separate verification on EC2" + echo "Run on EC2: docker compose ps && docker compose logs" diff --git a/.github/workflows/performance-test.yml b/.github/workflows/performance-test.yml index c1157c7..1f56844 100644 --- a/.github/workflows/performance-test.yml +++ b/.github/workflows/performance-test.yml @@ -19,151 +19,151 @@ jobs: runs-on: ubuntu-latest steps: - - name: 코드 체크아웃 - uses: actions/checkout@v4 - - - name: Python 환경 설정 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: 성능 테스트 도구 설치 - run: | - pip install locust==2.31.0 requests==2.32.3 fastapi==0.111.0 uvicorn==0.30.0 - - - name: 테스트용 FastAPI 서버 시작 - run: | - # 간단한 테스트 서버 생성 - cat > test_server.py << 'EOF' - from fastapi import FastAPI - import uvicorn - import asyncio - - app = FastAPI(title="Test Ururu AI Server") - - @app.get("/health") - async def health_check(): - return {"status": "healthy", "service": "ururu-ai-test"} - - @app.post("/api/v1/recommendations") - async def mock_recommendations(): - await asyncio.sleep(0.1) # 실제 AI 처리 시간 시뮬레이션 - return { - "recommendations": [ - {"product_id": 1, "score": 0.95, "name": "테스트 상품 1"}, - {"product_id": 2, "score": 0.89, "name": "테스트 상품 2"} - ], - "total_count": 2, - "processing_time_ms": 100 - } - EOF - - # 백그라운드에서 서버 실행 - python -c " - import uvicorn - import sys - sys.path.append('.') - uvicorn.run('test_server:app', host='0.0.0.0', port=8000, log_level='warning') - " & - - # 서버 시작 대기 - sleep 10 - - - name: 서버 준비 상태 확인 - run: | - for i in {1..30}; do - if curl -f http://localhost:8000/health; then - echo "✅ 테스트 서버가 준비되었습니다." - break + - name: 코드 체크아웃 + uses: actions/checkout@v4 + + - name: Python 환경 설정 + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: 성능 테스트 도구 설치 + run: | + pip install locust==2.31.0 requests==2.32.3 fastapi==0.111.0 uvicorn==0.30.0 + + - name: 테스트용 FastAPI 서버 시작 + run: | + # 간단한 테스트 서버 생성 + cat > test_server.py << 'EOF' + from fastapi import FastAPI + import uvicorn + import asyncio + + app = FastAPI(title="Test Ururu AI Server") + + @app.get("/health") + async def health_check(): + return {"status": "healthy", "service": "ururu-ai-test"} + + @app.post("/api/v1/recommendations") + async def mock_recommendations(): + await asyncio.sleep(0.1) # 실제 AI 처리 시간 시뮬레이션 + return { + "recommendations": [ + {"product_id": 1, "score": 0.95, "name": "테스트 상품 1"}, + {"product_id": 2, "score": 0.89, "name": "테스트 상품 2"} + ], + "total_count": 2, + "processing_time_ms": 100 + } + EOF + + # 백그라운드에서 서버 실행 + python -c " + import uvicorn + import sys + sys.path.append('.') + uvicorn.run('test_server:app', host='0.0.0.0', port=8000, log_level='warning') + " & + + # 서버 시작 대기 + sleep 10 + + - name: 서버 준비 상태 확인 + run: | + for i in {1..30}; do + if curl -f http://localhost:8000/health; then + echo "✅ 테스트 서버가 준비되었습니다." + break + fi + echo "테스트 서버 시작을 기다리는 중... ($i/30)" + sleep 2 + done + + - name: Create Locust Performance Test File + run: | + cat > locustfile.py << 'EOF' + from locust import HttpUser, task, between + import json + + class UruruAITestUser(HttpUser): + wait_time = between(1, 3) + + def on_start(self): + self.client.verify = False + + @task(3) + def get_recommendations(self): + payload = { + "user_diagnosis": "I have dry skin and lack moisture", + "top_k": 10, + "max_price": 50000 + } + + with self.client.post( + "/api/v1/recommendations", + json=payload, + headers={"Content-Type": "application/json"}, + catch_response=True + ) as response: + if response.status_code == 200: + response.success() + else: + response.failure(f"Recommendation API failed: {response.status_code}") + + @task(1) + def health_check(self): + with self.client.get("/health", catch_response=True) as response: + if response.status_code == 200: + response.success() + else: + response.failure(f"Health check failed: {response.status_code}") + EOF + + - name: Run Performance Test + run: | + echo "🚀 Starting performance test (Users: ${{ github.event.inputs.concurrent_users }}, Time: ${{ github.event.inputs.test_duration }}s)" + locust \ + --host=http://localhost:8000 \ + --users=${{ github.event.inputs.concurrent_users }} \ + --spawn-rate=1 \ + --run-time=${{ github.event.inputs.test_duration }}s \ + --headless \ + --csv=performance_results \ + --html=performance_report.html || echo "Performance test completed" + + - name: Analyze Performance Test Results + run: | + echo "=== Performance Test Results Summary ===" + if [ -f performance_results_stats.csv ]; then + echo "📊 Request Statistics:" + cat performance_results_stats.csv | head -5 + echo "" + echo "❌ Failure Statistics:" + cat performance_results_failures.csv 2>/dev/null || echo "No failures" + else + echo "⚠️ Performance test result files not found." fi - echo "테스트 서버 시작을 기다리는 중... ($i/30)" - sleep 2 - done - - - name: Create Locust Performance Test File - run: | - cat > locustfile.py << 'EOF' - from locust import HttpUser, task, between - import json - - class UruruAITestUser(HttpUser): - wait_time = between(1, 3) - - def on_start(self): - self.client.verify = False - - @task(3) - def get_recommendations(self): - payload = { - "user_diagnosis": "I have dry skin and lack moisture", - "top_k": 10, - "max_price": 50000 - } - - with self.client.post( - "/api/v1/recommendations", - json=payload, - headers={"Content-Type": "application/json"}, - catch_response=True - ) as response: - if response.status_code == 200: - response.success() - else: - response.failure(f"Recommendation API failed: {response.status_code}") - - @task(1) - def health_check(self): - with self.client.get("/health", catch_response=True) as response: - if response.status_code == 200: - response.success() - else: - response.failure(f"Health check failed: {response.status_code}") - EOF - - - name: Run Performance Test - run: | - echo "🚀 Starting performance test (Users: ${{ github.event.inputs.concurrent_users }}, Time: ${{ github.event.inputs.test_duration }}s)" - locust \ - --host=http://localhost:8000 \ - --users=${{ github.event.inputs.concurrent_users }} \ - --spawn-rate=1 \ - --run-time=${{ github.event.inputs.test_duration }}s \ - --headless \ - --csv=performance_results \ - --html=performance_report.html || echo "Performance test completed" - - name: Analyze Performance Test Results - run: | - echo "=== Performance Test Results Summary ===" - if [ -f performance_results_stats.csv ]; then - echo "📊 Request Statistics:" - cat performance_results_stats.csv | head -5 echo "" - echo "❌ Failure Statistics:" - cat performance_results_failures.csv 2>/dev/null || echo "No failures" - else - echo "⚠️ Performance test result files not found." - fi - - echo "" - echo "=== Test Environment Information ===" - echo "- Concurrent Users: ${{ github.event.inputs.concurrent_users }}" - echo "- Test Duration: ${{ github.event.inputs.test_duration }}s" - echo "- Server Environment: GitHub Actions (ubuntu-latest)" - echo "- Test Target: Mock FastAPI Server" - - - name: Upload Result Files - uses: actions/upload-artifact@v4 - if: always() - with: - name: performance-test-results-${{ github.run_number }} - path: | - performance_results*.csv - performance_report.html - - - name: Cleanup Test Environment - if: always() - run: | - echo "🧹 Cleaning up test environment" - pkill -f "uvicorn" || echo "Server process cleanup completed" - echo "✅ Performance test completed!" + echo "=== Test Environment Information ===" + echo "- Concurrent Users: ${{ github.event.inputs.concurrent_users }}" + echo "- Test Duration: ${{ github.event.inputs.test_duration }}s" + echo "- Server Environment: GitHub Actions (ubuntu-latest)" + echo "- Test Target: Mock FastAPI Server" + + - name: Upload Result Files + uses: actions/upload-artifact@v4 + if: always() + with: + name: performance-test-results-${{ github.run_number }} + path: | + performance_results*.csv + performance_report.html + + - name: Cleanup Test Environment + if: always() + run: | + echo "🧹 Cleaning up test environment" + pkill -f "uvicorn" || echo "Server process cleanup completed" + echo "✅ Performance test completed!" From 3738a8dd780843134c23ca8a26cb18c4fd3dfd50 Mon Sep 17 00:00:00 2001 From: 23MinL Date: Sat, 12 Jul 2025 02:04:35 +0900 Subject: [PATCH 8/8] =?UTF-8?q?GitHub=20Actions=20=EB=B3=B4=EC=95=88=20?= =?UTF-8?q?=EA=B0=95=ED=99=94=20=EB=B0=8F=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EB=85=B8=EC=B6=9C=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docker-health-check.yml: 환경변수 바인딩 검증 시 sed 처리 추가 - ci-cd.yml: development/production 환경 모두에서 환경변수 마스킹 적용 - 공개 로그에서 민감한 환경변수 값 노출 방지 --- .github/workflows/ci-cd.yml | 10 ++++++++-- .github/workflows/docker-health-check.yml | 12 ++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 0849507..896921e 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -139,7 +139,10 @@ jobs: echo "Validating docker-compose.development.yml syntax" ENVIRONMENT=development docker compose -f docker-compose.development.yml config --quiet echo "Verifying environment variable bindings" - ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 10 "environment:" || true + ENVIRONMENT=development docker compose -f docker-compose.development.yml config \ + | grep -A 10 "environment:" \ + | grep "^[[:space:]]*[[:alpha:]]" \ + | sed 's/.*$/&/' || true echo "Development environment deployment preparation completed" - name: Simulate Deployment (No actual EC2 deployment) @@ -187,7 +190,10 @@ jobs: echo "Validating docker-compose.production.yml syntax" ENVIRONMENT=production docker compose -f docker-compose.production.yml config --quiet echo "Verifying environment variable bindings" - ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 10 "environment:" || true + ENVIRONMENT=production docker compose -f docker-compose.production.yml config \ + | grep -A 10 "environment:" \ + | grep "^[[:space:]]*[[:alpha:]]" \ + | sed 's/.*$/&/' || true echo "Production environment deployment preparation completed" - name: Prepare Deployment Notification diff --git a/.github/workflows/docker-health-check.yml b/.github/workflows/docker-health-check.yml index 1906686..3164eac 100644 --- a/.github/workflows/docker-health-check.yml +++ b/.github/workflows/docker-health-check.yml @@ -69,10 +69,18 @@ jobs: run: | echo "🔍 Verifying environment variable bindings" echo "Development environment key variables:" - ENVIRONMENT=development docker compose -f docker-compose.development.yml config | grep -A 20 "environment:" | head -15 || true + ENVIRONMENT=development docker compose -f docker-compose.development.yml config \ + | grep -A 20 "environment:" \ + | grep "^[[:space:]]*[[:alpha:]]" \ + | sed 's/.*$/&/' \ + | head -10 echo "" echo "Production environment key variables:" - ENVIRONMENT=production docker compose -f docker-compose.production.yml config | grep -A 20 "environment:" | head -15 || true + ENVIRONMENT=production docker compose -f docker-compose.production.yml config \ + | grep -A 20 "environment:" \ + | grep "^[[:space:]]*[[:alpha:]]" \ + | sed 's/.*$/&/' \ + | head -10 - name: Simulate Health Check run: |