SRE 与 DevOps 的关系
站点可靠性工程师(SRE)是 Google 工程副总裁 Ben Treynor Sloss 创造的术语。DevOps 是一种理念和工作方法,SRE 是 DevOps 的实现,比 DevOps 更具体、更清晰。
DevOps 核心思想
- 运维和开发团队不应该独立,各自为政,需要打穿部门墙。
- 意外不可避免,缺少保障措施才是问题。重心应该放在如何从故障中恢复,而不是防范故障的发生。
- 每次变更要尽量小,变更次数要尽量多。不应该为了提高效率或者为了“降低”风险,把变更积攒起来一起做。应该用正确的方式面对变更的风险,比如将其拆分为规模更小、风险也更小的变更,拼接起来形成一个前向稳定的变更序列,并在实施过程中不断优化变更流程,实现变更管理方式的转型。
- 自动化工具固然非常重要,但工程师文化更重要,良好的文化可以解决糟糕工具造成的麻烦,反之不然。
- 统一的度量是不同部门之间沟通的基石。
SRE 核心思想
- 做好运维是一个软件开发问题。SRE 应该使用软件工程的方法来解决问题。
- 不企图提供 100% 的可用性。应该以服务质量目标(SLO)为准绳,任何设计不应该违反 SLO。
- 尽量减少琐事,琐事不是工作。在运维任务上多花一分钟,在项目上就少了一分钟。虽然合理的运维任务可以更好地为设计系统提供信息,但是项目工作才能提高服务的可靠性和扩展性。
- 确定自动化目标(哪些需要自动化,什么条件下的自动化,如何自动化)。团队成员在琐事上花费的时间不能超过 50%。
- 减少常规故障平均修复时间(MTTR),会提升开发人员的迭代速度。
- 理想情况下 SRE 和产品团队都应该对技术栈有整体的了解(包括前端、后端、存储、内核、物理机),SRE 需要和产品团队共享服务的所有权。
- 负责同一个服务的不同团队应该使用相同的工具,这样优化工具的成本越小,收益更大。
SRE 与 DevOps 的异同
共同点:
- 都认为实施改变是进步的源泉,没有改变就没有太大的操作空间。
- 都会在整个组织中广泛传播自己的理念,让不同团队之间的壁垒更容易打破。
- 少量多次地做变更,大部分变更应该经历自动化测试和自动部署。
- 都以数据为基础,指定统一的度量至关重要。
- 推行对事不对人的时候总结,避免毫无建设性的互相指责。
- 都希望通过共同合作使整个团队(组织、企业)变得更好,都会带来更快的迭代速度。
不同点:
- DevOps 是一种更宽泛的理念和文化,比 SRE 影响范围更广,在不同环境中有不同的具体表现,不太涉及操作层面的细节。
- SRE 职责定义相对狭窄,面向服务而非面向整体业务。SRE 与 DevOps 所认可的东西相同,但认可的原因略有不同。
DevOps 与 SRE 理念带来收益的前提条件
- 不要把激励措施与发布和可靠性成果联系在一起。
- 避免组织层面的甩锅,应该积极鼓励工程师做变更,并授予其较高的自由度。同时推行对事不对人的总结报告。停止对一些无可救药的产品的支持,“如果总是给他们分配过多的运维工作,而飞起了他们的真材实料,可能就辞职不干了”。
- 是否需要将 SRE 岗位规划成一个新的组织架构或部门,取决于公司规模,但要认识到专业化带来的优势与挑战。
- SRE 与开发团队的紧密合作,有助于在决策上可以达成一致的目标。
- DevOps/SRE 团队与开发团队应该要得到同等的尊重与激励。
实施 SLO
服务质量目标(Service Level Object,SLO),描述服务可靠性的程度,是 SRE 实践的核心。
SLO 的重要性
SLO 是对可靠性工作的机会成本做出数据驱动型决策的关键,有利于合理安排可靠性工作的优先级。
SRE 受 SLO 驱动:捍卫短期 SLO,并确保可以在中长期内对其进行持续维护。
SLO 入门
- 想要达到 100% 可靠性是一个错误的目标。
- 服务质量指标(Service Level Indicator,SLI)。建议将 SLI 视为两个数字的比率,比如成功实践数量/事件总数,范围是 0%-100%。SLO 就是目标的百分比数值,错误预算是 (100% – SLO)。
- SLI 规范与 SLI 实现,可用性、延时,时效性、耐用性、正确性、质量和覆盖率都可以成为有价值的 SLO。
范例系统分析
不同类型组件的潜在 SLI:
- 请求驱动:
- 可用性(返回结果为成功的请求的比例)
- 延迟(响应速度快于阈值的请求的比例)
- 质量(服务降级比例)
- 流水线
- 时效性(最近一次更新时间距现在小于某个时间阈值的数据的比例)
- 正确率(已进入流水线的数据记录,正确地生成了处理结果的比例)
- 覆盖率(处理了超过某些目标量级数据的作业的比例,或者在某个时间窗口内成功处理了传入记录的比例)
- 存储
- 持久性(写入记录可以被成功读取的比例)
- SLI 数据源:应用服务器日志、负载均衡器监控、黑盒监控、客户端嵌码
- 使用历史 SLI 数据得到初始 SLO
选择合适的时间窗口
建议用周作为合适的时间窗口(周末流量高峰),防止长时间窗口更多的不确定性
获取所有利益干系者的认同
宽松的 SLO 还是严谨的 SLO?
- SRE 觉得劳民伤财
- 开发团队和产品经理觉得修复可靠性需要投入更多资源吗,导致发布速度降低至不能接受的程度
- 产品经理觉得是否让相当大量的用户感到崩溃
错误预算一旦耗尽:
- 开发团队需要找出与可靠性相关优先级最高的缺陷
- 开发团队需要专注于修复可靠性缺陷,暂缓外部特性开发需求
- 生产环境需要冻结,某些系统需要停止变更,知道错误预算恢复
SLO 和错误预算策略应该文档化。
SLO 报表:展示各个服务 SLO 是否达标以及 SLI 趋势。
错误预算消耗图:展示某个服务错误预算消耗情况。
SLO 目标持续改进
需要一个可以获取用户对服务满意度的信息源,比如:
- 公共论坛帖子、工单、客服热线,人工统计,得出服务中断次数
- 评估和分析社交媒体上用户情绪的宣泄(积极?消极?)
- 定期的用户满意度调查程序
- 面对面用户访谈调查采样
如果故障时间或工单数量陡增没有被 SLI 或 SLO 捕捉到,或 SLI 下降和 SLO 不达标没有反映出实际问题,就是 SLO 覆盖度低的表现,需要持续改进:
- 变更 SLO:放宽或收紧
- 变更 SLI 实现:将服务端指标移到 LB 或用户端
- SLO 要符合现实:在满意度和成本之间妥协
- 迭代:时候评审
基于 SLO 和错误预算的决策
SLO 决策矩阵
SLO | 琐事 | 客户满意度 | 行动 |
---|---|---|---|
达标 | 低 | 高 | 继续保持,或将重心转移到其他更需要可靠性的服务上 |
达标 | 低 | 低 | 收紧 SLO |
达标 | 高 | 高 | 降低敏感度、放宽 SLO、提高自动化故障恢复(消除琐事) |
达标 | 高 | 低 | 收紧 SLO |
不达标 | 低 | 高 | 放宽 SLO |
不达标 | 低 | 低 | 提高告警敏感度 |
不达标 | 高 | 高 | 放宽 SLO |
不达标 | 高 | 低 | 提高自动化故障恢复(消除琐事) |
进阶主题
SLO 应该以改善用户体验为核心。SLI 的制定应该从用户行为出发。
SLO 按用户等级(付费、免费)分组,按预期响应度(100ms、5s)分组。
依赖服务的 SLO 也要考虑在整体 SLO 之内。
对可用性进行可控的实验,可以挖掘 SLI 与用户满意度之间的关系。
SLO 工程案例研究
Evernote 的 SLO 故事
错误预算/SLO 模式可以促使开发和运维两个团队在相同的前提条件下做出相近的决定,因为 SRE 消除了对话中大量的主观性。
将实体数据中心迁移至公共云平台。
导入 SLO,所有团队向 SLO 看齐。
与云平台共享 SLO,目标对齐,共同担当成功或失败。
Home Depot 的 SLO 故事
VALET
- Volume:容量(流量),服务可以处理多少业务量
- Availability:可用性,服务是否在需要时可用
- Latency:延迟,服务能否快速响应
- Errors:服务是否会出错
- Tickets:工单,服务请求是否需要人工干预才能完成
监控
监控系统应该实现的目标
- 在需要人工接入的情况下,发出告警
- 调查或诊断这些问题
- 展示有关于系统的可视化信息
- 获取有关资源使用率或服务健康度的趋势分析,用于制定长期的规划
- 比较系统变更前后的行为,或者比较两个实验组的差距
监控系统的必备特性
- 数据获取速度,对事件作出响应的速度
- 计算,对监控指标做离线分析(周报,日报)
- 仪表盘(曲线图、热图、直方图、对数刻度表)
- 告警,告警抑制(多个节点同时触发告警,只需要发送一次;依赖的服务触发告警时,并不需要为自己的服务发出告警)
监控数据源(日志、指标、分布式追踪、运行态自查)
- 日志:优点精度高,缺点延时高
- 指标:缺点颗粒度低,优点实时性高
- 用指标来配置仪表盘和告警,用日志定位问题根本原因
- 将日志导出为指标,用于图表和告警
- 维护一个类库,集成到每个应用程序的框架语言中,在关键逻辑处调用来写入日志或导出指标。
- 告警触发后自动执行日志查询脚本,返回详细数据
管理监控系统
- 配置即代码(Configuration as Code):将系统配置视为代码存储在 VCS 中。
- 保存了更改历史记录;把特定变更与任务跟踪系统(Jira)关联起来;易于回滚和语法语义检查;强化了代码评审流程的执行。
- 按意图配置的监控系统优于只提供 Web UI 或 CRUD API 的系统。
- 如果所有服务都能导出一组一致的基础指标,就可以在整个组织范围内自动化采集这些指标,并提供一致的仪表盘。
- 监控组件之间应该松耦合,确保稳定的监控接口。比如数据采集与规则计算(Prometheus)、长期时间序列存储(InfluxDB)、告警聚合(Alertmanager)、仪表盘(Grafana)。
度量指标的意图
- SLI 指标应该放在最明显的位置上。
- 监控二进制文件的版本;监控命令行参数;监控动态配置的版本(或监控最新一次编译或打包的时间戳)。
- 监控直接依赖的服务的响应状态。
- 饱和度监控:当资源有硬性限制时;当超过阈值就会导致性能降级时。
- 服务流量(状态码以及速率配额限制监控)
- 用于告警还是用于排错。
测试告警逻辑
通过仿真数据测试监控告警系统的正确性。
基于 SLO 的告警
评估一条告警策略时,应该考虑:
- 精确率:告警事件中重大事件所占的比例
- 召回率:所有客观重大事件中,告警覆盖到的比例是多少
- 检测用时:在特定条件下发出告警通知需要多长时间
- 重置用时:问题解决之后告警还会持续多长时间
基于多个错误预算燃烧率发出告警
处理低流量服务的告警方法:
- 生成人工流量
- 组合多个服务
- 改变服务和基础设施,降低单个失败请求对用户的影响
- 降低 SLO,延长告警时间窗口
强烈建议不要为每个服务单独设定告警时间窗口和燃烧率参数。如果这样做,大量的琐事和各种特殊情况的具体分析工作会堆积如山。
根据相似可用性要求和阈值所设置的请求分组:
请求类别 | 可用性 | 延迟 p90 | 延迟 p99 | 举例 |
---|---|---|---|---|
CRITICAL | 99.99% | 100ms | 200ms | 用户登录 |
HIGH_FAST | 99.9% | 100ms | 200ms | 浏览广告收入 |
HIGH_SLOW | 99.9% | 1000ms | 5000ms | 查看广告活动报告 |
LOW | 99% | 无 | 无 | 处理账户通知的轮询程序 |
NO_SLO | 无 | 无 | 无 | 用户完全不可见的功能 |
消除琐事
琐事的定义
与维护服务相关的、重复发生的、可预测的、常规的任务流。
手动性、重复性、可自动化、非战术性/被动性、没有持久价值、与服务规模同步增长。
比如:当服务器上 /var/log 目录达到 95% 使用率的时候,工程师登录到机器,删除掉无用的日志文件。
时间一旦花在了琐事上,通常就没有用在批判性思考或表达创意力方面的时间了;削减琐事是对工程师人类智慧的认可,人类的判断和表达能力将在与之适合的领域中得到了更好地发挥。
通过消除琐事所节省的时间(至少)要与首次开发工作以及后续维护自动化解决方案所投入的时间成正比。
自动化工具潜在的收益
- 随着时间的推移和业务规模的增长,可以减少未来的琐事
- 提高团队士气,减少团队人员流失和透支的情况
- 降低上下文切换的中断,从而提高团队的工作效率
- 提高流程的清晰度和标准化
- 提高团队成员的技术水平和职业发展
- 更少的培训时间
- 减少人为错误导致的宕机
- 提高安全性
- 更短的用户请求响应时间
琐事来源
业务流程
举例:资源管理团队,管理着计算、存储、网络、负载均衡器、数据库,以及提供这些资源所需要的硬件。你的工作是负责处理新用户注册,配置他们的计算机并加固其安全性,执行软件更新,或者为调节集群容量而添加或删除服务器。最大限度地降低所使用资源的成本和浪费。通过工单方式与有需求的内部客户进行交互。
生产中断
举例:手动释放磁盘空间,重新启动内存泄露的应用程序,提交工单更换硬盘驱动器。
产品发布
举例:发布请求、回滚、紧急补丁、重复性或手动配置更改。
迁移
数据存储、云供应商、源代码控制系统、应用程序库和工具的改变。
工程成本和容量规划
- 制定 CPU、内存、IOPS 等资源在未来的需求计划,可能转化为资源采购订单、公有云预留实例或合同谈判。
- 为关键的高流量时间开始和结束做准备,如产品发布或假期促销。
- 检查下游和尚有的服务水平/限制。
- 根据不同的服务规格优化工作负载(买一个高配虚拟机还是四个低配虚拟机?)。
- 根据云服务产品特有的计费希捷优化应用程序。
- 重构工具以更好地使用更便宜的竞价实例或可抢占式资源。
- 处理超额购买的资源。
不透明架构的故障排查
举例:分布式微服务架构故障排除工作需要登录到不同的系统或使用临时编写的日志分析工具。
琐事管理策略
- 采用数据驱动的方式识别琐事源头
- 尝试在源头上消灭琐事
- 拒绝琐事
- 根据 SLO 做出明确的决策
- 从半自动化开始
- 提供各种自助服务方法(表单、二进制、脚本、API,甚至文档)
- 获得管理层和同事的支持(通过拒绝新需求的方式保护员工免受打扰至关重要)
- 大力推广
- 不要试图设计一个没有琐事的完美系统。可以自动化一些高优先级的事情,再用节省下来的时间去做更多的改进。
- 设施统一化。团队可以自由选择自己的方法,但必须自己承担遗留系统所产生的琐事。
- 降低自动化带来的风险(防御性地处理用户输入,完整的安全检查,适时切换为人工处理)
- 使用开源和第三方工具(降低开发成本)
- 积极地收集反馈,不断进行改进
简单性
简单性应该是 SRE 一个重要的目标,与可靠性密切相关。
简单的软件不会经常发生故障,即是出了问题,修复起来也很快、也更容易。
简单的系统更易于理解、维护和测试。
系统级复杂性的度量
- 培训时间(文档缺失或文档质量较低)
- 解释时间(架构图)
- 管理的多样性(配置文件集中存储还是分散在多个位置)
- 部署配置的多样性(生产环境有多少种独有的配置)
- 年龄(系统使用了多久)
SRE 团队应该负责降低系统的复杂性,绘制系统的架构图是一个很好的开始,同时确保 SRE 参与评审所有的设计文档。
我们要把成功的简化项目视为和发布新功能一样,并把添加代码和删除代码的功劳同等看待。
On-Call 值班
On-Call 的目标是为重要的服务保驾护航,但不能为这个目标而牺牲工程师的健康。
工程师需要有一系列应对流程和上报渠道。
On-Call 工作应当有所补偿,比如提供额外的假期或不超过薪水一定比例的加班津贴,以激励参与 On-Call 的员工,也保证不会因为津贴过度参与 On-Call。
案例
Google:组建一个新的团队
编写工作清单:
- 管理生产工作
- 理解调试信息
- 把流量从一个特定集群上切出
- 回滚一次有问题的部署
- 拦截或限制非预期的流量
- 为服务扩容
- 使用监控系统(包括告警和仪表盘)
- 描述服务的系统架构、组成部分、依赖关系
文档、代码示例、新手项目、即兴报告。
排查告警,鼓励新人寻求帮助。
与其他 SRE 团队和产品开发人员交流(视频会议、电子邮件、IRC),参与生产例会、阅读 On-Call 交接记录和事故总结、浏览已有服务文档。
明确 On-Call 工程师准确的、具体的职责: - 每次值班开始前,需要阅读上一位工程师的交接记录。
- 必须先降低对用户的影响,再研究如何彻底解决问题。
- 每次值班结束前,应当通过电子邮件的方式,将交接记录发给下一位工程师。
维护 On-Call 工作手册,提供每个告警的严重性、影响范围、应对思路、处理方法。这些条目会减轻 On-Call 工程师的压力,缩短平均修复时间(MTTR),降低人为失误风险。
Evernote:从本地数据中心迁移至云数据中心
重新定义告警规则,以 SLO 为目标,重点关注 API 响应状态,而不是 MySQL 某个指标。换句话说更关注用户在事故中的实际痛点,而不是在偶发的、短暂的问题上花费太多时间,这样才能给团队带来更多的睡眠、更高的收益,最终带来更高的工作满意度。
告警事件分类:
- P1:立即处理
- 应当立即采取行动
- 发出 On-Call 告警
- 展开事件诊断处理
- 判断对 SLO 的影响
- P2:在下一个工作日内处理
- 一般不直接影响用户,或影响范围有限
- 向团队发送电子邮件,通知到相关频道
- P3:仅供参考
- 在仪表盘上显示,必要时才发送电子邮件
- 包含容量规划相关信息
事故处理流程:
- 评估对用户造成的影响
- 事件升级
- 组织事故处理小组,启动事故管理流程
- 向事故负责人发送告警,选出联络员
- 处理事故
- 撰写事故报告,找出工具或流程上的缺陷
服务总结例会。
与 Google 用户可靠性工程师(CRE) 合作,与 CRE 共享 SLO。
用节省下来的时间将业务持续向前推进。
实施细节
告警压力
合理的响应时间:
事故描述 | 响应时间 | 对 SRE 要求 |
---|---|---|
影响营收的网络故障 | 5 分钟 | 必须有一台电量充足、授权足够、网络通畅的电脑;不能出行;时刻与副 On-Call 保持联系 |
处理用户批量订单的系统卡住了 | 30 分钟 | SRE 可以短暂离开或短暂通勤;副 On-Call 无需介入 |
一个预发布服务数据库备份故障 | 工单(工作时间内响应) | 无 |
告警压力的来源:生产环境中的 bug、自动告警、人工变更操作
检查或预防未产生告警的旧 bug:
- 确保系统复杂性已达到最低
- 集成测试和单元测试
- 定期更新依赖库
- 定期进行破坏性测试和混沌测试
- 定期进行压力测试
避免发布中引入新 bug:
- 不断提高测试水平
- 尽量保持预发布环境和生产环境的一致性
- 金丝雀发布
- 对新 bug 保持较低的容忍度。遵循发现 bug、立即回滚、修复、再发布策略;而不是发现 bug、无视 bug、继续发布、修复。
防止人工误操作:
- 让所有生产环境的变更都由自动化系统根据(人开发的)配置文件实施
如何加速定位故障的根本原因:
- 优化告警和仪表盘
- 故障演练(“命运之轮”练习)
- 执行小批量发布
- 保留变更日志
- 请求帮助
缓解故障的措施:
- 回滚变更
- 特性开关
- 重定向用户请求
自动告警:
- 所有的告警都应当是可以立即处理的,应当有一系列处理步骤,人工可以立即操作,机器无法自动完成。
- 一切为 SLO 服务。
- 每个新的告警都应当被彻底、全面地评审,每个告警都应当有一条对应的工作手册条目。
- 要搞清楚每个告警背后的根本原因,如果找不到根本原因,就增加相应的监控和日志。不应该下结论说:“告警是由一个未知原因引起的”。
简单地修复一个眼前的 bug,会错失一个预防未来类似告警的黄金机会。
On-Call 灵活性
自动化排班系统:
- 可以对值班表做出调整,以适应团队成员的需求变化
- 可以自动重新平衡每个人的 On-Call 工作量
- 把类似于“4月周末不担任主 On-Call”之类的需求、每个工程师近期值班历史都考虑进去,尽力保证公平合理
- 不能随意改动已经生成的值班表
鼓舞 On-Call 团队士气
- 给予 SRE 更多权限(提交 MR 修复)
- 加强团队之间的联系(团建)
事故响应
事故管理的前提是有条不紊地响应事故。
Google 的事故管理
Google 事故管理系统(IMAG)基于事故指挥系统(ICS)。
事故响应框架的目标(三个C):
- 协调(Coordinate)
- 沟通(Communicate)
- 控制(Control)
事故响应中的主要角色:
事故总负责人(Incident Commander)
发言人(Communications Lead)
业务负责人(Operations Lead)
案例
Google Home 案例
成功的事故管理不能依赖于其成员在工作日以外的无私奉献。我们应该改在工作时间发布新版本,或者在非工作时间安排有报酬的 On-Call 值班。
在完全定位根本原因前,应该推迟发布新版本。
尽早地声明事故。
GKE 案例
使用了多个清晰记载的事故升级渠道,并对事故响应策略谙熟于心。
高度复杂性和对专家的高度依赖都是有问题的。
缓解问题是头等大事。
一个正在发生的事故应该这么处理:
- 评估事故的影响范围
- 缓解这些影响
- 分析事故地根本原因
- 在事故结束后,修复那些引发事故地问题,并撰写事故报告
闪电击中 Google 数据中心案例
一开始就声明事故。
事故总负责人将标准化工作分配给合适的业务负责人,工程师努力修复问题,并向业务负责人汇报。
事故负责人确保事故处理的同时,专业于事故地核心部分:尽快满足受影响客户的需求。
PagerDuty 服务时钟漂移事故
通过以下手段来促进团队合作:
- 共同参与实战模拟练习
- 进行有时间限制的模拟游戏
- 以史为鉴(撰写并定期阅读事后总结)
事故响应流程用到的工具:
- PagerDuty
- Slack
- 电话会议系统
实施
事故响应培训
可以开发一个适合自己团队的事故处理框架,例如:
- On-Call 人员知道他们在事故处理过程中可以委派和调用相关人员,也可以升级给合适的人员。
- 营造缓解问题第一、彻底修复第二的氛围。
- 设立事故地总负责人、发言人、业务负责人等相关角色。
可以调整和描述你们自己的事故响应框架,并用幻灯片的形式展示给新加入的成员。
前期准备
在响应事故时,在协调和共同方式上达成共识。
- 确定好沟通渠道(Slack、电话会议系统、IRC、HipChat 等等)
- 及时通告情况发展
- 准备联系人名单
- 建立事故标准
演习
灾难恢复测试(Disaster Recovery Testing, DiRT)
“小题大做”
定期举行演习
事后总结:从失败中学习
一份差劲的事后总结:
- 缺少上下文(背景情况、词汇表),你的受众群体不仅仅只有直接相关的团队
- 省略关键细节
- 量化影响的大小
- 确定根本原因和触发因素
- 修复的过程
- 关键行动项目缺失
- 试图改变人类的行为,不如改变自动化系统和流程来得可靠。(“让我们为未来做好计划,要注意未来的我们像今天一样愚蠢”)
- 所有行动项都标记为相同的优先级,没有办法确定先要采取哪项行动
- 只有一个项目指派了工单,如果没有跟踪流程,行动项目往往会被遗忘,导致再次发生
- 适得其反的指责:在事故总结中强调个人过失,会导致团队变得厌恶风险,还会导致工程师们故意掩盖一些有助于理解故障和防止故障复发的至关重要的事实。
- 生动的语言和对事件戏剧性的描述会分散读者对关键信息的关注
- 缺少负责机制:设置一个负责人和多个协作者比较好一些
- 受众有限:应该尽可能广泛地分享时候总结,甚至与客户分享。全面诚实的事后总结是巩固不可动摇的信任关系的关键工具。
- 发布延迟:事故发生后 4 个月后才发布
一份优秀的事后总结:
- 明晰(词汇表、行动项目、可量化的指标)
- 具体的行动项目有负责人、优先级,并且可以对完成结果进行度量,有预防、缓解的措施
- 对事不对人
- 深度(多个团队的系统缺陷及影响)
- 迅速(1 周内撰写完成并发布)
- 简明(细节以链接的形式展示,在完整性和可读性之间取得平衡)
组织激励
树立和加强对事不对人的模式
- 使用对事不对人的语言
- 让所有参与事故处理的人员参与时候总结的撰写
- 收集反馈
奖励事后总结的成果
- 奖励行动项目的落实(不能虎头蛇尾)
- 奖励积极地组织改革(奖金、绩效、晋升)
- 强调可靠性的提高(报告、幻灯片、绩效考评)
- 给事故总结的负责人以应得荣誉(电子邮件、提供讲述经验的机会)
- 游戏化(计分板、燃尽图)
公开分享事后总结
- 在整个组织内分享公告(电子邮件、Slack、定期全体大会)
- 进行跨团队审阅(提出问题并相互学习,跨职能小组)
- 举办培训活动(“命运之轮”练习)
- 故障周报
面对事后总结文化的淡化
- 尽可能避免撇清干系的想法
- 加强对事不对人的文化
- 保证事后总结的优先级
- 防止事故复发
工具和模板
事后总结模板
事后总结工具
- Google 的 Requiem 工具
- PagerDuty 的时候总结
- Etsy 的 Morgue 工具
- VictorOps
管理负载
Google 云负载均衡
基于 DNS 的负载均衡:最简单、最有效;本地 DNS 记录更新需要依赖客户端的合作。
Anycast:将客户端发送到最近的集群而不依赖于 DNS 的地理位置,将数据包重定向至最近的服务,从而可以使用一个 VIP 提供低延迟服务。
Anycast
通过网络中多个点的边界网关协议(BGP)公布 IP 地址,将来自用户的数据包转发至一个最近的前端。但存在两个主要的问题:
- 附近用户太多的情况有可能淹没一个前端站点。
- BGP 路由计算可能会重置链接:ISP 重新计算 BGP 路由导致路由“震荡”,所有正在进行的 TCP 流会被重置。
Maglev
Maglev 解决 Anycast 的路由震荡问题。
GSLB
全球负载均衡系统
GFE
Google 前端
- 接受 TCP,终结 SSL,检查 HTTP header 和 URL,重新加密数据转发至对应服务。
- 对后端服务进行健康检查,摘除失效的后端。
- 会话保持,降低延迟。
GCLB
Google 云负载均衡系统
- 低延迟
- 高可用
自动伸缩
考虑不健康实例
根据实例的平均利用率来判断是否需要伸缩,如果实例需要长时间准备才能开始服务,或者实例卡在非服务状态时,容易引发故障。
改善策略:
- 根据负载均衡器观察到的容量指标进行自动伸缩
- 等待新实例就绪后再收集指标
- 自动修复(重启)异常实例
有状态服务
基于智能分级路由(一致性哈希)的负载均衡
垂直扩容(对所有实例生效,造成资源浪费)
保守地配置
对流量的突增的反应应该比对流量下降更敏感。
自动伸缩器需要足够的时间做出反应,同时处于过载保护的预留冗余的目的,面向用户的服务应该预留足够的备用容量。
设置约束
- Bug 导致跑满 CPU,自动伸缩器无限制地扩大规模,知道资源配额耗尽。
- 依赖服务发生故障,请求卡住,自动缩放器扩大规模,导致更多的请求卡住。
需要配置好最小和最大限制,确保有足够的资源配额。
准备中止开关
如果出现问题,On-Call 工程师可以手动禁用,中止功能应当简单、明显、快速,且有玩呗的说明文档。
避免后端超载
部署自动伸缩器之前,对后端服务进行详细的依赖性分析,确保后端有足够的额外容量来应对突发流量,并在过载时能够优雅地降级。分析后确定自动伸缩器的上限。
避免流量不均衡
跨区域自动伸缩
管理负载的多种策略
- 负载均衡:GCLB、Nginx、HAProxy
- 减载:Zuul、Envoy
- 自动伸缩:Kubernetes Pod AutoScaling
为 RPC 请求设置截止时间
非抽象大型系统设计
NALSD
基本设计阶段
- 可能吗?
如果不必担心 CPU、内存、网络带宽等资源限制,我们能做出什么样的设计来满足需求? - 还能做得更好吗?
能否是系统更快、更小、更高效?
纵向扩展阶段
- 这还可行吗?
考虑到资金、硬件方面的限制,此设计是否可以扩展? - 有弹性吗?
这个组件发生故障时会发生什么?整个数据中心出现故障时系统会怎样? - 还能做得更好吗?
数据处理流水线
流水线应用
ETL 模型(Extract Transform Load)
从数据源中提取、转置、重新加载为特定结构化数据。
Transform 阶段用例:
- 变更数据格式,添加或删除字段
- 跨数据源的聚合计算
- 建立数据索引,为使用数据的任务提供更好地特性
ETL 流水线常用使用场景:
- 机器学习或商业智能用例的预处理步骤
- 聚合计算
- 建立索引
数据分析
分析日报、月度报告。
机器学习
- 从较大的数据集中萃取数据特征及标签
- 根据提取到的特征,用 ML 算法训练模型
- 通过一组测试评估模型
- 准备其他服务使用做好的模型
- 其他系统根据模型所提供的反馈作出决策
最佳实践
定义和度量 SLO
数据新鲜度
- X% 的数据在 Y 时间内完成处理
- 最旧的数据不超过 Y 时间
- 流水线作业会在 Y 时间内成功完成
数据正确性
- 模拟对账检查正确性
- Y 时间范围内的错误率不超过账单总额的 X%
数据隔离/负载均衡
- 优先级队列
端到端的度量
- 以客户的视角度量系统端到端的健康状况
为依赖性故障做好准备
创建和维护流水线文档
系统图示:每个组件(流水线应用程序或数据存储)以及每个步骤发生的转换。监控每个阶段的当前状态,每个阶段的执行时间。
流程文档:记录常规任务是如何执行的,记录不太常见(手动)的任务,自动化。
工作手册:描述解决问题的步骤。
梳理开发生命周期
- 原型
- 1% 试运行
- 预发布
- 金丝雀发布
- 部分部署
- 生产环境部署
减少热点和工作负载模式
将热点隔离在一个数据集上。
将任务细化,然后进行动态重新平衡。
快速开关、快速发布配置。
实现自动扩容和资源规划
资源成本和流水线效率的权衡。
定期检查清理不再使用的资源。
快速定位导致资源使用量显著增加的任务。
ACL 和安全策略
避免将个人身份信息(PII)明文放在临时存储空间中。
限制对数据的访问,仅授予每个流水线阶段读取前一阶段输出数据所需的最小访问权限。
为日志和 PII 设置 TTL 限制。
流水线需求分析和设计
需要什么功能?
幂等:多次执行,产生相同结果
检查点:对于长期运行的任务定期地将部分状态保存下来,以便在中断时可以继续处理。
代码模式:重用、微服务
投产准备:成熟度矩阵
流水线故障的预防和响应
潜在的故障模式
- 延迟的数据
- 受损的数据
可能的原因
- 依赖组件故障(存储、网络)
- 程序 bug、配置错误
- 负载意外增加
- 可用区故障
配置设计与最佳实践
什么是配置
用于改变软件系统行为的人机界面。
软件系统的三个关键组件:
- 软件本身
- 数据集
- 配置
配置原则:
- 如何构造配置
- 如何实施正确的抽象级别
- 如果无缝地支持不同的用例
配置机制:
- 语言设计
- 部署策略
- 与其他系统的交互
配置原则
理想中的配置管理是完全不需要进行配置。但对于许多系统来说,这种理想的情形不可能在现实中出现。
最小化用户输入原则:远离大量的可调因子,趋向简单化。可以减少人员在系统中可操作的控制量,可以缩小误差的表面积和操作员的认知负担。
现代软件系统,从两个不同角度去看待人机交互模型:
- 以基础设施为中心的视角:提供尽可能多的配置选项。
- 以用户为中心的视角:选项越少越好。
在系统刚开始建设的时候,我们可以使用基础设施为中心的视角,随着系统的渐渐成熟,可以通过各种方式删除掉一部分配置选项,然后系统可能会慢慢地转型为更以用户为中心的视角。
满足高级用户的一种策略:提供复杂配置项,将复杂配置项作为默认选项。
配置机制
配置和数据分离:
- 界面:Web UI、API、配置语言(JSON、YAML、INI、XML、DSL)
- 配置数据:与配置语言相分离,避免相互耦合
- 目标系统
配置系统应当具备:
- 语义验证
- 配置语法:语法高亮、代码风格检查、自动语法格式化
- 每段配置代码应当标明负责人
- 实现版本控制,支持回退
安全配置变更必备的主要特性:
- 循序渐进地部署,避免全有全无的变更。(为什么 Kubernetes 使用滚动更新而不是热更新的原因)
- 如果已发布的配置存在问题,能够回滚变更。
- 如果变更导致操作员的失控,能够自动回滚(或中断变更)。
配置规范
减少配置带来的琐事
考虑完全抛弃配置,程序内动态检测更改某些值
通过自动化来减少配置参数集中的重复劳动,集成新的语言,改进或替换现有配置结构
配置系统设计的陷阱
- 没有把配置作为一种编程语言
- 设计一种特殊的语言
- 在特定领域做了很多优化,用户群体却很小
- 将“配置评估”与“外部干扰”交织在一起,在配置运行期间查询或更改外部系统
- 使用现有通用脚本语言(Python、Ruby、Lua)
集成配置语言
集成现有应用程序:Kubernetes
- 将 YAML 转换为 JSON
- 通过 Jsonnet 格式化程序运行上述 JSON
- 手动添加 Jsonnet 构造函数,用于抽象并实例化代码
针对不同配置的公共性进行抽象可以促进关注点的分离,与编程语言中的模块化有相同的优势:
- 一个团队可能需要为一个组件创建几乎相同(但不完全相同)的多个版本的配置
- 不同的组件通过不同的模板维护,任何团队都可以实例化这个模板,并覆盖必要的配置信息
集成定制应用程序
使用 Jsonnet 的最佳实践
- 输入是根据功能将配置拆分为多个文件,输出是一个文件
- 用对象名称作为 key,对象实体作为 value,避免使用 name 字段的对象数组
1 2 3 4
{ "cat": {...}, "dog": {...} }
优于
1 2 3 4
[ {"name": "cat", ...}, {"name": "dog", ...} ]
- 避免在顶层按类型对实体进行分组,要对 JSON 进行结构化,以便将与逻辑相关的配置分组在同一子树中。
1 2 3 4
{ "cat": {"age": 1, "weight": 20}, "dog": {"age": 3, "weight": 100} }
优于
1 2 3 4
{ "ages": {"cat": 1, "dog": 3}, "weights": {"cat": 20, "dog": 100} }
有效地运行配置系统
版本控制
当对模板库进行重大更改时,有两种选择:
- 提交一个涉及所有服务的全局更新,触发所有服务的重构。
- 对库进行版本控制,以便不同使用者可以使用不同的版本,并独立迁移。选择过时版本的用户将无法享受到新版本的好处,并且会积累技术债务,总有一天,他们将不得不为了适配新的库而重构配置相关代码。
通过目录实现版本控制。
源代码控制
将配置纳入源代码管理。
工具
规范配置语言中的代码风格,集成到工作流程中。
测试
对上游的模板库实施单元测试。
何时生成配置
在提交前进行生成
流程:
- 修改 Jsonnet 文件
- 运行 Jsonnet 命令行工具重新生成 JSON 文件
- 使用 hook 确保 Jsonnet 代码与 JSON 输出结果始终保持一致
- 将所有内容提交到 PR 中,进入代码评审阶段
优点: - 评审者可以对具体的变更进行完整性检查
- 可以查看多个编写者在不同版本上对每行的注释,对于评审很有用
- 无需在运行时运行 Jsonnet,有助于限制复杂性和风险
缺点: - 生成的 JSON 不一定是可读的
- 如果 JSON 太大或者包含机密信息,可能不适合直接存储在版本控制系统
- 如果某一个 JSON 文件上同时进行了多个不同的编辑,就可能会发生合并冲突
在编译时进行生成
流程:
- PR 中只包含 Jsonnet 代码
- 在编译时运行 Jsonnet 工具,将生成的 JSON 嵌入到发布制品中
- 应用程序在初始化时读取 JSON 文件
优点: - 可以控制运行时的复杂性和风险,不必在每个 PR 时重建 JSON 文件
- 原始 Jsonnet 代码与 JSON 之间不会存在不一致的风险
缺点: - 编译期复杂
- 在代码评审期间很难评估具体的变更
在运行时生成
流程:
- PR 中只包含 Jsonnet 代码
- 应用程序本身引入了 Jsonnet 库,可以随时对配置进行解释,将生成的 JSON 配置在内存中展示出来。
优点:
- 操作简单,不需要事先生成
- 可以在运行期间评估用户提供的 Jsonnet 代码
缺点:
- 任何引入的库都不会留下使用记录,同时增加了暴露的风险
- 在运行时发现配置错误的话就有些晚了
- 不受信任的 Jsonnet 代码可导致滥用
防止滥用配置
Jsonnet 或其他配置语言的灵活性导致 CPU 或内存的滥用。
金丝雀发布
发布工程:从获取代码到将它真正运行在生产环境的过程中,我们所处理的所有相关流程和制品。
金丝雀发布:为了对服务完成一次变更和评估,按时间阶段进行循序渐进的部署。
金丝雀:已经完成变更的这部分服务
控制:尚未完成变更的剩余服务
发布工程的基本原则
可复现的构建
自动化的构建
自动化的测试
自动化的部署
小规模的部署
平衡发布速率和可靠性
通过 SLO 和错误预算来衡量发布对可靠性的影响,而不是倡导不做变更。
目标应该是:在实现用户期望的可靠性目标的同时,尽快发布软件。
发布工程和金丝雀发布
通过用很少的一部分流量来测试每一个变更,从而降低引入新特性或新功能所带来的风险,确保不会造成大面积不良影响。
金丝雀流程的需求:
- 需要有将金丝雀变更部署到一个服务群体子集的方法
- 需要有能对金丝雀变更部分进行评估的流程
- 将金丝雀评估流程整合到发布流程里
实施金丝雀发布
- 将 SLO 和错误预算的风险降至最低
总错误率 = 金丝雀比例 * 错误率 - 选择金丝雀群体和持续时间
一次只运行一个金丝雀部署 - 规模和持续时间
- 流量
- 时段
- 指标
选择和评估指标
- 指标应该反映出问题
- 指标应该有代表性和归因性
- 多阶段评估
依赖关系和隔离
非交互系统中的金丝雀
- 确保金丝雀的最小持续时间比单个工作单元的持续时间更长
- 始终确保将待处理的特定工作单元任务交给同一个处理机池
- 指标选取:端到端时间、处理质量
监控数据需求
- 进行细粒度分类,区别金丝雀群体和对照群体的指标
- 聚合时间的区别,金丝雀指标需要更敏感
相关概念
- 蓝绿部署
- 人工负载生成
- 流量复制
发现运维超负荷并从中恢复
超负荷:是一种职业压力,会降低生产力
包括:
- 认知上困难的任务(调试内存泄露或段错误)
- 需要进行频繁上下文切换的小任务
导致超负荷的原因:
- 团队没有足够的时间处理过多的任务
- 无法预测 On-Call 时出现的问题以及工作量有多少
- 频繁的中断和外部压力因素交织在一起
- 没有给工作设定合理的优先级
案例1:团队规模缩小一半后的工作超负荷
实践
- 低成本的自动化技术,一旦部署了就会大大削减运维负荷
- 撰写参考文档,以便客户自助查询
- 关闭积压工单(延期、冗余、不紧急)
经验
- 识别工作中超负荷状况,并确定它的影响范围
- 避免新的中断积压
- 每两周对中断进行一次分类
- 每个成员待处理工单数量不超过 10 个
- 提醒成员关闭过期工单
- 沟通工作进展,转移部分工单
- 提醒成员关注工单列表
- 安排一次全体成员的工单修复日
- 通过一定的工作解决某类工单的共同问题
案例2:团队发生变化后的感知超负荷
实践
- 短期:减轻压力并改善心理安全,以建立健康的工作氛围
- 设立半定期的圆桌会议来讨论问题
- 通过 On-Call 人员在值班结束之后解决工单所需的时间来衡量负荷
- 删除无效邮件告警
- 静默告警
- 增加一个专门负责该团队的直线经理
- 重新平衡团队
- 开展团建活动,缓解团队紧张感并提高心理安全性
- 中期:查找导致超负荷问题的本质原因
- 将运维工作尽可能限制在 On-Call 轮值期间
- 将服务所有权交换给开发团队
- 通过培训建立团队成员的自信心
- 从远程团队引进 SRE,提供一些有价值的新视角
- 填补团队空缺位置
- 静默过期前解决每个告警
- 管理层有意识地倾听团队痛点,并找到驱动团队的解决方案
- 长期:解决导致级联的持续性问题
- SLO 保持一致,减少 SRE 认知负担
- 重新审查服务以符合当前生产的标准
经验
- 感知上的超负荷也是超负荷
- 告警数量实际没有太大变化,但失去了两名成员,导致增加了认知负担
- 少数成员感知上的超负荷会很快导致整个团队的超负荷
缓解超负荷的策略
识别超负荷的症状
- 团队士气低迷
- 团队成员长时间工作,甚至带病工作
- 更频繁地生病
- 不健康的任务队列
- 指标不平衡
- 长时间解决某个问题
- 处理琐事的比例很高
- 用大量时间解决 On-Call 问题
减少超负荷并恢复团队健康
- 发现和缓解心理压力源
- 对一个记录的工作进行优先级排序
- 保护未来的自己
- 建立评估团队工作量的指标,并确保它是正确的
- 更优先地安排项目工作,以减少重复的琐事
- 每个人都应对超负荷早期预警信号负责
SRE 参与模式
服务的生命周期
SRE 团队对服务可靠性的贡献贯穿于服务生命周期的所有阶段。
阶段 1:架构与设计
- 创建最佳实践
- 基于先前的经验,记录特定基础设施系统的各种该做的事与不该做的事,让开发人员避免已知的陷阱
- 提供早期的咨询,讨论特定的体系结构和详细的设计选型,辅助在目标原型的帮助下验证假设
- 加入开发团队,并参与开发工作
- 共同设计服务的某个部分
阶段 2:积极开发
生产化(Productionizing)
- 容量规划
- 负载平衡
- 监控告警、性能调优
阶段 3:有限可用性
- 在 GA 之前就要定义 SLO,以便服务团队能客观地度量服务的可靠性
- 告警与 SLO 匹配
- 在 GA 以前,运维事件需要通知服务所有者(共同参与)
阶段 4:一般可用性(GA)
GA 早期,开发团队需要对系统在真实压力下的表现保持持续地关注
GA 后期,开发团队需要提供一些小的功能增量和程序补丁,其中一些来源于运维的需求,以及过去所发生的一些生产事件
阶段 5:退出
SRE 需要在没有开发团队介入的情况下运维现有系统,并同时支持新系统的开发和运维工作
阶段 6:放弃
SRE 将服务交还给开发团队
阶段 7:中止支持
SRE 帮助删除该服务
建立关系
沟通业务和生产的优先级
- 了解产品开发人员期望 SRE 参与实现的目标
- SRE 应该对产品和业务目标有过深入的了解
- SRE 应该能够阐明自己的角色,如何使开发人员能够朝着那些目标前进
理想情况下,SRE 和开发人员的领导团队应该工作得犹如一个整体,定期的开会,并就技术和优先级方面的挑战交换意见。
识别风险
专注于系统可靠性,尽可能识别风险,以及它们的潜在影响。
对其目标
SRE 的工作重点是支持服务的长期运行,而不是新功能的发布
制定基本规则
在 Google 每个 SRE 团队都有两个主要目标:
- 短期:通过提供可运作的稳定系统来满足产品的业务需求,该系统不仅可用,还可根据需求扩展,并着眼于可维护性
- 长期:将服务运维优化到不再需要目前所执行的人工工作的水平,因此 SRE 团队可以继续开展下一个高价值工作
为此团队之间应该商定一些合作原则:
- 运维工作的定义
- 可度量的 SLO
- 季度错误预算
- 开发人员参与运维工作(看到持续存在的问题,并确定修复问题的优先级)
规划与执行
与开发人员领导一起确定产品和服务的年度路线图
定期回顾和更新路线图,并努力实现与路线图一致的目标
维持有效的持续关系
花时间在更好地合作中
- 与开发团队定期会面
- 与其他 SRE 团队定期会面
保持顺畅的沟通
- SRE 可以每季度与产品开发负责人进行一次“生产状态”演讲,帮助他们了解应该在哪些方面投入资源,以及 SRE 在产品或服务方面究竟做出了什么贡献。
- 开发人员可以向 SRE 团队定期进行一次“产品状态”演讲,使 SRE 团队可以大致了解开发团队在每个季度中所取得的成果,以及后续的方向。
进行定期服务审查
基本规则开始下滑时需要追因
根据 SLO 和错误预算调整优先级
合理处置错误
- 在感到疲倦或情绪激动时,如果可能的话不要进行跟进对话
- 当面开会解决问题
- 保持乐观
- 了解沟通的差异性
在更大的环境里规模化推行 SRE
- 一个 SRE 团队支持多个服务
- 根据技术线(存储、网络)组织 SRE 团队,而不是根据产品域
- SRE 团队结构主动顺应变化的环境(优先拆分而不是从头开始构建)
- 多地域分布式的 SRE 团队
终止关系
SRE:跨越壁垒
- SLO 和 SLI 是 SRE 的语言
- 审核监控系统,构建共享仪表盘
- 度量并重新协商
- 审计评审流程,进行风险分析
- 实践、实践、再实践
- 有思想、有纪律
SRE 团队生命周期
原则:
SRE 需要制定面向后果的 SLO。
SRE 必须有时间使明天变得比今天更好。
SRE 团队需要有工作量的调控权。
第一个 SRE 团队
组建期
- 作为重大项目的一部分组建新团队
- 组建横向新团队
- 转型一个现有团队
规范期
- SLO 和错误预算已经落实到位,领导层对 SLO 度量很感兴趣
- On-Call 轮值已建立且可持续,成员有足够的工具、文档、培训支持
- 琐事工作被文档记录、限制和管理
- 事后总结文化已经建立
- 团队已展示出上面的原则
- 从问题中总结经验教训,并预防复发
- 产品开发团队将 On-Call 中收益
- 定期生成报表
执行期
- 成为所有架构设计和变更的合作伙伴
- 拥有完全的工作量自决定权
打造更多 SRE 团队
原因:
- 服务复杂度:按架构、语言、地域拆分
- SRE 推广:影响越大,优先级越高
- 地域分隔
多团队运作的建议实践
- 角色交换:开发在 SRE 团队实习
- SRE 交换:SRE 与其他团队 SRE 工作
- 培训
- 横向项目:减少重复劳动
- SRE 流动性:转向产品开发团队
- 出差:资金支持
- 成立协调工程团队(LCE)
- 卓越生产:定期进行服务评审
- SRE 预算和招聘:人数应该小于需求,确保定期评估支持服务的优先级
管理 SRE 的组织变革
变革管理:
- Lewin 的三阶段模型
- McKinsey 的 7-S 模型
- Kotter 的变革八步法
- Prosci ADKAR 模型
- 基于情感的模型
- 戴明环
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/303184.html