Cloud Design 1st janna - 100-hours-a-week/KTB4-1st-wiki GitHub Wiki

๋ชฉํ‘œ

  • ์ž‘์€ ๋น„์šฉ๊ณผ ์šด์˜ ๋ณต์žก๋„๋กœ ์„œ๋น„์Šค๋ฅผ ๋น ๋ฅด๊ฒŒ ์ถœ์‹œํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ ๊ตฌ์ถ•
  • EC2๋ฅผ ๋ช‡ ๋Œ€ ์‚ฌ์šฉํ•  ๊ฑฐ์•ผ?
  • ์–ด๋–ค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ปจํ…Œ์ด๋„ˆํ™”ํ•  ๊ฑฐ์•ผ?
  • ๋‹จ์ˆœ ๊ตฌ์„ฑ์„ ์„ ํƒํ•จ์œผ๋กœ์จ ์–ด๋–ค ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ๊ฐ์ˆ˜ํ–ˆ์–ด?
  • ๊ณผ์—ฐ ์„œ๋น„์Šค ๊ทœ๋ชจ์— ๋งž๋Š” ์ ์ ˆํ•œ ์•„ํ‚คํ…์ฒ˜์ผ๊นŒ?

์„œ๋น„์Šค ๊ทœ๋ชจ

ํ•ญ๋ชฉ v1 v2 v3
ํƒ€๊ฒŸ ์œ ์ € KTB ๊ต์œก์ƒ (20๋Œ€ ์ค‘๋ฐ˜-30๋Œ€ ์ดˆ๋ฐ˜) ๋Œ€ํ•™๊ต ๊ธฐ์ˆ™์‚ฌ์ƒ, 1์ธ ๊ฐ€๊ตฌ ์ƒํƒœ๊ฐ€ ์ข‹์ง€๋งŒ ๋ถˆํ•„์š”ํ•œ ๋ฌผํ’ˆ์ด ์žˆ๋Š” ํ•œ๊ตญ ๊ฑฐ์ฃผ์ž
SOM(๋ชฉํ‘œ ์‹œ์žฅ) 30๋ช… 100๋งŒ ๋ช… 500๋งŒ ๋ช…
MAU 30๋ช… 100๋งŒ ๋ช… 500๋งŒ ๋ช…
DAU 30๋ช… 25๋งŒ ๋ช… 125๋งŒ ๋ช…
Peak ๋™์‹œ ์‚ฌ์šฉ์ž 20๋ช… 12,500 62,500
์ฒด๋ฅ˜ ์‹œ๊ฐ„ 180์ดˆ 300์ดˆ 600์ดˆ
์„ธ์…˜ ํ‰๊ท  ์š”์ฒญ์ˆ˜ 27 11 14.23
์„ธ์…˜ ์ดˆ๋‹น ์š”์ฒญ์ˆ˜ 0.15 0.04 0.02
Peak RPS 3 500 1,250
ํŠธ๋ž˜ํ”ฝ ์ง‘์ค‘ ์‹œ๊ฐ„ ํ‰์ผ ์‰ฌ๋Š” ์‹œ๊ฐ„(9:00-18:00 ์ค‘ ๋งค์‹œ 50๋ถ„๋ถ€ํ„ฐ ์ •๊ฐ ์‚ฌ์ด)
19-21์‹œ
์ ์‹ฌ์‹œ๊ฐ„(11:50-13:00)
19-21์‹œ -
ํŠธ๋ž˜ํ”ฝ/์ž์› ์‚ฌ์šฉ๋Ÿ‰ ํฐ ๊ธฐ๋Šฅ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ v1 ๋™์ผ + ์Šค๋งˆํŠธ ๊ฒ€์ƒ‰
๋ฌผํ’ˆ ๋“ฑ๋ก ๊ฒ€์ฆ ์‹œ AI API ํ˜ธ์ถœ
v2 ๋™์ผ + ์ฑ„ํŒ… ์‹œ๋ฎฌ๋ ˆ์ด์…˜

Peak RPS ๊ณ„์‚ฐ๋ฒ•

  1. user flow์˜ ์š”์ฒญ ์ˆ˜ ๊ณ„์‚ฐ
  2. ์„ธ์…˜ ๋ณ„ user flow ํ™•๋ฅ ์„ ์„ค์ •ํ•ด ์„ธ์…˜๋ณ„ ์š”์ฒญ ์ˆ˜ ๊ณ„์‚ฐ
  3. ์„ธ์…˜ RPS๋ฅผ Peak ๋™์‹œ ์‚ฌ์šฉ์ž์™€ ๊ณฑํ•ด Peak ์‹œ๊ฐ„์˜ RPS ์‚ฐ์ •

ํ•ต์‹ฌ User Flow

์š”์ฒญ ํšŸ์ˆ˜๋Š” ์กฐํšŒํ•  ๋ฌผํ’ˆ ๊ฐœ์ˆ˜, ์ฑ„ํŒ… ํšŸ์ˆ˜ ๋“ฑ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ธฐ์— ํ๋ฆ„์„ ์ง์ ‘ ์ž‘์„ฑํ•ด๋ณด๋ฉฐ ์‚ฐ์ •ํ–ˆ๋‹ค.

  1. ๋””์Šค์ฝ”๋“œ ๋งํฌ ์ง„์ž… ๋ฐ ํšŒ์›, ๊ทธ๋ฃน ๊ฐ€์ž… => ์š”์ฒญ 5
  2. [์• ์ž1] ํŒ€์›๋“ค์ด ๋“ฑ๋กํ•œ ๊ฐ„์‹ ํ™•์ธ => ์š”์ฒญ 6
  3. ๋ณธ์ธ ๋ฌผํ’ˆ 1๊ฐœ ๋“ฑ๋ก ๊ตํ™˜ ์š”์ฒญ => ์š”์ฒญ 2
  4. ์ฑ„ํŒ…์œผ๋กœ ๊ตํ™˜ ์‹œ๊ฐ„ ๋ฐ ์œ„์น˜ ์กฐ์œจ => ์š”์ฒญ 11
  5. ๊ต์œก์žฅ ๋‚ด๋ถ€์—์„œ ์ง๊ฑฐ๋ž˜ => ์š”์ฒญ 2(์™„๋ฃŒ ์ฒ˜๋ฆฌ)

์„œ๋ฒ„ ๊ตฌ์„ฑ์š”์†Œ_ํ˜„์‹œ์  AI/ํ’€์Šคํƒ ํŒ€์›๋ถ„๋“ค์ด ํ•„์š”๋กœ ํ•˜๋Š” ์š”์†Œ

  • SpringBoot(Backend) 1
  • React(Frontend_CSR) 1
  • MySQL(RDBMS) 1
  • MongoDB(NoSQL) 1
  • FastAPI(AI server) 1
  • Redis(Cache) 1

๋ฐฐํฌ ๋นˆ๋„

  • ๋งค์ฃผ ์›”์š”์ผ ์˜ค์ „ 10์‹œ

๋ฐฐํฌ ๋ฐฉ์‹

์šฐ๋ฆฌ๋Š” ์ž์ฒด์ ์ธ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ธ๋ ฅ๋„, ๋น„์šฉ๋„ ์—†์œผ๋‹ˆ๊นŒ ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค(IaaS)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค

์„œ๋น„์Šค ์ข…๋ฅ˜
  • AWS
    • ์‹œ์žฅ์˜ ์„ ๋‘์ฃผ์ž๋กœ, ์ ์œ ์œจ(์•ฝ 40%)๋„ ๊ฐ€์žฅ ๋†’์•„ ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋งŽ๋‹ค.
    • IAM ๊ถŒํ•œ ๋“ฑ ์„ธ๋ฐ€ํ•œ ์„ค์ • ์ปค์Šคํ…€์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์ด๋กœ ์ธํ•ด ์ง„์ž… ์žฅ๋ฒฝ์ด ๋†’๊ณ  ๋น„์šฉ ์˜ˆ์ธก์ด ๋ณต์žกํ•˜๋‹ค.
    • ์ˆ˜๋ฐฑ ๊ฐœ์— ๋‹ฌํ•˜๋Š” ๋‹ค์–‘ํ•œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋น„์šฉ ๋Œ€๋น„ ๊ผญ ํ•„์š”ํ•œ ์„œ๋น„์Šค์ธ์ง€ ๋ฉด๋ฐ€ํžˆ ๊ฒ€ํ† ํ•ด์•ผ ํ•œ๋‹ค.
    • 1๊ฐœ์›”์ฏค ์ „์— ํด๋ผ์šฐ๋“œ ํ˜„์ง์ž ํŠน๊ฐ•์—์„œ "3์‚ฌ ๋ชจ๋‘ 99.9% ์ด์ƒ์˜ ๊ฐ€์šฉ์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค์ง€๋งŒ, ์‹ค์ œ ์‚ฌ์šฉ์„ ํ•˜๋‹ค๋ณด๋ฉด AWS๊ฐ€ ํ›จ์”ฌ ๊ฐ€์šฉ์„ฑ์ด ๋†’๋‹ค"๋Š” ์ด์•ผ๊ธฐ๋ฅผ ๋“ค์—ˆ๋Š”๋ฐ ๊ด€๋ จ ์ž๋ฃŒ๋ฅผ ์ฐพ์ง€๋Š” ๋ชปํ•ด์„œ AWS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋กœ ๊ผฝ๊ธฐ๋Š” ๋ถ€์กฑํ•˜๋‹ค.
  • Azure
    • ์‹œ์žฅ ์ ์œ ์œจ(์•ฝ 20%) 2์œ„๋กœ, Microsoft์˜ ์„œ๋น„์Šค๋“ค๊ณผ ํ˜ธํ™˜์„ฑ์ด ํ›Œ๋ฅญํ•˜๋‹ค.
    • ํ”Œ๋žซํผ์ด๋‚˜ ๋ฌธ์„œ๊ฐ€ ๋ณต์žกํ•ด์„œ ์ง„์ž…์žฅ๋ฒฝ์ด ์žˆ๋‹ค.
  • GCP
    • ์‹œ์žฅ ์ ์œ ์œจ(์•ฝ 10%) 3์œ„๋กœ, ๊ตฌ๊ธ€์˜ ๊ธฐ์ˆ (K8s, Google API ๋“ฑ)๋“ค๊ณผ ํ˜ธํ™˜์„ฑ์ด ํ›Œ๋ฅญํ•˜๋‹ค.
    • ๋‹ค๋ฅธ ์„œ๋น„์Šค์— ๋น„ํ•ด ์•ฝ๊ฐ„ ์ €๋ ดํ•œ ํŽธ์ด์ง€๋งŒ, ์„œ๋น„์Šค ์„ ํƒํญ์ด ์ข๋‹ค.
    • ๋ฐ์ดํ„ฐ ๋ถ„์„์ด๋‚˜ AI ๋ถ„์•ผ(TensorFlow, Vertex AI)๋ฅผ ์ฃผ๋ ฅ์œผ๋กœ ์‚ผ๊ณ  ์žˆ๋‹ค.
    • v2๋ถ€ํ„ฐ๋Š” ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ AI๋ฅผ API ํ˜ธ์ถœ์—์„œ ๋ชจ๋ธ ์„œ๋น™ ๋ฐฉ์‹์œผ๋กœ ์ „ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์–ด์„œ ๊ฐ€์„ฑ๋น„ ์ธก๋ฉด์—์„œ GCP๋„ ๊ณ ๋ คํ•ด๋ณผ ๋งŒํ•˜๋‹ค.
  1. ์ง€๊ธˆ ์šฐ๋ฆฌ๋Š” ์ž‘์€ ๋น„์šฉ๊ณผ ์šด์˜ ๋ณต์žก๋„๋กœ ๋นจ๋ฆฌ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒŒ ๋ชฉํ‘œ๋‹ค.
  2. ๋ญ˜ ์„ ํƒํ•˜๋“  DAU๊ฐ€ 30๋ช…์ด๊ณ  ์ตœ๋Œ€ 100๋ช…์˜ ์œ ์ €๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋„ ์ธ์Šคํ„ด์Šค 1~2๋Œ€๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ค.
  3. ์šฐ๋ฆฌํŒ€ ํด๋ผ์šฐ๋“œ ์ธ์›์ด ๋‘ ๋ช…์ธ๋ฐ, ๋‘ ๋ช… ๋‹ค AWS๋งŒ ์‚ฌ์šฉ ๊ฒฝํ—˜์ด ์žˆ๋‹ค.
  4. ํ˜„์žฌ๋กœ์„œ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ๋Š” AWS์™€ Azure, GCP์—์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ์„œ๋น„์Šค์˜ ์ฐจ์ด๊ฐ€ ํฌ์ง€ ์•Š๊ณ , ๋น„์šฉ๋„ ํ˜„์žฌ ํ•„์š”ํ•œ ๊ตฌ์„ฑ์ด๋ฉด ๊ฑฐ์˜ ์ฐจ์ด๊ฐ€ ์—†๋‹ค.
  5. ๋น ๋ฅด๊ฒŒ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ์ต์ˆ™ํ•˜๊ณ  ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋งŽ์€ AWS๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

