高可用部署
用 HAProxy、数据库复制与集群组件在标准部署之上构建高可用
高可用部署消除单点故障,让 API、Worker 与数据层在实例故障时自动恢复。
理论配方
本页是运维参考配方,不是自带部署文件的说明。自带的 Compose/Kubernetes 部署中 PostgreSQL、Redis、Qdrant 均为单实例(带健康检查),API/Worker/Frontend 通过副本扩容。请按下面的配方在标准部署之上自行扩展。
健康检查以 GET /api/v1/health 为准,正常返回 {"code": 0, "data": {"status": "healthy"}, "msg": "success"}。该端点只反映应用进程存活,不探测数据库与 Redis,依赖项状态看日志与可观测性。
负载均衡
HAProxy
option httpchk GET /api/v1/health 必须指向 /api/v1/health,不要用 /health。前端走 roundrobin,WebSocket/流式后端用 leastconn 并关闭缓冲。
backend backend_api
balance roundrobin
option httpchk GET /api/v1/health
http-check expect status 200
server backend1 backend-1:8000 check inter 2000 rise 2 fall 3
server backend2 backend-2:8000 check inter 2000 rise 2 fall 3
server backend3 backend-3:8000 check inter 2000 rise 2 fall 3
backend backend_websocket
balance leastconn
option http-server-close
option forceclose
server backend1 backend-1:8000 check inter 2000 rise 2 fall 3
server backend2 backend-2:8000 check inter 2000 rise 2 fall 3前端 frontend 监听 80 跳转 HTTPS,443 按 path_beg /api 与 Upgrade 头分流到 API/长连接/前端后端。统计页用独立 listen stats 监听 8404。
Keepalived
两台 HAProxy 前加虚拟 IP,主备配置只有 state 与 priority 不同:
vrrp_script chk_haproxy {
script "killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 101
advert_int 1
virtual_ipaddress {
192.168.1.100/24
}
track_script {
chk_haproxy
}
}备机把 state 改为 BACKUP、priority 改为 100,其余相同。认证密码自行替换,不要用示例值。
数据库高可用
PostgreSQL 主从复制
主库开 wal_level = replica,允许复制连接;备库用 pg_basebackup 从主库拉取基线后以 standby.signal 启动:
sudo -u postgres pg_basebackup -h primary-host -D /var/lib/postgresql/14/main -U replicator -v -P -W
sudo -u postgres touch /var/lib/postgresql/14/main/standby.signal主库用 SELECT * FROM pg_stat_replication; 确认从库连接,备库用 SELECT pg_is_in_recovery(); 确认处于恢复模式。
Patroni 自动故障转移
需要自动选主时用 Patroni(3 节点 etcd 做 DCS):关键参数是 ttl: 30、loop_wait: 10、maximum_lag_on_failover: 1048576 与 use_pg_rewind: true。每个 Patroni 节点配独立 name/connect_address/data_dir,复制用户与超级用户密码各自设置。
Redis Sentinel
sentinel.conf 核心只有四行:监控主库、判定下线时间、并行同步数、故障转移超时,并配 auth-pass。
port 26379
sentinel monitor mymaster redis-master 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 10000部署 1 主 2 从 + 3 个 Sentinel。客户端通过 Sentinel 拿主库写、从库读,故障转移后自动切换。
Qdrant 集群
3 节点组成集群,2/3 节点用 QDRANT__CLUSTER__P2P__BOOTSTRAP__URI 指向首节点,P2P 端口 6335 互通。集合创建时指定复制因子:
client.create_collection(
collection_name="documents",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
replication_factor=3,
write_consistency_factor=2,
)无状态应用
多实例 API 共享上传目录(NFS 或对象存储),会话状态放 Redis,不要写本地磁盘。滚动发布时先排空连接再停止进程,给在途请求留出完成时间。
消息集成的高可用
消息集成没有独立进程,跟着 worker 一起做副本:
- 飞书长连接:每个消费
agent队列的 Worker 都会启动接收器监督线程,但只有拿到 Redis 租约(15 秒 TTL、2 秒对账)的那一个真正持有长连接;持有者退出后租约过期,另一个 Worker 在十几秒内接管,不会重复收消息。 - 微信轮询:批次槽位与认领记录都在 PostgreSQL(
WECHAT_POLL_MAX_BATCHES,默认8,每批 30 秒截止、认领 90 秒后可被接管)。任意数量的worker副本都能安全参与,崩溃的批次由其他副本接手。 - Beat:仍然只能一个副本。它每 2 秒从 PostgreSQL 对账集成调度,重启后自动重建条目,不需要手工恢复任务。
沙箱的高可用边界
sandbox-worker 不是可高可用扩展的无状态服务:会话绑定在首次落地的 worker_id 与节点磁盘上,检查点也在同一块磁盘。能做的只有:
- 每个节点运行一个 Sandbox Worker(Compose 单实例、K8s DaemonSet),节点故障只影响该节点上的会话。
- 进程崩溃或重启时从检查点恢复,单个会话默认只允许恢复一次(
SANDBOX_SESSION_MAX_RESETS=1)。 - 把要长期保留的产物上传到对象存储或导出,而不是备份
hostPath;节点或磁盘丢失后这些会话无法在别的节点重建。
容灾与多地域
每天从主库 pg_dump 并压缩上传异地存储;Qdrant 按集合打快照;Redis 只存缓存与队列状态,可选备份。恢复演练必须包含 Qdrant 与上传文件,并记录 RTO/RPO。多地域用 DNS 故障转移(短 TTL + 健康检查)切主备地域。
监控告警
后端没有内置 Prometheus /metrics 端点,告警基于外部探针与管理端可观测接口:健康检查失败、认证异常、队列积压、磁盘耗尽、备份失败必须配告警。LB 与数据库的指标走各自 exporter 或云监控。
故障转移演练
定期停主库验证备库提升、停单个后端验证流量转移。演练清单:备库 pg_is_in_recovery() 返回 false、LB 健康检查仍返回 200、队列无堆积;停一个 worker 后飞书长连接租约在十数秒内由其他副本接管、微信轮询批次继续推进;停一个沙箱节点时预期该节点上的会话不可恢复,其余节点不受影响。
最佳实践
应该做:消除单点、处处健康检查、优雅停机、定期演练、自动化恢复、文档化流程。不要做:依赖单实例、跳过健康检查、强制杀进程、只手动恢复、把流量直连实例。
这篇文章对你有帮助吗?