Merge pull request #125 from Yoonhojoon/hotfix/redis #202
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Terraform CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| jobs: | |
| terraform: | |
| runs-on: ubuntu-22.04 | |
| defaults: | |
| run: | |
| working-directory: infrastructure | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: 1.6.6 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ap-northeast-2 | |
| - name: Create S3 bucket if not exists | |
| run: | | |
| aws s3api head-bucket --bucket mclass-terraform-state-20250731 2>/dev/null || aws s3 mb s3://mclass-terraform-state-20250731 --region ap-northeast-2 | |
| aws s3api put-bucket-versioning --bucket mclass-terraform-state-20250731 --versioning-configuration Status=Enabled | |
| aws dynamodb describe-table --table-name terraform-lock --region ap-northeast-2 2>/dev/null || aws dynamodb create-table --table-name terraform-lock --attribute-definitions AttributeName=LockID,AttributeType=S --key-schema AttributeName=LockID,KeyType=HASH --billing-mode PAY_PER_REQUEST --region ap-northeast-2 | |
| - name: Terraform Init | |
| run: terraform init | |
| - name: Terraform Format Check | |
| run: terraform fmt -check | |
| - name: Terraform Validate | |
| run: terraform validate | |
| - name: Create terraform.tfvars from ENV_PROD | |
| run: | | |
| # 민감한 정보를 환경 변수로 설정하여 마스킹 (개행/공백 제거) | |
| echo "${{ secrets.ENV_PROD }}" | tr -d '\r' | sed 's/[[:space:]]*$//' > .env.prod | |
| # 환경 변수들을 추출하여 마스킹된 형태로 로그 출력 | |
| echo "🔍 환경변수 추출 중..." | |
| DATABASE_PASSWORD=$(grep DATABASE_PASSWORD .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| DATABASE_URL=$(grep DATABASE_URL .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| JWT_SECRET=$(grep JWT_SECRET .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| REDIS_URL=$(grep REDIS_URL .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| KAKAO_CLIENT_ID=$(grep KAKAO_CLIENT_ID .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| KAKAO_CLIENT_SECRET=$(grep KAKAO_CLIENT_SECRET .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| GOOGLE_CLIENT_ID=$(grep GOOGLE_CLIENT_ID .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| GOOGLE_CLIENT_SECRET=$(grep GOOGLE_CLIENT_SECRET .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| NAVER_CLIENT_ID=$(grep NAVER_CLIENT_ID .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| NAVER_CLIENT_SECRET=$(grep NAVER_CLIENT_SECRET .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| INITIAL_ADMIN_EMAIL=$(grep INITIAL_ADMIN_EMAIL .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| INITIAL_ADMIN_PASSWORD=$(grep INITIAL_ADMIN_PASSWORD .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| INITIAL_ADMIN_NAME=$(grep INITIAL_ADMIN_NAME .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| EMAIL_HOST=$(grep EMAIL_HOST .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| EMAIL_USER=$(grep EMAIL_USER .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| EMAIL_PASS=$(grep EMAIL_PASS .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| EMAIL_FROM=$(grep EMAIL_FROM .env.prod | cut -d '=' -f2- | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| # METRICS_TOKEN 자동 생성 (64자리 랜덤 문자열) | |
| METRICS_TOKEN=$(openssl rand -hex 32) | |
| # DATABASE_URL 개행/공백 제거 확인 및 로그 | |
| echo "🔍 DATABASE_URL 형식 검증:" | |
| echo "원본 DATABASE_URL 길이: ${#DATABASE_URL}" | |
| CLEAN_DATABASE_URL=$(echo "$DATABASE_URL" | tr -d '\r' | sed 's/[[:space:]]*$//') | |
| echo "정리된 DATABASE_URL 길이: ${#CLEAN_DATABASE_URL}" | |
| if [ "$DATABASE_URL" != "$CLEAN_DATABASE_URL" ]; then | |
| echo "⚠️ DATABASE_URL에서 개행/공백이 제거되었습니다" | |
| DATABASE_URL="$CLEAN_DATABASE_URL" | |
| else | |
| echo "✅ DATABASE_URL 형식이 올바릅니다" | |
| fi | |
| # 민감한 정보를 마스킹하여 로그 출력 | |
| echo "DATABASE_PASSWORD: ${DATABASE_PASSWORD:0:4}***" | |
| echo "DATABASE_URL: ${DATABASE_URL:0:20}***" | |
| echo "JWT_SECRET: ${JWT_SECRET:0:8}***" | |
| echo "REDIS_URL: ${REDIS_URL:0:20}***" | |
| echo "KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID:0:10}***" | |
| echo "GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:0:10}***" | |
| echo "NAVER_CLIENT_ID: ${NAVER_CLIENT_ID:0:10}***" | |
| echo "INITIAL_ADMIN_EMAIL: ${INITIAL_ADMIN_EMAIL:0:10}***" | |
| echo "INITIAL_ADMIN_NAME: ${INITIAL_ADMIN_NAME:0:5}***" | |
| echo "METRICS_TOKEN: ${METRICS_TOKEN:0:16}***" | |
| # .env.prod 파일에서 환경변수들을 추출하여 terraform.tfvars 생성 | |
| cat > terraform.tfvars << EOF | |
| database_password = "$DATABASE_PASSWORD" | |
| database_url = "$DATABASE_URL" | |
| jwt_secret = "$JWT_SECRET" | |
| redis_url = "$REDIS_URL" | |
| kakao_client_id = "$KAKAO_CLIENT_ID" | |
| kakao_client_secret = "$KAKAO_CLIENT_SECRET" | |
| google_client_id = "$GOOGLE_CLIENT_ID" | |
| google_client_secret = "$GOOGLE_CLIENT_SECRET" | |
| naver_client_id = "$NAVER_CLIENT_ID" | |
| naver_client_secret = "$NAVER_CLIENT_SECRET" | |
| initial_admin_email = "$INITIAL_ADMIN_EMAIL" | |
| initial_admin_password = "$INITIAL_ADMIN_PASSWORD" | |
| initial_admin_name = "$INITIAL_ADMIN_NAME" | |
| metrics_token = "$METRICS_TOKEN" | |
| email_host = "$EMAIL_HOST" | |
| email_user = "$EMAIL_USER" | |
| email_pass = "$EMAIL_PASS" | |
| email_from = "$EMAIL_FROM" | |
| EOF | |
| # 민감한 정보가 포함된 .env.prod 파일 삭제 | |
| rm -f .env.prod | |
| echo "✅ ENV_PROD에서 환경변수 추출 완료" | |
| echo "📋 terraform.tfvars 파일이 생성되었습니다." | |
| - name: Terraform Plan | |
| run: terraform plan -no-color -lock=false -var-file=terraform.tfvars | |
| # 실제 배포는 main 브랜치에서만 실행되도록 조건 분기 | |
| - name: Terraform Apply | |
| if: github.ref == 'refs/heads/main' | |
| run: terraform apply -auto-approve -lock=false -var-file=terraform.tfvars | |
| - name: Show Terraform Outputs | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| echo "🚀 Terraform으로 생성된 리소스들:" | |
| echo "📦 S3 Bucket: mclass-terraform-state-20250731" | |
| echo "🔒 DynamoDB Table: terraform-lock" | |
| echo "🏗️ VPC: $(terraform output -raw vpc_id 2>/dev/null || echo 'N/A')" | |
| echo "🌐 ALB DNS: $(terraform output -raw alb_dns_name 2>/dev/null || echo 'N/A')" | |
| echo "📦 ECR Repository: $(terraform output -raw ecr_repository_url 2>/dev/null || echo 'N/A')" | |
| echo "🔗 RDS Endpoint: $(terraform output -raw rds_endpoint 2>/dev/null || echo 'N/A')" | |
| echo "🔗 ECS Cluster: mclass-cluster" | |
| echo "⚙️ ECS Service: mclass-service" | |
| echo "📋 Task Definition: mclass-task" | |
| echo "" | |
| echo "✅ 모든 인프라가 Terraform으로 관리됩니다!" | |
| # Prometheus 설정은 이미 prometheus.yml에 하드코딩되어 있음 | |
| - name: Verify AWS Resources | |
| if: github.ref == 'refs/heads/main' | |
| run: | | |
| echo "🔍 AWS에서 실제 생성된 리소스 확인:" | |
| echo "" | |
| echo "📦 ECR Repository:" | |
| aws ecr describe-repositories --repository-names mclass-server --region ap-northeast-2 --query 'repositories[0].repositoryUri' --output text || echo "❌ ECR Repository not found" | |
| echo "" | |
| echo "🔗 ECS Cluster:" | |
| aws ecs describe-clusters --clusters mclass-cluster --region ap-northeast-2 --query 'clusters[0].clusterName' --output text || echo "❌ ECS Cluster not found" | |
| echo "" | |
| echo "⚙️ ECS Service:" | |
| aws ecs describe-services --cluster mclass-cluster --services mclass-service --region ap-northeast-2 --query 'services[0].serviceName' --output text || echo "❌ ECS Service not found" | |
| echo "" | |
| echo "🌐 ALB:" | |
| aws elbv2 describe-load-balancers --region ap-northeast-2 --query 'LoadBalancers[?contains(LoadBalancerName, `mclass`)].DNSName' --output text || echo "❌ ALB not found" | |
| echo "" | |
| echo "🔍 모니터링 설정 검증:" | |
| echo "📊 Prometheus 설정: prometheus.yml 업데이트됨" | |
| echo "🛡️ ALB 리스너 규칙: VPC 내부 IP 제한 적용됨" | |
| echo "🔒 HTTP: 포트 80으로 간단한 구성" | |
| echo "✅ 간소화된 모니터링 구성 완료!" | |
| echo "" | |
| echo "🔧 ECS 접속 명령어:" | |
| echo "Prometheus 접속: aws ecs execute-command --cluster mclass-cluster --task [TASK_ID] --container prometheus --interactive --command \"/bin/sh\" --region ap-northeast-2" | |
| echo "Grafana 접속: aws ecs execute-command --cluster mclass-cluster --task [TASK_ID] --container grafana --interactive --command \"/bin/sh\" --region ap-northeast-2" | |
| echo "앱 접속: aws ecs execute-command --cluster mclass-cluster --task [TASK_ID] --container mclass-server --interactive --command \"/bin/sh\" --region ap-northeast-2" |