FE ๋ฐฐํฌ๋„ AWS์—์„œ ํ•ด์•ผ ํ• ๊นŒ?

FE ๋ฐฐํฌ ๋ฐฉ์‹
  • EC2 Nginx
    • ํŠน์ง•
      • EC2 ๋””์Šคํฌ์— ์ €์žฅ๋œ ๋นŒ๋“œ ํŒŒ์ผ์„ Nginx ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฝ์–ด์„œ HTTP ์‘๋‹ต
      • ๋น„์šฉ: EC2 ์ธ์Šคํ„ด์Šค ๋น„์šฉ(์œ ๋ฃŒ ์ž์› ์†Œ๋ชจ)
      • ์บ์‹ฑ ๋ฐ ์†๋„: EC2 ๋ฆฌ์ „์— ์˜์กด
      • SSL/TLS ์ธ์ฆ์„œ: ์ธ์ฆ์„œ๋ฅผ Nginx์— ์ง์ ‘ ์„ธํŒ…ํ•˜๊ณ  ์ฃผ๊ธฐ์  ๊ฐฑ์‹  ํ•„์š”
      • CI/CD: EC2๋กœ ๋นŒ๋“œ ํŒŒ์ผ ์ „์†ก
      • SSR: Node.js ์ปจํ…Œ์ด๋„ˆ ์ถ”๊ฐ€
    • ๋‹จ์ 
      • ๋ฐฐํฌ ์‹œ EC2์— ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ์ž‘์—… ํ•„์š”
      • ์‚ฌ์šฉ์ž๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด FE ์ •์  ํŒŒ์ผ ์ „์†ก์ด EC2 ์ธ์Šคํ„ด์Šค์˜ ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์„ ์‚ฌ์šฉํ•ด์„œ BE API ์‘๋‹ต ์†๋„์— ์•…์˜ํ–ฅ
      • ์ธ์Šคํ„ด์Šค์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด FE ํ™”๋ฉด๋„ ๋…ธ์ถœ๋˜์ง€ ์•Š์•„ ์ ๊ฒ€ ์ค‘ ์•ˆ๋‚ด ๋“ฑ ๋ถˆ๊ฐ€
  • AWS S3 + CloudFront
    • ํŠน์ง•
      • S3 ๋ฒ„ํ‚ท์— ๋นŒ๋“œ ํŒŒ์ผ ์—…๋กœ๋“œ ํ›„ CloudFront ์—ฃ์ง€ ์บ์‹ฑ
      • ๋น„์šฉ: ๊ฑฐ์˜ ๋ฌด๋ฃŒ(ํ”„๋ฆฌํ‹ฐ์–ด 1TB/์›”)
      • ์บ์‹ฑ ๋ฐ ์†๋„: ๊ธ€๋กœ๋ฒŒ ๋กœ์ผ€์ด์…˜ ์บ์‹ฑ, ๋น ๋ฆ„
      • SSL/TLS ์ธ์ฆ์„œ: AWS ACM์œผ๋กœ ๊ด€๋ฆฌ(์ž๋™ ๊ฐฑ์‹ )
      • CI/CD: S3๋กœ sync ํ›„ ์บ์‹œ ๋ฌดํšจํ™”
      • SSR: Lambda ์ถ”๊ฐ€
    • ๋‹จ์ 
      • ๋ฐฐํฌ ์‹œ S3์— ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ์ž‘์—… ํ•„์š”
      • S3 ๋ฒ„ํ‚ท ์ •์ฑ… ๊ด€๋ฆฌ
  • Vercel
    • ํŠน์ง•
      • Git Push ์‹œ ์ž๋™ ๋นŒ๋“œ ๋ฐ ๊ธ€๋กœ๋ฒŒ Edge Network ๋ฐฐํฌ
      • ๋น„์šฉ: Hobby(๊ฐœ์ธ/ํ•™์Šต์šฉ) ํ‹ฐ์–ด 100GB/์›” ๋ฌด๋ฃŒ, ์ƒ์—…์  ์ด์šฉ ์‹œ ์œ ๋ฃŒ
      • ์บ์‹ฑ ๋ฐ ์†๋„: ๊ธ€๋กœ๋ฒŒ ์—ฃ์ง€ ๋„คํŠธ์›Œํฌ ๊ธฐ๋ณธ ํƒ‘์žฌ, ๋น ๋ฆ„
      • SSL/TLS ์ธ์ฆ์„œ: ๋„๋ฉ”์ธ ์—ฐ๊ฒฐ ์‹œ ์ž๋™ ๋ฐœ๊ธ‰ ๋ฐ ๊ฐฑ์‹ 
      • CI/CD: GitHub ๋ ˆํฌ ์—ฐ๋™ํ•˜๋ฉด ๋
      • SSR: ์ง€์›
    • ๋‹จ์ 
      • ์ƒ์—…์  ํ”„๋กœ์ ํŠธ๋‚˜ ํŒ€ ํ”„๋กœ์ ํŠธ(GitHub Organization)๋Š” ๋น„์šฉ ๋ฐœ์ƒ
      • ์›”๊ฐ„ ๋Œ€์—ญํญ 100GB ์ดˆ๊ณผ ์‹œ ์„œ๋น„์Šค ์ผ์‹œ ์ •์ง€
  • Github Pages
    • ํŠน์ง•
      • GitHub ๋ ˆํฌ์˜ ํŠน์ • ๋ธŒ๋žœ์น˜/์ •์  ํŒŒ์ผ ํ˜ธ์ŠคํŒ…
      • ๋น„์šฉ: 100GB/์›” ๋ฌด๋ฃŒ
      • ์บ์‹ฑ ๋ฐ ์†๋„: ๊ธ€๋กœ๋ฒŒ CDN์ด ํƒ‘์žฌ๋˜์–ด ์žˆ์œผ๋‚˜ ์„ค์ • ์ œ์•ฝ์ด ์žˆ์–ด ๋ณดํ†ต ์ˆ˜์ค€
      • SSL/TLS ์ธ์ฆ์„œ: ์ž๋™ ๋ฐœ๊ธ‰ ๋ฐ ๊ฐฑ์‹ 
      • CI/CD: GitHub Actions ํ…œํ”Œ๋ฆฟ ์ œ๊ณต
      • SSR: ๋ฏธ์ง€์›
    • ๋‹จ์ 
      • SPA ๋ผ์šฐํŒ… ์ฃผ์†Œ๋กœ ์ง์ ‘ ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ 404 ์—๋Ÿฌ ๋ฐœ์ƒ ์œ„ํ—˜์œผ๋กœ ์šฐํšŒ ์„ค์ • ํ•„์š”
      • ์›”๊ฐ„ ๋Œ€์—ญํญ 100GB ์ดˆ๊ณผ ์‹œ ์„œ๋น„์Šค ์ผ์‹œ ์ •์ง€
      • ์ €์žฅ์†Œ๊ฐ€ public์ด์–ด์•ผ ํ•˜๋ฉฐ private ์ €์žฅ์†Œ ๋ฐฐํฌ๋Š” ์œ ๋ฃŒ ํ”Œ๋žœ ํ•„์š”
  1. ์ง€๊ธˆ 1์ฐจ ๋ฐฐํฌ์—์„œ๋Š” ์œ ์ €๊ฐ€ 30~100๋ช…์ด๋ผ ์›”๊ฐ„ ๋Œ€์—ญํญ 100GB์— ๋ฏธ์น˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ์— ํ† ํฐ ํ•œ๋„๋Š” ๊ณ ๋ คํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
  2. ์ €์žฅ์†Œ๋Š” public์ด์ง€๋งŒ, ํŒ€ ํ”„๋กœ์ ํŠธ(organization)์ด๊ธฐ ๋•Œ๋ฌธ์— vercel์€ ์ถ”๊ฐ€ ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜๊ธฐ์— ํƒˆ๋ฝ์ด๋‹ค.
  3. GitHub Pages๋Š” 404 ์šฐํšŒ ์„ค์ •์ด ํ•„์š”ํ•œ ์ ์—์„œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š”๋ฐ, ํŽธ์˜์„ฑ๋„ ์žˆ์ง€๋งŒ ๋ณต์žก๋„๊ฐ€ ๋” ํด ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ•ด์„œ ํƒˆ๋ฝ์ด๋‹ค.
  4. CloudFront๋Š” ์ตœ๋Œ€ ์žฅ์ ์ด ๊ธ€๋กœ๋ฒŒ ์บ์‹ฑ์ธ๋ฐ, [๋ฐ”๊พธ๊นŒ]๋Š” ๊ตญ๋‚ด ๊ฑฐ์ฃผ์ž๋งŒ ์‚ฌ์šฉํ•  ์„œ๋น„์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š” ์—†๋‹ค.
  5. ๋”ฐ๋ผ์„œ CloudFront์™€ Nginx๋Š” ํ˜„์žฌ ๊ทœ๋ชจ์—์„œ ํฌ๊ฒŒ ์ฐจ์ด๊ฐ€ ์—†๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
  6. ๋‹ค๋งŒ, S3 ๋ฒ„ํ‚ท ์ •์ฑ…์„ FE ๋ฐฐํฌ๋งŒ์„ ์œ„ํ•ด ๋”ฐ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” Docker Compose๋กœ ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ๋“ค๊ณผ ๋ฌถ์–ด์„œ ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๋Š” ๊ฒŒ ๋” ํŽธ๋ฆฌํ•˜๊ณ  ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šธ ๊ฒƒ์ด๋‹ค.
  7. ๊ฒฐ๋ก ์ ์œผ๋กœ Nginx ๋‹น์ฒจ

EC2 ์ธ์Šคํ„ด์Šค ๊ตฌ์„ฑ

1์ฐจ ๋ฐฐํฌ์—์„œ๋Š” FastAPI๊ฐ€ AI ๋ชจ๋ธ์„ ์ง์ ‘ ๋‹ค๋ฃจ์ง€ ์•Š๊ณ  ์™ธ๋ถ€ API ์ค‘๊ณ„ ์—ญํ• ๋งŒ ์ˆ˜ํ–‰ํ•œ๋‹ค.

์‚ฌ์šฉ์ž๋„ ๊ต์œก์žฅ ๋‚ด๋ถ€ ์ธ์›์œผ๋กœ ํ•œ์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋ž˜ํ”ฝ ๊ธ‰์ฆ ๋“ฑ ๋Œ๋ฐœ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ํ™•๋ฅ ์ด 0์— ์ˆ˜๋ ดํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์ตœ์†Œํ•œ์˜ ๋น„์šฉ๊ณผ ๋น ๋ฅธ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด 1๋Œ€์˜ ์ธ์Šคํ„ด์Šค์— ๋ชจ๋“  ์š”์†Œ๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.

์ด๋•Œ ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์„๊นŒ?

  • ๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ OOM Killer๊ฐ€ FastAPI ๋˜๋Š” SpringBoot์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ, ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŽ์ด ์“ฐ๊ณ  ์žˆ๋˜ MySQL์„ ์ข…๋ฃŒ
    • ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ปจํ…Œ์ด๋„ˆํ™”ํ•˜์—ฌ ๋…๋ฆฝ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ œํ•œํ•˜๊ณ  ์ตœ๋Œ€ ์‚ฌ์šฉ ํ•œ๋„ ์ง€์ •
  • CPU ๊ฒฝํ•ฉ์ด ๋ฐœ์ƒํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ ๊ฐ„ Context Switching ์˜ค๋ฒ„ํ—ค๋“œ๋กœ ์ธํ•ด ์ „์ฒด ์„œ๋น„์Šค ์ง€์—ฐ
    • 1์ฐจ ๋ฐฐํฌ ํƒ€๊ฒŸ์€ ํ•œ์ •์ ์ด๋ผ ๋ฐœ์ƒ ํ™•๋ฅ ์ด ๋‚ฎ๊ณ , ๊ธฐํš ํŠน์„ฑ ์ƒ ์ˆ˜์ดˆ ์ด๋‚ด์˜ ์ผ์‹œ์  ์ง€์—ฐ์€ ํ—ˆ์šฉ ๊ฐ€๋Šฅ
  • ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋กœ๊ทธ๊ฐ€ ์Œ“์—ฌ ๋””์Šคํฌ ์šฉ๋Ÿ‰์ด ๊ณ ๊ฐˆ๋˜๊ณ , DB ์“ฐ๊ธฐ ์ž‘์—… ์ค‘๋‹จ์œผ๋กœ ์ „์ฒด ์‹œ์Šคํ…œ ์ค‘๋‹จ
    • S3, CloudWatch ๋“ฑ ์™ธ๋ถ€ ๋กœ๊ทธ ์ €์žฅ์†Œ ํ™œ์šฉ

๊ฐ ์ปจํ…Œ์ด๋„ˆ์— ์š”๊ตฌ๋˜๋Š” ์ตœ์†Œ ๋ฉ”๋ชจ๋ฆฌ

DAU๊ฐ€ 30์ด๊ธฐ์— ์ผ๋ฐ˜์ ์ธ ์šด์˜ ํ™˜๊ฒฝ ๊ธฐ์ค€์ด ์•„๋‹Œ, ๊ธฐ๋ณธ ๊ตฌ๋™์ด ๊ฐ€๋Šฅํ•œ ์ˆ˜์ค€์œผ๋กœ ๋น„์šฉ ์ตœ์ ํ™”
=> ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ จ ์„ค์ •๊ฐ’(JVM ํž™, ์ตœ๋Œ€ Connection ๋“ฑ) ํŠœ๋‹ ํ•„์š”
=> FE, BE, AI ๊ฐœ๋ฐœ ๊ฒฐ๊ณผ๋ฌผ์— ๋”ฐ๋ผ ์กฐ์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Nginx: 50MB

    • ์›Œ์ปค ํ”„๋กœ์„ธ์Šค 1๊ฐœ ๋ฉ”๋ชจ๋ฆฌ 10MB ๋‚ด์™ธ ์˜ˆ์ƒ
  • Redis: 128MB

    • ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ ํฌ๊ธฐ 10MB ๋‚ด์™ธ ์˜ˆ์ƒ
    • BE์˜ Redis ํ™œ์šฉ ๋ฒ”์œ„๊ฐ€ ๋ฏธ์ •์ธ ๊ด€๊ณ„๋กœ ์—ฌ์œ  ์žˆ๊ฒŒ ์‚ฐ์ •
  • MySQL: 256MB

    • BufferPool ์ถ•์†Œ, ์ตœ๋Œ€ Connection ์ œํ•œ ๋“ฑ ์„ค์ •์œผ๋กœ 220MB ์„ ์—์„œ ๋ถ€ํŒ… ์˜ˆ์ƒ
  • MongoDB: 512MB

    • WiredTiger ์—”์ง„์˜ ๋‚ด๋ถ€ ์บ์‹œ ํ•˜ํ•œ์„ ์ด 216MB
    • ์บ์‹œ ์™ธ์— ์—”์ง„ ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ ์Šคํƒ, ์—ฐ๊ฒฐ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ์‹ค์ œ ์•ฝ 500MB๊นŒ์ง€ ์ ์œ 
    • ๊ฐ•์ œ๋กœ ์ถ•์†Œํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋ฆฌ๋ˆ…์Šค Swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •์„ ๊ณ ๋ คํ•˜๋Š” ๋“ฑ OOM Kill์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ธฐ์— ์•ˆ์ •์ ์œผ๋กœ ์ตœ์†Œ 512MB ํ™•๋ณด
  • FastAPI: 512MB

    • ์™ธ๋ถ€ API ํ˜ธ์ถœ ์ „์šฉ ์„œ๋ฒ„์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋ถ„ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ
  • SpringBoot: 1GB

    • JVM ํž™, metaspace, TreadPool ๋“ฑ ์ถ•์†Œ ์‹œ ์ถฉ๋ถ„ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ
  • Certbot: 128MB

    • ์ƒ์‹œ ๊ตฌ๋™๋˜์ง€ ์•Š๊ณ , SSL ์ธ์ฆ์„œ ๊ฐฑ์‹ ์„ ์œ„ํ•ด 60์ผ ์ฃผ๊ธฐ๋กœ ์‹คํ–‰๋  ์ž„์‹œ ์ปจํ…Œ์ด๋„ˆ

