CD - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki

3๋‹จ๊ณ„: CD(์ง€์†์  ๋ฐฐํฌ) ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•

1. ๊ฐœ์š”

1.1. CD์˜ ํ•„์š”์„ฑ

  • Continuous Deployment๋Š” ์ฝ”๋“œ๊ฐ€ ์ค€๋น„๋˜๋Š” ์ฆ‰์‹œ ์‚ฌ๋žŒ์˜ ์Šน์ธ ์—†์ด ์ž๋™์œผ๋กœ ์šด์˜ ํ™˜๊ฒฝ์— ๋ฐ˜์˜ํ•˜์—ฌ, ๋ฐฐํฌ ์†๋„๋ฅผ ์ตœ๋Œ€ํ™”ํ•˜๊ณ  ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ง€์—ฐยท์ˆ˜๋™ ์ž‘์—…์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ.
  • ๊ฐœ๋ฐœ ๊ณผ์ •: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ, ๋ฒ„๊ทธ ์ˆ˜์ •์„ ๊ฐœ๋ฐœ ์‹œ ๋ฐ˜์˜ ๋‚ด์—ญ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ์„œ๋ฒ„ ๋“ฑ์— ๋ฐฐํฌ ํ•„์š”
  • ์šด์˜ ๊ณผ์ •: ์ˆ˜๋™ ๋ฐฐํฌ ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ˆ˜ ์›์ฒœ ๋ฐฉ์ง€, ์†Œ๋ชจ ์‹œ๊ฐ„ ๋ฐ ๋น„์šฉ ๊ฐ์†Œ ์œ„ํ•ด ํ•„์š”

1.2. ๊ธฐ๋Œ€ํšจ๊ณผ

  • ์ˆ˜๋™ ๋ฐฐํฌ ๋Œ€๋น„ ์‹œ๊ฐ„ ๋Œ€ํญ ๊ฐ์†Œย (15๋ถ„ โ†’ 3~5๋ถ„)
  • ์ˆ˜๋™ ๋ฐฐํฌ ์‹ค์ˆ˜ ์›์ฒœ ๋ฐฉ์ง€
  • ๋กค๋ฐฑ ์ž๋™ํ™” ๊ธฐ๋ฐ˜ ํ™•๋ณด
  • ์ž‘์€ ๋‹จ์œ„์˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ์„ ์ž์ฃผ ๋ฐฐํฌํ•˜์—ฌ ๋น ๋ฅธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ

1.3. ์š”์•ฝ

  1. Artifacts:ย AWS S3 (Versioning: Commit SHA)
  2. Agent:ย AWS CodeDeploy
  3. Secrets:ย AWS Parameter Store
  4. Notification:ย Discord Webhook
  5. Environment Strategy:
    • Dev/Staging: Continuous Deployment (์ฆ‰์‹œ ๋ฐฐํฌ)
    • Prod: Continuous Delivery (์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ)
  6. Deployment Strategy:ย Blue/Green

2. ์˜์‚ฌ ๊ฒฐ์ •

2.1. Artifact Repo

2.1.1. Artifact Repo ์š”๊ตฌ์‚ฌํ•ญ

  • CI ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ(jar, zip, static files ๋“ฑ)์„ ์ง€์นญ
  • ํ˜„์žฌ ์ปจํ…Œ์ด๋„ˆ(Docker)๋ฅผ ๋„์ž…ํ•˜์ง€ ์•Š์€ ๋‹จ๊ณ„์ด๋ฏ€๋กœ Artifact Repo๋Š” ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ํŒŒ์ผ์„ ์›๋ณธ ๊ทธ๋Œ€๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ.
  • ๋ฒ„์ „ ๊ด€๋ฆฌ: ๋ฐฐํฌ ์‹คํŒจ ์‹œ ์ฆ‰์‹œ ๋กค๋ฐฑ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์•„ํ‹ฐํŒฉํŠธ์˜ ๋ฒ„์ „ ์‹๋ณ„์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•จ.
  • ์šฐ์ˆ˜ํ•œ ์ ‘๊ทผ์„ฑ: CD ๋‹จ๊ณ„์—์„œ ๋ณด์•ˆ ์ธ์ฆ ๋ฐ ํ†ต์‹ ์ด ์›ํ™œํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ์ง„ํ–‰๋˜์–ด์•ผ ํ•จ
  • ์œ ์ง€๋ณด์ˆ˜ ์ตœ์†Œํ™”: ๋ณ„๋„์˜ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜์ง€์•Š๊ณ  ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์•ผํ•จ.

