211 lines
11 KiB
Markdown
211 lines
11 KiB
Markdown
# 配置与后台任务
|
||
|
||
本文列出 API、Worker 和 DbMigrator 当前实际读取的配置。敏感值应通过环境变量、Secret Manager 或部署平台密钥注入,不能提交到仓库。
|
||
|
||
## 进程与依赖
|
||
|
||
| 进程 | PostgreSQL | Redis | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `Tiku.Api` | 必需 | Development 可选;Production 必需 | 提供 HTTP API、认证、授权、FusionCache L1/L2 和 Output Cache |
|
||
| `Tiku.Worker` | 必需 | Development 可选;Production 必需 | 承载周期任务、业务缓存 Backplane 和授权缓存失效重试 |
|
||
| `Tiku.DbMigrator` | 必需 | 不需要 | 执行 Migration、内置目录 seed 和管理员引导;FusionCache 仅使用本地 L1 |
|
||
|
||
Development 未配置 Redis 时,业务 FusionCache 只使用进程内 L1,安全服务使用进程内/数据库防线。Production 不允许 Redis 降级;后台任务在所有环境统一使用 PostgreSQL。
|
||
|
||
## 数据库
|
||
|
||
解析顺序:
|
||
|
||
1. `ConnectionStrings:Database`
|
||
2. `DATABASE_URL`
|
||
3. 仅 Development:`Host=localhost;Database=tiku;Username=<当前系统用户>`
|
||
|
||
DbMigrator 命令:
|
||
|
||
```bash
|
||
dotnet run --project Tiku.DbMigrator
|
||
|
||
dotnet ef migrations script \
|
||
--project Tiku.Infrastructure \
|
||
--startup-project Tiku.DbMigrator
|
||
|
||
dotnet ef migrations has-pending-model-changes \
|
||
--project Tiku.Infrastructure \
|
||
--startup-project Tiku.DbMigrator
|
||
```
|
||
|
||
Production 首次创建平台管理员必须显式执行:
|
||
|
||
```bash
|
||
export TIKU_BOOTSTRAP_PLATFORM_ADMIN_EMAIL='admin@example.com'
|
||
export TIKU_BOOTSTRAP_PLATFORM_ADMIN_PASSWORD='use-a-strong-temporary-password'
|
||
export TIKU_BOOTSTRAP_PLATFORM_ADMIN_NAME='Platform Administrator'
|
||
dotnet run --project Tiku.DbMigrator -- --bootstrap-platform-admin
|
||
```
|
||
|
||
该命令只允许在不存在平台角色用户绑定时执行。管理员首次登录后必须改密。
|
||
|
||
## Redis
|
||
|
||
连接串读取 `ConnectionStrings:Redis` 或 `REDIS_URL`。当前用途:
|
||
|
||
- 密码、短信发送和短信校验的安全窗口计数;
|
||
- `TikuBusiness` FusionCache 的租户目录、Feature 快照和运行时配置 L2;
|
||
- FusionCache Backplane,用于跨 API/Worker 节点驱逐业务 L1;
|
||
- 可选的 Session、安全状态和版本化权限快照缓存;
|
||
- Production 的 ASP.NET Core Output Cache。
|
||
|
||
业务缓存 key 和 Backplane channel 使用 `tiku:<environment>:business:v2` 前缀;配置解析会强制 `AbortOnConnectFail=false`。FusionCache 复用现有 `IConnectionMultiplexer`,不创建额外 Redis 连接。业务缓存禁用 fail-safe 与后台分布式写入,Redis 异常时回退 PostgreSQL;Redis 不是用户、Session、权限、套餐或用量的权威数据源。
|
||
|
||
`TikuBusiness` 的 TTL 和安全边界见[认证、授权与租户隔离](architecture/security-and-tenancy.md#fusioncache-业务缓存)。FusionCache、授权缓存与 Output Cache 是三套不同语义:业务读模型使用 FusionCache,安全状态保留版本化专用实现,HTTP 响应继续由 ASP.NET Core Output Cache 管理。
|
||
|
||
授权缓存由 `Security:AuthorizationCache` 配置,默认 `Mode` 为 `Disabled`。切换为 `Shadow` 或 `Active` 前,应先确认 API 与 Worker 使用同一 Redis 和 PostgreSQL,并观察 `Tiku.Security.AuthorizationCache` 的 mismatch、fallback 与 Redis 延迟指标。回滚只需切回 `Disabled`,不应回退授权版本或失效事件相关数据库结构。详细故障语义见[认证、授权与租户隔离](architecture/security-and-tenancy.md#redis-授权缓存模式)。
|
||
|
||
## Worker 与后台处理配置
|
||
|
||
```json
|
||
{
|
||
"Worker": {
|
||
"Enabled": true,
|
||
"JobPollSeconds": 2,
|
||
"JobParallelism": 4,
|
||
"JobBatchSize": 5
|
||
},
|
||
"TenantDomains": {
|
||
"Enabled": true,
|
||
"PollSeconds": 60,
|
||
"BatchSize": 50,
|
||
"DnsJsonEndpoint": "https://cloudflare-dns.com/dns-query",
|
||
"VerificationRecordPrefix": "_tiku-verification",
|
||
"AllowedCnameTargets": [],
|
||
"GatewayBaseUrl": null,
|
||
"GatewayApiKey": null
|
||
},
|
||
"TenantProvisioning": {
|
||
"DefaultBaseOfferingCode": "starter",
|
||
"DefaultTrialDays": 14,
|
||
"OwnerActivationMinutes": 30
|
||
},
|
||
"SaasSubscriptions": {
|
||
"Enabled": true,
|
||
"BatchSize": 100,
|
||
"PastDueGraceDays": 7
|
||
},
|
||
"FeatureUsageReconciliation": {
|
||
"Enabled": true,
|
||
"BatchSize": 100,
|
||
"IntervalMinutes": 60
|
||
},
|
||
"CommercialBilling": {
|
||
"Enabled": true,
|
||
"BatchSize": 100,
|
||
"AllowedWebhookHosts": []
|
||
}
|
||
}
|
||
```
|
||
|
||
域名只有在 `AllowedCnameTargets`、DNS JSON endpoint、Gateway URL 和 API key 配置完成后,才可能从 Pending/Failed 进入 Active。仅 DNS 验证成功不代表 TLS 已就绪。
|
||
|
||
`TenantProvisioning:DefaultBaseOfferingCode` 必须指向 Active 基础套餐的有效 Published 版本。Production 启动与租户开通事务都会校验,缺失时返回 `default_offering_unavailable`。完整流程见[空数据库到租户建站验收](tenant-provisioning.md)。
|
||
|
||
后台任务状态和 `RunAfter` 存在 PostgreSQL。`BackgroundJobsWorker` 使用 `FOR UPDATE SKIP LOCKED`、五分钟租约和有限重试,允许多个 Worker 实例并行消费;其他周期处理器使用 PostgreSQL advisory lock 防止重复执行。通用任务由模块注册的 `IBackgroundJobHandler` 处理,Jobs 模块负责查找、租约、重试和失败补偿。七个循环处理域名、订阅、Feature 用量、通用任务、授权缓存失效、商业账务和平台审批执行。`Worker:Enabled=false` 会关闭全部循环。
|
||
|
||
API 和 Worker 必须使用同一 PostgreSQL 数据库与一致的对象存储配置。迁移必须在两者启动前由 `Tiku.DbMigrator` 单独执行。
|
||
|
||
容器镜像从仓库根目录构建:
|
||
|
||
```bash
|
||
docker build -f Tiku.Api/Dockerfile -t tiku-api .
|
||
docker build -f Tiku.Worker/Dockerfile -t tiku-worker .
|
||
```
|
||
|
||
API 和 Worker 应独立设置副本数与资源限制。发布顺序固定为 `Tiku.DbMigrator` → `Tiku.Worker` → `Tiku.Api` → `Tiku.PlatformAdmin.Web`;不要在容器入口自动执行 Migration。
|
||
|
||
## 发布门禁、备份恢复和告警
|
||
|
||
`.gitea/workflows/ci.yaml` 执行 restore、build、完整测试、format、EF 模型漂移、幂等 Migration SQL、平台前端检查、OpenAPI 生成漂移、API/Worker 镜像构建和 `git diff --check`。本地发布前应执行相同检查。
|
||
|
||
PostgreSQL 恢复演练使用标准 `PGHOST`、`PGPORT`、`PGUSER`、`PGPASSWORD` 环境变量,并通过 `TIKU_BACKUP_SOURCE_DATABASE` 指定源数据库:
|
||
|
||
```bash
|
||
TIKU_BACKUP_SOURCE_DATABASE=tiku scripts/rehearse-postgres-restore.sh
|
||
```
|
||
|
||
脚本只创建并删除名称受限的 `tiku_restore_drill_*` 临时数据库,校验 Migration 历史与租户表后自动清理。生产演练仍应在隔离实例上执行,并把耗时、备份大小和恢复点记录到变更单。
|
||
|
||
`deploy/monitoring/postgres-exporter-queries.yaml` 提供 Worker stale、最老 Pending Job、支付回调失败、逾期应收和催缴死信指标;`deploy/monitoring/commercial-alerts.yaml` 提供对应 Prometheus 告警。部署时必须让 PostgreSQL exporter 使用只读监控账号,并先在预发布验证规则名称与采集前缀。
|
||
|
||
## 安全与网络配置
|
||
|
||
Production 启动至少需要核对:
|
||
|
||
| 配置 | 作用 |
|
||
| --- | --- |
|
||
| `Security:Jwt` | issuer、audience、当前 key ID、RSA 私钥和验证公钥 |
|
||
| `Security:DataProtection` | application name、X509 证书路径和密码 |
|
||
| `Security:TenantSecrets` | key ID 和 Base64 编码的 32 字节 master key |
|
||
| `Authentication:Sms` | 至少 32 字符的验证码 pepper 和频控阈值 |
|
||
| `Tenancy:Resolution` | 正式平台 Host、可信代理、tenant code 允许路径 |
|
||
| `AllowedHosts` | 非通配 Host allowlist |
|
||
| `Cors` | 明确的 Origin、Header、Method 和凭据策略 |
|
||
| `BrowserAuth:AllowedOrigins` | 允许使用 Browser Auth 的 HTTP(S) Origin |
|
||
| `RateLimiting` | 全局和认证端点限流 |
|
||
|
||
可用环境变量覆盖包括:
|
||
|
||
- `TIKU_DATA_PROTECTION_APPLICATION_NAME`
|
||
- `TIKU_DATA_PROTECTION_CERTIFICATE_PATH`
|
||
- `TIKU_DATA_PROTECTION_CERTIFICATE_PASSWORD`
|
||
- `TIKU_TENANT_SECRET_KEY_ID`
|
||
- `TIKU_TENANT_SECRET_MASTER_KEY`
|
||
- `TIKU_SMS_CODE_PEPPER`
|
||
|
||
不要在命令输出、文档、Git diff 或错误报告中粘贴这些值。
|
||
|
||
## 对象存储与外部 Provider
|
||
|
||
对象存储读取 `Storage`、`Storage:AliyunOss` 与 `Storage:S3Compatible`。API 与 Worker 支持 `STORAGE_*`、`ALIYUN_OSS_*` 以及 `S3_ENDPOINT`、`S3_ACCESS_KEY`、`S3_SECRET_KEY`、`S3_REGION`、`S3_SECURE` 环境变量。Production 默认并强制使用已配置的阿里云 OSS;非 Production 没有完整 OSS 凭据时回退到 `local_dev`,该 Provider 使用 S3 兼容存储,快速部署环境由 MinIO 提供。所有托管 Provider 都强制租户 key 前缀、上传大小和 MIME allowlist。租户导出使用 `application/gzip`,该类型不能从 allowlist 删除。
|
||
|
||
## ClamAV 资源安全扫描
|
||
|
||
API 和 Worker 都读取 `Security:ClamAV`:
|
||
|
||
```json
|
||
{
|
||
"Security": {
|
||
"ClamAV": {
|
||
"Host": "clamav",
|
||
"Port": 3310,
|
||
"TimeoutSeconds": 30,
|
||
"ChunkBytes": 65536,
|
||
"StreamMaxLength": 524288000
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Worker 使用 ClamAV `INSTREAM` 协议,不在本地落盘待扫描对象。启动校验要求 `StreamMaxLength >= Storage:MaxUploadBytes`;同时必须把 clamd 自身的 `StreamMaxLength` 配到相同或更高值。扫描结果为 FOUND 时资源标记为 Failed 并记录病毒签名;ClamAV 超时或不可用时任务保留 Pending 并退避重试。只有 `Passed` 或系统明确标记为可信的 `NotRequired` 资源可以签发访问地址。
|
||
|
||
身份、短信、支付、通知和 AI 的租户配置由业务后台写入 `TenantExternalProvider`;敏感值写入加密的 `TenantSecret`。全局默认配置不能绕过租户 Provider 状态和 Secret 边界。
|
||
|
||
## 健康检查与观测
|
||
|
||
- `GET /api/system/health`:轻量 liveness,只说明 API 进程可响应。
|
||
- `GET /api/system/health/ready`:检查 PostgreSQL 和已配置 Redis;依赖未就绪时返回 503,匿名响应只包含总体状态和检查时间。
|
||
- `GET /api/platform/operations/health`:需要 `platform:operations:view`,返回 PostgreSQL、Redis、Worker heartbeat、ClamAV 和对象存储配置状态。
|
||
- `GET /api/platform/operations/workers`:查询 Worker 心跳、周期循环和 stale 状态。
|
||
- `GET /api/platform/operations/job-metrics`:查询队列状态、最老 Pending 任务与过期租约。
|
||
- 设置 `OpenTelemetry:OtlpEndpoint` 后导出 ASP.NET Core、HTTP client、数据库和 FusionCache 的 hit/miss、L1/L2、工厂、Backplane 指标与 trace。
|
||
- Serilog 输出结构化请求日志;数据库性能拦截器记录慢查询指标。
|
||
|
||
Readiness 为绿色不等于认证授权、跨租户隔离或后台任务恢复演练已通过,发布仍需执行对应集成测试。
|
||
|
||
## 租户归档与导出发布门禁
|
||
|
||
- 租户归档是逻辑归档,禁止硬删除。
|
||
- 归档前必须存在 24 小时内成功完成的租户导出,且不能有 Processing 后台任务。
|
||
- 导出包是 `tar.gz`,包含 manifest、租户、成员、域名和资源元数据;明确排除密码哈希、令牌、密钥明文、Data Protection keys 和全局平台数据。
|
||
- 归档会撤销该租户授权域 Session、禁用域名并失效运行时缓存;恢复后租户为 `Suspended`,域名为 `Pending`,必须重新审核后再激活。
|
||
- Owner 转移目标必须是已有 Active 成员,并在同一事务内同步成员角色和后台角色绑定。
|
||
- 发布前至少演练一次导出可下载、归档阻断条件、归档、恢复和 Owner 转移。
|