์ธ์Šคํ„ด์Šค ๋น„์šฉ ์‚ฐ์ •

๊ฐ ์ปจํ…Œ์ด๋„ˆ์˜ ์ตœ์†Œ ๋ฉ”๋ชจ๋ฆฌ์˜ ํ•ฉ์€ ์•ฝ 2.5GB์ด๊ณ , OS ์ปค๋„๊ณผ Docker ๋ฐ๋ชฌ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๊ณ ๋ คํ•˜๋ฉด 3GB๊ฐ€ ์กฐ๊ธˆ ์•ˆ ๋  ๊ฑฐ ๊ฐ™๋‹ค.

SpringBoot, FastAPI ์ปจํ…Œ์ด๋„ˆ์˜ ์‚ฌ์šฉ๋Ÿ‰์„ ๋„‰๋„‰ํ•˜๊ฒŒ ์‚ฐ์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Swap ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋ฉด 2GiB RAM์œผ๋กœ๋„ ๊ตฌ๋™์ด ๊ฐ€๋Šฅํ•  ๊ฑฐ ๊ฐ™๋‹ค.

  • t4g.small, t3a.small, t3.small

Swap ์„ค์ • ์—†์ด ๋„‰๋„‰ํžˆ ์šด์˜ํ•˜๋ ค๋ฉด 4GiB RAM์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • t4g.medium, t3a.medium, t3.medium

๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์ฐจ์ด๋Š” ์ œ์กฐ์‚ฌ(AWS, AMD, Intel)์ด๊ณ , ๊ตณ์ด x86 ํ”„๋กœ์„ธ์„œ๋ฅผ ๊ณ ์ˆ˜ํ•˜๋ฉฐ ๋น„์šฉ์„ ๋” ์ง€๋ถˆํ•  ์ด์œ ๊ฐ€ ์—†๋‹ค.
=> t4g.small ๋˜๋Š” t4g.medium ์„ ํƒ

