Skip to content

feat: dev/prod 환경 분리 — main 머지 시 dev 배포, 태그 시 prod 배포 (#33)#34

Open
jaeyeonling wants to merge 5 commits into
mainfrom
feat/33-dev-prod-environment-split
Open

feat: dev/prod 환경 분리 — main 머지 시 dev 배포, 태그 시 prod 배포 (#33)#34
jaeyeonling wants to merge 5 commits into
mainfrom
feat/33-dev-prod-environment-split

Conversation

@jaeyeonling

Copy link
Copy Markdown
Contributor

closes #33

요약

GitHub Environments(dev/prod)로 secret을 자동 lookup하고, CodeDeploy hook에 자동 주입되는 DEPLOYMENT_GROUP_NAME suffix(-dev)로 deploy.sh가 PM2명/디렉토리를 분기한다. workflow가 environment별로 appspec destination을 sed 치환하고 S3 키를 분리해 추후 EC2 분리 시 코드 변경 0을 보장한다.

변경

  • .github/workflows/deploy.yml (+31 / -6) — main push trigger, environment 동적 결정, sed step, S3 key 분리, timeout-minutes
  • scripts/deploy.sh (+19 / -4) — DEPLOYMENT_GROUP_NAME case 분기 + 운영 컨벤션 주석
  • CLAUDE.md (+74 / -23) — Operations 섹션 사전/사후/상시 분리 + dev environment 가이드

검증

정적 PASS · 페르소나 5명 PASS · plan 4회 · check 2회 — 정정/수정 사이클이 길었음, 머지 후 동작 확인 권장.

상세는 본 PR의 자동 게시 코멘트 참조.

CodeDeploy hook 실행 시 자동 주입되는 DEPLOYMENT_GROUP_NAME으로
PM2 프로세스명과 배포 디렉토리를 분기한다.

- *-dev suffix → discord-bot-dev / /home/ubuntu/discord-bot-dev
- 그 외 → discord-bot / /home/ubuntu/discord-bot (prod 폴백)

추후 EC2 2대 분리 시 deploy.sh 변경 없이 deployment-group의
EC2 tag만 교체하면 되도록 분기를 단일 환경변수에 위임했다.
- on.push에 branches: [main] 추가 (기존 v[1-9]* 태그 트리거 유지)
- environment / env.ENV / concurrency.group을 ref 기반 동적 결정
  (tags면 prod, 아니면 dev)
- Override appspec destination for dev step 추가
  - sed로 destination을 /home/ubuntu/discord-bot-dev로 치환
  - grep으로 치환 결과 검증 (sed silently fail 방지)
- S3 패키지 키와 CodeDeploy s3-location key를 environment별 분리
  (deployment-package-{env}.zip) — dev/prod zip race 차단

GitHub Environments에 dev/prod를 두면 secret이 자동 우선 lookup되어
secret 동적 lookup 코드 없이 환경별 다른 값을 사용할 수 있다.
- CI/CD 섹션: 트리거별 환경 매핑 표 + main push 시 ci.yml/deploy.yml
  독립 동시 실행 명시
- Operations 섹션 재구성:
  - Discord Developer Portal에서 dev 봇 앱 생성 (Gateway 1세션 제약)
  - GitHub Environments dev/prod에 secret 등록 (환경별 동일 키, 다른 값)
  - prod environment required reviewer 1명 (안전망)
  - CodeDeploy dev deployment group 추가 (단일 EC2 + 추후 분리 호환)
  - EC2 /home/ubuntu/discord-bot-dev 디렉토리 사전 생성
- DOTENV 미등록 시 매 main push deploy fail되는 동작을 명시
- deploy.sh: prod 그룹명에 -dev 포함 금지 운영 컨벤션 주석 강화
  (현 case 패턴은 *-dev 끝매칭이라 'discord-bot-production-dev' 같은
  prod 그룹명을 dev로 잘못 분기시킬 수 있음 → 컨벤션으로 차단)
- deploy.yml: 동기화 주석에 concurrency.group 추가 (기존엔 environment/env.ENV만 명시),
  CodeDeploy hang 방지를 위해 timeout-minutes: 15 추가 (기본 30분 wait 차단)
- CLAUDE.md: Operations 섹션을 '사전 준비 (1~4)' / '첫 배포 후 정리 (5)' /
  '배포 보안 (상시)'로 분리. install-pm2.sh 경로의 ... 플레이스홀더를
  curl 다운로드 명령으로 대체 (첫 배포 전 디렉토리 부재 상태 대응).
  DOTENV에 .env.example 참조 명시, DEPLOYMENT_GROUP_NAME 컨벤션 강조,
  dev 디렉토리 생성 타이밍 명시, prod DEPLOYMENT_GROUP_NAME 예시 추가.
@jaeyeonling

Copy link
Copy Markdown
Contributor Author

TL;DR

  • 후속 이슈 후보 5건 (아래 ## 다음 작업)
  • 1차에서 CRITICAL 2건 + HIGH 5건 잡혔다 모두 해소 또는 본 PR 무관으로 분류 (598826e)
  • 검증 강도: plan 4회 · check 2회 — 정정/수정 사이클이 길었음, 머지 후 동작 확인 권장

다음 작업

  • chmod 600 .env permission denied 가능성 검증 (Chaos HIGH) — CodeDeploy agent가 root로 .env 복사 후 runas: ubuntu deploy.sh가 chmod 시도 시 EPERM 가능. 첫 v1.x.x 배포 전 검증 필요.
  • scripts/ubuntu/install-code-deploy.sh 정합성 (Maintainer HIGH) — ap-northeast-2 리전 S3 URL 하드코딩 + set -euo pipefail 누락. install-pm2.sh 대비 일관성 결여.
  • deploy.yml paths 필터 도입 검토 (Chaos HIGH) — 현재 docs/scripts만 수정해도 dev 재배포. 의도된 동작이지만 부담 vs 의도 절충.
  • main + tag 동시 push race 시나리오 방어 (Chaos HIGH) — 발생 가능성 낮으나 발생 시 pm2 save race로 EC2 재부팅 후 자동 시작 실패 가능.
  • ENV 표현식 단일 source 추출 (Chaos MEDIUM) — environment / env.ENV / concurrency.group 세 곳 표현식 동기화 enforcement 없음. composite action 검토.

리뷰 시 확인 — 1차에서 잡혔다 해소된 우려

  • First-Timer HIGH · CLAUDE.md install-pm2.sh 경로의 ... 플레이스홀더 → 598826e (curl 다운로드 명령으로 대체)
  • First-Timer HIGH · CLAUDE.md dev DOTENV 등록 안내에 .env.example 참조 누락 → 598826e (테이블에 명시적 링크 추가)
  • Maintainer HIGH · deploy.yml 동기화 주석에 concurrency.group 누락 → 598826e (셋 다 동기화 주석으로 강화)
  • Maintainer HIGH · *-dev 컨벤션 분산 + 위반 시 피해 근거 부재 → 598826e (deploy.sh "운영 컨벤션 !!" 블록 + CLAUDE.md "중요 ②" 박스에 위반 시나리오까지 서술)
  • Watchdog HIGH · aws deploy wait job timeout 부재 (최대 30분 hang) → 598826e (timeout-minutes: 15 추가, 비관 추정 10.8분 대비 38% 여유)
  • Chaos CRITICAL · *-dev suffix 매칭으로 discord-bot-production-dev 같은 prod 그룹명이 dev로 잘못 분기 → 598826e (코드 패턴 유지하고 운영 컨벤션 강제 — 코드+문서 두 곳에 prod 그룹명에 -dev 금지 명시)

본 PR 무관: Chaos/Maintainer HIGH ×2 는 기존 코드 잠재 이슈(scripts/deploy.sh:35의 chmod 권한, scripts/ubuntu/install-code-deploy.sh 리전 하드코딩)로 #33 도입 아님 — 후속 이슈로 분리.

참고: 분석 상세 — 스코프 · 작업 순서 · 검증 시도 추적

스코프

project-only · 외부 파일 영향: 없음

최종 계획 (v4) 작업 순서

  1. appspec.yml: 변경 없음. destination = /home/ubuntu/discord-bot (4칸 들여쓰기 유지). 검증: grep -cE "^[[:space:]]*destination:" appspec.yml = 1
  2. scripts/deploy.sh: DEPLOYMENT_GROUP_NAME 분기 case 추가 (*-dev → discord-bot-dev / 그 외 → discord-bot prod 폴백). 검증: bash -n scripts/deploy.sh
  3. .github/workflows/deploy.yml: trigger에 branches: [main] + 기존 tags 유지, environment / env.ENV / concurrency.group 동적 결정, dev 전용 sed step + grep fail-fast 검증, S3 key 환경별 분리, timeout-minutes: 15. 검증: yaml 구문 + 실제 push로 environment 활성화 확인
  4. CLAUDE.md: Operations 섹션을 사전 준비(1~4) / 첫 배포 후 정리(5) / 배포 보안(상시) 분리, GitHub Environments dev/prod 가이드 + 운영 컨벤션 강조

계획 진화 (v1 → v4)

  • v1: GitHub Environments 자동 lookup + DEPLOYMENT_GROUP_NAME 분기 + appspec staging 우회 경로 도입
  • v2: staging 우회 제거 (workflow에서 sed 치환으로 단순화) + S3 key 환경별 분리 + ci.yml/deploy.yml 동시 실행 documentation
  • v3: Upload to S3 step S3 key 변경 명시 + sed 패턴에 ^[[:space:]]* capture group + grep fail-fast 검증 + workflow-level env → job-level env 이동
  • v4: deploy.sh DEPLOYMENT_GROUP_NAME 분기 코드 명시 (case 구문 형태) + create-dotenv composite action이 이미 fast-fail 처리함을 명시 + concurrency.group은 env 컨텍스트 미가용으로 표현식 직접 작성 확정

plan 시도별 발견 우려

  • 시도 1/3: BLOCK (CRITICAL 1건 — concurrency 그룹 race + S3 키 미분리, HIGH 3건 — staging 우회 복잡성, S3 key 분리, ci/deploy 동시 실행 미문서화) — Scope Watcher
  • 시도 2/3: BLOCK (HIGH 1건 — Upload to S3 step 변경 명시 누락, MEDIUM 1건 — sed 패턴 들여쓰기 불일치)
  • 시도 3/3: BLOCK (HIGH 1건 — deploy.sh DEPLOY_DIR 분기 코드 미명시, MEDIUM 2건)
  • 시도 소진: 사용자 판단 위임 → v4 확정 후 build 진입

check 시도별 진화

  • 시도 1/3: BLOCK (Chaos CRITICAL 2건, First-Timer HIGH 2건, Maintainer HIGH 2건, Watchdog HIGH 1건) — *-dev 잘못된 매칭, install-pm2.sh 경로 placeholder, 동기화 주석 누락, aws deploy wait timeout 부재 등
  • 시도 2/3: PASS — CRITICAL 0건, HIGH 1명만 지적 2건 (모두 PR 범위 외 기존 코드 → 후속 이슈로 분리). First-Timer/Watchdog HIGH 전부 해소.

판정 기준 (모든 PR 공통)

등급 합의 판정 규칙
CRITICAL 어느 1명이라도 지적 → 블록 (즉시 수정 후 재검증)
HIGH 2명 이상 지적 → 블록 / 1명만 지적 → 경고 (작성자 판단 위임)
MEDIUM 기록만, PR 진입 가능
LOW 기록만, PR 진입 가능

처리 컬럼: 해소 (수정 커밋으로 닫힘) / 후속 (별도 이슈로 분리) / 무관 (기존 코드 또는 다른 이슈 영역)

검증 강도 권고 임계값: plan ≥ 3회 또는 check ≥ 2회 → 정정/수정 사이클이 길었음, 머지 후 동작 확인 권장

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements multi-environment support for the deployment pipeline, including detailed documentation updates in CLAUDE.md and logic changes in scripts/deploy.sh to branch between dev and prod environments based on the deployment group name. Feedback was provided to improve the safety of the deployment script by adding a validation check for the DEPLOYMENT_GROUP_NAME variable, preventing potential accidental deployments to the production environment if the variable is missing.

Comment thread scripts/deploy.sh Outdated
Comment on lines +14 to +23
case "${DEPLOYMENT_GROUP_NAME:-}" in
*-dev)
PM2_NAME=discord-bot-dev
DEPLOY_DIR=/home/ubuntu/discord-bot-dev
;;
*)
PM2_NAME=discord-bot
DEPLOY_DIR=/home/ubuntu/discord-bot
;;
esac

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

DEPLOYMENT_GROUP_NAME 변수가 비어있거나 예상치 못한 패턴일 경우 기본적으로 운영(prod) 환경(*) 분기)으로 동작하는 'fail-open' 방식입니다. 이는 환경 변수 주입 실패나 설정 실수 시 운영 환경의 디렉토리나 PM2 프로세스를 의도치 않게 조작할 위험이 있습니다.