2.1.2. ํ›„๋ณด ๊ธฐ์ˆ 

  1. GitHub Actions (https://docs.github.com/ko/[email protected]/actions/tutorials/store-and-share-data)
  • CI ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰ ์ค‘์— ์ƒ์„ฑ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ™์€ ์›Œํฌํ”Œ๋กœ์šฐ ๋‚ด์˜ ๋‹ค๋ฅธ Job๊ณผ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜, ์‹คํ–‰ ์™„๋ฃŒ ํ›„ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Œ
  • ์žฅ์ 
    • ๋ณ„๋„์˜ ์Šคํ† ๋ฆฌ์ง€ ๋ฐ ์„œ๋น„์Šค ๊ตฌ์ถ• ์—†์ด GitHub Actions ์Šคํฌ๋ฆฝํŠธ๋กœ ๊ตฌ์ถ• ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
    • ๋ฌด๋ฃŒ ํ”Œ๋žœ ๋‚ด ์Šคํ† ๋ฆฌ์ง€ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ๋‹จ์ 
    • ๋ณด์กด ๊ธฐ๊ฐ„: ๋ณด๊ด€ ๊ธฐ๊ฐ„ 90์ผ๋กœ ์ œํ•œ
    • ๋‚ฎ์€ ์ ‘๊ทผ์„ฑ: CD ๊ณผ์ •์—์„œ EC2 ์ธ์Šคํ„ด์Šค์— ์ „์†กํ•˜๋ ค๋ฉด GitHub API ํ† ํฐ ์ธ์ฆ์„ ํ†ตํ•œ ์„ค์ • ํ•„์š”
    • ์šฉ๋„ ๋ถˆ์ผ์น˜: Build Job โ†’ Test Job์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ, ๋กœ๊ทธ ์ €์žฅ์„ ์œ„ํ•ด ์„ค๊ณ„๋œ ๊ธฐ๋Šฅ.

โ‡’ CD๋ฅผ ์œ„ํ•œ ์ €์žฅ์†Œ๋กœ๋Š” ๋ถ€์ ํ•ฉ

  1. AWS S3
  • AWS์˜ ๊ฐ์ฒด ์Šคํ† ๋ฆฌ์ง€ ์„œ๋น„์Šค
  • ์žฅ์ 
    • ๋†’์€ ์ ‘๊ทผ์„ฑ: AWS EC2์—์„œ IAM Role ๊ธฐ๋ฐ˜์œผ๋กœ ์ธ์ฆ ์—†์ด ์ ‘๊ทผ ๋ฐ ์—ฐ๋™ ๊ฐ€๋Šฅ. ์ถ”ํ›„ AWS Code Deploy ์—ฐ๋™ ์‚ฌ์šฉ์‹œ ํŽธ์˜์„ฑ
    • ๋น„์šฉ: AWS ์˜ˆ์‚ฐ ๋‚ด์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜์—ฌ ์ถ”๊ฐ€ ๋น„์šฉ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ
    • ์œ ์—ฐ์„ฑ: Artifact ํŒŒ์ผ์„ ์›๋ณธ์œผ๋กœ ์ €์žฅ ๊ฐ€๋Šฅ
    • ๋ฒ„์ „ ๊ด€๋ฆฌ: S3 ๋ฒ„ํ‚ท ๋ฒ„์ „ ๊ด€๋ฆฌ / ๊ณ„์ธตํ˜• ๊ตฌ์กฐ ์‚ฌ์šฉ์œผ๋กœ ๋กค๋ฐฑ ์‹œ์  ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
  • ๋‹จ์ 
    • ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋“ฑ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๋ถ€์žฌ

โ‡’ ํ˜„์žฌ ์‚ฌ์šฉ ๊ตฌ์กฐ ๋ฐ ์ถ”ํ›„ AWS ์ธํ”„๋ผ ๋‚ด์—์„œ์˜ ํ™•์žฅ์„ ๊ณ ๋ คํ•˜์—ฌ ๊ฐ€์žฅ ์ ์ ˆํ•˜๋‹ค ํŒ๋‹จ.

  1. Jfrog Artifatory
  • ๋ฒ”์šฉ artifact Repository ์†”๋ฃจ์…˜(ํŒŒ์ผ, ํŒจํ‚ค์ง€, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€ ๋“ฑ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ)
  • ์žฅ์ 
    • ์œ ์—ฐ์„ฑ: ๋‹ค์–‘ํ•œ ์–ธ์–ด์™€ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋ฅผ ์ง€์›, ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ
    • ๋ณด์•ˆ: Xray ๊ธฐ๋Šฅ์„ ํ†ตํ•œ ์•„ํ‹ฐํŒฉํŠธ ๋ณด์•ˆ ์ทจ์•ฝ์  ์Šค์บ” ๊ฐ€๋Šฅ
  • ๋‹จ์ 
    • ๋น„์šฉ: SaaS ์‚ฌ์šฉ ์‹œ ์ถ”๊ฐ€ ๋น„์šฉ ๋ฐœ์ƒ. ๋ฌด๋ฃŒ ๋ฒ„์ „์€ ์šด์˜์„ ์œ„ํ•œ Self-host ์ธํ”„๋ผ ๊ตฌ์ถ• ํ•„์š”
    • Over engineering: CI/CD ์ „๋ฐ˜์— ๊ฑธ์ณ ์•„ํ‹ฐํŒฉํŠธ ๋ฟ๋งŒ ์•„๋‹Œ ๋‹ค์–‘ํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๊ด€๋ฆฌ๋ฅผ ์ œ๊ณต. ๋‹จ์ˆœ Artifact Repo ๋กœ์„œ ๊ณผ๋„ํ•œ ๊ธฐ๋Šฅ

โ‡’ ๊ทœ๋ชจ์— ๋งž์ง€ ์•Š๋Š” ๊ณผ๋„ํ•œ ์„œ๋น„์Šค

  1. Sonatype Nexus Repository Manager(https://help.sonatype.com/en/sonatype-nexus-repository.html)
  • ์†Œํ”„ํŠธ์›จ์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ์˜์กด์„ฑ, ์•„ํ‹ฐํŒฉํŠธ ์ €์žฅ ๊ด€๋ฆฌ ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ(Java ์ƒํƒœ๊ณ„ ์ค‘์‹ฌ)
  • ์žฅ์ 
    • ๊ธฐ๋Šฅ: ๋‹ค์–‘ํ•œ ๋ฐฐํฌ ์ •์ฑ…์„ ์ง€์›(Release, Snapshot, Mixed)ํ•˜์—ฌ ๊ด€๋ฆฌ ์šฉ์ด
    • Proxy Repository: ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์บ์‹ฑํ•˜์—ฌ ๋น ๋ฅธ ๋นŒ๋“œ ๊ฐ€๋Šฅ
  • ๋‹จ์ 
    • ๋น„์šฉ: ์„œ๋น„์Šค ์‹คํ–‰์„ ์œ„ํ•œ ์ธํ”„๋ผ๋ฅผ ์ง์ ‘ ๊ตฌ์ถ•ํ•ด์•ผํ•จ. (์ƒ์šฉ Saas ์†”๋ฃจ์…˜์€ ์œ ๋ฃŒ ํ”Œ๋žœ)

โ‡’ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ค‘์‹ฌ ๊ธฐ๋Šฅ, ๊ณผ๋„ํ•œ ์„œ๋น„์Šค

2.2. Versioning

2.2.1. ๋ฒ„์ €๋‹์˜ ํ•„์š”์„ฑ

  • ์ถ”์ : ํ˜„์žฌ ๋ฐฐํฌ๋˜์–ด ํ…Œ์ŠคํŠธ/์šด์˜ ์ค‘์ธ ํŒŒ์ผ์ด ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๊ฒƒ์ธ์ง€ ์‹๋ณ„
  • ์œ ์ผ์„ฑ: ๋ฒ„์ „๊ฐ„ ์ด๋ฆ„์„ ๊ตฌ๋ถ„ํ•˜์—ฌ ํ˜ผ๋ž€ ๋ฐ ์—๋Ÿฌ ๋ฐฉ์ง€
  • ๋กค๋ฐฑ: ์žฅ์•  ๋ฐœ์ƒ ์‹œ ์–ด๋–ค ๋ฒ„์ „์œผ๋กœ ๋Œ์•„๊ฐˆ ์ง€ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ

2.2.2. ํ›„๋ณด๊ตฐ

  1. Commit SHA
  • Git์˜ ์ปค๋ฐ‹ ํ•ด์‹œ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฒ„์ „ ๋ช…์œผ๋กœ ์‚ฌ์šฉ
  • ์žฅ์ 
    • ์™„๋ฒฝํ•œ ์ถ”์  ๊ฐ€๋Šฅ
    • ์ž๋™ํ™” ์šฉ์ด: ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ณ„๋„ ๋กœ์ง ์—†์ด GitHub Actions ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๋ฐ”๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ
  • ๋‹จ์ 
    • ์ˆœ์„œ ๋ฐ ์‹œ์  ํŒŒ์•… ๋ถˆ๊ฐ€
    • ๋‚ฎ์€ ๊ฐ€๋…์„ฑ
  1. Semantic Versioning(SemVer)
  • Major.Minor.Patch ํ˜•์‹์˜ ์ „ํ†ต์  ๋ฒ„์ €๋‹ ๋ฐฉ๋ฒ•(์˜ˆ: 0.1.2 ๋ฒ„์ „)
  • ์žฅ์ 
    • ๋ช…ํ™•ํ•œ ์˜๋ฏธ: ์—…๋ฐ์ดํŠธ ์ค‘์š”๋„์— ๋”ฐ๋ฅธ ๋ฒ„์ „ ์ฐจ์ด๋ฅผ ์ง๊ด€์ ์œผ๋กœ ๊ตฌ๋ถ„ ๊ฐ€๋Šฅ
  • ๋‹จ์ 
    • ์ž๋™ํ™” ์–ด๋ ค์›€: ๋งค ๋ฐฐํฌ๋งˆ๋‹ค Major, Minor,Patch ์ค‘ ์–ด๋–ค ๋ฒ„์ „ ์—…์ธ์ง€์— ๋”ฐ๋ผ ์ˆ˜๋™์œผ๋กœ ๊ตฌ๋ถ„์ง€์–ด์ฃผ๊ฑฐ๋‚˜, ๋ณต์žกํ•œ ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ ํ•„์š”
  1. Timestamp
  • ๋นŒ๋“œ๋œ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ๋ฒ„์ „๋ช…์œผ๋กœ ์‚ฌ์šฉ
  • ์žฅ์ 
    • ์ •๋ ฌ ์ตœ์ ํ™”: ํŒŒ์ผ๋ช…์œผ๋กœ ์ •๋ ฌ ๋ฐ ์ตœ๊ทผ ๋ฒ„์ „ ํƒ์ƒ‰์ด ๊ฐ€๋Šฅ
    • ์ง๊ด€์„ฑ: ์–ธ์ œ ๋ฐฐํฌ๋œ ํŒŒ์ผ์ธ์ง€ ํŒŒ์•…ํ•˜๊ธฐ ์‰ฌ์›€
  • ๋‹จ์ 
    • ์ฝ”๋“œ ์ถ”์  ์–ด๋ ค์›€: ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋นŒ๋“œํ–ˆ๋Š”์ง€ ์•Œ์ˆ˜ ์—†์Œ
  1. ์ตœ์ข… ๊ฒฐ์ •: Hybrid ์ „๋žต
  • {YYYYMMDD_HHmmss}-{SHA}.{ํ™•์žฅ์ž} ๊ตฌ์กฐ๋กœ ์ž‘์„ฑ
  • ์žฅ์ 
    • ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์•ž์— ๋‘์–ด ์ •๋ ฌ์„ ํ†ตํ•ด ์ตœ์‹  ๋ฐฐํฌ ํŒŒ์ผ์„ ์ฆ‰์‹œ ์‹๋ณ„ ๊ฐ€๋Šฅ
    • ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ SHA ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ์ถ”์  ๊ฐ€๋Šฅ
    • ์ž๋™ํ™”: ์ถ”๊ฐ€ ๋กœ์ง ์—†์ด ๋‹จ์ˆœ ๋ช…๋ น์–ด๋กœ ์ƒ์„ฑ ๊ฐ€๋Šฅ
    • ๋กค๋ฐฑ: ํƒ€์ž„์Šคํƒฌํ”„, SHA ์ •๋ณด ๊ธฐ๋ฐ˜์œผ๋กœ ์‹œ๊ฐ„๊ธฐ์ค€, ๊ธฐ๋Šฅ ๋ฐฐํฌ ๊ธฐ์ค€(์ฝ”๋“œ ์ˆ˜์ •)์œผ๋กœ ๋ชจ๋‘ ๋กค๋ฐฑ ๊ฐ€๋Šฅ

2.3. ์„œ๋ฒ„ ์šด์˜ ์ „๋žต

2.3.1. ์„œ๋ฒ„ ์š”๊ตฌ์‚ฌํ•ญ

  • ๊ฐœ๋ฐœ ์„œ๋ฒ„: ๋†’์€ ์„ฑ๋Šฅ ๋ถˆํ•„์š”(์šด์˜ ์„œ๋ฒ„์™€ ๊ฐ™์„ ํ•„์š” ์—†์Œ), ์ƒ์‹œ ํ…Œ์ŠคํŠธ ๋ฐ ๋””๋ฒ„๊น…์„ ์œ„ํ•ด ์ƒ์‹œ ์šด์˜ ํ•„์š”
  • ์šด์˜ ์„œ๋ฒ„: ๋†’์€ ์„ฑ๋Šฅ ํ•„์š”. ์„œ๋น„์Šค ์ƒ์‹œ ์šด์˜ ํ•„์š”
  • ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„: ์šด์˜ ํ™˜๊ฒฝ ๊ฒ€์ฆ์„ ์œ„ํ•ด ์œ ์‚ฌํ•˜๊ฑฐ๋‚˜ ๋™์ผํ•œ ์ธํ”„๋ผ ํ•„์š”. ๋น„์šฉ ์ ˆ๊ฐ์„ ์œ„ํ•œ ๋ฐฉ์•ˆ ํ•„์š”

2.3.2. ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„ ์šด์˜ ์ „๋žต ๋น„๊ต

๋น„์šฉ ์ถ”์ •์€ t3.medium ์„œ์šธ๋ฆฌ์ „ ๊ธฐ์ค€

  1. ์„ ํƒ์  ์šด์˜
  • ์šด์˜ ๋ฐฐํฌ ์ง์ „, QA ํ•„์š”์‹œ์—๋งŒ ์ˆ˜๋™์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ์‹œ์ž‘ํ•˜๊ณ , ๊ฒ€์ฆ ํ›„ ์ˆ˜๋™์œผ๋กœ ์ค‘์ง€ํ•˜๋Š” ๋ฐฉ์‹
  • ์žฅ์ 
    • ๋น„์šฉ ์ตœ์ ํ™”: ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ ์‹œ๊ฐ„์—๋งŒ ์šด์˜
    • ๋‹จ์ˆœํ•จ
  • ๋‹จ์ 
    • ์ž๋™ํ™” ๋‹จ์ ˆ: CI/CD ํŒŒ์ดํ”„๋ผ์ธ์€ ์„œ๋ฒ„๊ฐ€ ์ผœ์ง„ ์ƒํ™ฉ์„ ์ „์ œ๋กœ ๋™์ž‘. ์™„์ „ ์ž๋™ํ™” ๋ถˆ๊ฐ€๋Šฅ
    • Cold Start: ์„œ๋ฒ„ ์‹œ์ž‘, ์„œ๋น„์Šค ๊ตฌ๋™๊นŒ์ง€ ๋Œ€๊ธฐ ์‹œ๊ฐ„ ๋ฐœ์ƒ

โ‡’ ๊ทน ์ดˆ๊ธฐ ๋ฐฐํฌ์‹œ, ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„์—์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฑฐ์˜ ์—†๋Š” ๊ฒฝ์šฐ ์„ ํƒ ๊ฐ€๋Šฅ. ์ •๊ธฐ ๋ฐฐํฌ, ์ˆ˜์‹œ ๋ฐฐํฌ ์‚ฌ์ดํด์ด ์™„์„ฑ๋˜๋ฉด ์™„์ „ ์ž๋™ํ™”๋กœ ์ „ํ™˜ ๊ฐ€๋Šฅ

  1. On-Demand(์ƒ์‹œ ์šด์˜)
  • ์šด์˜ ์„œ๋ฒ„์™€ ๋น„์Šทํ•œ ๊ตฌ์กฐ๋กœ ์ฆ‰์‹œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•˜๋‚˜, ๋งŽ์€ ๋น„์šฉ ๋ฐœ์ƒ
  • ๋น„์šฉ ์ถ”์ •: ์•ฝ $37
  1. On-Demand + Scheduler(Time-based)
  • ์—…๋ฌด์‹œ๊ฐ„, ๋ฐฐํฌ ์ด์ „ ์‹œ๊ฐ„์—๋งŒ ์ผœ๋‘๊ณ , ์ด ์™ธ ์‹œ๊ฐ„์€ ์ž๋™์œผ๋กœ ๋„๋Š” ๋ฐฉ์‹
  • ์‚ฌ์šฉ ์‹œ๊ฐ„๋งŒํผ ๊ณผ๊ธˆ์œผ๋กœ ๋น„์šฉ ์ ˆ์•ฝ ๊ฐ€๋Šฅ.
  • ๋‹ค์–‘ํ•œ ๋ฐฐํฌ๊ฐ€ ์ง„ํ–‰๋˜๋Š” ์ดˆ๊ธฐ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์— ๋Œ€์‘ํ•˜๊ธฐ ์–ด๋ ค์›€
  • ๋ฐค, ์ฃผ๋ง ์‹œ๊ฐ„ ๋ฐฐํฌ ํ•„์š” ์‹œ ์ˆ˜๋™ ์ž‘๋™ ํ•„์š”
  • ๋น„์šฉ ์ถ”์ •: ์•ฝ $11(์˜จ๋””๋ฉ˜๋“œ ๋Œ€๋น„ ์•ฝ 70% ์ ˆ๊ฐ)
  1. Spot Instance
  • AWS ์ŠคํŒŸ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์Šทํ•œ ์ธํ”„๋ผ๋ฅผ ์ €๊ฐ€์— ์ผœ๋‘๋Š” ๋ฐฉ์‹
  • ์ƒ์‹œ ์šด์˜์œผ๋กœ ์ฆ‰์‹œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
  • AWS ํšŒ์ˆ˜ ์‹œ ๊บผ์งˆ ์ˆ˜ ์žˆ์Œ. ์ƒํƒœ ์œ ์ง€(EBS ์„ค์ •) ํ•„์š”(Interruption Behavior ์ˆ˜์ •, ๊ธฐ์กด Terminate(์‚ญ์ œ) โ†’ Stop(์ค‘์ง€) ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด EBS ์œ ์ง€ ์ƒํƒœ๋กœ ์žฌ์‹œ์ž‘ ๊ฐ€๋Šฅ)
  • ASG์™€ ์—ฐ๋™ํ•˜์—ฌ ์ž๋™ ๋ณต๊ตฌ ๊ฐ€๋Šฅ(Spot Instance๋กœ ๊ตฌ์„ฑ๋œ ASG ์ƒ์„ฑ)
  • ๋น„์šฉ ์ถ”์ •: ์•ฝ $11(์˜จ๋””๋ฒค๋“œ ๋Œ€๋น„ ์•ฝ 70% ์ ˆ๊ฐ)
  1. ์ž„์‹œ ํ™˜๊ฒฝ ๊ตฌ์ถ•
  • Dev โ†’ Release PR ์ƒ์„ฑ์„ ํŠธ๋ฆฌ๊ฑฐ๋กœ ์ž„์‹œ ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑ ํ›„ ๋ฐฐํฌ.
  • ์™„๋ฒฝํ•œ ๊ฒฉ๋ฆฌ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ ๋ฐ ๋น„์šฉ ๊ทน๋‹จ์  ์ ˆ์•ฝ
  • ๋ณต์žกํ•œ ๊ตฌ์ถ• ๋ฐฉ์‹(IaC)ํ•„์š”.(์˜ค๋ฒ„์—”์ง€๋‹ˆ์–ด๋ง)

์ตœ์ข… ๊ฒฐ์ •: Spot Instance ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„ ๊ตฌ์ถ•

ํ™˜๊ฒฝ ์šด์˜ ๋ฐฉ์‹ ์žฅ์  ๋‹จ์ 
๊ฐœ๋ฐœ (Dev) ์ƒ์‹œ ๊ฐ€๋™, ์ฆ‰์‹œ ๋ฐฐํฌ - ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ ์ตœ์ ํ™” (๊ธฐ๋‹ค๋ฆผ ์—†์Œ). - ์–ธ์ œ๋“  ์ ‘์†ํ•ด ๋กœ๊ทธ ํ™•์ธ ๊ฐ€๋Šฅ. - ์ƒ์‹œ ์šด์˜์œผ๋กœ ์ธํ•œ ๋น„์šฉ ๋ฐœ์ƒ.
์Šคํ…Œ์ด์ง• (Staging) AWS Spot Instance ์‚ฌ์šฉ - ๋น„์šฉ ์ ˆ๊ฐ ํšจ๊ณผ ํ™•์‹ค. - ๋น ๋ฅธ ์Šคํ…Œ์ด์ง• ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ ์ค‘๋‹จ ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ
์šด์˜ (Prod) ์ƒ์‹œ ๊ฐ€๋™, ์ˆ˜๋™ ์Šน์ธ - ์•ˆ์ •์„ฑ ํ™•๋ณด. - ์‚ฌ์šฉ์ž ์„œ๋น„์Šค ๋ณด์žฅ.

2.4. Rollback ์ „๋žต

  1. ๊ฐœ๋ฐœ ์„œ๋ฒ„: ์ˆ˜๋™ ๋กค๋ฐฑ
  • ๋””๋ฒ„๊น… ํ™˜๊ฒฝ ๋ณด์กด์„ ์œ„ํ•ด ์ƒํƒœ๋ฅผ ์œ ์ง€ ํ•  ํ•„์š”๊ฐ€ ์žˆ์Œ
  • ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋Š” ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑ ํ›„ ๋‹ค์‹œ ํ™•์ธํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค ๋ฒ„๊ทธ ์ˆ˜์ • ํ›„ ์ปค๋ฐ‹ํ•˜์—ฌ ์žฌ ๋ฐฐํฌํ•˜๊ณ  ํ™•์ธํ•˜๋Š”๊ฒƒ์ด ํšจ์œจ์ 
  1. ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„: Hybrid
  • ๋ฐฐํฌ ์‹คํŒจ ์‹œ ์ž๋™ ๋กค๋ฐฑ
    • ์šด์˜๊ณผ ๋™์ผํ•œ ๋ฐฐํฌ / ๋กค๋ฐฑ ์Šคํฌ๋ฆฝํŠธ์˜ ์ž‘๋™์„ ํ™•์ธํ•ด์•ผ ํ•จ
    • ํ…Œ์ŠคํŠธ(QA)๋ฅผ ์œ„ํ•œ ์„œ๋ฒ„ ์ž‘๋™ ํ•„์š”
  • ๊ธฐ๋Šฅ ๊ฒฐํ•จ ์‹œ ์ˆ˜๋™ ๋กค๋ฐฑ
    • ๋ฐฐํฌ์—๋Š” ๋ฌธ์ œ ์—†์œผ๋‚˜, ๊ธฐ๋Šฅ์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ ๋ฐ ๊ฐœ๋ฐœ์ž ํ™•์ธ ํ›„ ์ˆ˜๋™ ๋กค๋ฐฑ ๋ฐ ํ•ซํ”ฝ์Šค ๋ฐฐํฌ
  1. ์šด์˜์„œ๋ฒ„: ์ž๋™ ๋กค๋ฐฑ(+ ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ)
  • ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์„œ๋ฒ„ ๋‹ค์šดํƒ€์ž„ ์ตœ์†Œํ™” ์œ„ํ•ด ์ง์ „ ๋ฐฐํฌ๋กœ ์ž๋™ ๋กค๋ฐฑ
  • ์ˆจ์€ ๋กœ์ง ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ ๋Œ€์‘์„ ์œ„ํ•œ ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ๋Š” ํ•„์š”

2.5. ํ™˜๊ฒฝ๋ณ„ CD ์šด์˜ ์ „๋žต

ํ™˜๊ฒฝ ๋Œ€์ƒ ๋ธŒ๋žœ์น˜ ํŠธ๋ฆฌ๊ฑฐ(Trigger) ์กฐ๊ฑด ๋ฐฐํฌ ์ „๋žต ์„ค๋ช…
Dev (๊ฐœ๋ฐœ) dev ์ž๋™ ํŠธ๋ฆฌ๊ฑฐ(PR Merge ์‹œ) ๋‹จ์ˆœ ์žฌ์‹œ์ž‘ - ์†๋„ ์šฐ์„  - ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ฆฌ์ž๋งˆ์ž ์„œ๋ฒ„์— ๋ฐ˜์˜๋˜์–ด ๊ธฐ๋Šฅ ํ™•์ธ ๊ฐ€๋Šฅ - ๋‹ค์šดํƒ€์ž„ ํ—ˆ์šฉ
Staging (๊ฒ€์ฆ) release ์ž๋™ ํŠธ๋ฆฌ๊ฑฐ(PR Merge ์‹œ) Blue/Green (๋ฌด์ค‘๋‹จ ๋ฐฐํฌ) - ๊ฒ€์ฆ ์šฐ์„  - ์šด์˜ ๋ฐฐํฌ ์ „, ์‹ค์ œ ๋ฐ์ดํ„ฐ์™€ ์œ ์‚ฌํ•œ ํ™˜๊ฒฝ์—์„œ QA ์ง„ํ–‰ - ์šด์˜ํ™˜๊ฒฝ๊ณผ ๋™์ผํ•œ ์„ค์ • ๊ฒ€์ฆ
Prod (์šด์˜) main ์ˆ˜๋™ ์Šน์ธ (Manual) Blue/Green(๋ฌด์ค‘๋‹จ ๋ฐฐํฌ) - ์•ˆ์ •์„ฑ ์šฐ์„  - main์— ๋จธ์ง€๋˜์–ด๋„ ์ฆ‰์‹œ ๋ฐฐํฌํ•˜์ง€ ์•Š์Œ - ๊ด€๋ฆฌ์ž๊ฐ€ GitHub Actions์—์„œ ์Šน์ธํ•˜์—ฌ ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ - ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ

2.6. ๋ฐฐํฌ ์—์ด์ „ํŠธ ์„ ํƒ

๋ฐฉ์‹ ์„ค๋ช… ์žฅ์  (Pros) ๋‹จ์  (Cons) ์šฐ๋ฆฌ ์„œ๋น„์Šค ์ ํ•ฉ์„ฑ
SSH Script (via GitHub Actions) GitHub Runner๊ฐ€ EC2์— SSH๋กœ ์ ‘์†ํ•˜์—ฌ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ - ๊ตฌ์ถ• ์†๋„: appleboy/ssh-action ์‚ฌ์šฉํ•˜์—ฌ ๋น ๋ฅธ ์„ค์ • ๊ฐ€๋Šฅ - ์œ ์—ฐ์„ฑ: ๋ฆฌ๋ˆ…์Šค ๋ช…๋ น์–ด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. - ๋ณด์•ˆ: GitHub Secrets์— PEM ํ‚ค๋ฅผ ์ €์žฅํ•ด์•ผ ํ•จ, ssh ํฌํŠธ ์ „์ฒด ๊ฐœ๋ฐฉ ํ•„์š” - IP ์ œํ•œ: ๋ณด์•ˆ๊ทธ๋ฃน(SG)์—์„œ GitHub IP ๋Œ€์—ญ์„ ์—ด๊ฑฐ๋‚˜ Bastion์„ ๊ฑฐ์ณ์•ผ ํ•จ. - ํ™•์žฅ์„ฑ: ์ถ”ํ›„ ์˜คํ† ์Šค์ผ€์ผ๋ง ์ ์šฉ ์‹œ ์ž๋™ ๋ฐฐํฌ ๋ถˆ๊ฐ€ - ๋‹จ์ผ ์ธ์Šคํ„ด์Šค ํ™˜๊ฒฝ์—์„œ ๊ฐ€์žฅ ๋น ๋ฅด๊ณ  ์ง๊ด€์  - ์ถ”ํ›„ ASG ์„ค์ • ์‹œ AWS CodeDeploy๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•„์ˆ˜.(๊ธฐ์ˆ ๋ถ€์ฑ„ ๋ฐœ์ƒ)
AWS CodeDeploy AWS์˜ ๊ณต์‹ ๋ฐฐํฌ ๊ด€๋ฆฌ ์„œ๋น„์Šค ์‚ฌ์šฉ (Agent ์„ค์น˜ ํ•„์š”) - ์•ˆ์ •์„ฑ: ๋ฐฐํฌ ์‹คํŒจ ์‹œ ์ž๋™ ๋กค๋ฐฑ ๊ธฐ๋Šฅ ๋‚ด์žฅ. - ํ™•์žฅ์„ฑ: ์ถ”ํ›„ Auto Scaling Group ์ ์šฉ ์‹œ ์ž๋™ ๋ฐฐํฌ ์ง€์›. - ๋ฌด๋ฃŒ: EC2 ๋ฐฐํฌ ์‹œ ์ถ”๊ฐ€ ๋น„์šฉ ์—†์Œ. - ๋ณด์•ˆ: Inbound ํฌํŠธ ์˜คํ”ˆ ๋ถˆํ•„์š” - ๋Ÿฌ๋‹ ์ปค๋ธŒ: IAM Role, AppSpec.yml ๋“ฑ ์ดˆ๊ธฐ ์„ค์ • ๋ณต์žก. - ์ข…์†์„ฑ: AWS ์ „์šฉ ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ ํ•„์š”. - ๋‹ค์ค‘ ์ธ์Šคํ„ด์Šค(ASG) ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๋ฉด ๋„์ž… ํ•„์ˆ˜ - ๊ธฐ์ˆ ๋ถ€์ฑ„ ๋ฐฉ์ง€ ๋ฐ ๋ณด์•ˆ์„ฑ, ์•ˆ์ •์„ฑ ์šฐ์ˆ˜.
Self-hosted Runner EC2 ๋‚ด๋ถ€์— GitHub Runner ์„ค์น˜ํ•˜์—ฌ ์‹คํ–‰ - ๋ณด์•ˆ ์ตœ์ƒ: ์™ธ๋ถ€์—์„œ ๋“ค์–ด์˜ค๋Š” SSH ํฌํŠธ(22)๋ฅผ ์—ด ํ•„์š” ์—†์Œ (Outbound ํ†ต์‹ ). - ๋ฆฌ์†Œ์Šค ์†Œ๋ชจ: Runner ํ”„๋กœ์„ธ์Šค๊ฐ€ ์„œ๋ฒ„ CPU/RAM์„ ์ ์œ ํ•จ (๋‹จ์ผ ์ธ์Šคํ„ด์Šค์— ์น˜๋ช…์ ). - ๊ด€๋ฆฌ ๋น„์šฉ: Runner ์—…๋ฐ์ดํŠธ ๋ฐ ์ข€๋น„ ํ”„๋กœ์„ธ์Šค ๊ด€๋ฆฌ ํ•„์š”. ๋‹จ์ผ ์ธ์Šคํ„ด์Šค ํ™˜๊ฒฝ์ธ V1์—์„œ๋Š” ๋ถˆ๊ฐ€. ์„œ๋ฒ„ ์ž์›์ด ์ถ”๊ฐ€ ํ•„์š”ํ•˜์—ฌ ๋ถ€์ ์ ˆํ•˜๋‹ค๊ณ  ํŒ๋‹จ.

2.7. ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…

  1. GitHub Secret
    • ๋ฐฐํฌ ์‹œ์ ์— GitHub๊ฐ€ย .envย ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ์„œ๋ฒ„์— ์ „์†ก
    • ์žฅ์ : ๊นƒํ—ˆ๋ธŒ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์—์„œ ๋ฐ”๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Œ
    • ๋‹จ์ : ๋ณด์•ˆ๋ฌธ์ œ(Secret ๊ฐ’์ด ์„œ๋ฒ„ ๋””์Šคํฌ์— ๋‚จ์Œ)
  2. AWS Parameter Store
    • ๋ฐฐํฌ ํ›„ ์„œ๋ฒ„ ๋ถ€ํŒ… ์‹œ์ ์— AWS API ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
    • ์žฅ์ : ๋ณด์•ˆ(๊ฐ’์„ ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ์ €์žฅํ•˜๊ฑฐ๋‚˜, ์•”ํ˜ธํ™” ๊ด€๋ฆฌ ๊ฐ€๋Šฅ), ์ด๋ ฅ ๊ด€๋ฆฌ ๋ฐ ๊ณ„์ธตํ˜• ๊ด€๋ฆฌ ๊ฐ€๋Šฅ
    • ๋‹จ์ : ๋ณต์žกํ•œ ์„ค์ •(SDK ์—ฐ๋™ ์ฝ”๋“œ ์ž‘์„ฑ ํ•„์š”)

2.8. ์•Œ๋ฆผ ์ฑ„๋„

  1. Discord(์„ ์ •)
    • ํ˜„์žฌ ํŒ€ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ์ค‘์ธ ๋ฉ”์‹ ์ €.
    • ์žฅ์ : ๋น„์šฉ ๋ฌด๋ฃŒ, ๋‹จ์ˆœํ•œ ์—ฐ๋™(Webhook)
    • ๋‹จ์ : ๊ฐœ๋ฐœ ๋„๊ตฌ์— ์ตœ์ ํ™” ๋˜์ง€ ์•Š์€ ํ”Œ๋žซํผ
  2. Slack
    • ๋ฉ”์‹ ์ € ๊ธฐ๋ฐ˜ ํ˜‘์—… ํˆด
    • ์žฅ์ : ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ๋„๊ตฌ์™€ ์—ฐ๋™์„ ์ง€์›ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • ๋‹จ์ : ์ถ”๊ฐ€ ๋น„์šฉ ๋ฐœ์ƒ(๋ฌด๋ฃŒ ํ”Œ๋žœ ์‚ฌ์šฉ์‹œ 90์ผ ์ดํ›„ ๋ฉ”์‹œ์ง€ ์‚ญ์ œโ†’ ๋ฐฐํฌ ๋“ฑ ์ด๋ ฅ ์ถ”์  ๋ถˆ๊ฐ€)

2.9. ๋ฐฐํฌ ๋ฒ”์œ„ ๋ฐ ๋ฐฉ์‹

  • Dev(๊ฐœ๋ฐœ), Staging ์„œ๋ฒ„

    • ๊ฐœ๋ฐœ ๋ฐ ๊ฒ€์ฆ ๊ณผ์ •์€ ํ•˜๋ฃจ์— ์ˆ˜ ํšŒ ๋ฐœ์ƒ
    • ๋น ๋ฅธ MVP ๊ฐœ๋ฐœ๊ณผ์ •์„ ๋ณด์กฐํ•˜๊ธฐ ์œ„ํ•ด ๋น ๋ฅธ ๋ฐฐํฌ ํ•„์š”
    • ๋ฒ„๊ทธ๊ฐ€ ์žˆ์–ด๋„ ํ™•์ธ ํ›„ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๋ฏ€๋กœ ์Šน์ธ ์ ˆ์ฐจ ๋ถˆ์š”

    โ‡’ Continous Deployment(์™„์ „ ์ž๋™ ๋ฐฐํฌ) ์„ ์ •

  • Production(์šด์˜)

    • ์šด์˜ ๋ฐฐํฌ๋Š” ๋‚ฎ์€ ๋นˆ๋„๋กœ ๋ฐœ์ƒ
    • ์šด์˜์˜ ์•ˆ์ •์„ฑ์ด ์ตœ์šฐ์„ 
    • ํœด๋จผ์—๋Ÿฌ(์ž˜๋ชป๋œ ์ฝ”๋“œ ๋จธ์ง€, ์ˆ˜์ • ๋“ฑ)๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๋ฐฐํฌ ์ „ ํ•œ๋ฒˆ ๋” ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋Š” ์•ˆ์ „์žฅ์น˜ ํ™•๋ณด ํ•„์š”

    โ‡’Continuous Delivery(์Šน์ธ ํ›„ ๋ฐฐํฌ) ์„ ์ •

2.10. ๋ฐฐํฌ ์ „๋žต

  • ์„œ๋น„์Šค ํŠน์„ฑ(์ทจ์—… ์ผ์ • ๊ด€๋ฆฌ, AI ํ”ผ๋“œ๋ฐฑ)์ƒ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(๋Š๊น€ ์—†์Œ) ์ค‘์š”
  • ์„œ๋น„์Šค ์ค‘๋‹จ ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ
    • ์ผ์ • ๊ด€๋ฆฌ ์„œ๋น„์Šค์—์„œ ์•Œ๋ฆผ ๋จนํ†ต ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ
    • ์ผ์ • ์ƒ์„ฑ, ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ธ€ ์ž‘์„ฑ ๋„์ค‘ ์—ฐ๊ฒฐ ๋Š๊น€์œผ๋กœ ๋ฐ์ดํ„ฐ ์œ ์‹ค
    • AI ์ด๋ ฅ์„œ ํ”ผ๋“œ๋ฐฑ ์‘๋‹ต ์ค‘ (Long-polling) ์š”์ฒญ ๋Š๊น€
  • ๋”ฐ๋ผ์„œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ์š”๊ตฌ

๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ „๋žต ๋น„๊ต

  • ๋กค๋ง ์—…๋ฐ์ดํŠธ

    • ์ผ๋ถ€ ์„œ๋ฒ„๋ฅผ ๋„๊ณ  ์ƒˆ ๋ฒ„์ „์„ ์‹คํ–‰ํ•˜์—ฌ ์ ์ง„์ ์œผ๋กœ ๊ต์ฒดํ•˜๋Š” ๋ฐฉ์‹. ๋‹จ์ผ ์ธ์Šคํ„ด์Šค์˜ ๊ฒฝ์šฐ ๋‹ค์šดํƒ€์ž„์ด ๋ฌด์กฐ๊ฑด ๋ฐœ์ƒ.
    • ์žฅ์ : ์ถ”๊ฐ€ ๋ฆฌ์†Œ์Šค ๋ถˆ์š”
    • ๋‹จ์ : ์„œ๋น„์Šค ์ค‘๋‹จ ๋ฐœ์ƒ

    โ‡’ ๋ฌด์ค‘๋‹จ ํ•„์š”ํ•˜๋ฏ€๋กœ ๋ฐฐ์ œ

  • ๋ธ”๋ฃจ/๊ทธ๋ฆฐ

    • ๋™์ผํ•œ ์ธํ”„๋ผ๋ฅผ ํ•˜๋‚˜ ๋” ๋„์šฐ๋Š” ๋ฐฉ์‹
    • ์žฅ์ : ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ฆ‰์‹œ ๋กค๋ฐฑ ๊ฐ€๋Šฅ, ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ฐ€๋Šฅ
    • ๋‹จ์ : ์ผ์‹œ์ ์œผ๋กœ ์ธํ”„๋ผ 2๋ฐฐ ํ•„์š”

    โ‡’ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฌด์ค‘๋‹จ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ

  • ์นด๋‚˜๋ฆฌ

    • ํŠธ๋ž˜ํ”ฝ์˜ ์ผ๋ถ€๋งŒ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ๋ณด๋‚ด, ์ ์ง„์  ์ „ํ™˜ํ•˜๋Š” ๋ฐฉ์‹
    • ์žฅ์ : ์„œ๋ฒ„ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ๋ฆฌ์Šคํฌ ์ตœ์†Œํ™” ๊ฐ€๋Šฅ, ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ์— ์œ ์šฉ
    • ๋‹จ์ : ๋ผ์šฐํŒ… ์„ค์ •์˜ ๋ณต์žก์„ฑ, ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ ์žฅ๋น„ ํ•„์š”, ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜ ๋™์•ˆ ์ธํ”„๋ผ 2๋ฐฐ ํ•„์š”

    โ‡’ ๋งŽ์ง€ ์•Š์€ ํŠธ๋ž˜ํ”ฝ ์ค‘ ์ผ๋ถ€๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๋ถ„์„ํ•ด์•ผํ•˜๋ฏ€๋กœ, ํŠธ๋ž˜ํ”ฝ์ด ์ ์€ ํ˜„ ์‹œ์ ์—์„œ๋Š” ํšจ์œจ์„ฑ์ด ๋–จ์–ด์ ธ ๋ฐฐ์ œ (10%๋ฅผ ์šฐ์„  ๋ณด๋‚ด๋Š” ์ƒํ™ฉ ๊ฐ€์ • ์‹œ V1 ํŠธ๋ž˜ํ”ฝ ํ”ผํฌ 100๋ช… ๊ธฐ์ค€ 10๋ช… ๋งŒ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์ „ํ™˜)

  • ์…ฐ๋„์šฐ ๋ฐฐํฌ

    • ํŠธ๋ž˜ํ”ฝ ์ผ๋ถ€๋ฅผ ์ƒˆ ๋ฒ„์ „์— ์ „๋‹ฌํ•˜๋ฉฐ, ์‘๋‹ต์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹
    • ์นด๋‚˜๋ฆฌ ์ „๋žต๊ณผ์˜ ์ฐจ์ด์ 
      • ์นด๋‚˜๋ฆฌ๋Š” ์ผ๋ถ€ ํŠธ๋ž˜ํ”ฝ์„ ๋Œ€์ƒ์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ๋„. ์…ฐ๋„์šฐ๋Š” ์ „์ฒด ํŠธ๋ž˜ํ”ฝ ๋Œ€์ƒ
      • ์นด๋‚˜๋ฆฌ๋Š” ์ƒˆ ๋ฒ„์ „ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์ผ๋ถ€ ํŠธ๋ž˜ํ”ฝ์ด ์˜ํ–ฅ ๋ฐ›์Œ. ์…ฐ๋„์šฐ๋Š” ์˜ํ–ฅ ์—†์Œ.
    • ์žฅ์ : ์‹ค์ œ ํŠธ๋ž˜ํ”ฝ์„ ํ†ตํ•œ ๊ฒ€์ฆ ๊ฐ€๋Šฅ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๋ฌด์†์‹ค, ์ƒˆ ๋ฒ„์ „ ๋Œ€์ƒ ์‹ค์‹œ๊ฐ„ ํ”ผ๋“œ๋ฐฑ ๊ฐ€๋Šฅ, ์ƒˆ ๋ฒ„์ „ ๋ฌธ์ œ ๋ฐœ์ƒ์‹œ์—๋„ ์„œ๋น„์Šค ์˜ํ–ฅ ์—†์Œ
    • ๋‹จ์ : ํŠธ๋ž˜ํ”ฝ ์„ค์ • ๋ณต์žก์„ฑ, ์ธํ”„๋ผ ๋‘๋ฐฐ ํ•„์š”, Shadow ํŠธ๋ž˜ํ”ฝ ์‘๋‹ต ๋ถ„์„์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๋„๊ตฌ ํ•„์š”, ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜ ๋™์•ˆ ์ธํ”„๋ผ 2๋ฐฐ ํ•„์š”

    โ‡’ ๊ทน๋‹จ์ ์œผ๋กœ ๋†’์€ ์‹ ๋ขฐ์„ฑ์„ ์š”๊ตฌํ•˜๋Š” ๊ฒฝ์šฐ ์ ์ ˆํ•˜๊ฒ ์œผ๋‚˜, ์ฆ‰์‹œ ๋กค๋ฐฑ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์ตœ์šฐ์„  ๊ณ ๋ คํ•˜์ง€ ์•Š์Œ.

โ‡’ ๋ธ”๋ฃจ/๊ทธ๋ฆฐ ๋ฐฐํฌ ์„ ์ •

3. ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์ƒ์„ธ ์„ค๊ณ„

์ด์ „ ๋‹จ๊ณ„์˜ ์˜์‚ฌ๊ฒฐ์ •์„ ๋ฐ”ํƒ•์œผ๋กœ CD ๊ณผ์ •์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๊ณ„.

3.1. ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ํ๋ฆ„๋„

  • CI ์™„๋ฃŒ(S3) โ†’ ๋ธŒ๋žœ์น˜ ๋ถ„๊ธฐ โ†’ ํ™˜๊ฒฝ๋ณ„(Dev/Staging/Prod) ๋ฐฐํฌ ํ”„๋กœ์„ธ์Šค ๋‹ค์ด์–ด๊ทธ๋žจ

3.2. ์„œ๋ฒ„ ๋‚ด๋ถ€ ๋ฐฐํฌ/๋กค๋ฐฑ ํ๋ฆ„๋„ (Micro View)

  • CodeDeploy Agent ๋™์ž‘ โ†’ ํฌํŠธ ํ™•์ธ โ†’ Blue/Green ์Šค์œ„์นญ โ†’ ํ—ฌ์Šค ์ฒดํฌ โ†’ ๋กค๋ฐฑ/์„ฑ๊ณต ํŒ์ • ๋ฉ”์ปค๋‹ˆ์ฆ˜

3.2.1. ๊ฐœ๋ฐœ์„œ๋ฒ„

๋ฐฐํฌ ๋ฐฉ์‹ ์š”์•ฝ

  1. Stop: ๋Œ๊ณ  ์žˆ๋˜, ๊ตฌ๋ฒ„์ „ ํ”„๋กœ์„ธ์Šค ๋ฐ ์„œ๋ฒ„๋ฅผ ์ข…๋ฃŒ(๋‹ค์šดํƒ€์ž„ ํ—ˆ์šฉ)

  2. Update: Artifact Repo์ธ S3์—์„œ ์ตœ์‹  ํŒŒ์ผ์„ ๋ฐ›์•„์™€ ๋ฎ์–ด์“ฐ๊ธฐ

  3. Start: ์ƒˆ ๋ฒ„์ „์„ ๋ฐ›์•„์™€ ์‹คํ–‰

  4. ๊ฒ€์ฆ: ์ƒˆ ๋ฒ„์ „์˜ ์ •์ƒ ์‹คํ–‰ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌ

    4.1. ์ •์ƒ ์‹คํ–‰: ๋ฐฐํฌ ์™„๋ฃŒ ์•Œ๋ฆผ ๋ฐ ํ›„์† ์ž‘์—…

    4.2. ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ์‹คํŒจ ์•Œ๋ฆผ ๋ฐ ํ™˜๊ฒฝ ๋ณด์กด

  5. ๋กค๋ฐฑ: ๋””๋ฒ„๊น… ํ›„ ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ ์ดํ›„ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑ ๋ฐ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์‹ ๊ทœ ๋ฐฐํฌ

ํ•„์ˆ˜ ์„ค์ • ์‚ฌํ•ญ

  • EC2 ์„ค์ •

    • ์ธ์Šคํ„ด์Šค ์„ค์ •: t3.medium

    • ์Šค์™‘ ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •: 4GB

    • ์„œ๋ฒ„ ์ดˆ๊ธฐ ์„ค์ • ์Šคํฌ๋ฆฝํŠธ (๊ฐ์ข… ์˜์กด์„ฑ ๋ฒ„์ „์€ ์Šคํƒ์— ๋งž๊ฒŒ ํ™•์ • ํ›„ ์ˆ˜์ • ์˜ˆ์ •)

      #!/bin/bash
      # 1. ์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ ๋ฐ ํ•„์ˆ˜ ํŒจํ‚ค์ง€ ์„ค์น˜
      sudo apt-get update -y
      sudo apt-get install -y ruby wget curl git jq awscli
      
      # 2. Swap ๋ฉ”๋ชจ๋ฆฌ 4GB ์„ค์ •
      sudo fallocate -l 4G /swapfile
      sudo chmod 600 /swapfile
      sudo mkswap /swapfile
      sudo swapon /swapfile
      echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
      
      # 3. CodeDeploy Agent ์„ค์น˜
      cd /home/ubuntu
      wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
      chmod +x ./install
      sudo ./install auto
      
      # 4. Runtimes ์„ค์น˜
      # 4-1. Java 21
      wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo apt-key add -
      echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
      sudo apt-get update
      sudo apt-get install -y temurin-21-jdk
      
      # 4-2. Node.js 20 & PM2
      curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
      sudo apt-get install -y nodejs
      sudo npm install -g pm2
      
      # 4-3. Python 3.10 & venv
      sudo apt-get install -y python3.10 python3.10-venv python3-pip
      
      # 5. ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ์„ค์ •
      mkdir -p /home/ubuntu/apps/{backend,frontend,ai}
      sudo chown -R ubuntu:ubuntu /home/ubuntu/apps
      
  • AWS CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ์„ค์ •

    • ๋ฐฐํฌ ์œ ํ˜•: In-Place ๋ฐฐํฌ
    • ๋ฐฐํฌ ๊ตฌ์„ฑ: CodeDeployDefault.AllAtOnceย (ํ•œ ๋ฒˆ์— ๋ฐฐํฌ)
    • IAM Role ์„ค์ •:ย Parameter Store๋ฅผ ํ†ตํ•œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…์„ ์œ„ํ•ด AmazonSSMReadOnlyAccessย ๊ถŒํ•œ ์„ค์ •
    • ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ: ์„ค์ • ํ•ด์ œ
    • ๋กค๋ฐฑ ๊ตฌ์„ฑ: ํ•ด์ œ
  • ์„œ๋น„์Šค ๋ณ„ ์„ค์ • ๋ฐ ์Šคํฌ๋ฆฝํŠธ

    • BE(Spring Boot)

      • ๊ฒฝ๋กœ: /home/ubuntu/apps/backend

      • ํฌํŠธ: 8080

      • appspec.yml

        version: 0.0
        os: linux
        files:
          - source: /
            destination: /home/ubuntu/apps/backend
            overwrite: true
        
        permissions:
          - object: /home/ubuntu/apps/backend/scripts
            pattern: "**"
            owner: ubuntu
            group: ubuntu
            mode: 755
        
        hooks:
          ApplicationStop:
            - location: scripts/stop.sh
              timeout: 60
              runas: ubuntu
          ApplicationStart:
            - location: scripts/start.sh
              timeout: 60
              runas: ubuntu
          ValidateService:
            - location: scripts/validate.sh
              timeout: 60
              runas: ubuntu
        
      • stop.sh

        #!/bin/bash
        # ์‹คํ–‰ ์ค‘์ธ JAR ํ”„๋กœ์„ธ์Šค ์ฐพ๊ธฐ
        PID=$(pgrep -f 'backend.jar')
        
        if [ -z "$PID" ]; then
          echo ">>> ๊ตฌ๋™ ์ค‘์ธ ๋ฐฑ์—”๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—†์Šต๋‹ˆ๋‹ค."
        else
          echo ">>> ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ๋„ (PID: $PID)"
          kill -15 $PID # Graceful Shutdown
          sleep 5
        fi
        
      • start.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/backend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ] ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ(ERR) ์‹œ ์‹คํ–‰๋จ
        error_handler() {
          echo ">>> [Error] ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์‹คํŒจ"
          bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ (Backend)" "ApplicationStart ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.\n๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”." "fail"
        }
        # ์—๋Ÿฌ(ERR) ์‹œ๊ทธ๋„์„ ๋ฐ›์œผ๋ฉด error_handler ํ•จ์ˆ˜ ์‹คํ–‰
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        # ์‹œ์ž‘ ์•Œ๋ฆผ ์ „์†ก
        echo ">>> [Start] ๋ฐฑ์—”๋“œ ๋ฐฐํฌ ์‹œ์ž‘"
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹œ์ž‘ (Backend)" "Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค." "info"
        
        # ์•ฑ ์‹คํ–‰
        nohup java -jar backend.jar > app.log 2>&1 &
        
        # nohup์€ ์„ฑ๊ณตํ•ด๋„ ๋ฐ”๋กœ ๋ฆฌํ„ด. ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ง„์งœ ๋–ด๋Š”์ง€ 1์ดˆ ๋’ค ํ™•์ธ
        sleep 2
        PID=$(pgrep -f 'backend.jar')
        
        if [ -z "$PID" ]; then
          echo ">>> ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
          exit 1 # ์—ฌ๊ธฐ์„œ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋ฉด ์œ„ trap์ด ๋ฐœ๋™ํ•ด์„œ ๋””์Šค์ฝ”๋“œ ์•Œ๋ฆผ
        fi
        
        echo ">>> [Start] ํ”„๋กœ์„ธ์Šค ์‹คํ–‰ ํ™•์ธ๋จ (PID: $PID)"
        
      • validate.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/backend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ]
        error_handler() {
          bash $DISCORD_SCRIPT "ํ—ฌ์Šค ์ฒดํฌ ์‹คํŒจ (Backend)" "์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (Timeout/500).\nํ™•์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค." "fail"
        }
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        echo ">>> [Validate] Health Check ์‹œ์ž‘"
        
        for i in {1..10}; do
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)
          
          if [ "$response" == "200" ]; then
            echo ">>> Health Check ์„ฑ๊ณต (200 OK)"
            
            # ์ตœ์ข… ์„ฑ๊ณต ์•Œ๋ฆผ
            bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (Backend)" "ํ—ฌ์Šค ์ฒดํฌ ํ†ต๊ณผ. ์ •์ƒ ์„œ๋น„์Šค ์ค‘์ž…๋‹ˆ๋‹ค." "success"
            exit 0
          fi
          
          bash $DISCORD_SCRIPT "๊ตฌ๋™ ๋Œ€๊ธฐ์ค‘... ($i/10)" "์‘๋‹ต ์ฝ”๋“œ: $response" "info"
          sleep 10
        done
        
        # ๋ฐ˜๋ณต๋ฌธ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ 200์ด ์•ˆ ๋‚˜์˜ค๋ฉด ์—๋Ÿฌ ์ฒ˜๋ฆฌ
        echo ">>> Health Check ์ตœ์ข… ์‹คํŒจ"
        exit 1 # -> trap ๋ฐœ๋™ -> ์‹คํŒจ ์•Œ๋ฆผ ์ „์†ก
        
    • FE(Next.js)

      • ๊ฒฝ๋กœ: /home/ubuntu/apps/frontend

      • ํฌํŠธ: 3000

      • appspec.yml

        version: 0.0
        os: linux
        files:
          - source: /
            destination: /home/ubuntu/apps/frontend
            overwrite: true
        
        permissions:
          - object: /home/ubuntu/apps/frontend/scripts
            pattern: "**"
            owner: ubuntu
            group: ubuntu
            mode: 755
        
        hooks:
          ApplicationStop:
            - location: scripts/stop.sh
              timeout: 60
              runas: ubuntu
          AfterInstall:
            - location: scripts/install_dependencies.sh
              timeout: 300
              runas: ubuntu
          ApplicationStart:
            - location: scripts/start.sh
              timeout: 60
              runas: ubuntu
          ValidateService:
            - location: scripts/validate.sh
              timeout: 60
              runas: ubuntu
        
      • stop.sh

        #!/bin/bash
        echo ">>> [Stop] Frontend ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ"
        pm2 delete frontend || true
        
      • install_dependencies.sh

        ๐Ÿ’กNEXT_PUBLIC_ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” ์‹คํ–‰ ๋‹จ๊ณ„์—์„œ ์ฃผ์ž…ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ. ๋นŒ๋“œ๊ฐ€ ์ง„ํ–‰๋˜๋Š” CI์—์„œ ์ฃผ์ž… ํ•„์š”!

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/frontend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ]
        error_handler() {
          echo ">>> [Error] ์˜์กด์„ฑ ์„ค์น˜ ์‹คํŒจ"
          bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ (Frontend)" "AfterInstall(npm ci) ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." "fail"
        }
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        echo ">>> [Install] Production ์˜์กด์„ฑ ์„ค์น˜"
        # CI์—์„œ node_modules๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์„ค์น˜
        npm ci --only=production
        
      • start.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/frontend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ]
        error_handler() {
          echo ">>> [Error] PM2 ์‹คํ–‰ ์‹คํŒจ"
          bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ (Frontend)" "ApplicationStart(PM2) ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค." "fail"
        }
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        # ์‹œ์ž‘ ์•Œ๋ฆผ
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹œ์ž‘ (Frontend)" "Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(PM2) ๊ตฌ๋™์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค." "info"
        
        echo ">>> [Start] Frontend(PM2) ์‹คํ–‰"
        # ๊ธฐ์กด ๋กœ๊ทธ ๋น„์šฐ๊ธฐ
        pm2 flush frontend || true
        
        # PM2 ์‹œ์ž‘ (์ด๋ฏธ ๋– ์žˆ์œผ๋ฉด ๋ฆฌ์Šคํƒ€ํŠธ, ์—†์œผ๋ฉด ์‹œ์ž‘)
        # ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” ๋นŒ๋“œ ํƒ€์ž„ ์ฃผ์ž… or ๋Ÿฐํƒ€์ž„ ์ฝ”๋“œ ๋ ˆ๋ฒจ ์ฒ˜๋ฆฌ ๊ฐ€์ •
        pm2 start npm --name "frontend" -- start
        
      • validate.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/frontend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ]
        error_handler() {
          bash $DISCORD_SCRIPT "ํ—ฌ์Šค ์ฒดํฌ ์‹คํŒจ (Frontend)" "์„œ๋ฒ„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‘๋‹ตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (Timeout/500)." "fail"
        }
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        echo ">>> [Validate] Frontend ๊ตฌ๋™ ํ™•์ธ"
        
        for i in {1..10}; do
          # Next.js ๋ฉ”์ธ ํŽ˜์ด์ง€(3000ํฌํŠธ) ํ™•์ธ
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000)
          
          if [ "$response" == "200" ]; then
            echo ">>> Frontend ์ •์ƒ ๋™์ž‘ ํ™•์ธ"
            
            # ์ตœ์ข… ์„ฑ๊ณต ์•Œ๋ฆผ
            bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (Frontend)" "ํ—ฌ์Šค ์ฒดํฌ ํ†ต๊ณผ! ์ •์ƒ ์„œ๋น„์Šค ์ค‘์ž…๋‹ˆ๋‹ค." "success"
            exit 0
          fi
          
          bash $DISCORD_SCRIPT "๊ตฌ๋™ ๋Œ€๊ธฐ์ค‘... ($i/10)" "์‘๋‹ต ์ฝ”๋“œ: $response" "info"
          sleep 5
        done
        
        echo ">>> Frontend ๊ตฌ๋™ ์‹คํŒจ"
        exit 1 # -> trap ๋ฐœ๋™
        
    • AI(FastAPI)

      • ๊ฒฝ๋กœ: /home/ubuntu/apps/ai

      • ํฌํŠธ: 8000

      • appspec.yml

        version: 0.0
        os: linux
        files:
          - source: /
            destination: /home/ubuntu/apps/ai
            overwrite: true
        
        permissions:
          - object: /home/ubuntu/apps/ai/scripts
            pattern: "**"
            owner: ubuntu
            group: ubuntu
            mode: 755
        
        hooks:
          ApplicationStop:
            - location: scripts/stop.sh
              timeout: 60
              runas: ubuntu
          AfterInstall:
            - location: scripts/setup_env.sh
              timeout: 60
              runas: ubuntu
          ApplicationStart:
            - location: scripts/start.sh
              timeout: 180 # AI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋กœ๋”ฉ ์‹œ๊ฐ„ ๊ณ ๋ ค
              runas: ubuntu
        
      • stop.sh

        #!/bin/bash
        PID=$(pgrep -f 'uvicorn main:app')
        
        if [ -z "$PID" ]; then
          echo ">>> ๊ตฌ๋™ ์ค‘์ธ AI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—†์Šต๋‹ˆ๋‹ค."
        else
          kill -15 $PID
          sleep 3
        fi
        
      • start.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/ai"
        VENV_PATH="$PROJECT_ROOT/venv"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ]
        error_handler() {
          echo ">>> [Error] AI ์„œ๋ฒ„ ์‹คํ–‰ ์‹คํŒจ"
          bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ (AI)" "ApplicationStart(Pip/Uvicorn) ๋‹จ๊ณ„์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.\n๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”." "fail"
        }
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        # ์‹œ์ž‘ ์•Œ๋ฆผ
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹œ์ž‘ (AI)" "FastAPI ๊ฐ€์ƒํ™˜๊ฒฝ ์„ค์ • ๋ฐ ๊ตฌ๋™์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค." "info"
        
        echo ">>> [Start] ๊ฐ€์ƒํ™˜๊ฒฝ ์„ค์ • ๋ฐ ํŒจํ‚ค์ง€ ์„ค์น˜"
        if [ ! -d "$VENV_PATH" ]; then
            echo ">>> ๊ฐ€์ƒํ™˜๊ฒฝ ์ƒ์„ฑ ์ค‘..."
            python3 -m venv $VENV_PATH
        fi
        
        # ๊ฐ€์ƒํ™˜๊ฒฝ ํ™œ์„ฑํ™”
        source $VENV_PATH/bin/activate
        
        # ์˜์กด์„ฑ ์„ค์น˜
        pip install -r requirements.txt
        
        echo ">>> [Start] Uvicorn ์„œ๋ฒ„ ์‹คํ–‰"
        # ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” Python ์ฝ”๋“œ(boto3) ๋‚ด๋ถ€์—์„œ ๋กœ๋“œ๋จ
        nohup uvicorn main:app --host 0.0.0.0 --port 8000 > app.log 2>&1 &
        
        # ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ ํ™•์ธ
        sleep 2
        PID=$(pgrep -f 'uvicorn main:app')
        
        if [ -z "$PID" ]; then
          echo ">>> ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
          exit 1 # trap ๋ฐœ๋™
        fi
        
        echo ">>> PID: $PID ์‹คํ–‰ ํ™•์ธ๋จ"
        
      • validate.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/ai"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # [์—๋Ÿฌ ํ•ธ๋“ค๋Ÿฌ]
        error_handler() {
          bash $DISCORD_SCRIPT "ํ—ฌ์Šค ์ฒดํฌ ์‹คํŒจ (AI)" "AI ์„œ๋ฒ„๊ฐ€ ์‘๋‹ตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (/health ์—”๋“œํฌ์ธํŠธ ํ™•์ธ ํ•„์š”)" "fail"
        }
        trap 'error_handler' ERR
        
        # --------------------------------------------------------
        
        echo ">>> [Validate] AI Server Health Check"
        
        for i in {1..10}; do
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health)
          
          if [ "$response" == "200" ]; then
            echo ">>> AI Server ์ •์ƒ ๋™์ž‘ ํ™•์ธ"
            
            # ์ตœ์ข… ์„ฑ๊ณต ์•Œ๋ฆผ
            bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (AI)" "ํ—ฌ์Šค ์ฒดํฌ ํ†ต๊ณผ! ๋ชจ๋ธ ์„œ๋น™ ์ค€๋น„ ์™„๋ฃŒ." "success"
            exit 0
          fi
          
          bash $DISCORD_SCRIPT "AI ๊ตฌ๋™ ๋Œ€๊ธฐ์ค‘... ($i/10)" "์‘๋‹ต ์ฝ”๋“œ: $response" "info"
          sleep 5
        done
        
        echo ">>> AI Server ๊ตฌ๋™ ์‹คํŒจ"
        exit 1 # -> trap ๋ฐœ๋™
        

3.2.2. ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„

๋ฐฐํฌ๋ฐฉ์‹ ์š”์•ฝ

  • Blue/Green ๋ฐฐํฌ
    • ๋‹จ์ผ ์ธ์Šคํ„ด์Šค ํ™˜๊ฒฝ์—์„œ Blue/Green ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด Port ์Šค์œ„์นญ ์‚ฌ์šฉ
  • Spot Instance ์‚ฌ์šฉ์œผ๋กœ ๋น„์šฉ ์ ˆ๊ฐ
    • AWS ASG๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Spot Instance ์ค‘๋‹จ ์‹œ ์ž๋™ ๋ณต๊ตฌ ์„ค์ •
    • ๋ฐฐํฌ ์‹œ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊บผ์ ธ์žˆ๋Š” ๊ฒฝ์šฐ, ASG๊ฐ€ ์ƒˆ ์ธ์Šคํ„ด์Šค ์‹คํ–‰ ํ›„ ๋ฐฐํฌ ์ง„ํ–‰
  • Port Switching์œผ๋กœ ์ธํ•œ ์ผ์‹œ์  ๋ฆฌ์†Œ์Šค 2๋ฐฐ ์†Œ๋ชจ์— ๋Œ€ํ•œ ๋Œ€์‘
    • ํ›„์ˆ  ํ•œ ์—ฌ๋Ÿฌ ํ•ด๊ฒฐ์ฑ… ์ค‘, ์„œ๋น„์Šค ๊ตฌํ˜„์— ๋”ฐ๋ผ ํƒ1 ํ•˜์—ฌ ๋Œ€์‘
      1. JVM ํž™ ๋ฉ”๋ชจ๋ฆฌ ์šฉ๋Ÿ‰ ์ œํ•œ(์‹คํ–‰ ์‹œ -Xmx ์˜ต์…˜ ์ ์šฉ)
      2. Swap ๋ฉ”๋ชจ๋ฆฌ ์ ๊ทน์  ์„ค์ •(๊ธฐ๋ณธ 4GB ์„ค์ • ์˜ˆ์ •)
      3. Startup CPU ์ œ์–ด( BE ์‹œ์ž‘์‹œ CPU ์‚ฌ์šฉ๋ฅ  ์ฆ๊ฐ€. t3 Burst ๊ธฐ๋Šฅ ์‚ฌ์šฉ(๊ธฐ๋ณธ ๋™์ž‘))
      4. OOM Killer ์šฐ์„ ์ˆœ์œ„ ์กฐ์ • (์ƒˆ๋กœ ๋œฌ ํ”„๋กœ์„ธ์Šค์— ๋†’์€ ์ ์ˆ˜๋ฅผ ์ค˜ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ ์ƒˆ ํ”„๋กœ์„ธ์Šค ํฌ์ƒ)

