Cloudflare R2 + 香港服务器混合存储方案:零出口流量费CDN分发与成本优化

Cloudflare R2 + 香港服务器混合存储方案:零出口流量费CDN分发与成本优化

存储需求分两类:高频访问的静态资源(产品图、JS/CSS),和低频访问的归档数据(备份、历史日志)。把所有数据都放同一地方,要么贵(AWS S3 出口流量费),要么慢(本地服务器全球访问延迟高)。本文构建一套混合存储架构:热数据放 Cloudflare R2(零出口流量费 + 全球 CDN),冷数据放香港服务器本地,按数据温度分层存储,将存储总成本降低 60% 以上。


一、存储方案成本横向对比

方案存储费(1TB/月)出口流量费(1TB/月)全球访问速度
AWS S3 标准$23$90优秀
阿里云 OSS(香港)¥130¥0.5/GB(¥500)良好
Cloudflare R2$15(超出10GB免费后)$0(永久免费!)优秀(CDN)
香港 VPS 本地(MinIO)磁盘费用分摊(约¥50)含在 VPS 套餐良好(亚太)
R2 + VPS 混合$15+¥50$0优秀

二、混合存储架构设计

<code">
数据分层策略:

热数据(R2 + Cloudflare CDN)
    ├── 产品图片(高频全球访问)
    ├── 前端静态资源(JS/CSS/字体)
    ├── 用户上传的公开文件
    └── 营销素材

冷/温数据(香港 VPS 本地)
    ├── 数据库备份(每日)
    ├── 应用日志(30天保留)
    ├── 开发资源(私有不公开)
    └── 超大文件(视频原始素材)

访问路径:
用户 → Cloudflare CDN(全球边缘)→ R2 存储(热数据,近乎零延迟)
用户 → 香港服务器(冷数据,需要认证的私有文件)

三、创建 Cloudflare R2 Bucket

<code">
在 Cloudflare Dashboard 操作:

1. 登录 dashboard.cloudflare.com
2. 左侧菜单 → R2 Object Storage
3. Create bucket
   - Bucket name: myapp-assets
   - Location: Automatic(Cloudflare 自动选择最近节点)

4. 创建 API Token(用于程序访问)
   R2 → Manage R2 API tokens → Create API token
   - Permissions: Object Read & Write
   - Specify bucket: myapp-assets
   复制:Account ID / Access Key ID / Secret Access Key

5. 设置自定义域名(让图片通过 CDN 分发)
   Bucket → Settings → Custom Domains
   → 添加 assets.yourdomain.com
   (在 Cloudflare DNS 中会自动创建 CNAME 记录)

四、Python 代码:智能存储路由

<code">import boto3
import os
from botocore.config import Config
from enum import Enum

class StorageTier(Enum):
    HOT = "r2"          # 高频访问,R2 存储
    COLD = "local"      # 低频访问,本地 VPS

# R2 客户端(S3 兼容 API)
r2 = boto3.client(
    's3',
    endpoint_url=f'https://{os.getenv("CF_ACCOUNT_ID")}.r2.cloudflarestorage.com',
    aws_access_key_id=os.getenv('R2_ACCESS_KEY_ID'),
    aws_secret_access_key=os.getenv('R2_SECRET_ACCESS_KEY'),
    config=Config(signature_version='s3v4'),
    region_name='auto'
)

# 本地 MinIO 客户端(香港服务器)
local = boto3.client(
    's3',
    endpoint_url='https://storage.yourdomain.com',
    aws_access_key_id=os.getenv('MINIO_ACCESS_KEY'),
    aws_secret_access_key=os.getenv('MINIO_SECRET_KEY'),
    config=Config(signature_version='s3v4'),
    region_name='us-east-1'
)

def upload_file(
    file_path: str,
    object_key: str,
    tier: StorageTier = StorageTier.HOT,
    content_type: str = 'application/octet-stream',
    public: bool = False
) -> str:
    """上传文件到对应存储层,返回访问 URL"""

    extra_args = {
        'ContentType': content_type,
        'CacheControl': 'public, max-age=31536000' if public else 'private'
    }
    if public:
        extra_args['ACL'] = 'public-read'

    if tier == StorageTier.HOT:
        r2.upload_file(file_path, 'myapp-assets', object_key, ExtraArgs=extra_args)
        return f"https://assets.yourdomain.com/{object_key}"   # CDN URL
    else:
        local.upload_file(file_path, 'backups', object_key, ExtraArgs=extra_args)
        return f"https://storage.yourdomain.com/backups/{object_key}"

