LinkedIn:扩展 Salt 以解决大规模机器命令执行需求

Himanshu Chandwani 2023年4月23日

在 LinkedIn,站点工程师喜欢自动化各种基础设施层面的运营任务,以最小化手动干预,并且可以扩展得很好并易于操作。某些自动化是通过按需作业执行来完成的。

LinkedIn工程师已经使用Salt超过十年了,这是一款基于Python的开源软件,用于在主机上执行任务,因为它具有高性能和可插拔性。由于它配备了丰富的执行模块,可以直接使用或通过自定义模块使用,因此适用于诸如操作系统升级、自动修复、应用程序分析、流量转移、固件升级、交换机管理等任务。

Salt 中的基本主从流程如图1所示。Minion(主机上的代理)通过订阅由 master 服务发布在事件总线上的事件来获取作业、上报结果。它使用 ZMQ(ZeroMQ)实现高速异步通信。目标 minion 在主机上执行作业并返回到 master。Master 和 minion 使用 AES 密钥加密通信以确保安全性。

20230423151609

图1:通过事件总线的Salt主从通信流程

在这篇文章中,我们将分享如何包装并将其与 LinkedIn 基础设施集成来扩展 Salt,以实现比以前更高效的可伸缩性,并获得 10 倍以上的远程执行能力,从而自动化任何类型的即时操作工作。

背景

在过去的十年中,LinkedIn已经实现了巨大的增长。为了支持这种增长,LinkedIn工程师将基础设施从一个数据中心扩展到多个数据中心。随着成千上万的微服务不断增加,对更多硬件的需求也在预期之内。近年来,它已经发展到超过300,000个应用程序主机。这种巨大的增长要求工具堆栈在最近4-5年内以10倍速度扩展。这种10倍速度的增长促进了每天构建、发布和部署、应用程序配置推送、网络策略、系统配置等许多方面的高数量级需求。

在过去的十年中,LinkedIn 的许多基础设施工具都在不断发展,以解决各种规模挑战。这包括为数千个应用程序提供的部署系统、容器化、持续集成和交付、服务发现以及应用程序的密钥管理。无论是针对数据/应用程序/用户的授权管理的 RBAC 系统,还是面向服务和用户身份验证与安全性的 PKI 基础架构,或者主机状态/配置管理或应用程序配置,工程师们设计了出色的体系结构和自动化流水线,在大规模运作方面表现出色。

除了 Salt,LinkedIn 还使用其他工具来配置和管理主机的状态,例如cfengine和puppet。拥有多个类似场景的工具会带来操作上的挑战。此外,LinkedIn 的现有解决方案都没有通过 Rest API 提供远程执行功能,可以像 Salt 一样即时和异步响应。

Salt 在 LinkedIn 的情况

在2019年之前,Salt被不同的团队用于不同的用例,例如配置管理、状态管理、日志管理、远程执行作业、制品分发、应用部署、应用性能分析工具、自动修复、用户账户管理、网络设备维护和流量转移等。

20230423162620

图2:老的单集群 Salt

在图2中,单主机用于编排集群中的所有从节点。许多从节点经常将执行结果返回到单个主机,导致由于负载过高而出现停机时间。

直到2018年中期,在每个生产的数据中心每个业务网,我们仍然使用单 Salt master 的部署结构,每个 master 大概管理 55-60k 的 minion。其他数据中心大概还有 6k-10k 的机器,总共加起来大概有 180k 的业务机器,最终有 15 套 salt master(master 一主一备),部署在 24 台机器上。由于执行请求数量过多,Salt 主控节点经常会严重超载,导致作业失败、不可用性增加、停机时间增加,并影响依赖它的各种自动化。除了单一主控节点设置带来的性能影响外,还存在各种运维和性能方面的挑战,例如:

  • Salt 开发几乎没有代码覆盖;我们在构建和维护多个产品时面临着各种挑战,例如我们定期升级的 6 个 RPM、有问题的客户端库、令人困惑的客户端 cli、监控 Salt 服务和日志,以及更多的痛点
  • 手动管理 Salt Master 故障转移
  • 通过一组复杂的 cfengine 策略管理 minions 和 master 配置
  • 为每个数据中心的 Salt 管理 Salt API SSL 证书
  • 配置管理每个数据中心的 Salt master 的 cname
  • 缺少对客户端 Salt 模块的安全检查,并且缺少模块所有权
  • Salt 组件通过自行生成的 RPM 进行管理,这使得升级具有挑战性且耗时
  • 在生产环境中,每个 master 的单个 master 达到近 65K 个 minions,有时会导致性能不佳

仅通过 REST API 远程执行