ํ•„์ˆ˜ ์„ค์ • ์‚ฌํ•ญ

  • EC2 ๋ฐ ASG

    • ์ธ์Šคํ„ด์Šค ํƒ€์ž…: t3.medium(Spot Instance)

    • ์Šค์™‘๋ฉ”๋ชจ๋ฆฌ: 4GB

    • ์„œ๋ฒ„ ์ดˆ๊ธฐ ์„ค์ • ์Šคํฌ๋ฆฝํŠธ (ํฌํŠธ์Šค์œ„์นญ์„ ์œ„ํ•œ Nginx ์„ค์ • ํฌํ•จ)

      #!/bin/bash
      # 1. ์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ ๋ฐ ํ•„์ˆ˜ ํŒจํ‚ค์ง€ ์„ค์น˜
      sudo apt-get update -y
      sudo apt-get install -y ruby wget curl git jq awscli
      
      # 2. Swap ๋ฉ”๋ชจ๋ฆฌ 4GB ์„ค์ • (ํ•„์ˆ˜)
      sudo fallocate -l 4G /swapfile
      sudo chmod 600 /swapfile
      sudo mkswap /swapfile
      sudo swapon /swapfile
      echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
      
      # 3. CodeDeploy Agent ์„ค์น˜
      cd /home/ubuntu
      wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
      chmod +x ./install
      sudo ./install auto
      
      # 4. Runtimes ์„ค์น˜
      # 4-1. Java 21 (Temurin)
      wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo apt-key add -
      echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
      sudo apt-get update
      sudo apt-get install -y temurin-21-jdk
      
      # 5. ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ์„ค์ •
      # ๊ฐ ์„œ๋น„์Šค(Backend, Frontend, AI)๊ฐ€ ์œ„์น˜ํ•  ํด๋”๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
      mkdir -p /home/ubuntu/apps/{backend,frontend,ai}
      
      # ์ƒ์„ฑ๋œ ํด๋”์˜ ์†Œ์œ ๊ถŒ์„ ubuntu ์œ ์ €์—๊ฒŒ ๋ถ€์—ฌํ•˜์—ฌ 
      # CodeDeploy Agent(ubuntu ์œ ์ €๋กœ ์‹คํ–‰๋จ)๊ฐ€ ์“ฐ๊ธฐ ๊ถŒํ•œ์„ ๊ฐ–๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
      sudo chown -R ubuntu:ubuntu /home/ubuntu/apps
      
      # 6. [Staging ์ „์šฉ] Nginx Blue/Green ์ดˆ๊ธฐ ์„ค์ • (BE + AI)
      sudo apt-get install -y nginx
      
      # 6-1. Backend์šฉ ๋™์  ํฌํŠธ ํŒŒ์ผ (๊ธฐ๋ณธ 8080)
      echo 'set $service_url http://127.0.0.1:8080;' | sudo tee /etc/nginx/conf.d/service-url.inc
      
      # 6-2. AI์šฉ ๋™์  ํฌํŠธ ํŒŒ์ผ (๊ธฐ๋ณธ 8000)
      echo 'set $ai_service_url http://127.0.0.1:8000;' | sudo tee /etc/nginx/conf.d/ai-service-url.inc
      
      # 6-3. Nginx ๋ฉ”์ธ ์„ค์ • (Reverse Proxy)
      cat <<EOF | sudo tee /etc/nginx/sites-available/default
      server {
          listen 80;
          server_name _;
      
          # Backend (Root)
          include /etc/nginx/conf.d/service-url.inc;
          location / {
              proxy_pass \$service_url;
              proxy_set_header Host \$host;
              proxy_set_header X-Real-IP \$remote_addr;
          }
      
          # AI Server (Prefix: /api/ai ๋˜๋Š” ๋‚ด๋ถ€ ํ˜ธ์ถœ์šฉ ํฌํŠธ ๋ถ„๋ฆฌ ๊ฐ€๋Šฅ)
          # ์—ฌ๊ธฐ์„œ๋Š” /ai ๊ฒฝ๋กœ๋กœ ๋“ค์–ด์˜ค๋ฉด AI ์„œ๋ฒ„๋กœ ๋ณด๋‚ธ๋‹ค๊ณ  ๊ฐ€์ •
          include /etc/nginx/conf.d/ai-service-url.inc;
          location /ai/ {
              proxy_pass \$ai_service_url;
              proxy_set_header Host \$host;
              proxy_set_header X-Real-IP \$remote_addr;
              # FastAPI ๋ฌธ์„œ(Swagger) ๋“ฑ์„ ์œ„ํ•ด rewrite ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ
              rewrite ^/ai/(.*)\$ /\$1 break;
          }
      }
      EOF
      
      sudo nginx -t
      sudo systemctl restart nginx
      
  • AWS CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ์„ค์ •

    • ๋ฐฐํฌ ์œ ํ˜•: In-Place ๋ฐฐํฌ(๋‹จ์ผ ์„œ๋ฒ„์ด๋ฏ€๋กœ AWS ์ž…์žฅ์—์„œ๋Š” In-Place / ๋‚ด๋ถ€ ์Šคํฌ๋ฆฝํŠธ๋กœ Blue/Green ๊ตฌํ˜„)
    • ํ™˜๊ฒฝ ๊ตฌ์„ฑ: Auto Scaling Group์„ ํƒ
    • ๋ฐฐํฌ ์„ค์ •: CodeDeployDefault.OneAtATime
    • IAM Role: AmazonSSMReadOnlyAccessย ํ•„์ˆ˜
    • ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ:ย ์„ค์ • ํ•ด์ œ(Nginx๊ฐ€ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ์—ญํ• )
  • ์„œ๋น„์Šค ๋ณ„ ์„ค์ • ๋ฐ ์Šคํฌ๋ฆฝํŠธ

    • BE(Spring Boot)

      • ์ „๋žต: Blue/Green ๋ฐฐํฌ 8080โ†” 8081 ํฌํŠธ์Šค์œ„์นญ

      • appspec.yml

        version: 0.0
        os: linux
        files:
          - source: /
            destination: /home/ubuntu/apps/backend
            overwrite: true
        
        permissions:
          - object: /home/ubuntu/apps/backend/scripts
            pattern: "**"
            owner: ubuntu
            group: ubuntu
            mode: 755
        
        hooks:
          # Stop ๋‹จ๊ณ„๋Š” ์ƒ๋žต (์ƒˆ ๋ฒ„์ „ ๋„์šฐ๊ณ  ์Šค์œ„์นญ ํ›„ ์ข…๋ฃŒํ•จ)
          ApplicationStart:
            - location: scripts/start.sh
              timeout: 120
              runas: ubuntu
          ValidateService:
            - location: scripts/validate_and_switch.sh
              timeout: 180
              runas: ubuntu
        
      • start.shย (์œ ํœด ํฌํŠธ ์ฐพ๊ธฐ ๋ฐ ์‹คํ–‰)

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/backend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # ํ˜„์žฌ ๊ตฌ๋™ ์ค‘์ธ ํฌํŠธ ํ™•์ธ (service-url.inc ํŒŒ์‹ฑ)
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/service-url.inc)
        
        if [ "$CURRENT_PORT" == "8080" ]; then
          TARGET_PORT=8081
          PROFILE="green"
        else
          TARGET_PORT=8080
          PROFILE="blue"
        fi
        
        # ์‹œ์ž‘ ์•Œ๋ฆผ
        bash $DISCORD_SCRIPT "์Šคํ…Œ์ด์ง• ๋ฐฐํฌ ์‹œ์ž‘ (Backend)" "Target Port: $TARGET_PORT ($PROFILE)๋กœ ๋ฐฐํฌ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค." "info"
        
        # [3] ์ƒˆ ํฌํŠธ์—์„œ ์•ฑ ์‹คํ–‰ (AWS SDK๊ฐ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ)
        # -Dserver.port ์˜ต์…˜์œผ๋กœ ํฌํŠธ ๋™์  ํ• ๋‹น
        nohup java -jar -Dserver.port=$TARGET_PORT backend.jar > app.log 2>&1 &
        
        echo ">>> $TARGET_PORT ํฌํŠธ์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์ค‘..."
        
      • validate_and_switch.shย (๊ฒ€์ฆ ๋ฐ ์Šค์œ„์นญ ํ›„ ๊ธฐ์กด ์„œ๋น„์Šค ์ข…๋ฃŒ)

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/backend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # ํ˜„์žฌ ์‹คํ–‰ํ•˜๋ ค๋Š” ํƒ€๊ฒŸ ํฌํŠธ ์žฌํ™•์ธ
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/service-url.inc)
        if [ "$CURRENT_PORT" == "8080" ]; then TARGET_PORT=8081; else TARGET_PORT=8080; fi
        
        # Health Check
        echo ">>> Health Check ์‹œ์ž‘ (Port: $TARGET_PORT)"
        for i in {1..10}; do
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/health)
          if [ "$response" == "200" ]; then
            break
          fi
          
          # ... (๋Œ€๊ธฐ ๋กœ๊ทธ) ...
          
          # ์ž๋™ ๋กค๋ฐฑ ๋กœ์ง
          if [ $i -eq 10 ]; then
             echo ">>> [Auto Rollback] Health Check ์‹คํŒจ."
             bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ & ์ž๋™ ๋กค๋ฐฑ (Backend)" "Nginx ์ „ํ™˜ ์—†์ด ์‹ ๊ทœ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค." "fail"
             
             # ์ƒˆ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ
             PID=$(lsof -t -i:$TARGET_PORT)
             if [ -n "$PID" ]; then kill -15 $PID; fi
             
             exit 1 # CodeDeploy Fail ์ฒ˜๋ฆฌ
          fi
        done
        
        # Nginx ์Šค์œ„์นญ (ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜)
        echo ">>> Nginx Port Switching ($CURRENT_PORT -> $TARGET_PORT)"
        echo "set \$service_url http://127.0.0.1:$TARGET_PORT;" | sudo tee /etc/nginx/conf.d/service-url.inc
        sudo nginx -s reload
        
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (Staging)" "Nginx ํฌํŠธ ์ „ํ™˜ ์™„๋ฃŒ. ($TARGET_PORT)" "success"
        
        # ๊ตฌ ๋ฒ„์ „ ์ข…๋ฃŒ (Graceful Shutdown)
        echo ">>> ๊ตฌ ๋ฒ„์ „ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ (Port: $CURRENT_PORT)"
        OLD_PID=$(lsof -t -i:$CURRENT_PORT)
        if [ -n "$OLD_PID" ]; then kill -15 $OLD_PID; fi
        
    • FE(Next.js) + PM2

      • ๋ฐฐํฌ ์ „๋žต: PM2์˜ reload ๊ธฐ๋Šฅ ํ™œ์šฉํ•˜์—ฌ ๋ฌด์ค‘๋‹จ ์žฌ์‹œ์ž‘ ๊ตฌํ˜„

      • appspec.yml , install.sh, validate.sh ๋Š” ๊ฐœ๋ฐœ์„œ๋ฒ„์™€ ๋™์ผ

      • start.sh

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/frontend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # ์‹œ์ž‘ ์•Œ๋ฆผ
        bash $DISCORD_SCRIPT "์Šคํ…Œ์ด์ง• ๋ฐฐํฌ ์‹œ์ž‘ (Frontend)" "PM2 Zero-downtime Reload๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค." "info"
        
        cd $PROJECT_ROOT
        
        # PM2 Reload (ํ”„๋กœ์„ธ์Šค๊ฐ€ ์—†์œผ๋ฉด Start, ์žˆ์œผ๋ฉด Reload)
        if pm2 list | grep -q "frontend"; then
            pm2 reload frontend --update-env
            echo ">>> PM2 Reload Complete"
        else
            pm2 start npm --name "frontend" -- start
            echo ">>> PM2 Start Complete"
        fi
        
    • AI(fastAPI)

      • ๋ฐฐํฌ ์ „๋žต

      • appspec.yml

        version: 0.0
        os: linux
        files:
          - source: /
            destination: /home/ubuntu/apps/ai
            overwrite: true
        permissions:
          - object: /home/ubuntu/apps/ai/scripts
            pattern: "**"
            owner: ubuntu
            group: ubuntu
            mode: 755
        hooks:
          ApplicationStart:
            - location: scripts/start.sh
              timeout: 180
              runas: ubuntu
          ValidateService:
            - location: scripts/validate_and_switch.sh
              timeout: 120
              runas: ubuntu
        
      • start.shย (ํฌํŠธ ๊ฒฐ์ • ๋ฐ ์‹คํ–‰)

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/ai"
        VENV_PATH="$PROJECT_ROOT/venv"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # ํ˜„์žฌ AI ํฌํŠธ ํ™•์ธ (ai-service-url.inc ํŒŒ์‹ฑ)
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/ai-service-url.inc)
        
        if [ "$CURRENT_PORT" == "8000" ]; then
          TARGET_PORT=8001
          PROFILE="green"
        else
          TARGET_PORT=8000
          PROFILE="blue"
        fi
        
        bash $DISCORD_SCRIPT "์Šคํ…Œ์ด์ง• ๋ฐฐํฌ ์‹œ์ž‘ (AI)" "Target Port: $TARGET_PORT ($PROFILE)" "info"
        
        # ๊ฐ€์ƒํ™˜๊ฒฝ ๋ฐ ํŒจํ‚ค์ง€ ์„ค์น˜
        if [ ! -d "$VENV_PATH" ]; then python3 -m venv $VENV_PATH; fi
        source $VENV_PATH/bin/activate
        pip install -r requirements.txt
        
        # ์ƒˆ ํฌํŠธ์—์„œ ์•ฑ ์‹คํ–‰
        # AWS SDK(boto3)๊ฐ€ ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œํ•จ
        echo ">>> Uvicorn ์‹คํ–‰ (Port: $TARGET_PORT)"
        nohup uvicorn main:app --host 0.0.0.0 --port $TARGET_PORT > app.log 2>&1 &
        
      • validate_and_switch.shย (๊ฒ€์ฆ ๋ฐ ์ž๋™ ๋กค๋ฐฑ ๋กœ์ง)

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/ai"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # ํ˜„์žฌ ํฌํŠธ์™€ ํƒ€๊ฒŸ ํฌํŠธ ๋‹ค์‹œ ํ™•์ธ
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/ai-service-url.inc)
        if [ "$CURRENT_PORT" == "8000" ]; then TARGET_PORT=8001; else TARGET_PORT=8000; fi
        
        echo ">>> [Validate] AI Health Check (Port: $TARGET_PORT)"
        
        # Health Check (10ํšŒ ์‹œ๋„)
        for i in {1..10}; do
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/health)
          
          if [ "$response" == "200" ]; then
            echo ">>> Health Check ์„ฑ๊ณต!"
            break
          fi
          
          bash $DISCORD_SCRIPT "AI ๊ฒ€์ฆ ๋Œ€๊ธฐ์ค‘... ($i/10)" "Port: $TARGET_PORT ์‘๋‹ต: $response" "info"
          sleep 5
          
          # ์ž๋™ ๋กค๋ฐฑ ๋กœ์ง
          if [ $i -eq 10 ]; then
             echo ">>> [Auto Rollback] ๋ฐฐํฌ ์‹คํŒจ ๊ฐ์ง€."
             bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ & ์ž๋™ ๋กค๋ฐฑ (AI)" "Health Check ์‹คํŒจ. Nginx๋ฅผ ์ „ํ™˜ํ•˜์ง€ ์•Š๊ณ  ์ƒˆ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค." "fail"
             
             # ์ƒˆ ํ”„๋กœ์„ธ์Šค ๊ฐ•์ œ ์ข…๋ฃŒ (๋กค๋ฐฑ ์ˆ˜ํ–‰)
             # fuser -k -n tcp $TARGET_PORT (๋˜๋Š” kill -9 PID)
             # Ubuntu์—์„œ๋Š” fuser๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ pgrep/kill ์‚ฌ์šฉ ๊ถŒ์žฅ
             PID=$(lsof -t -i:$TARGET_PORT)
             if [ -n "$PID" ]; then kill -9 $PID; fi
        
             # ์Šคํฌ๋ฆฝํŠธ ์‹คํŒจ ์ฒ˜๋ฆฌ -> CodeDeploy ์ƒํƒœ 'Fail'๋กœ ๊ธฐ๋ก๋จ
             exit 1 
          fi
        done
        
        # Nginx ์Šค์œ„์นญ (๋ฐฐํฌ ์„ฑ๊ณต ์‹œ)
        echo ">>> [Switch] Nginx Port Switching ($CURRENT_PORT -> $TARGET_PORT)"
        echo "set \$ai_service_url http://127.0.0.1:$TARGET_PORT;" | sudo tee /etc/nginx/conf.d/ai-service-url.inc
        sudo nginx -s reload
        
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (AI)" "Nginx ํฌํŠธ ์ „ํ™˜ ์™„๋ฃŒ ($TARGET_PORT). ์ •์ƒ ์„œ๋น„์Šค ์ค‘์ž…๋‹ˆ๋‹ค." "success"
        
        # ๊ตฌ ๋ฒ„์ „ ์ข…๋ฃŒ (Graceful Shutdown)
        # ์‚ฌ์šฉ์ž์˜ AI API ์‘๋‹ต ๋Œ€๊ธฐ๋ฅผ ์œ„ํ•ด 30์ดˆ ๋ถ€์—ฌ
        echo ">>> [Cleanup] ๊ตฌ ๋ฒ„์ „ ์ข…๋ฃŒ (Port: $CURRENT_PORT)"
        OLD_PID=$(lsof -t -i:$CURRENT_PORT)
        if [ -n "$OLD_PID" ]; then kill -15 $OLD_PID; fi
        

3.2.3. ์šด์˜ ์„œ๋ฒ„

์šด์˜์„œ๋ฒ„ ํŠน์„ฑ

  • ์•ˆ์ •์„ฑ ๋ฐ ๊ฐ€์šฉ์„ฑ์ด ์ตœ์šฐ์„ 
  • ์ˆ˜๋™ ์Šน์ธ์„ ํŠธ๋ฆฌ๊ฑฐ๋กœ ๋ฐฐํฌ, ๋ณด์ˆ˜์ ์ธ ๋กค๋ฐฑ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ์ „๋žต ํ•„์š”

๋ฐฐํฌ ๋ฐฉ์‹ ์š”์•ฝ

  • Blue/Green ๋ฐฐํฌ(ํฌํŠธ ์Šค์œ„์นญ)
  • ์ˆ˜๋™ ์Šน์ธ
  • ๋ณด์ˆ˜์  ๊ฒ€์ฆ๊ณผ ๋กค๋ฐฑ
    • ์Šคํ…Œ์ด์ง•๋ณด๋‹ค ๋” ๊ธด Health Check ๋ฐ ์ข…๋ฃŒ ๋Œ€๊ธฐ ์‹œ๊ฐ„
    • ๋ฐฐํฌ ์‹คํŒจ ์‹œ ๋ฌด์กฐ๊ฑด ์ž๋™ ๋กค๋ฐฑ ์‹คํ–‰(ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜ ์ฐจ๋‹จ)
    • ๋ฐฐํฌ ์„ฑ๊ณต ํ›„ ๋กœ์ง ์ƒ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ CodeDeploy ์žฌ๋ฐฐํฌ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•œ ์ˆ˜๋™ ๋กค๋ฐฑ ๊ฐ€๋Šฅ

ํ•„์ˆ˜ ์„ค์ •์‚ฌํ•ญ

  • ์ธ์Šคํ„ด์Šค ํƒ€์ž…: t3.large

  • ์Šค์™‘๋ฉ”๋ชจ๋ฆฌ: 4~8GB

  • ์„œ๋ฒ„ ์ดˆ๊ธฐ ์„ค์ • ์Šคํฌ๋ฆฝํŠธ

    • ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„์™€ ์Šค์™‘๋ฉ”๋ชจ๋ฆฌ ๋ฐ JVM ํž™ ๋ฉ”๋ชจ๋ฆฌ ์„ค์ • ์ œ์™ธ ๋™์ผ.
    • Nginx ์„ค์ • ๋™์ผ
  • AWS CodeDeploy ๋ฐฐํฌ ๊ทธ๋ฃน ์„ค์ •

    • ๋ฐฐํฌ ์œ ํ˜•: In-Place ๋ฐฐํฌ (๋‹จ์ผ ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€ ์Šคํฌ๋ฆฝํŠธ๋กœ B/G ๊ตฌํ˜„)
    • ํ™˜๊ฒฝ ๊ตฌ์„ฑ:ย CodeDeployDefault.OneAtATime
    • IAM Role:ย AmazonSSMReadOnlyAccess
    • ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ: ์„ค์ • ํ•ด์ œ (Nginx๊ฐ€ ์—ญํ•  ๋Œ€์ฒด)
    • ๋กค๋ฐฑ ๊ตฌ์„ฑ: ๋ฐฐํฌ ์‹คํŒจ์‹œ ์ž๋™ ๋กค๋ฐฑ ํ™œ์„ฑํ™”
  • ์„œ๋น„์Šค ๋ณ„ ์„ค์ • ๋ฐ ์Šคํฌ๋ฆฝํŠธ

    • BE(Spring Boot)

      • appspec.yml : ์Šคํ…Œ์ด์ง•๊ณผ ๋™์ผ

      • start.sh (๋ฉ”๋ชจ๋ฆฌ ์„ค์ • ์ฐจ์ด)

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/backend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        cd $PROJECT_ROOT
        
        # ํ˜„์žฌ ๊ตฌ๋™ ์ค‘์ธ ํฌํŠธ ํ™•์ธ
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/service-url.inc)
        if [ "$CURRENT_PORT" == "8080" ]; then
          TARGET_PORT=8081
          PROFILE="green"
        else
          TARGET_PORT=8080
          PROFILE="blue"
        fi
        
        # ์‹œ์ž‘ ์•Œ๋ฆผ
        bash $DISCORD_SCRIPT "์šด์˜ ๋ฐฐํฌ ์‹œ์ž‘ (Backend)" "Target Port: $TARGET_PORT ($PROFILE)" "info"
        
        # ์ƒˆ ํฌํŠธ์—์„œ ์•ฑ ์‹คํ–‰ (Prod ํ™˜๊ฒฝ์— ๋งž์ถฐ ๋ฉ”๋ชจ๋ฆฌ ์ƒํ–ฅ)
        # t3.large(8GB) ๊ธฐ์ค€: Heap 2GB~4GB ํ• ๋‹น ์„ค์ • ํ•„์š”
        nohup java -jar -Xms2048m -Xmx4096m -Dserver.port=$TARGET_PORT backend.jar > app.log 2>&1 &
        
        echo ">>> $TARGET_PORT ํฌํŠธ์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์ค‘..."
        
      • validate_and_switch.shย (์ข…๋ฃŒ ๋Œ€๊ธฐ ์‹œ๊ฐ„ 60์ดˆ ๋ฐ ํ—ฌ์Šค์ฒดํฌ ์ˆ˜ ์ฆ๊ฐ€)

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/backend"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/service-url.inc)
        if [ "$CURRENT_PORT" == "8080" ]; then TARGET_PORT=8081; else TARGET_PORT=8080; fi
        
        # Health Check (20ํšŒ ์‹œ๋„)
        echo ">>> Health Check ์‹œ์ž‘ (Port: $TARGET_PORT)"
        for i in {1..20}; do
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/health)
          if [ "$response" == "200" ]; then break; fi
        
          bash $DISCORD_SCRIPT "๊ฒ€์ฆ ๋Œ€๊ธฐ์ค‘... ($i/20)" "Port: $TARGET_PORT" "info"
          sleep 5
        
          if [ $i -eq 20 ]; then
             bash $DISCORD_SCRIPT "โŒ ๋ฐฐํฌ ์‹คํŒจ & ์ž๋™ ๋กค๋ฐฑ (Backend)" "Prod Health Check ์‹คํŒจ." "fail"
             PID=$(lsof -t -i:$TARGET_PORT)
             if [ -n "$PID" ]; then kill -15 $PID; fi
             exit 1
          fi
        done
        
        # Nginx ์Šค์œ„์นญ
        echo "set \$service_url http://127.0.0.1:$TARGET_PORT;" | sudo tee /etc/nginx/conf.d/service-url.inc
        sudo nginx -s reload
        
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (Production)" "Nginx ํฌํŠธ ์ „ํ™˜ ์™„๋ฃŒ. ($TARGET_PORT)" "success"
        
        # ๊ตฌ ๋ฒ„์ „ ์ข…๋ฃŒ (Grace Period 60์ดˆ + graceful down 30์ดˆ)
        echo ">>> ๊ธฐ์กด ์—ฐ๊ฒฐ ์ข…๋ฃŒ ๋Œ€๊ธฐ (60์ดˆ)..."
        sleep 60
        OLD_PID=$(lsof -t -i:$CURRENT_PORT)
        if [ -n "$OLD_PID" ]; then kill -15 $OLD_PID;
        fi
        
    • FE(Next.js)

      • ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„์™€ ๋™์ผํ•œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ „๋žต
      • ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„์™€ ์Šคํฌ๋ฆฝํŠธ ์ „์ฒด ๋™์ผ
    • AI(FastAPI)

      • start.sh : ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„์™€ ๋™์ผ

      • validate_and_switch.sh: Grace Period 60์ดˆย ์ ์šฉ. ๊ธด ์ถ”๋ก  ์š”์ฒญ์ด ๋Š๊ธฐ์ง€ ์•Š๋„๋ก ๋ณดํ˜ธ.

        #!/bin/bash
        PROJECT_ROOT="/home/ubuntu/apps/ai"
        DISCORD_SCRIPT="$PROJECT_ROOT/scripts/utils/discord.sh"
        
        # ํ˜„์žฌ ํฌํŠธ์™€ ํƒ€๊ฒŸ ํฌํŠธ ๋‹ค์‹œ ํ™•์ธ
        CURRENT_PORT=$(grep -oP '127.0.0.1:\K\d+' /etc/nginx/conf.d/ai-service-url.inc)
        if [ "$CURRENT_PORT" == "8000" ]; then TARGET_PORT=8001; else TARGET_PORT=8000; fi
        
        echo ">>> [Validate] AI Health Check (Port: $TARGET_PORT)"
        
        # Health Check (10ํšŒ ์‹œ๋„)
        for i in {1..10}; do
          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$TARGET_PORT/health)
          
          if [ "$response" == "200" ]; then
            echo ">>> Health Check ์„ฑ๊ณต!"
            break
          fi
          
          bash $DISCORD_SCRIPT "AI ๊ฒ€์ฆ ๋Œ€๊ธฐ์ค‘... ($i/10)" "Port: $TARGET_PORT ์‘๋‹ต: $response" "info"
          sleep 5
          
          # ์ž๋™ ๋กค๋ฐฑ ๋กœ์ง
          if [ $i -eq 10 ]; then
             echo ">>> [Auto Rollback] ๋ฐฐํฌ ์‹คํŒจ ๊ฐ์ง€."
             bash $DISCORD_SCRIPT "๋ฐฐํฌ ์‹คํŒจ & ์ž๋™ ๋กค๋ฐฑ (AI)" "Health Check ์‹คํŒจ. Nginx๋ฅผ ์ „ํ™˜ํ•˜์ง€ ์•Š๊ณ  ์ƒˆ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค." "fail"
             
             # ์ƒˆ ํ”„๋กœ์„ธ์Šค ๊ฐ•์ œ ์ข…๋ฃŒ (๋กค๋ฐฑ ์ˆ˜ํ–‰)
             # fuser -k -n tcp $TARGET_PORT (๋˜๋Š” kill -9 PID)
             # Ubuntu์—์„œ๋Š” fuser๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ pgrep/kill ์‚ฌ์šฉ ๊ถŒ์žฅ
             PID=$(lsof -t -i:$TARGET_PORT)
             if [ -n "$PID" ]; then kill -9 $PID; fi
        
             # ์Šคํฌ๋ฆฝํŠธ ์‹คํŒจ ์ฒ˜๋ฆฌ -> CodeDeploy ์ƒํƒœ 'Fail'๋กœ ๊ธฐ๋ก๋จ
             exit 1 
          fi
        done
        
        # Nginx ์Šค์œ„์นญ (๋ฐฐํฌ ์„ฑ๊ณต ์‹œ)
        echo ">>> [Switch] Nginx Port Switching ($CURRENT_PORT -> $TARGET_PORT)"
        echo "set \$ai_service_url http://127.0.0.1:$TARGET_PORT;" | sudo tee /etc/nginx/conf.d/ai-service-url.inc
        sudo nginx -s reload
        
        bash $DISCORD_SCRIPT "๋ฐฐํฌ ์™„๋ฃŒ (AI)" "Nginx ํฌํŠธ ์ „ํ™˜ ์™„๋ฃŒ ($TARGET_PORT). ์ •์ƒ ์„œ๋น„์Šค ์ค‘์ž…๋‹ˆ๋‹ค." "success"
        
        # ๊ตฌ ๋ฒ„์ „ ์ข…๋ฃŒ (AI ์š”์ฒญ ๋ณดํ˜ธ๋ฅผ ์œ„ํ•ด 60์ดˆ ๋Œ€๊ธฐ)
        echo ">>> ๊ธฐ์กด AI ์š”์ฒญ ์ข…๋ฃŒ ๋Œ€๊ธฐ (60์ดˆ)..."
        sleep 60
        OLD_PID=$(lsof -t -i:$CURRENT_PORT)
        if [ -n "$OLD_PID" ]; then kill -15 $OLD_PID; fi
        ``
        
        
        