def smart_upload(file_path: str, object_key: str, file_size_mb: float) -> str:
    """根据文件特征自动选择存储层"""
    ext = os.path.splitext(file_path)[1].lower()

    # 图片/静态资源:热存储(CDN 分发)
    if ext in ['.jpg', '.jpeg', '.png', '.webp', '.svg', '.css', '.js', '.woff2']:
        return upload_file(file_path, object_key, StorageTier.HOT,
                          public=True, content_type=f'image/{ext[1:]}')

    # 视频:根据大小分层(大文件放本地,小视频放 R2)
    if ext in ['.mp4', '.mov']:
        tier = StorageTier.COLD if file_size_mb > 500 else StorageTier.HOT
        return upload_file(file_path, object_key, tier, 'video/mp4')

    # 备份文件:冷存储(本地)
    if ext in ['.sql', '.tar', '.gz', '.zip']:
        return upload_file(file_path, object_key, StorageTier.COLD)

    # 默认:热存储
    return upload_file(file_path, object_key, StorageTier.HOT)

五、Django/Flask 集成示例(产品图上传)

<code">from django.core.files.storage import Storage
import boto3

class R2Storage(Storage):
    """Django 自定义存储后端(R2)"""

    def __init__(self):
        self.client = boto3.client(
            's3',
            endpoint_url=f'https://{CF_ACCOUNT_ID}.r2.cloudflarestorage.com',
            aws_access_key_id=R2_ACCESS_KEY_ID,
            aws_secret_access_key=R2_SECRET_ACCESS_KEY,
            region_name='auto'
        )
        self.bucket = 'myapp-assets'
        self.cdn_url = 'https://assets.yourdomain.com'

    def _save(self, name, content):
        self.client.upload_fileobj(
            content,
            self.bucket,
            name,
            ExtraArgs={
                'ContentType': content.content_type,
                'CacheControl': 'public, max-age=31536000',
            }
        )
        return name

    def url(self, name):
        return f"{self.cdn_url}/{name}"   # 返回 CDN URL

# settings.py
DEFAULT_FILE_STORAGE = 'myapp.storage.R2Storage'

六、备份数据自动归档到本地

<code"># 定时备份脚本:数据库 → 本地 MinIO(冷存储)
cat > /opt/backup/r2-cold-archive.sh << 'EOF' #!/bin/bash DATE=$(date +%Y%m%d_%H%M) # 1. 数据库备份 mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases \ | gzip > /tmp/db-backup-$DATE.sql.gz

# 2. 上传到本地 MinIO(冷存储,香港服务器)
mc cp /tmp/db-backup-$DATE.sql.gz local/backups/mysql/
rm /tmp/db-backup-$DATE.sql.gz

# 3. 清理60天前的备份
mc find local/backups/mysql/ --older-than 60d --exec "mc rm {}"

echo "[$DATE] 备份完成并归档到冷存储"
EOF
chmod +x /opt/backup/r2-cold-archive.sh
echo "0 3 * * * /opt/backup/r2-cold-archive.sh >> /var/log/backup.log 2>&1" >> /etc/crontab

七、R2 访问分析

<code"># R2 内置了详细的请求分析(在 Cloudflare Dashboard 查看)
# 可以看到:请求量、带宽、缓存命中率、热门对象

# 通过 Cloudflare Workers 实现访问控制(需要鉴权的资源)
# Workers 脚本示例:验证 Token 后才允许访问 R2 对象
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const token = request.headers.get('Authorization');

    // 验证 Token
    if (!token || token !== `Bearer ${env.ACCESS_TOKEN}`) {
      return new Response('Unauthorized', { status: 401 });
    }

    // 从 R2 获取对象
    const object = await env.MY_BUCKET.get(url.pathname.slice(1));
    if (!object) return new Response('Not Found', { status: 404 });

    return new Response(object.body, {
      headers: { 'Content-Type': object.httpMetadata.contentType }
    });
  }
};

八、每月成本估算(1TB 热数据 + 500GB 冷数据)

费用项目金额说明
Cloudflare R2 存储(1TB)$15/月超出 10GB 免费后 $0.015/GB
R2 出口流量$0永久免费!
R2 API 操作(写入)~$1$4.50/百万次 A 类操作
香港 VPS(含 500GB 磁盘)¥150/月本地冷存储
合计约¥260/月含全球 CDN 分发
对比 AWS S3(同等规模)约¥900/月含出口流量费

九、总结

R2 + 香港 VPS 混合存储将存储成本降低约 70%,核心原因是 R2 的零出口流量费——传统对象存储的成本大头往往不是存储费而是流量费。热数据放 R2 享受 Cloudflare 全球 CDN 加速,冷数据放香港 VPS 本地低成本归档,两者通过 S3 兼容 API 统一管理,业务代码无缝切换。

Telegram