系统架构
本章从部署拓扑、组件职责、运行时依赖与子系统间数据流等角度,完整描述 Cirrus CDN 的系统架构。
总体架构
Cirrus CDN 由四个核心层组成:
- 控制平面——基于 FastAPI 的应用(
src/cirrus/app.py),提供配置与自动化的 REST 接口。 - 数据平面——基于 OpenResty 的反向代理(
openresty/),负责服务终端流量、执行缓存并输出指标。 - 自动化底盘——Celery worker/beat 进程(
src/cirrus/celery_app.py),承担 ACME 申请、证书续签与节点健康检查。 - 可观测性与运维工具——Prometheus、Grafana、logrotate 以及在
docker-compose.yml中定义的支撑服务。
Redis 作为各层共享的状态存储与发布订阅总线。
+-------------------+
| Frontend |
| Next.js (SPA) |
+-------------------+
|
v
+-------------------+
| FastAPI App |
| (src/cirrus/) |
+-------------------+
|
Redis <------> Celery Workers/Beat
|
v
+----------------------------------+
| OpenResty Edge (Data Plane) |
+----------------------------------+
|
v
End-user Requests
控制与数据平面
| 控制平面 | 数据平面 |
|---|---|
| 配置中心(Redis) | 内容缓存与边缘路由 |
| 分发管道(Redis 发布订阅 + Celery 队列) | 健康检测与自治修复 |
| 调度引擎/区域构建 | 日志回传与指标导出 |
高可用性设计
- 多区域多活(独立边缘节点与权威 DNS 从节点)
- 零中断发布(模板化 Compose/Ansible,蓝绿/滚动)
- 自动容灾(NOTIFY 驱动的从节点 + 健康感知激活)
- 自愈循环(Celery 健康检查驱动
active切换并触发区域重建)
部署拓扑
docker-compose.yml 定义了本地场景下的服务编排。关键服务包括:
redis:启用 AOF 持久化的主数据存储(services.redis)。api:由Dockerfile第二阶段构建的 FastAPI 服务,挂载./src为只读,并封装静态化的 Next.js 导出内容。openresty:基于openresty/Dockerfile的自定义镜像,使用宿主网络直接暴露 8080/8443 端口。worker与beat:与 API 共享 Python 基础镜像的 Celery worker/beat 进程。caddy:用于本地证书签发的内部 CA 与 ACME 目录。acmedns:负责 ACME DNS-01 验证的权威服务器。nsd:从隐藏主节点接收 NOTIFY 的权威 DNS 从节点。prometheus、grafana、logrotate与fakedns:运维支撑系统。
api、worker、beat、caddy、acmedns 与 nsd 共同接入自定义的 acmetest bridge 网络,并通过静态 IP 简化 DNS/MX 信任关系。
构建与运行产物
Python 与前端镜像(Dockerfile)
- 第一阶段执行
pnpm build构建 Next.js 前端,并利用缓存挂载加速.next产物。 - 第二阶段(
ghcr.io/astral-sh/uv:python3.13-trixie-slim)通过uv sync安装 Python 依赖(遵循uv.lock),将静态导出复制至/app/static,并配置入口脚本(docker/entrypoint.sh)在调用uvicorn前修复权限并按需复制 CA 证书。
OpenResty 镜像(openresty/Dockerfile)
- 构建阶段安装
lua-resty-http、nginx-lua-prometheus并编译nginx_cache_multipurge。 - 模板阶段依据构建参数(如
NGX_HTTP_PORT、NGX_METRICS_ALLOW)渲染openresty/conf/nginx.conf.j2。 - 运行阶段预置占位 TLS 证书、拷贝 Lua 脚本并以前台模式启动
openresty。
Prometheus 镜像(prometheus/Dockerfile)
Prometheus 镜像根据环境加载 prometheus.dev.yml 或 prometheus.prod.yml,默认抓取 127.0.0.1:9145 的 OpenResty 指标。
配置与状态管理
Redis 是唯一可信源。核心键空间如下:
| 键模式 | 作用 |
|---|---|
cdn:domains(集合) | 所有受管域名。 |
cdn:dom:{domain} | JSON 编码的域名配置(DomainConf)。 |
cdn:nodes(集合) | 已知边缘节点 ID。 |
cdn:node:{id} | 保存节点 IP、健康计数与激活状态的哈希。 |
cdn:cert:{domain} | TLS 资产(完整链 PEM、私钥、签发时间)。 |
cdn:acme:{domain} | ACME 注册状态,包括 acme-dns 凭据。 |
cdn:acme:lock:{domain} / cdn:acme:task:{domain} | 证书任务并发锁。 |
cdn:tokens、cdn:token:{id}、cdn:token_hash:{hash} | 服务令牌注册与检索。 |
cdn:acmeacct:global | 共享的 ACME 账号密钥材料。 |
cdn:acmecertkey:{domain} | 复用续签的证书私钥。 |
发布订阅通道包括 cdn:cname:dirty(触发 DNS 区域重建)与 cdn:purge(缓存失效通知)。
数据流概览
- 域名接入——前端向
/api/v1/domains/{domain}发起 POST;API 将配置写入 Redis;DNS 服务重建区域;OpenResty 在后续请求中加载配置。 - 节点生命周期——操作人员向
/api/v1/nodes提交列表;Redis 更新节点哈希;触发 DNS 重建;Celery 健康检查调整active状态。 - 缓存清除——API 向
cdn:purge发布 JSON 消息;OpenResty 订阅者调用cache_multipurge执行 PURGE。 - TLS 签发——API 入队 Celery 任务;worker 申请/续签证书并写入 Redis;OpenResty 在 SNI 协商时动态加载。
依赖与集成
- Redis(Python 使用
redis.asyncio,Lua 使用resty.redis)——作为配置存储、缓存与发布订阅通道。 - Celery——使用 Redis 作为 broker/backend 的异步任务执行引擎。
- acme-dns——处理 ACME 的 DNS-01 验证。
- Caddy——托管本地 ACME CA;Celery worker 通过
docker/entrypoint.sh复制 CA 证书实现信任。 - NSD——接收
HiddenMasterServerNOTIFY 的权威 DNS 从节点。 - Prometheus 与 Grafana——指标采集与可视化。
环境
Docker 组合主要面向本地开发;生产环境依赖 just deploy 调用的 Ansible 剧本(见 ansible/)。环境变量(如 CNAME_BASE_DOMAIN、ACME_DIRECTORY、DNS_MASTER_PORT)需按环境调整。第 11 章列出了完整清单。
可扩展性考量
- API/服务进程除内存会话缓存外是无状态的,如将会话机制迁移至 Redis,可横向扩容(参见第 11 章)。
- OpenResty 可通过
/api/v1/nodes注册更多节点实现扩容;一致性哈希保证域名在节点 churn 下的稳定性。 - DNS 隐藏主节点仍为单实例;NSD 可横向扩展以满足区域冗余。
- Redis 是单点;生产工作负载可考虑托管或集群部署。
本章之后将逐层展开,首先是第 3 章的控制平面 API。