早在 2021 年 8 月 Sprint 2.0 大会上发布 NGINX 现代应用参考架构 (MARA) 时,我们便明确了设计目标。我们希望创建一个在 Kubernetes 中运行的现代应用架构,以支持安全性、可扩展性、可靠性、监控和自检。该项目需要跨不同的基础架构进行部署,并使用“即插即用”的方法添加功能组件,从而避免耗时的集成。
在 Sprint 大会之后的几个月里,我们一直按照我们的产品路线图向前推进。与任何项目一样,我们取得过成功,也经历过失败,最终我们成功开发出了 MARA,同时也积累了丰富的经验教训。我们希望通过记录这些问题,让别人在实施设计时少走弯路、多避坑。
最近,我们推出了 MARA 版本 1.0.0,创造了一个里程碑,该版本重新组织了项目目录,并更改了创建和清除部署环境的过程。我们清理了重复的代码,创建了一个可以灵活适应日后功能增加的目录结构,并对管理脚本进行了标准化处理。此版本是 MARA 历史上浓墨重彩的一笔,我们希望将我们取得的进展反馈给社区,记录我们认为一些较为重要的经验教训,并提供关于我们产品路线图的一些信息。
MARA 的核心贡献者不仅拥有大规模运行软件的经验,而且清楚地了解交付服务需要满足哪些运维、工程和管理方面的需求。他们深知那种无法快速找出应用异常的根源时的那种挫败感,因为没有一个简单的方法可以知道问题的根源所在。“调试微服务就像破解连环谋杀案一样”是 DevOps 界广为流传的一句话,它明确地道出了问题的症结所在。
鉴于此,我们将添加可观测性列为短期路线图的首要工作。此处的可观测性包含日志管理、指标和追踪数据。然而,只集数据还不够,您还必须能够深挖数据,并针对环境中所发生的事情做出重要的决策。
我们不断探索追踪选项,最终确定了 OpenTelemetry (OTEL),因为它支持整个遥测数据,包括日志 (logs)、指标 (metrics) 和链路追踪 (traces)。我们设想了这样一个实现:OTEL 代理负责收集、处理所有追踪数据并删除重复数据,然后再将其传输到 OTEL 收集器进行聚合和导出,同时在工作流程结束时使用可视化系统确保数据直观可用。这种方法为用户提供了最广泛的数据显示和分析选项,同时还利用 OTEL 标准简化了早期阶段的操作(采集、聚合等)。
我们分两个阶段实施这个设想:
部署面向 Kubernetes 的 OpenTelemetry Operator,以管理环境中的 OTEL 收集器
装备 Bank of Sirius 应用,以提供链路追踪和指标
MARA 的原始版本使用 Filebeat 和 Elasticsearch 记录日志,尽管我们曾考虑用 Grafana Loki 进行替换,但最终还是决定暂时保留原来的选择。在指标方面,我们意识到,我们需要根据 Prometheus 和 Grafana 修改原来的部署,使用社区维护的 kube-prometheus-stack Helm Chart 替换定制的 Prometheus 配置,前者支持完整的 Prometheus 环境,包括 Grafana、node exporters 以及一系列预先配置的仪表盘和适合管理 Kubernetes 环境的抓取目标。为此我们为 F5 NGINX Ingress Controller 和 Bank of Sirius 应用额外添加了一些仪表盘。
实现 OTEL 需要开展大量的工作,以上只是简短的总结。为了减少大家的工作难度,我们在博文《现代应用参考架构之 OpenTelemetry 集成进展报告》中记录了整个实现流程。
在 MARA 的初始版本中,我们选择了 Amazon Elastic Kubernetes Service (EKS) 作为部署环境。后来许多用户反馈道,考虑到成本因素,他们希望在“本地”环境中运行 MARA,因为这种环境需要的资源(例如实验室或工作站)较少。项目最初(包括现在)的一大目标是实现可移植性,而这正是我们证明我们可以实现该目标的机会。为了更轻松地完成这项任务,我们决定设计一个能够在任何 Kubernetes 集群上运行的基础部署程序。
首先,我们在 MARA 中添加了一个新的 Pulumi 项目,该项目读取 kubeconfig 文件,以便与 Kubernetes 集群通信。该项目位于 Pulumi Kubernetes 项目与基础架构项目(例如 AWS 和 Digital Ocean 项目)之间。实际上,kubeconfig 项目的创建减少了集成新基础架构项目的障碍。如果基础架构项目可以将 kubeconfig 文件、集群名称和集群上下文传递给 kubeconfig 项目,那么 MARA 接下来便能无缝进行部署了。
我们在测试中使用了几个易于安装且对 CPU 和内存要求较小的 Kubernetes 发行版,包括 K3s、Canonical MicroK8s 和 minikube。这些发行版全都部署在运行 Ubuntu 21.10 且配有 2 个 CPU 和 16GB RAM 的虚拟机 (VM) 上。此外,所有发行版均配置为提供持久卷(persistent volumes)和 Kubernetes LoadBalancer 支持。
在这个过程中,如何处理自定义构建 NGINX Ingress Controller(项目的组成部分)时使用的私有注册表成为最大的难题。(注意,在部署 MARA 时,您可以使用基于 NGINX 开源版或 NGINX Plus 的标准 NGINX Ingress Controller,以及这个自定义构建的 NGINX Ingress Controller。)我们发现,我们需要将镜像托管逻辑从 Amazon Elastic Container Registry (ECR) 中分离出来,以支持更为不受平台限制的方法,目前这项工作正在实施当中。此外,我们还意识到,提取出向地址主机名的逻辑仅限于 AWS Elastic Load Balancing (ELB),因此只能重写后再应用于其他用例。
MARA 管理脚本和 Pulumi 项目目前使用一些特定的逻辑来解决上述问题。目前,基于 Kubernetes 配置的部署必须使用来自官方 NGINX 镜像仓库的 NGINX Ingress Controller(基于 NGINX 开源版或 NGINX Plus)。
我们在 MARA 配置文件中添加了几个调整参数,以适应不提供云部署所需资源的本地部署。大多数参数与 Elastic Stack 的各个组件所需的副本数量有关。随着测试的推进,我们将根据部署环境的资源限制添加额外的 MARA 调优参数。
完成这些更改之后,我们可以成功部署到 K3s、MicroK8s 和 Minikube,并且我们已成功在 Azure Kubernetes Service (AKS)、Digital Ocean、Google Kubernetes Engine、Linode 和 Rancher 的 Harvester 上对逻辑进行了测试。如欲了解更多信息,请参阅 “MARA 提供商状态”页面和《在您附近的工作站上运行 MARA》博文。
我们的合作伙伴十分赞同和支持 MARA 相关工作,许多合作伙伴主动联系我们,了解有关该项目的更多信息,询问他们如何在其产品中利用 MARA 甚至添加功能。
考虑到 Pulumi 易于使用并支持 Python,我们选择了它作为 MARA 的核心部分 —— 其中 Python 是一门十分流行的语言,它能够让大型社区轻松地理解 MARA 代码。此外,Pulumi 社区充满了活力,并在该项目中展现了极高的参与热情,这正是我们希望通过 MARA 实现的社区参与的典范。
2021 年底,我们与 Sumo Logic 合作,通过 NGINX Ingress Controller 使 MARA 成为其云监控解决方案的一部分。这是一个检验 MARA 可插拔性的好机会。我们面临的挑战是使用 Sumo Logic 解决方案替代 MARA 中的 Grafana、Prometheus 和 Elastic。令人高兴的是,我们使用其他部署中的逻辑成功构建了该解决方案,并将其配置为不仅连接到 Sumo Logic SaaS,而且还能从我们的环境中提取指标数据。
作为 OpenTelemetry 工作的一部分,我们与 Lightstep 合作,轻松地重新配置了 OTEL 收集器,将链路追踪和指标导出到 Lightstep 的 SaaS 产品。我们迫切希望对该领域展开进一步的研究,因为我们坚信 OTEL 是可观测性的未来。
到目前为止,我们最大的收获是知道了模块化方法的好处。与 Sumo Logic 的合作表明,我们能够成功地混搭使用 MARA 组件。随着我们将 OTEL 更全面地集成到部署环境中,这一结论将有望得到进一步的确认。我们之前提到过,我们正在考虑用 Grafana Loki 取代 Elasticsearch 作为日志管理环境,因为前者减少了堆栈的资源占用。也就是说,我们提倡“实用的模块化”,而不是走将一切都变成微服务的极端。举例来说,尽管使用专门的服务来处理许多应用的日志是有道理的,但需要单独的微服务来收集、存储和可视化日志就有点说不过去了。
此外,我们还了解到,在设置默认值时,比较实用的方法是把它们明确添加到配置中,而不是仅仅省略相关参数。这可以在两个方面给管理员带来便利:管理员无需记住默认值,并且他们只需修改在配置中的正确位置以正确语法出现的参数,即可轻松地更改默认值。
我们学到的另一个痛苦的教训是,一些解决方案之所以受欢迎,并不是因为它们是最有效的,而是因为它们容易安装或者是拥有最好的教程。正因如此,质疑思维和虚心请教才在设计过程中显得如此重要 —— 这种开放沟通文化有助于及早发现和解决问题。
也就是说,有几次我们执行的逻辑是可行的,但结果要么是进入了死胡同,要么引发了其他问题。举例来说,我们一开始通过 Pulumi 部署应用时,我们为 ConfigMaps 使用了 YAML manifest,并依赖 Pulumi 转换来更新变量。虽然这样做是可行的,但出于一些原因却不是很理想,尤其是在可维护性上。在下一次迭代中,我们改进了代码的可读性和可维护性,使用 kube2pulumi 将 YAML manifest 转换为可以用来构建 ConfigMap 的 Pulumi 代码。
当在 deployment YAML 中插入无效设置时,我们学到了另外一个教训。我们不得不重写并仔细检查 YAML 的大部分内容,以确保代码的语法正确并且其输出结果符合我们的预期,这个过程既繁琐又耗时。为了避免再生枝节,现在,我们在 GitHub 推送过程中自动化的进行通用 YAML 和 Kubernetes 特定的检测 (linting)和验证。
最后,我们的目标始终是确保我们的主线版始终可运行。如果您在主线分支新项目时还必须要解决维护人员给主线版带来的问题,这种感觉肯定不好受。遗憾的是,我们在这方面经历了几次失败,包括以下 Bank of Sirius 子模块的示例:
ssh
连接 GitHub,所以这对我们来说不是问题。但是没有 GitHub SSH 密钥的用户在初始化子模块时,会收到铺天盖地的错误消息。
未来几个月,我们制定了宏大的开发计划,包括重构 NGINX Ingress Controller build、Docker 仓库推送和入向主机名/IP 地址逻辑。
我们注意到,在 MARA 的每个开发阶段,攻击者扫描和攻击 NGINX Ingress Controller 的速度都非常快。这促使我们开始将包含 NGINX App Protect WAF 的 NGINX Ingress Controller 集成到 MARA 中,那么如何有效管理 App Protect 生成的日志记录?这个问题既是挑战也是机遇。
在未来几个月,我们还计划让所有模块只从 Kubernetes(而非 Pulumi 和 Kubernetes)中提取 secret。这意味着所有模块都使用公共 secret 仓库,并支持管理员控制如何填充 secret。我们正在编写一个新的模块,该模块能够从用户选择的仓库中读取 secret,并创建相应的 Kubernetes secret。
目前,MARA 包含一个负载生成工具,该工具在 Locust 工具的基础上进行了升级和简单修改,同时也是原 Bank of Anthos 应用(我们从中引出了 Bank of Sirius 分支)的自带工具。我们正在编写的新测试工具 Cicada Swarm 不仅会生成负载,而且还会在指标超过设定的阈值时进行追踪和报告,因此成为了快速测试云软件产品性能的框架。它利用并行化技术来交付测试结果,这些结果具有您需要的置信度、更高的精确度和可自定义的回归分析,可有效确定 CI/CD 流水线上 builds 的成功与否。
最后,说到负载测试,我们就不得不讨论如何衡量负载测试的影响,这又将我们带回到了遥测技术上。OpenTelemetry 蕴含着巨大的潜力,希望很快就会有更全面的实现。即使没有完整的实现,我们的目标也是能够运行测试,衡量其影响,并根据数据洞察制定运营决策。
我们一如既往地欢迎大家拉取请求、提出问题、发表评论。我们的 MARA 目标是鼓励社区共同讨论、尝试、测试和迭代不同的潜在解决方案,以增进我们对如何有效地部署和管理现代应用的理解。
本文是以下系列博文中的一篇:随着 MARA 功能的不断增加,我们发表了以下博文来进行详细介绍:
"This blog post may reference products that are no longer available and/or no longer supported. For the most current information about available F5 NGINX products and solutions, explore our NGINX product family. NGINX is now part of F5. All previous NGINX.com links will redirect to similar NGINX content on F5.com."