6dff8e7777
- Domain, Infrastructure, Application (class libraries) - Web (ASP.NET Core empty) - Admin (Blazor Server) - All net8.0 target framework - Project references configured Co-Authored-By: Claude <noreply@anthropic.com>
17 KiB
17 KiB
클라우드 서버 설정 가이드 (hz-prod-01)
시놀로지(Synology DSM)에서 클라우드 VPS(
178.104.200.7)로 이전. 이 문서는 서버에서 실제 수집된 데이터 기반이며, 운영 하네스로 사용한다.
참조 인덱스
| # | 섹션 | 핵심 내용 |
|---|---|---|
| 1 | 서버 기본 정보 | 호스트명, IP, OS, CPU/RAM/디스크, 타임존 |
| 2 | 접속 정보 | SSH 접속, 사용자, 인증 방식 |
| 3 | 소프트웨어 스택 | Python, .NET, PG, Nginx, Docker Compose, fail2ban |
| 3.1 | 런타임 | 버전/경로 일람 |
| 3.2 | Python 가상 환경 | ~/.venv, python3 사용 규칙 |
| 3.3 | 주요 Python 패키지 | 시스템/venv 패키지 구분 |
| 4 | 서비스 아키텍처 | 포트 맵, Nginx 리버스 프록시 |
| 4.1 | 포트 맵 | 22, 80, 2222, 3000, 5000, 5432 |
| 4.2 | Nginx 리버스 프록시 | / → Gitea, /quant/ → Blazor |
| 5 | Gitea | Docker Compose 설정, 시크릿, 데이터 경로 |
| 5.1 | Docker Compose | gitea:1.26.4, PG 연동 |
| 5.2 | 시크릿 관리 | /opt/stacks/gitea/.env |
| 5.3 | 데이터 | Gitea 볼륨, giteadb |
| 6 | Gitea Act Runner (CI) | 6× 러너, 네트워크, 구성 디렉토리 |
| 6.1 | 컨테이너 현황 | 러너 6개 실행 상태 |
| 6.2 | 러너 설정 | hz-prod-runner, gitea_default 네트워크 |
| 6.3 | 러너 구성 디렉토리 | ~/gitea-runner[-N]/ |
| 7 | QuantEngine Blazor Admin | systemd, symlink 배포, DLL 구성 |
| 7.1 | systemd 서비스 | quantengine.service 전문 |
| 7.2 | 배포 구조 | 타임스탬프 디렉토리 + symlink 교체 |
| 7.3 | 주요 DLL | Web, Core, Infrastructure, MudBlazor, Dapper |
| 8 | PostgreSQL 18 | v18.4, localhost 바인드, Docker 연동 |
| 9 | 보안 | SSH hardening, UFW, fail2ban, 네트워크 격리 |
| 9.1 | SSH 보안 설정 | 공개키 전용, root 차단 |
| 9.2 | UFW 방화벽 | ENABLED=yes, 포트 개방/차단 |
| 9.3 | fail2ban | SSH 브루트포스 방어 |
| 9.4 | Docker 네트워크 격리 | 로컬바인드 정책 |
| 10 | 디렉토리 맵 | /home/kjh2064/, /opt/stacks/, /opt/backups/ |
| 11 | 시놀로지 → 클라우드 마이그레이션 매핑 | 항목별 구↔신 비교표 |
| 12 | 운영 명령 치트시트 | 서비스 관리, 배포, 러너 등록, SSH |
| 13 | 검증 하네스 | 헬스체크, 엔드포인트, 마이그레이션 체크리스트 |
관련 문서 상호 참조
| 문서 | 역할 |
|---|---|
AGENTS.md |
운영 헌법, Directory Routing 인덱스 |
GITEA_SECRETS_SETUP.md |
Gitea 시크릿 설정/검증 가이드 |
ROADMAP_WBS.md |
.gs → Python 및 xlsx → sqlite WBS |
docs/GITEA_TOKEN_HOME_RUNBOOK.md |
Gitea 토큰 관리 런북 |
spec/00_execution_contract.yaml |
실행 계약 원본 권위 |
governance/agents_index.yaml |
거버넌스 규칙 인덱스 |
1. 서버 기본 정보
| 항목 | 값 |
|---|---|
| 호스트명 | hz-prod-01 |
| IP | 178.104.200.7 |
| OS | Ubuntu 26.04 LTS (Resolute Raccoon) |
| 커널 | 7.0.0-22-generic (x86_64, PREEMPT_DYNAMIC) |
| CPU | AMD EPYC-Rome, 2 vCPU |
| 메모리 | 3.7 GiB (사용 ~958 MiB, 가용 ~2.8 GiB) |
| 스왑 | 2.0 GiB |
| 디스크 | /dev/sda1 38 GB (사용 8.5 GB / 28 GB 가용, 24%) |
| 타임존 | Asia/Seoul (KST, +0900), NTP 동기화 활성 |
2. 접속 정보
| 항목 | 값 |
|---|---|
| SSH 접속 | ssh kjh2064@178.104.200.7 |
| SSH 포트 | 22 (기본) |
| 사용자 | kjh2064 (uid=1000) |
| 그룹 | kjh2064, sudo, users, docker |
| 인증 방식 | 공개키 전용 (PasswordAuthentication no) |
| Root 로그인 | 비활성 (PermitRootLogin no) |
| Max Auth Tries | 3 |
| Keep-Alive | ClientAliveInterval 300, ClientAliveCountMax 2 |
3. 소프트웨어 스택
3.1. 런타임
| 소프트웨어 | 버전 | 경로 |
|---|---|---|
| Python | 3.14.4 | /usr/bin/python3 |
| .NET SDK | 10.0.109 | /usr/lib/dotnet/sdk |
| .NET Runtime | ASP.NET Core 10.0.9 + NETCore 10.0.9 | /usr/lib/dotnet/shared/ |
| PostgreSQL | 18.4 | postgresql@18-main.service |
| Nginx | 시스템 패키지 | nginx.service |
| Docker Compose | v5.2.0 | Docker 플러그인 |
| fail2ban | 1.1.0 | fail2ban.service |
3.2. Python 가상 환경
경로: ~/.venv
Python: 3.14.4
주의: 이 서버에서는
python3을 사용한다 (시놀로지/Windows와 다름). CI 워크플로우와 로컬 서버 모두python3을 사용하므로 통일됨.
3.3. 주요 Python 패키지 (시스템)
boto3, cryptography, Jinja2, jsonschema, fail2ban 등 시스템 레벨로 설치됨.
프로젝트 의존성은 ~/.venv에 별도 관리.
4. 서비스 아키텍처
4.1. 포트 맵
| 포트 | 서비스 | 바인드 | 비고 |
|---|---|---|---|
| 22 | SSH | 0.0.0.0 |
공개키 전용 |
| 80 | Nginx (리버스 프록시) | 0.0.0.0 |
외부 진입점 |
| 2222 | Gitea SSH | 0.0.0.0 |
Git SSH 접속 |
| 3000 | Gitea Web | 127.0.0.1 |
Nginx 프록시 경유 |
| 5000 | QuantEngine Blazor | 127.0.0.1 |
Nginx /quant/ 경유 |
| 5432 | PostgreSQL | 127.0.0.1 + 172.17.0.1 |
로컬 + Docker 네트워크 |
4.2. Nginx 리버스 프록시
# /etc/nginx/sites-enabled/gitea-ip.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
client_max_body_size 512M;
# QuantEngine Blazor Web App
location /quant/ {
proxy_pass http://127.0.0.1:5000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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;
}
# Gitea (기본)
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
}
라우팅 요약:
http://178.104.200.7/→ Gitea Web UIhttp://178.104.200.7/quant/→ QuantEngine Blazor Adminssh://178.104.200.7:2222→ Gitea Git SSH
5. Gitea
5.1. Docker Compose
# /opt/stacks/gitea/docker-compose.yml
services:
gitea:
image: docker.gitea.com/gitea:1.26.4
container_name: gitea
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
USER_UID: "1000"
USER_GID: "1000"
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: host.docker.internal:5432
GITEA__database__NAME: giteadb
GITEA__database__USER: gitea
GITEA__database__PASSWD: "${GITEA_DB_PASSWORD}"
GITEA__server__DOMAIN: "${SERVER_IP}"
GITEA__server__ROOT_URL: "http://${SERVER_IP}/"
GITEA__server__SSH_DOMAIN: "${SERVER_IP}"
GITEA__server__SSH_PORT: "2222"
GITEA__security__INSTALL_LOCK: "true"
GITEA__service__DISABLE_REGISTRATION: "true"
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
- "2222:22"
5.2. 시크릿 관리
.env파일:/opt/stacks/gitea/.env(소유자 전용,600)- 포함 변수:
GITEA_DB_PASSWORD,SERVER_IP
5.3. 데이터
- Gitea 데이터:
/opt/stacks/gitea/gitea/ - DB: PostgreSQL
giteadb(Docker → host.docker.internal:5432 경유)
6. Gitea Act Runner (CI)
6.1. 컨테이너 현황
| 이름 | 이미지 | 상태 |
|---|---|---|
gitea-runner |
gitea/act_runner:latest |
실행 중 |
gitea-runner-2 |
gitea/act_runner:latest |
실행 중 |
gitea-runner-3 |
gitea/act_runner:latest |
실행 중 |
hopeful_galileo |
gitea/act_runner:latest |
실행 중 |
jovial_bouman |
gitea/act_runner:latest |
실행 중 |
upbeat_chatelet |
gitea/act_runner:latest |
실행 중 |
총 6개 러너가 활성 상태. 네트워크는
gitea_defaultDocker 네트워크 사용.
6.2. 러너 설정
# ~/gitea-runner/config.yaml
container:
network: "gitea_default"
- 러너 이름:
hz-prod-runner - 러너 UUID:
d6d9120b-5070-4874-88d7-b86fe817d5a0 - 러너 이미지:
docker.gitea.com/runner-images:ubuntu-latest(2.33 GB)
6.3. 러너 구성 디렉토리
~/gitea-runner/ # 1번 러너
~/gitea-runner-2/ # 2번 러너
~/gitea-runner-3/ # 3번 러너
7. QuantEngine Blazor Admin
7.1. systemd 서비스
# /etc/systemd/system/quantengine.service
[Unit]
Description=Quant Engine Blazor Admin Web App (.NET 10)
After=network.target
[Service]
WorkingDirectory=/home/kjh2064/quantengine_active
ExecStart=/usr/bin/dotnet /home/kjh2064/quantengine_active/QuantEngine.Web.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=quantengine
User=kjh2064
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://127.0.0.1:5000
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
7.2. 배포 구조
~/quantengine_active → ~/deployments/quantengine_20260625_182821 (symlink)
~/deployments/
├── quantengine_20260625_155649/
├── quantengine_20260625_164548/
├── quantengine_20260625_164928/
└── quantengine_20260625_182821/ ← 현재 활성
배포 방식: 타임스탬프 디렉토리 생성 → symlink 교체 → systemctl restart quantengine
7.3. 주요 DLL
QuantEngine.Web.dll— 웹 진입점QuantEngine.Core.dll— 핵심 도메인QuantEngine.Application.dll— 애플리케이션 서비스QuantEngine.Infrastructure.dll— 인프라 (DB, 외부 연동)Npgsql.dll— PostgreSQL 드라이버MudBlazor.dll— UI 컴포넌트Dapper.dll— 마이크로 ORM
8. PostgreSQL 18
| 항목 | 값 |
|---|---|
| 버전 | 18.4 (Ubuntu 패키지) |
| 서비스 | postgresql@18-main.service |
| listen_addresses | localhost (기본값, 로컬 전용) |
| 바인드 | 127.0.0.1:5432, 172.17.0.1:5432 (Docker), [::1]:5432 |
| Gitea DB | giteadb (사용자: gitea) |
Docker 컨테이너는
host.docker.internal:5432로 호스트 PG에 접속.listen_addresses는postgresql.conf에서 기본값localhost로 설정됨 (외부 접속 차단).
9. 보안
9.1. SSH 보안 설정
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
9.2. UFW 방화벽
- 상태:
ENABLED=yes(/etc/ufw/ufw.conf) - 로그 레벨:
low - 외부 개방 포트: 22 (SSH), 80 (HTTP/Nginx), 2222 (Gitea SSH)
- 내부 전용: 3000 (Gitea Web), 5000 (QuantEngine), 5432 (PostgreSQL)
상세 규칙 확인:
sudo ufw status numbered(TTY + sudo 비밀번호 필요)
9.3. fail2ban
fail2ban.service활성 상태- SSH 브루트포스 방어 활성
9.4. Docker 네트워크 격리
- Gitea Web:
127.0.0.1:3000(로컬 전용) - QuantEngine:
127.0.0.1:5000(로컬 전용) - PostgreSQL:
127.0.0.1+ Docker bridge (172.17.0.1) - 외부 노출: SSH(22), HTTP(80), Gitea SSH(2222)만 개방
10. 디렉토리 맵
/home/kjh2064/
├── quantengine_active → deployments/quantengine_YYYYMMDD_HHMMSS (symlink)
├── deployments/ # QuantEngine 배포 히스토리
│ └── quantengine_YYYYMMDD_HHMMSS/
│ └── wwwroot/
├── gitea-runner/ # Gitea Act Runner 1
├── gitea-runner-2/ # Gitea Act Runner 2
├── gitea-runner-3/ # Gitea Act Runner 3
├── apps/ # 추가 앱
│ └── python-test/.venv/
├── .venv/ # Python 3.14 가상 환경
├── tmp/ # 임시 작업
└── .ssh/ # SSH 키
/opt/stacks/
├── gitea/
│ ├── docker-compose.yml
│ ├── .env # GITEA_DB_PASSWORD, SERVER_IP
│ └── gitea/ # Gitea 데이터 볼륨
└── dotnet-app/ # .NET 관련
/opt/backups/ # 백업
11. 시놀로지 → 클라우드 마이그레이션 매핑
| 항목 | 시놀로지 (구) | 클라우드 (신) |
|---|---|---|
| 프로젝트 경로 | /volume1/projects/data_feed |
미배치 (TBD) |
| Python | python3 (시스템) |
python3 (/usr/bin/python3, 3.14.4) |
| Gitea | Docker on DSM | Docker on Ubuntu (gitea:1.26.4) |
| Gitea SSH | 포트 변동 | 2222 고정 |
| CI Runner | Synology Act Runner | 6× act_runner:latest (Docker) |
| DB | SQLite (파일 기반) | PostgreSQL 18 + SQLite (하이브리드) |
| 웹 Admin | 없음 | QuantEngine Blazor (.NET 10, MudBlazor) |
| 리버스 프록시 | Synology 내장 | Nginx (/ → Gitea, /quant/ → Blazor) |
| 보안 | DSM 방화벽 | fail2ban + SSH 공개키 + 서비스 로컬바인드 |
| 시크릿 관리 | .secrets/kis_real.env |
/opt/stacks/gitea/.env |
| OS | Synology DSM 7.x | Ubuntu 26.04 LTS |
| 타임존 | (설정 의존) | Asia/Seoul (NTP 동기화) |
12. 운영 명령 치트시트
서비스 관리
# QuantEngine
sudo systemctl status quantengine
sudo systemctl restart quantengine
sudo journalctl -u quantengine -f
# Gitea
cd /opt/stacks/gitea && docker compose up -d
docker compose logs -f gitea
# Nginx
sudo systemctl reload nginx
sudo nginx -t
# PostgreSQL
sudo systemctl status postgresql@18-main
sudo -u postgres psql
# Docker 전체 상태
docker ps -a
QuantEngine 배포
# 1. 새 배포 디렉토리 생성
DEPLOY_DIR=~/deployments/quantengine_$(date +%Y%m%d_%H%M%S)
mkdir -p "$DEPLOY_DIR"
# 2. 빌드 산출물 복사 (로컬에서 scp 또는 CI에서)
scp -r publish/* kjh2064@178.104.200.7:"$DEPLOY_DIR"/
# 3. symlink 교체
ln -sfn "$DEPLOY_DIR" ~/quantengine_active
# 4. 서비스 재시작
sudo systemctl restart quantengine
sudo systemctl status quantengine
Gitea Act Runner 등록
# 새 러너 등록 (Gitea 웹 → Settings → Actions → Runners에서 토큰 복사)
docker run -d \
--name gitea-runner-N \
--restart unless-stopped \
--network gitea_default \
-v /var/run/docker.sock:/var/run/docker.sock \
gitea/act_runner:latest
SSH 접속
# Windows 로컬에서
ssh kjh2064@178.104.200.7
# Gitea Git 접속
git remote set-url origin ssh://git@178.104.200.7:2222/kjh2064/QuantEngineByItz.git
13. 검증 하네스
13.1. 서버 헬스 체크
ssh kjh2064@178.104.200.7 "
echo '=== Services ==='
systemctl is-active quantengine nginx docker postgresql@18-main fail2ban
echo '=== Docker ==='
docker ps --format '{{.Names}}: {{.Status}}'
echo '=== Disk ==='
df -h /
echo '=== Memory ==='
free -h | head -2
"
기대 결과:
- 5개 서비스 모두
active - Docker 컨테이너 7개 (gitea + runner ×6)
Up - 디스크 사용률 < 80%
- 메모리 가용 > 1 GiB
13.2. 엔드포인트 접근 확인
# Gitea Web
curl -s -o /dev/null -w "%{http_code}" http://178.104.200.7/
# 기대: 200
# QuantEngine
curl -s -o /dev/null -w "%{http_code}" http://178.104.200.7/quant/
# 기대: 200
# Gitea SSH
ssh -T -p 2222 git@178.104.200.7 2>&1 | head -1
# 기대: "Hi there, ..." Gitea 응답
13.3. data_feed 프로젝트 마이그레이션 체크리스트
- 프로젝트 경로 결정 및 clone
- Python venv에 프로젝트 의존성 설치 (
pip install -r requirements.txt) - KIS 시크릿 설정 (
~/.secrets/kis_real.env) - crontab 또는 systemd timer 등록
GatherTradingData.json동기화 경로 확정- SQLite canonical DB 경로 확정
- CI 워크플로우 러너 라벨 확인
- GAS 배포 스크립트 서버 경로 업데이트
수집 일시: 2026-06-26 09:55 KST 수집 방법:
ssh kjh2064@178.104.200.7라이브 명령 실행 provenance: 모든 값은 서버 실시간 명령 출력에서 추출. 임의 값 없음.