使用GitOps管理数千个边缘Kubernetes集群

F5生态系统 | 2019年12月18日

在Volterra,SRE团队负责运营一个全球性的基于SaaS的边缘平台。我们需要应对管理大量不同状态(例如在线、离线、管理员停用等)的应用集群所带来的各种挑战。为此,我们利用Kubernetes生态系统和工具,通过GitOps采用声明式拉取模型。

本文将介绍:

使用GitOps有效管理和监控大规模基础设施(物理主机或云主机)及K8s集群

  1. 我们为解决CI/CD相关问题而构建的工具
  2. 对象编排和配置管理
  3. 覆盖整个基础设施和K8s集群群组的可观测性

我们将深入探讨大规模部署(3,000个边缘站点)中的经验教训,这也是我最近在圣迭戈Cloud Native Rejekts演讲中分享的内容。

太长不看(摘要)

  1. 我们找不到一种简单易用的开箱即用型解决方案,能够跨公有云、本地环境或移动位置部署和运维数千个(甚至可能数百万个)应用和基础设施集群
  2. 该解决方案需要为主机(物理主机或云主机)、Kubernetes控制平面和应用工作负载提供生命周期管理,并支持持续配置各种服务。此外,该解决方案还需要满足我们的SRE设计要求,包括声明式定义、不可变生命周期、GitOps,以及禁止直接访问集群。
  3. 在评估了多个开源项目(例如用于部署Kubernetes的Kubespray+Ansible,以及用于管理工作负载的Helm/Spinnaker)后,我们得出结论:这些解决方案都无法在不显著增加各边缘站点软件负担的情况下满足上述要求。因此,我们决定自行构建一个基于Golang的软件守护进程,用于管理主机(物理主机或云主机)、Kubernetes控制平面和应用工作负载的生命周期。
  4. 当我们将系统扩展到单个租户内3,000个乃至更多集群时,先前对公有云提供商、软件守护进程的可扩展性、运维工具和可观测性基础设施所做的假设逐一失效。为应对这些挑战,我们需要重新设计部分软件组件的架构。

边缘的定义

  • 客户边缘(CE),包括客户在云端的位置(如AWS、Azure、GCP或私有云)、本地位置(如工厂车间、石油和天然气设施等),或移动位置(如汽车、机器人等)。CE由Volterra SRE团队管理,客户也可以按需将其部署到所选位置。
  • 区域边缘(RE),这些是Volterra在主要都市市场托管设施中部署的接入点(PoP),通过高度网状互联的自有专用骨干网相互连接。这些区域边缘站点还用于安全互连客户边缘(CE)位置,以及将应用服务公开到公共互联网。RE站点由Volterra基础设施运营(Infra SRE)团队全权管理和拥有。
管理01
图 1:系统概览

上方的架构图(图 1)展示了RE与CE之间的逻辑连接关系,其中每个CE都会与距离最近的RE建立冗余连接(IPSec或SSL VPN)。

边缘管理要求

大约2年前开始设计平台时,产品团队要求我们解决以下挑战:

  1. 系统可扩展性 ,客户需要我们支持数千个(最终达到数百万个)客户边缘站点,这与在云区域中运行少量Kubernetes集群截然不同。例如,我们的一位客户拥有约17,000家便利店,另一位客户运营着超过20,000个充电站。如此庞大的规模意味着,我们必须采用与管理少量集群截然不同的方式来构建工具。
  2. 零接触部署 ,任何人都应能部署新站点,无需深入了解硬件、软件或Kubernetes。边缘站点需要像黑盒一样运行:启动后自动连接中心系统并上线。
  3. 集群管理:简化数千个站点和工作负载的管理,无需逐一处理。请求变更或升级时,任何站点都可能处于离线或不可用状态。因此,站点必须在上线后获取更新。
  4. 容错:即使任何组件发生故障,边缘站点也必须保持正常运行。所有内容都必须支持远程管理,并提供恢复出厂设置或站点重建等故障恢复功能。我们必须假定无法对站点进行现场访问。

