From 7fb3cfabd29c70b49ca4a26da5c72a21fbac18ab Mon Sep 17 00:00:00 2001 From: ysb21 Date: Thu, 27 Aug 2026 21:41:38 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20AWS=20=EB=B0=B0=ED=8F=AC=20=EC=82=B0?= =?UTF-8?q?=EC=B6=9C=EB=AC=BC=20=EC=99=84=EB=B9=84=20+=20Docker=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EC2에 올려 실행만 하면 기동되도록 배포 산출물을 갖춘다. - backend/Dockerfile(+.dockerignore): FastAPI 이미지(python:3.11-slim). - docker-compose.yml: ollama + model-loader(ollama create 자동) + backend 3서비스. - backend/finetune/Modelfile: 로컬 Ollama에서 복구한 raw passthrough 정의(모델 재현용). - deploy/nginx-wtgmate.conf: 정적 프론트 + /api 프록시(certbot HTTPS용). - .github/workflows/docker-build.yml: 백엔드 이미지 실제 빌드 + compose config 검증(CI 근거). - .gitattributes: Linux 실행 파일 LF 고정(CRLF로 sh/Dockerfile 깨짐 방지). - DEPLOY.md: 산출물 기반 구체 런북 + 실제 데모는 Cloudflare Tunnel로 운영함을 명시. Co-Authored-By: Claude Opus 4.8 --- .gitattributes | 16 ++++++++ .github/workflows/docker-build.yml | 38 ++++++++++++++++++ DEPLOY.md | 62 +++++++++++++++++++++++++----- DEVELOPMENT_JOURNEY.md | 1 + backend/.dockerignore | 14 +++++++ backend/Dockerfile | 22 +++++++++++ backend/finetune/Modelfile | 17 ++++++++ deploy/nginx-wtgmate.conf | 35 +++++++++++++++++ docker-compose.yml | 59 ++++++++++++++++++++++++++++ 9 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/docker-build.yml create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100644 backend/finetune/Modelfile create mode 100644 deploy/nginx-wtgmate.conf create mode 100644 docker-compose.yml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b1b1668 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,16 @@ +# Linux(EC2/CI/컨테이너)에서 실행되는 파일은 LF로 고정한다. +# (Windows에서 편집해도 CRLF가 섞여 sh 스크립트/Dockerfile이 깨지지 않도록) +Dockerfile text eol=lf +*.sh text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.conf text eol=lf +Modelfile text eol=lf +.dockerignore text eol=lf + +# 바이너리(정규화 제외) +*.gguf binary +*.safetensors binary +*.bin binary +*.png binary +*.jpg binary diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..1b009a3 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,38 @@ +# 백엔드 Docker 이미지가 실제로 빌드되는지 CI에서 검증한다. +# (배포 산출물이 항상 빌드 가능한 상태임을 보장 — 포트폴리오용 "Docker build passing" 근거) +name: docker-build + +on: + push: + branches: [main] + paths: + - 'backend/**' + - 'docker-compose.yml' + - '.github/workflows/docker-build.yml' + pull_request: + paths: + - 'backend/**' + - 'docker-compose.yml' + - '.github/workflows/docker-build.yml' + workflow_dispatch: + +jobs: + build-backend-image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build backend image + uses: docker/build-push-action@v6 + with: + context: ./backend + push: false + tags: wtgmate-backend:ci + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Validate docker-compose.yml + run: docker compose -f docker-compose.yml config >/dev/null && echo "compose config OK" diff --git a/DEPLOY.md b/DEPLOY.md index 7375042..7677571 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -41,7 +41,11 @@ ALB(월 ~$16)와 API Gateway(요청 29초 하드 리밋)를 모두 피하기 위 | Ollama 호스트 | ✅ | `OLLAMA_HOST`/`OLLAMA_MODEL` env | `backend/main.py` | | 의존성 고정 | ✅ | `backend/requirements.txt` 생성(fastapi·uvicorn[standard]·httpx·python-dotenv·pydantic·ortools) | `backend/requirements.txt` | | env 템플릿 | ✅ | `backend/.env.example`, `frontend/.env.example` 추가 | - | -| 컨테이너화 | ⬜ | (선택) `Dockerfile` + `docker-compose.yml`. bare-metal(venv+systemd)로도 가능 | - | +| 컨테이너화 | ✅ | `backend/Dockerfile`(+`.dockerignore`), 루트 `docker-compose.yml`(ollama + model-loader + backend) | - | +| 모델 등록 | ✅ | `backend/finetune/Modelfile`(raw passthrough) 커밋 → `ollama create`로 재현 | - | +| nginx | ✅ | `deploy/nginx-wtgmate.conf`(정적 프론트 + /api 프록시, certbot용) | - | + +**→ 배포 산출물이 모두 준비됨. EC2에 올려 아래 "배포 순서 요약"만 실행하면 기동된다.** **배포 시 주입할 env 요약** - 백엔드: `KAKAO_REST_API_KEY`, `TMAP_APP_KEY`, `ODSAY_API_KEY`, `ALLOWED_ORIGINS`(배포 도메인), `OLLAMA_HOST`/`OLLAMA_MODEL` @@ -75,12 +79,52 @@ ALB(월 ~$16)와 API Gateway(요청 29초 하드 리밋)를 모두 피하기 위 --- -## 배포 순서 요약 +## 배포 순서 요약 (산출물 준비 완료 → EC2에서 실행만) + +**로컬(완료됨):** Phase 1 코드 정리 + Dockerfile/compose/nginx/Modelfile 산출물. GGUF는 Colab 노트북으로 생성. + +**EC2(Ubuntu, t3.medium+)에서:** +```bash +# 0) 도구 +sudo apt update && sudo apt install -y docker.io docker-compose-plugin nginx certbot python3-certbot-nginx +sudo usermod -aG docker $USER # 재로그인 + +# 1) 코드 + 모델 +git clone https://github.com/ysb2152/WTGMate.git && cd WTGMate +mkdir -p model +aws s3 cp s3://<버킷>/wtgmate-parser.gguf model/wtgmate-parser.gguf # GGUF 전달(S3) +cp backend/finetune/Modelfile model/Modelfile + +# 2) 키 주입(.env 또는 SSM). ALLOWED_ORIGINS는 배포 도메인. +cat > .env < +PARAMETER stop <|endoftext|> +PARAMETER temperature 0.1 diff --git a/deploy/nginx-wtgmate.conf b/deploy/nginx-wtgmate.conf new file mode 100644 index 0000000..fabbab2 --- /dev/null +++ b/deploy/nginx-wtgmate.conf @@ -0,0 +1,35 @@ +# WTGMate nginx 설정 (단일 EC2: 정적 프론트 서빙 + /api 리버스 프록시 + HTTPS). +# +# 배포: +# 1) 프론트 빌드: cd frontend && VITE_API_BASE_URL="" npm run build (동일 오리진이라 상대 /api) +# => frontend/dist 를 /var/www/wtgmate 로 복사. +# 2) 이 파일을 /etc/nginx/sites-available/wtgmate 로 복사하고 YOUR_DOMAIN 치환, sites-enabled 링크. +# 3) sudo nginx -t && sudo systemctl reload nginx +# 4) sudo certbot --nginx -d YOUR_DOMAIN (443 서버블록 + HTTP→HTTPS 리다이렉트 자동 삽입) +# +# 프론트와 /api가 같은 도메인이라 CORS도 필요 없다(백엔드 ALLOWED_ORIGINS는 그대로 둬도 무방). + +server { + listen 80; + server_name YOUR_DOMAIN; # 예: wtgmate.example.com + + # vite build 산출물(frontend/dist) 배포 위치. + root /var/www/wtgmate; + index index.html; + + # SPA: 정적 파일 없으면 index.html로 폴백. + location / { + try_files $uri $uri/ /index.html; + } + + # 백엔드(FastAPI, docker-compose가 8000 노출) 리버스 프록시. + location /api/ { + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + # LLM 파싱/최적화 대기 여유(브루트포스·OR-Tools 포함). + proxy_read_timeout 120s; + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8fd4370 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +# WTGMate 백엔드 + Ollama 오케스트레이션 (단일 EC2 등에서 `docker compose up -d --build`). +# +# 준비물: +# 1) 파인튜닝 GGUF와 Modelfile을 ./model/ 에 둔다: +# ./model/wtgmate-parser.gguf (git 제외 ~1.9GB, S3에서 내려받거나 Colab로 생성) +# ./model/Modelfile (backend/finetune/Modelfile 복사) +# 2) 루트 .env 또는 셸 환경변수로 키 주입: +# KAKAO_REST_API_KEY / TMAP_APP_KEY / ODSAY_API_KEY / ALLOWED_ORIGINS +# 3) docker compose up -d --build +# 4) 헬스체크: curl http://localhost:8000/api/health +# +# 정적 프론트 서빙 + HTTPS는 호스트 nginx + certbot으로 처리한다(deploy/nginx-wtgmate.conf). + +services: + # Ollama 서버(로컬 파인튜닝 모델 구동). CPU만으로도 3B 동작. + ollama: + image: ollama/ollama:latest + restart: unless-stopped + volumes: + - ollama_data:/root/.ollama + expose: + - "11434" + + # ollama가 뜨면 모델을 한 번 등록하고 종료하는 1회성 서비스. + model-loader: + image: ollama/ollama:latest + depends_on: + - ollama + environment: + - OLLAMA_HOST=ollama:11434 + volumes: + - ./model:/model:ro + entrypoint: + - /bin/sh + - -c + - > + until ollama list >/dev/null 2>&1; do echo 'waiting for ollama...'; sleep 2; done; + ollama create wtgmate-parser -f /model/Modelfile && + echo 'wtgmate-parser 등록 완료' + restart: "no" + + # FastAPI 백엔드. + backend: + build: ./backend + depends_on: + - ollama + environment: + - OLLAMA_HOST=http://ollama:11434 + - OLLAMA_MODEL=wtgmate-parser + - KAKAO_REST_API_KEY=${KAKAO_REST_API_KEY:-} + - TMAP_APP_KEY=${TMAP_APP_KEY:-} + - ODSAY_API_KEY=${ODSAY_API_KEY:-} + - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-*} + ports: + - "8000:8000" + restart: unless-stopped + +volumes: + ollama_data: