Cloud Design 1st - 100-hours-a-week/KTB4-1st-wiki GitHub Wiki
๋ฌธ์ ์ ์: ์คํํธ์ ์๋น์ค๋ฅผ ์ ๋ ดํ๊ณ ๋น ๋ฅด๊ฒ ๋ฐฐํฌํด์ผ ํ๋ค.
- ํ์ฌ ๊ฐ๋ฐ ์ธ๋ ฅ 6๋ช
- 4๋ช ์ ๊ธฐ๋ฅ ๊ฐ๋ฐ ๋ด๋น
- 2๋ช ์ ์ธํ๋ผ ๋ด๋น
- ์คํํธ์ ์ด๋ฏ๋ก ๋น์ฉ๊ณผ ์๊ฐ์ด ๋ถ์กฑํ๋ฉฐ, ๋น์ฉ์ด ์ ๋์ ์ผ๋ก ๋ถ์กฑํจ
- ์์ ๋น์ฉ๊ณผ ์ด์ ๋ณต์ก๋๋ก ์๋น์ค๋ฅผ ๋น ๋ฅด๊ฒ ์ถ์ํ ์ ์๋ ํ๊ฒฝ ๊ตฌ์ถ
๊ธฐ์ค
- ์ ์ ๋น์ฉ
- ๋น ๋ฅด๊ฒ ๋ฐฐํฌ(์ ์ ์ด์ ๋ณต์ก๋)
1์ฐจ ๋ฐฐํฌ์์๋ ์ฅ์ ๋ก ์ธํด ์๋น์ค ์ ๊ณต์ด ์ ์ ์ค๋จ๋์ด๋ ๊ด์ฐฎ๋ค๊ณ ํ๋จํ๋ค.
์ฐ๋ฆฌ ์๋น์ค๋ ์ด๊ธฐ์ ๋ถํธ์บ ํ ๋ด๋ถ์์๋ง ํ์์ ์ผ๋ก ๋ฐฐํฌ๋๋ฏ๋ก ์๋น์ค๊ฐ ์ค๋จ๋๋๋ผ๋ ํฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
| ํญ๋ชฉ | v1 |
|---|---|
| ํ๊ฒ ์ ์ | ์นดํ ๋ถ ๊ต์ก์ (20๋ ์ค๋ฐ-30๋ ์ด๋ฐ) |
| SOM(๋ชฉํ ์์ฅ) | 120๋ช |
| MAU | 30๋ช |
| DAU | 30๋ช |
| ์ธ์ ์์ฒญ์ | 21.23 |
| ์ธ์ ์ | 30 (DAU์ ๋์ผ) |
| ํ๋ฃจ ์ด ์์ฒญ ์ | 636.9 |
| ํธ๋ํฝ ์ง์ค ์๊ฐ | 140๋ถ = 8,400์ด |
| ํ๊ท RPS | 0.076 |
| ํธ๋ํฝ/์์ ์ฌ์ฉ๋ ํฐ ๊ธฐ๋ฅ | ์ด๋ฏธ์ง ์ ๋ก๋, ๋ฌผํ ๋ฑ๋ก ๊ฒ์ฆ ์ AI API ํธ์ถ |
ํธ๋ํฝ ์ง์ค ์๊ฐ: ํ์ผ ์ฌ๋ ์๊ฐ(9:00โ18:00 ์ค ๋งค์ 50๋ถโ์ ๊ฐ, 9ํร10๋ถ=90๋ถ) + ์ ์ฌ์๊ฐ(12:00โ13:00, 60๋ถ) = 140๋ถ = 8,400์ด
v1์ ์ฌ์ฉ์ ์๊ฐ ์ ๊ณ ์ฌ์ฉ ์๊ฐ๋๊ฐ ๊ณ ์ ๋์ด ์์ด ํผํฌ RPS๋ณด๋ค ํ๊ท RPS๊ฐ ๋ ์ ์๋ฏธํ๋ค. ํ๊ท RPS = ํ๋ฃจ ์ด ์์ฒญ ์ รท ํธ๋ํฝ ์ง์ค ์๊ฐ(์ด)
| ๋ฒํธ | User Flow | ์์ฒญ | ์์ฒญ ์ |
|---|---|---|---|
| 1 | ๋์ค์ฝ๋ ์ง์ โ ์นด์นด์ค ๋ก๊ทธ์ธ โ ์ทจํฅ ์ค์ โ ๊ทธ๋ฃน ํ์ โ ๊ทธ๋ฃน ๊ฐ์ | ์นด์นด์ค ๋ก๊ทธ์ธ ๋ฆฌ๋ค์ด๋ ํธ 1, ์ธ์ฆ ์ฝ๋ฐฑ 1, ๊ฑฐ๋ ์ทจํฅ ์ค์ 1, ๊ทธ๋ฃน ํ์ 1, ๊ทธ๋ฃน ์ฐธ๊ฐ 1 | 5 |
| 2 | ๋ฌผ๊ฑด ๋ฑ๋ก | ์ด๋ฏธ์ง ์ ๋ก๋ 1, ๋ฑ๋ก ์ ์ฅ 1 | 2 |
| 3 | ๊ทธ๋ฃน ๋ด ๋ฌผ๊ฑด ํ์ โ ์์ธ ํ์ธ | ํ ๋ชฉ๋ก ์กฐํ 1, ๋ฌผ๊ฑด ์์ธ ์กฐํ 5 | 6 |
| 4 | ๊ตํ ์์ฒญ โ ์ฑํ ์กฐ์จ โ ์ง๊ฑฐ๋ | ๋ด ๋ฌผ๊ฑด ์ ํ ์กฐํ 1, ๊ตํ์์ฒญ ์์ฑ 1, ์ฑํ ๋ฐฉ ์์ฑ 1, ํ์คํ ๋ฆฌ ์กฐํ 1, ๋ฉ์์ง ์๋ณต 7 | 11 |
| 5 | ๊ตํ ์๋ฃ ์ฒ๋ฆฌ | ์๋ฃ ์ฒ๋ฆฌ 1, ์์ธ ์ฌ์กฐํ 1 | 2 |
| ํฉ๊ณ | 26 |
| ๋ฒํธ | User Flow | ์์ฒญ ์ | ํ๋ฅ | ๊ฐ์ค ์์ฒญ ์ |
|---|---|---|---|---|
| 1 | ์นด์นด์ค ๋ก๊ทธ์ธ โ ๋ชฉ๋ก ํ์ | 3 | 100% | 3.0 |
| 2 | ๋ฌผ๊ฑด ๋ฑ๋ก | 2 | 10% | 0.2 |
| 3 | ๋ฌผ๊ฑด ์์ธ ํ์ธ | 6 | 50% | 3.0 |
| 4 | ๊ตํ ์์ฒญ โ ์ฑํ ์กฐ์จ โ ์ง๊ฑฐ๋ | 11 | 30% | 3.3 |
| 5 | ๊ตํ ์๋ฃ ์ฒ๋ฆฌ | 2 | 30% | 0.6 |
| ํฉ๊ณ | 10.1 |
์ธ์ ํ๊ท ์์ฒญ์
| ๊ตฌ๋ถ | ๋น์จ | ์์ฒญ ์ |
|---|---|---|
| ์ ๊ท | 70% | 26 |
| ์ฌ๋ฐฉ๋ฌธ | 30% | 10.1 |
| ๊ฐ์ค ํ๊ท | 100% | 21.23 |
ํ๋ฃจ ์ด ์์ฒญ ์ = ์ธ์
์ ร ์ธ์
ํ๊ท ์์ฒญ ์ = 30 ร 21.23 = 636.9
ํ๊ท RPS = 636.9 รท 8,400 โ 0.076
- ์ฌ์ฉ์ ๊ท๋ชจ(DAU 30๋ช ): ์๊ท๋ชจ ์ด๊ธฐ ์๋น์ค์์ 30๋ช ์ ์์ด ํน์ ์๊ฐ์ ์ผ์ ํ ์ธ์ ์์ฒญ์ ์์๋ถ๋ ๊ทน๋จ์ ์ธ ํผํฌ ์ํฉ์ ๋นํ์ค์
- ๋ช ํํ ํ๋ ์๊ฐ๋: 24์๊ฐ ์์ ์ ์ ์๋น์ค๊ฐ ์๋๋ฏ๋ก, ์ค์ ์์ฒญ์ด ๋ฐ์ํ๋ ์ฃผ์ ํ๋ ์๊ฐ๋(์ฌ๋ ์๊ฐ + ์ ์ฌ์๊ฐ = 140๋ถ / 8,400์ด)๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฐ์ ํด์ผ ํ์ค์ ์ธ ์ด๋น ๋ถํ๋ฅผ ํ์ ํ ์ ์์
- ๊ฒฐ๋ก : v1 ์ธํ๋ผ ์ฌ์ด์ง์ ํ์ค์ ์ธ ๊ธฐ์ค์ ๋นํ์ค์ ์ธ ์๊ฐ ํผํฌ๊ฐ ์๋, ์ง์ค ์๊ฐ๋ ํ๊ท RPS
1์ฐจ ๋ฐฐํฌ์ ๋ชฉํ: ์ ์ ๋น์ฉ๊ณผ ๋น ๋ฅธ ๋ฐฐํฌ์ฐ๋ฆฌ ์๋น์ค๋ ํด๋ผ์ฐ๋ ์๋น์ค๋ฅผ ์ฌ์ฉํด 1์ฐจ ๋ฐฐํฌํ๋ค.
- ๋ฐฐํฌ๋ ํฌ๊ฒ ์จ ํ๋ ๋ฏธ์ค์ ํด๋ผ์ฐ๋ ์๋น์ค๋ก ๋๋๋ค.
- ์จ ํ๋ ๋ฏธ์ค๋ ์ด๊ธฐ ํฌ์ ๋น์ฉ์ด ๋์ผ๋ฏ๋ก([N100 ๋ฏธ๋ PC 22๋ง์]((https://prod.danawa.com/info/?pcode=35770394&keyword=n100), ๊ณต์ ๊ธฐ 4.5๋ง์ , UPS 18๋ง์๋ฅผ ํฉ์ณ 44.5๋ง์) ํ์ฌ ์ํฉ์ ์ ํฉํ์ง ์์๋ค.
- ํด๋ผ์ฐ๋๋ ์จ ํ๋ ๋ฏธ์ค ๋๋น ์ ์ ๋น์ฉ(AWS EC2 t3.medium ๊ธฐ์ค ํ ๋ฌ ์ฝ 38๋ฌ๋ฌ)์ผ๋ก ์๋น์ค๋ฅผ ๋ฐฐํฌํ ์ ์์ด์ ์ด๊ธฐ ๋น์ฉ์ ์ค์ฌ์ผ ํ๋ ์ง๊ธ ์ํฉ์ ์ ๋ง์๋ค.
- ๋ฐ๋ผ์ 1์ฐจ ๋ฐฐํฌ๋ ํด๋ผ์ฐ๋ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ค.
๋จ์ผ ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ์ ๋, CSP ๊ฐ ๋น์ฉ ์ฐจ์ด๊ฐ ํฌ์ง ์์ผ๋ฏ๋ก ๋น ๋ฅธ ๋ฐฐํฌ๋ฅผ ์ํด ํ์์ ๊ฒฝํ์ด ์๋ AWS๋ฅผ ์ฌ์ฉํด ๋ฐฐํฌํ๋ค.
- ์ฐ๋ฆฌ ์๋น์ค๋ DAU๊ฐ 30๋ช ์ด๋ฏ๋ก ํ๋์ ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ํ๋์ ์ธ์คํด์ค๋ง์ ์ฌ์ฉํ ๋์ CSP ๊ฐ์ ๋น์ฉ ์ฐจ์ด๊ฐ ์ ์๋ค.(ํ๋จ ๋น์ฉ ํ ์ฐธ์กฐ) ๋ฐ๋ผ์ '์ ์ ๋น์ฉ'์ด๋ผ๋ ๊ธฐ์ค์์๋ CSP๋ฅผ ๊ณ ๋ฅผ ์ ์๋ค๊ณ ํ๋จํ๋ค.
- '๋น ๋ฅธ ๋ฐฐํฌ' ๊ธฐ์ค์์๋ ์ ์ ์จ์ด ๋์ ๋ ํผ๋ฐ์ค๊ฐ ๋ง๊ณ ํ์ ๋ชจ๋์ ์ฌ์ฉ ๊ฒฝํ์ด ์๋ AWS๊ฐ ์ฐ์ํ๋ค๊ณ ํ๋จํ๋ค.
- ๋ฐ๋ผ์ CSP๋ก AWS๋ฅผ ์ ํํ๋ค.
- ๋์: ๋ํ CSP 3์ฌ(AWS, Azure, GCP)
- ๊ธฐ์ค ์กฐ๊ฑด: Ubuntu, ์จ๋๋งจ๋(Pay-As-You-Go), ํ ๋ฌ 730์๊ฐ ๊ฐ๋, ์์ ์ปดํจํ ์ธ์คํด์ค ๋น์ฉ๋ง ๊ณ ๋ ค
| ํด๋ผ์ฐ๋ (CSP) | ์ธ์คํด์ค ์ ํ | CPU / RAM ์ฌ์ | ์๊ฐ๋น ์๊ธ (USD) | ํ ๋ฌ ์๊ธ (USD) | ํน์ง ๋ฐ CPU ๋ฐฉ์ |
|---|---|---|---|---|---|
| AWS | t3.medium |
2 vCPU / 4 GiB | $0.052 | $37.96 | โข ๋ฒ์คํฐ๋ธ(Burstable) ์ธ์คํด์ค โข ๊ธฐ๋ณธ ์ฑ๋ฅ ๊ธฐ์ค ์ด๊ณผ ์ ํฌ๋ ๋ง ์๋ชจ |
| Azure | B2ls v2 |
2 vCPU / 4 GiB | $0.052 | $37.96 | โข ํฌ๋ ๋ง ๊ธฐ๋ฐ ๋ฒ์คํธ ์ง์ |
| GCP | e2-medium |
2 vCPU / 4 GB | $0.04298286 | $31.38 | โข E2 ๊ณต์ ์ฝ์ด ๋ฐฉ์ โข ๊ธฐ๋ณธ ๋ถํ๊ฐ ์ ์ ์น/API ์๋ฒ์ ์ต์ ํ โข ์์ ์ก์ ๊ฐ์ ํ์ง ์์๋ ์๋์ผ๋ก ์ฌ์ฉ ๊ธฐ๊ฐ์ ๋ํด ํ ์ธ ์ ์ฉ |
๋ฒ์คํฐ๋ธ ํด๋์ค์ ํฌ๋ ๋ง ๊ฐ๋
- t3.medium, Standard_B2s๋ ํฌ๋ ๋ง ๊ธฐ๋ฐ์ด๋ผ ํฌ๋ ๋ง์ด ์์ผ๋ฉด baseline ์ฑ๋ฅ์ผ๋ก ๋์๊ฐ๊ฑฐ๋ ํฌ๋ ๋ง์ ์ฌ์ผ ํ๋ค.(aws t3 unlimited์ ๊ฒฝ์ฐ)
- e2-medium์ ๊ณต์ ์ฝ์ด ๋ฐฉ์์ด๋ผ ๋ฌผ๋ฆฌ ์๋ฒ์ ์ฌ์ ๊ฐ ์๋ ํ, ๊ณ์ํด์ ๋ฒ์คํธํ ์ ์๋ค.
- ์ฐ๋ฆฌ ์๋น์ค๋ ํน์ ์๊ฐ๋์ ์ฌ์ฉ๋๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ฏ๋ก ํฌ๋ ๋ง์ ์ค์ํ ์์๊ฐ ์๋๋ผ๊ณ ํ๋จํ๋ค.
์ด๊ธฐ ๋ฐฐํฌ์์๋ ๋จ์ผ EC2 ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ๊ฒฝ์ ์ ์ด๊ณ ๋จ์ํ๋ค.
- ์ฐ๋ฆฌ ์๋น์ค๋ ์ ์ ์์ ์ฌ์ฉ์๊ฐ ์ฌ์ฉ(ํ๊ท RPS 0.076)ํ๋ฏ๋ก ์ ์ ๋น์ฉ์ผ๋ก ๋ฐฐํฌํด์ผ ํ๊ณ , ๋น ๋ฅด๊ฒ ๋ฐฐํฌํด์ผ ํ๋ฏ๋ก ํ๋์ ์ธ์คํด์ค์ ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌํ๋ ๊ฒ์ด ์ข๋ค๊ณ ํ๋จํ๋ค.
1๋์ EC2 ์์ ์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ๋ํ๋ฉด์ ๋ฐ์ํ๋ ํ๊ฒฝ ์ถฉ๋๊ณผ ์ฅ์ ์ ํ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์ปจํ ์ด๋ ๊ฐ์ํ ๊ธฐ์ ๋ก Docker๋ฅผ ์ฌ์ฉํ๋ค.
- ๋จ์ผ EC2 ์ธ์คํด์ค ์์ ๋ฐฑ์๋(Java/Spring), AI(Python/FastAPI), ํ๋ก ํธ์๋(React), ๋ฐ์ดํฐ๋ฒ ์ด์ค(PostgreSQL), ์บ์(Redis) ๋ฑ ๋ค์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ ๋ฐฐํฌํ๊ณ ์ด์ํด์ผ ํ๋ค.
- ์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ง์ ์ค์นํ์ฌ ๋ฐฐํฌํ๋ ๊ฒฝ์ฐ ํ๊ฒฝ ์ถฉ๋์ด๋ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฅ์ ๊ฐ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํฅ์ ๋ฏธ์น ์ ์๋ค.
- ๋ฐ๋ผ์ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์ปจํ ์ด๋ ๊ธฐ์ Docker๋ฅผ ๋์ ํ๋ค.
- ํ๋ก ํธ์๋
FE ๋ฐฐํฌ ๋ฐฉ์
-
ํน์ง
- EC2 ๋์คํฌ์ ์ ์ฅ๋ ๋น๋ ํ์ผ์ Nginx ํ๋ก์ธ์ค๊ฐ ์ฝ์ด์ HTTP ์๋ต
- ๋น์ฉ: EC2 ์ธ์คํด์ค ๋น์ฉ(์ ๋ฃ ์์ ์๋ชจ)
- ์บ์ฑ ๋ฐ ์๋: EC2 ๋ฆฌ์ ์ ์์กด
- SSL/TLS ์ธ์ฆ์: ์ธ์ฆ์๋ฅผ Nginx์ ์ง์ ์ธํ ํ๊ณ ์ฃผ๊ธฐ์ ๊ฐฑ์ ํ์
- CI/CD: EC2๋ก ๋น๋ ํ์ผ ์ ์ก
- SSR: Node.js ์ปจํ ์ด๋ ์ถ๊ฐ
-
๋จ์
- ๋ฐฐํฌ ์ EC2์ ํ์ผ์ ์ ์กํ๋ ์์ ํ์
- ์ฌ์ฉ์๊ฐ ๋์ด๋๋ฉด FE ์ ์ ํ์ผ ์ ์ก์ด EC2 ์ธ์คํด์ค์ ๋คํธ์ํฌ ๋์ญํญ์ ์ฌ์ฉํด์ BE API ์๋ต ์๋์ ์ ์ํฅ
- ์ธ์คํด์ค์ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด FE ํ๋ฉด๋ ๋ ธ์ถ๋์ง ์์ ์ ๊ฒ ์ค ์๋ด ๋ฑ ๋ถ๊ฐ
-
ํน์ง
- S3 ๋ฒํท์ ๋น๋ ํ์ผ ์ ๋ก๋ ํ CloudFront ์ฃ์ง ์บ์ฑ
- ๋น์ฉ: ๊ฑฐ์ ๋ฌด๋ฃ(ํ๋ฆฌํฐ์ด 1TB/์)
- ์บ์ฑ ๋ฐ ์๋: ๊ธ๋ก๋ฒ ๋ก์ผ์ด์ ์บ์ฑ, ๋น ๋ฆ
- SSL/TLS ์ธ์ฆ์: AWS ACM์ผ๋ก ๊ด๋ฆฌ(์๋ ๊ฐฑ์ )
- CI/CD: S3๋ก sync ํ ์บ์ ๋ฌดํจํ
- SSR: Lambda ์ถ๊ฐ
-
๋จ์
- ๋ฐฐํฌ ์ S3์ ํ์ผ์ ์ ์กํ๋ ์์ ํ์
- S3 ๋ฒํท ์ ์ฑ ๊ด๋ฆฌ
-
ํน์ง
- Git Push ์ ์๋ ๋น๋ ๋ฐ ๊ธ๋ก๋ฒ Edge Network ๋ฐฐํฌ
- ๋น์ฉ: Hobby(๊ฐ์ธ/ํ์ต์ฉ) ํฐ์ด 100GB/์ ๋ฌด๋ฃ, ์์ ์ ์ด์ฉ ์ ์ ๋ฃ
- ์บ์ฑ ๋ฐ ์๋: ๊ธ๋ก๋ฒ ์ฃ์ง ๋คํธ์ํฌ ๊ธฐ๋ณธ ํ์ฌ, ๋น ๋ฆ
- SSL/TLS ์ธ์ฆ์: ๋๋ฉ์ธ ์ฐ๊ฒฐ ์ ์๋ ๋ฐ๊ธ ๋ฐ ๊ฐฑ์
- CI/CD: GitHub ๋ ํฌ ์ฐ๋ํ๋ฉด ๋
- SSR: ์ง์
-
๋จ์
- ์์ ์ ํ๋ก์ ํธ๋ ํ ํ๋ก์ ํธ(GitHub Organization)๋ ๋น์ฉ ๋ฐ์
- ์๊ฐ ๋์ญํญ 100GB ์ด๊ณผ ์ ์๋น์ค ์ผ์ ์ ์ง
-
ํน์ง
- GitHub ๋ ํฌ์ ํน์ ๋ธ๋์น/์ ์ ํ์ผ ํธ์คํ
- ๋น์ฉ: 100GB/์ ๋ฌด๋ฃ
- ์บ์ฑ ๋ฐ ์๋: ๊ธ๋ก๋ฒ CDN์ด ํ์ฌ๋์ด ์์ผ๋ ์ค์ ์ ์ฝ์ด ์์ด ๋ณดํต ์์ค
- SSL/TLS ์ธ์ฆ์: ์๋ ๋ฐ๊ธ ๋ฐ ๊ฐฑ์
- CI/CD: GitHub Actions ํ ํ๋ฆฟ ์ ๊ณต
- SSR: ๋ฏธ์ง์
-
๋จ์
- SPA ๋ผ์ฐํ ์ฃผ์๋ก ์ง์ ์ ๊ทผํ๊ฑฐ๋ ์๋ก๊ณ ์นจ ์ 404 ์๋ฌ ๋ฐ์ ์ํ์ผ๋ก ์ฐํ ์ค์ ํ์
- ์๊ฐ ๋์ญํญ 100GB ์ด๊ณผ ์ ์๋น์ค ์ผ์ ์ ์ง
- ์ ์ฅ์๊ฐ public์ด์ด์ผ ํ๋ฉฐ private ์ ์ฅ์ ๋ฐฐํฌ๋ ์ ๋ฃ ํ๋ ํ์
- ์ง๊ธ 1์ฐจ ๋ฐฐํฌ์์๋ ์ ์ ๊ฐ 30~100๋ช ์ด๋ผ ์๊ฐ ๋์ญํญ 100GB์ ๋ฏธ์น์ง ์์ ๊ฒ์ด๊ธฐ์ ํ ํฐ ํ๋๋ ๊ณ ๋ คํ์ง ์์๋ ๋๋ค.
- ์ ์ฅ์๋ public์ด์ง๋ง, ํ ํ๋ก์ ํธ(organization)์ด๊ธฐ ๋๋ฌธ์ vercel์ ์ถ๊ฐ ๋น์ฉ(ํ์ ํ ๋ช ๋น 20$/์)์ด ๋ฐ์ํ๊ธฐ์ ํ๋ฝ์ด๋ค.
- GitHub Pages๋ 404 ์ฐํ ์ค์ ์ด ํ์ํ ์ ์์ ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ๋๋ฐ, ํธ์์ฑ๋ ์์ง๋ง ๋ณต์ก๋๊ฐ ๋ ํด ๊ฒ์ผ๋ก ํ๋จํด์ ํ๋ฝ์ด๋ค.
- CloudFront๋ ์ต๋ ์ฅ์ ์ด ๊ธ๋ก๋ฒ ์บ์ฑ์ธ๋ฐ, [๋ฐ๊พธ๊น]๋ ๊ตญ๋ด ๊ฑฐ์ฃผ์๋ง ์ฌ์ฉํ ์๋น์ค์ด๊ธฐ ๋๋ฌธ์ ํ์ ์๋ค.
- ๋ฐ๋ผ์ CloudFront์ Nginx๋ ํ์ฌ ๊ท๋ชจ์์ ํฌ๊ฒ ์ฐจ์ด๊ฐ ์๋ค๊ณ ํ๋จํ๋ค.
- ๋ค๋ง, S3 ๋ฒํท ์ ์ฑ ์ FE ๋ฐฐํฌ๋ง์ ์ํด ๋ฐ๋ก ๊ด๋ฆฌํ๋ ๊ฒ๋ณด๋ค๋ Docker Compose๋ก ๋ค๋ฅธ ๊ตฌ์ฑ ์์๋ค๊ณผ ๋ฌถ์ด์ ํ ๋ฒ์ ๊ด๋ฆฌํ๋ ๊ฒ ๋ ํธ๋ฆฌํ๊ณ ํ๋์ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๊ธฐ ์ฌ์ธ ๊ฒ์ด๋ค.
- ๊ฒฐ๋ก ์ ์ผ๋ก Nginx ์ ์
- ๋ฐฑ์๋ & AI ์๋ฒ
- Java์ Python์ ์๋ก ๋ค๋ฅธ ๋ฐํ์ ์์กด์ฑ ์ถฉ๋์ ๋ฐฉ์งํ๋ฉฐ, ์ธ๋ถ LLM์ ํธ์ถํ๋ AI ์๋ฒ์ ๋ณ๋ชฉ์ด ๋ฐฑ์๋๋ก ์ ํ๋์ง ์์์ผ ํ๋ค.
- [๊ฒฐ๋ก ] ๋ฐ๋ผ์ ๋ฐฑ์๋์ AI ์๋ฒ๋ ๊ฐ๊ฐ ๋ ๋ฆฝ๋ Docker ์ปจํ ์ด๋๋ก ๋ฐฐํฌํ๋ค.
- DB(MySQL, MongoDB, Redis)
- ์ปจํ ์ด๋๋ ์ข ๋ฃ ์ ๋ด๋ถ ๋ฐ์ดํฐ๊ฐ ์๋ฉธ๋๋ค. ๊ด๋ฆฌํ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ถ๊ฐ ๋น์ฉ์ด ๋ฐ์๋๋ค.(RDS ์ $21)
- [๊ฒฐ๋ก ] ๋น์ฉ ์ ๊ฐ์ ์ํด DB์ Redis๋ฅผ ๊ฐ๊ฐ ์ปจํ ์ด๋๋ก ์ง์ ๋์ฐ๋, ์ธ์คํด์ค์ ๋์คํฌ(EBS)์ ๋ณผ๋ฅจ์ ์ฐ๊ฒฐํด ๋ฐ์ดํฐ ์์์ฑ์ ๋ณด์ฅํ๋ค.
- DAU๊ฐ 30์ด๊ธฐ์ ์ผ๋ฐ์ ์ธ ์ด์ ํ๊ฒฝ ๊ธฐ์ค์ด ์๋, ๊ธฐ๋ณธ ๊ตฌ๋์ด ๊ฐ๋ฅํ ์์ค์ผ๋ก ๋น์ฉ ์ต์ ํ
- ๋ฉ๋ชจ๋ฆฌ ๊ด๋ จ ์ค์ ๊ฐ(JVM ํ, ์ต๋ Connection ๋ฑ) ํ๋ ํ์
์ปจํ ์ด๋ ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์์ธ
-
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 ๋ฑ ์ถ์ ์ ์ถฉ๋ถํ ๊ฒ์ผ๋ก ์์
๊ฐ ์ปจํ ์ด๋์ ์ต์ ๋ฉ๋ชจ๋ฆฌ์ ํฉ์ ์ฝ 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
๊ฐ ์ฌ์์ ์นฉ์ ์ํคํ ์ฒ์์ ์ฐจ์ด๊ฐ ์๋ค.(ARM, AMD x86, Intel x86) ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ์ ํ๋ฆฌ์ผ์ด์ ์ ARM์์๋ ์๋ํ๋ฏ๋ก ๊ตณ์ด x86 ํ๋ก์ธ์๋ฅผ ๊ณ ์ํ๋ฉฐ ๋น์ฉ์ ๋ ์ง๋ถํ ์ด์ ๊ฐ ์๋ค. ๋ฐ๋ผ์ ์ฌ์์ ๋์ผํ๋ ๋น์ฉ์ด ๋ ์ ๋ ดํ t4g.medium์ ์ฌ์ฉํ๋ค.
EC2 ๋น์ฉํ(์์ธ ๋ฆฌ์ , ์ ์๊ธ)
| ์ ํ | 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๋ง์ ์ ๋์ ๊ธ์ก์ด๋ผ ์น๋ช ์ ์ด์ง ์๋ค. ์ค๊ณ ๋จ๊ณ์ ์ฐ์ถ๋ฌผ์ด ์๋ ์ํ์์ ๊ฐ ์ปจํ ์ด๋๋ฅผ ์์๋ก ๊ฐ์ ํ๊ณ ์ง์ ํ๋์ ํด์ ํ ์คํธํ๊ธฐ์๋ ์๊ฐ์ , ๋น์ฉ์ ์ผ๋ก ํจ์จ์ ์ด์ง ์๋ค๊ณ ํ๋จํ์ฌ, medium ์ฌ์์ผ๋ก ๊ฐ์ ํ ์๋์ ๋จ๊ณ๋ฅผ ๊ฑฐ์น๊ธฐ๋ก ํ๋ค.
- t4g.medium์ด swap ๋ฉ๋ชจ๋ฆฌ ์ค์ ์ด๋ ์ปจํ ์ด๋ ํ๋์ด ํฌ๊ฒ ํ์ํ์ง ์์์ ์ฐ์ ์ ์ผ๋ก ์ ํํ๋ค.
- ํ์๋ค์ด medium์์ ๋์์ ํ ์คํธํ๊ณ ๊ฐ๋ฐํ๋ ๋์, ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ์ ์ํด ํ๋ํ ๋ชจ๋ ์ปจํ ์ด๋๊ฐ small ์ฌ์์์ ๋ฌด๋ฆฌ ์์ด ๋์ํ๋์ง ํ์ธํ๋ค.
- CI/CD ์ ๋ฌด์ค๋จ ๋ฐฐํฌ๋ฅผ ํ ๊ฒฝ์ฐ ์ผ์์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๊ฐ 2๋ฐฐ๋ก ํ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ํ์์ ์ด ๋ถ๋ถ๋ ํ์ธํ๋ค.
- ๊ฒ์ฆ์ ์ฑ๊ณต์ ์ผ๋ก ๋ง์น ๊ฒฝ์ฐ t4g.medium์์ t4g.small๋ก ์ ํํ๋ค.
์ฌ๋ฌ ์ปจํ ์ด๋๋ฅผ ์ ์ธ์ ์ผ๋ก ์ผ๊ด ์ ์ดํ๊ณ ํ๊ฒฝ ์ค์ ์ค์๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด Docker Compose๋ฅผ ์ฌ์ฉํ๋ค.
- ๊ฐ๋ณ `docker run`์ผ๋ก ๊ด๋ฆฌํ๋ฉด ํฌํธ/๋ณผ๋ฅจ/ํ๊ฒฝ๋ณ์ ๋ฑ ๋ณต์กํ CLI ์ต์ ๋๋ฝ์ผ๋ก ํด๋จผ ์๋ฌ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ๊ณ , DB๊ฐ ์คํ๋๊ธฐ ์ ๋ฐฑ์๋๊ฐ ๋จผ์ ์์๋๋ ๋ฑ์ ์ปจํ ์ด๋ ๊ฐ ์คํ ์์์ ๋ด๋ถ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์ผ์ผ์ด ์๋ ๊ด๋ฆฌํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ์๋ค.
- Docker Compose๋ฅผ ํตํด ๋จ์ผ `yaml` ํ์ผ๋ก ์ ์ฒด ์๋น์ค์ ์ค์ ๊ณผ ์์กด ๊ด๊ณ๋ฅผ ๊ด๋ฆฌํ์ฌ ๋ช ๋ น์ด ํ ์ค๋ก ์ผ๊ด ์คํ ๋ฐ ์ฌํ์ด ๊ฐ๋ฅํ๋ค.
EBS ๋ง์ดํธ ๋๋ ํฐ๋ฆฌ ์์ฑ
EBS ๋ง์ดํธ ๊ฒฝ๋ก ์๋ ๋๋ ํฐ๋ฆฌ ์์ฑ
sudo mkdir -p /data/mysql /data/mongodb /data/nginx/conf.d /data/nginx/certbot /data/frontend
์ปจํ ์ด๋ ๋ด๋ถ UID์ ๋ง์ถ ๊ถํ ์ค์
sudo chown -R 999:999 /data/mysql
sudo chown -R 999:999 /data/mongodb
sudo chmod 755 /data/frontend
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:
- /data/mysql:/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:
- /data/mongodb:/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:
- /data/nginx/conf.d:/etc/nginx/conf.d:ro
- /data/nginx/certbot:/etc/letsencrypt:ro
- /data/frontend:/usr/share/nginx/html:ro # React ๋น๋ ์ฐ์ถ๋ฌผ ๋ง์ดํธ
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
# ๋คํธ์ํฌ ๋ถ๋ฆฌ: DB/์บ์๋ ์ธ๋ถ์ ๋
ธ์ถ๋์ง ์๋๋ก ์์ ๊ฒฉ๋ฆฌ
networks:
backend-net:
driver: bridge
frontend-net:
driver: bridge
์๋น์ค ์์ฝ
- ์คํํธ์
์ด ์ต์ด๋ก ๋ฐฐํฌํ๋ ์๋น์ค, DAU 30, ํ๊ท RPS 0.07, ๋ถํธ์บ ํ ๋ด๋ถ ์ธ์์ ํ๊ฒ์ผ๋ก ๋ฐฐํฌ ์์
- AWS EC2 ํ ๋์ ๊ฐ ์๋น์ค ์ปดํฌ๋ํธ๋ฅผ ์ปจํ
์ด๋ํํ์ฌ Docker Compose๋ก ๋ฐฐํฌ์ฐ๋ฆฌ ์๋น์ค๋ ํ๋์ ์ธ์คํด์ค๋ก ๋ฐฐํฌํ๋ฏ๋ก AWS์ ๊ธฐ๋ณธ VPC์ ์๋ธ๋ท์ ์ฌ์ฉํ๋ค.
๊ธฐ๋ณธ VPC์ ํผ๋ธ๋ฆญ ์๋ธ๋ท์ ์ฌ์ฉํ์ฌ ์ธ์คํด์ค๊ฐ ์ธํฐ๋ท์ ์ง์ ๋ ธ์ถ๋๋ฏ๋ก, ์ ๊ทผ ๋ฒ์๋ฅผ ์ค์ด๊ณ ๋น์ธ๊ฐ ์ ๊ทผ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์๋น์ค ์ด์ ๋ฐ ๊ด๋ฆฌ์ ํ์ํ ์ต์ ํฌํธ๋ง ํ์ฉํ๋ค.
Inbound
- 80 TCP (HTTP) 0.0.0.0/0(์ ์ญ): ์ฌ์ฉ์์ HTTP ์์ฒญ, HTTPS๋ก ๋ฆฌ๋ค์ด๋ ์
ํ๋ค.
- 443 TCP (HTTPS) 0.0.0.0/0(์ ์ญ): ์ฌ์ฉ์์ HTTPS ์์ฒญ, ํธ๋ํฝ์ Nginx ์ปจํ
์ด๋๋ก ์ ๋ฌํด ์์ฒญ ๋ผ์ฐํ
์ฒ๋ฆฌํ๋ค.
- 22 TCP (SSH) ๊ฐ๋ฐIP: ํ์ฌ ์ธํ๋ผ ํ์ IP์์๋ง ์ ๊ทผํ๋๋ก ์ค์ ํ๋ค.์ฐ๋ฆฌ ํ์ ์ธํ๋ผ ๋ด๋น์ tobby์ janna 2์ธ์ด๋ค.
aws root ๊ณ์ ์ด ํ์ทจ๋๋ ๊ฒฝ์ฐ, ๋น์ผ EC2 ๋ฑ์ ์์ฑ/์ฌ์ฉํ์ฌ ์ธํ๋ผ ๋น์ฉ์ด ๊ณผ๋ํ๊ฒ ์ง์ถ๋ ์ ์๋ค.
๋ฐ๋ผ์ root ๊ณ์ ์ด ํ์ทจ๋์ง ์๋๋ก ํ๊ณ , root ๊ณ์ ์ ์ธํ๋ผ ๋ด๋น์๊ฐ ์ ๊ทผํ์ง ์๋๋ก ์ธํ๋ผ ๋ด๋น์ ๊ฐ์์ ๊ฐ๋ณ IAM User๋ฅผ ์์ฑํ๊ณ 'Infra-Admin' IAM ๊ทธ๋ฃน์ผ๋ก ๋ฌถ์ด ์ ํ๋ ๊ถํ๋ง์ ๋ถ์ฌํด ์ฌ์ฉํ๋๋ก ํ๋ค.
AWS Account
โ
โโ Root
โ โโ ์ผ๋ฐ์ ์ผ๋ก ์์
์ ์ฌ์ฉํ์ง ์๋๋ค
โ
โโ IAM Group: Infra-Admin
โ โโ tobby
โ โโ janna
โ
โโ IAM Users
โโ tobby
โโ janna
์๋๋ 'Infra-Admin' IAM ๊ทธ๋ฃน ์ค์ ์ ํ์ํ ๋ด์ฉ์ด๋ค.
- MFA๊ฐ ์์ผ๋ฉด ๋ชจ๋ ์์ฒญ์ ๊ฑฐ๋ถ(MFA ๋ฑ๋ก ์ ์ธ)
- ๊ถํ ์์น(IAM ์์ ) ๋ถ๊ฐ
- ํ์ฉ๋ ์๋น์ค์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๊ณ , ํ์ฉ๋ ๋ฆฌ์์ค๋ง ์์ฑํ ์ ์์
- ec2(t3), S3, CloudFront, CloudWatch, Billing(ํ์ฌ ๋น์ฉ ์ฝ๊ธฐ๋ง ํ์ฉ)
'Infra-Admin' IAM ๊ทธ๋ฃน์ ์ ์ฉํ JSON ์ ๋ฌธ (ํด๋ฆญํ์ฌ ํผ์น๊ธฐ)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListActions",
"Effect": "Allow",
"Action": [
"iam:ListUsers",
"iam:ListVirtualMFADevices"
],
"Resource": "*"
},
{
"Sid": "AllowUserToCreateVirtualMFADevice",
"Effect": "Allow",
"Action": "iam:CreateVirtualMFADevice",
"Resource": "arn:aws:iam::*:mfa/${aws:username}"
},
{
"Sid": "AllowUserToManageTheirOwnMFA",
"Effect": "Allow",
"Action": [
"iam:EnableMFADevice",
"iam:GetMFADevice",
"iam:ListMFADevices",
"iam:ResyncMFADevice"
],
"Resource": "arn:aws:iam::*:user/${aws:username}"
},
{
"Sid": "AllowUserToDeactivateTheirOwnMFAOnlyWhenUsingMFA",
"Effect": "Allow",
"Action": "iam:DeactivateMFADevice",
"Resource": "arn:aws:iam::*:user/${aws:username}",
"Condition": {
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
},
{
"Sid": "BlockMostAccessUnlessSignedInWithMFA",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ListMFADevices",
"iam:ListUsers",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"iam:ChangePassword",
"iam:GetUser"
],
"Resource": "*",
"Condition": {
"BoolIfExists": { "aws:MultiFactorAuthPresent": "false" }
}
},
{
"Sid": "DenyIAMPrivilegeEscalation",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:CreateRole",
"iam:CreateAccessKey",
"iam:CreateLoginProfile",
"iam:UpdateLoginProfile",
"iam:UpdateAssumeRolePolicy",
"iam:AttachUserPolicy",
"iam:AttachGroupPolicy",
"iam:AttachRolePolicy",
"iam:PutUserPolicy",
"iam:PutGroupPolicy",
"iam:PutRolePolicy",
"iam:CreatePolicy",
"iam:CreatePolicyVersion",
"iam:SetDefaultPolicyVersion",
"iam:AddUserToGroup",
"iam:PassRole"
],
"Resource": "*"
},
{
"Sid": "DenyExpensiveEC2Instances",
"Effect": "Deny",
"Action": [
"ec2:RunInstances",
"ec2:ModifyInstanceAttribute"
],
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": { "ec2:InstanceType": "t3.*" }
}
},
{
"Sid": "AllowEC2",
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
},
{
"Sid": "AllowS3",
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
},
{
"Sid": "AllowCloudFront",
"Effect": "Allow",
"Action": "cloudfront:*",
"Resource": "*"
},
{
"Sid": "AllowCloudWatch",
"Effect": "Allow",
"Action": "cloudwatch:*",
"Resource": "*"
},
{
"Sid": "AllowBillingReadOnly",
"Effect": "Allow",
"Action": [
"ce:Get*",
"ce:Describe*",
"billing:Get*",
"billing:List*",
"budgets:ViewBudget",
"budgets:DescribeBudget"
],
"Resource": "*"
}
]
}์ฌ์ฉ์๊ฐ ์ฐ๋ฆฌ ์๋น์ค๋ฅผ ๊ธฐ์ตํ๊ธฐ ์ฌ์ด ๊ณ ์ ๋ ์ฃผ์๋ก ๋ค์ด์์ผ ํ๋ฏ๋ก, ๋๋ฉ์ธ์ ๊ตฌ๋งคํ์ฌ ๊ณ ์ IP(Elastic IP)์ ์ฐ๊ฒฐํ๋ค.
- ์ฌ์ฉ์๋ IP์ฃผ์๊ฐ ์๋ ๊ธฐ์ตํ๊ธฐ ์ฌ์ด ๋๋ฉ์ธ ์ฃผ์๋ก ์ ๊ทผํ ์ ์์ด์ผ ํ๋ค.
- ๋๋ฉ์ธ ์ฃผ์๋ฅผ EC2 IP ์ฃผ์์ ์ฐ๊ฒฐํ ๋, EC2 ์ฌ์์ ์ IP ๋ณ๊ฒฝ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๊ณ ์ IP(Elastic IP)๋ฅผ ์ฌ์ฉํ๋ค.
์ถ๊ฐ ์ธํ๋ผ ๋น์ฉ์ ๋ฐ์์ํค์ง ์๊ณ ๋น ๋ฅธ ๋ฐฐํฌ๋ฅผ ์ํด, ALB๋ CloudFront ๋์ EC2 ๋ด๋ถ์ Nginx์ Let's Encrypt(Certbot)๋ฅผ ์ฌ์ฉํ์ฌ HTTPS๋ฅผ ์ ์ฉํ๋ค.
- ALB + ACM: ACM ๋ฌด๋ฃ ์ธ์ฆ์ ์ง์ ๋ฐ ๊ด๋ฆฌ๊ฐ ํธํ์ง๋ง, ALB ๊ธฐ๋ณธ ์๊ธ(์ ์ฝ $16~$22)์ด ๋ฐ์ํ์ฌ ๋น์ฉ ์ธก๋ฉด์์ ๋ถ๋ฆฌํ๊ณ , ๋จ์ผ ์ธ์คํด์ค ํ๊ฒฝ์์ ๋ก๋๋ฐธ๋ฐ์ฑ์ด ๋ถํ์ํ์ฌ ์ ์ธํจ
- CloudFront + ACM: 1TB ์ ์ก/10,000,000๊ฑด ๋ฌด๋ฃ, SSL ์ธ์ฆ์ ๊ฐฑ์ ์ด ์๋์ผ๋ก ๋์ง๋ง ์ด๋ฏธ ํ๋ก ํธ ๋ฐฐํฌ์ ์ธ์คํด์ค ๋ด๋ถ ๋ผ์ฐํ ์ ์ํด ์กด์ฌํ๋ Nginx๊ฐ ์๋ ์ํ์์ ์ธ์ฆ์ ๊ด๋ฆฌ๋ฅผ ์ํด Route53, CloudFront, ACM ์ค์ ์ ํ ํ์๊ฐ ์๋ค๊ณ ํ๋จํจ
- Nginx + Let's Encrypt: Let's Encrypt์ Certbot์ ์ฐ๋ํ์ฌ SSL ๋ฐ๊ธ๊ณผ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํจ. ์ธ์ฆ์ 90์ผ ๋ง๋ฃ ๊ด๋ฆฌ๋ Certbot ์๋ ๊ฐฑ์ ์ค์ผ์ค๋ฌ๋ฅผ ๋ฑ๋กํ์ฌ ์๋ํ๋ก ํด๊ฒฐ ๊ฐ๋ฅํ๊ณ , ์ด๋ฏธ ๋จ์ผ ์ธ์คํด์ค ๋ด ๋ผ์ฐํ ์ ์ํด Nginx ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ๋ฏ๋ก, ์ถ๊ฐ ๋น์ฉ ์์ด ๋น ๋ฅด๊ฒ ๋ฐฐํฌ๊ฐ ๊ฐ๋ฅํ Nginx๋ฅผ ์ ํํจ
์ฌ์ฉ์ (๋ธ๋ผ์ฐ์ )
โ
โโ [HTTP :80] โโ> Nginx โโ> HTTPS(:443)๋ก 301 ๋ฆฌ๋ค์ด๋ ํธ
โ
โโ [HTTPS :443] โโ> Nginx (SSL ์ข
๋ฃ ๋ฐ ๋ผ์ฐํ
)
โ
โโ [/] ์ ์ ํ์ด์ง โโ> React ๋น๋ ํ์ผ ์ง์ ์๋น
โ
โโ [/api] ์์ฒญ โโ> ๋ฐฑ์๋ ์ปจํ
์ด๋ (Spring Boot)
โ
โโ AI ๊ฒ์ฆ โโ> AI ์ปจํ
์ด๋ (FastAPI)
โโ ๋ฐ์ดํฐ โโ> DB ์ปจํ
์ด๋
โโ ์บ์/์ธ์
โโ> Redis ์ปจํ
์ด๋
๋น์ฉ์ ์ค์ด๊ธฐ ์ํด, RDS ๊ฐ์ ๊ด๋ฆฌํ DB๋ฅผ ์ฐ์ง ์๊ณ ์ปจํ ์ด๋๋ก ์ง์ ๋์ฐ๋, ์ปจํ ์ด๋ ์ข ๋ฃ ์ ๋ฐ์ดํฐ ์๋ฉธ์ ๋ง๊ธฐ ์ํด EC2์ ๊ธฐ๋ณธ EBS ๋ณผ๋ฅจ์ ๋ง์ดํธํ์ฌ ์ถ๊ฐ ๋น์ฉ ์์ด ๋ฐ์ดํฐ ์์์ฑ์ ๋ณด์ฅํ๋ค.
- PostgreSQL RDS ์ ๋น์ฉ(์์ธ): ์ธ์คํด์ค(t4g.micro, $0.0250/์๊ฐ * 730์๊ฐ = $18.25) + ์คํ ๋ฆฌ์ง($0.131/GB * 20GB = 2.62) = $20.87 RDS ๋น์ฉ ๋ฌธ์
์ฌ์ฉ์ ์ ๋ก๋ ์ฌ์ ์ ์ ์ฅํ๊ธฐ ์ํด, ์ ๋ ดํ ์ ์๋ S3๋ฅผ ์ฌ์ฉํ๋ค.
- EBS gp3 0.08$/GB
- S3 ์คํ ๋ค๋ 0.023$~0.025$/GB
S3์ ๋นํด EBS๊ฐ 3๋ฐฐ ์ด์ ๋น์ฉ์ด ํฌ๊ธฐ ๋๋ฌธ์ S3๋ก ๊ฒฐ์ ํ๋ค.
ํผ๋ธ๋ฆญ ์ด๋ฏธ์ง ๋ฐฐํฌ ์ ์ธ ๋๊ตฌ ๋ชจ๋ ๋ฌด๋ฃ์ด๋ฏ๋ก, ๋ณ๋์ ์ธ๋ถ ๊ณ์ ์ฐ๋์ด๋ ์ํฌ๋ฆฟ ๋ฐ๊ธ ์์ด ๋น ๋ฅด๊ณ ํธ๋ฆฌํ๊ฒ GitHub ์ํ๊ณ์์ ๊ด๋ฆฌํ ์ ์๋ GHCR์ ์ฌ์ฉํ๋ค.
- Public ์ปจํ ์ด๋ ๋ ์ง์คํธ๋ฆฌ, ์์์ docker ์ด๋ฏธ์ง ๋น๋/์ ๋ก๋ ์กฐ๊ฑด์์๋ Docker hub, AWS ECR, GHCR ์ธ ์ปจํ ์ด๋ ๋ ์ง์คํธ๋ฆฌ ๋ชจ๋ ๋ฌด๋ฃ
- CI/CD ๋จ๊ณ์์ Docker hub์ ECR์ ๊ฐ๊ฐ ์ํฌ๋ฆฟ ๊ด๋ฆฌ๊ฐ ํ์ํ์ง๋ง, GHCR๋ github์์ ์ ๊ณตํ๋ ์ธ์ฆ ํ ํฐ์ ์ฌ์ฉํ ์ ์์ด ํธ๋ฆฌํ๋ค.(
${{ secrets.GITHUB_TOKEN }})
ํ๋ก๋ฉํ ์ฐ์ค๋ ๊ทธ๋ผํ๋ ๊ฐ์ ๋ณ๋ ๋ชจ๋ํฐ๋ง ํด ๊ตฌ์ถ์ ๋ฆฌ์์ค ๋ญ๋น์ ์ด์ ๋ณต์ก๋๊ฐ ์ปค์ง๋ฏ๋ก, ์ถ๊ฐ ๋น์ฉ๊ณผ ์ค์น ์์ด EC2์ CPU/๋ฉ๋ชจ๋ฆฌ/๋คํธ์ํฌ ์ํ๋ฅผ ๊ธฐ๋ณธ ์ ๊ณตํ๋ AWS CloudWatch ๊ธฐ๋ณธ ๋ฉํธ๋ฆญ๊ณผ ์๋์ ์ฌ์ฉํ๋ค.
์ฐ๋ฆฌ ์๋น์ค๋ ์ ์ ๋น์ฉ๊ณผ ๋น ๋ฅธ ๋ฐฐํฌ๋ฅผ ํด์ผํ๋ ํ์ฌ ๋ชฉํ์, ์๋น์ค๊ฐ ํน์ ์ฌ์ฉ์์ ํน์ ์๊ฐ๋์ ์ฌ์ฉ๋ ๊ฒ์ ๊ณ ๋ คํ์๊ธฐ ๋๋ฌธ์ ์๋์ ํ๊ณ์ ์ ๊ฐ๋๋ค.
- ์ ์ฒด ์๋น์ค ์ค๋จ: EC2 ์ธ์คํด์ค 1๋์ ์ฅ์ ๊ฐ ๋ฐ์ํ๋ฉด ์น, API, DB, AI๊น์ง ๋ชจ๋ ์๋น์ค๊ฐ ์ฆ์ ์ค๋จ๋๋ค.
- ๋ฌด์ค๋จ ๋ฐฐํฌ ๋ถ๊ฐ: ๋จ์ผ ์ธ์คํด์ค๋ก ๋ฐฐํฌํ๋ฏ๋ก ์ ๋ฒ์ ์ ๋ฐฐํฌํ ๋ ์ปจํ ์ด๋ ์ฌ์์ ๋์ ์๋น์ค ์ค๋จ(๋ค์ดํ์)์ด ๋ฐ์ํ๋ค.
- ๊ณ ๊ฐ์ฉ์ฑ(HA) ๋ถ์ฌ: ๋จ์ผ AZ ์ข ์์ผ๋ก ์ธํด AZ ์ฅ์ ์ ์๋ ๋ณต๊ตฌ๊ฐ ๋ถ๊ฐํ๊ณ ๋ค์ดํ์์ด ๊ธธ์ด์ง๋ค.
- ์์ ๋ณต์(PITR) ๋ถ์ฌ: ๊ด๋ฆฌํ RDS์ ๋ฌ๋ฆฌ ์งง์ ์๊ฐ ๋จ์(e.g. ์ด/๋ถ ๋จ์) ๋ณต์์ด ์ด๋ ค์ ์ฅ์ ์ง์ ๋ฐ์ดํฐ ๋ณต๊ตฌ์ ํ๊ณ๊ฐ ์๋ค.
- ์ฐ๊ธฐ ์์ ์ ์ค๋ ์ท ๋ฐ์ดํฐ ๋ถ์ผ์น ์ํ์ฑ: ์ค๋ ์ท์ ๋จ์ํ EBS์ ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด๋ผ ์ฐ๊ธฐ ์์ ์ค ์ค๋ ์ท์ ์์ฑํ๋ฉด ๋ฐ์ดํฐ๊ฐ ๋๋ฝ๋ ์ ์์
- ํธ๋ํฝ์ด ๋์ด๋ฌ์ ๋ ํธ๋ํฝ์ ๋ถ์ฐํด ์ค ๋ก๋๋ฐธ๋ฐ์(ALB)๊ฐ ์๊ณ , DB(PostgreSQL)๊ฐ ์ธ์คํด์ค ๋ด๋ถ ์ปจํ ์ด๋๋ก ํจ๊ป ๋ ์์ด ์๋ฒ ๊ฐ ๋ฐ์ดํฐ ๊ณต์ ๊ฐ ๋ถ๊ฐ๋ฅํ๋ฏ๋ก Scale-out์ด ๋ถ๊ฐ๋ฅํ๋ฉฐ ์ธ์คํด์ค ์ฒด๊ธ์ ์ฌ๋ฆฌ๋ Scale-up๋ง ๊ฐ๋ฅํ๋ค.