ClouisleClouisle

高可用部署

用 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 后飞书长连接租约在十数秒内由其他副本接管、微信轮询批次继续推进;停一个沙箱节点时预期该节点上的会话不可恢复,其余节点不受影响。

最佳实践

应该做:消除单点、处处健康检查、优雅停机、定期演练、自动化恢复、文档化流程。不要做:依赖单实例、跳过健康检查、强制杀进程、只手动恢复、把流量直连实例。

这篇文章对你有帮助吗?

本页目录