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 ๋์ผ + ์ฑํ ์๋ฎฌ๋ ์ด์ |
- user flow์ ์์ฒญ ์ ๊ณ์ฐ
- ์ธ์ ๋ณ user flow ํ๋ฅ ์ ์ค์ ํด ์ธ์ ๋ณ ์์ฒญ ์ ๊ณ์ฐ
- ์ธ์ RPS๋ฅผ Peak ๋์ ์ฌ์ฉ์์ ๊ณฑํด Peak ์๊ฐ์ RPS ์ฐ์
์์ฒญ ํ์๋ ์กฐํํ ๋ฌผํ ๊ฐ์, ์ฑํ ํ์ ๋ฑ์ ๋ฐ๋ผ ๋ค๋ฅด๊ธฐ์ ํ๋ฆ์ ์ง์ ์์ฑํด๋ณด๋ฉฐ ์ฐ์ ํ๋ค.
- ๋์ค์ฝ๋ ๋งํฌ ์ง์ ๋ฐ ํ์, ๊ทธ๋ฃน ๊ฐ์ => ์์ฒญ 5
- [์ ์1] ํ์๋ค์ด ๋ฑ๋กํ ๊ฐ์ ํ์ธ => ์์ฒญ 6
- ๋ณธ์ธ ๋ฌผํ 1๊ฐ ๋ฑ๋ก ๊ตํ ์์ฒญ => ์์ฒญ 2
- ์ฑํ ์ผ๋ก ๊ตํ ์๊ฐ ๋ฐ ์์น ์กฐ์จ => ์์ฒญ 11
- ๊ต์ก์ฅ ๋ด๋ถ์์ ์ง๊ฑฐ๋ => ์์ฒญ 2(์๋ฃ ์ฒ๋ฆฌ)
- 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๋ ๊ณ ๋ คํด๋ณผ ๋งํ๋ค.
- ์ง๊ธ ์ฐ๋ฆฌ๋ ์์ ๋น์ฉ๊ณผ ์ด์ ๋ณต์ก๋๋ก ๋นจ๋ฆฌ ๊ตฌ์ถํ๋ ๊ฒ ๋ชฉํ๋ค.
- ๋ญ ์ ํํ๋ DAU๊ฐ 30๋ช ์ด๊ณ ์ต๋ 100๋ช ์ ์ ์ ๊ฐ ์๋ค๊ณ ํด๋ ์ธ์คํด์ค 1~2๋๋ฉด ์ถฉ๋ถํ๋ค.
- ์ฐ๋ฆฌํ ํด๋ผ์ฐ๋ ์ธ์์ด ๋ ๋ช ์ธ๋ฐ, ๋ ๋ช ๋ค AWS๋ง ์ฌ์ฉ ๊ฒฝํ์ด ์๋ค.
- ํ์ฌ๋ก์ ์ฒด๊ฐํ ์ ์๋ AWS์ Azure, GCP์์ ์ฌ์ฉํ๊ฒ ๋ ์๋น์ค์ ์ฐจ์ด๊ฐ ํฌ์ง ์๊ณ , ๋น์ฉ๋ ํ์ฌ ํ์ํ ๊ตฌ์ฑ์ด๋ฉด ๊ฑฐ์ ์ฐจ์ด๊ฐ ์๋ค.
- ๋น ๋ฅด๊ฒ ๊ตฌ์ถํ๊ธฐ ์ํด ์ต์ํ๊ณ ๋ ํผ๋ฐ์ค๊ฐ ๋ง์ 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์ฐจ ๋ฐฐํฌ์์๋ ์ ์ ๊ฐ 30~100๋ช ์ด๋ผ ์๊ฐ ๋์ญํญ 100GB์ ๋ฏธ์น์ง ์์ ๊ฒ์ด๊ธฐ์ ํ ํฐ ํ๋๋ ๊ณ ๋ คํ์ง ์์๋ ๋๋ค.
- ์ ์ฅ์๋ public์ด์ง๋ง, ํ ํ๋ก์ ํธ(organization)์ด๊ธฐ ๋๋ฌธ์ vercel์ ์ถ๊ฐ ๋น์ฉ์ด ๋ฐ์ํ๊ธฐ์ ํ๋ฝ์ด๋ค.
- GitHub Pages๋ 404 ์ฐํ ์ค์ ์ด ํ์ํ ์ ์์ ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ๋๋ฐ, ํธ์์ฑ๋ ์์ง๋ง ๋ณต์ก๋๊ฐ ๋ ํด ๊ฒ์ผ๋ก ํ๋จํด์ ํ๋ฝ์ด๋ค.
- CloudFront๋ ์ต๋ ์ฅ์ ์ด ๊ธ๋ก๋ฒ ์บ์ฑ์ธ๋ฐ, [๋ฐ๊พธ๊น]๋ ๊ตญ๋ด ๊ฑฐ์ฃผ์๋ง ์ฌ์ฉํ ์๋น์ค์ด๊ธฐ ๋๋ฌธ์ ํ์ ์๋ค.
- ๋ฐ๋ผ์ CloudFront์ Nginx๋ ํ์ฌ ๊ท๋ชจ์์ ํฌ๊ฒ ์ฐจ์ด๊ฐ ์๋ค๊ณ ํ๋จํ๋ค.
- ๋ค๋ง, S3 ๋ฒํท ์ ์ฑ ์ FE ๋ฐฐํฌ๋ง์ ์ํด ๋ฐ๋ก ๊ด๋ฆฌํ๋ ๊ฒ๋ณด๋ค๋ Docker Compose๋ก ๋ค๋ฅธ ๊ตฌ์ฑ ์์๋ค๊ณผ ๋ฌถ์ด์ ํ ๋ฒ์ ๊ด๋ฆฌํ๋ ๊ฒ ๋ ํธ๋ฆฌํ๊ณ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๊ธฐ ์ฌ์ธ ๊ฒ์ด๋ค.
- ๊ฒฐ๋ก ์ ์ผ๋ก Nginx ๋น์ฒจ
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๋ง์ ์ ๋์ ๊ธ์ก์ด๋ผ ์น๋ช ์ ์ด์ง ์๋ค. ์ค๊ณ ๋จ๊ณ์ ์ฐ์ถ๋ฌผ์ด ์๋ ์ํ์์ ๊ฐ ์ปจํ ์ด๋๋ฅผ ์์๋ก ๊ฐ์ ํ๊ณ ์ง์ ํ๋์ ํด์ ํ ์คํธํ๊ธฐ์๋ ์๊ฐ์ , ๋น์ฉ์ ์ผ๋ก ํจ์จ์ ์ด์ง ์๋ค.
- t4g.medium์ด swap ๋ฉ๋ชจ๋ฆฌ ์ค์ ์ด๋ ์ปจํ ์ด๋ ํ๋์ด ํฌ๊ฒ ํ์ํ์ง ์์์ ์ฐ์ ์ ์ผ๋ก ์ ํํ๋ค.
- ํ์๋ค์ด medium์์ ๋์์ ํ ์คํธํ๊ณ ๊ฐ๋ฐํ๋ ๋์, ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ์ ์ํด ํ๋ํ ๋ชจ๋ ์ปจํ ์ด๋๊ฐ small์์ ๋ฌด๋ฆฌ ์์ด ๋์ํ๋์ง ํ์ธํ๋ค.
- CI/CD ์ ๋ฌด์ค๋จ ๋ฐฐํฌ๋ฅผ ํ ๊ฒฝ์ฐ ์ผ์์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๊ฐ 2๋ฐฐ๋ก ํ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ํ์์ ์ด ๋ถ๋ถ๋ ํ์ธํ๋ค.
- ๊ฒ์ฆ์ ์ฑ๊ณต์ ์ผ๋ก ๋ง์น ๊ฒฝ์ฐ t4g.medium์์ t4g.small๋ก ์ ํํ๋ค.
์ถ๊ฐ๋ก ํ์ํ ๊ตฌ์ฑ
- CI/CD ๋๊ตฌ
- ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋ ์ ์ฅ์๋ฅผ GitHub์ผ๋ก ์ฌ์ฉ ์ค์ด๋ค.
=> ๋ณ๋ ์๋ฒ ๊ตฌ์ถ์ด ๋ถํ์ํ๋ฉฐ ์ถ๊ฐ ๋น์ฉ์ด ์๋ GitHub Actions๋ฅผ ์ฌ์ฉํ๋ค.
- ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋ ์ ์ฅ์๋ฅผ GitHub์ผ๋ก ์ฌ์ฉ ์ค์ด๋ค.
- ์ธ๋ถ ์คํ ๋ฆฌ์ง
- ์ ์ ๊ฐ ๋ฌผํ ๋ฑ๋ก ์ ์ฌ์ง์ ์ฒจ๋ถํ๊ธฐ ์ํด ์ด๋ฏธ์ง ํ์ผ์ ์ ์ฅํ ์ ์ฅ์๊ฐ ํ์ํ๋ค.
=> ๋ฐ์ดํฐ ์ฆ๊ฐ ์ ์ ์ฐํ ํ์ฅ์ด ๊ฐ๋ฅํ๋ฉฐ, ์ฌ์ฉํ ๋งํผ๋ง ๋น์ฉ์ ์ฒญ๊ตฌํ์ฌ ๊ฒฝ์ ์ ์ธ Amazon S3๋ฅผ ์ฌ์ฉํ๋ค.
- ์ ์ ๊ฐ ๋ฌผํ ๋ฑ๋ก ์ ์ฌ์ง์ ์ฒจ๋ถํ๊ธฐ ์ํด ์ด๋ฏธ์ง ํ์ผ์ ์ ์ฅํ ์ ์ฅ์๊ฐ ํ์ํ๋ค.
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: