多门店运维闭环全景架构:监控+告警+工单+SLA+复盘,一套最小可用系统怎么串起来
如果你是一个连锁奶茶品牌的运维工程师,每天面对全国 200 家门店的监控告警,你会怎么做? 今天我们不聊大厂那套复杂的 Prometheus + Grafana + PagerDuty 全栈方案,就讲一个最小可用、能跑通闭环的系统怎么搭起来。 我会用 Python 和简单技术栈,把“监控 → 告警 → 工单 → SLA → 复盘”这条链完整串给你看。—## 一、闭环是什么?为什么需要闭环?先想一个场景: 门店 A 的冰柜温度报警了,监控系统发了消息,运维看一眼,哦,好了。 但第二天同样的问题又来了,第三天还来。没人知道到底修没修好,也没人知道维修花了多久。这就是没有闭环——监控只管告警,没人跟踪处理结果。而闭环架构要解决的是:1.监控:发现异常(温度、门禁、网络)2.告警:通知到人(钉钉/微信/短信)3.工单:自动创建任务(谁处理?什么优先级?)4.SLA:承诺多长时间内必须响应/修复5.复盘:事后统计,哪里频繁出问题,哪里响应慢下面我用一个最小系统来演示这个流程。—## 二、架构总览:四层模型┌─────────────────────────────────────────┐│ 复盘层 (复盘报表) │├─────────────────────────────────────────┤│ SLA层 (超时计算+升级告警) │├─────────────────────────────────────────┤│ 工单层 (创建/分配/状态流转) │├─────────────────────────────────────────┤│ 监控+告警层 (采集/规则/通知) │└─────────────────────────────────────────┘每层之间通过事件总线(简单点就用 Redis 队列或 Kafka)串联,数据流方向是单向的:监控 → 工单 → SLA → 复盘。—## 三、第一步:监控 + 告警层(最小实现)我们用 Python 模拟一个门店温度监控器。 实际生产中可能是 IoT 设备上报,这里为演示写一个模拟脚本。python# monitor_simulator.py# 模拟20家门店的温度数据,超过阈值则触发告警事件import randomimport timeimport jsonimport redis# 连接Redis作为事件总线r = redis.Redis(host='localhost', port=6379, db=0)STORES = [f"store_{i:03d}" for i in range(1, 21)] # 20家门店TEMP_THRESHOLD = 8.0 # 温度告警阈值(摄氏度)while True: store_id = random.choice(STORES) # 模拟温度波动:大部分正常,偶尔超标 temperature = round(random.uniform(2.0, 12.0), 1) if temperature > TEMP_THRESHOLD: event = { "type": "temperature_alert", "store_id": store_id, "value": temperature, "timestamp": time.time() } # 发布到redis频道 r.publish("monitor_events", json.dumps(event)) print(f"[ALERT] {store_id} 温度 {temperature}°C 超过阈值 {TEMP_THRESHOLD}°C") else: print(f"[OK] {store_id} 温度 {temperature}°C") time.sleep(random.uniform(0.5, 2.0)) # 随机间隔这段代码做了三件事:- 模拟门店温度- 判断是否超过阈值- 超过则发布告警事件到 Redis 频道—## 四、第二步:告警 → 工单自动创建有了告警事件,我们需要自动创建工单。 工单需要包含:门店、问题描述、优先级、创建时间、处理人。python# ticket_creator.py# 监听Redis事件,自动创建工单并推送到SLA检查队列import jsonimport timeimport redisfrom datetime import datetime, timedeltar = redis.Redis(host='localhost', port=6379, db=0)pubsub = r.pubsub()pubsub.subscribe("monitor_events")# 模拟一个简单的工单存储(实际可用SQLite或MySQL)tickets = {}def create_ticket(event): """根据告警事件创建工单""" ticket_id = f"TICKET-{int(time.time())}" ticket = { "id": ticket_id, "store_id": event["store_id"], "problem": f"温度异常: {event['value']}°C", "priority": "high" if event["value"] > 10 else "medium", "status": "open", "created_at": datetime.now().isoformat(), "sla_deadline": (datetime.now() + timedelta(hours=2)).isoformat(), # 2小时SLA "assigned_to": None } tickets[ticket_id] = ticket # 将工单推送到SLA检查队列 r.lpush("sla_check_queue", json.dumps(ticket)) print(f"[工单创建] {ticket_id} for {event['store_id']}") return ticketfor message in pubsub.listen(): if message["type"] == "message": event = json.loads(message["data"]) if event["type"] == "temperature_alert": create_ticket(event)这里的关键设计: - 工单创建后立即推送到sla_check_queue,供后续SLA检查模块消费 - 工单优先级根据异常严重程度动态调整(>10°C 为 high) - SLA 期限设为2小时,超时未关闭则触发升级—## 五、第三步:SLA 监控与升级机制SLA 不是只设一个死线,而是要主动检查工单是否超时。 如果超时,需要升级告警(比如通知运维经理)。python# sla_checker.py# 从队列中取出工单,检查是否超时,超时则升级import jsonimport timeimport redisfrom datetime import datetimer = redis.Redis(host='localhost', port=6379, db=0)def check_sla(): """持续检查SLA队列中的工单是否超时""" while True: # 阻塞地从队列中取工单 _, ticket_data = r.brpop("sla_check_queue", timeout=5) if ticket_data: ticket = json.loads(ticket_data) deadline = datetime.fromisoformat(ticket["sla_deadline"]) now = datetime.now() if now > deadline and ticket["status"] == "open": # 超时!升级告警 print(f"[SLA超时] {ticket['id']} 已超时! 原定 {deadline}") # 这里可以发送钉钉/邮件给经理 # 同时标记工单为 escalated ticket["status"] = "escalated" ticket["escalated_at"] = now.isoformat() # 重新入队,下次检查还能看到 r.lpush("sla_check_queue", json.dumps(ticket)) else: # 未超时,重新放回队列(或放回延迟队列) # 简单起见,我们放回队列并sleep一会儿 time.sleep(30) r.lpush("sla_check_queue", json.dumps(ticket)) time.sleep(1)if __name__ == "__main__": check_sla()这个模块的难点在于如何避免死循环——如果工单未超时,不能立刻再检查,否则 CPU 会跑满。 生产环境会使用延迟队列(Redis ZSET 或 RabbitMQ 的 TTL),这里简化处理。—## 六、第四步:工单流转与状态管理工单需要有人来关闭。 我们模拟一个简单的处理流程:python# ticket_handler.py# 模拟运维人员处理工单(实际通过API或前端操作)import jsonimport timeimport redisr = redis.Redis(host='localhost', port=6379, db=0)def close_ticket(ticket_id): """关闭工单(模拟操作)""" # 简单起见,我们假设工单存储在Redis Hash里 ticket_key = f"ticket:{ticket_id}" ticket = r.hgetall(ticket_key) if ticket: ticket["status"] = "closed" ticket["closed_at"] = time.time() r.hmset(ticket_key, ticket) print(f"[工单关闭] {ticket_id}") else: print(f"[错误] 未找到工单 {ticket_id}")# 模拟:每10秒随机关闭一个工单while True: # 获取所有工单 all_tickets = r.keys("ticket:*") if all_tickets: ticket_id = random.choice(all_tickets).decode().split(":")[1] close_ticket(ticket_id) time.sleep(10)实际系统中,这里应该是运维人员通过 Web 界面点击“处理完成”,或者 IOT 设备自动上报修复信号。—## 七、第五步:复盘统计有了完整的工单数据,复盘就很简单了。 我们可以统计:- 每个门店的告警次数- 平均响应时间(从告警到工单被认领)- 平均修复时间(从工单创建到关闭)- 按门店/问题类型的分布python# review_report.py# 生成复盘报表(示例:按门店统计告警次数和平均修复时间)import jsonimport redisfrom datetime import datetimer = redis.Redis(host='localhost', port=6379, db=0)def generate_report(): """生成简单的复盘报表""" report = {} all_tickets = r.keys("ticket:*") for key in all_tickets: ticket = r.hgetall(key) ticket = {k.decode(): v.decode() for k, v in ticket.items()} store = ticket["store_id"] if store not in report: report[store] = {"count": 0, "total_duration": 0, "closed_count": 0} report[store]["count"] += 1 if ticket["status"] == "closed" and "closed_at" in ticket and "created_at" in ticket: created = datetime.fromisoformat(ticket["created_at"]) closed = datetime.fromisoformat(ticket["closed_at"]) duration = (closed - created).total_seconds() / 3600 # 小时 report[store]["total_duration"] += duration report[store]["closed_count"] += 1 print("=== 门店运维复盘报表 ===") print(f"{'门店':<12} {'告警次数':<10} {'平均修复时间(h)':<15}") for store, data in sorted(report.items(), key=lambda x: x[1]["count"], reverse=True): avg = data["total_duration"] / data["closed_count"] if data["closed_count"] else 0 print(f"{store:<12} {data['count']:<10} {avg:<15.2f}")if __name__ == "__main__": generate_report()这个报表可以直接发给管理层,或者集成到 Grafana 面板中。—## 八、总结:最小可用系统的核心设计原则| 环节 | 关键设计要点 | 技术选型参考 ||------|-------------|-------------|| 监控 | 阈值可配置、数据采集频率可调 | Redis Pub/Sub, MQTT || 告警 | 分级通知、去重、抑制 | 钉钉/飞书 Webhook || 工单 | 自动创建、分配规则、状态机 | Redis + Python dict || SLA | 超时检测、升级机制、延迟队列 | Redis ZSET / RabbitMQ || 复盘 | 聚合统计、趋势分析 | SQLite / Pandas |这套系统的优点:- 全部用 Python + Redis 实现,没有复杂组件- 每层解耦,可以单独替换(比如把 Redis Pub/Sub 换成 Kafka)- 从监控到复盘 5 个环节完整覆盖局限性(也就是未来可以加的功能):- 缺少持久化存储(建议上 SQLite 或 MySQL)- 没有 Web 界面(可以加 Flask + Vue)- 告警去重需要额外逻辑如果你正在管理几十到几百家门店,这个最小系统完全可以跑起来,帮你把“出了事没人管”变成“每个问题都有记录、有跟踪、有结果”。 下次老板问“上周哪家门店问题最多”,你直接甩一张复盘报表就完事了。