2018 年,在网站工程组织的协调努力下,我们的工程师决定减少工具堆栈中的噪音,只为每个特定任务保留一个工具。我们的团队还决定将 Salt 客户端的其他用例转移到 LinkedIn 的其他扩展和管理良好的解决方案,这些解决方案专门为此类用例而设计,并通过 REST api 将 Salt 限制为仅用于远程执行用例。我们的计划主要受到 Tim Peters 的 19 句格言之一(即 The Zen of python)的启发。

There should be one and preferably only one obvious way to do it.

我们重建 Salt 基础架构的目的不仅仅是扩展它,还在于简化其所有操作方面并改善客户体验。2019 年年中,我们决定重新构建 LinkedIn Salt 生态系统,将其与 LinkedIn 开发和部署基础架构集成。我们想利用它的众多优势,例如 LinkedIn 的 CI/CD、部署工作流系统、服务发现、应用程序配置管理、密钥管理、容器化和托管服务认证。好处是巨大的。

新产品

我们通过创建五个新的 python multiproduct 来重新构建 Salt 的开发,其中 li-salt-master 和 li-minion 使用上游 python Salt 作为主要依赖项。Multiproduct 模板是一个开发框架,它有助于应用程序开发的各个方面,即使用 pygradle 构建包、依赖管理、应用程序配置管理(它也可以捕获应用程序的密钥)、通过单元测试的代码覆盖率、用于类型的 mypy 检查,以及 flake8 用于样式和语法检查。我们创建的每个 multiproduct 的用途简要说明如下:

  • li-salt-master: 可部署的 Master & API 服务,用于编排 minions 并为客户端公开新的 Salt rest API 端点
  • li-minion: 可安装在所有 300K + 主机上的 python agent。它被包装并打包为带有自定义代码的 RPM,可自动发现相关的 master 主机并在每次启动时生成 minion 配置
  • lipy-lisaltmaster: 客户端的 Python 库。对于非 python 客户端,即 java 或 go lang,记录了简单的 curl 示例
  • lisaltmaster-fileroot: 包含所有 Salt 客户端 ACL 和自定义模块。该产品对客户端模块强制执行安全检查,以确保客户端遵循安全编码实践
  • salt-execute: 此 CLI 命令行工具允许团队通过 api 端点执行他们的模块,即我们的新设计公开的 /execute

新的设计

上游 Salt 的灵活性使我们能够插入自定义插件/模块以实现新的架构流程

Li-salt-master

它是一个充当整体服务的 python 应用程序;它将上游 Salt 库固定为直接依赖项。在部署时,它会启动 3 个服务,Salt-master、Salt-api 和 Nginx。

20230423174046

图 3:带有少量 li-minion 的单独 li-salt-master

  • Salt-master 对批准的客户端进行身份验证和授权,向 minions 发布工作指令,并收集 minions 的工作响应
  • Salt-api 使用 python cherrypy 框架并为客户端公开 rest API;它写在 Salt netapi 之上。它还公开了一个用于服务健康和发现的端点
  • Nginx 主要用作实现 mTLS 的反向代理

我们通过插入自定义模块来覆盖一些 Salt 函数。这项工作利用了上游 multimaster 教程提供的一些知识。

配置:Salt 和 Nginx 配置是使用 jinja 模板动态生成的。模板中的占位符使用来自应用程序配置的值和主私钥、路径、mysql 数据库密码等秘密值进行更新。为了确保 master 冗余,所有 master 主机将具有相同的私钥/公钥对,以便任何 minion 都可以连接到已部署集群中的任何 master 主机。

授权模块:我们插入了自己的 Salt PKI 授权模块。这允许客户端/服务使用其客户端证书进行身份认证和鉴权。

Netapi 模块:我们修改了现有的 Salt 的 rest_cherrypy 代码并添加了新的 API 端点,即:

  • /execute 包装现有的 Salt rest_cherrypy API,即 /minions/jobs,用于在目标 minions 上执行作业并汇总它们的响应
  • /connected 获取连接到集群中每个 master 的 minions 数量,这用于运维目的或者收集监控指标
  • /login 被修改为依赖 Nginx 级别的 mTLS
  • /stats,这个现有的 Salt api,围绕 Salt master & API、Salt Auth QPS / Failures、每秒请求数、每个请求字节数等,扩展了更多的监控指标
  • 添加 /admin 以公开集群的整体健康检查,它还允许通过 DNS 发现服务。 Nginx 被用作反向代理,mTLS 也是通过它强制执行的
  • 客户端无法访问其余所有其他 api 端点

20230423180939

图4:Li-salt-master 认证和执行 API 的流程

  • 令牌:我们插入了自定义模块来管理身份验证令牌,定义令牌创建、检索、列出和从存储中删除。它使用 MySQL DB 作为存储
  • 引擎:我们插入了自定义引擎,用于监控所有执行并同步来自 lisaltmaster-fileroot 数据包的 ACLS 和模块。监控各种 Salt master 和 api 指标,例如连接的 minions 数量或与 Salt 服务的各种组件相关的指标,包括所有 Salt 子进程的内存使用情况、cpu 使用情况、磁盘 I/O、API 2xx/4xx/5xx, QPS、指标等
  • Reactor & Runner:添加了一个自定义的 runner 模块,当 minion 与 master 认证失败的时候,该模块就会触发,认证失败大部分情况都是因为机器重新做了镜像用了一个新的 key 而服务端仍然使用了一个老的 key。在每次重启时,minion 都会与 master 交换其密钥以建立 TLS 连接。 Runner 在接受 master 主机上的新公钥之前验证 minion。

Li-minion

它是一个使用 LinkedIn python gradle 构建的 python 产品,它构建了 RPM 包,安装在所有 LinkedIn 主机上。它包装了默认的 salt-minion agent。它在启动之前生成 minion 配置(配置可能因集群而异),用于在主机上运行 li-minion 代理的 systemd service 文件,用于管理 minion 日志的 logrotate 配置,在本地生成 minion 指标以供稍后离线分析。模块同步作业计划每 10 分钟从各自的 lisaltmaster-fileroot 获取新的/修改的模块。Li-minion 还定义了自己的资源限制,如文件操作数和活动内存,以避免干扰生产服务。它使用主机上可用的事实(如fabric、tag等)发现 master 主机,并且 li-salt-master DNS 记录通过 LinkedIn 服务发现暴露出来。

20230423184337

图5:Li-minion 流程图

20230423184428

图6:Li-salt-masters <–> Li-minions 架构流图,使用 mysql 作为认证 token 的存储(minion 通过 DNS 记录发现 master)

Lisaltmaster-fileroot

它是一个 python 产品,它生成数据包(数据包是一个可部署的包,只包含要放置在主机上某个位置的静态文件)。它包含客户端 Salt ACL 和 Salt 模块。在持续集成期间,每次代码更改后都会运行 python Bandit,以查找客户端模块中的常见安全问题。ACL 或模块中的每个更改都会构建一个新的数据包,并在几分钟内通过 LinkedIn 部署工作流自动推送到所有 Salt Master 主机,因此不需要手动干预。

监控和日志分析

所有指标都发送到 Kafka 管道,这些管道在 LinkedIn 的内部工具“inGraphs”中可视化。异常通过 Autoalert 发出警报,并通过 Iris 通知值班工程师。除了所有 Salt 应用程序和系统指标外,所有 Master/API 日志都通过 Apache Kafka 流式传输到 Azure Data Explorer。日志使用 Azure Kusto 查询语言查询进行分析,并使用 Azure 数据资源管理器可视化以进行实时分析。

20230423185836

图7:Li-salt-master 事件仪表板屏幕截图

总结

新的 Salt 架构支持每天在 LinkedIn 基础设施的主机上执行超过 15000 个远程作业,从而可以轻松扩展和操作,解决以前的挑战。它现在执行的作业数量增加了 10 倍,可靠性和可扩展性也比以前更高。

致谢

非常感谢基础设施工具 SRE 团队的所有同事,除了 Himanshu Chandwani 的贡献外,该项目还得到了 Aastha Nandwani、Bhavik Patel、Ritish Verma 和 Sergii Shchypa 的大力贡献。我们还要感谢 Nidhi Mehta 成为该项目的 TPM 合作伙伴。此外,我们非常感谢我们在 LinkedIn 上的合作伙伴团队,即 Production SRE、Traffic SRE、Espresso SRE、LPS SRE、OSUA 团队、Monitoring infra、On-demand Profiling 团队、InfoSec 团队,以及 LinkedIn 内支持我们的其他站点工程团队努力。最后,如果没有我们的工程负责人 Senthilkumar Eswaran 和 Xi Chen 的支持,这个项目是不可能完成的。

英文地址:https://engineering.linkedin.com/blog/2023/scaling-salt-for-remote-execution-to-support-linkedin-infra-grow


译者注:看起来 LinkedIn 虽然做了很多改造,但是 Salt 的核心没有改,这个基于 ZMQ 的方案看起来性能杠杠的。各位运维同仁,你们用的什么命令执行工具?遇到什么坑和心得?欢迎在评论区交流:)

开源版
Flashcat
Flashduty