4. ๊ธฐ์ˆ ์  ๊ตฌํ˜„ ๋ช…์„ธ

๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ์‹ค์ œ ์ธํ”„๋ผ ํ™˜๊ฒฝ์—์„œ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ์ˆ ์  ์„ธ๋ถ€์‚ฌํ•ญ ๋ฐ ์„ค์ • ๊ฐ’ ์ •์˜

4.1. ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์•„ํ‚คํ…์ฒ˜

  • ๋™์  ํฌํŠธ ๋ผ์šฐํŒ… (Dynamic Port Routing)
    • Nginx๊ฐ€ ๊ณ ์ •๋œ ํฌํŠธ๊ฐ€ ์•„๋‹Œ, ๋ณ„๋„์˜ ์„ค์ • ํŒŒ์ผ์„ ํ†ตํ•ด ๋™์ ์œผ๋กœ ํ”„๋ก์‹œ ๋Œ€์ƒ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ
    • ๋งค์ปค๋‹ˆ์ฆ˜
      1. ๋ฐฐํฌ ๋กœ์ง ์‹œ์ž‘ ์‹œ Idle ํฌํŠธ๋ฅผ ์‹๋ณ„ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™
      2. Health Check ์„ฑ๊ณต ์‹œ, service-url.inc ํŒŒ์ผ์˜ ํฌํŠธ ์ •๋ณด ๋ฎ์–ด์“ฐ๊ธฐ
      3. nginx -s reload ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋ฌด์ค‘๋‹จ ์„ค์ • ๊ฐฑ์‹ 
  • ํฌํŠธ ๊ฒฐ์ • ๋กœ์ง
    • ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ํฌํŠธ๋ฅผ ํ™•์ธ ํ›„, ๋ฐฐํฌ ํ•  ํƒ€์ผ“ ํฌํŠธ๋ฅผ ๊ฒฐ์ •.
    • ํฌํŠธ ๊ฐ์ง€: grep ๋ช…๋ น์–ด๋กœ service-url.inc ํŒŒ์ผ ๋‚ด๋ถ€์˜ ํฌํŠธ ๋ฒˆํ˜ธ ์ถ”์ถœ
    • ์ดํ›„ ํฌํŠธ ํ† ๊ธ€ ๋กœ์ง์„ ํ†ตํ•ด ๋ณ€๊ฒฝ

4.2. ๋Ÿฐํƒ€์ž„ ๋ฐ ๋ฆฌ์†Œ์Šค ์ตœ์ ํ™”

๋‹จ์ผ ์ธ์Šคํ„ด์Šค ๋‚ด์—์„œ ๋‹ค์ˆ˜์˜ ์„œ๋น„์Šค(BE, FE, AI, Nginx)๊ฐ€ ๋™์‹œ์— ๊ตฌ๋™๋˜๋ฏ€๋กœ, ์—„๊ฒฉํ•œ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ, ์ตœ์ ํ™”๊ฐ€ ์š”๊ตฌ๋จ.

  • JVM ๋ฉ”๋ชจ๋ฆฌ ์ œ์–ด
    • ๋ฐฐํฌ ์‹œ ์ผ์‹œ์ ์œผ๋กœ ๋‘๊ฐœ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ตฌ๊ฐ„ ๋ฐœ์ƒ
    • OOM(๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ)์œผ๋กœ ์ธํ•œ ์ข…๋ฃŒ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํž™ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ œํ•œ
    • ๋ฐฐํฌ ์ˆœ๊ฐ„์˜ ๋ฉ”๋ชจ๋ฆฌ ์ŠคํŒŒ์ดํฌ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œย Xms,ย Xmxย ํž™ ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ ์„ค์ •.
  • Swap Memory ํ™œ์šฉ
    • ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ(OOM) ์ƒํ™ฉ์„ ๋ฐฉ์–ดํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„๋ณ„๋กœ 4~8GB Swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •
    • fallocate๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šค์™‘ ํŒŒ์ผ์„ ์ƒ์„ฑ,ย /etc/fstab์— ๋“ฑ๋กํ•˜์—ฌ ์žฌ๋ถ€ํŒ… ํ›„์—๋„ ์„ค์ • ์œ ์ง€
  • ์„œ๋น„์Šค ๊ฒฉ๋ฆฌ
    • ๊ฐ ์„œ๋น„์Šค ๊ฐ„ ์ƒํ˜ธ ๊ฐ„์„ญ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋…๋ฆฝ๋œ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊ตฌ์„ฑ
    • ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ
      • BE: /home/ubuntu/apps/be
      • FE: /home/ubuntu/apps/fe
      • AI: /home/ubuntu/apps/ai

4.3. AWS CodeDeploy ๊ตฌ์„ฑ

AWS CodeDeploy์˜ Lifecycle Hooks ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐํฌ ๋‹จ๊ณ„ ์„ธ๋ถ„ํ™” ๋ฐ ๋‹จ๊ณ„๋ณ„ ์Šคํฌ๋ฆฝํŠธ ๋ชจ๋“ˆํ™”

4.3.1. Lifecycle Hooks

  • ApplicationStop
    • ๊ธฐ์กด ์‹คํ–‰ ์ค‘์ธ ์„œ๋น„์Šค ํ”„๋กœ์„ธ์Šค ํ™•์ธ ๋ฐ ์ข…๋ฃŒ
    • B/G ๋ฐฐํฌ ์‹œ ์ƒ๋žต (๋ฐฐํฌ ์‹คํŒจ ์‹œ ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ์— ์‚ฌ์šฉ ๊ฐ€๋Šฅ)
  • AfterInstall
    • S3์—์„œ ๋‹ค์šด๋กœ๋“œ ํ•œ Artifact์˜ ๊ถŒํ•œ ์„ค์ •, ์˜์กด์„ฑ ์„ค์น˜ ์ง„ํ–‰
  • ApplicationStart
    • ๊ฐ ์„œ๋น„์Šค ๋ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ชจ๋“œ๋กœ ์‹คํ–‰(nohup, pm2)
    • JVM ์˜ต์…˜, ํฌํŠธ ์„ค์ • ๋“ฑ ์‹คํ–‰์‹œ์  ์„ค์ •๋‚ด์šฉ ์ •๋ฆฌ
  • ValidateService
    • ์‹คํ–‰ ๋œ ์„œ๋น„์Šค์˜ Health Check ์ˆ˜ํ–‰
    • ๊ฒ€์ฆ ์„ฑ๊ณต ์‹œ Port Switch ๋ฐ ๊ตฌ ๋ฒ„์ „ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ

4.3.2. ์Šคํฌ๋ฆฝํŠธ ๋ชจ๋“ˆํ™”

์„œ๋น„์Šค๋ณ„, ๊ธฐ๋Šฅ๋ณ„๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ถ„ํ• ํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ™•๋ณด, ์ค‘๋ณต ๊ฐ์†Œ

  • scripts/start.sh: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ๋ฐ ํฌํŠธ ํ• ๋‹น
  • scripts/stop.sh: ํ”„๋กœ์„ธ์Šค ์‹๋ณ„ ๋ฐ ์ข…๋ฃŒ (Graceful Shutdown)
  • scripts/validate_and_switch.sh: ํ—ฌ์Šค ์ฒดํฌ, Nginx Reload, ๊ตฌ ๋ฒ„์ „ ์ •๋ฆฌ

4.4. ๋ณด์•ˆ ๊ฐ•ํ™” ์„ค์ •

  • AWS ParameterStore์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…
    • .env ํŒŒ์ผ ์ž‘์„ฑ ๋ฐ ์ „์†ก์„ ํ†ตํ•œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…์€ ๋ณด์•ˆ ์œ„ํ—˜์„ ์•ผ๊ธฐ(ํ‰๋ฌธ ํ‚ค ๋ฐ PW ๋…ธ์ถœ, ํœด๋จผ์—๋Ÿฌ ๋“ฑ)
    • ๋ชจ๋“  ๋ฏผ๊ฐ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™” ํ•˜์—ฌ ์ €์žฅ ๋ฐ ๊ด€๋ฆฌ
    • ์„œ๋ฒ„ ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ๋™ ์‹œ AWS SDK๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ์— ๊ฐ’ ๋กœ๋“œ

5. ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์šด์˜

๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„ ์ถ”์ , ์šด์˜ ์žฅ์•  ๊ฐ์ง€ ๋ฐ ์ „ํŒŒ, ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ๋ฅผ ์œ„ํ•œ ์ „๋žต ์ˆ˜๋ฆฝ

5.1. ์‹ค์‹œ๊ฐ„ ๋ฐฐํฌ ์•Œ๋ฆผ ์‹œ์Šคํ…œ

