Azure VM 배포
Azure VM(B2s, Ubuntu 22.04 amd64)에 quant-ai를 무중단 배포하기 위한 절차입니다.
Apple Silicon 개발 머신에서 amd64 이미지를 크로스 빌드하고, ACR에 푸시한 뒤,
scripts/deploy_azure.sh가 SSH로 VM에 적용합니다.
사전 요구 사항
- Azure VM (Standard B2s, Ubuntu 22.04, 퍼블릭 IP 보유)
- Azure Container Registry (
quantaiacr.azurecr.io) - 로컬 개발 머신에 다음이 설치됨
azCLI (az login완료)docker buildx(Docker Desktop 4.25+에 포함)- SSH 키 (
~/.ssh/id_*가 VM의azureuser에 등록됨)
- VM에 다음이 설치됨
- Docker Engine 24+
git,curl,cron
모든 Azure 리소스(VM, ACR, NSG)는 Owner, Department, CostCenter,
Environment 4개 태그를 필수로 갖습니다. 태그 누락 시 정책 위반으로
배포가 거절됩니다.
1. 환경 변수 설정
로컬 셸에 다음을 export 합니다 (필요 시 ~/.zshrc 등에 영구 반영).
export VM_IP="20.196.217.226" # 실제 VM IP로 교체
export VM_USER="azureuser"
export ACR_NAME="quantaiacr"
export ACR_LOGIN_SERVER="${ACR_NAME}.azurecr.io"
2. VM 초기 셋업 (1회)
ssh ${VM_USER}@${VM_IP} '
set -e
sudo apt-get update && sudo apt-get install -y docker.io docker-compose-plugin git curl
sudo usermod -aG docker $USER
mkdir -p ~/quantai
'
# .env / docker-compose.prod.yml 송부
scp .env ${VM_USER}@${VM_IP}:~/quantai/.env
scp docker-compose.prod.yml ${VM_USER}@${VM_IP}:~/quantai/
ssh ${VM_USER}@${VM_IP} '
chmod 600 ~/quantai/.env
'
VM의 .env에는 로컬과 다른 값을 둡니다.
| 변수 | VM 값 | 비고 |
|---|---|---|
DB_PASSWORD | 강한 임의 문자열 | docker-compose.prod.yml이 :?Set DB_PASSWORD로 강제 |
AUTH_SECRET_KEY | 새 임의 문자열 (로컬과 다름) | JWT 서명 |
ENCRYPTION_SALT | 새 임의 문자열 | Fernet 파생 |
ALLOW_LIVE | 0 (초반), 1 (라이브 활성 시) | FEATURE_EQUITY_LIVE의 킬 스위치 |
IMAGE_TAG | (배포 스크립트가 자동 갱신) | git short sha |
docker-compose.prod.yml의 app-env 앵커:
FEATURE_EQUITY_LIVE: ${ALLOW_LIVE:+${FEATURE_EQUITY_LIVE:-false}}
ALLOW_LIVE가 비어 있으면 FEATURE_EQUITY_LIVE도 빈값(=false)으로 강제됩니다.
페이퍼 .env를 실수로 VM에 복사해도 라이브가 켜지지 않게 하기 위함입니다.
3. ACR 연결
# 로컬에서 ACR 로그인 (deploy 스크립트가 매번 자동 실행하지만, 첫 회는 수동 권장)
az acr login --name "${ACR_NAME}"
# VM에서도 ACR pull 권한 필요
ssh ${VM_USER}@${VM_IP} "
sudo az acr login --name ${ACR_NAME}
"
장기적으로는 VM에 managed identity를 붙이고 ACR AcrPull 역할을 할당합니다.
4. 첫 배포
scripts/deploy_azure.sh가 다음을 자동화합니다.
docker buildx build --platform linux/amd64 --push(api + web 2개 이미지)- VM의
~/quantai/.deploy_env에IMAGE_TAG기록 (이전 태그는.deploy_env.previous로 백업) docker-compose.prod.yml+scripts/rolling_restart.sh+infra/healthcheck.sh동기화 (rsync/scp)- VM에서
docker compose pull→alembic upgrade head→bash scripts/rolling_restart.sh http://${VM_IP}/health로 스모크 테스트- 실패 시 자동 롤백 (
--rollback)
# 로컬 quant-ai 레포 루트에서
bash scripts/deploy_azure.sh
빌드까지 평균 812분, 배포는 추가 12분입니다.
명시적 태그를 쓰고 싶다면:
bash scripts/deploy_azure.sh --tag v2026.04.26
이미 푸시한 이미지로 재배포만 하려면:
bash scripts/deploy_azure.sh --skip-build
이전 태그로 즉시 롤백:
bash scripts/deploy_azure.sh --rollback
5. 검증
# 헬스 200
curl -fsS "http://${VM_IP}/health"
# 웹 200/304
curl -I "http://${VM_IP}/"
# 컨테이너 상태
ssh ${VM_USER}@${VM_IP} 'cd ~/quantai && sudo docker compose -f docker-compose.prod.yml ps'
# 최근 50행 로그
ssh ${VM_USER}@${VM_IP} 'cd ~/quantai && sudo docker compose -f docker-compose.prod.yml logs --tail=50 api'
자동 일일 헬스체크는 헬스체크 cron에서 설정합니다.
6. 무중단 배포 (rolling restart)
scripts/rolling_restart.sh가 내부적으로 다음 순서로 재시작합니다.
| 순서 | 컨테이너 | graceful 타임아웃 | 의도 |
|---|---|---|---|
| 1 | position-reconciler | 30s | 단일 replica, 5분 누락 허용 |
| 2 | analysis-worker | 60s | lease 반환 시간 확보 |
| 3 | api | (force-recreate) | /health 200을 30회 폴링 |
| 4 | web | (recreate) | nginx 재기동 |
api가 30회 폴링 안에 healthy가 아니면 스크립트가 비제로 종료하므로
deploy_azure.sh는 자동으로 --rollback을 호출합니다.
7. 자주 쓰는 운영 명령
# Compose 명령을 VM에서 실행
ssh ${VM_USER}@${VM_IP} 'cd ~/quantai && sudo -E docker compose -f docker-compose.prod.yml ps'
# 로그 실시간
ssh ${VM_USER}@${VM_IP} 'cd ~/quantai && sudo -E docker compose -f docker-compose.prod.yml logs -f --tail=100 api'
# alembic 현재 revision
ssh ${VM_USER}@${VM_IP} 'cd ~/quantai && sudo -E docker compose -f docker-compose.prod.yml exec -T api alembic current'
# emergency stop (모든 사용자 라이브 차단)
ssh ${VM_USER}@${VM_IP} 'cd ~/quantai && sed -i "s/^ALLOW_LIVE=.*/ALLOW_LIVE=0/" .env && sudo -E docker compose -f docker-compose.prod.yml restart api'
자세한 절차는 비상 정지 참조.
트러블슈팅
| 증상 | 원인 / 조치 |
|---|---|
buildx 단계에서 exec format error | --platform linux/amd64가 없음 — scripts/deploy_azure.sh 사용 권장 |
| ACR push가 401 | az acr login 토큰 만료. 로컬에서 다시 로그인 |
| VM에서 pull이 401 | VM의 sudo az acr login 실행 또는 managed identity 미할당 |
--rollback인데 no previous tag recorded | .deploy_env.previous 부재 (첫 배포 또는 수동 정리됨). 이전 태그를 명시적으로 --tag로 재배포 |
/health 200인데 web 503 | nginx upstream(api) 부팅 직전. 30~60초 후 재시도. 그래도 503이면 DB 마이그 실패 |