# Deployment and Operations - Related REQ: N/A ## 목표 PortfolioHub를 Linux 환경에서 안정적으로 배포하고 HTTPS 종단, 프로세스 복구, 기본 점검 경로를 표준화한다. ## 사전 조건 - public IP를 가진 Linux/Ubuntu 서버 - 해당 IP를 가리키는 도메인 DNS `A` record - 방화벽에서 `80`, `443` 포트 개방 ## 권장 배포 토폴로지 - App: `uvicorn app.main:app` - Reverse proxy/TLS: Caddy - Process manager: systemd - 선택: Docker Compose + Caddy sidecar ## Linux Direct Run 기준 1. Python 가상환경 생성 후 패키지를 설치한다. 2. 환경 변수 또는 `.env`를 구성한다. 3. `uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 2`를 실행한다. 4. `GET /health`를 확인한다. ## Caddy 설치와 구성 Ubuntu/Debian 설치 예시: ```bash sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \ | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \ | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install caddy ``` 설치 검증: ```bash caddy version ``` 기본 Caddyfile: ```text your-domain.com { reverse_proxy localhost:8000 } ``` 동작 기준: - TLS 인증서 자동 발급/갱신 - `80`에서 `443`으로 HTTP redirect - 외부 요청을 내부 `uvicorn` 프로세스(`127.0.0.1:8000`)로 전달 적용 후 reload: ```bash sudo systemctl reload caddy ``` ## systemd 운영 기준 - 서비스 계정 권한 최소화 - `Restart=on-failure` - 로그 수집 경로(journalctl 또는 외부 수집기) 확보 예시 unit 파일: ```ini [Unit] Description=PortfolioHub API After=network.target [Service] User=www-data WorkingDirectory=/opt/portfoliohub EnvironmentFile=/opt/portfoliohub/.env ExecStart=/opt/portfoliohub/.venv/bin/uvicorn app.main:app \ --host 127.0.0.1 --port 8000 --workers 2 Restart=on-failure [Install] WantedBy=multi-user.target ``` 등록/기동: ```bash sudo systemctl daemon-reload sudo systemctl enable portfoliohub sudo systemctl start portfoliohub ``` ## HTTPS/Caddy 운영 기준 - 도메인 DNS가 서버 IP를 가리켜야 한다. - 포트 `80/443`을 개방한다. - Caddyfile에서 앱을 `reverse_proxy`로 연결한다. - 브라우저 기반 frontend가 있으면 `PORTFOLIOHUB_ALLOWED_ORIGINS`에 정확한 HTTPS origin을 지정한다. 예시: ```bash PORTFOLIOHUB_ALLOWED_ORIGINS=https://app.example.com,https://www.example.com ``` ## Docker Compose 운영 기준 - 앱 컨테이너는 내부 포트만 expose하고 외부 종단은 Caddy가 담당한다. - `caddy_data`, `caddy_config` 볼륨을 유지해 인증서 상태를 보존한다. - 배포 후 `docker compose ps`, `curl /health`로 기본 상태를 확인한다. 예시 compose: ```yaml services: app: build: . expose: - "8000" environment: - PORTFOLIOHUB_ALLOWED_ORIGINS=https://your-domain.com restart: unless-stopped caddy: image: caddy:2-alpine ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - app restart: unless-stopped volumes: caddy_data: caddy_config: ``` compose 네트워크 안에서는 `reverse_proxy app:8000`을 사용한다. 기동: ```bash docker compose up -d ``` ## 방화벽 설정 UFW: ```bash sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw reload ``` firewalld: ```bash sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload ``` ## 배포 검증 HTTPS health check: ```bash curl https://your-domain.com/health ``` 운영 상태 확인: ```bash sudo systemctl status caddy sudo journalctl -u caddy -f ``` ## Validation Notes - HTTPS 엔드포인트에서 `/health`, `/docs` 접근이 가능해야 한다. - DB 사용 배포에서는 마이그레이션 적용 후 앱을 기동한다. - CORS 오리진 설정과 실제 프론트엔드 도메인이 일치해야 한다. ## Open Implementation Points - blue/green 또는 rolling update 같은 무중단 배포 전략 도입 여부를 확정한다. - Loki/metrics 통합 운영 절차를 별도 런북으로 분리할지 결정한다.