Some checks failed
Deploy to CF Multisite / deploy (push) Failing after 1m53s
- Cloudflare Workers + R2 기반 - Edge 캐싱으로 비용 절감 - 티어별 Rate Limiting (free/basic/pro) - KV 기반 사용량 추적 - Admin API (usage, customers, tiers, stats) - Gitea Actions 배포 워크플로우 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
359 lines
11 KiB
Markdown
359 lines
11 KiB
Markdown
# CF Multisite
|
||
|
||
Cloudflare Workers + R2 기반 멀티테넌트 정적 사이트 호스팅 플랫폼
|
||
|
||
## 아키텍처
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Gitea (gitea.anvil.it.com) │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ site-a │ │ site-b │ │ site-c │ ... │
|
||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||
│ │ push │ push │ push │
|
||
└───────┼─────────────┼─────────────┼─────────────────────────────┘
|
||
▼ ▼ ▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Gitea Actions Runner (jp1) │
|
||
│ aws s3 sync → R2 │
|
||
└───────────────────────────┬─────────────────────────────────────┘
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Cloudflare R2 Bucket │
|
||
│ /sites/site-a/index.html │
|
||
│ /sites/site-b/index.html │
|
||
│ /sites/site-c/index.html │
|
||
└───────────────────────────┬─────────────────────────────────────┘
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Cloudflare Workers │
|
||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||
│ │ Cache │ │ Rate Limit │ │ Admin API │ │
|
||
│ │ (Edge) │ │ (KV) │ │ (REST) │ │
|
||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||
└───────────────────────────┬─────────────────────────────────────┘
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Custom Domains │
|
||
│ site-a.actions.it.com site-b.actions.it.com ... │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 주요 기능
|
||
|
||
### 멀티테넌트 호스팅
|
||
- 서브도메인 기반 고객 분리: `{customer}.actions.it.com`
|
||
- R2에 고객별 디렉토리 구조: `/sites/{customer}/`
|
||
- 자동 index.html 라우팅
|
||
|
||
### 캐싱 전략
|
||
- Edge 캐시로 R2 요청 최소화 → 비용 절감
|
||
- 파일 타입별 TTL 최적화:
|
||
|
||
| 파일 타입 | 캐시 TTL |
|
||
|-----------|----------|
|
||
| HTML | 1시간 |
|
||
| CSS, JS, JSON | 24시간 |
|
||
| 이미지 (PNG, JPG, GIF, SVG) | 7일 |
|
||
| 폰트 (WOFF, WOFF2, TTF) | 30일 |
|
||
|
||
### Rate Limiting (티어별)
|
||
|
||
| 티어 | 분당 요청 | 일일 대역폭 | 월간 대역폭 |
|
||
|------|-----------|-------------|-------------|
|
||
| Free | 60 | 5GB | ~150GB |
|
||
| Basic | 300 | 50GB | ~1.5TB |
|
||
| Pro | 1,000 | 500GB | ~15TB |
|
||
|
||
### 사용량 추적 (KV)
|
||
- 고객별 일일 요청 수
|
||
- 고객별 일일 대역폭
|
||
- 분당 요청 수 (Rate Limit용)
|
||
- 7일간 데이터 보관
|
||
|
||
## Admin API
|
||
|
||
모든 API는 Bearer 토큰 인증 필요:
|
||
```bash
|
||
curl -H "Authorization: Bearer $ADMIN_TOKEN" https://site.actions.it.com/api/...
|
||
```
|
||
|
||
### 엔드포인트
|
||
|
||
| Method | Endpoint | 설명 |
|
||
|--------|----------|------|
|
||
| GET | `/api/usage/:customer?days=7` | 고객 사용량 조회 |
|
||
| GET | `/api/customers` | 전체 고객 목록 |
|
||
| PUT | `/api/tier/:customer` | 고객 티어 변경 |
|
||
| GET | `/api/stats` | 전체 통계 |
|
||
| DELETE | `/api/customer/:customer` | 고객 데이터 삭제 |
|
||
|
||
### 사용 예시
|
||
|
||
```bash
|
||
# 고객 사용량 조회
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://multisite-demo.actions.it.com/api/usage/multisite-demo?days=7"
|
||
|
||
# 티어 변경
|
||
curl -X PUT \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"tier": "basic"}' \
|
||
"https://multisite-demo.actions.it.com/api/tier/customer-name"
|
||
|
||
# 전체 통계
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://multisite-demo.actions.it.com/api/stats"
|
||
```
|
||
|
||
## 설치 및 배포
|
||
|
||
### 1. 프로젝트 클론
|
||
|
||
```bash
|
||
cd ~/projects
|
||
git clone <repo> cf-multisite
|
||
cd cf-multisite
|
||
npm install
|
||
```
|
||
|
||
### 2. Cloudflare 설정
|
||
|
||
```bash
|
||
# wrangler 로그인
|
||
npx wrangler login
|
||
|
||
# KV 네임스페이스 생성
|
||
npx wrangler kv:namespace create USAGE
|
||
|
||
# R2 버킷 생성 (이미 있으면 스킵)
|
||
npx wrangler r2 bucket create multisite-bucket
|
||
```
|
||
|
||
### 3. wrangler.toml 설정
|
||
|
||
```toml
|
||
name = "cf-multisite"
|
||
main = "src/worker.js"
|
||
compatibility_date = "2024-12-01"
|
||
|
||
[[r2_buckets]]
|
||
binding = "BUCKET"
|
||
bucket_name = "multisite-bucket"
|
||
|
||
[[kv_namespaces]]
|
||
binding = "USAGE"
|
||
id = "<KV_NAMESPACE_ID>"
|
||
|
||
routes = [
|
||
{ pattern = "*.actions.it.com", zone_name = "actions.it.com" }
|
||
]
|
||
```
|
||
|
||
### 4. 시크릿 설정
|
||
|
||
```bash
|
||
# Admin API 토큰 생성 및 설정
|
||
openssl rand -hex 32 # 토큰 생성
|
||
npx wrangler secret put ADMIN_TOKEN
|
||
```
|
||
|
||
### 5. 배포
|
||
|
||
```bash
|
||
npm run deploy
|
||
# 또는
|
||
npx wrangler deploy
|
||
```
|
||
|
||
## 고객 사이트 추가
|
||
|
||
### 1. Gitea 저장소 생성
|
||
|
||
고객용 저장소를 Gitea에 생성합니다.
|
||
|
||
### 2. Workflow 파일 추가
|
||
|
||
`.gitea/workflows/deploy.yml`:
|
||
|
||
```yaml
|
||
name: Deploy to CF Multisite
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Install AWS CLI
|
||
run: |
|
||
curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/awscliv2.zip
|
||
cd /tmp && unzip -q awscliv2.zip && sudo ./aws/install
|
||
|
||
- name: Deploy to R2
|
||
env:
|
||
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY }}
|
||
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_KEY }}
|
||
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
|
||
run: |
|
||
SITE_ID="${{ github.event.repository.name }}"
|
||
|
||
aws s3 sync . "s3://multisite-bucket/sites/${SITE_ID}/" \
|
||
--endpoint-url "${R2_ENDPOINT}" \
|
||
--region auto \
|
||
--exclude ".git/*" \
|
||
--exclude ".gitea/*" \
|
||
--exclude "README.md" \
|
||
--delete
|
||
|
||
echo "Deployed to: https://${SITE_ID}.actions.it.com"
|
||
```
|
||
|
||
### 3. Secrets 설정 (Gitea Organization)
|
||
|
||
| Secret | 값 |
|
||
|--------|-----|
|
||
| `R2_ACCESS_KEY` | R2 API 토큰 Access Key |
|
||
| `R2_SECRET_KEY` | R2 API 토큰 Secret Key |
|
||
| `R2_ENDPOINT` | `https://<account-id>.r2.cloudflarestorage.com` |
|
||
|
||
### 4. Push → 자동 배포
|
||
|
||
```bash
|
||
git add .
|
||
git commit -m "Initial site"
|
||
git push origin main
|
||
# → https://{repo-name}.actions.it.com 에서 확인
|
||
```
|
||
|
||
## 로컬 개발
|
||
|
||
```bash
|
||
# 개발 서버 실행
|
||
npm run dev
|
||
|
||
# 테스트 URL
|
||
# http://localhost:8787?site=demo
|
||
# http://localhost:8787?site=multisite-demo
|
||
```
|
||
|
||
## 파일 구조
|
||
|
||
```
|
||
cf-multisite/
|
||
├── src/
|
||
│ └── worker.js # Workers 메인 코드
|
||
│ # - 라우팅
|
||
│ # - 캐싱
|
||
│ # - Rate Limiting
|
||
│ # - Admin API
|
||
├── scripts/
|
||
│ └── upload.js # 수동 R2 업로드 스크립트
|
||
├── sample-site/ # 샘플 사이트
|
||
│ ├── index.html
|
||
│ ├── about.html
|
||
│ ├── contact.html
|
||
│ └── style.css
|
||
├── .gitea/
|
||
│ └── workflows/
|
||
│ └── deploy.yml # Gitea Actions 템플릿
|
||
├── wrangler.toml # Workers 설정
|
||
├── package.json
|
||
└── README.md
|
||
```
|
||
|
||
## 인프라 구성
|
||
|
||
| 컴포넌트 | 위치 | 용도 |
|
||
|----------|------|------|
|
||
| Gitea | gitea.anvil.it.com | Git 호스팅 |
|
||
| Gitea Runner | jp1 (Incus) | CI/CD 실행 |
|
||
| R2 Bucket | multisite-bucket | 정적 파일 저장 |
|
||
| KV Namespace | USAGE | 사용량 추적 |
|
||
| Workers | cf-multisite | 라우팅/캐싱/API |
|
||
| Domain | *.actions.it.com | 와일드카드 도메인 |
|
||
|
||
## 비용 구조
|
||
|
||
### Cloudflare 무료 티어
|
||
|
||
| 항목 | 무료 한도 |
|
||
|------|-----------|
|
||
| Workers 요청 | 일 10만 건 |
|
||
| R2 저장 | 10GB |
|
||
| R2 Class A (쓰기) | 월 100만 건 |
|
||
| R2 Class B (읽기) | 월 1000만 건 |
|
||
| KV 읽기 | 일 10만 건 |
|
||
| KV 쓰기 | 일 1000건 |
|
||
|
||
### 예상 비용 (1000 고객 기준)
|
||
|
||
```
|
||
저장: 1000 × 50MB = 50GB → 초과 40GB × $0.015 = $0.60/월
|
||
읽기: 캐시 히트율 90% 가정 → 대부분 무료
|
||
Workers: 캐시 히트 시에도 실행됨 → 유료 플랜 권장 ($5/월)
|
||
```
|
||
|
||
## 모니터링
|
||
|
||
### 응답 헤더
|
||
|
||
| 헤더 | 설명 |
|
||
|------|------|
|
||
| `X-Cache: HIT/MISS` | 캐시 상태 |
|
||
| `X-Customer` | 고객 ID |
|
||
| `X-Tier` | 고객 티어 |
|
||
| `X-RateLimit-Reason` | Rate Limit 사유 (rpm/bandwidth) |
|
||
|
||
### 사용량 확인
|
||
|
||
```bash
|
||
# 특정 고객
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://any.actions.it.com/api/usage/customer-name"
|
||
|
||
# 전체 현황
|
||
curl -H "Authorization: Bearer $TOKEN" \
|
||
"https://any.actions.it.com/api/stats"
|
||
```
|
||
|
||
## 확장 계획
|
||
|
||
### 클러스터링 옵션
|
||
|
||
| 방식 | 장점 | 적합한 규모 |
|
||
|------|------|-------------|
|
||
| Incus 단일 노드 | 간단, 저비용 | 현재 |
|
||
| Incus 클러스터 (jp1+kr1) | HA, 마이그레이션 | 중규모 |
|
||
| Kubernetes | 자동 스케일링 | 대규모 |
|
||
|
||
### 결제 연동 (검토 중)
|
||
|
||
| PG | 대상 | 비고 |
|
||
|----|------|------|
|
||
| Toss Payments | 국내 고객 | 보증보험 필요 |
|
||
| Stripe (일본 법인) | 해외/텔레그램 | 직접 연동 가능 |
|
||
|
||
## 크레덴셜 (Vault)
|
||
|
||
```
|
||
Vault: https://vault.anvil.it.com
|
||
Path: secret/cf-multisite
|
||
|
||
- admin_token: Admin API 인증 토큰
|
||
- r2_access_key: R2 API Access Key
|
||
- r2_secret_key: R2 API Secret Key
|
||
```
|
||
|
||
## 관련 링크
|
||
|
||
- Cloudflare Dashboard: https://dash.cloudflare.com
|
||
- Gitea: https://gitea.anvil.it.com
|
||
- 샘플 사이트: https://multisite-demo.actions.it.com
|