์œ ํ˜• small ๊ฐ€๊ฒฉ(์„œ์šธ ๋ฆฌ์ „)/์›” medium ๊ฐ€๊ฒฉ(์„œ์šธ ๋ฆฌ์ „)/์›” ํŠน์ง•
t4g 15.18$ 30.37$ ARM ํ”„๋กœ์„ธ์„œ๋กœ, x86 ์ „์šฉ C-extension ๋ชจ๋“ˆ ์‚ฌ์šฉ ๋ถˆ๊ฐ€
t3a 17.08$ 34.16$ x86 ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, t3๋ณด๋‹ค 10% ์ €๋ ด
t3 18.98$ 37.96$ Intel CPU์— ์ตœ์ ํ™”๋œ ์—ฐ์‚ฐ ๋ชจ๋“ˆ์ด ํ•„์š”ํ•œ ๊ฒŒ ์•„๋‹ˆ๋ฉด ๊ฐ€์„ฑ๋น„๊ฐ€ ์•ˆ ์ข‹์•„์„œ ๊ตณ์ด ์„ ํƒํ•  ์ด์œ  ์—†์Œ

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

  1. t4g.medium์ด swap ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •์ด๋‚˜ ์ปจํ…Œ์ด๋„ˆ ํŠœ๋‹์ด ํฌ๊ฒŒ ํ•„์š”ํ•˜์ง€ ์•Š์•„์„œ ์šฐ์„ ์ ์œผ๋กœ ์„ ํƒํ•œ๋‹ค.
  2. ํŒ€์›๋“ค์ด medium์—์„œ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๊ฐœ๋ฐœํ•˜๋Š” ๋™์•ˆ, ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ์„ ์œ„ํ•ด ํŠœ๋‹ํ•œ ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ๊ฐ€ small์—์„œ ๋ฌด๋ฆฌ ์—†์ด ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  3. CI/CD ์‹œ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ํ•  ๊ฒฝ์šฐ ์ผ์‹œ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ 2๋ฐฐ๋กœ ํ•„์š”ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•„์š”์‹œ ์ด ๋ถ€๋ถ„๋„ ํ™•์ธํ•œ๋‹ค.
  4. ๊ฒ€์ฆ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋งˆ์นœ ๊ฒฝ์šฐ t4g.medium์—์„œ t4g.small๋กœ ์ „ํ™˜ํ•œ๋‹ค.

์ „์ฒด ์•„ํ‚คํ…์ฒ˜

์ถ”๊ฐ€๋กœ ํ•„์š”ํ•œ ๊ตฌ์„ฑ

  • CI/CD ๋„๊ตฌ
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ์ €์žฅ์†Œ๋ฅผ GitHub์œผ๋กœ ์‚ฌ์šฉ ์ค‘์ด๋‹ค.
      => ๋ณ„๋„ ์„œ๋ฒ„ ๊ตฌ์ถ•์ด ๋ถˆํ•„์š”ํ•˜๋ฉฐ ์ถ”๊ฐ€ ๋น„์šฉ์ด ์—†๋Š” GitHub Actions๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์™ธ๋ถ€ ์Šคํ† ๋ฆฌ์ง€
    • ์œ ์ €๊ฐ€ ๋ฌผํ’ˆ ๋“ฑ๋ก ์‹œ ์‚ฌ์ง„์„ ์ฒจ๋ถ€ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์ €์žฅํ•  ์ €์žฅ์†Œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
      => ๋ฐ์ดํ„ฐ ์ฆ๊ฐ€ ์‹œ ์œ ์—ฐํ•œ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์‚ฌ์šฉํ•œ ๋งŒํผ๋งŒ ๋น„์šฉ์„ ์ฒญ๊ตฌํ•˜์—ฌ ๊ฒฝ์ œ์ ์ธ Amazon S3๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

Docker Compose ๊ธฐ๋ฐ˜ ๋ฐฐํฌ ๊ตฌ์กฐ

1๋Œ€์˜ EC2 ์ธ์Šคํ„ด์Šค์— ์—ฌ๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” Docker Compose๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์„ ์–ธ์ ์œผ๋กœ ๋ฐฐํฌ/์šด์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
=> ๊ตฌ์„ฑ ์š”์†Œ๋“ค์˜ ์ „๋ฐ˜์ ์ธ ๋ฐฐํฌ ํ™˜๊ฒฝ์„ ํ•œ ๊ฐœ์˜ ํŒŒ์ผ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์ผ๊ด€์ ์œผ๋กœ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋‹ค.

EBS ๋งˆ์šดํŠธ

EBS ๋งˆ์šดํŠธ ๊ฒฝ๋กœ /var/lib/docker/volumes/๋กœ ์ง€์ •
=> Docker ์—”์ง„์ด ํ•˜์œ„ ์Šคํ† ๋ฆฌ์ง€ ์ž๋™ ์ƒ์„ฑ
=> ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ์‹œ ๋‚ด๋ถ€ UID์— ๋งž์ถฐ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ถŒํ•œ ์ž๋™ ์„ค์ •

