Skip to content
Go back

퀀트 플랫폼 개발기 #6 — Mac → Vultr Seoul VPS 이주, paper 자동매매 띄우기

5편까지 룰을 깎았으면 다음은 어디서 돌릴 것인가 다. 처음에는 안 쓰는 Windows 노트북에 cron 을 박아 24/7 돌리면 된다고 생각했다. 결론적으로 일주일 만에 접고 Vultr Seoul VPS 로 갈아탔다. 1시간 안에 끝난 작업이지만 셋업의 거의 모든 단계에서 한 번씩 막혔고, 그게 더 재밌어서 회고로 남긴다.

왜 클라우드였나

노트북 24/7 cron 의 세 가지 한계가 차례로 드러났다.

  1. 노트북 sleep · 절전 모드 — 평일 15:20 cron 이 절전 중이면 그냥 안 돈다. wake on schedule 같은 옵션이 있긴 하지만 뚜껑이 닫히면 끝이다.
  2. DB 가 두 곳에 분산 — Mac 에서 백테스트하고 룰을 만든 뒤 운영 머신으로 옮길 때마다 export/import 가 필요했다.
  3. 두 머신에서 KIS API 동시 호출 위험 — 계좌가 1개인데 호스트가 2개면 사고 시나리오만 늘어난다.

결론은 VPS 가 single source of truth 가 되어야 cron · UI · 전략 관리가 하나의 흐름으로 정리된다는 것. Mac 은 그 VPS 에 SSH 터널로 붙어 쓰기·디버깅하는 단말 역할로 내려가면 된다.

호스팅 옵션

옵션가격위치선택 사유
AWS Lightsail$10~Tokyo친숙하지만 1-2개월 테스트엔 과함
Vultr$10Seoul한국 IP (KIS 친화) + 가성비
DigitalOcean$12SingaporeUI 깔끔, Tokyo 없음
Railway / Render가변-docker-compose 통째 ❌

KIS 실전 전환 시에도 한국 IP 가 그대로 쓰일 수 있는 게 결정적이었다. Vultr Seoul 1vCPU / 2GB / 55GB SSD / $10/mo 로 확정.

셋업 단계와 실제 소요 시간

단계소요
Vultr 가입 + 인스턴스 생성5분
SSH + apt upgrade + Docker 설치10분
Repo clone + .env + docker compose up -d3분
Mac → VPS DB pg_dump/restore20분
KIS dry-run 검증1분
cron 등록 + 시간대 KST 변경1분
합계약 1시간

문서를 따라가면 1시간이지만, 막히는 곳은 거의 다 paste 와 protocol 의 미묘한 차이 였다.

핵심 시행착오 3개

(a) scp 의 새 SFTP 백엔드와 ~/ 확장

OpenSSH 9.x 부터 scp 가 SFTP 를 기본 백엔드로 쓰는데, 일부 환경에서 source path 의 ~/ 확장이 깨진다.

# ❌ "/root/quant.dump: No such file or directory"
#    Mac 의 ~ 가 안 풀림
scp ~/quant.dump root@vps:/root/

# ✅ -O (옛 SCP protocol) + 절대 경로
scp -O /Users/dongho/quant.dump root@vps:/root/

서브 함정: VPS 안 쉘에서 scp 를 쳐서 한 번 더 실수했다. 프롬프트가 root@quant-server:~# 인 상태에서 Mac 경로를 지정하면 당연히 못 찾는다. 출발지 (Mac) 에서 실행해야 한다는, 너무 당연한데 자주 헷갈리는 포인트.

(b) TimescaleDB dump · restore 정석

지난번 Mac (ARM) → Windows (x86) 시도가 실패했던 원인은 두 가지였다 — 아키텍처 차이 + TimescaleDB 의 pre_restore / post_restore 함수 누락. 정석은 다음 4단계.

# 1. extension 먼저
psql -d quant -c "CREATE EXTENSION timescaledb;"

# 2. pre_restore — hypertable[^hypertable] 메타 일시 비활성
psql -d quant -c "SELECT timescaledb_pre_restore();"

# 3. restore
pg_restore -d quant --no-owner --no-acl /tmp/quant.dump

# 4. post_restore — hypertable 메타 복구
psql -d quant -c "SELECT timescaledb_post_restore();"

dump 단계에서 NOTICE: hypertable data are in chunks, no data will be copied 가 떠서 잠깐 당황했지만, chunks 는 별도 테이블처럼 dump 떠지므로 무시해도 된다. 1GB dump 안에 다 들어있다.

이번엔 클라우드 → 클라우드 (둘 다 x86 Linux) 라 cross-arch 함정이 없었다. 같은 아키텍처면 TimescaleDB dump/restore 는 정석대로 깔끔하게 돈다.

(c) docker-compose 노출 포트는 기본적으로 외부 공개

docker compose up -d 직후 engine 로그에 처음 보는 외부 IP 가 /mcp, /jsonrpc, /security.txt 같은 경로를 GET/POST 하는 게 찍혔다. 인터넷 봇이 노출된 8000 / 8080 포트를 자동 스캔하는 거다. 30초도 안 걸렸다.

원인은 docker-compose.yml 의 ports: ["8000:8000"] 표기가 0.0.0.0:80001 에 바인딩된다는 것. 인터넷 누구나 접근 가능한 상태다. SSH 터널로만 붙으면 되니 외부에 열어둘 이유가 없다.

# Before — 0.0.0.0 바인딩 (외부 공개)
ports:
  - "8000:8000"
  - "8080:8080"

# After — localhost 만 허용
ports:
  - "127.0.0.1:8000:8000"
  - "127.0.0.1:8080:8080"

docker compose ps 의 PORTS 컬럼이 127.0.0.1:8000->8000/tcp 로 보이면 외부 차단 확인.

최종 운영 흐름

[Mac 브라우저] → localhost:3000

[Mac 의 web (pnpm dev)]
       ↓ API 호출 → localhost:8080
[SSH 터널 (Mac:8080 → VPS:8080)]

[VPS Go API] ←→ [VPS DB] ←→ [VPS engine cron]
Cron (KST 평일)동작
08:00incremental ingest2 — 전 영업일 종가 1일치
15:20라이브 매매 (paper, 동시호가 시장가 → 15:30 batch fill)

미묘한 포인트 — 월요일 ingest 의 의미

처음에는 월요일 08:00 ingest 는 토 · 일 비거래일이라 noop 아니냐 고 생각했지만 틀렸다.

cron 을 화-금만 돌리면 안 되는 이유다. 시점 · 타임존의 이런 미묘함이 자가호스팅 자동매매의 디테일이다.

회고

VPS 셋업은 매뉴얼이 무서워 보이지만 실제로는 한 시간 안에 끝났다. 막히는 곳은 거의 다 paste 와 protocol 의 미묘한 차이 — scp -O, .env 의 trailing 공백, docker-compose vs docker compose. 시스템 자체의 복잡성보다 도구의 작은 변경 이력이 발목을 잡는다.

세 가지로 정리하면.

  1. 클라우드 → 클라우드 dump/restore 는 깔끔하다 — 같은 아키텍처면 TimescaleDB 도 정석대로 돈다. ARM ↔ x86 같은 cross-arch 마이그레이션은 권장하지 않는다.
  2. docker compose 노출 포트는 기본적으로 외부 공개 — 봇이 30초 안에 스캔을 시작한다. 127.0.0.1 바인딩 + SSH 터널이 가장 단순하고 안전.
  3. .env 의 trailing 공백 · CR 은 의외로 흔한 함정nano 로 paste 하지 말고 cat <<'EOF' heredoc3 으로 쓰는 편이 안전하다.

다음 편은 로컬 작업 → VPS 자동 반영 파이프라인 — Mac 에서 코드를 수정해 push 하면 VPS 의 컨테이너가 자동으로 갱신되도록 묶는 작업이 될 예정이다.

Footnotes

  1. 0.0.0.0 vs 127.0.0.1 바인딩0.0.0.0 은 모든 네트워크 인터페이스에서 listen, 즉 외부 인터넷에서도 접근 가능. 127.0.0.1 (loopback) 은 같은 호스트 안에서만 접근 가능. SSH 터널이 있다면 후자가 거의 항상 안전한 선택.

  2. incremental ingest — 전체 데이터를 새로 받지 않고, DB 의 마지막 적재 시점 이후 변경분만 추가하는 적재 방식. 한 번에 1일치만 받아 cron 부하·API 호출 한도가 작아진다.

  3. heredoc — shell 의 <<EOF ... EOF 문법. 여러 줄 텍스트를 그대로 명령의 표준 입력으로 전달한다. 'EOF' 처럼 따옴표로 감싸면 변수 치환·이스케이프가 모두 비활성화돼서 paste 안전성이 높다.


Share this post on:

Comments


Previous Post
퀀트 플랫폼 개발기 #7 — Tailscale + GitHub Actions runner 로 운영 흐름 만들기
Next Post
Dependency-cruiser로 GitLab MR 변경 영향 분석 봇 만들기