Files
portfolio-hub/docs/technical/deployment-and-operations.md
T

194 lines
4.3 KiB
Markdown

# 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 통합 운영 절차를 별도 런북으로 분리할지 결정한다.