Docker - 100-hours-a-week/21-iceT-wiki GitHub Wiki
4๋จ๊ณ: Docker ์ปจํ ์ด๋ํ ๋ฐฐํฌ
์ปจํ ์ด๋ ๊ตฌ์กฐ ๋ค์ด์ด๊ทธ๋จ
Docker ๋์
ํ ์๋น์ค๋ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
๊ฐ ํ๊ฒฝ์ ๋์ผํ ๊ตฌ์ฑ ์์๋ก ์ด๋ฃจ์ด์ ธ ์์ผ๋ฉฐ, Dev ํ๊ฒฝ์ ํ
์คํธ ๋ฐ ๊ฒ์ฆ์ฉ, Prod ํ๊ฒฝ์ ์ค์ ์๋น์ค ์ฌ์ฉ์ ๋์์
๋๋ค.
Docker ๋์ ๊ฒฐ์ ๋ฐ ๋ฒ์
1. ๋์ ๋ฐฐ๊ฒฝ (์ Docker๋ฅผ ๋์ ํ๋๊ฐ?)
์ฐ๋ฆฌ ์๋น์ค๋ ๋จ์ผ EC2 ์ธ์คํด์ค์์ React(ํ๋ก ํธ์๋), Spring Boot(๋ฐฑ์๋), MySQL(DB)๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ, ๊นํ๋ธ์์ git clone์ผ๋ก EC2๋ก ๋ฐ์์ค๋ ๋ฐฉ์์ผ๋ก ์๋ ๋ฐฐํฌ๋ฅผ ์งํํด ์์ต๋๋ค. ์ดํ GitHub Actions ๊ธฐ๋ฐ์ CI/CD ํ์ดํ๋ผ์ธ์ ๋์ ํ๋ฉฐ ์๋ํ ์์ค์ ๋์์ง๋ง, ์ฌ์ ํ ๊ฐ ์๋น์ค๊ฐ ๋์ผํ EC2 OS ๋ด์์ ์คํ๋จ์ ๋ฐ๋ผ ํ๊ฒฝ ์ถฉ๋, ํ์ฅ์ฑ ๋ถ์กฑ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
๋ฐ๋ผ์, Docker๋ฅผ ๋์ ํ๊ธฐ๋ก ๊ฒฐ์ ํ ์ด์ ๋ ์๋์ ๊ฐ์ต๋๋ค:
2. ๋์ ๋ชฉ์
์ด์ | ๋ถ๊ฐ ์ค๋ช |
---|---|
ํ๊ฒฝ ๋ถ์ผ์น ํด๊ฒฐ | - ๊ฐ๋ฐ์๋ง๋ค ์ธ์ด ๋ฒ์ ๋๋ OS ํ๊ฒฝ์ด ๋ฌ๋ผ ์๊ธฐ๋ ๋ฌธ์ , ์์กด์ฑ ์ถฉ๋ ๋ฑ ๋ฐ์ - Docker ์ด๋ฏธ์ง๋ก ํต์ผ๋ ์คํ ํ๊ฒฝ์ ์ ๊ณตํ์ฌ ๋ก์ปฌ์์ ๋๋๋ฐ, ์๋ฒ์์ ์ ๋๋ ๋ฌธ์ ํด์ |
์๋น์ค ๊ฐ ๊ฐ์ญ ๋ฐฉ์ง | - ๋ชจ๋ ๊ตฌ์ฑ์์๊ฐ ๋์ผํ OS ๋ด์์ ์คํ๋ ๊ฒฝ์ฐ ์์กด์ฑ ์ถฉ๋, ํฌํธ ์ถฉ๋ ๋ฐ์ ์ํ - ์ปจํ ์ด๋๋ก ๊ฒฉ๋ฆฌํ์ฌ ์ถฉ๋ ์์ด ๋ ๋ฆฝ์ ์คํ ๊ฐ๋ฅ |
ํ์ฅ์ฑ ๋ถ์กฑ ํด์ | - ํ์ฌ๋ ๋จ์ผ ์ธ์คํด์ค์ ์๋น์ค๊ฐ ์ง์ ์ฌ๋ผ๊ฐ ์์ด ์ํ ํ์ฅ ์ด๋ ค์ - Docker ๊ธฐ๋ฐ์ผ๋ก ์๋น์ค ๋จ์ ์ปจํ ์ด๋ํํ์ฌ ํฅํ ๋ถ์ฐ ์ด์ ๊ตฌ์กฐ๋ก ์ ํ์ ์ํ ๋ฐํ ๋ง๋ จ |
๋ณต๊ตฌ ๋ฐ ์ด์ ๋น์ฉ ์ ๊ฐ | - Docker ์ด๋ฏธ์ง ์ฌ๋ฐฐํฌ๋ง์ผ๋ก ์ธํ๋ผ ๋ณ๊ฒฝ ์์๋ ๋์ผ ์๋น์ค ์ฌํ ๊ฐ๋ฅ |
๋ฒ์ ๊ด๋ฆฌ ์ฉ์ด | - Dockerfile ๊ธฐ๋ฐ์ผ๋ก ๊ฐ ๋ฒ์ ์ ๋ช
ํํ๊ฒ ํ๊น
๊ฐ๋ฅ - ํ๊น
์ผ๋ก ์ด์ ์ด๋ฏธ์ง๋ก ์ฝ๊ฒ ๋กค๋ฐฑ ๊ฐ๋ฅ (release-v2.0.0 -> release-v1.0.0 ) |
Blue-Green ๋ฐฐํฌ ๊ตฌ์กฐ ์ ํ ์ฉ์ด | - ์ปจํ ์ด๋ ๋จ์๋ก ํฌํธ๋ฅผ ๋ฐ๊ฟ๊ฐ๋ฉฐ ํธ๋ํฝ ์ ํ ๊ฐ๋ฅ - ์คํ ์ค์ธ ์ธ์คํด์ค๋ ๊ทธ๋๋ก ๋๊ณ ์ปจํ ์ด๋๋ง ๊ต์ฒดํ์ฌ ์๋น์ค ์ ํ ๊ฐ์ํ |
Terraform ์ฐ๊ณ ๊ฐ๋ฅ์ฑ ํ๋ณด | - ์ธํ๋ผ๋ Terraform์ผ๋ก ๊ณ ์ ํ ์ฑ, Docker๋ก ์๋น์ค๋ง ์ ์ฐํ๊ฒ ์ ํ ๊ฐ๋ฅ - IaC(Terraform) + ์ปจํ ์ด๋ ๋ฐฐํฌ ์ ๋ต์ ํตํฉ ๊ด๋ฆฌ ๊ธฐ๋ฐ ๋ง๋ จ |
3. ๋์ ๋ฒ์
Docker๋ฅผ ๋์ ํ์ฌ ์๋น์ค ๊ตฌ์ฑ ์์๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ถ๋ฆฌ๋ ์ปจํ ์ด๋๋ก ๊ด๋ฆฌํ๊ณ ์ ํฉ๋๋ค:
-
React ํ๋ก ํธ์๋ โ Nginx ๊ธฐ๋ฐ ์ ์ ํ์ผ ์๋น ์ปจํ ์ด๋
-
Spring Boot ๋ฐฑ์๋ โ JDK ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋
-
FastAPI(AI) โ Python ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋
-
MySQL ๋ฐ์ดํฐ๋ฒ ์ด์ค โ MYSQL ์ปจํ ์ด๋๋ก ์ฌ์ฉ
4. ๋์ ์ ์์๋๋ ๋ณํ
-
๋ฐฐํฌ ํจ์จ์ฑ ํฅ์: ์ด๋ฏธ์ง ๋น๋ ๋ฐ ํธ์ ํ ๊ฐ๋จํ pull & run์ผ๋ก ๋ฐฐํฌ ์๋ํ
-
ํ ์คํธ ํ๊ฒฝ ํต์ผ: ๋์ผํ Docker ์ด๋ฏธ์ง๋ก QA ํ ์คํธ ๋ฐ ๋ก์ปฌ ํ ์คํธ ๊ฐ๋ฅ
-
๋ฒ์ ๊ด๋ฆฌ ๋ช ํํ: ๊ฐ ์๋น์ค๋ณ Dockerfile ๋ฐ ์ด๋ฏธ์ง ํ๊ทธ๋ฅผ ํตํด ๋ฆด๋ฆฌ์ฆ ๊ด๋ฆฌ ์ฉ์ด
-
์ฅ๊ธฐ์ ์ผ๋ก ์ค์ผ์คํธ๋ ์ด์ ๊ฐ๋ฅ์ฑ ํ๋ณด: Docker Compose ๋ฐ ์ถํ Kubernetes์์ ์ฐ๊ณ ์ ์ฉ์ด
์ปจํ ์ด๋ ๋ฐฐํฌ ์ ๋ต
1. Docker ๋์ ์ ๋ต ์ค๋ช ์
๋น๋ ๋ฐฉ์
์๋น์ค๋ณ๋ก ๋ ๋ฆฝ๋ Dockerfile์ ์์ฑํ์ฌ GitHub Actions์์ ์ด๋ฏธ์ง ๋น๋ ์๋ํ
-
React: ์ ์ ํ์ผ ๋น๋ ํ Nginx์ ํฌํจ
-
Spring Boot: JAR ํ์ผ์ ํฌํจํ OpenJDK ๊ธฐ๋ฐ ์ด๋ฏธ์ง
-
FastAPI: Python ํ๊ฒฝ ๊ธฐ๋ฐ์ผ๋ก
requirements.txt
์ค์น ํ Uvicorn ์คํ ์ด๋ฏธ์ง
์ด๋ฏธ์ง ํ๊น ์ ๋ต
latest
- ๊ฐ๋ฐ ํ๊ฒฝ์์ ์ ์ฉ
- ํญ์ ์ต์ ๋ฒ์ ์ ๊ฐ๋ฆฌํด
- ๊ฐ์ฅ ์ต๊ทผ์ ๋น๋๋ ์ด๋ฏธ์ง
v1.0.0
- ๋ช ์์ ๋ฒ์ ๊ด๋ฆฌ
- ํ๋ก๋์ ํ๊ฒฝ์์ ์ ์ฉ
์ต์ข
์ ํ: ์ด์ ํ๊ฒฝ์ v1.0.0
ํ๊ทธ ๊ธฐ๋ฐ ๋ฐฐํฌ, ๊ฐ๋ฐ ํ๊ฒฝ์ latest
ํ์ฉ
Docker ๋์ ํ CI/CD ์์
-
GitHub Actions์์ ์ฝ๋ ํธ์ ๊ฐ์ง
- ๋ธ๋์น ํธ์ ๋๋ PR ๋ณํฉ ๋ฐ์ ์ ์ํฌํ๋ก์ฐ ํธ๋ฆฌ๊ฑฐ
-
์ฝ๋ ๋น๋ (Dockerfile)
- Spring Boot๋
./gradlew build
๋ฅผ ์ํํ์ฌ.jar
์์ฑ - React๋
npm run build
๋ฅผ ์ํํ์ฌbuild
ํด๋ ์์ฑ - FAST API๋
requirements.txt
๋ฅผ ์ค์น
- Spring Boot๋
-
Docker ์ด๋ฏธ์ง ๋น๋
-
docker build -t my-app:release-v1.0.0 .
-> ์๋น์ค๋ณ๋ก ์ด๋ฏธ์ง ๊ฐ๊ฐ ๋น๋ (ํ๋ก ํธ, ๋ฐฑ์๋๋ฑ)
-
-
์ด๋ฏธ์ง ๋ ์ง์คํธ๋ฆฌ์ ํธ์(ECR)
-
docker push my-app:release-v1.0.0
โ DockerHub์ ํธ์
-
-
AWS CodeDeploy๋ฅผ ํตํด EC2์ ๋ฐฐํฌ
- CodeDeploy๊ฐ ์ด๋ฏธ์ง๊ฐ ํธ์๋ ๊ฒ์ ๊ฐ์งํ๊ณ EC2 ์ธ์คํด์ค์ ๋ฐฐํฌ ์ํ
- AppSpec ํ์ผ(appspec.yml)์ ํตํด ์ปจํ ์ด๋ pull, stop, run ๋จ๊ณ ์๋ํ
์ด๋ฏธ์ง ์ ์ฅ์
- ECR ์ฌ์ฉ
์ปจํ ์ด๋ ๊ฐ ์ฐ๋ ๋ฐฉ์
- ๋ชจ๋ ์ปจํ
์ด๋๋ ๋์ผํ ๋ธ๋ฆฟ์ง ๋คํธ์ํฌ (
app-network
) ์์ ์กด์ฌ. - ์ปจํ ์ด๋ ๊ฐ ํต์ ์ ์๋น์ค ์ด๋ฆ(DNS ์ด๋ฆ์ฒ๋ผ ์๋)์ผ๋ก ๊ฐ๋ฅํ๋ฉฐ, ๋ณ๋ IP ์ค์ ์์ด ๋์ปค ๋ด๋ถ ๋คํธ์ํฌ๋ก ์ ๊ทผ.
- ๋ด๋ถ ํฌํธ๋ ์ปจํ ์ด๋ ๊ฐ ํต์ ์ฉ์ด๋ฉฐ, ์ธ๋ถ ํฌํธ๋ ๋ณด์ ๊ทธ๋ฃน ๋๋ ํ๋ก์(Nginx ๋ฑ)๋ก ์ ํ.
- ์๋ฅผ ๋ค์ด, React ์ฑ์์
http://spring:8080
์ผ๋ก Spring Boot์ ์ ๊ทผ.
docker-compose.yml
๋ด ์ปจํ
์ด๋ ์ฐ๋
์์: version: '3.8'
services:
react:
build:
context: ./frontend
dockerfile: Dockerfile
networks:
- app-network
ports:
- "8080:8080"
environment:
- NODE_ENV=development
depends_on:
- spring
- fastapi
spring:
build:
context: ./backend
dockerfile: Dockerfile
networks:
- app-network
ports:
- "8001:8001"
environment:
- SPRING_PROFILES_ACTIVE=local
- DB_HOST=db
- DB_PORT=3306
- DB_USERNAME=root
- DB_PASSWORD=secret
depends_on:
- db
fastapi:
build:
context: ./ai
dockerfile: Dockerfile
networks:
- app-network
ports:
- "8000:8000"
networks:
app-network:
ํธ์คํธ ๋จธ์ ์์ ํ ๋น ์ ๋ต
ํ ์ํฉ
ํ์ฌ EC2 ์ธ์คํด์ค๋ t3.medium (2 vCPU, 4GB RAM) ์คํ์ผ๋ก, React + Spring Boot์ ๋จ์ผ ๋จธ์ ์์ React์ Spring Boot๋ฅผ ์ปจํ ์ด๋๋ก ์ด์ํ ์์ . Docker๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ปจํ ์ด๋์ ๋ฆฌ์์ค ์ ํ์ ๋์ง ์๊ธฐ ๋๋ฌธ์, ๊ฐ ์ปจํ ์ด๋๊ฐ ๊ณผ๋ํ๊ฒ ์์์ ์ ์ ํ์ง ์๋๋ก ๋ช ์์ ์ผ๋ก CPU/๋ฉ๋ชจ๋ฆฌ ์ ํ์ ์ค์ .
ํธ์คํธ ๋จธ์ ์์ ํ ๋นํ
์๋น์ค | ํน์ฑ | ์์ ๋ฆฌ์์ค | ์ ํ ์์ |
---|---|---|---|
React (Nginx) | ์ ์ ํ์ผ ์๋น, ๊ฒฝ๋ | CPU: ๋งค์ฐ ๋ฎ์RAM: ๋ฎ์ | cpus: 0.4 , memory: 256M |
Spring Boot | ๋ฉ์ธ API ์๋ฒ | CPU: ์ค๊ฐ~๋์RAM: ๋์ | cpus: 1.6 , memory: 3072M |
โป ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ํฉ์ณ๋ 2 vCPU, 4GB ์ด๋ด๋ก ๊ตฌ์ฑํด์ผ ํจ
์ดํฉ: 2.0 vCPU, 3328MB RAM ์ฌ์ฉ (์ฌ์ ๋ถ 768MB ํ๋ณด)
๊ฐ๋ฐ ํ๊ฒฝ์์์ Docker Compose ๊ตฌ์ฑ
version: "3.8"
services:
frontend:
build: ./frontend
ports:
- "8080:80"
volumes:
- ./frontend:/app
environment:
- NODE_ENV=development
backend:
build: ./backend
ports:
- "8001:8001"
environment:
- SPRING_PROFILES_ACTIVE=dev
ai:
build: ./ai
ports:
- "8000:8000"
๐ฏ ์ด์ ํ๊ฒฝ์์๋
-
GitHub Actions โ Docker ์ด๋ฏธ์ง ๋น๋ โ ECR ํธ์
-
EC2์์
docker pull
+docker run
์ผ๋ก ์ด์ -
๋ฆฌ์์ค ์ ํ (
--memory
,--cpus
) ์ค์ ํ์ -
.env
๋์ ์ด์ ๋ณ์๋EC2 ํ๊ฒฝ ๋ณ์
,AWS Secrets Manager
,.tfvars
๋ฑ์ผ๋ก ๋ถ๋ฆฌ
๊ณตํต ๊ตฌ์ฑ ์์ (Dev / Prod)
๊ตฌ์ฑ ์์ | ์ค๋ช |
---|---|
VPC/Subnet | ํผ๋ธ๋ฆญ ๋ฐ ํ๋ผ์ด๋น ์๋ธ๋ท์ผ๋ก ๋ถ๋ฆฌ ๊ตฌ์ฑ (AZ ๋ถ์ฐ) |
EC2 (Auto Scaling Group) | ํ๋ก ํธ์๋, ๋ฐฑ์๋, FastAPI ์ฑ์ด Docker ์ปจํ ์ด๋ ํํ๋ก ๋ฐฐํฌ |
MySQL (AWS RDS) | ์ปจํ ์ด๋ํ ์์ด AWS์ RDS๋ฅผ ์ฌ์ฉํด ํ๋ผ์ด๋น ์๋ธ๋ท์์ ์ ๊ทผ |
Docker + GitHub Actions | ๋น๋ ๋ฐ ECR ์ด๋ฏธ์ง ํธ์๋ GitHub Actions๋ฅผ ํตํด ์ํ |
AWS CodeDeploy | ๋ฐฐํฌ๋ CodeDeploy๊ฐ ๋ด๋นํ๋ฉฐ, EC2 ์ธ์คํด์ค์์ ์ด๋ฏธ์ง pull & ์คํ |
OpenVPN | ์ธ๋ถ ์ ๊ทผ ์ ๋ณด์ ์ฐ๊ฒฐ ์ฉ๋ |
ALB + Route53 | ALB๋ฅผ ํตํด ํธ๋ํฝ ๋ถ์ฐ, Route53์ผ๋ก ์ฌ์ฉ์ ๋๋ฉ์ธ ์ฐ๊ฒฐ ์ฒ๋ฆฌ |
ํ๊ฒฝ๋ณ ์ฐจ์ด
ํ๊ฒฝ ๊ตฌ๋ถ | ๋ชฉ์ | ์ ๊ทผ ๋์ | ํน์ง |
---|---|---|---|
Dev | ๊ฐ๋ฐ ๋ฐ ํ ์คํธ | ๊ฐ๋ฐ์ | ์ค์ ์ด์๊ณผ ๋์ผํ ๊ตฌ์กฐ์์ ํ ์คํธ ์ํ |
Prod | ์ค ์๋น์ค ์ด์ | ์ต์ข ์ฌ์ฉ์ | ์ค ํธ๋ํฝ ๋์, ๊ณ ๊ฐ์ฉ์ฑ ๊ตฌ์ฑ ๋ฐ ๋ณด์ ๊ฐํ |
์๋น์ค ๋ด ์ปจํ ์ด๋ ์ญํ
์ปจํ ์ด๋ | ๊ธฐ๋ฅ |
---|---|
React (Nginx) | ์ ์ ํ์ผ ์๋น |
Spring Boot | ์ฌ์ฉ์ ์ธ์ฆ, ๋น์ฆ๋์ค ๋ก์ง |
FastAPI | AI ๋ชจ๋ธ ์ถ๋ก ๋ฐ API ์ฒ๋ฆฌ |
์ธ๋ถ ์ฐ๋
- Docker ์ด๋ฏธ์ง ์ ์ฅ์: AWS ECR
- CI: GitHub Actions
- CD: AWS CodeDeploy
- AI ์๋น์ค: FastAPI๋ ํผ๋ธ๋ฆญ ์ธ์คํด์ค์์ ์ง์ ํธ์คํ
์ด ๊ตฌ์กฐ๋ ํ๊ฒฝ ์ผ๊ด์ฑ, ํ์ฅ์ฑ, ๋ณด์์ฑ์ ๊ณ ๋ คํ์ฌ ์ค๊ณ๋์์ผ๋ฉฐ, CI/CD ์๋ํ์ ํด๋ผ์ฐ๋ ์์ ํ์ฉ์ ํตํด ์๋น์ค ๋ฐฐํฌ ์๋์ ์์ ์ฑ์ ํ๋ณดํ์ต๋๋ค.
2. Docker ๊ธฐ๋ฐ ์๋น์ค ๊ธฐ์ ๋ช ์ธ
1. React (Static SPA)
- ๋ฒ ์ด์ค ์ด๋ฏธ์ง:
node:18-alpine
,nginx:stable-alpine
- ์ปจํ ์ด๋ ์: 8๊ฐ (Prod์ฉ, Dev์ฉ 4๊ฐ, AZ ๋น 2๊ฐ)
- ์ญํ : React ์ฑ์ ๋น๋ํ์ฌ Nginx๋ก ์ ์ ํ์ผ ์๋น
- ํฌํธ: Host 3000 โ Container 80
- ํ๊ฒฝ๋ณ์:
REACT_APP_API_URL=http://localhost:8080
๋ฑ
Dockerfile (frontend/Dockerfile)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:stable-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
2. Spring Boot
- ๋ฒ ์ด์ค ์ด๋ฏธ์ง:
openjdk:17
,gradle:8.5-jdk17-alpine
- ์ปจํ ์ด๋ ์: 8๊ฐ (Prod์ฉ, Dev์ฉ 4๊ฐ, AZ ๋น 2๊ฐ)
- ์ญํ : ์ฌ์ฉ์ ์ธ์ฆ, ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ฑ ๋น์ฆ๋์ค ๋ก์ง ์ํ
- ํฌํธ: Host 8080 โ Container 8080
- ํ๊ฒฝ๋ณ์ ์์:
SPRING_PROFILES_ACTIVE=prod
,DB_HOST=root
๋ฑ
Dockerfile (backend/Dockerfile)
FROM gradle:8.5-jdk17-alpine AS build
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
RUN gradle build --no-daemon || return 0
COPY . .
RUN gradle clean build --no-daemon
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
3. FastAPI (AI ์๋น์ค)
- ๋ฒ ์ด์ค ์ด๋ฏธ์ง:
python:3.13-slim
- ์ปจํ ์ด๋ ์: 4๊ฐ (GCP Compute Engine๋น 2๊ฐ์ฉ)
- ์ญํ : ๋ชจ๋ธ ์ถ๋ก ๋ฐ AI API ์ ๊ณต
- ํฌํธ: Host 8000 โ Container 8000
- ํ๊ฒฝ๋ณ์ ์์:
MODEL_PATH=/app/model
,ENV=prod
๋ฑ
Dockerfile (ai/Dockerfile)
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]