webhook 자동 배포 설정
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
Makefile 설정 사용 예시
|
||||||
|
- 배포: make deploy
|
||||||
|
- 백엔드 로그: make logs S=backend
|
||||||
|
- 전체 로그: make logs
|
||||||
|
- 상태: make status
|
||||||
|
|||||||
@@ -16,3 +16,7 @@ ENV PYTHONUNBUFFERED=1
|
|||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|
||||||
|
ARG APP_VERSION=dev
|
||||||
|
ENV APP_VERSION=$APP_VERSION
|
||||||
|
LABEL org.opencontainers.image.version=$APP_VERSION
|
||||||
|
|||||||
@@ -342,3 +342,7 @@ def api_recommend_batch_save(body: BatchSave):
|
|||||||
|
|
||||||
return {"saved": True, "created_ids": created, "deduped_ids": deduped}
|
return {"saved": True, "created_ids": created, "deduped_ids": deduped}
|
||||||
|
|
||||||
|
@app.get("/api/version")
|
||||||
|
def version():
|
||||||
|
import os
|
||||||
|
return {"version": os.getenv("APP_VERSION", "dev")}
|
||||||
|
|||||||
5
deployer/Dockerfile
Normal file
5
deployer/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
WORKDIR /app
|
||||||
|
RUN pip install --no-cache-dir fastapi uvicorn
|
||||||
|
COPY app.py /app/app.py
|
||||||
|
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9000"]
|
||||||
29
deployer/app.py
Normal file
29
deployer/app.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import os, hmac, hashlib, subprocess
|
||||||
|
from fastapi import FastAPI, Request, HTTPException
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
SECRET = os.getenv("WEBHOOK_SECRET", "")
|
||||||
|
|
||||||
|
def verify(sig: str, body: bytes) -> bool:
|
||||||
|
# Gitea: X-Gitea-Signature = sha256=...
|
||||||
|
if not SECRET:
|
||||||
|
return False
|
||||||
|
mac = hmac.new(SECRET.encode(), msg=body, digestmod=hashlib.sha256).hexdigest()
|
||||||
|
expected = f"sha256={mac}"
|
||||||
|
return hmac.compare_digest(expected, sig)
|
||||||
|
|
||||||
|
@app.post("/webhook")
|
||||||
|
async def webhook(req: Request):
|
||||||
|
body = await req.body()
|
||||||
|
sig = req.headers.get("X-Gitea-Signature", "")
|
||||||
|
|
||||||
|
if not verify(sig, body):
|
||||||
|
raise HTTPException(401, "bad signature")
|
||||||
|
|
||||||
|
# 배포 스크립트 실행
|
||||||
|
# (컨테이너에 /scripts 가 마운트되어 있어야 함)
|
||||||
|
p = subprocess.run(["/scripts/deploy.sh"], capture_output=True, text=True)
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise HTTPException(500, f"deploy failed:\n{p.stdout}\n{p.stderr}")
|
||||||
|
|
||||||
|
return {"ok": True, "out": p.stdout}
|
||||||
@@ -2,7 +2,10 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
build: ./backend
|
build:
|
||||||
|
context: ./backend
|
||||||
|
args:
|
||||||
|
APP_VERSION: ${APP_VERSION:-dev}
|
||||||
container_name: lotto-backend
|
container_name: lotto-backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
@@ -45,3 +48,16 @@ services:
|
|||||||
- /volume1/docker/webpage/travel-thumbs:/data/thumbs:ro
|
- /volume1/docker/webpage/travel-thumbs:/data/thumbs:ro
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
deployer:
|
||||||
|
build: ./deployer
|
||||||
|
container_name: webpage-deployer
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "19010:9000" # 외부 노출 필요 없으면 내부만 (리버스프록시로 /webhook만 노출 추천)
|
||||||
|
environment:
|
||||||
|
- WEBHOOK_SECRET=여기에_긴_랜덤값
|
||||||
|
volumes:
|
||||||
|
- /volume1/workspace/web-page-backend:/repo:rw
|
||||||
|
- /volume1/docker/webpage:/runtime:rw
|
||||||
|
- /volume1/docker/webpage/scripts:/scripts:ro
|
||||||
|
|||||||
24
script/deploy.sh
Normal file
24
script/deploy.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SRC="/repo"
|
||||||
|
DST="/runtime"
|
||||||
|
|
||||||
|
cd "$SRC"
|
||||||
|
git fetch --all --prune
|
||||||
|
git pull --ff-only
|
||||||
|
|
||||||
|
# 릴리즈 백업(롤백용): 아래 5번과 연결
|
||||||
|
TAG="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$DST/.releases/$TAG"
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude ".releases" \
|
||||||
|
"$DST/" "$DST/.releases/$TAG/"
|
||||||
|
|
||||||
|
# 소스 → 운영 반영 (네가 이미 만든 deploy-nas.sh가 있으면 그걸 호출해도 됨)
|
||||||
|
# 예: repo/scripts/deploy-nas.sh가 운영으로 복사/동기화하는 로직이라면:
|
||||||
|
bash "$SRC/scripts/deploy-nas.sh"
|
||||||
|
|
||||||
|
cd "$DST"
|
||||||
|
docker compose up -d --build backend travel-proxy frontend
|
||||||
|
echo "DEPLOY_OK $TAG"
|
||||||
@@ -20,3 +20,7 @@ EXPOSE 8000
|
|||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|
||||||
|
ARG APP_VERSION=dev
|
||||||
|
ENV APP_VERSION=$APP_VERSION
|
||||||
|
LABEL org.opencontainers.image.version=$APP_VERSION
|
||||||
|
|||||||
@@ -218,3 +218,8 @@ def get_thumb(album: str, filename: str):
|
|||||||
# src로부터 thumb 생성/확인 (원본 확장자 유지)
|
# src로부터 thumb 생성/확인 (원본 확장자 유지)
|
||||||
p = ensure_thumb(src, album)
|
p = ensure_thumb(src, album)
|
||||||
return FileResponse(str(p))
|
return FileResponse(str(p))
|
||||||
|
|
||||||
|
@app.get("/api/version")
|
||||||
|
def version():
|
||||||
|
import os
|
||||||
|
return {"version": os.getenv("APP_VERSION", "dev")}
|
||||||
|
|||||||
Reference in New Issue
Block a user