이 프로젝트는 사이드 프로젝트지만,
“배포할 때마다 서비스가 몇 분씩 죽는” 상황은 만들고 싶지 않았다.
코인 시세는 계속 움직이고,
SSE로 실시간 가격을 쏘고 있고,
채팅도 WebSocket으로 열려 있는데,
배포 한 번 할 때마다 연결 다 끊기고 에러 뜨면
유저 입장에서는 그냥 “이상한 사이트”가 된다.
그래서 1인 개발 + 저비용이라는 전제를 유지하면서,
내가 감당할 수 있는 선에서 무중단에 가까운 배포 흐름을 하나 만들었다.
이 글은 그 구조를 정리한 내용이다.
전체 배포 구조 한 줄 요약
지금 투자킹의 배포 구조는 대략 이렇게 나뉜다.
- 프론트엔드: Cloudflare Pages (React SPA 빌드 결과)
- 백엔드:
- AWS Lightsail 인스턴스 1대
- Nginx 리버스 프록시
- Uvicorn으로 FastAPI 멀티 워커
- 별도 worker 프로세스(백그라운드 작업 전용)
- DB/인증: Supabase (PostgreSQL + Auth)
- 캐시/채팅/실시간: Redis
이 중 무중단 배포에 중요한 파트는 딱 두 개다.
- Nginx + Uvicorn 멀티 워커 구조
- 백그라운드 worker 프로세스를 API와 분리한 것
나머지는 그냥 “어디에 올려놨느냐” 수준이라 크게 중요하지 않다.
API 서버와 worker를 분리한 이유
처음에는 uvicorn main:app 하나 띄워서
API도 처리하고, APScheduler도 돌리고, 가격 업데이트도 같이 돌렸다.
근데 이 방식은 아래 문제가 있다.
- Uvicorn 워커를 2개, 3개로 늘리는 순간
- 백그라운드 작업이 워커 개수만큼 중복 실행됨
- 예를 들어, 자동 청산 작업이 3개 프로세스에서 동시에 돌 수 있다
- 스케줄러가 꼬이면, 같은 포지션을 여러 번 청산하려고 덤빈다
그래서 구조를 이렇게 갈랐다.
- API 서버
- FastAPI + Uvicorn 멀티 워커
- HTTP 요청 처리만 담당
- 백그라운드 스케줄 작업 없음
- worker 프로세스
- FastAPI 앱을 띄우지 않음
- APScheduler + 무한 루프로 백그라운드 잡만 실행
- 항상 1개 프로세스만 띄움
배포할 때도:
- API 서버는 워커를 늘렸다 줄였다 해도 되고
- worker는 항상 한 개만 조용히 돌아가게 놔두면 끝이다
무중단 배포에서 중요한 건 같은 작업이 중복 실행되지 않게 하는 것이라
처음부터 이 부분을 분리해두는 게 마음이 편했다.
Nginx + 멀티 워커로 무중단에 가까운 배포 만들기
기본 아이디어
“한쪽 워커는 항상 살아 있게 한다”
이게 끝이다.
구체적으로는 이렇게 잡았다.
- Uvicorn 워커를 포트 8000, 8001 두 개까지 사용할 수 있게 구성
- 평소에는 8000만 쓰고, 배포할 때만 8001을 잠깐 켠다
- Nginx upstream에서 두 포트를 모두 바라보되,
- 헬스 체크 + 장애 서버 자동 제외 옵션을 건다
대충 이런 느낌이다:
upstream twojaking_backend {
least_conn;
server 127.0.0.1:8000 max_fails=3 fail_timeout=10s;
server 127.0.0.1:8001 max_fails=3 fail_timeout=10s backup;
}
server {
listen 443 ssl;
server_name api.twojaking.com;
location /api/v1/health {
access_log off;
proxy_pass http://twojaking_backend;
}
location / {
proxy_pass http://twojaking_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
- least_conn으로 현재 연결이 적은 워커부터 사용
- backup은 평소엔 거의 8000만 쓰고, 8000이 이상하면 8001로 보내도록 하는 용도
- /api/v1/health는 Uvicorn 쪽에서 단순 200 OK만 돌려주는 엔드포인트
배포 시나리오
실제 무중단 배포 흐름은 이렇게 간다.
- 새 코드 배포 준비
- Lightsail에 새 코드 pull
- 가상환경/패키지 업데이트
- 두 번째 워커 띄우기
- twojaking-api@1.service (포트 8001) 시작
- 새 코드로 Uvicorn 실행
- Nginx가 헬스 체크 후 8001로도 트래픽 분산
- /health가 200이면 upstream 풀에 자동 포함
- 이제 8000/8001 둘 다 살아있어서, 어느 쪽이든 응답 가능
- 기존 워커 교체
- twojaking-api@0.service(포트 8000)를 재시작
- 이때도 8001은 계속 받아주고 있기 때문에 끊김이 없다
- 안정화 확인 후 스케일 다운
- 양쪽 모두 정상 동작 확인
- 다시 twojaking-api@1 내리고, 평소처럼 8000 한 개만 유지
이걸 쉘 스크립트 두세 개로 묶어두면
사실상 deploy.sh 한 번으로 처리된다.
사이드 프로젝트라 오토 스케일링 그룹 같은 건 쓰지 않지만,
이 정도만 해도 체감상 “멀쩡히 돌면서 배포되는 서비스” 느낌은 충분히 난다.
systemd 템플릿으로 멀티 워커 관리
워커를 늘렸다 줄였다 하려면 systemd 템플릿이 편하다.
대략 이런 식이다:
# /etc/systemd/system/twojaking-api@.service
[Unit]
Description=TwojaKing API Server %i
After=network.target
[Service]
WorkingDirectory=/srv/twojaking
ExecStart=/srv/twojaking/.venv/bin/uvicorn app.main:app \
--host 0.0.0.0 --port 800%i --workers 1
Restart=always
RestartSec=3
User=www-data
[Install]
WantedBy=multi-user.target
그러면:
- twojaking-api@0 → 8000 포트
- twojaking-api@1 → 8001 포트
이런 식으로 포트를 정해놓을 수 있다.
실제 배포할 때는:
# 새 워커 띄우기
sudo systemctl start twojaking-api@1
# 기존 워커 교체
sudo systemctl restart twojaking-api@0
# 안정화 후 서브 워커 내리기
sudo systemctl stop twojaking-api@1
이 정도면, 스크립트 하나에 넣어두고 써도 크게 어렵지 않다.
SSE / WebSocket과 무중단 배포
실시간 기능이 있을 때는 배포 시 끊김을 어느 정도 감수해야 한다.
- SSE는 HTTP 단방향 스트림이기 때문에
- 배포 시 워커가 내려가면 연결은 한 번 끊긴다
- WebSocket 채팅도 비슷하게 재연결이 필요하다
무중단 배포라고 해도,
연결이 한번도 안 끊긴다는 뜻은 아니다.
내가 목표로 한 건 그보다는:
- 배포 중에도 새 요청은 항상 최소 한 워커에서 받는다
- 끊긴 SSE/WebSocket은 프론트에서 자동 재연결한다
- SSE → 브라우저가 알아서 재연결해주고
- WebSocket → 프론트에서 “끊기면 n초 후 재연결” 로직만 넣어둔다
이 정도면,
유저 입장에서는 “잠깐 끊겼다가 다시 붙네?” 수준에서 끝난다.
정리
이 프로젝트의 무중단 배포 구조를 한 줄로 요약하면:
- API 서버와 백그라운드 worker를 분리하고
- Nginx + Uvicorn 멀티 워커를 쓰되
- 배포할 때만 워커를 잠깐 두 개로 늘려서
- 한쪽씩 갈아끼우는 방식이다.
기업 서비스에서 쓰는 완전한 블루/그린 배포,
쿠버네티스 롤링 업데이트 같은 수준은 아니지만,
1인 개발 + Lightsail 1대 기준으로는
“과하지 않으면서, 다운타임 거의 없는 배포”라는 면에서
지금 구조가 딱 적당하다고 생각한다.
모의투자 커뮤니티 1인 개발기
투자킹 링크
코인 모의투자 | 투자킹 - 실시간 암호화폐 모의투자 플랫폼
실시간 시세로 가상 투자 경험. 투자킹은 초보자도 쉽게 시작할 수 있는 암호화폐 모의투자 플랫폼입니다.
twojaking.com
'모의투자 개발기' 카테고리의 다른 글
| 1인 개발 모의투자 커뮤니티 "투자킹" 소개 (1) | 2025.11.25 |
|---|---|
| 모의투자 커뮤니티 1인 개발기 (4) – 커뮤니티 기능 개발 (0) | 2025.11.25 |
| 모의투자 커뮤니티 1인 개발기 (3) – 게임적 요소 한스푼 (0) | 2025.11.25 |
| 모의투자 커뮤니티 1인 개발기 (2) – 실시간 가격 업데이트 구현 (0) | 2025.11.25 |
| 모의투자 커뮤니티 1인 개발기 (1) – 서비스 구조 설계 (0) | 2025.11.25 |