변수가 설정되지 않았을 경우 배포를 즉시 중단(exit 1)하도록 검증 로직을 추가하는 것이 안전합니다.

Suggested change
case "${DEPLOYMENT_GROUP_NAME:-}" in
*-dev)
PM2_NAME=discord-bot-dev
DEPLOY_DIR=/home/ubuntu/discord-bot-dev
;;
*)
PM2_NAME=discord-bot
DEPLOY_DIR=/home/ubuntu/discord-bot
;;
esac
if [ -z "${DEPLOYMENT_GROUP_NAME:-}" ]; then
echo "Error: DEPLOYMENT_GROUP_NAME is not set. Deployment aborted." >&2
exit 1
fi
case "${DEPLOYMENT_GROUP_NAME}" in
*-dev)
PM2_NAME=discord-bot-dev
DEPLOY_DIR=/home/ubuntu/discord-bot-dev
;;
*)
PM2_NAME=discord-bot
DEPLOY_DIR=/home/ubuntu/discord-bot
;;
esac

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 지적 감사합니다. fail-closed로 변경했습니다 (7e8bc3e).

원래는 "CodeDeploy 정상 흐름에서는 항상 주입됨"을 근거로 prod 폴백을 두었지만, 말씀대로 변수 주입 실패나 로컬 수동 실행 시 prod를 의도치 않게 건드릴 위험이 더 큽니다.

검증:

  • unset/empty → exit 1 + 명확한 에러 메시지
  • *-dev / 그 외 분기는 그대로 동작

PR #34 Gemini 리뷰 반영.

기존: 미정의 시 prod 폴백 (fail-open) — 변수 주입 실패나 로컬 수동
실행 시 prod 디렉토리/PM2 프로세스를 의도치 않게 조작할 위험.

변경: 미정의/빈 값 시 즉시 exit 1로 배포 중단.
CodeDeploy 정상 흐름에서는 항상 주입되므로 정상 운영에 영향 없음.

검증:
- DEPLOYMENT_GROUP_NAME unset/empty → exit 1 + 명확한 에러 메시지
- *-dev → discord-bot-dev / DEPLOY_DIR 분기 그대로
- 그 외 → discord-bot prod 분기 그대로
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: dev/prod 환경 분리 — main 머지 시 dev 배포, 태그 시 prod 배포

1 participant