设计原则(不使用Kubectl!不使用Ansible!不使用软件包!)

考虑到我们的需求以及运营高度分布式系统所面临的挑战,我们决定制定几项原则,要求SRE团队遵循,以减少下游问题:

  1. 声明式定义:整个系统必须以声明方式描述,以便创建简单的抽象模型,并根据该模型进行验证。
  2. 不可变生命周期管理:过去,我们曾使用Ansible、Salt或Puppet等可变生命周期管理工具来部署大型私有云。这一次,我们希望尽可能简化基础操作系统,并尝试将所有内容都以容器形式交付,无需使用软件包管理或配置管理工具。
  3. GitOps 为管理Kubernetes集群提供标准操作模型。它还提供开箱即用的变更审批、审计和工作流,无需额外构建工作流管理系统。因此,我们决定所有内容都必须通过git管理。
  4. 禁用 kubectl:这是最重要的原则之一,因为任何人都不得直接访问应用集群。因此,我们禁止在各个边缘集群内运行 kubectl,也禁止使用从中心位置运行的脚本,包括集中式持续交付(CD)系统中的脚本。采用推送方式的集中式 CD 系统适用于数十个集群,但如果无法保证网络可用性达到 100%,则显然不适用于数千个集群。
  5. 不盲目追捧热门技术(或工具),以往经验表明,许多热门开源工具并没有宣传的那么出色。我们评估了多个用于交付基础设施、K8s和应用工作负载的社区项目(例如Helm、Spinnaker和Terraform),最终仅在虚拟基础设施部分使用了Terraform,并开发了自定义代码。我们将在本博客后续部分介绍这些代码。

站点生命周期管理

在边缘站点生命周期管理中,我们需要解决以下问题:如何预置主机操作系统、完成基本配置(例如用户管理、证书颁发机构和大页等)、启动K8s、部署工作负载,以及管理持续发生的配置变更。

我们曾考虑使用KubeSpray+Ansible(用于管理OS和部署K8s)以及Helm/Spinnaker(用于部署工作负载),但最终放弃了这一方案。原因是,该方案需要我们管理2至3款开源工具,并进行大量修改才能满足需求。随着边缘集群自动扩缩容、安全TPM模块支持、差分升级等功能的增加,我们的需求也在不断增长。

由于我们的目标是保持简单,并尽量减少直接在操作系统中(Kubernetes之外)运行的组件数量,因此我们决定编写一个名为Volterra Platform Manager(VPM)的轻量级Golang守护进程。这是操作系统中唯一的systemd Docker容器,如同一把多功能工具,可执行多种功能:

主机生命周期

VPM负责管理主机操作系统的生命周期,包括安装、升级、补丁和配置等。需要配置的内容很多,例如大页内存分配、/etc/hosts等。

  1. 操作系统升级管理:遗憾的是,边缘环境并非只涉及Kubernetes,我们还必须管理内核版本和整个操作系统的版本。我们的边缘环境基于CoreOS(或根据客户需求采用CentOS),并设有主动分区和被动分区。计划升级时,更新始终会下载到被动分区。更新的最后一步是重启,此时主动分区与被动分区会互换。唯一需要谨慎处理的是多节点集群的重启策略,因为不能同时重启所有节点。我们在VPM中自行实现了etcd重启锁,使集群中的节点逐一重启。
  2. 操作系统用户访问管理,我们需要限制用户及其对SSH和控制台的远程访问。VPM负责执行所有此类操作,例如轮换SSH CA。
  3. 由于我们自行开发了L3-L7数据路径,因此需要根据硬件(或云虚拟机)的类型,在主机操作系统中配置2M或1G大页

Kubernetes 生命周期

管理功能用于为Kubernetes清单提供生命周期管理。我们决定使用K8s client-go库,而不是Helm。我们将该库集成到VPM中,并使用了其中的多项功能:

  1. 乐观部署与悲观部署:此功能可对需要等待其恢复正常状态的应用进行分类。它只需监视K8s清单中的注解ves.io/deploy: optimistic。乐观部署:创建资源,但不等待状态。这与kubernetes apply命令非常相似,使用该命令时,无法确定实际Pod是否成功启动。悲观部署:等待Kubernetes资源进入相应状态。例如,部署会等待所有Pod就绪。这与新的kubectl wait命令类似。
  2. 预拉取等更新前操作:有时无法依赖K8s滚动更新,尤其是在部署网络数据平面时。这是因为旧Pod会先被销毁,然后才开始拉取新Pod所需的镜像。但对于数据平面,这会导致网络连接中断,因而无法拉取新的容器镜像,Pod也将无法启动。包含镜像列表的元数据注解ves.io/prepull会在应用K8s清单之前触发拉取操作。
  3. 重试和回滚,用于应对应用失败。当K8s API服务器出现间歇性中断时,这种情况很常见。

持续配置

除了与K8s清单相关的配置外,我们还需要通过API配置各种Volterra服务。以IPsec/SSL VPN配置为例,VPM从全局控制平面接收配置,并将其应用到各个节点。

恢复出厂设置

此功能支持远程将设备恢复到初始状态,并重新执行完整的安装和注册流程。对于恢复需要通过控制台或现场访问的站点,此功能至关重要。

尽管K8s生命周期管理在许多人看来可能是一个重要议题,但对我们团队而言,它可能只占整体工作量的40%–50%。

零接触式配置

对位于任何位置(云端、本地或移动边缘)的边缘站点进行零接触配置是一项关键功能,因为我们无法确保能够访问每个站点,也不希望配备大量Kubernetes专家来安装和管理各个站点。这种方式无法扩展到数千个站点。

下图(图2)展示了VPM如何参与新站点的注册过程:

manage02
图 2:零接触配置流程
  1. 启动后,在CE上运行的VPM(以绿色框表示)会向全局控制平面(GC)提供注册令牌,以创建新注册。该注册令牌作为云VM的cloud-init配置的一部分提供,可以是在启动过程中由人工输入的密钥,也可以是预先写入边缘硬件TPM的密钥。
  2. GC收到包含令牌的请求后,可在该令牌中编码的租户下创建新注册。客户操作员可立即在地图上看到新的边缘站点并予以批准,同时可输入名称及其他配置参数。
  3. GC中的VP-Controller生成配置(例如,确定哪些节点作为K8s主节点、工作节点等),并为etcd、K8s和VPM生成证书。
  4. VPM开始引导站点,包括下载Docker镜像、配置大页、安装K8s集群,以及启动Volterra控制平面服务。
  5. VPM会配置连接到最近两个Regional Edge站点的冗余隧道(IPSec/SSL VPN),用于传输数据流量,以及实现站点之间和通过公共网络的互连。

可以看到,整个过程完全自动化,用户无需了解具体配置,也无需执行任何手动步骤。整个设备大约需要5分钟即可上线,并准备好为客户应用和请求提供服务。

基础设施软件升级

升级是我们必须解决的最复杂问题之一。下面明确说明边缘站点中需要升级的内容:

  • 操作系统升级,包括内核和所有系统软件包。运维过标准Linux操作系统发行版的人都知道,跨次要版本升级(例如从Ubuntu 16.04.x升级到16.04.y)很麻烦,而跨主要版本升级(例如从Ubuntu 16.04升级到18.04)则更加困难。对于数千个站点,升级必须具有确定性,不能因站点而异。因此,我们选择了CoreOS和CentOS Atomic,以便通过2个分区和路径上的只读文件系统执行A/B升级。这样,只需切换分区的启动顺序即可立即回滚,并且无需维护操作系统软件包也能保持操作系统一致。不过,我们无法再通过安装新软件包来升级系统中的单个组件,例如openssh服务器。对单个组件的更改必须作为新的不可变操作系统版本发布。
  • 软件升级 ,这包括VPM、etcd、Kubernetes,以及作为K8s工作负载运行的Volterra控制服务。如前所述,我们的目标是让K8s中的所有组件都作为systemd容器运行。所幸,除VPM、etcd和kubelet这3项服务外,我们已将其他所有组件转换为K8s工作负载。