CodeDeploy ์•Œ๋ฆผ์€ ๋‹จ์ˆœ ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€๋งŒ ํ™•์ธ ๊ฐ€๋Šฅ. ๊ฐ ๋‹จ๊ณ„๋ณ„(์‹œ์ž‘, ๊ฒ€์ฆ, ์ข…๋ฃŒ ๋ฐ ๊ฒฐ๊ณผ)์™€ ์ƒ์„ธํ•œ ์›์ธ์„ ์ฆ‰๊ฐ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ์•Œ๋ฆผ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•

  • discord.sh

    • ๋””์Šค์ฝ”๋“œ ์•Œ๋ฆผ์„ ์œ„ํ•œ ์Šคํฌ๋ฆฝํŠธ
    • curl์„ ์บก์Аํ™”ํ•˜์—ฌ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ธ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ๋ชจ๋“ˆ ๊ตฌํ˜„.
    • Webhook URL์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ์•Š๊ณ  AWS Parameter Store์—์„œ ์กฐํšŒํ•˜์—ฌ ๋ณด์•ˆ ๊ฐ•ํ™”
    • ํŒŒ์ผ ๊ฒฝ๋กœ:ย /home/ubuntu/apps/{service}/scripts/utils/discord.sh (์„œ๋น„์Šค๋ณ„๋กœ ๊ฐ๊ฐ ํฌํ•จ)
    • ๊ถŒํ•œ:ย 755
    #!/bin/bash
    
    # ------------------------------------------------------------------
    # Usage: ./discord.sh "Title" "Description" "Status(success/fail/info)"
    # ------------------------------------------------------------------
    
    TITLE=$1
    MESSAGE=$2
    STATUS=$3
    
    # Webhook URL
    # AWS Parameter Store์—์„œ Webhook URL ์กฐํšŒ
    # ๋ฆฌ์ „์€ ์„œ์šธ(ap-northeast-2)๋กœ ๊ณ ์ •
    WEBHOOK_URL=$(aws ssm get-parameter \
      --name "/global/discord_webhook" \
      --with-decryption \
      --query Parameter.Value \
      --output text \
      --region ap-northeast-2)
    
    # Status Color
    if [ "$STATUS" == "fail" ]; then
      COLOR=16711680 # Red (์žฅ์• /์‹คํŒจ)
    elif [ "$STATUS" == "success" ]; then
      COLOR=65280    # Green (์„ฑ๊ณต/๋ณต๊ตฌ)
    else
      COLOR=3447003  # Blue (์ง„ํ–‰ ์ค‘/์ •๋ณด)
    fi
    
    # Payload
    # JSON ํฌ๋งทํŒ…. ์ค„๋ฐ”๊ฟˆ ๋ฌธ์ž ๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด cat <<EOF
    PAYLOAD=$(cat <<EOF
    {
      "username": "Deploy Bot",
      "avatar_url": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
      "embeds": [{
        "title": "${TITLE}",
        "description": "${MESSAGE}",
        "color": ${COLOR},
        "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)",
        "footer": {
          "text": "AWS CodeDeploy โ€ข $(hostname)"
        }
      }]
    }
    EOF
    )
    
    # ์•Œ๋ฆผ ๋ฐœ์†ก
    # curl์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ „์†ก
    curl -H "Content-Type: application/json" -d "$PAYLOAD" "$WEBHOOK_URL" > /dev/null 2>&1
    
  • ๋‹จ๊ณ„๋ณ„ ์ƒํƒœ ์ค‘๊ณ„:ย ์‹œ์ž‘, ์„ฑ๊ณต, ์‹คํŒจ

  • ์—๋Ÿฌ ํ•ธ๋“ค๋ง ์ž๋™ํ™”:ย ๋ฆฌ๋ˆ…์Šคย trapย ๋ช…๋ น์–ด๋ฅผ ํ™œ์šฉ, ์Šคํฌ๋ฆฝํŠธ ๋น„์ •์ƒ ์ข…๋ฃŒ ์‹œ ์ฆ‰์‹œ ์•Œ๋ฆผ ์ „์†ก.(๊ฐœ๋ณ„ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ์— ํฌํ•จ)

5.2. ๋ฆฌ์†Œ์Šค ๋ฐ ๋กœ๊ทธ ๋ชจ๋‹ˆํ„ฐ๋ง

๋‹จ์ผ ์ธ์Šคํ„ด์Šค ๋‚ด ๋‹ค์ค‘ ์„œ๋น„์Šค ๊ตฌ๋™์— ๋งž๊ฒŒ ๊ฐœ๋ณ„ ์„œ๋น„์Šค ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•„์š”

5.2.1. ์ธ์Šคํ„ด์Šค ๋ฆฌ์†Œ์Šค ๋ชจ๋‹ˆํ„ฐ๋ง(CloudWatch)

  • ๋Œ€์ƒ: ์ธ์Šคํ„ด์Šค ๋ฆฌ์†Œ์Šค(CPU ์‚ฌ์šฉ๋ฅ , ๋ฉ”๋ชจ๋ฆฌ ์ ์œ ์œจ, ๋””์Šคํฌ I/O, SWap ์‚ฌ์šฉ๋Ÿ‰, ๋„คํŠธ์›Œํฌ ๋“ฑ)
  • ์„ค์ •: AWS CloudWatch Agent๋ฅผ ์„ค์น˜ํ•˜์—ฌ ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€ ์ง€ํ‘œ ์ˆ˜์ง‘
  • ์•Œ๋ฆผ ์„ค์ •
    • CPU ์‚ฌ์šฉ๋ฅ  80% ์ด์ƒ 5๋ถ„ ์ง€์†์‹œ ์•Œ๋ฆผ
    • ๋ฉ”๋ชจ๋ฆฌ ์—ฌ์œ ๊ณต๊ฐ„ 500MB ์ดํ•˜์‹œ ์ฆ‰์‹œ ์•Œ๋ฆผ(OOM ๊ฒฝ๊ณ )

5.2.2. ๋กœ๊ทธ ํŒŒ์ผ ๊ด€๋ฆฌ

๋””์Šคํฌ ์šฉ๋Ÿ‰ ๋ถ€์กฑ์„ ์œ„ํ•œ ๋กœ๊ทธํŒŒ์ผ ๊ด€๋ฆฌ ์ •์ฑ… ์ˆ˜๋ฆฝ

๋กœ๊ทธํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ์••์ถ•, ๋ฐฑ์—… ์‚ญ์ œ, ๋กœํ…Œ์ด์…˜์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ(logrotate) ์‚ฌ์šฉ

  • ๋กœ๊ทธ ๊ฒฝ๋กœ

    • Backend:ย /home/ubuntu/apps/backend/app.log
    • Frontend:ย ~/.pm2/logs/ย (PM2์—์„œ ๊ด€๋ฆฌ)
    • AI:ย /home/ubuntu/apps/ai/app.log
    • CodeDeploy:ย /var/log/aws/codedeploy-agent/
  • Logrotate์„ค์ •: ๋ฆฌ๋ˆ…์Šค Logrotate ์œ ํ‹ธ๋ฆฌํ‹ฐ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ณ„ ์ˆœํ™˜, ์ตœ๋Œ€ 7์ผ ๋ณด๊ด€, ์••์ถ•

    # ๋Œ€์ƒ ๋กœ๊ทธ ํŒŒ์ผ ๊ฒฝ๋กœ
    /home/ubuntu/apps/be/*.log
    /home/ubuntu/apps/ai/*.log
    /home/ubuntu/apps/fe/*.log 
    /var/log/aws/codedeploy-agent/codedeploy-agent.log
    {
        daily           # ๋งค์ผ ๋กœํ…Œ์ดํŠธ ์‹คํ–‰
        missingok       # ๋กœ๊ทธ ํŒŒ์ผ์ด ์—†์–ด๋„ ์—๋Ÿฌ ์—†์ด ๋„˜์–ด๊ฐ
        rotate30        # ์ตœ๊ทผ 30๊ฐœ(30์ผ์น˜) ํŒŒ์ผ๋งŒ ๋ณด๊ด€ํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ์‚ญ์ œ
        compress        # ์ง€๋‚œ ๋กœ๊ทธ ํŒŒ์ผ์€ gzip์œผ๋กœ ์••์ถ•ํ•˜์—ฌ ์šฉ๋Ÿ‰ ์ ˆ์•ฝ (.gz)
        delaycompress   # ์••์ถ•์„ ๋ฐ”๋กœ ํ•˜์ง€ ์•Š๊ณ  ํ•˜๋ฃจ ๋’ค์— ์ˆ˜ํ–‰ (ํ˜„์žฌ ์“ฐ๊ธฐ ์ค‘์ธ ํŒŒ์ผ ๋ณดํ˜ธ)
        notifempty      # ๋กœ๊ทธ ๋‚ด์šฉ์ด ๋น„์–ด์žˆ์œผ๋ฉด ๋กœํ…Œ์ดํŠธ ํ•˜์ง€ ์•Š์Œ
        copytruncate    # ํ•ต์‹ฌ: ํ˜„์žฌ ๋กœ๊ทธ ํŒŒ์ผ์„ ๋ณต์‚ฌ(copy) ํ›„ ์›๋ณธ ๋‚ด์šฉ์„ ๋น„์›€(truncate).
                        # ํ”„๋กœ์„ธ์Šค ์žฌ์‹œ์ž‘ ์—†์ด ๋กœ๊ทธ ํŒŒ์ผ์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•.
        create 0644 ubuntu ubuntu # ์ƒˆ ๋กœ๊ทธ ํŒŒ์ผ ์ƒ์„ฑ ์‹œ ๊ถŒํ•œ ๋ฐ ์†Œ์œ ์ž ์ง€์ •
        dateext         # ๋ฐฑ์—… ํŒŒ์ผ๋ช…์— ๋‚ ์งœ ์ถ”๊ฐ€ (app.log-20240101.gz)
    }
    

5.2.3. Artifact ๊ด€๋ฆฌ

๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์ธ Artifact๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์Œ“์—ฌ ๋น„์šฉ์ , ์„ฑ๋Šฅ์  ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Lifecycle ๊ด€๋ฆฌ

  • ์ ์šฉ ๋Œ€์ƒ: S3 ๋‚ด๋ถ€์˜ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ๊ฐ์ฒด ์ „์ฒด
  • ์ •์ฑ…
    • Transition to IA(Infrequent Access)
      • ์ž์ฃผ ์ ‘๊ทผํ•˜์ง€ ์•Š๋Š” ์˜ค๋ž˜๋œ ๋นŒ๋“œํŒŒ์ผ์€ ์ €๋ ดํ•œ ์Šคํ† ๋ฆฌ์ง€ํด๋ž˜์Šค(Standard-IA)๋กœ ์ด๋™ํ•˜์—ฌ ๋ณด๊ด€ ๋น„์šฉ ์•ฝ 40% ์ ˆ์•ฝ(์„œ์šธ ๋ฆฌ์ „ ๊ธฐ์ค€ ์ผ๋ฐ˜ S3 GB๋‹น USD 0.025 / AI GB๋‹น USD0.011๋กœ 50% ์ด์ƒ ๋น„์šฉ ์ ˆ๊ฐ
    • Expiration
      • ์ƒ์„ฑ ํ›„ 90์ผ(3๊ฐœ์›”)์ด ์ง€๋‚œ ์ฝ”๋“œ๋กœ ๋กค๋ฐฑํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์—†์Œ.
      • ์˜๊ตฌ ์‚ญ์ œํ•˜์—ฌ ๋น„์šฉ ๋ˆ„์ˆ˜ ์ฐจ๋‹จ

5.3. ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ ๋ฐ ๋ฐฑ์—…

๋ฐฐํฌ ์‹คํŒจ, ์ธ์Šคํ„ด์Šค ์žฅ์•  ๋“ฑ์œผ๋กœ ์ธํ•œ ๋ฐ์ดํ„ฐ ์œ ์‹ค์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ „๋žต ์ˆ˜๋ฆฝ

5.3.1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐฑ์—…

  • ๋กœ์ปฌ DB ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์กดํ•˜๊ธฐ ์œ„ํ•ด EBS ๋ณผ๋ฅจ ์Šค๋ƒ…์ƒท์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ƒ์„ฑ
    • ์‚ฌ์šฉ์ž๊ฐ€ ์ ์–ด ์„ฑ๋Šฅ ์˜ํ–ฅ์ด ์ ์€ ์ƒˆ๋ฒฝ 2~5์‹œ ์‚ฌ์ด์— ์Šค๋ƒ…์ƒท ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ

5.3.2. CodeDeploy ๋ฐฐํฌ ์ด๋ ฅ

  • AWS CodeDeploy์˜ ๋ฐฐํฌ ์ด๋ ฅ ๋ณด์กด์„ ํ†ตํ•ด ๊ธฐ๋ก ํ™•์ธ ๋ฐ ๋กค๋ฐฑ ๊ฐ€๋Šฅ

5.4. ์žฅ์•  ์ „ํŒŒ ๋ฐ ๋Œ€์‘

์„œ๋ฒ„ ์šด์˜ ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์•  ์‹œ๋‚˜๋ฆฌ์˜ค ๋ณ„ ๋Œ€์‘ ์ ˆ์ฐจ

5.4.1. ๋ฐฐํฌ ์งํ›„ Health Check ์‹คํŒจ

  • ์„œ๋ฒ„ ๋‚ด๋ถ€ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ ์ค‘ validate_and_switch.sh๊ฐ€ ๊ฐ์ง€ํ•˜์—ฌ exit1 ๋ฐ˜ํ™˜
  • ์ž๋™ ๋กค๋ฐฑ ์„ค์ •์ด ๋œ ๊ฒฝ์šฐ CodeDeploy๊ฐ€ ๋ฐฐํฌ ์ƒํƒœ ํ™•์ธ ํ›„ ์ž๋™ ๋กค๋ฐฑ ํŠธ๋ฆฌ๊ฑฐ
  • ํŠธ๋ž˜ํ”ฝ ๋ณ€๊ฒฝ์ด ์—†์–ด ๊ตฌ ๋ฒ„์ „ ์œ ์ง€ โ†’ ์‚ฌ์šฉ์ž ์˜ํ–ฅ ์—†์Œ

5.4.2. ๋ฐฐํฌ ์„ฑ๊ณต ํ›„ ๋กœ์ง ์˜ค๋ฅ˜ ๋ฐœ๊ฒฌ

  • ๋””๋ฒ„๊น…, QA, ๊ฐ์ข… ์•Œ๋ฆผ ๋“ฑ์„ ํ†ตํ•ด ๊ฐœ๋ฐœ ๋ฐ ์šด์˜์ง„์ด ์˜ค๋ฅ˜ ์ธ์ง€
  • GitHub Actions์—์„œ ์ด์ „ ๋ฒ„์ „์œผ๋กœ ๋กค๋ฐฑ ํŠธ๋ฆฌ๊ฑฐ ๋ฐ ์žฌ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰

5.4.3. ์„œ๋ฒ„ ๋‹ค์šด

  • ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„: ASG๊ฐ€ HealthCheck ์‹คํŒจ ๊ฐ์ง€ ํ›„ ์ƒˆ ์ธ์Šคํ„ด์Šค ์ž๋™ ์‹คํ–‰ ๋ฐ ๋ฐฐํฌ ๋กœ์ง ์‹คํ–‰
  • ์šด์˜ ์„œ๋ฒ„: ASG๋ฅผ ํ†ตํ•œ ์ž๋™ ๋ณต๊ตฌ ๋ฐ CloudWatch๋ฅผ ํ†ตํ•œ ์•Œ๋ฆผ ์ „ํŒŒ