docker-compose.yml
services:
  # ๋ฉ”์ธ DB
  mysql:
    image: mysql:8.4
    container_name: mysql
    restart: unless-stopped
    # dirty page ๋””์Šคํฌ ํ”Œ๋Ÿฌ์‹œ๋ฅผ ์œ„ํ•œ ๋Œ€๊ธฐ ์‹œ๊ฐ„ ํ™•๋ณด
    stop_grace_period: 1m
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      TZ: Asia/Seoul
    # 256MB ํ™˜๊ฒฝ ๊ตฌ๋™์„ ์œ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋‹ค์ด์–ดํŠธ ์˜ต์…˜ ์ „๋‹ฌ
    command:
      - --innodb-buffer-pool-size=128M
      - --performance-schema=OFF
      - --max-connections=30
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    volumes:
      - mysql-data:/var/lib/mysql
    deploy:
      resources:
        limits:
          memory: 384M # 256MB ๋ชฉํ‘œ์ด๋‚˜ ๊ตฌ๋™ ์‹œ ๋ฒ„ํผ ๊ณ ๋ คํ•ด 384MB ํ•˜๋“œ ๋ฆฌ๋ฐ‹
    healthcheck:
      # ํฌํŠธ ์˜คํ”ˆ ์—ฌ๋ถ€๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ์ฟผ๋ฆฌ ์‘๋‹ต ์—ฌ๋ถ€ ๊ฒ€์ฆ
      test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    logging: &default-logging
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - backend-net

  # ์ฑ„ํŒ… DB
  mongodb:
    image: mongo:7.0
    container_name: mongodb
    restart: unless-stopped
    stop_grace_period: 30s
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      TZ: Asia/Seoul
    # WiredTiger ์บ์‹œ๋ฅผ 256MB๋กœ ๋ช…์‹œ์  ์ œํ•œํ•˜์—ฌ OOM Crash ๋ฐฉ์ง€
    command: ["mongod", "--wiredTigerCacheSizeGB", "0.25"]
    volumes:
      - mongo-data:/data/db
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "mongosh --eval 'db.adminCommand(\"ping\")' --quiet"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s
    logging: *default-logging
    networks:
      - backend-net

  # ์„ธ์…˜ ๊ด€๋ฆฌ ๋ฐ ์‘๋‹ต ์บ์‹ฑ
  redis:
    image: redis:7.2-alpine
    container_name: redis
    restart: unless-stopped
    command: >
      redis-server
      --requirepass ${REDIS_PASSWORD}
      --maxmemory 96mb
      --maxmemory-policy allkeys-lru
    deploy:
      resources:
        limits:
          memory: 128M
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 5s
      timeout: 3s
      retries: 3
      start_period: 5s
    logging: *default-logging
    networks:
      - backend-net

  # AI ์ค‘๊ณ„ ์„œ๋ฒ„
  fastapi:
    image: ${FASTAPI_IMAGE} # GitHub Actions์—์„œ ๋นŒ๋“œ/ํ‘ธ์‹œํ•œ ์ด๋ฏธ์ง€ ํƒœ๊ทธ
    container_name: fastapi
    restart: unless-stopped
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - CLAUDE_API_KEY=${CLAUDE_API_KEY}
      - TZ=Asia/Seoul
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging: *default-logging
    networks:
      - backend-net

  # ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง API ์„œ๋ฒ„
  springboot:
    image: ${SPRINGBOOT_IMAGE}
    container_name: springboot
    restart: unless-stopped
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?useSSL=false&allowPublicKeyRetrieval=true
      - SPRING_DATASOURCE_USERNAME=${MYSQL_USER}
      - SPRING_DATASOURCE_PASSWORD=${MYSQL_PASSWORD}
      - SPRING_DATA_MONGODB_URI=mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongodb:27017/${MONGO_DATABASE}?authSource=admin
      - SPRING_DATA_REDIS_HOST=redis
      - SPRING_DATA_REDIS_PORT=6379
      - SPRING_DATA_REDIS_PASSWORD=${REDIS_PASSWORD}
      - FASTAPI_BASE_URL=http://fastapi:8000
      - TZ=Asia/Seoul
      # JVM ํž™ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ 512MB๋กœ ์ œํ•œํ•˜๊ณ  GC ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•œ ์˜ต์…˜
      - JAVA_OPTS=-Xms256m -Xmx512m -XX:MaxMetaspaceSize=192m
    depends_on:
      mysql:
        condition: service_healthy # MySQL ํ•‘ ์‘๋‹ต ํ™•์ธ ํ›„ ๊ตฌ๋™ ์‹œ์ž‘
      mongodb:
        condition: service_healthy
      redis:
        condition: service_healthy
      fastapi:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 1024M
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/actuator/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 40s # Spring Boot JVM ๋ถ€ํŒ… ์‹œ๊ฐ„์„ ๊ฐ์•ˆํ•œ ์œ ์˜ˆ์‹œ๊ฐ„
    logging: *default-logging
    networks:
      - backend-net
      - frontend-net

  # Reverse Proxy & Static Web Server
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - certbot-certs:/etc/letsencrypt:ro # ๋ฐœ๊ธ‰๋œ ์ธ์ฆ์„œ
      - certbot-challenge:/var/www/certbot:ro # Let's Encrypt ๊ฒ€์ฆ ํŒŒ์ผ
    depends_on:
      springboot:
        condition: service_healthy # ๋ฐฑ์—”๋“œ ๊ธฐ๋™ ์™„๋ฃŒ ํ›„ ๋ผ์šฐํŒ… ์˜คํ”ˆ
    deploy:
      resources:
        limits:
          memory: 64M
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://localhost/ || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
    logging: *default-logging
    networks:
      - frontend-net

  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    profiles: ["tools"] # ์ƒ์‹œ ๊ตฌ๋™ํ•  ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•  ๋•Œ๋งŒ ๋™์ž‘
    volumes:
      - certbot-certs:/etc/letsencrypt:rw
      - certbot-challenge:/var/www/certbot:rw

# ๋„คํŠธ์›Œํฌ ๋ถ„๋ฆฌ: DB/์บ์‹œ๋Š” ์™ธ๋ถ€์— ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์™„์ „ ๊ฒฉ๋ฆฌ
networks:
  backend-net:
    driver: bridge
  frontend-net:
    driver: bridge

volumes:
  mysql-data:
  mongo-data:
  certbot-certs:
  certbot-challenge:
โš ๏ธ **GitHub.com Fallback** โš ๏ธ