Files
cf-multisite/README.md
kappa 8850031c45
Some checks failed
Deploy to CF Multisite / deploy (push) Failing after 1m53s
Initial commit: CF Multisite 멀티테넌트 정적 호스팅
- 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>
2026-01-29 09:20:46 +09:00

359 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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