可通过以下两种已知方法向边缘站点交付更新:

  1. 基于推送,推送方法通常通过集中式CD(持续交付)工具实现,例如Spinnaker、Jenkins或基于Ansible的CM。在这种情况下,中央工具需要能够访问目标站点或集群,并且必须处于可用状态才能执行操作。
  2. 拉取式:拉取式方法无需任何集中式交付机制,即可独立获取升级信息。它具有更好的可扩展性,也无需将所有站点的凭据集中存储在一处。

我们的升级目标是尽可能简化流程并提高可靠性,类似于常规的手机升级。此外,升级策略还必须满足其他要求,例如,可能只有站点操作员掌握升级所需的上下文信息,或者设备可能因连接问题而暂时离线或不可用。拉取方法更容易满足这些要求,因此我们决定采用这种方法。

GitOps

此外,我们选择了GitOps,因为它有助于为SRE团队提供标准化的运维模式,以便管理Kubernetes集群和工作流,并审计变更。

为解决数千个站点的扩展问题,我们设计了图3所示的SRE架构:

manage03
图3:GitOps流程

首先需要强调的是,我们使用Git并非只是为了存储状态或清单。这是因为我们的平台不仅要处理K8s清单,还要处理持续更新的API配置、K8s版本等内容。在我们的场景中,K8s清单约占整个声明式配置的60%。因此,我们必须在此基础上设计自己的DSL抽象,并将其存储在Git中。此外,由于Git不提供API,也不具备任何参数合并功能,我们还必须为SRE开发额外的Golang守护进程:Config API、Executor和VP Controller。

下面介绍如何使用我们的SaaS平台在客户边缘发布新软件版本:

  1. 操作员决定发布新版本,并针对git模型创建合并请求(MR)
  2. 此MR获批并合并后,CI会触发相应操作,将git模型配置加载到我们的SRE Config-API守护进程中。该守护进程提供多个API,用于参数合并、内部DNS配置等。
  3. Executor守护进程会监视Config-API;git更改加载后,它会立即开始渲染最终的K8s清单,并在注解中标明版本。随后,这些清单会上传到类似S3的Artifact存储中,路径为ce01-site//.yml
  4. 新版本完成渲染并上传到制品存储后,Executor会向客户API提供一个新状态,表明有可用版本;这与手机提示有新版本可用非常相似
  5. 客户(或操作员)可以安排将其站点更新至最新版本,此信息会传递给VP-Controller。VP-Controller是负责站点管理的守护进程,包括预配、停用或迁移至其他位置。零接触预配部分已对此作了部分说明;VP-Controller负责通过mTLS API更新边缘站点。
  6. 图中的最后一步在边缘站点执行。IPSec/SSL VPN连接建立后,VP-Controller会通知边缘站点的VPM下载新版本更新;但是,如果连接中断或时断时续,VPM会每5分钟轮询一次更新
  7. 获取新的K8s清单和配置,并将其部署到K8s中。借助上一节所述的保守部署功能,VPM会等待所有Pod就绪
  8. 最后,VPM将升级状态发送回VP Controller,随后该状态会推送至客户API。

可在此观看整个工作流的演示:

3,000个边缘站点测试的经验总结

在前文中,我们介绍了如何使用工具部署边缘站点并管理其生命周期。为验证设计,我们决定构建一个包含3,000个客户边缘站点的大规模环境(如图4所示)。

manage04
图 4:3,000 个客户边缘站点

我们使用Terraform在AWS、Azure、Google以及自有的本地裸机云中预置了3,000个VM,以模拟大规模环境。所有这些VM都是独立的CE(客户边缘站点),并与我们的区域边缘站点(也称为PoP)建立了冗余隧道。

以下截图来自我们的SRE仪表板。圆圈大小表示各位置的边缘站点数量。截图拍摄时,约有2,711个健康的边缘站点和356个不健康的边缘站点。

manage05
图5:3,000个客户边缘站点部署

主要发现:运营

在扩展过程中,我们确实发现了一些配置和运维方面的问题,因此需要修改软件守护进程。此外,我们还遇到了某云服务提供商的许多问题,并为此提交了多个支持工单,例如API响应延迟、无法在单个区域获取超过500台虚拟机等。

  1. 优化VP-Controller:最初,我们以串行方式处理注册。由于需要为etcd、kubernetes和VPM生成各种证书,每次注册大约需要2分钟。我们通过使用更高熵的预生成密钥,并增加工作进程数量来并行处理,将注册时间缩短至100个站点不到20秒。VP-Controller仅占用约1个vCPU和2 GB内存,即可为全部3,000个边缘站点提供服务。
  2. 优化Docker镜像交付:开始扩展规模后,我们发现边缘站点的数据传输量非常大。每个边缘站点需要下载约600 MB的数据,按3,000个站点计算,总传输量达到1.8 TB。此外,测试期间我们多次重建边缘站点,因此实际传输量要大得多。为此,我们必须优化Docker镜像的大小,并构建预拉取Docker镜像的云镜像和ISO镜像,以减少下载量。目前,我们仍在使用公有云容器镜像仓库服务,同时也在积极设计通过RE(PoP)分发容器镜像仓库并执行增量(二进制差异)升级的方案。
  3. 优化全局控制数据库操作:我们的所有Volterra控制服务均基于Golang服务框架,并使用ETCD作为数据库。每个站点都表示为一个配置对象。每个站点配置对象都有若干StatusObject,例如software-upgrade、hardware-info或ipsec-status。这些StatusObject由不同的平台组件生成,并统一在全局配置API中引用。当站点数量达到3,000个时,我们必须对对象架构进行一些优化。例如,限制全局配置API接受的StatusObject类型数量,或将其迁移到专用ETCD实例,以降低配置对象数据库过载的风险。这样有助于提高配置数据库的可用性并缩短响应时间,也便于在发生故障时重建状态数据库。其他优化措施包括停止对所有租户执行不必要的站点对象列表操作,或引入二级索引以降低数据库负载。

主要发现:可观测性

随着系统规模扩大,实现整个分布式系统的可观测性带来了更多、更严峻的挑战。

最初,我们使用Prometheus联邦来处理指标:全局控制中的中央Prometheus与区域边缘(RE)中的多个Prometheus实例建立联邦。区域边缘中的Prometheus抓取自身的服务指标,并与其连接的CE所提供的指标建立联邦。顶层Prometheus负责评估告警,并作为指标源供进一步分析。该方案很快达到上限(约1,000个CE),因此我们尝试尽量降低CE数量持续增长带来的影响。我们开始为直方图和其他高基数指标生成预计算序列。这让系统多维持了一两天,之后我们不得不对指标采用白名单。最终,我们将每个CE站点的时间序列指标数量从约60,000个减少到2,000个。

最终,在CE站点持续扩展至3,000多个并在生产环境中运行多日后,我们明确意识到这种方案无法继续扩展,必须重新考虑监控基础设施。我们决定弃用全局控制中的顶层Prometheus,并将每个RE中的Prometheus拆分为两个独立实例:一个负责抓取本地服务指标,另一个负责联合CE指标。两个实例都会生成告警,并将指标推送到Cortex中的中央存储。Cortex用作分析和可视化的数据源,不属于核心监控告警流程。我们测试了多种集中式指标解决方案,包括Thanos和M3db,最终发现Cortex更符合我们的需求。

manage06
图 6:指标采集架构

以下屏幕截图(图 7)显示了在端点数量达到 3,000 个时,抓取 prometheus-cef 所产生的内存消耗。值得注意的是,内存占用为 29.7 GB;考虑到系统规模,这一数值其实并不算高。还可以通过将抓取任务拆分为多个任务,或将向 Cortex 远程写入的操作直接移至边缘端,进一步优化内存占用。

manage07
图 7:RE 站点的资源利用率

下方截图(图 8)显示了在此规模下,Cortex ingestor(最大占用 19 GB RAM)和 distributor 所需的内存及 CPU 资源。Cortex 的主要优势在于支持横向扩展,可以增加更多副本;相比之下,Prometheus 必须采用纵向扩展。

manage08
图8:Global Control的资源利用率

对于CE和RE中的日志基础设施,我们在每个节点上使用Fluentbit服务来收集服务和系统日志事件,并将其转发到所连接RE中的Fluentd。Fluentd将数据转发到RE中的ElasticSearch。Elastalert会评估ElasticSearch中的数据,并根据配置的规则创建Alertmanager告警。我们使用从Elastalert到Alertmananger的自定义集成来生成告警,其标签与Prometheus生成的标签相同。

监控历程中的关键要点:

  • 使用新的Prometheus联邦过滤器丢弃未使用的指标和标签最初,每个CE约有50,000个时间序列,每个时间序列平均有15个标签;经过优化后,每个CE平均减少到2,000个。我们对指标名称使用简单的白名单,对标签名称使用黑名单。
  • 从全局Prometheus联邦迁移到Cortex集群-集中式Prometheus抓取所有RE和CE的Prometheus指标;当CE达到1,000个时,指标数量已难以持续管理;目前,每个RE均部署Prometheus(与所连接CE的Prometheus进行联邦),并通过RW写入Cortex
  • Elasticsearch集群和日志- 分散式日志记录架构 - 每个节点上的Fluentbit作为收集器,将日志转发到RE中的Fluentd(聚合器)- 每个RE中均部署ElasticSearch,并使用远程集群搜索功能,通过单个Kibana实例查询日志

摘要

希望本文能帮助您了解,在管理部署于全球各地的数千个边缘站点和集群时,需要考虑哪些方面。尽管我们已经满足并验证了大部分初始设计要求,但仍有许多方面有待改进……

分享
标签: 2021

相关博客文章

F5纪念2021年世界艾滋病日
F5生态系统 | 2021/12/01

F5纪念2021年世界艾滋病日

值此世界艾滋病日,F5郑重承诺,将与员工、客户、社区和合作伙伴共同努力,消除社会对HIV感染者仍然存在的污名与偏见。

Thingbot正转变为攻击平台
F5生态系统 | 2018/11/29

Thingbot正转变为攻击平台

Lori MacVittie重点介绍了F5 Labs对2018年1月至6月全球物联网设备攻击的研究,并指出了其中令人警醒的影响。物联网设备不仅持续遭到利用,还被转变为攻击平台,这意味着攻击者可以更充分地利用这个必然增长的市场。

借助F5全新Quick Start,在AWS上快速开展业务
F5生态系统 | 2018/11/20

借助F5全新Quick Start,在AWS上快速开展业务

在AWS re:Invent开幕前,我们很高兴与大家分享F5与AWS之间全新的Quick Start集成。F5的Tom Atkins将介绍该服务如何启动、配置和运行在AWS上部署工作负载所需的计算、网络、存储及其他AWS服务。

为什么声明式模型对NetOps自动化至关重要?
F5生态系统 | 2018/04/30

为什么声明式模型对NetOps自动化至关重要?

为什么声明式模型对NetOps自动化至关重要?阅读F5博客,了解基础设施即代码如何简化网络扩展。

开源聚焦:F5基础设施即代码与多云可管理性
F5生态系统 | 2017/11/16

开源聚焦:F5基础设施即代码与多云可管理性

开源聚焦:基础设施即代码与多云。阅读博客,了解模板和Terraform如何简化F5应用服务的部署。

破解IT“无解困局”的答案:私有云
F5生态系统 | 2016/09/12

破解IT“无解困局”的答案:私有云

破解IT“无解困局”的答案:私有云。阅读F5博客,了解本地部署的敏捷性如何化解安全与速度难以兼顾的问题。