2023 年初,Slack 面对一个底层问题:如何在企业级规模上服务大语言模型,同时满足客户对安全、可靠性和性能的预期。三年间,他们从最基础的基础设施起步,逐步演进到一套成熟的多云编排架构。这不是追逐新模型的炫技,而是要构建一个能抵御区域级故障、在云厂商之间灵活调度的系统。
从单云到多云:为什么不能只靠一家
LLM 推理的负载特征和传统 Web 服务截然不同——单次请求耗 GPU 算力大、延迟敏感、流量波动剧烈。当 Slack 开始把 AI 功能(如摘要、搜索增强)推向所有企业客户时,单云部署很快暴露出两个硬伤:
- 区域故障即全局故障。一家云的区域宕机,所有 AI 功能随之停摆,对 Slack 这种工作流中枢产品不可接受。
- GPU 供给瓶颈。单一云厂商的 GPU 实例库存有限,高峰期拿不到机器,推理队列堆积。
多云不是"多花点钱买冗余"那么简单。它意味着推理请求要在不同云之间路由、模型权重要跨区域同步、监控和回滚策略要统一——编排复杂度陡然上升。
推理服务的编排:把模型当作可迁移的工作负载
Slack 的核心思路是:把 LLM 推理当作一种可跨云迁移的有状态工作负载,而不是绑定在某家云的托管服务上。这要求:
- 模型权重存储与分发独立于推理集群。用对象存储(如 S3/GCS)做权重仓库,推理节点启动时拉取,而非把权重 baked 进镜像。
- 请求路由层感知每朵云的健康与容量。不是简单轮询,而是根据延迟、可用 GPU 数、区域距离做加权调度。
- 统一的可观测性。不同云的日志、指标要汇聚到同一套监控栈,否则故障发生时你根本不知道问题在哪。
下面是一个简化版的多云推理路由配置思路,用 YAML 描述推理集群的拓扑,供参考和改造:
# multi_cloud_inference_topology.yaml
# 描述多云推理集群的端点与权重,供路由层消费
clusters:
- name: aws-us-west-2
provider: aws
region: us-west-2
endpoint: https://infer-usw2.internal.slack.ai/v1/chat
gpu_type: a10g
max_concurrency: 200
weight: 40 # 正常状态时承担 40% 流量
priority: 1 # 优先级,故障切换参考
- name: gcp-us-central1
provider: gcp
region: us-central1
endpoint: https://infer-usc1.internal.slack.ai/v1/chat
gpu_type: l4
max_concurrency: 180
weight: 35
priority: 2
- name: aws-eu-west-1
provider: aws
region: eu-west-1
endpoint: https://infer-euw1.internal.slack.ai/v1/chat
gpu_type: a10g
max_concurrency: 120
weight: 25
priority: 3
health_check:
interval_seconds: 10
timeout_seconds: 3
failure_threshold: 3 # 连续 3 次失败则摘除
failover:
strategy: weighted_rebalance # 摘除后按剩余权重重新分配
cooldown_seconds: 120 # 摘除后冷却 2 分钟再尝试恢复
路由层可以是一个轻量 Go 服务或 Python FastAPI 应用,定期读取这份拓扑配置,结合健康检查结果做实时调度。实际生产中,这份配置通常存在分布式 KV 存储(如 Consul)里,支持热更新。
模型权重的跨云同步:推理启动的零等待设计
推理节点冷启动时拉取模型权重是最大的延迟陷阱。一个 7B 参数模型的权重文件约 14 GB(FP16),跨区域下载动辄数分钟。Slack 的做法:
- 预热缓存。在每朵云的对象存储中保持一份最新权重副本,推理节点从同云同区域的存储拉取,不走跨云链路。
- 增量更新。当模型微调后只变更部分权重层(如 LoRA adapter),只同步 adapter 文件(几十 MB 到几百 MB),而非全量权重。
- 启动探针。推理节点在权重加载完成前不接入路由,避免用户请求打到还没 ready 的实例上。
一个用 Python 实现的权重同步与推理启动检查脚本骨架:
#!/usr/bin/env python3
"""
权重预热与推理就绪检查脚本(简化版)
假设权重存储在同云 S3 兼容对象存储中
"""
import os
import time
import boto3
import requests
MODEL_BUCKET = os.getenv("MODEL_BUCKET", "slack-ai-models-usw2")
MODEL_KEY = os.getenv("MODEL_KEY", "llm/v2.1/full_weights.bin")
LOCAL_PATH = "/opt/models/full_weights.bin"
ROUTER_REGISTER_URL = os.getenv("ROUTER_REGISTER_URL", "http://router.internal:8080/register")
HEALTH_TIMEOUT = 300 # 权重下载+加载最长等待 5 分钟
def download_weights():
"""从同区域对象存储拉取权重"""
s3 = boto3.client("s3", region_name=os.getenv("AWS_REGION", "us-west-2"))
print(f"Downloading {MODEL_KEY} from bucket {MODEL_BUCKET}...")
s3.download_file(MODEL_BUCKET, MODEL_KEY, LOCAL_PATH)
size_mb = os.path.getsize(LOCAL_PATH) / (1024 * 1024)
print(f"Download complete: {size_mb:.1f} MB")
def wait_for_inference_ready():
"""等待本地推理服务加载权重并就绪"""
infer_url = os.getenv("INFER_LOCAL_URL", "http://localhost:8000/health")
start = time.time()
while time.time() - start < HEALTH_TIMEOUT:
try:
r = requests.get(infer_url, timeout=2)
if r.status_code == 200 and r.json().get("model_loaded"):
print("Inference service is ready.")
return True
except requests.RequestException:
pass
time.sleep(5)
raise RuntimeError("Inference service did not become ready within timeout")
def register_with_router():
"""向路由层注册本节点,开始接收流量"""
payload = {
"instance_id": os.getenv("INSTANCE_ID", "infer-usw2-001"),
"endpoint": os.getenv("PUBLIC_ENDPOINT", "https://infer-usw2.internal.slack.ai/v1/chat"),
"capacity": int(os.getenv("MAX_CONCURRENCY", "200")),
}
r = requests.post(ROUTER_REGISTER_URL, json=payload, timeout=5)
print(f"Registered with router: status={r.status_code}")
if __name__ == "__main__":
download_weights()
wait_for_inference_ready()
register_with_router()
运行前设置环境变量:
export MODEL_BUCKET=slack-ai-models-usw2
export MODEL_KEY=llm/v2.1/full_weights.bin
export AWS_REGION=us-west-2
export ROUTER_REGISTER_URL=http://router.internal:8080/register
export INSTANCE_ID=infer-usw2-001
python3 weight_sync_and_register.py
多云的代价:运维复杂度与一致性博弈
多云不是免费的午餐。Slack 在演进过程中需要持续应对:
- 配置漂移。两朵云上的推理框架版本、CUDA 驱动、网络策略稍有差异,行为就可能不一致。需要统一的 CI/CD 管线把同一套推理镜像部署到所有云。
- 成本追踪。GPU 实例价格在不同云、不同区域波动大。没有细粒度的成本标签和流量归因,财务团队根本看不清钱花在哪。
- 安全合规。企业客户的数据在哪个云、哪个区域被处理,必须可审计。跨云数据传输要满足 GDPR 等法规的边界约束。
Slack 的经验是:先在单云上把推理服务做到稳定可观测,再逐步引入第二朵云。跳过单云成熟阶段直接上多云,往往是在两个云上同时踩坑。
落地建议
如果你也在考虑 LLM 推理的多云架构,可以按这个节奏推进:
- 单云站稳——推理服务在单云上跑满一个季度,延迟 P99、错误率、GPU 利用率都有清晰基线。
- 抽象路由层——在用户请求和推理集群之间加一层路由,支持按权重和健康状态调度,这是多云的前提。
- 权重存储解耦——推理镜像不含权重,启动时从对象存储拉取,为跨云迁移扫清障碍。
- 引入第二朵云——先承载 10-20% 流量做影子验证,确认模型输出一致、延迟可接受后逐步提升权重。
- 统一监控与告警——所有云的指标汇聚到同一套 Prometheus/Grafana 或等效栈,故障发现和定位不能靠人肉跳板。
多云架构的目标不是"每个云都跑满",而是在任何一朵云出问题时,用户感知不到中断。Slack 三年的路径说明:这件事值得做,但必须一步步来,每一步都要有数据支撑。