Skip to content

Profect-cloud/profect-eatcloud-msa-v1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

214 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿฝ Eat Cloud Project

Goorm ํ”„๋กœํŽ™ํŠธ ํด๋ผ์šฐ๋“œ ์—”์ง€๋‹ˆ์–ด๋ง ๊ณผ์ • 3๊ธฐ โ€“ 2์ฐจ ํ”„๋กœ์ ํŠธ

๐Ÿ“Œ ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

Eat Cloud๋Š” '๋ฐฐ๋‹ฌ์˜ ๋ฏผ์กฑ'์„ ๋ฒค์น˜๋งˆํ‚นํ•œ ์ฃผ๋ฌธ ๊ด€๋ฆฌ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค.
1์ฐจ ํ”„๋กœ์ ํŠธ์—์„œ ๋งŒ๋“  ๋ชจ๋†€๋ฆฌ์‹ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ์ „ํ™˜ํ•˜๊ณ  ๊ทธ์— ๋งž๋Š” ์ธํ”„๋ผ๊นŒ์ง€ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“† ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„

  • 25.08.07 ~ 25.08.24

๐Ÿ‘ฅ ๋ฉค๋ฒ„ ๊ตฌ์„ฑ

๐Ÿ›  ๊ธฐ์ˆ  ์Šคํƒ

Java Spring Boot Spring Security PostgreSQL PostGIS Redis QueryDSL Spring Cloud Netflix Eureka Rest Template AWS GitHub Actions ECS Docker MSK RDS

โœจ ์ฃผ์š” ๊ธฐ๋Šฅ

๐Ÿ— ์•„ํ‚คํ…์ฒ˜

๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

profect-eatcloud-msa-v1/
โ”œโ”€โ”€ ๐Ÿ“ ๊ณตํ†ต ๋ชจ๋“ˆ
โ”‚   โ”œโ”€โ”€ auto-response/                  # ๊ณตํ†ต ์‘๋‹ต/์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ชจ๋“ˆ
โ”‚   โ””โ”€โ”€ auto-time/                      # ๊ณตํ†ต ์‹œ๊ฐ„ ๊ด€๋ฆฌ ๋ชจ๋“ˆ
โ”‚
โ”œโ”€โ”€ ๐Ÿ“ ์ธํ”„๋ผ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ eureka-server/                  # ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ
โ”‚   โ””โ”€โ”€ api-gateway/                    # API ๊ฒŒ์ดํŠธ์›จ์ด
โ”‚
โ”œโ”€โ”€ ๐Ÿ“ ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ auth-service/                   # ์ธ์ฆ/์ธ๊ฐ€ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ customer-service/               # ๊ณ ๊ฐ ๊ด€๋ฆฌ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ store-service/                  # ๋งค์žฅ/๋ฉ”๋‰ด ๊ด€๋ฆฌ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ order-service/                  # ์ฃผ๋ฌธ ๊ด€๋ฆฌ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ payment-service/                # ๊ฒฐ์ œ ์ฒ˜๋ฆฌ ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ admin-service/                  # ๊ด€๋ฆฌ์ž ์„œ๋น„์Šค
โ”‚   โ””โ”€โ”€ manager-service/                # ๋งค๋‹ˆ์ € ์„œ๋น„์Šค
โ”‚
โ”œโ”€โ”€ ๐Ÿ“ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”
โ”‚   โ””โ”€โ”€ database-init/                  # SQL ์Šคํ‚ค๋งˆ ๋ฐ ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™”
โ”‚
โ””โ”€โ”€ ๐Ÿ“ ๋ฐฐํฌ ๋ฐ ํ™˜๊ฒฝ ์„ค์ •
    โ”œโ”€โ”€ deploy/
    โ”‚   โ””โ”€โ”€compose/                     # Docker Compose ์„ค์ •
    โ””โ”€โ”€ docker-compose.yml              # ๋ฉ”์ธ Docker Compose

์ธํ”„๋ผ ์•„ํ‚คํ…์ฒ˜

git_readme_img-001

CI/CD ์•„ํ‚คํ…์ฒ˜

git_readme_img-002


๐Ÿš€ ์ธํ”„๋ผ ๊ตฌ์ถ• ์ƒ์„ธ

๐ŸŽฏ ํ•ต์‹ฌ ์„ฑ๊ณผ

  • ๋ฐฐํฌ ์‹œ๊ฐ„ 58% ๋‹จ์ถ•: 1์‹œ๊ฐ„ โ†’ 25๋ถ„
  • ์ค‘๋ณต ์ฝ”๋“œ 80% ์ œ๊ฑฐ: auto-time/auto-response ๊ณตํ†ต ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐฐํฌ
  • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ: ECS Blue/Green ๋ฐฐํฌ + ์ž๋™ ๋กค๋ฐฑ
  • ๋ณด์•ˆ ๊ฐ•ํ™”: AWS Secrets Manager๋กœ ๋ฏผ๊ฐ์ •๋ณด ์ค‘์•™ ๊ด€๋ฆฌ

1. CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ํ™”

๋ฌธ์ œ ์ •์˜

7๊ฐœ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋นŒ๋“œ/๋ฐฐํฌํ•˜๋ฉด 1์‹œ๊ฐ„ ์ด์ƒ ์†Œ์š”๋˜์–ด ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์ด ์ €ํ•˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

1) Git Diff ๊ธฐ๋ฐ˜ ๋ณ€๊ฒฝ ๊ฐ์ง€

๋ณ€๊ฒฝ๋œ ์„œ๋น„์Šค๋งŒ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•˜์—ฌ ์„ ํƒ์  ๋ฐฐํฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

# .github/workflows/aws.yml
detect-changes:
  runs-on: ubuntu-latest
  outputs:
    services: ${{ steps.changes.outputs.services }}
    matrix: ${{ steps.changes.outputs.matrix }}
  steps:
    - name: Detect changed services
      id: changes
      run: |
        # Git diff๋กœ ๋ณ€๊ฒฝ๋œ ํŒŒ์ผ ํƒ์ง€
        changed_files=$(git diff --name-only HEAD~1 HEAD)
        
        services=()
        
        # ๊ฐ ์„œ๋น„์Šค ๋””๋ ‰ํ† ๋ฆฌ ํ™•์ธ
        for service in auth-service customer-service admin-service \
                       manager-service store-service order-service payment-service; do
          if echo "$changed_files" | grep -q "^$service/"; then
            services+=("$service")
          fi
        done
        
        # ๊ณตํ†ต ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ ์ „์ฒด ๋ฐฐํฌ
        if echo "$changed_files" | grep -qE "^(build\.gradle|settings\.gradle)"; then
          services=(auth-service customer-service admin-service \
                   manager-service store-service order-service payment-service)
        fi
        
        # Matrix ์ „๋žต์šฉ JSON ์ƒ์„ฑ
        services_json=$(printf '%s\n' "${services[@]}" | jq -R -s -c 'split("\n")[:-1]')
        echo "services=$services_json" >> $GITHUB_OUTPUT

ํšจ๊ณผ:

  • โœ… ๋ณ€๊ฒฝ๋œ ์„œ๋น„์Šค๋งŒ ์„ ํƒ์  ๋ฐฐํฌ
  • โœ… ๋ถˆํ•„์š”ํ•œ ๋นŒ๋“œ/๋ฐฐํฌ ์‹œ๊ฐ„ ์ œ๊ฑฐ
  • โœ… ๊ณตํ†ต ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ ์ „์ฒด ๋ฐฐํฌ ์ž๋™ ๊ฐ์ง€

2) Matrix ๋ณ‘๋ ฌ ๋ฐฐํฌ

GitHub Actions์˜ Matrix ์ „๋žต์œผ๋กœ ์—ฌ๋Ÿฌ ์„œ๋น„์Šค๋ฅผ ๋™์‹œ์— ๋นŒ๋“œ/๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

test-and-build:
  needs: detect-changes
  runs-on: ubuntu-latest
  strategy:
    matrix: ${{fromJson(needs.detect-changes.outputs.matrix)}}
    fail-fast: false  # ํ•˜๋‚˜ ์‹คํŒจํ•ด๋„ ๋‚˜๋จธ์ง€ ๊ณ„์† ์ง„ํ–‰

  steps:
    - name: Set up JDK 21
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'temurin'

    - name: Cache Gradle packages
      uses: actions/cache@v4
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}

    - name: Test ${{ matrix.service }}
      run: ./gradlew :${{ matrix.service }}:test

    - name: Build ${{ matrix.service }}
      run: ./gradlew :${{ matrix.service }}:bootJar

    - name: Upload build artifacts
      uses: actions/upload-artifact@v4
      with:
        name: jar-${{ matrix.service }}-${{ github.sha }}
        path: ${{ matrix.service }}/build/libs/*.jar

ํšจ๊ณผ:

  • โœ… 7๊ฐœ ์„œ๋น„์Šค ๋™์‹œ ๋นŒ๋“œ/ํ…Œ์ŠคํŠธ
  • โœ… Gradle ์บ์‹œ๋กœ ๋นŒ๋“œ ์‹œ๊ฐ„ ๋‹จ์ถ•
  • โœ… ํ•˜๋‚˜ ์‹คํŒจํ•ด๋„ ๋‹ค๋ฅธ ์„œ๋น„์Šค๋Š” ๊ณ„์† ์ง„ํ–‰

3) ECR ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ํ‘ธ์‹œ

build-and-push-ecr:
  needs: [detect-changes, test-and-build]
  runs-on: ubuntu-latest
  strategy:
    matrix: ${{fromJson(needs.detect-changes.outputs.matrix)}}

  steps:
    - 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: Login to Amazon ECR
      uses: aws-actions/amazon-ecr-login@v2

    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        file: ${{ matrix.service }}/Dockerfile
        push: true
        tags: |
          ${{ env.ECR_REGISTRY }}/eatcloud/${{ matrix.service }}:latest
          ${{ env.ECR_REGISTRY }}/eatcloud/${{ matrix.service }}:${{ github.sha }}
        cache-from: type=gha
        cache-to: type=gha,mode=max
        platforms: linux/arm64

ํšจ๊ณผ:

  • โœ… ๋ฉ€ํ‹ฐ ํƒœ๊ทธ ์ „๋žต (latest, commit SHA)
  • โœ… GitHub Actions ์บ์‹œ๋กœ ๋นŒ๋“œ ์†๋„ ํ–ฅ์ƒ
  • โœ… ARM64 ์•„ํ‚คํ…์ฒ˜ ์ง€์› (๋น„์šฉ ์ ˆ๊ฐ)

๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ํ๋ฆ„

graph LR
    A[Git Push] --> B[๋ณ€๊ฒฝ ๊ฐ์ง€]
    B --> C{๋ณ€๊ฒฝ๋œ ์„œ๋น„์Šค?}
    C -->|์žˆ์Œ| D[Matrix ๋ณ‘๋ ฌ ๋นŒ๋“œ]
    C -->|์—†์Œ| E[๋ฐฐํฌ ์Šคํ‚ต]
    D --> F[ํ…Œ์ŠคํŠธ ์‹คํ–‰]
    F --> G[JAR ๋นŒ๋“œ]
    G --> H[Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ]
    H --> I[ECR ํ‘ธ์‹œ]
    I --> J[ECS ๋ฐฐํฌ]
    J --> K[Blue/Green ์ „ํ™˜]
    K --> L[ํ—ฌ์Šค์ฒดํฌ]
    L -->|์„ฑ๊ณต| M[๋ฐฐํฌ ์™„๋ฃŒ]
    L -->|์‹คํŒจ| N[์ž๋™ ๋กค๋ฐฑ]
    
    style A fill:#e1f5ff
    style D fill:#fff4e1
    style M fill:#e1ffe1
    style N fill:#ffe1e1
Loading

์„ฑ๊ณผ

  • โœ… ๋ฐฐํฌ ์‹œ๊ฐ„: 1์‹œ๊ฐ„ โ†’ 25๋ถ„ (58% ๋‹จ์ถ•)
  • โœ… ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ: 7๊ฐœ ์„œ๋น„์Šค ๋™์‹œ ๋นŒ๋“œ/๋ฐฐํฌ
  • โœ… ์„ ํƒ์  ๋ฐฐํฌ: ๋ณ€๊ฒฝ๋œ ์„œ๋น„์Šค๋งŒ ์ž๋™ ๊ฐ์ง€
  • โœ… ์บ์‹œ ์ „๋žต: Gradle + Docker ๋ ˆ์ด์–ด ์บ์‹ฑ

2. ECS Blue/Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ

๋ฌธ์ œ ์ •์˜

๊ธฐ์กด ๋ฐฐํฌ ๋ฐฉ์‹์€ ์„œ๋น„์Šค ์ค‘๋‹จ ์‹œ๊ฐ„์ด ๋ฐœ์ƒํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ €ํ•˜์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

1) Smart Task Definition Update

์ด๋ฏธ์ง€๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋งŒ ์ƒˆ๋กœ์šด Task Definition์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

- name: Smart Task Definition Update
  id: task-def
  run: |
    TASK_DEF_FAMILY="eatcloud-${{ matrix.service }}"
    NEW_IMAGE="${{ env.ECR_REGISTRY }}/eatcloud/${{ matrix.service }}:${{ github.sha }}"
    
    # ํ˜„์žฌ Task Definition ์กฐํšŒ
    CURRENT_TASK_DEF=$(aws ecs describe-services \
      --cluster eatcloud-cluster \
      --services ${{ matrix.service }} \
      --query 'services[0].taskDefinition' \
      --output text)
    
    # ํ˜„์žฌ ์ด๋ฏธ์ง€ ํ™•์ธ
    CURRENT_IMAGE=$(aws ecs describe-task-definition \
      --task-definition "$CURRENT_TASK_DEF" \
      --query 'taskDefinition.containerDefinitions[0].image' \
      --output text)
    
    if [ "$CURRENT_IMAGE" != "$NEW_IMAGE" ]; then
      echo "Image changed, creating new task definition..."
      
      # ์ƒˆ Task Definition ์ƒ์„ฑ
      TASK_DEF_JSON=$(aws ecs describe-task-definition \
        --task-definition "$CURRENT_TASK_DEF" \
        --query 'taskDefinition' --output json)
      
      echo "$TASK_DEF_JSON" | jq --arg IMAGE "$NEW_IMAGE" \
        '.containerDefinitions[0].image = $IMAGE' > new-task-def.json
      
      NEW_TASK_DEF_ARN=$(aws ecs register-task-definition \
        --cli-input-json file://new-task-def.json \
        --query 'taskDefinition.taskDefinitionArn' --output text)
      
      echo "NEW_TASK_DEF_ARN=$NEW_TASK_DEF_ARN" >> $GITHUB_OUTPUT
    else
      echo "Image unchanged, force deployment..."
      echo "NEW_TASK_DEF_ARN=$CURRENT_TASK_DEF" >> $GITHUB_OUTPUT
    fi

2) ECS Blue/Green ๋ฐฐํฌ ์‹œ์ž‘

- name: Start ECS Blue/Green Deployment
  run: |
    aws ecs update-service \
      --cluster eatcloud-cluster \
      --service ${{ matrix.service }} \
      --task-definition ${{ steps.task-def.outputs.NEW_TASK_DEF_ARN }}
    
    echo "Blue/Green deployment initiated"

3) ๋ฐฐํฌ ์ง„ํ–‰ ์ƒํ™ฉ ๋ชจ๋‹ˆํ„ฐ๋ง

- name: Monitor Deployment Progress
  run: |
    for i in {1..40}; do
      SERVICE_STATUS=$(aws ecs describe-services \
        --cluster eatcloud-cluster \
        --services ${{ matrix.service }} \
        --query 'services[0]' \
        --output json)
      
      RUNNING_COUNT=$(echo "$SERVICE_STATUS" | jq -r '.runningCount')
      DESIRED_COUNT=$(echo "$SERVICE_STATUS" | jq -r '.desiredCount')
      
      # ์•ˆ์ •ํ™” ํ™•์ธ
      STABLE_DEPLOYMENTS=$(echo "$SERVICE_STATUS" | jq -r \
        '.deployments | map(select(.status == "PRIMARY")) | length')
      
      if [ "$STABLE_DEPLOYMENTS" = "1" ] && \
         [ "$RUNNING_COUNT" = "$DESIRED_COUNT" ]; then
        echo "Deployment reached stable state!"
        break
      fi
      
      sleep 30
    done

4) Target Group ํ—ฌ์Šค์ฒดํฌ ๊ฒ€์ฆ

- name: Verify Deployment Results
  run: |
    # Target Group ํ—ฌ์Šค ์ƒํƒœ ํ™•์ธ
    TARGET_GROUPS=$(aws elbv2 describe-target-groups \
      --query "TargetGroups[?contains(TargetGroupName, '${{ matrix.service }}')]" \
      --output json)
    
    for tg_arn in $(echo "$TARGET_GROUPS" | jq -r '.[].Arn'); do
      HEALTHY_COUNT=$(aws elbv2 describe-target-health \
        --target-group-arn "$tg_arn" \
        --query 'length(TargetHealthDescriptions[?TargetHealth.State==`healthy`])' \
        --output text)
      
      echo "Healthy targets: $HEALTHY_COUNT"
    done
    
    if [ "$HEALTHY_COUNT" -gt 0 ]; then
      echo "Deployment completed successfully!"
    else
      echo "Deployment verification failed"
      exit 1
    fi

Blue/Green ๋ฐฐํฌ ํ๋ฆ„

sequenceDiagram
    participant GHA as GitHub Actions
    participant ECS as ECS Service
    participant ALB as Application Load Balancer
    participant Blue as Blue Environment
    participant Green as Green Environment
    
    GHA->>ECS: ์ƒˆ Task Definition ๋“ฑ๋ก
    ECS->>Green: Green Environment์— ์ƒˆ Task ์ƒ์„ฑ
    Green->>Green: ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘ ๋ฐ ํ—ฌ์Šค์ฒดํฌ
    Green-->>ALB: ํ—ฌ์Šค์ฒดํฌ ์„ฑ๊ณต ๋ณด๊ณ 
    ALB->>ALB: Green์„ Target Group์— ๋“ฑ๋ก
    
    alt ํ—ฌ์Šค์ฒดํฌ ์„ฑ๊ณต
        ALB->>Green: ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜ ์‹œ์ž‘
        ALB->>Blue: ํŠธ๋ž˜ํ”ฝ ๋น„์œจ ๊ฐ์†Œ
        ALB->>Green: ํŠธ๋ž˜ํ”ฝ 100% ์ „ํ™˜
        ECS->>Blue: ์ด์ „ Task ์ข…๋ฃŒ
        GHA->>GHA: ๋ฐฐํฌ ์„ฑ๊ณต
    else ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ
        ALB->>Blue: ํŠธ๋ž˜ํ”ฝ ์œ ์ง€
        ECS->>Green: ์ƒˆ Task ์ข…๋ฃŒ
        GHA->>GHA: ์ž๋™ ๋กค๋ฐฑ
    end
Loading

์„ฑ๊ณผ

  • โœ… ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ: Blue/Green ์ „ํ™˜์œผ๋กœ ๋‹ค์šดํƒ€์ž„ 0์ดˆ
  • โœ… ์ž๋™ ๋กค๋ฐฑ: ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ ์‹œ ์ฆ‰์‹œ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋ณต๊ตฌ
  • โœ… ๋ฐฐํฌ ์•ˆ์ •์„ฑ: Target Group ํ—ฌ์Šค์ฒดํฌ ์ž๋™ ๊ฒ€์ฆ
  • โœ… ์‚ฌ์šฉ์ž ๊ฒฝํ—˜: ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์ƒˆ ๋ฒ„์ „ ๋ฐฐํฌ

3. ๊ณตํ†ต ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐฐํฌ

๋ฌธ์ œ ์ •์˜

๋ชจ๋“  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์—์„œ ์ค‘๋ณต๋œ ์ฝ”๋“œ(์‘๋‹ต ํฌ๋งท, ์‹œ๊ฐ„ ๊ด€๋ฆฌ)๊ฐ€ ๋ฐ˜๋ณต๋˜์–ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

1) auto-response ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋ชจ๋“  API ์‘๋‹ต์„ ์ผ๊ด€๋œ ํฌ๋งท์œผ๋กœ ์ž๋™ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

// auto-response/src/main/java/com/eatcloud/autoresponse
@Configuration
public class AutoResponseConfiguration {
    
    @Bean
    public ResponseBodyAdvice<Object> apiResponseAdvice() {
        return new ApiResponseAdvice();
    }
    
    @RestControllerAdvice
    public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {
        @Override
        public Object beforeBodyWrite(Object body, ...) {
            if (body instanceof ApiResponse) {
                return body;
            }
            return ApiResponse.success(body);
        }
    }
}

์‚ฌ์šฉ ๋ฐฉ๋ฒ•:

// ๊ฐ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค์˜ build.gradle
dependencies {
    implementation project(':auto-response')
}
// ์„œ๋น„์Šค ์ฝ”๋“œ์—์„œ ์ž๋™ ์ ์šฉ
@RestController
public class CustomerController {
    
    @GetMapping("/customers/{id}")
    public Customer getCustomer(@PathVariable UUID id) {
        return customerService.findById(id);
        // ์ž๋™์œผ๋กœ ApiResponse๋กœ ๊ฐ์‹ธ์ง
    }
}

2) auto-time ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์—”ํ‹ฐํ‹ฐ์˜ ์ƒ์„ฑ/์ˆ˜์ • ์‹œ๊ฐ„๊ณผ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

// auto-time/src/main/java/com/eatcloud/autotime
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
    
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    @CreatedBy
    @Column(updatable = false)
    private String createdBy;
    
    @LastModifiedBy
    private String updatedBy;
}

์‚ฌ์šฉ ๋ฐฉ๋ฒ•:

dependencies {
    implementation project(':auto-time')
}
// ๊ฐ ์—”ํ‹ฐํ‹ฐ์—์„œ ์ƒ์†๋งŒ ํ•˜๋ฉด ์ž๋™ ์ ์šฉ
@Entity
public class Customer extends BaseTimeEntity {
    @Id
    private UUID id;
    private String name;
    // createdAt, updatedAt, createdBy, updatedBy ์ž๋™ ๊ด€๋ฆฌ
}

์„ฑ๊ณผ

  • โœ… ์ค‘๋ณต ์ฝ”๋“œ ์ œ๊ฑฐ: 80% ๊ฐ์†Œ
  • โœ… ์ผ๊ด€์„ฑ: ๋ชจ๋“  ์„œ๋น„์Šค์—์„œ ๋™์ผํ•œ ์‘๋‹ต ํฌ๋งท/์‹œ๊ฐ„ ๊ด€๋ฆฌ
  • โœ… ์œ ์ง€๋ณด์ˆ˜์„ฑ: ๊ณตํ†ต ๋กœ์ง ์ˆ˜์ • ์‹œ ํ•œ ๊ณณ๋งŒ ์ˆ˜์ •
  • โœ… ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ: ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ๋ถˆํ•„์š”

4. ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ๊ด€๋ฆฌ

๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ (Eureka + API Gateway)

# application-docker.properties
spring.cloud.gateway.routes[0].id=auth-service
spring.cloud.gateway.routes[0].uri=lb://auth-service
eureka.client.service-url.defaultZone=http://eureka-server:8761/eureka/

AWS ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ (ALB + Secrets Manager)

1) AWS Secrets Manager ํ†ตํ•ฉ

๊ฐ ECS Task๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ Secret์„ Secrets Manager๋กœ ๊ด€๋ฆฌํ•˜์—ฌ ๋ณด์•ˆ์„ฑ๊ณผ ๊ด€๋ฆฌ ํšจ์œจ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.

Secrets Manager ๊ตฌ์กฐ:

// eatcloud/auth-service/database
{
  "username": "auth_service",
  "password": "****",
  "engine": "postgresql",
  "host": "eatcloud-postgres.xxx.ap-northeast-2.rds.amazonaws.com",
  "port": 5432,
  "dbname": "auth_db"
}

// eatcloud/auth-service/redis
{
  "host": "eatcloud-redis.xxx.cache.amazonaws.com",
  "port": 6379
}

// eatcloud/auth-service/jwt
{
  "secret": "****",
  "expiration": 3600
}

2) ECS Task Definition์—์„œ Secrets ์ฃผ์ž…

{
  "family": "eatcloud-auth-service",
  "containerDefinitions": [
    {
      "name": "auth-service",
      "image": "xxx.dkr.ecr.ap-northeast-2.amazonaws.com/eatcloud/auth-service:latest",
      "secrets": [
        {
          "name": "DB_USERNAME",
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:xxx:secret:eatcloud/auth-service/database:username::"
        },
        {
          "name": "DB_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:xxx:secret:eatcloud/auth-service/database:password::"
        },
        {
          "name": "REDIS_HOST",
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-2:xxx:secret:eatcloud/auth-service/redis:host::"
        }
      ]
    }
  ]
}

3) Application ์„ค์ •์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์‚ฌ์šฉ

# application-aws.properties
# Eureka ์‚ฌ์šฉ ์•ˆ ํ•จ - ALB๊ฐ€ ๋ผ์šฐํŒ… ๋‹ด๋‹น
eureka.client.enabled=false

# RDS ์—ฐ๊ฒฐ - Secrets Manager์—์„œ ์ฃผ์ž…
spring.datasource.url=jdbc:postgresql://${DB_HOST}:5432/auth_db
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}

# Redis - Secrets Manager์—์„œ ์ฃผ์ž…
spring.redis.host=${REDIS_HOST}
spring.redis.port=${REDIS_PORT:6379}

# JWT - Secrets Manager์—์„œ ์ฃผ์ž…
jwt.secret=${JWT_SECRET}
jwt.expiration=${JWT_EXPIRATION:3600}

Secrets Manager ์•„ํ‚คํ…์ฒ˜

graph TB
    subgraph "AWS Secrets Manager"
        Secret1[auth-service secrets<br/>DB/Redis/JWT]
        Secret2[customer-service secrets<br/>DB/Redis]
        Secret3[order-service secrets<br/>DB/Redis/Kafka]
        Secret4[payment-service secrets<br/>DB/Redis/PG]
    end
    
    subgraph "ECS Fargate Cluster"
        Task1[Auth Service Task] 
        Task2[Customer Service Task]
        Task3[Order Service Task]
        Task4[Payment Service Task]
    end
    
    Secret1 -->|ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…| Task1
    Secret2 -->|ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…| Task2
    Secret3 -->|ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…| Task3
    Secret4 -->|ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…| Task4
    
    Task1 --> RDS1[(Auth DB)]
    Task2 --> RDS2[(Customer DB)]
    Task3 --> RDS3[(Order DB)]
    Task4 --> RDS4[(Payment DB)]
    
    Task1 --> Redis[(Redis Cluster)]
    Task2 --> Redis
    Task3 --> Kafka[MSK Kafka]
    
    style Secret1 fill:#ffe1e1
    style Secret2 fill:#ffe1e1
    style Secret3 fill:#ffe1e1
    style Secret4 fill:#ffe1e1
Loading

Secrets Manager ์žฅ์ 

1) ๋ณด์•ˆ์„ฑ ๊ฐ•ํ™”

  • โœ… ์•”ํ˜ธํ™” ์ €์žฅ: ๋ชจ๋“  ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ์ €์žฅ
  • โœ… ์ ‘๊ทผ ์ œ์–ด: IAM ์ •์ฑ…์œผ๋กœ ์„œ๋น„์Šค๋ณ„ ์ ‘๊ทผ ๊ถŒํ•œ ๋ถ„๋ฆฌ
  • โœ… ๊ฐ์‚ฌ ์ถ”์ : CloudTrail๋กœ Secret ์ ‘๊ทผ ๋กœ๊ทธ ๊ธฐ๋ก

2) ๊ด€๋ฆฌ ํšจ์œจ์„ฑ

  • โœ… ์ค‘์•™ ๊ด€๋ฆฌ: ๋ชจ๋“  ์„œ๋น„์Šค์˜ Secret์„ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌ
  • โœ… ๋ฒ„์ „ ๊ด€๋ฆฌ: Secret ๋ณ€๊ฒฝ ์ด๋ ฅ ์ถ”์  ๋ฐ ๋กค๋ฐฑ ๊ฐ€๋Šฅ
  • โœ… ์ž๋™ ๋กœํ…Œ์ด์…˜: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž๋™ ๊ต์ฒด

3) ๋ฐฐํฌ ํŽธ์˜์„ฑ

  • โœ… ์ฝ”๋“œ ๋ถ„๋ฆฌ: ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ์ฝ”๋“œ์— ํ•˜๋“œ์ฝ”๋”ฉ๋˜์ง€ ์•Š์Œ
  • โœ… ํ™˜๊ฒฝ๋ณ„ ๊ด€๋ฆฌ: dev/staging/prod ํ™˜๊ฒฝ๋ณ„ Secret ๋ถ„๋ฆฌ
  • โœ… ์ฆ‰์‹œ ๋ฐ˜์˜: Secret ๋ณ€๊ฒฝ ์‹œ Task ์žฌ์‹œ์ž‘๋งŒ์œผ๋กœ ๋ฐ˜์˜

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors