Need U V1 : CD 파이프라인 설계 - 100-hours-a-week/KTB4-4th-wiki GitHub Wiki

목차

  1. 설계 목적
  2. V1 배포 구조와 CD 대상
  3. CD 방식 및 Trigger 정책
  4. 공통 배포 원칙
  5. Backend CD
  6. AI Server CD
  7. Frontend CD
  8. Artifact 및 ECR 관리
  9. Application Secret 관리
  10. 배포 전략
  11. Health Check 및 Rollback
  12. Monitoring
  13. 배포 보안
  14. 동시 배포 방지
  15. 전체 CD Pipeline
  16. CI / CD 경계
  17. V1 최종 기준

1. 설계 목적

Need U는 CI를 통해 Frontend, Backend, AI Server의 코드를 검증하고 배포 Artifact를 생성한다.

CI 성공만으로 Production 배포가 완료되는 것은 아니다.

CI
↓
배포 가능한 Artifact 생성
↓
배포 버전 선택
↓
Production 반영
↓
Health Check
↓
실패 시 Rollback

V1 CD의 목적은 CI에서 검증한 Artifact를 동일한 절차로 운영 환경에 반영하고, 배포 실패 시 직전 정상 버전으로 복구할 수 있도록 하는 것이다.

수동으로 EC2에 접속해 배포하면 다음 문제가 발생할 수 있다.

  • 잘못된 버전 배포
  • 담당자마다 다른 배포 절차
  • 환경 설정 누락
  • Rollback 지연
  • 현재 운영 버전 추적 어려움

따라서 GitHub Actions, AWS OIDC, SSM Run Command, Docker Compose를 이용해 배포 절차를 표준화한다.

2. V1 배포 구조와 CD 대상

V1 Production 구조는 다음과 같다.

EC2 t3.medium

├─ Nginx
│  ├─ /      → React/Vite 정적 파일
│  └─ /api   → Spring Boot Backend
│
├─ Docker Compose
│  ├─ Backend (Spring Boot)
│  ├─ AI Server (FastAPI)
│  └─ Redis
│
├─ MySQL
└─ PostgreSQL + pgvector

CD 관점에서는 세 개의 배포 단위를 구분한다.

구분 Artifact 저장 위치 Production 반영 방식
Frontend Vite dist/ GitHub Actions Artifact Host Nginx 정적 파일 교체
Backend Docker Image Amazon ECR Docker Compose Service 교체
AI Server Docker Image Amazon ECR Docker Compose Service 교체

Redis, MySQL, PostgreSQL + pgvector는 Application 배포 시 매번 교체하지 않는다.

Application 배포
├─ Frontend → 필요 시 교체
├─ Backend → 필요 시 교체
└─ AI Server → 필요 시 교체

유지
├─ Redis
├─ MySQL
├─ PostgreSQL + pgvector
└─ Nginx

Backend와 AI Server 배포 책임

Backend와 AI Server는 각각 독립적인 Docker Image로 관리하며, 각 Repository의 Git Commit SHA를 Image Tag로 사용한다.

Backend   → needu-backend:<Backend Commit SHA>
AI Server → needu-ai:<AI Commit SHA>

3. CD 방식 및 Trigger 정책

3.1 Continuous Delivery 선택

Need U V1은 Continuous Delivery 방식을 사용한다.

구분 Continuous Delivery Continuous Deployment
CI 자동 자동
Artifact 생성 자동 자동
Production 배포 담당자가 실행 자동
V1 적용 O X

V1에서는 main Merge와 Production 배포를 분리한다.

main CI 성공
↓
Artifact 생성
↓
배포 가능 상태
↓
담당자 배포 결정
↓
workflow_dispatch
↓
Production

즉:

main Merge ≠ Production Deploy

3.2 Continuous Delivery를 선택한 이유

  • V1 예상 Peak Traffic은 약 0.16 RPS로 낮다.
  • 계획 배포는 주 2회, 사용자 활동이 적은 새벽 시간대를 기본으로 한다.
  • V1은 Recreate 방식의 중단 배포를 사용한다.
  • Merge 시점과 실제 서비스 중단 시점을 분리해야 한다.
  • 배포 빈도가 높지 않아 완전 자동 Production 배포의 이점이 크지 않다.

3.3 Trigger

Frontend, Backend, AI Server CD 모두 수동 Trigger를 사용한다.

on:
  workflow_dispatch:

각 Repository에서 자신이 배포할 버전을 입력받는다.

Frontend CD
→ commit_sha = Frontend Commit SHA

Backend CD
→ image_tag = Backend Commit SHA

AI CD
→ image_tag = AI Commit SHA

3.4 배포 전 확인 조건

Frontend

  • 해당 Commit의 main CI 성공
  • needu-frontend-<SHA> Artifact 존재
  • Playwright E2E 성공
  • 배포할 Commit SHA 확인

Backend

  • 해당 Commit의 main CI 성공
  • needu-backend:<SHA> Image가 ECR에 존재
  • 배포할 Commit SHA 확인

AI Server

  • 해당 Commit의 main CI 성공
  • needu-ai:<SHA> Image가 ECR에 존재
  • Docker Build Check 성공
  • 배포할 Commit SHA 확인

Redis / PostgreSQL + pgvector Integration Test는 현재 필수 배포 조건에 포함하지 않는다. 관련 Adapter와 Test가 구현되어 CI release-check에 추가되면 이후 배포 조건에도 반영한다.

공통적으로 계획된 배포 시간인지 확인한 뒤 Production CD를 실행한다.

4. 공통 배포 원칙

4.1 Build Once, Deploy Many

Production EC2에서 Source Code를 다시 Build하지 않는다.

CI
↓
검증된 Artifact 생성
↓
Artifact 저장
↓
CD
↓
동일 Artifact Production 배포

Backend와 AI Server는 CI에서 만든 Docker Image를 ECR에 저장하고 CD에서 그대로 Pull한다.

Frontend도 CI에서 생성한 dist/를 그대로 배포한다.

4.2 Git Commit SHA 기준 배포

배포 버전은 Git Commit SHA로 식별한다.

Frontend
needu-frontend-93fbc21

Backend
needu-backend:a82f3c1

AI Server
needu-ai:7e42c91

latest는 사용하지 않는다.

4.3 SSM을 통한 배포

GitHub Actions에서 EC2에 직접 SSH 접속하지 않는다.

GitHub Actions
↓
AWS OIDC
↓
IAM Role
↓
SSM Run Command
↓
EC2 Deploy Script

SSM은 배포 명령을 EC2에 전달하는 수단으로 사용한다.

4.4 Service 단위 배포

Backend를 배포할 때 AI Server나 Redis를 함께 재시작하지 않는다.

AI Server를 배포할 때 Backend나 Redis를 함께 재시작하지 않는다.

Backend 배포
→ Backend만 교체

AI 배포
→ AI Server만 교체

Frontend 배포
→ dist만 교체

Application 간 불필요한 영향 범위를 줄이기 위한 정책이다.

5. Backend CD

Backend는 Spring Boot Docker Image를 ECR에서 Pull하여 Docker Compose Service를 교체한다.

5.1 Backend CD 흐름

수동 CD 실행
↓
배포할 Backend SHA 입력
↓
ECR Image 존재 확인
↓
SSM Run Command
↓
기존 정상 Backend Tag 기록
↓
신규 Image Pull
↓
Backend Service 교체
↓
Health Check

성공:

Health Check 성공
↓
신규 Tag 저장
↓
배포 완료

실패:

Health Check 실패
↓
직전 정상 Tag로 변경
↓
이전 Image Pull
↓
Backend Service 재실행
↓
Rollback Health Check

5.2 Backend 배포 중 유지되는 구성

Backend                → 교체
AI Server              → 유지
Redis                  → 유지
MySQL                  → 유지
PostgreSQL + pgvector  → 유지
Nginx                  → 유지

5.3 Backend workflow_dispatch 예시

name: Backend Production CD

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: "배포할 Backend Commit SHA"
        required: true
        type: string

permissions:
  contents: read
  id-token: write

concurrency:
  group: production-deploy
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.BACKEND_CD_ROLE_ARN }}
          aws-region: ap-northeast-2

      - name: Verify Image
        run: |
          aws ecr describe-images \
            --repository-name needu-backend \
            --image-ids imageTag=${{ inputs.image_tag }}

      - name: Deploy via SSM
        run: |
          COMMAND_ID=$(aws ssm send-command \
            --instance-ids "${{ vars.EC2_INSTANCE_ID }}" \
            --document-name "AWS-RunShellScript" \
            --parameters "commands=[\"cd /home/ec2-user/needu && ./deploy-backend.sh ${{ inputs.image_tag }}\"]" \
            --query "Command.CommandId" \
            --output text)

          echo "COMMAND_ID=$COMMAND_ID" >> "$GITHUB_ENV"

          aws ssm wait command-executed \
            --command-id "$COMMAND_ID" \
            --instance-id "${{ vars.EC2_INSTANCE_ID }}"

5.4 Backend deploy script 예시

고정 container_name에 의존하지 않고 Compose Service ID를 기준으로 Health 상태를 확인한다.

#!/bin/bash

set -Eeuo pipefail

NEW_TAG="$1"
PREV_TAG=$(cat .last_deployed_backend_tag 2>/dev/null || true)

export BACKEND_IMAGE_TAG="$NEW_TAG"

docker compose pull backend
docker compose up -d --no-deps backend

HEALTHY=false

for i in $(seq 1 12); do
  CONTAINER_ID=$(docker compose ps -q backend)

  STATUS=$(docker inspect \
    -f '{{.State.Health.Status}}' \
    "$CONTAINER_ID" 2>/dev/null || true)

  if [ "$STATUS" = "healthy" ]; then
    HEALTHY=true
    break
  fi

  sleep 5
done

if [ "$HEALTHY" = true ]; then
  echo "$NEW_TAG" > .last_deployed_backend_tag
  exit 0
fi

if [ -z "$PREV_TAG" ]; then
  exit 1
fi

export BACKEND_IMAGE_TAG="$PREV_TAG"

docker compose pull backend
docker compose up -d --no-deps backend

exit 1

Rollback 이후에도 Health Check를 다시 수행하도록 실제 Script에서 보완한다.

5.5 Backend Graceful Shutdown

진행 중인 HTTP Request가 즉시 끊기지 않도록 Spring Boot Graceful Shutdown을 사용한다.

예:

server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 20s

Docker Compose:

services:
  backend:
    stop_grace_period: 25s

20s, 25s는 초기값이며 실제 Request 처리 시간과 Shutdown 시간을 측정한 뒤 조정한다.

6. AI Server CD

AI Server는 FastAPI 기반 Docker Image를 ECR에서 Pull하여 Docker Compose Service를 교체한다.

ECR
└─ needu-ai:<AI Commit SHA>

6.1 AI Server CD 흐름

수동 AI CD 실행
↓
배포할 AI SHA 입력
↓
ECR needu-ai Image 존재 확인
↓
SSM Run Command
↓
기존 정상 AI Tag 기록
↓
신규 AI Image Pull
↓
AI Server Service 교체
↓
AI Health Check

성공:

Health Check 성공
↓
신규 AI Tag 저장
↓
AI 배포 완료

실패:

Health Check 실패
↓
직전 정상 AI Tag로 변경
↓
이전 AI Image 실행
↓
Rollback Health Check

6.2 AI 배포 중 유지되는 구성

AI Server              → 교체
Backend                → 유지
Redis                  → 유지
MySQL                  → 유지
PostgreSQL + pgvector  → 유지
Nginx                  → 유지

Redis를 재시작하지 않으므로 AI Server 배포 때문에 대화 State나 Cache가 불필요하게 초기화되는 것을 방지한다.

PostgreSQL + pgvector 역시 AI Application Image와 수명주기를 분리한다.

6.3 AI Health Check

AI Server 배포 후에는 FastAPI Health Endpoint로 Application이 요청을 받을 수 있는 상태인지 확인한다.

예:

GET /health

초기 배포 성공 기준:

  • AI Container가 실행 중일 것
  • Health Endpoint가 HTTP 200을 반환할 것
  • FastAPI Application이 요청을 받을 수 있을 것

/health Endpoint가 현재 AI Repository에 구현되어 있는지는 배포 전 확인한다. 아직 없다면 CD 적용 전에 간단한 Health Endpoint를 추가한다.

Redis, PostgreSQL + pgvector 등 Dependency 상태를 Health Endpoint에 포함할지는 실제 운영 필요에 따라 확장한다.

6.4 AI workflow_dispatch 예시

name: AI Production CD

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: "배포할 AI Commit SHA"
        required: true
        type: string

permissions:
  contents: read
  id-token: write

concurrency:
  group: production-deploy
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AI_CD_ROLE_ARN }}
          aws-region: ap-northeast-2

      - name: Verify Image
        run: |
          aws ecr describe-images \
            --repository-name needu-ai \
            --image-ids imageTag=${{ inputs.image_tag }}

      - name: Deploy via SSM
        run: |
          COMMAND_ID=$(aws ssm send-command \
            --instance-ids "${{ vars.EC2_INSTANCE_ID }}" \
            --document-name "AWS-RunShellScript" \
            --parameters "commands=[\"cd /home/ec2-user/needu && ./deploy-ai.sh ${{ inputs.image_tag }}\"]" \
            --query "Command.CommandId" \
            --output text)

          echo "COMMAND_ID=$COMMAND_ID" >> "$GITHUB_ENV"

          aws ssm wait command-executed \
            --command-id "$COMMAND_ID" \
            --instance-id "${{ vars.EC2_INSTANCE_ID }}"

6.5 AI deploy script 개념

#!/bin/bash

set -Eeuo pipefail

NEW_TAG="$1"
PREV_TAG=$(cat .last_deployed_ai_tag 2>/dev/null || true)

export AI_IMAGE_TAG="$NEW_TAG"

docker compose pull ai
docker compose up -d --no-deps ai

# Health Check
# 실패 시 AI_IMAGE_TAG를 PREV_TAG로 되돌리고
# 동일한 Compose Service를 다시 실행한다.

실제 Service 이름과 Health Endpoint는 AI Repository / Compose 구현과 맞춘다.

6.6 AI Graceful Shutdown

LLM API 또는 Embedding API 응답을 기다리는 중에 AI Container가 즉시 종료되면 진행 중인 요청이 끊길 수 있다.

따라서 FastAPI Server도 Graceful Shutdown이 가능하도록 구성한다.

Docker Compose에서는 AI Service에 stop_grace_period를 설정하고 실제 요청 시간을 측정한 뒤 값을 조정한다.

7. Frontend CD

Frontend는 Docker Image를 사용하지 않는다.

CI에서 생성한 dist/ Artifact를 EC2 Nginx 정적 파일 경로에 반영한다.

7.1 Frontend CD 흐름

수동 CD 실행
↓
Frontend Commit SHA 입력
↓
GitHub Actions Artifact 확인
↓
dist 다운로드
↓
EC2 전달
↓
기존 dist 백업
↓
신규 dist 적용
↓
Nginx 검증

성공:

신규 dist 유지
↓
Frontend 배포 완료

실패:

신규 dist 제거
↓
이전 dist 복구

7.2 Directory 단위 교체

Frontend는 파일 하나씩 덮어쓰지 않고 Directory 단위로 교체한다.

예:

/var/www/needu
/var/www/needu.prev
/var/www/needu.new

7.3 Frontend workflow_dispatch 예시

name: Frontend Production CD

on:
  workflow_dispatch:
    inputs:
      commit_sha:
        description: "배포할 Frontend Commit SHA"
        required: true
        type: string

permissions:
  contents: read
  actions: read
  id-token: write

concurrency:
  group: production-deploy
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Download Frontend Artifact
        run: |
          echo "Download needu-frontend-${{ inputs.commit_sha }}"

      - name: Validate Artifact
        run: |
          test -f dist/index.html

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.FRONTEND_CD_ROLE_ARN }}
          aws-region: ap-northeast-2

      - name: Deploy via SSM
        run: |
          aws ssm send-command \
            --instance-ids "${{ vars.EC2_INSTANCE_ID }}" \
            --document-name "AWS-RunShellScript" \
            --parameters "commands=[\"cd /home/ec2-user/needu && ./deploy-frontend.sh ${{ inputs.commit_sha }}\"]"

GitHub Actions Artifact를 EC2로 전달하는 구체적인 전송 방식은 구현 단계에서 확정한다.

중요한 기준은 Frontend에 Docker/ECR을 추가하지 않고 dist/ 자체를 배포한다는 점이다.

8. Artifact 및 ECR 관리

8.1 Artifact 구분

구성요소 Artifact 저장 위치
Frontend Vite dist/ GitHub Actions Artifact
Backend Docker Image Amazon ECR needu-backend
AI Server Docker Image Amazon ECR needu-ai
Redis 없음 공식 Redis Image 사용
MySQL 없음 EC2 Host Process
PostgreSQL + pgvector 없음 EC2 Host Process

8.2 ECR Repository

Amazon ECR
├─ needu-backend
└─ needu-ai

8.3 ECR Push 정책

PR 단계에서는 Docker Build 가능 여부만 확인한다.

PR
→ Docker Build Check
→ ECR Push X

main CI 성공 후에만 실제 Production 후보 Image를 ECR에 저장한다.

main
→ Docker Build
→ Commit SHA Tag
→ ECR Push

8.4 Rollback을 위한 Image 유지

V1 초기에는 이전 Production Image를 ECR에 유지한다.

현재 Backend
needu-backend:a82f3c1

이전 Backend
needu-backend:91bd020
현재 AI
needu-ai:7e42c91

이전 AI
needu-ai:51ad933

배포 횟수가 증가해 Image가 많이 누적되면 ECR Lifecycle Policy를 검토한다.

9. Application Secret 관리

9.1 Image에 Secret을 포함하지 않는다

다음과 같은 Secret은 Docker Image와 Git Repository에 포함하지 않는다.

Backend 예:

KAKAO_API_KEY
DB_PASSWORD
JWT_SECRET

AI Server 예:

LLM_API_KEY
EMBEDDING_API_KEY
AI_DB_PASSWORD

9.2 EC2 환경 파일

V1에서는 EC2의 환경 파일을 Docker Compose에 주입한다.

예:

/home/ec2-user/needu/.env

필요한 경우 Service별 환경 파일로 분리할 수 있다.

.env.backend
.env.ai

핵심 원칙은 각 Container에 필요한 Secret만 전달하는 것이다.

파일 권한은 운영 계정만 읽을 수 있도록 제한한다.

chmod 600 .env

Git에는 포함하지 않는다.

.env
.env.*

9.3 향후 확장

V1에서는 관리 단순성을 위해 EC2 환경 파일을 사용한다.

팀 규모 증가, Secret Rotation, 중앙 관리 필요성이 생기면 Parameter Store 또는 Secrets Manager를 검토한다.

10. 배포 전략

10.1 선택: Recreate

Backend와 AI Server 모두 Recreate 방식을 사용한다.

기존 Service
↓
종료
↓
신규 Container 실행
↓
Health Check
↓
정상 서비스

10.2 무중단 배포와 비교

항목 Recreate Blue-Green / Rolling
동일 Service 동시 실행 1개 2개 이상
Downtime 발생 최소화
Memory 사용량 낮음 증가
Port / Routing 관리 단순 복잡
Rollback 이전 Image 재실행 Traffic 전환
V1 운영 복잡도 낮음 높음

10.3 V1에서 Recreate를 선택하는 이유

낮은 Traffic

V1의 예상 Peak Traffic은 약 0.16 RPS다.

짧은 계획 Downtime의 사용자 영향이 상대적으로 작다.

낮은 배포 빈도

기본 배포 빈도는 주 2회이며 사용자 활동이 적은 새벽 시간대를 사용한다.

단일 EC2 Resource

V1은 하나의 t3.medium에서 여러 Process를 함께 운영한다.

EC2
├─ Nginx
├─ Backend
├─ AI Server
├─ Redis
├─ MySQL
└─ PostgreSQL + pgvector

Blue-Green을 적용하면 Backend 또는 AI Server를 기존 버전과 신규 버전 두 개씩 동시에 실행해야 하므로 Memory 여유가 더 필요하다.

따라서 V1에서는 짧은 Downtime을 허용하고 운영이 단순한 Recreate 방식을 사용한다.

11. Health Check 및 Rollback

11.1 Backend 성공 판단

Backend는 Spring Boot Actuator Endpoint를 사용한다.

GET /actuator/health

배포 성공 기준:

  • Container 실행
  • Health Endpoint HTTP 200
  • Application Status 정상

11.2 AI Server 성공 판단

AI Server는 FastAPI Health Endpoint를 사용한다.

예:

GET /health

배포 성공 기준:

  • Container 실행
  • Health Endpoint HTTP 200
  • FastAPI Process가 Request 처리 가능 상태

정확한 Endpoint와 Dependency Check 범위는 AI 구현 시 확정한다.

11.3 Backend Rollback

신규 Backend
↓
Health 실패
↓
이전 Backend SHA
↓
ECR Pull
↓
Backend Recreate
↓
Rollback Health Check

11.4 AI Server Rollback

신규 AI Server
↓
Health 실패
↓
이전 AI SHA
↓
ECR Pull
↓
AI Service Recreate
↓
Rollback Health Check

11.5 Frontend Rollback

신규 dist
↓
검증 실패
↓
needu.prev 복구

11.6 수동 Rollback

Health Check를 통과했지만 Smoke Test 또는 실제 사용자 동작에서 문제가 발견되면 직전 정상 SHA로 동일한 workflow_dispatch를 다시 실행한다.

별도 Rollback Workflow를 만들지 않는다.

정상 버전 SHA 입력
↓
기존 CD Pipeline 재실행

배포와 Rollback이 동일한 절차를 사용하도록 하기 위함이다.

11.7 Database Rollback 한계

Application Image Rollback과 Database Schema Rollback은 별개다.

Backend 또는 AI Server가 새로운 Schema를 요구하는데 Database를 파괴적으로 변경한 경우 Application Image만 되돌려도 정상 동작하지 않을 수 있다.

따라서 V1에서는 가능한 한 하위 호환 가능한 Schema 변경을 사용한다.

피해야 할 예:

  • 기존 Column 즉시 삭제
  • 기존 Column Rename
  • 비호환 Type 변경
  • 기존 Vector Schema를 즉시 제거

PostgreSQL + pgvector의 Embedding Model / Document Schema 변경으로 전체 재임베딩이 필요한 경우에도 Application Rollback과 Vector Data Rollback을 별도로 고려해야 한다.

12. Monitoring

배포 직후 다음을 확인한다.

공통

  • EC2 CPU
  • EC2 Memory
  • Disk 사용량
  • Docker Container 상태

Backend

  • /actuator/health
  • Backend Application Log
  • HTTP 5xx
  • MySQL 연결 오류
  • AI Server 호출 오류

AI Server

  • /health
  • FastAPI Log
  • HTTP 5xx
  • Redis 연결 오류
  • PostgreSQL + pgvector 연결 오류
  • Vector Search 오류 / 지연
  • LLM / Embedding API 오류

Frontend

  • Nginx 상태
  • 정적 파일 제공 여부
  • 주요 화면 Smoke Test

V1에서는 Canary 분석이나 Error Rate 기반 자동 Rollback까지 적용하지 않는다.

Health Check
+
Application Log
+
CloudWatch
+
간단한 Smoke Test

를 기준으로 배포 직후 상태를 확인한다.

13. 배포 보안

배포 경로는 다음과 같다.

GitHub Actions
↓
AWS OIDC
↓
IAM Role
↓
SSM Run Command
↓
EC2
항목 정책
SSH 배포에 사용하지 않음
GitHub → AWS OIDC
장기 AWS Access Key 사용하지 않음
EC2 배포 명령 SSM Run Command
Backend Image Pull EC2 IAM Role
AI Image Pull EC2 IAM Role
Application Secret EC2 환경 파일
환경 파일 Git 관리 금지

GitHub Actions Role

CD Workflow는 필요한 범위의 권한만 가진다.

예:

Backend CD Role
├─ needu-backend Image 조회
└─ 대상 EC2 SSM Command

AI CD Role
├─ needu-ai Image 조회
└─ 대상 EC2 SSM Command

Frontend CD Role
└─ 대상 EC2 SSM Command

EC2 Instance Role

EC2는 Backend와 AI Image를 ECR에서 Pull할 수 있어야 한다.

EC2 Instance Role
├─ needu-backend Pull
└─ needu-ai Pull

14. 동시 배포 방지

V1은 단일 EC2를 사용한다.

Frontend, Backend, AI CD가 동시에 실행되면 Resource 사용량과 배포 Script 상태가 겹칠 수 있다.

따라서 Production 배포는 하나의 공통 concurrency Group으로 직렬화한다.

concurrency:
  group: production-deploy
  cancel-in-progress: false
Backend CD 실행 중
↓
AI CD 요청
↓
대기
↓
Backend CD 완료
↓
AI CD 실행

기존 배포를 강제로 취소하지 않는다.

15. 전체 CD Pipeline

15.1 각각 독립 배포

변경된 Component만 배포한다.

Frontend만 변경
→ Frontend CD

Backend만 변경
→ Backend CD

AI만 변경
→ AI CD

15.2 여러 Component가 함께 변경된 경우

기본 배포 순서는 다음과 같이 둔다.

AI Server
↓
Health Check

Backend
↓
Health Check

Frontend
↓
Smoke Test

AI Server를 먼저 배포하는 이유는 신규 Backend가 새로운 AI API를 호출하는 경우 Backend보다 AI Endpoint가 먼저 준비되어 있어야 하기 때문이다.

Frontend는 Backend API에 의존할 수 있으므로 Backend가 정상 상태가 된 뒤 배포한다.

단, 이 순서는 API 변경이 하위 호환된다는 전제에서 사용한다.

호환되지 않는 API 변경이 필요한 경우 한 번에 양쪽을 깨뜨리는 방식보다 다음과 같이 단계적으로 변경한다.

1. AI Server가 기존 + 신규 Contract를 모두 지원
2. AI Server 배포
3. Backend가 신규 Contract 사용
4. Backend 배포
5. 필요 시 이전 Contract 제거

15.3 전체 흐름

각 Repository main CI 성공
↓
Frontend dist / Backend Image / AI Image 생성
↓
배포 대기
↓
담당자 workflow_dispatch 실행
↓
AI Server 필요 시 배포
↓
AI Health Check
↓
Backend 필요 시 배포
↓
Backend Health Check
↓
Frontend 필요 시 배포
↓
Smoke Test
↓
전체 배포 완료

중간 단계에서 실패하면 해당 Component를 직전 정상 버전으로 Rollback하고 이후 Component 배포는 진행하지 않는다.

16. CI / CD 경계

구분 CI CD
목적 배포 가능한 Artifact 검증 / 생성 Artifact를 Production에 반영
Trigger PR / main Push workflow_dispatch
Frontend Build / E2E → dist/ Artifact Nginx 정적 파일 반영
Backend Test / Docker Build → ECR Push Image Pull → Backend Recreate → Health Check
AI Server Unit Test / Ruff / Docker Build → ECR Push Image Pull → AI Recreate → Health Check
실패 Merge 차단 / Artifact 생성 중단 Rollback / 기존 버전 유지
EC2 접근 없음 SSM

CI가 보장하는 것

  • Frontend Build / E2E와 dist/ Artifact 생성
  • Backend Test / Docker Build와 ECR Image 생성
  • AI Unit Test / Ruff / Docker Build와 ECR Image 생성
  • 배포할 Artifact가 Git Commit SHA로 추적 가능한 상태

Redis / PostgreSQL + pgvector Integration Test는 현재 AI CI 필수 범위가 아니며, 관련 Adapter와 Test 구현 후 추가한다.

CD가 보장하는 것

  • 지정한 Artifact 존재 확인
  • Production 반영
  • Backend / AI Service 단위 Recreate
  • Health Check
  • 배포 실패 시 직전 정상 버전 복구

V1 한계

한계 내용
Recreate Downtime Backend / AI Service 교체 중 짧은 중단 가능
단일 EC2 Host 장애 시 전체 서비스 영향
Smoke Test 일부 수동 Health Check가 모든 기능 정상 여부를 보장하지 않음
DB Rollback 자동화 없음 Application Rollback과 Schema Rollback 분리
Secret 수동 관리 환경 파일 변경 / Rotation 자동화 없음
외부 API 의존 LLM / Embedding API 장애는 Application Rollback만으로 해결되지 않을 수 있음
수동 Production 결정 담당자가 CD를 실행해야 함

17. V1 최종 기준

Production 구조

EC2 t3.medium

├─ Nginx
├─ Docker Compose
│  ├─ Backend
│  ├─ AI Server
│  └─ Redis
├─ MySQL
└─ PostgreSQL + pgvector

Artifact

Frontend
→ needu-frontend-<SHA>
→ GitHub Actions Artifact

Backend
→ needu-backend:<SHA>
→ Amazon ECR

AI Server
→ needu-ai:<SHA>
→ Amazon ECR

CD

Frontend
→ workflow_dispatch
→ dist 배포
→ 실패 시 이전 Directory 복구
Backend
→ workflow_dispatch
→ ECR Image 확인
→ SSM
→ Backend Recreate
→ /actuator/health
→ 실패 시 이전 SHA Image
AI Server
→ workflow_dispatch
→ ECR Image 확인
→ SSM
→ AI Service Recreate
→ /health
→ 실패 시 이전 SHA Image

핵심 원칙

  • main Merge와 Production 배포를 분리한다.
  • Frontend, Backend, AI Server는 각각 독립적으로 배포한다.
  • Backend와 AI Server는 각각 별도 ECR Repository를 사용한다.
  • Production EC2에서 Source를 다시 Build하지 않는다.
  • Git Commit SHA로 배포 버전을 추적한다.
  • Application 배포 시 Redis와 Database를 함께 재시작하지 않는다.
  • Backend와 AI Server는 Recreate 배포를 사용한다.
  • AI Server는 현재 main CI 성공 + Docker Build Check + ECR Image 존재를 배포 조건으로 사용한다.
  • 배포 성공은 단순 Container 실행이 아니라 Health Check로 판단한다.
  • 실패 시 직전 정상 Artifact로 복구한다.
  • 여러 Component가 함께 변경되면 AI → Backend → Frontend 순으로 배포하되 API 하위 호환성을 유지한다.
  • GitHub Actions → AWS는 OIDC, EC2 배포 명령은 SSM을 사용한다.
  • Production 배포는 공통 Concurrency Group으로 직렬화한다.
⚠️ **GitHub.com Fallback** ⚠️