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

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 토큰 인증 필요:

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 고객 데이터 삭제

사용 예시

# 고객 사용량 조회
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. 프로젝트 클론

cd ~/projects
git clone <repo> cf-multisite
cd cf-multisite
npm install

2. Cloudflare 설정

# wrangler 로그인
npx wrangler login

# KV 네임스페이스 생성
npx wrangler kv:namespace create USAGE

# R2 버킷 생성 (이미 있으면 스킵)
npx wrangler r2 bucket create multisite-bucket

3. wrangler.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. 시크릿 설정

# Admin API 토큰 생성 및 설정
openssl rand -hex 32  # 토큰 생성
npx wrangler secret put ADMIN_TOKEN

5. 배포

npm run deploy
# 또는
npx wrangler deploy

고객 사이트 추가

1. Gitea 저장소 생성

고객용 저장소를 Gitea에 생성합니다.

2. Workflow 파일 추가

.gitea/workflows/deploy.yml:

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 → 자동 배포

git add .
git commit -m "Initial site"
git push origin main
# → https://{repo-name}.actions.it.com 에서 확인

로컬 개발

# 개발 서버 실행
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)

사용량 확인

# 특정 고객
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

관련 링크

Description
Cloudflare Workers + R2 멀티테넌트 정적 호스팅
Readme MIT 73 KiB
Languages
JavaScript 78.4%
HTML 13.6%
CSS 8%