架构师的平台工程-全-
架构师的平台工程(全)
原文:
annas-archive.org/md5/ec641f51414ac10506c7857d2f65c8f4
译者:飞龙
前言
欢迎来到 架构师的云平台工程!平台工程是创建环境的实践,能够以安全且高效的方式构建、测试、验证、部署和运营软件。 平台工程关注自动化,并使平台的用户、开发人员和运维人员能够专注于价值创造。 平台通常被定义为 内部开发平台 或 内部开发者门户 (IDP),它抽象了基础设施的复杂性以及支持软件生命周期所需的所有活动组件,从其设置到上线到生产环境。 但平台工程不仅仅是让技术和组件能良好协同工作。 它需要开放的心态和整体性的方法来定义平台的目标,并在决策过程中遵循原则,推动变革文化 和创新。
在 架构师的云平台工程中,我们将引导你建立产品思维,构建一个随着时间推移而逐步老化和成熟,但始终保持年轻活力的解决方案。 一步步地,我们将确定战略方向,并为平台定义目标架构。 这将成为你和与你一起工作的人们的活文档。 在本书中,我们将覆盖平台的四个不同支柱,以及你需要做出的相关决策:由 Kubernetes 表示的基础设施、自动化、自服务能力,以及内置的可观察性和安全性。 到书的最后,你将掌握处理成本的工具,包括实际的基础设施成本和技术债务。 如果两者都管理得当,它们甚至可以成为你克服组织障碍 和政治斗争的最佳盟友。
对于本书中讨论的每个方面和主题,我们还提供了额外的资源,涵盖了完整的技术细节。 我们的目标是为你提供一个参考框架和方法,用于定义可以随着时间成熟的平臺架构,而不依赖于特定的技术或版本。 我们深知生活中唯一不变的就是变化,而这一点同样适用于平台工程。 这就是为什么我们鼓励你作为平台工程师和架构师,保持对这些变化的敏感,并将产品思维带给你的用户 和利益相关者。
本书适合谁阅读
本书适合平台工程师和架构师、DevOps 工程师以及云架构师,他们希望通过将平台作为 产品来转变实现云原生平台的方式。
本书还面向 IT 领导者、决策者和 IT 战略家,他们正在寻找改善其系统架构和软件交付的新方法,书中通过一种全面的方法,超越了简单的“你构建它,你 运行它。”
本书内容
第一章, 平台工程与平台构建的艺术, 介绍了平台和 IDP(内部开发平台)。 本章涵盖了产品思维的重要性,以及构建用户所期望的系统的雄心。
第二章, 理解平台架构以构建产品化平台, 将引导您完成创建平台架构的所有相关基础工作和方法。 您将发现平台作为产品的价值,最小可行平台的首次实现,以及如何观察和衡量其成功 和采用情况。
第三章, 为支持平台功能构建基础, 本章将引导您完成定义平台坚实基础的必要步骤和过程,帮助平台从初始功能集向支持企业的关键 平台功能发展。
第四章, 架构平台核心——Kubernetes 作为统一层, 本章提供了关于 Kubernetes 为何成为平台工程师首选平台的见解。 您将了解核心集成和我们在集中关注 额外增强功能之前必须做出的相关决策。
第五章, 集成、交付和部署——自动化无处不在, 本章为您提供了关于构建、部署、测试、验证、安全、操作、发布和扩展软件的复杂性以及如何通过 自助服务功能集中化和自动化这些体验的稳定理解。
第六章, 为开发者及其自助服务构建,回顾了 IDP 集成的概念,并分享了构建具有弹性、灵活性以及 以用户为中心的平台的最佳实践。
第七章, 构建安全和合规的产品,详细讲解了安全标准框架及趋势;如何利用软件物料清单;并定义了确保平台安全而不限制能力的正确行动。 此外,我们还展示了如何确保应用交付过程提供加固和安全的软件/容器包,并展示如何使用策略 引擎技术。
第八章, 成本管理与最佳实践,解释了平台成本增加元素的概念以及如何优化这些成本。 你将了解标签策略、一般的成本优化场景、如何利用可观察性来识别优化潜力,以及如何将最佳实践 付诸实践。
第九章, 选择技术债务以修复平台问题,为你提供了工具、框架和方法来积极管理你的技术债务。 与成本类似,技术债务可以不断增长,并且如果不加以处理,会对平台造成负面影响。 。
第十章, 为未来打造平台产品,强调了变革的必要性,以及我们作为平台工程师在以可控方式促进变革中的角色,平衡可靠性 和创新。
为了充分利用本书 |
你应该了解云计算的基础知识、Kubernetes、平台工程的理念,以及如何定义 这些架构方面的内容。
本书涵盖的软件 内容 |
---|
Kubernetes |
Backstage |
CI/CD 解决方案,如 GitHub Actions |
Keptn |
Argo CD |
Crossplane |
Prometheus |
OpenTelemetry |
Harbor |
OpenFeature |
Renovate Bot |
下载示例代码文件 |
你可以从 GitHub 下载本书的示例代码文件,链接是 https://github.com/PacktPublishing/Platform-Engineering-for-Architects。如果代码有更新,将在 GitHub 仓库中更新。
我们还有其他的代码包,来自我们丰富的图书和视频目录,您可以在 https://github.com/PacktPublishing/ 找到。快来查看一下!
使用的约定
本书中使用了多种文本约定。
文本中的代码
:表示文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter/X 用户名。 举个例子:“目录路径嵌套在图表中,使用这个名为 crds/
的目录允许 Helm 在将 CRD 添加到集群中后暂停,继续执行 图表执行。”
代码块如下所示:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
version: v1
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
任何命令行输入或输出如下所示:
$ kubectl label nodes platform-worker2 reserved=reserved
node/platform-worker2 labeled
粗体:表示新术语、重要词汇或屏幕上显示的词汇。 例如,菜单或对话框中的词汇会以 粗体显示。举个例子:“这些平台日志应尽可能不包含任何 个人身份信息 (PII) 。”
提示或重要说明
显示如下。
保持联系
我们欢迎读者的反馈。
常规反馈:如果你对本书的任何内容有疑问,请通过电子邮件联系我们,地址是 customercare@packtpub.com 并在邮件主题中提及书名。
勘误:尽管我们已尽一切努力确保内容的准确性,但仍可能出现错误。 如果你在本书中发现错误,我们将非常感激你能报告给我们。 请访问 www.packtpub.com/support/errata 并填写表单。
盗版:如果您在互联网上遇到任何我们作品的非法复制品,任何形式的,我们将非常感激您提供相关的网址或网站名称。 请通过 copyright@packt.com 与我们联系,附上 该材料的链接。
如果您有兴趣成为作者:如果您对某个主题有专业知识,并且有意撰写或贡献书籍,请访问 authors.packtpub.com。分享 您的想法
分享您的想法
一旦您读完 平台工程师的架构师指南,我们期待听到您的想法! 请 点击这里直接进入 Amazon 评论页面 为这本书分享 您的反馈。
您的评论对我们和技术社区非常重要,将帮助我们确保提供优质的 内容。
下载这本书的免费 PDF 副本
感谢您购买 这本书!
您喜欢在移动中阅读,但无法随身携带您的纸质书籍吗? 您的纸质书带不走吗?
您的电子书购买无法在您选择的设备上使用吗? 选择的设备是否不兼容?
别担心,现在每本 Packt 书籍都会附赠无 DRM 保护的 PDF 版本 完全免费。
随时随地,在任何设备上阅读。 直接从您最喜欢的技术书籍中搜索、复制并粘贴代码到您的应用程序中。
优惠不止如此,您还可以每天通过电子邮件获得独家折扣、新闻简报以及精彩的免费内容。 您的收件箱。
按照这些简单步骤获取 奖励:
- 扫描二维码或访问下面的链接
https://packt.link/free-ebook/978-1-83620-359-9
-
提交您的购买凭证 以获得奖励
-
就是这样! 我们会直接将您的免费 PDF 和其他福利发送到您的 电子邮件。
第一部分 – 平台工程与架构概述
在第一部分中,您将进一步了解平台工程和平台架构的基础。 我们将帮助您深入理解平台产品思维,向您展示平台工程不仅仅是构建系统。 然后,我们将引导您完成创建平台架构的过程,从定义平台的目标到最简可行的平台,并讲解如何衡量平台的成功和接受度。 在 第三章中,我们将重点设计平台的基础,以提升用户体验并避免 技术复杂性。
本部分包含以下章节:
-
第一章,平台工程与平台打造的艺术
-
第二章,理解平台架构,以将平台构建为产品
-
第三章,为支持平台能力打下基础
第一章:平台工程学与平台打造的艺术
在本章中,我们将学习如何判断我们的组织是否处于适合规划平台的状态。 为此,我们将澄清为什么平台成为如此重要的话题,产品思维如何融入其中,以及判断我们是否准备好构建平台的检查点是什么。 我们将了解平台之间的差异以及最常构建的哪些平台类型。 常见的平台类型。
接下来,我们将深入探讨平台的三个核心元素:普及的云、开发者体验和平台的主要特性。 总体而言,我们将看到云原生工程中的一些常见元素。 这引出了一个问题:我们是否真的需要另一个抽象层。 我们还将考虑,平台是否能帮助我们克服由于过度工程化复杂系统和开发流程所带来的高认知负担,还是最终只是成为又一层抽象层。 我们将反思一些这些层,以找到答案 为我们自己。
最后,我们将讨论超越技术和平台实现的方面。 理解社会技术层面的内容并将人类,我们的实际利益相关者,置于中心是至关重要的。 这使我们能够定义更好的平台产品,并为紧密合作找到方法。 紧密合作。
在本章中,我们将讨论以下 主要话题:
-
作为产品的需求 平台作为产品
-
实现以开发者和 产品为中心的解决方案
-
我们是否需要另一个 抽象层?
-
社会技术层面
作为产品的需求
在 云原生环境中,近年来几乎没有其他话题像“平台”这一术语一样,给人留下如此深刻的印象,平台 以及平台工程师的角色也随之而来。 就像第一个可用的 CI/CD 流水线的引入一样,这场淘金热导致了快速适应,往往没有明确的目的或理性。 现在我们已经进入知识的谷底,可以深入思考一个问题:你需要一个平台吗?如果需要,如何设计和实施它,确保它能够持续发展到 未来?
为了回答这个问题 我们应该首先看看构成这样一个平台的要素。 平台是不同能力的组合,这些能力是掌握传统和云原生环境所必需的,以便它能够在应用程序的开发、交付和运营中支持最终用户。 平台可以作为推动力,将非云原生基础设施转化为有价值的资源。 然而,今天大多数计算平台提供某种形式的 API,可用于自动化部署和工具化可用资源,并构建平台的基础。 平台为最终用户提供任何类型资源的一致性,并通过自服务 API、模板、CLI 或其他解决方案授予其功能访问权限。 以下示例还突出了平台由许多组件组成: 许多组件:
图 1.1:平台/IDP 示例
我们通常会看到 平台的主题出现在云原生的背景下,但为什么会这样呢? 云原生技术使组织能够在公共云、私有云和混合云中构建和运行可扩展的应用程序。 这种方法通过容器、标准化服务提供、不可变基础设施和声明式 API 等功能得到了最好的体现。 这些功能实现了松耦合的系统,这些系统具有弹性、可管理和可观察性。 这些功能使开发者能够以最小的努力进行频繁的更改。 简而言之,平台是云原生计算的推动力,并利用其工具将其 工具化。
公司和开发者从平台中获益是平等的
软件工程师在云原生平台上的体验不同于在云服务提供商上本地开发软件。 专注于构建面向某一 云服务提供商 (云服务提供商)(CSP)会将你绑定到该封闭生态系统的逻辑中。 当你在云原生平台上构建时,必然会有类似的效果,因为这些平台通常是以 Kubernetes 为中心,利用 Kubernetes API 的高度集成统一性。 然而,问题在于,云原生平台提供相同的体验,而你并不意识到底层基础设施的存在。 由于大多数公司至少有两个到三个云或类似云的服务提供商,而且已经在适应这些服务时遇到困难,云原生平台无疑是一个颠覆性的游戏改变者 [1]。 在云原生平台上开发软件改变了思维方式和架构。 然而,如果不采纳这种思维方式,失败的概率 会很高。
然而,有更多方面需要考虑,平台不仅仅是统一的基础设施管理。 平台必须是为了某个特定的目的而创建的。 平台必须有明确的用途。 关于平台面向谁构建,以及平台工程师的利益相关者是谁,通常的定义认为这些仅限于开发人员。 这些定义忽略了一个事实,那就是整个组织、运维团队和其他专业团队同样从平台中受益。 平台为软件工程师提供了一个简便的入口,来构建、测试、部署、发布和运营他们的软件。 它深入提供使用情况的洞察,并允许看护者和管理员无所畏惧地维护基础设施、平台和集成。 用商业术语来翻译,平台可以提供更快的市场进入时间,并且具备更大的灵活性来改变和调整其组件,同时保持高可靠性和 强大的稳健性。
这对现在的公司意味着什么? 由于市场上 IT 专业人员的短缺、IT 变化的快速节奏,以及培训团队在云技术和云服务提供商方面的超负荷工作,平台为能力引入了合适的断点。 我们需要这些断点,以避免将多个学科合并到一个角色中的趋势,例如 DevOps。 此外,利用 DevOps 方法论的平台工程师并不是 DevOps。 我们需要积极保护这个角色,避免重蹈 DevOps 角色的覆辙,并在定义上保持敏锐。 平台工程师整合专家提供的能力,通过他们的平台简化这些能力的使用,帮助开发人员使用,并为工程师提供自助服务。 然而,开发人员不需要成为多个领域的专家,例如安全性、可观测性、基础设施配置和自动化等。 这与常见的 DevOps 角色有所不同,DevOps 需要成为自己领域内所有必要方面的专家,以确保其应用的正常运行。 未来我们仍然需要 DevOps 来处理更高级的应用管理,但我们也必须让他们的工作 更轻松。
该平台为需要专业知识的自下而上的能力提供集成层,例如安全性、数据库,甚至是虚拟机或裸金属服务器的部署,同时也提供开发人员和 DevOps 的自上而下使用。 如下面的图所示,平台工程团队负责提供 这一层。
图 1.2:平台驱动型组织中的能力与责任
当然,这也 意味着必须培养和教育另一个专家团队。 然而,相比之下,平台工程师的小团队通常能够构建并运行庞大的环境。 理想情况下,平台减少了公司内其他团队的认知负担,让他们能够专注于核心价值,通过简化开发过程中的机制来帮助他们。 这个平台有助于减轻压力并提高透明度。 来自世界各地的公司常常分享他们在平台和平台团队方面的经验,通常的调子是讲述他们如何解决之前无法应对的问题,或者这种方法如何提高了他们产品 和服务的质量。
这些平台通常被 称为 内部开发者平台 (IDP),因为它们通常是为企业的内部开发团队而构建的。 在本书中,我们将交替使用平台、IDP、平台产品和云原生平台这些术语。 然而,我们首先会稍微强调一些方面:
-
平台:统一服务的跨技术层的通用术语,允许开发者使用。 这些平台通常被称为
-
IDP:强调开发者、 软件开发生命周期 (SDLC),以及 开发软件所需的工具
-
平台产品或平台即产品:强调专门的团队负责平台演进能力及其长期承诺,同时建立一种 不同的思维方式
-
云原生平台:专注于抽象和使能使用标准化 API 以及集成 的能力
这种视角 可能感觉很细致,但平台这一术语本身往往会导致更多的混淆。 云平台也是一种平台,对吧? 一个 软件即服务 (SaaS) 也可以视为平台。 提到云原生平台或 IDP 可以引导我们走向正确的方向并获得更准确的理解。 根据您组织的成熟度,明确这些术语并建立共同的理解、语言和 共享知识也是至关重要的。
平台案例研究和成功故事
为了 突出平台的积极影响,我们可以通过三个完全不同的公司及其使用 IDP 后的结果来进行展示。 所有这些案例主要集中在 Backstage 作为开发者门户和进入点的角色, 以及 IDP。
作为 Backstage 的发明者和 IDP 运动的发源地,Spotify 声称以下说法适用于其内部的 Backstage 用户:
-
GitHub 上活跃度高出 2.3 倍 更多
-
代码更改多出 2 倍 部署更改次数
-
部署次数增加了 2 倍
-
新开发者的入职时间从 60 天缩短至 20 天
Expedia 集团报告 了不同的数据:
-
平均而言,创建一个新组件 或应用程序需要四分钟
-
超过 4,000 名用户每天至少使用 IDP 20 分钟 以上
-
技术文档每月被查看超过 50,000 次 每月
-
超过 15%的内部开发工具已与 Backstage 集成 从而减少了上下文切换
现在,最后一个 我们应该关注的公司是丰田:
-
项目现在每周交付一次工件 而不是每月
-
每个团队节省了 8-12 周的额外工作,从而减少了超过 500 万美元的成本或用于 价值创造的时间和预算
-
标准化部署模板减少了失败并加速了部署 部署速度
这些数据对于了解数字原生公司、旅游科技公司以及世界上最大的汽车制造商之一的背景非常有趣。 这些数据中的任何一项都能清晰地展现出正面 效果 [2]。
项目与产品
谈到 组织,引入新解决方案通常作为一个项目来进行。 因此,在某个时间点,有人决定投资建立自己的平台。 这种方法面临一个根本性的问题:截止日期。 项目需要在规定的时间和预算框架内达成目标。 如果项目耗尽时间或资金,它将专注于其运维。 生命周期的这两个部分被当作不同的事情来处理,造成了朝阳与夕阳的时间段。 简单来说,在实施阶段,你会看到大量的投资、沟通和兴奋。 然而,在达到截止日期后,项目变成了一个需要维护的死物。 DevOps 并没有改变这种行为;它只是常常有了新的名称、新的角色和不同的流程。 然而,最终,预算、人力和注意力被转移到下一个项目,而仅有一小部分原有的预算得以保留。 这对于那些在实施过程中辛勤工作的工程师来说是令人沮丧的,随着纯粹运维成本的不断增加,这对于组织来说也将变得令人沮丧。 然而,构建该平台的人可能会离开或加入其他项目。 这种短期视角的系统实施方式,慢慢地扼杀了许多优秀的项目和团队精神。 更重要的是,它显示了解决方案的商业价值并不明确。 当一个实施方案(如平台)能够提供明确的价值时,就不应该有理由转移注意力并导致 它的夕阳。
尽管将许多实现作为项目来进行是一种有效的方式,但对于平台实现而言,这却是它的死刑。 常规实现是功能完整的;完成后它们可以存在。 但是,平台总是在不断发展、不断升级,并且总是实现新的功能。 在与开源和云服务提供商合作时,你将会早早了解到它们在自身开发周期中的工具和软件的更新速度。 功能、修复和安全补丁会持续发布。 这对大公司来说是一个重大挑战,因为它们仍然习惯于远远慢于此的发布周期。 保持与这种快速发展的速度同步的好处在于,作为一个组织,你可以频繁地从新的功能和能力中获利。 它是创新的驱动力和使能器,使你能够以其他方式实现系统,并解决你可能没有意识到的问题。 对于你来说,不痛苦的问题,真的算是个问题吗? 组织通常不会将这类问题视为问题,因为它们习惯于只识别痛苦的过程 和方法。
让我们看一个例子。 在当前年份(2024 年),欧盟发布了一项法律,旨在改善企业的碳排放报告。 从高层次来看,这也包括 IT 资源。 此外,在过去的几年里,多个开源基金会和项目已开始致力于为软件的能源消耗提供透明度。 一年前,我们只能报告非常粗略、估算的数字,关于数据中心、服务器的能源消耗,以及经过一些人工处理后,某个软件的能源消耗。 今天,我们可以为在裸金属、虚拟机管理程序或 Kubernetes 容器中运行的任何应用获取细粒度的信息,因为工具已经发展到可以提供这些数据。 公共 CSPs 提供越来越多的关于其自身能源消耗的见解。 我们可以期待来年会发生什么? 我们可以期待更好的数字,包括能源的区域性碳混合,以及这些数字的端到端可见性和透明度。 对于平台和平台工程团队来说,这种透明度将随着时间的推移自然到来。 它不需要一个项目来彻底颠覆 IT。 提出这一需求将促使平台工程团队将这些功能实现到平台的核心中,惠及所有使用该平台进行构建、部署、发布和 运营软件的人。
这被称为产品思维方式,对于平台来说,适应其环境的需求是自然而然的。 他们 的 环境。
平台作为一个产品
作为产品的 平台是以用户为中心,倾听并积极研究最终用户的需求,不断改进其服务。 一个产品也意识到它的价值。 类似于你手机上的任何应用,它利用自身的价值来为进一步的开发和新功能提供资金。 在这里,没有截止日期,也没有结束。 目标仅仅是通过每次发布变得更好。 这给组织带来的是一个专家团队,持续致力于为业务提供有价值的平台的核心能力。 价值创造。
将平台设计和开发作为一个产品,超越了单纯的工程学层面。 它面临着组织方面的挑战,这些挑战应当在你决定积极构建平台时考虑进去。 事实上,你需要提供有效的数据来证明你的平台带来了好处,并且展示出平台能够覆盖其自身的成本。 这必须在产品负责人和平台工程师的思维方式中。 这里的想法不是要成为商业人士,而是要能够清楚地传达存在的理由,更重要的是,成为一个没有 截止日期的产品。
现在,你可以找到三种不同类型的作为产品的平台:
-
IDP 平台:
-
为 软件工程师提供最佳体验
-
为开发和运营团队提供端到端支持和可视化, 支持他们的软件
-
引入治理、合规性 和安全性
-
为开发团队建立自助服务,并简化 部署过程
-
-
数据科学和机器学习平台 :
-
与 IDP 平台类似,并且通常是从这些平台演变而来的 。 这些平台
-
利用其可扩展性进行数据的研究、分析和处理, 高效地降低数据成本
-
克服了复杂的实施过程,并使它们 普遍可用
-
提供直接、安全的访问相关的 数据源
-
-
低代码/商业平台:
-
强烈的趋势驱动着提供平台,这些平台能带来解决方案,使得可以在相对较少甚至几乎没有编码需求的情况下实现新特性。
-
我们将在未来几年中更多地看到它们。
-
在我们的书中,我们将专注于产品中心化的 IDP 架构视角。
你需要一个平台吗?
和任何其他复杂环境一样,我们首先要问:我们真的需要一个平台吗?我们知道我们将如何使用它吗?
尽管平台可以提供很多好处,但它们并不总是你组织问题的答案。以下是你还未准备好使用平台的迹象:
-
你只有单体应用程序。
-
你没有自己的开发团队。
-
你的 DevOps、SysAdmin 或基础设施团队工作过度,或者被孤立。
-
你有非常简单的应用程序,这些应用可以运行在任何地方。
-
你很难为培训提供预算,以帮助你的团队提升技能。
-
你通常运行商业化的、现成的解决方案。
另一方面,什么时候使用 IDP 对你来说有意义呢?以下标准是你准备好使用IDP的指示:
-
你有多个基础设施环境的需求,或者推动一个多云战略。
-
你需要对你的环境进行高级控制(安全性、合规性,以及对基础设施和应用行为的深度洞察)。
-
你的开发团队不断被无价值的任务所压垮。
-
你有一个好奇且感兴趣的 DevOps 或基础设施团队,他们已经迈出了向平台过渡的第一步,尽管他们并没有意识到这一点。
-
你的应用程序由于微服务架构需要某种类型的编排,因为许多组件或不同的集成需要良好地协同工作。
-
你希望让组织能够优化 IT,以降低成本、提高透明度、质量,或安全性。
在为你的组织定义一个平台产品之前,你应该回答清楚检查清单上的所有问题。 有多个点来说明你为什么需要它是有道理的。 例如,仅仅因为某个团队请求一个 IDP,或有人在会议上提到他们听说过它,并不足以作为做出此类决定的坚实基础。 平台的引入是一个过程,根据我们的经验,它可以在相对较短的时间内成为公司核心的焦点。 在这样的压力下,你仍然需要有明确的目的 和方向。
现在我们已经学会了如何定义平台的目的,接下来我们需要讨论一个问题:我们真的需要这个额外的 抽象层吗?
我们真的需要另一个抽象层吗?
让我们简要总结一下到目前为止的内容。 一个平台为你现有的基础设施和环境加上了一个括号,并在其上加了一层抽象。 该平台通过进一步的功能增强它,使得你的开发团队能够以自动化的 自助方式利用它。
从技术角度来看,这代表着下一个抽象层次。 因此,讨论我们放置在其上的这层新结构是有道理的。 从底部到顶部,我们可以看到裸机,接着是虚拟化的超监视器;其上是云服务提供商。 有些人可能会包括容器、Kubernetes 或无服务器组件——而现在,我们将添加我们的平台。 这些至少是四个层次,每一层都承诺让其下方的层次更简单,并通过 一个难以定义的元级脚本、 基础设施即代码 (IaC)、云库和自动化将它们粘合在一起。 那么,我们真的需要这一层额外的抽象层吗,还是我们只是在用它来让自己 忙碌 地建造东西?
简化抽象层
没有 简单的答案,但通过探讨每一层的目的,你或许能够更好地理解它 。
最初引入虚拟化技术的目的是简化主机供应,以便软件能够运行并更好地利用服务器。 今天,它们仍然具有同样的作用,但可以被 Kubernetes 和容器运行时所取代。 反对这种替代方案的一个关键论点是,虚拟机提供更好的隔离和更高的安全性。 在不深入讨论的情况下,也有提供非常坚固隔离的选择,比如 Kata 容器。 唯一令人头疼的组件是带有操作系统和其运行时的容器。 展望未来几年,WebAssembly (Wasm)可能会成为解决这一问题的一部分。 在容器中没有操作系统,只有纯裸二进制文件,几乎没有攻击门路。 然而,让我们再给它一些 时间。
基础设施即服务 (IaaS)提供商 和公共云提供商通过软件定义的存储和网络增强了这一能力,减少了构建自己数据中心及管理所有物理依赖的复杂性。 此外,它们还提供了常用场景的进一步功能,如数据库、负载均衡、用户管理、消息队列或者 ML 平台和预训练的 AI 模型。 这是一个非常有用的实施方案,推动了行业的快速发展,扩展了可能性。 然而,这也导致整个行业朝着一个问题导向发展。 技术的发展速度超过了人们和组织,尤其是它们能够适应的速度。 我们看到全球专业工程师的短缺,而企业每年都在考虑提供更多的数字服务。 因此,解决方案及其依赖关系都是本地化到云端。 这种努力的回报可能是显著的。 你可以用一个相对较小的全球团队管理任何类型的基础设施和服务。 然而,现实也表明,平均每家公司拥有两到三个 IaaS 和公共 CPS,加上(通常还有)自己的计算能力,以及大约 10 个 SaaS 提供商,大企业可能达到 50 个 [1]。CSPs 还提供 40 到 200 种不同的服务。 换句话说,今天我们能够取得很大成就,但这些环境的复杂性也 变得相当重要。
为了驯服这个 规模,IaC 和 云开发工具包 (CDKs)已经成为管理你的着陆区和软件集成的首选工具。 故事中有趣的部分是,像 DevOps 这样的实践经常被误解,已经使情况变得更糟。 这些误解现在已经导致开发人员也期望设置和维护他们 软件需求的基础设施。
最后但并非最不重要的是,我们有基于容器、Kubernetes 或无服务器的系统。 每种环境都有几十种选项可供部署代码和运行组件。 理解这些需要关注的层次太多了。 然而,它们的发展是合理的,因为你不希望像以前那样运行软件。 将代码推送到镜像,然后选择运行时简化了 配置过程。
总体而言,要表示复杂性水平,我们可以想象一个三维对象,比如一个由立方体构成的立方体。 下面的插图展示了不同的服务层,代表了抽象的不同成熟度,以及每一层如何组合形成一个 IT 环境。
图 1.3:计算抽象和简化的多维复杂性
现在,这个图表 过于简化,考虑到每个维度中你拥有的数百或数千个选项。 然而,它仍然给出了一个很好的提示:如果你需要一个平台,建立一个包装器围绕这个结构,并驯服它的巨大复杂性以利用它的力量。
对软件工程师和其他 IT 专业人员的认知负荷
为了管理所有这些层次,我们需要了解并使用许多工具,并遵循各种流程。 在花费大量时间处理那些本应简化我们工作的事情时,专注于实际工作并创造价值变得困难。 这就是所谓的 认知负荷 。最早由丹尼尔·布赖恩特(Daniel Bryant)提出,这个术语概括了许多开发者以及其他 IT 专家的工作过载和心理压力, 以及其他 IT 领域的专业人士。 减少认知负荷能带来更多的幸福感和满足感,同时也能提升工程师的效率和可靠性。 看一下下面的图示,可以简化我们在不同时代作为专业人士需要处理的事务的视角。 然而,往前看,我们必须减少这种负荷。 人工智能可能是其中的一部分,此外,还有运行计算过程的新概念,以及 平台,当然也在其中。
图 1.4:扩展的认知负荷及其对理想未来的预测
不仅仅是 技术随时间变化,它还会不断积累。 这意味着我们不仅要运行和维护遗留系统,还要改变架构风格,引入新的编程范式和新工具。 这也改变了职责范围,并且大大超出了几年前职位描述中的典型边界。 拆解这个问题可以揭示出是否需要一个平台来解决我们的难题,或者实施一个平台是否会再次增加 复杂性。
最终,每个组织都是不同的。 有些组织停留在 2000 年代初,而有些组织则不断尝试适应未来的变化。 即便在同一个组织内部,你也常常会发现巨大的差异。 一个部门可能仍然在自己的数据中心上运行所有虚拟机,而下一个部门可能将功能部署在全球 CDN 或边缘提供商上。 因此,如果你确实需要平台,制定一个愿景、战略和目标就落在了你身上。
我们所经历的复杂性以及对工程师施加压力的行为需要被面对,因为信息技术随着时间的推移往往变得更加复杂。 在接下来的章节中,我们将重点讨论为开发者实施正确的解决方案,以克服我们正在走向的那个麻烦方向,而这个方向甚至可能导致 职业倦怠。
实施面向开发者和产品的解决方案
在接下来的几年里,我们将见证云计算的发展。 在此背景下,平台将发挥至关重要的作用。 一方面,云将无处不在,成为基础设施的抽象。 无论它是边缘计算的形式,还是非常专业化的服务或产品,这都不重要。 另一方面,正如我们在上一节中所了解到的,我们必须专注于提供能够为开发者和其他角色带来最佳体验的环境,这样他们就可以专注于创造价值。 将这些元素实践性地结合起来是 IT 组织跟上市场步伐并为公司持续创造价值的关键能力。
普遍存在的云
普遍存在的云 并非 单一的解决方案。 它聚集了多种正在经历 转型性变革的云计算能力,以显著推动业务和创新。 关键的进展专注于将云技术整合到任何地方,从私有数据中心、分布式计算网络到边缘计算。 然而,普遍存在的云不仅限于此。 它遵循通过传感器、物联网组件、移动设备和其他智能连接解决方案来弥合物理间隙的概念。 因此,它也被称为 无处不在的计算、 环境智能、 或 无所不在。
Gartner,这家研究公司,认为以下六项技术将塑造普遍存在的云并定义其 特性 [3]:
-
增强型 FinOps:将 DevOps 方法与成本优化 和预算管理相结合
-
云开发环境 (CDEs):简化和统一开发 环境,减少人为错误并 确保可重现性
-
云可持续性:实现 环境、社会和经济效益,减少云计算技术快速增长带来的有害影响,并利用其力量 造福社会
-
云原生:实现之前定义的 云特性
-
云端扩展至边缘:CSP 能力扩展至 边缘
-
Wasm:一种潜在的无处不在的运行时和二进制格式,适用于各个地方,但不 一定适用于所有事物
然而,我们 需要问自己,为什么这些现在对我们作为平台工程师、架构师 和开发者有意义。
首先,你可以在这些定义和假设中找到我们已经在使用的许多技术。 云原生、FinOps、边缘计算和 CDE 已成为日常现实,而可持续 IT 和 Wasm 在近年来经历了大量开发。 这一切都与明确表明我们讨论的不是未来 100 年内无法实现的科幻技术相关。 它正在发生,且已准备好投入使用。 我们开发并创新了所有这些基础;它们可能不像 GenAI 那样显而易见和突出。
其次,为了从云投资中提取最大价值,企业必须采用自动化的运营扩展,利用云原生平台工具,并实施有效的治理。 这些平台集成了 SaaS 等关键服务, 平台即服务 (PaaS),以及 IaaS,以创造具有模块化功能的综合产品。 鼓励 IT 领导者利用这些平台的模块化特性,以保持在快速变化的市场环境中的适应性和灵活性。 想象一下,在没有一个能够驾驭这些广泛变化的的平台的情况下,环境会有多复杂。 尽管如此,面对所有这些复杂性,我们仍然需要保持产品思维,否则在未来提供可靠的 IT 服务和解决方案将变得困难。
图 1.5:云概念在普遍的云中无处不在
查看 前面的图示,你可以发现无处不在的云元素。 我们不应该把这张图看作是独立的项目。 一切都是相互连接的。 手机上的应用与云端或本地集线器中的服务进行通信,企业有多个网络将不同的计算环境连接在一起,而我们完全忽略了更进步的概念 例如 Web3 在这里。
重要提示
信息技术正经历着一场重大的变革,无论是可见的还是不可见的领域。 随着我们迈出的每一步,我们在增加其复杂性的同时,面临着人口压力和专业人才短缺的挑战。 迟早,大多数公司将需要拥有自己的平台。 如果他们 没有,他们将购买 作为服务。
专注于开发者体验
希望每个开发者都能在没有过度疲劳的情况下,涵盖广泛的工具和技术领域,是不可持续的。 因此,用户体验的质量在决定平台的采用和成功中至关重要。 一个设计良好的平台意味着它是直观的,易于导航,并与开发者的期望和工作流相一致。 提升体验包括简化互动、最小化摩擦点,并提供一个视觉、技术和功能上都令人愉悦的环境。 这不仅提高了用户满意度,还提升了生产力和参与度。 这不仅提升了用户满意度,还增强了生产力和参与度。 问题是如何 实现这一点。
我们必须考虑到每个开发者在设计平台时可能有不同的偏好。 这直接涉及到平台与用户之间的互动问题。 开发者可能会提出各种问题,例如: 我们需要设置一个门户吗? 在 Git 服务上推送代码就足够了吗? 我可以通过 CLI 与平台互动吗? 这些问题可能难以回答,但成功的平台提供了所有这些互动。 从 API 中心的方法开始,将使得任何其他路径可以同时进行。 强大的 API 是一个好平台的核心。 实际上,大多数平台仍然提供多种不同的接口。 快速发展的工具将统一这些接口,从而克服这些挑战。如果考虑到在全新环境下构建,它可以直接嵌入到 核心中。
这样一个核心的例子是 Kratix。 这个 Apache 2.0 许可的开源平台自称是一个“... 用于构建可组合 IDP 的平台框架。” 在下图中,你可以看到 Kratix 如何在我们今天使用的所有常见工具之间定位自己,并提供一个 入口点。
图 1.6:Kratix 作为中央集成组件的概述
Kratix 通过 Promises的概念实现这一点,它本质上是一个 YAML 文档,定义了平台与用户之间的契约。 每个团队都必须经历一个复杂的入职过程,这并非由于平台本身,而是由于其他依赖项,如 CI/CD、Git 仓库以及将一切连接在一起。 通过 Kratix Promises,你可以将所有这些步骤封装起来,或者将多个 Promises 合并为一个。
目前,Kratix 支持简化平台基础架构以提升开发者体验,但仍然缺少一些内容。 问题的另一面是开发者门户。 Backstage 是一个由 Spotify 开发的开源 Apache 2.0 许可证解决方案的示例。 Kratix 与 Backstage 配合得很好,且实现了无缝集成。 Backstage 是一个框架,旨在声明式地创建图形用户界面,统一基础设施工具、服务和文档,从而提供极佳的开发者体验。 Backstage 有三个核心特性:服务定义、Backstage 服务目录和插件系统,通过它你可以启用其他功能,例如文档。
图 1.7:Backstage 的三个核心特性
此时,我们 已经看到了需要解决的挑战,并且我们已经对解决方案空间有了初步了解。 这应该能让我们在深入探讨接下来的章节中的细节之前,感受到当前的可能性。 下一章将继续探讨具体内容。
平台的属性
一个平台 必须具备某些属性,并提供一些核心组件,使我们能够向最终用户提供功能。 到目前为止,我们已经了解了需要处理的所有复杂性、需要集成的内容以及在创建最佳体验和产品时如何专注于最终用户的思维方式。 所有这些都必须与技术、过程或方法论的方式相匹配,以形成属性。 对于你平台的理念开发,我们只能鼓励你先研究这些属性,然后决定最适合你的解决方案,而不是先选择一个解决方案并围绕它构建对你情况的适用性。
一些属性,比如减少认知负担、将平台视为产品或开发者/用户体验,已经得到了很好的覆盖。不过,仍然有更多需要考虑的:
-
灵活性、可调性和可组合性:平台应该提供灵活性,允许它们以不同的方式被使用和与其他系统集成。 通过支持模块化和可组合的设计,用户可以根据自己的具体需求自定义和扩展平台,添加可选功能,而不会被不必要的功能所困扰。 这种方法使平台能够为拥有多样化需求的广泛用户群体提供服务,同时 保持简洁性 和可管理性。
-
安全:安全是必须嵌入平台设计和架构的关键属性。 默认安全 意味着 平台在开箱即用时采用最佳安全实践和配置。 用户应该拥有强大的 安全措施,无需进行大量配置。 这包括数据加密、安全访问控制以及定期安全 更新以防范漏洞。
-
自助服务:启用自助服务功能使用户能够执行设置环境、部署应用程序和访问服务等任务,无需等待 IT 支持。 这种能力不仅加速了工作流程,还减少了平台团队的运营负担。 自助服务门户应用户友好,并提供所有必要的工具和权限,使 用户能够独立管理他们的 任务。
-
文档和支持:有效的文档和入职培训对于赋予用户权力和减少与平台相关的初始学习曲线至关重要。 全面、清晰和易于访问的文档确保用户能够自主解决问题并了解平台的能力,而无需外部帮助。 入职过程应该指导新用户了解平台的核心功能和功能,使他们从 一开始就感到能力和信心。
你可能已经意识到,这些属性在某种程度上符合本书的大纲,因此它们是我们设计和规划的灯塔 平台。
有时,这些 属性可能过于软化,无法作为实施的参考点。 这一问题的替代方案被内部开发者平台社区(https://internaldeveloperplatform.org/)描述为 IDP 的五个核心组件 [4],我们鼓励您查看作为 替代来源:
-
应用配置管理:以动态、可扩展和 可靠的方式管理应用配置
-
基础设施编排:根据上下文以动态和智能的方式编排基础设施 方式
-
环境管理:使开发者能够在需要时创建新的并且完全配置好的环境 。
-
部署管理:实施持续交付或甚至持续部署的交付流水线 。
-
基于角色的访问控制:以可扩展的方式管理谁可以做什么 。
在 第二章中,我们将详细讨论平台的组织和技术方面,以及制定一个可实施计划来将你的平台打造成一个 产品。
关注开发者视角意味着影响组织,从而影响为其工作的人们。 接下来,我们将探讨在 创建平台时需要考虑的社会技术因素。
理解社会技术方面
设计一个 成功的平台不仅仅涉及技术能力;它还需要深入理解用户的多样化需求,从早期阶段开始促进和激励协作,并创建一个欢迎平台文化的开放环境。 平台工程的这些社会技术方面是至关重要的视角,它们不仅强调平台的技术组成部分,还强调人类元素——个人和团队如何与系统互动,系统如何影响他们的工作和行为。 理解这一常常不可见的部分 在创建平台时至关重要。 它是将技术上强大的系统与深度整合的日常工作流和用户行为联系起来的粘合剂。 尊重这一几乎元层面的因素意味着通过高满意度提高生产力并推动采用。 我们必须承认,每一个技术决策都具有后果,既影响技术本身,也影响人类。 因此,设计能够与用户产生共鸣的平台,促进协作文化,推动创新,并让日常工作变得更加轻松是很重要的。 通过关注这些社会技术方面,平台工程师可以创建出更具适应性、可持续性和以用户为中心的系统,这些系统能够在不断发展的 云原生环境中经得起时间的考验。
然而,我们必须意识到,我们的互动是在一个社会技术系统中进行的。 挑战在于,我们在工作、个人和私人环境中不断发生摩擦和优化。 工作环境代表着你在专业领域中的所作所为。 个人环境是一种以“我”为中心的视角,分享着与任何与你接触的人的经历,可以是工作或私人方面的内容。 最后,私人环境是别人可以估计和观察到的内容,也就是发生在你关闭门后但可能对你的观点和视角产生影响的事。 这种不断的变动和调整发生在公司或项目结构、人员、技术以及他们的任务之间。 在此过程中,我们有不同的影响因素和激励驱动因素。 以下的表示应能可视化这一持续的运动。 子系统和系统本身有其自身的规则、活动, 和权力。
图 1.8:社会技术系统,Trancossi 等人。
当我们 在确定实施平台的最佳方法时,试图了解需求、建立社区,并倡导开放时,我们必须意识到这一切都受到并发生在一个社会技术系统内。 有时我们可能会被阻塞或被推向不同的方向,或者发现很难激励人们。 这是花些时间理解子系统和外部系统推动你的时机。 这些子系统和外部系统正在 推动你。
理解平台设计中的用户需求
平台 设计必须本质上具有灵活性,以容纳比开发人员更广泛的利益相关者。 我们不能说得太多,但实施一个平台是一个全员参与的决策。 软件工程师、产品负责人、业务利益相关者和操作团队都有独特的需求和挑战,他们期望平台能够应对。 开发人员可能寻求简便的部署和测试工具,而最终用户需要直观的界面和无缝的互动。 与此同时,业务利益相关者很可能主要关注 投资回报率 (ROI)、安全性、治理、合规性和可扩展性。 作为平台工程师和架构师,你有责任识别这些需求,并在平台生命周期中保持平衡。
因此,第一步 在以用户为导向的平台设计中,是准确识别和理解与平台互动的不同利益相关者群体。 在早期阶段,可以通过访谈和调查来获得清晰的了解。 考虑从 360 度角度来看,记住采访一个人多于采访太少的人通常更好。 在后期阶段,更加依赖数据是有帮助的,可以使用平台各个组件的使用数据。 服务请求和帮助台上的未解决问题将是衡量平台可用性的良好指标。 此时,将平台视为产品并拥有产品思维是至关重要的,因为你必须从开发的最早阶段到整个产品生命周期中都考虑用户反馈,确保平台具有可访问性、直观性和高效性。 虚荣心的错觉肯定会将你的努力引向错误的方向。 定义诸如透明性或简洁性等原则会很有帮助,它们能指导你通过开发和决策过程。 在接下来的章节中,我们将深入探讨这个话题 。
为了保持相关性和效率,平台必须不断发展。 记住,项目在遇到截止日期时就会结束;产品则随着每次发布和用户反馈不断发展。 建立健全的发布文档和反馈循环对于这一迭代改进过程至关重要。 哪个渠道最适合你的组织和用户是你需要决定的,但除了调查和访谈之外,还有许多其他方式。 例如,通过沟通工具的直接互动、用户论坛、支持解决方案,甚至是内部企业社交媒体,或者在极端情况下,平台本身内嵌的反馈机制。 由真实用户体验和挑战定义的定期更新和升级,确保平台始终与用户需求和行业标准保持一致,促进忠诚度并 保持持续的参与。
促进和增强协作
透明且开放的反馈循环问题在于,它们需要良好的协作架构。 否则,它会显得不自然,从某种意义上说,就像传统的需求工程方法。 作为平台工程师,我们必须将这些经典方法包装成一种更具个人化和欢迎感的方式。 有效的协作是任何成功平台的基石。 通过整合合适的沟通工具,通过定制提供卓越的用户体验,并倡导跨职能团队合作,平台不仅能成为技术解决方案——它们还能成为不同开发团队之间的合作场所,并推动组织 和创新。
为了促进 合作,你的团队和平台必须是开放和欢迎的。 减少任何形式的障碍,使新产品团队能够顺利加入,并为他们提供不同呈现方式的起始点。 必须清楚地说明如何以及在何处联系你的团队以解答各种问题。 这在新用户团队加入你的平台的初期尤为重要。 消除任何隐性期望,即大家清楚地知道如何以及在哪里找到你的团队,因为通常并非如此,尤其是在大型组织中。 这要求你和你的团队成为平台的倡导者,并通过内部公共文档和登陆页面进行宣传。 你必须走出去,与其他团队合作,倾听他们讨论挑战,展示你的解决方案。 在可能的情况下,你必须向你的组织传达新功能和使用场景,并 不断向他们展示如何轻松 开始使用。
培养开放的、以平台为中心的文化
培养 一种开放和以平台为中心的文化可能是最难的部分。 这需要来自你组织内其他预算管理角色的支持,除了你自己的参与之外。 培养依赖于培训、激励(可能会有激励措施)和与社区或用户群体的互动。 所有这些活动超出了你的 预算责任。
培训可能看起来是一个昂贵且耗时的活动,而且很快就会过时,但几乎没有比这更好的方式能与最终用户进行紧密且真正开放的接触。 综合培训项目结合了不同的方法 和来源:
-
专注于平台各个组件的面对面工作坊 你的平台
-
入职培训的实践环节,以及一个简单的首次项目或特定的集成,这些 不会太复杂
-
自助教程
-
在线/视频教程 用于通用内容
-
在线/视频教程用于 你的特定需求
如你 所见,你不必一切亲力亲为。 购买常见知识的课程和培训,并提供你自己的内容来深化这些知识,教授你的平台特性。 此外,考虑创建一个资源库和维基,包括常见问题解答、最佳实践指南、故障排除技巧、代码片段和示例、用例推荐以及事后分析等。 这可以帮助用户更有信心,并减少新 技术采纳的学习曲线。
为了保持动力,培训和平台使用可以结合激励措施。 有不同的激励策略,如游戏化、表彰计划和基于绩效的奖励,可以鼓励平台的积极使用。 对于大公司,我们甚至见过内部徽章计划和 云驾驶执照,这些通常可以与官方认证结合。 但同时,你需要小心不要设定过高的人工障碍。 那样反而会适得其反。 理想情况下,你的激励措施应该能够激发新用户的兴趣,并为专家提供挑战。 例如,它们可以与现有的绩效指标整合,强化一个重视持续改进和有效利用技术的文化。 通过这种方式,你还可以通过定义的 KPI(如成本降低、绩效提升、 和稳定性)影响企业目标。
最后,我们需要将所有这些方法结合起来。 最好的方法是创建一个社区。 我们可以通过用户小组、定期聚会和论坛来建立这样的社区,让用户分享知识、共同解决问题,并相互提供支持。 为了培养一个社区,我们强烈建议你寻求 C 级高管和市场团队的支持。 这听起来可能很简单,只要把人们聚集在一起,似乎一切都会顺利。 实际上,这是一门完整的学科,需要探索吸引社区成员的策略、促进富有成效的讨论,并培养用户之间的归属感和责任感。 在用户中。
没有人,且不尊重他们的需求,你的平台要么很快被放弃,要么从一开始就无法获得任何吸引力。 专注于培训、激励和繁荣社区的战略举措可以加速开放、以平台为中心的文化。 为了提高平台的有效性,你必须超越其纯粹的技术部署。 这种整体性的方法确保了平台成为组织的基础组成部分,推动创新和效率在各个层级和利益相关者之间流动,从开发者和运营到业务和 流程所有者。
总结
在本章开头,我们探讨了平台在现代软件开发中的作用,特别是在云原生环境中的作用。 我们已经确定,平台不仅仅是基础设施元素,它们是需要战略规划和持续改进的基本产品,以适应不断变化的技术和 业务需求。
你已经了解了平台的基础知识,并且意识到平台结合了软件开发、运营和部署,形成了一个紧密的环境,具有全面性。 在此基础上,你看到了平台作为产品思维方式为何是一个关键组成部分。 它包含了持续开发、用户参与以及对反馈的响应,以保持其相关性 和价值。
接下来,我们讨论了简化开发者与平台交互的重要性,显著减少了认知负荷和操作复杂性。 两个简短的工具示例应该激发你对后续章节的好奇心和兴趣,同时也向你展示了所有这些复杂性和挑战 都是可以解决的。
在最后部分,我们了解了社会技术层面的因素以及人类因素的显著相关性。 平台必须同时考虑技术能力和人类因素,确保它们支持用户的工作流程、协作和生产力。 为此,我们介绍了三个支柱:在设计解决方案之前理解用户需求、促进和增强协作,以及培养开放的、 以平台为中心的文化。
这些课程应该激励你深入了解平台架构以及如何构建它们,因为它们提高了运营效率,并在组织内促进了创新和敏捷性。 通过内化这些概念,平台工程师和架构师可以设计出既稳健、以用户为中心,又能适应变化的解决方案,同时为 他们的组织创造真正的价值。
从人力资源方面转向架构,我们将探讨如何定义平台的原则和目标,如何定义平台架构,以及如何衡量平台在下一章中的成功。
进一步阅读
-
[1] CNCF 年度调查 2023: https://www.cncf.io/reports/cncf-annual-survey-2023
-
[2] 关于 IDP 成功的案例研究:
-
[3] Gartner 2023 年新兴技术的炒作周期: https://www.gartner.com/en/articles/what-s-new-in-the-2023-gartner-hype-cycle-for-emerging-technologies
-
[4] 内部开发者平台的 5 个核心组件(**IDP): https://internaldeveloperplatform.org/core-components/
第二章:理解平台架构以构建平台作为产品
在这一章中,你将被引导完成所有相关的基础工作和创建平台架构的方法。 首先,你将学习到平台原则,以及它们如何成为你平台战略的引导部分。 这将帮助你保持正确的方向,专注于最重要的事情。 我们将通过定义你平台的目标来结束这部分内容。
从这里开始,我们将定义你的架构。 你将学习到平台的组成部分、平台的可组合性,以及如何处理依赖关系。 接下来,你将学习如何为你的需求创建参考架构。 为了给你带来新的视角,我们收集了几个平台作为产品的 用例。
最后,你将学习如何用 最简可行平台 (TVP) 开始你的平台之旅,以及如何通过相关的 关键绩效 指标 (KPIs) 使平台的采用变得可视化。
你将在 第二章 学习到以下内容:
-
理解平台原则并定义你的平台 和团队的目标
-
探索平台架构——层次、组件, 以及元依赖关系
-
探索平台作为产品——用例 和实现
-
理解 TVP(平台作为产品)
-
通过相关的 KPI 来使 采用过程透明化
理解平台原则并定义你的平台和团队的目标
你需要两样 主要的东西来为你的平台开发合适的架构并遵循它。 首先,你必须定义引导你前进的护栏,以及开发和运行 你的 内部开发平台 (IDP) 作为产品的步骤。 第二,你需要理解你的目标是什么。 这不仅仅是架构,更是支撑你平台的已识别和定义的目的。
将原则作为决策的护栏进行引入
原则 可能听起来像是来自被遗忘的企业架构师时代的老派方法,或者非常僵化的组织。 在一切变得敏捷的时代,原则是积极决策的基石。 它们有助于缩短讨论时间,并专注于平台交付。 原则与能力或属性有何不同? 平台的能力是 IDP 可以提供或提供的功能。 想象一下你需要文件存储,通过在平台内应用资源定义的规范,系统为你提供一个符合 S3 标准的存储桶。 属性是所提供能力的超集,可以主动或被动消费。 如果你定义了一个安全平台,那就是属性,那么这可能结合了节点的硬化 、 常见漏洞和暴露 (CVE) 扫描,以及网络加密和严格的 基于角色的访问控制 (RBAC) 规则等。 一个 原则帮助你决定如何实现这些能力和属性。 因此,你通常会遇到两种类型 的原则:
-
通用原则:这些 是面向全组织的计数方向点。 近年来的一个常见例子是: 数据是资产。这意味着: 以下含义:
所有数据对组织都具有价值。 它是一个可衡量的资源,我们采取措施保护并 使用它:
-
对于每个数据源,某人 负责
-
数据对每个人可用 。
-
我们将保护数据免受丢失和 安全威胁
-
-
具体原则:这些 是面向特定用例的原则,可能与其他人相关,但旨在针对少数人或专家。 来自安全领域的一个例子是: 任何活动都由身份定义。这可以解释为: 如下:
-
系统中的每个过程都会记录一个身份 。
-
未知身份应立即 报告
-
每个系统都需要 用户身份验证
-
牢记 这两个原则将在你设计场景架构时,引导你做出不同的决策。 你会发现,特定原则看似描述了一个部门的显而易见的期望。 但如果你想确保事情按照你期望的方式完成,绝不要假设或隐性地期望某些事情。 你想要它的样子。
重要提示
定义 明确 规则、指南和框架,如果你对某个人的活动结果有特定的期望。 隐性 要求对方能够读懂你的心思,并拥有与你相同的世界观 。
平台和平台工程原则示例
在帮助你定义自己原则之前,我们想给你提供一些关于 可能原则的 建议:
-
关注共性问题:识别并优先解决那些能够帮助你组织中多个部门或人员的问题。 通过消除共享问题和滚雪球效应的难题,防止他人重新发明轮子,这些问题如果没有处理,会逐渐变得更大 和更严重。
-
不要重新发明轮子:研究市场上能够满足你需求的潜在解决方案。 定期检查自制的解决方案,寻找标准化的替代品。 市场发展比我们快。 专注于创造价值,而不是 内部依赖。
-
每个客户都很重要:你平台上的每个用户都至关重要,从大型系统到小型产品。 平等地收集和评估每一条反馈。 以同样的方式满足每个人的需求和要求,并提供相同水平的服务。 将这些反馈视为定义你 发展路径的主要方式。
-
消除浪费:我们将构建一个尽可能高效利用资源的平台,并帮助我们的客户尽可能优化资源。 我们推动资源消耗的透明度,并提供扩展、减少和调整工作负载的选项。 我们共同开发最佳实践,帮助开发人员了解如何配置和使用先进技术以实现 更大的灵活性。
-
平台就是一种产品:每个决策都要保持产品思维。 关注为你的内部客户(如开发人员、运营人员、安全人员等)提供真正价值的内容。 确保功能能够快速发布,并且有短的反馈循环。 识别那些由非价值驱动的技术带来的干扰。 你最高的目标是让你的客户愿意使用你的平台,而不是 强迫他们。
-
内部源代码:你平台的每一行代码都对你的内部组织开放。 问题和故障会公开跟踪,并且它们的解决方案会 被记录。 在相关情况下,保持一个常见问题解答(FAQ)。 任何人都可以提出功能、修复漏洞、提交代码和文档,以改进你的平台。 推动社区活动,促进你与 所有用户之间的交流。
这些原则试图涵盖不同的观点和细节层次。 哪个适合你很难说。 如果你发现它们 不起作用,不要害怕随着时间调整措辞或原则。
产品思维作为核心原则
你的内部客户应该希望使用你的平台。 他们不应该被 强迫使用。
定义你自己的原则
一旦你 适应了原则的理念,所有原则看起来都不错,对吧? 现在,挑战是以团队能够采纳的方式定义并写出这些原则。 它们需要融入你的用词,并与公司内部定义 类似元素的方式相契合。
原则可以来源于你的公司价值观、客户反馈或团队的内部研讨会。 无论你做什么,关于原则的来源保持透明非常重要;否则,团队可能会感到被排除在外。 你还需要对它们进行几轮迭代,精炼措辞,并将其浓缩到最基本的内容。 与所有原则一样,它们不应过长,但也不应过短。 句子应简洁明了,不应包含嵌套或条件。 条件往往意味着你对原则不确定,或者说明它可以分成两个原则。 合适的长度是:你只处理一个主题,给出两到三句解释,也许还加上一些动机 或合理化。
我看到过两种非常高效的写作方式,它们在定义原则和寻找常见引用及适应性时非常有效,有时,这些原则出现在 Slack/Teams 的频道描述或 电子邮件 页脚中。
命令和合理化
命令和合理化方法通常适用于那些层级较强的组织,在这些组织中,平台团队可能刚刚成立,或者整个倡议会被持怀疑态度审视。 这要求在初始化时有更严格的指导。 然而,我认为这些原则应随着时间的推移进行调整,并应收集平台工程团队和最终用户的意见。
以下是一个大纲,展示如何 编写它:
-
标题:每个原则都有类似标题的内容,应该牢记在 脑海中。
标题后应跟随 命令列表:
-
初始命令设定了 方向。
-
接着,应说明为什么 这很重要。
-
之后,应该考虑一些更详细的操作 步骤。
-
然后,应再给出一个行动 或理由。
-
在这里,责任应交给 读者。
-
这是基于前述大纲的一个示例:
-
标题: 自服务优先:
-
每个平台功能必须能够由最终用户使用,而无需与平台 工程团队进行交互。
-
这使得用户能够自我决定,并减轻平台和 DevOps 团队的工作负担。
-
不要规定具体方法,而是通过 最佳实践进行引导。
-
打开最终用户部署完全透明的可能性,并允许对 操作数据进行细粒度的访问。
-
你提供的是一套极有价值的工具和自动化系统,简化了软件的开发 和运营。
-
正如你所见,这种风格告诉读者该做什么。 标题很有力地表达了, 无论你做什么,首先要考虑让它可自服务化。首先处理这样的数字风格命名。 如果每个原则都以“首先”,“首要”或 “总体”命名,这样的做法就行不通了。 然而,在这里,你可以看到,所有你写下的潜在原则中,这个原则永远是第一位的,并且很可能会凌驾于 其他原则之上。
第二至第四句详细说明并合理化了第一句。 它们提供了支持的论点和考虑的指导原则。 结束语和标题或第一句话一样重要。 这必须是一个交接语句。 它像是号召行动,但并不是要求别人做什么——这是前面句子所要表达的。 它必须以个人层面来表达,直接对读者说话。 请查看下面的示例列表,了解如何为原则做结尾的其他想法。 一个原则的结束语。
平台团队将随着时间的推移增长他们的内部信任和动力,这需要一种不同风格的 原则。
我们将…
成熟的平台工程团队 和年轻的组织需要能够触动他们 我们 情感的原则。 这几乎就像是兄弟姐妹般的誓言。 然而,你也需要充分了解你的团队,并谨慎处理。 并非每个人都喜欢这种定义原则的方式,因为它们呼吁强大的纽带和沟通。 这只是一个个人问题,没有负面影响,但如果忽视它,你可能会失去宝贵的团队成员。 这就是我如何定义 我们 将 的原则:
-
大纲:
标题:标题保持不变;简洁有力,简短精炼, 就是这样:
-
第一句话通常结合了原则的意图,并包括帮助说明,以 合理化它
-
接下来的 句子都在定义更多的目标 或方法
-
每个句子将以“我们”,“一起”,“作为一个团队”,“为了我们的客户”等开始, 以此类推
-
-
示例:
标题: 自服务优先:
-
我们将以自服务的方式为客户提供每一项平台功能,专注于他们的用户体验 (UX)并支持自主决定 软件开发
-
我们提供最佳实践,并在定义最有效的方法上进行合作。 这些方法最有效。
-
我们共同创造一个透明、创造价值的环境,促进自动化和以最终用户为中心的功能,以简化软件开发 和操作
-
你对这种方法有何看法? 我保持了相同的原则,这样你可以做一个并排对比。 你支持哪一个?
你可以看到, 我们将 更加简洁,但稍显复杂。 这是因为你需要直接面对团队,告诉他们需要关注什么,并用一句话解释背后的理由。 将其切割成短句会失去同样的效果,可能会传达错误的信息。
两种方法的结合可以很好地发挥作用,帮助你识别哪种方式更适合你的团队。 然而,平台团队对这种混合方式感到满意并不罕见。 如果你有四到六个原则都在呼唤你的 我们 的感受,这可能会感到沉重,但如果有一长串 命令,它可能会给你带来很大的压力。
以下是这部分内容的总结: 在下面的列表中: :
-
原则是你在架构和 工程决策中的有益护栏
-
四到六个原则 是最有效的
-
如果你不确定,可以从较少的原则开始,并与最终用户和平台工程师一起识别 接下来的原则
-
经常检查你的原则,看看它们是否仍然适合你的团队,但不要每几 个月就改变它们
将平台的目标发展成一种产品
要成功地 开始构建一个平台,我们需要定义其目标和方向,以及如何构建或重构平台工程团队。 这一步骤还帮助你对之前定义的原则进行首次迭代。 反思你自己的看法,并挑战你最初的定义。 这将帮助你学习如何整合两种不同的力量和 相互矛盾的目标。
一开始将平台作为产品看似简单。 改变思维方式、与客户沟通并提供一些良好的功能,这些就是你需要做的。 然而,这只是故事的一部分。 我们需要让你意识到,云原生平台有潜力成为你组织的软件开发、交付和运营的核心动力。 因此,我们将向你介绍一些不同的概念,这些概念你也能在其他组织理论和实践中找到,例如看板(Kanban)、精益起步(Lean Inception)或 团队拓扑学(Team Topologies)。
理解你自己的价值
如果人类 在某一方面确实非常糟糕,那就是做估算。 没有尺度或参考点时,我们很难做出正确的估算。 比如,我问你,平台对你组织的价值是什么? 你可能会提出这样的论点,比如更快的软件开发周期、更少的运营开销、以及减少招聘成本的快乐工程师。 这些说法都没有错,但也没有什么是精确的。 用商业术语来说,平台作为产品的价值在于它促进了市场时间的缩短,或者优化了延迟成本的灵活性。 换句话说:你将服务推向市场所需的时间越长,你创造的价值就越少。 一个好的平台将通过提供高度的灵活性和速度来帮助你优化这个价值, 从而为实施团队提供支持。
在 延迟成本 成本理论(cost of delay) [1]中,假设容量 是固定的。 如果你想实施三个项目,你需要决定从哪个项目开始。 每个项目带来的价值不同。 如果你同时启动所有三个项目,假设的情况是你能在同一时间完成所有项目,但无论是依次进行还是同时进行,你的速度都不会更快或更慢。 有了一个支持自助服务、高度自动化和自治、可靠性等的 IDP,你可以减少对可用容量的依赖,从而允许多个项目同时实施。 进一步思考,这也是为什么公共云服务提供商如此成功的原因之一。 然而,随着复杂性的增加和角色定义的错误,这些好处变成了障碍。
图 2.1:没有平台与有平台的延迟成本
前面的图展示了在没有平台和有平台的情况下实施项目的区别。 左侧我们可以看到至少有两个延迟,这意味着第二个项目在年末或某个期间开始提供价值。 这种延迟可能是由手动流程、对其他团队和团队成员的依赖,或者没有彼此集成的各种技术步骤所导致的。 右侧,项目的同时启动将由于非常好的平台带来显著的价值增长。 请记住,下面的区域代表的是 价值。
重要提示
云原生平台,如 IDP,正在打破延迟成本理论,使得快速且早期的价值生成成为可能。 平台越好,对能力的依赖越少,团队实现和运营产品的效率越高,你的组织能创造的价值就越多。
从系统到平台作为产品
最终,一个产品无非是由客户需求、你的 业务 利益、一些技术以及整体用户体验(UX)构成的服务对象。 如果缺少这些元素中的任何一个,你的产品将会失败。 在项目的世界里,我们有三个基石在不断拉扯着彼此:时间、金钱和范围。 这被称为 铁三角 。在这三个方向之间找到平衡需要仔细的调整。 但由于我们希望摆脱项目视角,我们将在这里介绍产品的铁三角。 它由可行性、可取性 和可行性组成。
图 2.2:产品的铁三角
我们可以在这种平衡中找到不同的力量在拉扯产品。 所以,我们必须问自己以下问题:
-
我们为客户 和用户解决了什么需求或任务?
-
我们的客户想要什么? (通常,他们 并不知道。)
-
我们如何使 它运作起来?
-
我们应该使用什么技术? 该使用什么技术?
-
我们如何才能从中建立一个可持续的业务 呢?
我们现在可以将这种产品视角与 平台视角结合起来。
“数字平台是一个由自服务 API、工具、服务、知识和支持组成的基础,这些要素被安排成一个引人注目的内部产品” 甚至 Bottcher 曾这样描述过一个平台 [2]。此外,他还说,“自主交付团队可以利用平台以更快的速度交付产品功能,同时 减少协调。”
平台和产品有很大的重叠。 两者都 具备以下特点:
-
他们必须倡导其有用性并将其推广到 内部团队
-
它们应该经过精心设计 并且准确
-
它们是以用户为中心设计的,或者更好的是,与用户/开发者共同设计(关注 UX/DevEx)。
-
他们随着时间推移不断发展提供的能力,并且需要一份 清晰的路线图。
在开发 平台作为产品的清晰愿景和目标时,应考虑并检查这些方面。 你会自然发现,对于产品铁三角,两个角色会被 立即分配。
平台的产品负责人将处理可行性,而平台工程师将处理可行性。 但几乎每个组织都缺少负责可取性的人。 这个 角色通常被称为 开发者 体验 (DevX)。
没有 DevX,一个平台工程团队 是不完整的。
重要提示
平台的产品负责人处理可行性。 平台工程师处理可行性。 DevX 负责可取性。 这些角色定义了平台作为一个产品的成功。
利益相关者及其对你的待办事项的影响
谈论 利益相关者和客户时,企业内部世界展开了一幅由许多有不同利益的人组成的景象。 为了聚焦重要内容,我建议将他们分为 三组:
-
用户:真正会使用你的平台/产品的一群人。 在我们的案例中,使用者将是开发者 或 DevOps 人员。
-
客户:具有预算决策权和/或为整个团队、部门做出决策的人, 或类似角色。
-
影响者:其他对你的平台感兴趣的人,如安全团队或 运维团队。
你正在 为那些利益相关者群体构建平台,而不是为了自己! 在定义平台的目标时,你应当从不同的角度来看待不同的利益相关者群体。 他们的需求可能会随着时间变化,你可以通过频繁的迭代和调查来了解这些变化。 当你为待办事项定义功能时,要明确这些需求来自哪些利益相关者。 如果已经过去一段时间,通常六个月是一个合适的时间框架,那么你应该与利益相关者一起重新优先排序功能请求。 另一方面,这也会影响平台的目标。 让我们看一个 例子。
不久前,我们被要求稳定客户的平台。 我们对工作负载了解不多,但整体系统不可靠,几乎无法作为一个有用的系统。 几周后,经过一段时间的工作,新利益相关者出现了。 他们对平台产生了兴趣,因为它可以自动调整大小以适应工作负载,并且人们普遍对快速且简便的入驻过程感到满意。 此外,在之前的实现过程中,提供了严格隔离的单租户部署。 这些新利益相关者来自数据科学部门,他们希望能够轻松运行自己的模型,而不必成为专家。 他们使用的云服务提供商已经过于复杂,因为他们只专注于机器学习。 平台所有者同意提供几周的资源,用于在平台上实施数据科学家所需的工具。 尽管离完美还很远,但实现和平台足够好,易于使用,成本低廉,并且确保了所有的数据保护法规。 再过一段时间,平台团队的主要目标是为数据科学团队提供隔离的环境,这些环境配备了一整套工具,并且在完成任务后被拆除。 此外,平台仍然可以运行任何其他工作负载,目前对此没有需求。 暂无需求。
这个现实世界的例子表明,有时外部影响力比你自己的推动力更强,但它能导致更高的接受度和整个组织的采纳。 该组织的采纳情况。
重要提示
最终,你建立了一个平台,作为一个面向利益相关者的产品,主要是面向用户,而不是 为了你自己。
挑战康威定律
Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. (Melvin Conway, 1967.)
我认为平台的主要目标之一是比目前的状态做得更好。 为此,我们必须挑战组织的现状。 公司会不断进行重组,交换责任并调整结构。 但最终,组织结构保持不变。 这就是康威的定律——至少他描述了他对组织的观察。 平台的存在是为了颠覆和协调;一方面,运用 DevOps 方法学识别瓶颈并打破这些结构,另一方面,协调专业团队的努力,形成一个 统一的环境。
作为一个平台团队,你应该首先定义你的目标和你希望遵循的理念,以便在被迫遵循组织结构之前实现你的目标。 可惜的是,这往往导致政治游戏——如何获得更多权力,如何拥有最多用户,以及谁有最好的资源来推动决策。 但不要太天真,以为拥有一个不错的平台和一些出色的功能就足以 取得成功。
因此,你需要对团队的目标有清晰的定义,以便在风雨飘摇时保持方向,在一切都想拖住你时继续前进,或者在你过于迅速和具有牵引力时保持你在正轨上。 它旨在帮助你保持 平衡。
为了将这一切汇聚起来并为你的团队和未来保存,你可以使用 平台工程目标画布 作为起点。 你可以使用以下模板(https://miro.com/miroverse/platform-engineering-purpose-canvas-template/),或者自己创建一个。
图 2.3:平台工程目标画布 [3](此图仅作为视觉参考,文本信息并非关键内容。)
由于我们专注于 设计和创建一个作为产品的平台,我们将不会进一步探讨平台工程团队的详细信息,如何建立它们以及提供正确的变更管理方法。 还有其他专注于此的书籍和方法;因此,我们建议您查看 团队拓扑 如果您对团队 开发也感兴趣 [4]。
凭借您的平台目的,我们可以逐步接受创建您的平台架构的下一个挑战。 在接下来的部分,我们将为您创建一个参考架构和其他图表,以产品的形式呈现。
探索平台架构 – 层次结构、组件和元依赖关系
您的平台旅程的导航由参考架构定义,您可以根据这一架构来调整整体实施。 架构,特别是架构图,不是永恒的。 它们是 一个框架,一个蓝图,您必须根据它们进行定位,但是随着市场解决方案的演变,无论它们是开源的还是 闭源的,它们可以并且应该随时间变化。
为了使某些部分更易于理解,我们必须为您提供一些现成的工具和产品。 因此,我们 更倾向于突出开源解决方案而不是商业解决方案。 我们不希望给您任何偏见,因此请始终注意,对于我们提到的每件事情,至少有数种选择可供选择。 我们可能选择某些工具作为参考,因为我们更了解它们,或者因为我们看到它们在社区中更常用 比其他工具。
创建架构永远不应该只包括一个视角。 与以往每个主题一样,我们必须考虑各利益相关者从不同角度看待平台的情况。 我不想为您提供传统企业架构管理工具的完整集合,但我愿意为您提供一些相关的可视化和工具。
平台组件模型
Stephan Schneider 和 Mike Gatto 在 2023 年首次提出了 IDP 参考架构模型 [5],该模型经过社区多次改进。 不幸的是,该模型缺乏一些重要的视角,要么是由于命名问题,要么是因为缺少一个所谓的 plane。 因此,我们开发了一个更为全面的模型,其中包括以下几个 plane:
-
开发者体验 plane:这个 plane 涵盖了软件开发和基础设施工程所需的所有第一层组件
-
自动化与编排 plane:该 plane 集成了构建、测试、部署、存储和编排应用程序以及基础设施的工具 。
-
可观察性 plane:该 plane 包括任何需要的工具,以提供平台及其应用程序发生的透明性和可视化
-
安全与身份 plane:该 plane 包含安全执行和监控系统、用户与账户管理解决方案,以及密钥和证书管理工具
-
资源 plane:这是一个容器,包含所有可以用于平台的计算资源
-
能力 plane:该 plane 位于平台内或平台上方,例如 Kubernetes,提供面向用户的服务
图 2.4:平台参考组件
这个组件视角 表明,平台肯定不只是一个单一的服务器,我只是安装一些容器编排或开发者门户的地方。 通常,这些组件有多个可能的部署位置。 这是一种链接,将相关的元素联系在一起。 例如,为了加强安全性,我们将在资源 plane 中找到相关的元素来遵守合规性并提供加固的配置,比如收集数据并分析的外部系统,位于 Kubernetes 中的能力 plane 之上,扫描容器并保护网络,此外还会出现在 CI/CD 中,以便只交付加固过的容器。 你可以看到,单一的主题可以存在于平台的任何地方。
平台的可组合性
一个 平台由许多不断发展的部分组成。 随着这一发展,你的用户和组织可能也会发生变化。 我们经常发现的一个巨大问题是,平台的某些部分不愿意被替换。 虽然常常会有关于优先级或预算的争论,但最常见的问题是它无法轻易替换。 有时,这是因为平台和组件已经陈旧,实际上是相互紧密结合的;有时则是因为技术选择不当,引入了一系列依赖关系,使得替换变得困难。 但也有第三个原因:将自己做事的方式强行塞入 错误的工具中。
组件化是一项原则,也许是一种意识形态,肯定是一种平台构建的方式和决策。 当组件能够相互作用而无需在其间开发中介来进行翻译时,例如脚本或无服务器函数时,组件就是可组合的。 可组合的组件可以以最小的努力,且对平台功能影响最小甚至没有影响地被替换。 Kubernetes 是一个极力支持这种方法的环境,因此它成为了构建平台的事实标准。 然而,平台的组件是分布式的,并不是所有的组件都运行在 Kubernetes 上。 这使得找到 正确的工具 变得具有挑战性。
一个通用的、一刀切的平台听起来总是不错,但它是不现实的,并且增加了复杂性。 你需要将更多组件引入平台时,组件之间难以协作的可能性也会增大。 因此,能够为特定的使用场景提供调整,而无需改变整个架构,将提高其可用性。 组件化的另一个重要好处是关注点的分离。
在 第一章中,我们 强调了平台作为即插即用专用解决方案的抽象层的目的。 严格执行组件化将会给组织层面带来大量工作,但它将允许专业团队将其核心能力直接提供到 你的平台。
要评估平台的一个组件是否可组合,你需要寻找 四种能力:
-
可编排的:它可以在现代动态环境中运行,例如 Kubernetes,而不 受到影响
-
模块化:它不需要任何其他工具或技术前提条件,但可以利用这些工具来扩展 其功能
-
可发现的:它需要与其他组件进行交互,遵循标准化的 API,或遵循像 Kubernetes 的自定义资源定义这样的概念 。
-
自主的:它必须独立于其他外部输入并自主运行 。
现在,我们知道在设计平台和选择平台组件时应该关注什么。 在接下来的部分中,你将了解那些使组件之间保持联系的东西。
依赖关系和隐藏的胶水
无论平台的 组件多么可组合 ,总会存在依赖关系和一些 隐藏的胶水。这些隐藏的胶水是组件之间流动的信息,使它们相互响应。 作为架构师,我们的工作是确保这些信息不相互矛盾。 这些信息很重要,但并不 为人所充分理解。
管理和处理依赖关系
首先,让我们通过可视化组件之间的依赖关系来构建可组合性。 为此,我们可以使用一个简单的依赖矩阵。 这个工具可以帮助你做决策并规划未来的组件。 此外,它需要一些维护工作;许多平台依赖于单个工程师的知识,并且随着平台随着时间的推移变得更加复杂,文档也会变得更复杂。 因此,映射依赖关系是实现透明度的最短路径。
在下面的例子中,我们有 工具 A-C,例如 容器网络接口 (CNI) 1-3。现在,这些 工具可以是我们之前提到的参考组件列表中的任何工具。
在依赖关系中,我更倾向于明确指出我们拥有的依赖类型。 因此,你可以使用如下内容: 以下内容:
-
单向和双向依赖:你可以使用这些来描述 工具 B 是否依赖于工具 A,或者工具 C 是否依赖于工具 B,就像工具 B 依赖于 工具 C 一样。
-
增强依赖:增强依赖会解锁更多的能力和功能,如果建立了,但并非必须。 例如,如果你有一个手动用户管理的工具,现在你可以利用 单点登录 (SSO) 来减少你的 人工工作量。
-
冲突依赖:顾名思义,这两者无法 很好地协同工作。
图 2.5:依赖矩阵
你 看过了吗? 在双向依赖的示例中,工具 B 和 C 之间的关系没有正确 显示。 在工具 C 的列中,工具 B 的双向市场缺失。 当你实现依赖矩阵时,需要提供明确的指导,以跟踪这些关系。 我认为最有用的是为两个工具都设置标记。 然而,你必须明确阅读方向,或者引入更多的标记。 例如,你可以使用 (u) 来定义一个 传入依赖关系。
依赖管理是一个持续的任务。 随着时间的推移,你可能会忘记添加新工具。 因此,无论你使用什么方法来实现新功能,创建一个自动子任务来更新依赖矩阵可能是个好主意。 一个很大的好处是定期评估这个矩阵,这可能会发现潜在的 瓶颈或问题, 你可以着手解决。
应用 x 平台 – 相互影响
这个 相互影响 是 这样定义的:它们之间是相互关联的,以至于 一个补充另一个,或者等同于另一个。 在平台与用户应用之间,你有某种程度的相互影响。 例如,一个应用需要大幅扩展。 作为平台,你支持这种行为,但同时确保它不会影响到其他应用。 另一方面,该应用选择你的平台是因为你支持它的需求。 没有用户的平台是没用的。 为了作为应用发挥最佳效果,你应该在 该平台上运行。
然而,这个例子 非常通用。 相互影响也会影响到 YAML 文件中定义的层级之外的元素。 它也是没有被要求却自然而然产生的需求和要求;它是两个实施方之间的团队动态;它还体现了一个解决方案如何影响其他解决方案的方式 他们的工作。
因此, 依赖 这个词并不适合用来描述这种关系。 它是一种共存,一种协作,朝着相同的方向一起前进。 对于平台工程团队,尤其是作为架构师的你来说,在设计解决方案时,必须始终牢记这种相互影响。 要让这一点成为你的超能力,你需要教育平台工程师和用户了解这些影响,并专注于如何让他们从平台中获得最佳效果,同时不会感到 任何限制。
相互影响
相互影响是应用与平台之间的无形互动。 将其理解为一种共同的力量,使得平台和用户的应用能够比没有彼此时更好地融合在一起。 相互依赖。
另一个地方这种相互影响变得可见的是 在过程当中。 在过程中。
平台的过程定义机会
平台工程 借鉴了许多 DevOps 方法论中的做法。 在 DevOps 角色中,一些部分侧重于自动化。 这些自动化步骤转换为定义流程。 因此,我们也可以说,平台工程师为其创建技术流程,以便作为产品在用户导向的方式中实施。 然而,这些程序在明确的设计决策中并不常见。 流程的形式(或流程的样子)取决于某些组件的工作方式。 关于平台可组合性,这可能是一种固有的方法,但我们能否通过明确设计 流程来改善平台?
它不应该变成业务流程建模。 许多先进功能,诸如 GitOps 或 Kubernetes 上的操作员,支持的工具可能具有多个 基于决策路径的变量。 如果要将这些都绘制出来,那将是非常可笑的。 然而,文档化并设计流程对于理解平台是有帮助的。 困难之处在于多个入口点的流程,这些流程可以从以下开始:
-
基础设施配置
-
用户引导
-
应用部署
-
事件处理
每个都需要自己的 流程定义。
你有机会在关注用户友好性和操作性的基础上发现优化潜力。 利用流程视角可以将以用户为中心的平台开发与更常见的、纯粹的技术驱动特性定义区分开来。 不幸的是,目前我们无法为你提供更多的见解,因为这一领域仍然是新兴的,几乎没有相关的知识分享。 在 第三章中,我们将深入探讨 这个话题。
供应商锁定讨论
总结 关于依赖性的主题,我们必须触及关于供应商锁定的讨论。 这是战略和架构决策的一部分,并经常推动产生奇怪的结果。 此时,您应该充分意识到,您总是会有依赖性,而供应商锁定仅仅是其中之一。 正如开源为避免专有软件带来了良好的基础一样,它也被用作自行构建事物的论据。 要问的重要问题是:您希望为用户提供平台的速度和服务质量是多少? 您预期的运营成本是多少? 除此之外,您还必须考虑 团队的可用性、技能集以及您的 组织更倾向于的成本(运营费用 (OPEX)或 资本支出 (CAPEX**)。
有关供应商锁定的一些不同观点如下:
-
最好自己构建:完全自己构建某些能力可能会让您感到终极自由,避免任何类型的供应商。 事实上,您只是成为了自己的供应商。 在某些时候,您可能不想或无法再投资于您的开发(常见的项目行为)。 因此,您作为自己的供应商从自己的市场中退役,并且您害怕的一切都成为了现实。
-
选择每个问题中的最佳工具,而不是每个上下文中的最佳工具:在可观察性范围内,有许多选项可用于监控、日志记录和跟踪。 针对这个问题的常见方法是选择最适合的开源工具。 在此基础上,您需要实现日志收集器和代理来转发指标。 您所实现的是一个完全可组合的可观察性解决方案。 另一方面,与单一供应商解决方案相比,您可能会失去上下文意识、可扩展性或操作性等能力。 我不想说专有软件更好;然而,我见过许多组织使用 10-14 种不同版本的工具,运行在最大规模上,并拥有高昂的运营成本。 实际上,对于工具集来说,这种做法在处理事故时并不有益。 在这种情况下,我们不幸地倾向于回避真相,并为为何事情会变得这样找借口。 它更好。
-
专注于错误的事情:人们仍然关注部署 Kubernetes 的任务。 这被认为是一个挑战,并且变成了一个永无止境的故事。 考虑利用工具、有明确观点的平台或商业解决方案来完成容器编排,并专注于 你平台中创造价值的部分。
供应商锁定是架构师在定义目标时议程上的一个情感讨论点。 你必须保持眼界开阔,真正考虑什么是重要的,什么仅仅是满足自尊心的东西。 复杂且无法管理的解决方案并不会为你赢得奖项 。
到目前为止,你已经了解了依赖关系以及如何处理它们。 在接下来的章节中,我们将学习如何让 它们可见。
参考架构
参考架构 对平台工程团队、你的平台和相关用户来说是非常宝贵的 资产。 它们为讨论和决策过程奠定基础,并帮助通过前瞻性思维定义未来的工作。 不幸的是,许多平台 团队无法清晰地描绘出 他们的系统。
特定供应商的参考架构
现在,这些 参考架构可能会因你使用的不同供应商而有所变化。 平台工程社区 [6] 已经收集了一些可供构建的选项和模板。 一个好的起点是单一云提供商模板,这里以 AWS 为例。 然而,能力平面仍然缺失。 这里的重点是云服务提供商,因为它通常会为你的平台定义大量的组件。 正如你在下图中看到的, 云服务提供商 (CSP) 服务 可以在架构的任何部分找到。 你甚至可以绘制出一个更具观点的目标,利用 CSP 的 CI/CD、密钥管理和 云身份提供者(IDP)。
图 2.6:由 platformengineering.org 提供的 AWS 参考架构
与单一供应商平台相比,多供应商平台自带一定的复杂性。 除了每个供应商使用的资源外,我们还必须考虑存储和分发容器镜像的位置、如何提供可观察性,以及 如何跨供应商管理机密和证书。 你可以在以下的参考架构中看到,满足这些期望并不简单。 通过多云平台策略,你将面临大量需要处理的问题。 成本将急剧增加,实施和平台行为会随着每个云服务提供商(CSP)的不同而略有变化,而且很难定义一种完全合适的方法。 可用性、安全性、可扩展性和效率等方面会因构建在 多个环境上而产生冲突。
图 2.7:由 platformengineering.org 提供的多供应商参考架构
然而,并非每个平台都需要在云端运行。 可选择的方案是无穷无尽的。 我们在成熟企业中常见的是混合、私有云或扩展至云的环境,这些环境依赖于像 VMware Tanzu 或 Red Hat OpenShift 这样的成熟解决方案。 在以下图中,我们可以看到一个以 OpenShift 为核心的平台架构。
图 2.8:由 platformengineering.org 提供的 OpenShift 参考架构
调整 参考架构以适应其运行环境 非常重要。
重要提示
你为平台选择的环境会自动规定平台的一些部分,无论我们喜欢与否! 增加云和基础设施提供商的数量会呈指数级增加你平台设计的挑战。 这将显著增加你平台设计的挑战。
通过能力平面扩展参考架构
大多数参考架构的弱点在于它们缺乏对实际平台运行时上运行的组件所提供的能力的视图。 这种视角是必需的,因为没有它,用户无法使用平台的功能和特性。 没有它,用户将无法使用平台的功能和特性。
在下一个图中,我们 将提供一些关于 能力平面的参考:
-
用户空间:用户实际看到并可以用于托管其应用程序的环境
-
规模与调度:支持用户应用响应事件、缩小规模或提供自定义调度选项的能力
-
网络:处理进出流量,自动管理 DNS 条目或集群 负载均衡
-
重量级任务:通常更倾向于交由管理,而不是自己操作的事物,比如批处理框架或 消息流处理
-
资源集成:一个至关重要的组件,将实际的基础设施资源连接到平台运行时;存储或 GPU 集成,也包括资源控制,例如使用 Crossplane 工具
-
安全与合规:提供管理和存储机密、主动扫描集群和应用程序中的 CVE 漏洞的能力
-
可观察性:提供一个简单且快速的可观察性入口,统一数据收集,或帮助处理 成本
图 2.9:带有示例工具的能力平面
能力平面 对于实际的应用部署和运行至关重要。 它是你与技术互动最密切的空间,如果用户的应用在这里失败,所有其他的努力都将付诸东流。
平台架构超越了参考模型
总结参考架构,它们提供了一个易于理解的平台概念。 它们没有提供的是平台的实际架构。 源代码版本管理在哪里运行? CI/CD 管道组件在哪里? 容器注册中心在哪里? IDP 如何托管?
因此,作为平台架构师,我们必须提供一个实际的架构,展示这些组件的位置。 这为进一步的依赖关系、网络通信和需求、可用性以及平台的可扩展性提供了深刻的洞察,或者简单来说,是否平台中有太多其实并不帮助平台稳定性的组件。
以下图表展示了这种架构可能的样子。 你还会看到不同的组件和网络连接分布在各个位置。 然而,当谈到平台架构时,这就是它应该如何在技术上 被表示。
图 2.10:平台架构
很难找到合适的方法将基础设施和平台组件在一张图表中展示。 这不是最干净的方式,但它有必要突出所有的依赖关系和相互影响。 尽可能保持简单,但也要详细到足够的程度。 你甚至可以将其拆分成不同的图表,以突出某些信息。
拥有你的架构
看起来 管理和处理不同版本和变种的架构似乎需要大量文书工作。 然而,你可能已经体验过,平台变化的速度远快于图表的更新。 重要的是,图表作为你作为架构师工作的结果,不应拖慢平台的发展,而平台的发展也不应拖慢图表的更新。 我见过一些项目,架构师变成了一个杰出的等级制度。 所有这些项目都失败了,并且在做出正确决策时遇到了困难。 作为架构师,你并非无所不知,但你也不是平台工程团队。 共同合作,保持一致和同步是关键,几乎就像你希望构建的平台一样。 要建立的平台。
同样的道理适用于你的架构。 你必须拥有它们,但不是支配它们。 一个架构必须是一个思想的游乐场,是未来发展的沟通工具,是关于平台能力的真实来源。 这只有在你负责推动它,并且听取他人意见时 才是可能的。
在本书的后续章节中,我们会讨论一个相关的话题,关于拥有你的架构:如何应对技术债务。
有主见的平台与质量的成本
在构建平台时,会出现两个问题:有主见的工具和质量的成本。 这两者都有很强的论点需要 考虑,但它们也有强烈的反对理由。
有主见的工具和产品
有观点的 工具、产品和服务强调一个严格的概念,并且不提供替代方案。 几乎所有的东西都有其观点,但问题是它们的灵活性和可组合性如何。 对于如何做某件事的强烈观点使得后续的决策变得更容易,因为解决方案空间缩小了。 随着这种方法用户基础的增长,它可能会变得主导,最终成为事实上的标准。 我们必须在架构中仔细考虑这些因素,并澄清对它们潜在 未来影响的理解。
现在,严格执行一个概念可以带来很多好处。 例如,每个解决方案在提供 Kubernetes 集群的领域内都有其观点。 然而,它们提供的是一个一致且通常已经准备好投入生产的整体解决方案。 这样的工具在集成时也可能更快,因为它们覆盖了广泛的主题并预先集成了其他工具。 另一方面,这也可能是一个缺点,因为如果你想实现其他选项,会付出更多的努力。 然而,在某些情况下,这几乎是不可能的,或者成本 过于昂贵。
你还应该区分可见的大概念和那些更隐蔽的、隐藏的概念。 前者容易识别和评估。 它们附带大量的预定义集成,通常基于单一供应商的技术。 而那些小型和隐藏的有观点的工具则更难以识别, 因为你使用得越广泛,它们的缺点就越显现出来。
质量的成本
有一句话说: 与其频繁购买便宜的,不如一次性购买昂贵的。但在平台做得正确的情况下,这句话是错的。 采用产品思维意味着持续的投资。 这更像是你每天都要照料的花园,而不是你一次性投资的手表。
质量的成本是一个多维度的问题。 一个高质量的平台,很可能非常有偏见,对于某些用例来说,可能是完美的选择。 但质量不是免费的,它基于高投资或运营费用。 在大型公司中,你会遇到类似内部成本分摊的情况。 这也意味着你必须分担质量成本。 我们在最终用户社区中经常看到,像这样的平台往往会导致沮丧。 由于成本过高,入门门槛对托管简单应用程序来说太高,或者它的偏见性使得运行一个非常复杂的应用系统变得不可能。 这导致其他人开始从零开始为他们的应用程序构建自己的平台和解决方案。 如你所见,这是一个奇怪的问题。 由于平台的高成本,用户的运营成本也变得很高,而偏见性的解决方案并没有真正提供所需的功能。 这就是所谓的 质量的成本 成本。
然而,你也不能通过快速拼接一些随机工具来绕过这个问题,提供一个既便宜又尽可能开放的平台。 这可能会导致平台的项目初期快速上手,但我可以保证,这条路走得越快,用户离开你的解决方案的速度也越快。 记住,以产品思维方式看,你希望让用户能够自己做事,而你的平台则消除了所有繁重的工作。 理想情况下,你的用户也不需要成为所有 使用技术的专家。
从战略层面来看,质量成本的问题影响了许多决策。 我们见过一些平台经过多年开发,最终却因为其 性能/价格比 而遭遇挑战。
你需要为你的平台找到黄金路径。 然而,不要以为一个完美的平台就符合他人的需求。 如果你遇到这个问题,这显然是一个信号,说明 你已经失去了以产品为导向的思维方式,缺乏 用户导向的视角。
创建你自己的架构
行动的时候到了。 到目前为止,我们已经涵盖了所有相关的输入源、关注的主题和需要考虑的视角,现在可以开始构建架构了。 因此,我们准备了一个模板,你可以在本书的 GitHub 仓库中找到,或者作为一个 Miro
协作工作的 模板。
在这些模板中,你将通过不同的步骤和架构图来构建你的平台架构:
-
创建你的参考架构。
-
专注于能力层面及其组件。
-
定义平台的基础设施架构。
-
可视化平台的控制流。
由于架构是一个动态的产物,你需要反复修改它,遵循PLAN, DO, CHECK, ACT 循环。
花时间浏览这些模板:
-
GitHub: https://github.com/PacktPublishing/Platform-Engineering-for-Architects/tree/main/Chapter02
-
Miro: https://miro.com/miroverse/platform-architecture-workshop
创建你的参考架构
参考架构概述了你计划的组件,将其分类并将其作为平台实施的核心部分。这也是你在某个阶段定义依赖关系图的帮助来源。
为了给你的参考架构图注入活力,最好从已经确定的组件或给定的基础设施开始。这些固定组件可以是,比如 CI/CD 工具或安全解决方案。首先,基础设施变得重要,因为它影响所提供的资源。如果你选择使用公有云服务商,很多服务可以作为托管解决方案使用。在某些情况下,这也决定了你将使用基础设施即代码(IaC)工具,或者反过来,如果你仅选择基础设施即服务提供商,这意味着你需要考虑平台中其他地方可能运行的服务。它们很可能会最终出现在能力层面,这就引出了一个问题:我们是否需要为共享服务专门部署一个集群,还是采用服务部署在用户命名空间中的方法?
接下来,处理自动化和编排层面。 出于某种原因,这个层面相对较为确定,并为开发者体验层提供了坚实的决策基础。 你需要确保所选的解决方案是较新版本,并且能够与其他云原生服务集成,而无需任何脚本。
最后但同样重要的是,你需要考虑可观察性和安全性层面。 如前所述,可能你已经有了现成的解决方案。 但特别是对于那些平台工程和云原生之旅还比较新的组织来说,你可能会对现有的解决方案提出挑战。 安全工具需要能够理解云和 Kubernetes。 它们需要与这些平台集成,并能够应对它们非常动态的特性。 同样的理由也适用于可观察性和运营解决方案。 你不希望每次 pod 被终止或在集群中移动时都收到警报。 在集群中。
最重要的是,你最终将所有的视角汇集在一起:平台的原则和目标、关键用户、可组合性,以及这些移动组件之间的相互影响。 但是你的设计不需要完美。 在深入设计过程之前,请继续阅读,因为我们将在本章稍后讨论 TVP 的话题。
专注于能力层和组件
当 参考架构搭建完成后,我们需要在下一步中通过能力层来扩展它。 当然,这本身就是一个大步骤,因为实际上有数百种开源解决方案可用于能力层中的任何需求。 如果你从未见过 CNCF 景观,以下快照展示了大约三分之一的 CNCF 列出的开源 项目 [7]。
图 2.11:CNCF 景观(该图像仅作为视觉参考,文本信息并不重要。)
请注意, 至少还有一些其他开源组织也管理着数十个项目,你可以将它们算作云原生领域的一部分。
云原生宇宙的好处和坏处在于,对于每一个微小问题,你至少有三个±1 的选项来 解决它。
对于能力平面,建议从 底层向上工作:
-
我们需要哪些资源的集成?
-
我们如何将操作数据提供给可观察性解决方案? 我们需要在基础架构和应用空间之间拆分吗?如果需要,如何拆分?
-
确保安全和合规性措施正确地转移到 用户的领域。
-
我们如何处理网络流量? 我们需要在 集群内进行加密吗?
-
我们是暴露一个 API,还是将其与 云 API 集成?
-
扩展和调度可能需要什么? 是否有其他功能需要 提供?
预计大部分时间在能力平面上工作。 随着每个新用户和新产品的加入,会有新的需求出现。 每当新用户和新产品出现时,新的需求就会随之而来。 最棒的部分是当你能观察到产品的演变,并且要求更多 高级功能。
再次强调,首先关注核心要素。 正确整合网络管理和存储比提供机器学习工作台解决方案或新的容器运行时更为重要,例如 WebAssembly (Wasm) [8]。
为平台定义基础架构架构
在定义参考架构的有趣部分之后,我们必须直接进入严肃的架构定义。 听起来像笑话的部分往往被跳过,但许多平台无法提供这个视图。 事实上,这个图表比参考架构更相关,因为它涉及实现、架构讨论以及安全与合规性检查。 然而,它也更加复杂 理解。 。
如前所述,我们必须将一侧的基础架构架构与平台的相关组件结合起来。 这有助于我们理解系统中每个部分的位置以及所需的连接 。
该图必须突出显示计算基础设施使用的不同环境,消耗的托管服务,它们之间的连接方式,以及存在的外部连接。 每个组织都有自己绘制此类图表的方式,遵循特定的标准,或者做得更简化。 我们的模板尝试提供一个 中间方案。
你也可以在架构图中对突出部分进行不同的调整。 我们经常看到,在可视化精确的网络连接、网络隔离或协议方面具有巨大的相关性。 将一个隔离环境与相应的控制流结合,并展示如何将更新导入其中,将利用两种图表类型来 最大化理解。
记得在图表中包含诸如管理环境/集群、集成测试的暂存环境,甚至是开发环境之前的一个步骤。 为了保证连贯性,备份和恢复也可能是有帮助的。 你看,越是投入工作,更多的细节就会浮现出来。 当图表变得过于复杂时,你应该提供 不同的版本。
可视化平台的控制流
可能 对你来说比较新的可能是 控制流。它是 一个像流程一样的方法的第一步,旨在帮助我们澄清管理平台和用户交付物的相关路径。 平台内有多个流;其中这三个是 最为突出的:
-
基础设施提供:此流可视化了基础设施测试环境的重要性,以及在该层级上的变化如何影响它们,从在版本管理系统中提交 IaC 清单,到部署、测试,再到 最终发布。
-
从代码到部署:这对于平台用户最为相关。 一个人提交的代码是如何被拾取,构建、集成、扫描和测试的? 开发和生命周期设置程序 是什么样的?
-
平台能力管理:这对于负责 集群交付组件的平台注册工程师尤为重要。
有许多其他较小的控制流可以帮助我们理解平台,例如秘密和证书管理,或者 用户管理。
从纸面工作到实际使用案例,我们将看一些不同平台风格和实现的示例。 在下一部分,你将 把视角从通用平台扩展到 专门化解决方案。
探索平台作为产品——使用案例和实现
我们谈到了平台的目的,比如 优先考虑自服务。但是我们如何实现这一点, 我们希望提供哪些使用案例作为 自服务?
每个组织 的使用案例和平台实现都有些不同。 这是因为每个组织的技术栈不同,遗留/现有的工具和流程也各不相同,这些都是优化的候选项。
在这一部分,我们讨论了在我们多年来与组织合作中看到的使用案例。 我们可以假设其中一些案例将是你自己未来平台工程计划的好候选者。
寻找专家以及他们引起的瓶颈
有一篇 来自 Thoughtworks 的精彩博客文章,其中有一段引用,帮助我们开始寻找好的使用案例 案例 [9]。
是什么使某个东西成为平台?
平台是集中专业知识的手段,同时将创新下放到客户 或用户。
换句话说,在任何生产、运输和操作软件以支持其业务的 IT 组织中,我们可以找到诸如基础设施配置、访问控制、构建和部署工件、可观测性、质量工程、发布 管理、容量规划、 网站可靠性工程 (SRE)、事件响应、数据分析、自动化、业务洞察等方面的专家 等等。
我们的平台目标是识别使用案例以及它们所需的专业知识,然后以简单的方式将这些使用案例作为自服务提供给每个人使用,无需每次都去找专家 。
通过自服务来集中专业知识
允许每个人完成工作,而不必成为专家或在所有需要的领域请教专家来完成他们的 工作!
如果我们现在 查看整个 软件开发生命周期 (SDLC),并列出从新应用或功能的初步构想到它发布到最终用户所需的所有任务、专家和时间,我们将能够识别出许多阻碍 SDLC 进展的瓶颈。 这要么是因为某些任务是手动的,要么是因为这些任务需要某个专家来完成。 由于专家通常比较稀缺,他们成为了共享资源,团队在发布新功能时往往需要等待这些专家。 这会导致项目进度拖延。
在 第三章中,在 理解现有 SDLC 一节,我们将更详细地研究如何最好地理解当前的 SDLC 或组织中的 价值创造之旅 。 你将了解不同的方法来理解一个工件的生命周期、所涉及的任务、依赖关系,以及如何跟踪时间,以识别出那些适合自动化的任务,作为 平台服务的一部分。
现在让我们来探索一些我们曾与之合作过的组织中的这些用例和实施选项!
将专业知识集中作为自助服务用例
虽然 还有许多其他的用例,但以下是一些你可能会在平台中实施的自助服务功能示例。
提供合规的环境
根据行业的不同,数据存储、环境安全性以及所需的报告类型等方面都有一定的规定。 为了确保组织中任何团队请求的每个环境都符合所有这些规定,我们可以将其作为我们平台的自助服务功能来实施。
这是该用例的一个用户故事:“作为一名数据科学家,我希望将我的新数据模型与一个 类似生产的数据集进行验证!”
为了将这个故事分解成具体任务,有 一些细节需要解决,例如:这些数据集来自哪里? 数据科学家是否希望从多个可用的数据集里选择一个? 验证的输出是什么?它存储在哪里?谁可以访问它? 你如何选择数据模型? 这个验证运行多长时间?我们是否需要设置最大时间来关闭环境,以避免不必要的成本? 我们需要生成什么类型的审计数据,因为这涉及访问 生产数据?
另一个类似的用户故事可能是:“作为一名 QA 工程师,我希望使用与 80%终端用户相匹配的浏览器/操作系统组合,在连接到生产数据库的最新版本软件上运行我的手动测试!”
与前面的用户故事类似,我们需要提出一些额外的问题,例如:你想测试的软件产品是什么?它如何能够自动部署? 我们在哪里可以找到生产用户浏览器/操作系统使用的数据? 这个环境需要保持可用多长时间? 我们可以为这个配置的环境设置过期时间吗? 工程师可以看到哪些数据,哪些数据不能看到?因为我们正在处理访问 生产数据的问题。
这样的用例实现可能会有很大差异,但我们应该始终从终端用户的旅程开始考虑。 我们可能希望我们的数据科学家或质量工程师登录到我们的内部开发平台门户。 在那里,他们可以选择 配置符合要求的环境的用例。 然后,他们可以填写相关数据,以回答所有提出的问题。 这些输入可以用来创建该环境,并使其可供 请求该环境的团队使用。
进行性能和韧性测试
性能和韧性测试应该 成为每个软件版本发布的一部分,以确保新功能不会通过变得超级慢来影响用户体验(UX)。 我们还希望确保我们的新软件能够应对不可预见的情况,例如网络连接问题、缓慢或不可用的后端服务或数据,或者异常 高负载。
虽然有很多工具可以生成流量(负载测试工具)或模拟问题(混沌工程),但这些工具及其所需的环境往往需要大量专业知识来设置、配置、运行和后续分析
与其让我们的性能、站点可靠性或混沌工程师成为瓶颈,我们可以努力将这些专业知识集中化,并将其提供为我们工程团队的自助服务。 这里将是正确的用户故事:“作为开发团队,我们希望知道我们软件的最新版本是否存在任何性能或 可靠性下降!”
我们可以实现这种自助服务能力的多个迭代。 从提供包含相关工具的环境开始,所有开发人员需要做的就是执行测试并等待结果。 但理想情况下,我们希望最终达到这样的情况,即完全自动化甚至集成到我们的开发流程中。 我们应该朝着这样的目标努力:“作为开发团队,我们希望在主要拉取请求上获得性能和可靠性指标,以便知道最新的代码更改是否足够好,可以提升 到生产环境!”
就像在前面的例子中,我们可以从一个门户开始,团队可以请求性能测试环境。 要满足第二个完全自动化的用户故事,我们需要考虑提供一个可以从 CI/CD 流水线系统调用的 API,获取所有相关的输入参数,然后返回执行测试的实际结果。
新应用的入职
创建 新的应用程序或服务是开发团队的工作。 为此,他们通常必须经历许多不同的步骤,例如创建一个新的 Git 仓库,添加样板代码和元数据设置,配置构建(CI)流水线等等。 更多的步骤。
如果每个开发团队总是从头开始,我们不仅会出现多种本质上相同的做事方式,而且还会在所有开发团队中产生大量重复的工作,这会分散实际编码的时间。
一个适合平台自助功能的良好用户故事可能是这样的:“作为开发团队,我们希望基于一个完全配置的模板创建一个新的应用程序,这样我们就可以专注于编写代码,而不用担心如何构建、部署, 和运行!”
查看 整个 SDLC,我们的自服务甚至可以从产品团队使用诸如 Jira等工具创建的特性需求开始, 以下展示了作者曾与之合作的某个组织中新应用的端到端入职流程:
图 2.12:作为自服务的端到端应用入职(该图像仅作为视觉参考,文本信息并不重要。)
对于开发团队而言,旅程从 Jira 工单开始。 接着,他们前往该组织选择的 IDP——Backstage,在那里他们将走过自服务 应用程序入职 向导。 该向导会创建一个新的 Git 仓库,并预先加载好现成的代码模板、管道、部署说明、可观察性配置、所有权等内容。 一旦代码提交,管道会自动将构建物部署到开发代码空间,并自动连接到 Visual Studio Code!
访问可观察数据以应对事故响应
一旦新软件部署到生产环境,开发人员通常会将焦点转向下一个应用或功能。 然而,当灾难发生时,他们需要支持运维或 SRE 团队进行故障排除,以修复出现的任何问题。
在许多组织中,生产环境中的遥测或可观察数据仅限于由专门团队负责的生产环境访问。 还有合规性原因,毕竟不是每个人都应该访问可能敏感的数据。 但当身处所谓的 战情室时,快速访问相关的可观察数据非常重要,而不必填写太多请求表单、将数据从一个环境复制到另一个环境,或将生产可观察工具中的数据转换为开发人员常用的格式。
在 第三章,我们会更详细地讲解此用例,在这里我们简要描述用户故事:“作为开发团队,我们希望能轻松访问生产事故相关的可观察数据,以便快速解决 问题!”
现在我们有了几个用例,接下来让我们看看如何将这些想法转化为用户实际可以使用并从中受益的东西。
理解 TVP
在产品管理中,我们通常讨论的是一个 最小可行产品 (MVP)。 MVP 定义了你需要构建的产品最简单版本,以便将其推向市场。 MVP 的概念最初是由 精益创业 运动 引入的,这一运动由 埃里克·里斯 [10]推动。我鼓励大家阅读这本书或访问其优秀的博客文章,网址是 theleanstartup.com 网站。
MVP 中的 最小化 指的是一款帮助初创团队验证他们的想法是否真的解决了问题或痛点的产品版本。 所有这些精益创业的理念可以直接应用于平台工程 ,比如这样:
-
我们的团队是 初创公司
-
我们的平台是 产品
-
我们的目标市场是我们的 内部用户
-
我们的假设是它实现了 目标
在精益创业中, MVP 被定义为 一款足够好的产品的首个版本, 足够好 以至于可以发布。 它足够好,因为它已经解决了问题,尽管可能不是功能完整的。 它足够好,可以交到用户手中,验证我们的假设,并获得早期反馈,从而为后续迭代和改进提供基础。
现在,让我们深入探讨如何 应用所有这些精益创业的理念,定义我们自己的 MVP——或者,正如我们喜欢称之为的, 我们的 TVP!
寻找你的 TVP 用例
在 上一节中,我们已经探讨了我们可以通过平台提供哪些自助服务用例。 我们通过观察专家目前在哪些领域需要帮助以完成工作,或者在哪些地方涉及大量人工 工作,从而识别出了这些用例。
像任何新的软件产品一样,可能有很多我们想要实现的优秀功能。 问题是:我们应该从哪个开始做呢? 为了回答这个问题,最好根据几个维度来进行优先级排序。 其中一种方法是 ICE 评分 模型,但你也可以随意使用任何你熟悉的或已经在组织中使用的评分模型 [11]。 ICE 代表 影响力 (我们通过这个功能所能产生的影响), 信心 (我们有多大信心能够实现预期的影响),以及 容易程度 (实施这个功能所需的努力程度)。 我们为每个指标赋值 1-10,并将这些数值相乘。 如果我们对每个功能的想法都这么做,我们就能轻松地进行比较并得出首个优先级列表。 以下是我们之前讨论的四个用例的评分示例,使用一些虚构的数字作为 ICE 评分:
用例 | 影响力 | 信心 | 容易程度 | 分数 |
---|---|---|---|---|
提供 合规环境 | 6(良好的影响) | 5(中等信心) | 2(不容易) | 60 |
性能测试和 弹性测试 | 6 | 5 | 5 | 150 |
新应用程序的 入职培训 | 8 | 7 | 3 | 168 |
访问 可观察性数据 | 8 | 8 | 3 | 172 |
表 2.1:ICE 评分示例
正如我们所说, 前面的表格中的 ICE 分数只是虚构的。 然而,这些例子应该能给你一些关于这个评分如何工作的指示。 它的另一种形式还包括范围,因此被称为 RICE。 RICE 就是简单地将范围、影响力和信心相乘,并将结果除以努力——从而得出一个 易于使用的分数。
根据前面的例子,我们可以辩称,应该先从 新应用程序的引导 或 访问可观测性数据 开始,因为这两者得分最高。 另一方面,我们也可以认为 运行性能和弹性测试 会是更好的候选项,因为它似乎是最容易 实施的。
无论你使用的是 ICE、RICE,还是其他模型,它都应该帮助你决定首先解决哪个用例,目标是迅速产生影响。 在开始实施之前,让我们谈谈第一次实施应该达到的效果 !!
足够好与完美做到了!
我们大概 都同意,一个产品几乎不可能是完美的或已经完成的! 总有一些我们希望看到的功能,或者那些烦人的 bug 我们希望有人能最终修复。 当我们开始一个新产品时,比如我们的平台,我们需要改变这种思维,并接受第一次发布的版本是 足够好 的。这并不意味着我们在走捷径;这只是意味着我们需要抛开完美主义心态,勇敢地说,“ 它已经足够好——让我们 发布它! ”
在《精益创业》中,提到了谷歌地图的发布 [12] 。看起来团队当时正在向谷歌高级管理团队展示他们的新动态网页(使用 AJAX)地图解决方案。 他们的做法是首创的。 尽管开发团队仍然认为这只是一个早期原型,但管理团队仍然印象深刻。 据传,拉里和谢尔盖仅仅说了,“ 它已经足够好了。 发布它。 ”尽管开发团队有一些保留和担忧,但他们还是按要求进行了发布。 其余的——我们都知道——就是历史:谷歌地图是并且仍然是一个巨大的成功。 这个成功的背后是因为这个解决方案只做了一件事——但是那件事做得极其出色,并且成为了与其他竞争对手区分开来的关键。 仅仅通过有限的功能集发布,正是这让他们的竞争对手措手不及,并给了他们 领先的机会。
这如何转化为我们的 TVP 呢? 与 Google 的例子不同,我们不必害怕竞争——或者我们真的有吗? 事实上,我们是有竞争的:我们的竞争对手是那些开发团队,要么浪费时间按旧有方式做事,要么启动自己的项目,构建工具和自动化来解决问题,但只是为自己服务,而不是考虑如何为整个组织规模化解决这个问题。 整个组织。
这意味着我们不必完美无缺地交付我们的 TVP 第一次实施,但它必须足够好,以帮助我们展示提供的价值。 我们需要在假设中明确指出,这个价值是什么,具体是为哪些用例 我们实施的。
TVP – 验证我们的假设
因此,我们 选择了我们的用例,我们知道我们的第一次交付必须足够好,以便最终用户可以使用并从中获得价值。 但是,那个价值是什么呢? 我们如何衡量并证明我们的平台功能确实有 影响呢?
这是我们的产品假设所在。 回顾之前提到的相同用例,我们可以提出以下我们希望达到的假设影响: 有:
用例 | 假设 |
---|---|
提供 合规环境 | 验证新 数据模型 时合规违规减少 80% |
性能和 弹性测试 | 在生产环境中,识别并修复问题后,扩展性问题减少 50% |
新应用程序的 引导 | 新应用程序的交付时间减少 20% |
访问 可观察性数据 | 生产问题的故障排除时间减少 50% |
表 2.2:验证用例的产品假设
这个 假设也是一个很好的方式,可以向组织内需要为我们工作提供资金的人推介这个想法。 最终,我们是在做内部推销——我们需要充分论证为何我们要投资时间和金钱来建立一个新的内部开发平台。 我们所称之为假设的价值声明,很可能会与 你的领导层产生共鸣。
剩下的最后一个问题是:我们如何衡量和验证我们的假设? 对于一些人来说,假设我们有如 合规违规的工单数量, 与可扩展性相关的产品问题工单,或者 在事件响应工单上预定的开发时间,应该是很容易的。 更棘手的会是周转时间,因为一个组织如何定义周转时间,首先需要进行解释。 它是从创建初始功能请求开始,还是从开发者开始工作时算起? 此外,我们如何衡量完整的端到端流程? 虽然这一切都有可能,但我们必须确保知道如何衡量现状,以便在我们实施 TVP 后能与数字进行比较,从而验证 我们的假设。
构建、衡量并学习
我们知道我们想要构建什么,我们的假设是什么,以及如何衡量它。 现在,是时候将这些付诸实践了。 就像任何敏捷产品开发一样,我们要 构建、衡量并学习。 我们希望 尽早让终端用户参与进来。 最好的方式是,在我们仍处于原型阶段时,就将他们纳入并从中学习。 持续的反馈帮助我们及早做出重要决策,而不是等到我们有了最终版本,却因为错过了一些 显而易见的东西 而被用户拒绝。
该过程可以通过以下图示最佳地呈现,你也可以在许多受 Lean 创业运动启发的文献中找到:
图 2.13:构建、衡量并学习,以实现你的 TVP
这是一个持续的循环,旨在尽量减少构建、衡量和学习阶段之间的时间,以便在我们的测量反馈表明我们走错了 路径时,能够及时修正或调整。
在这一循环中,有几个里程碑是值得考虑的: 如下:
-
第一个原型:将其展示给有兴趣和潜在的个人重度用户。 他们会给你非常好的 早期反馈。
-
足够好:一旦你达到能够兑现用户故事承诺的阶段,扩大到一组早期采用者,以获取 更广泛的反馈。
-
假设已验证:一旦你通过首批早期采纳者验证了假设,便是时候向其他组织推广并宣传这个成果了。 利用你的早期采纳者为你推广这些新功能!
我们来了。 我们有了 我们的 TVP!
TVP
TVP 是我们平台的那个版本,它为最终用户提供价值。 它使平台工程团队能够验证我们的假设,即它实现了我们的使用案例的目的(例如,减少认知负担)。 它让我们以最少的努力收集最大量的验证性学习,为我们的下一个 产品迭代做准备。
当你开始引入一个平台时,追踪其采纳情况并判断是否走在正确的道路上是很重要的。 接下来,我们将帮助你定义 KPIs 以 做到这一点。
查看相关的 KPIs 以使采纳过程透明化
做正确的事情,并且谈论它。 (Georg Volkmar Earl of Zedtwitz-Arnim, 1978.)
一个常见的问题 我们在许多平台实施中看到的是缺乏其效益的证据。 理论上,这一切都说得通,但让我们用一些数字来支持它。 此外,它们有助于了解你是否朝着 正确的方向前进。
因此,定义不同的 KPIs 是有帮助的,通过这些 KPIs,可以了解平台采纳的进展情况。 第一步,你应该反思你已经衡量了什么。 通常,我们只需要以正确的方式组合现有的数字,就能够定义新的 绩效指标。
首先,我们需要理解度量标准、日志和追踪数据,定义我们如何获取它们所代表的值,以及它们的含义。 虽然它们都是相关的来源,但大多数对你平台的采用并不重要。 这只是代表了系统的一侧。 同时,应该明确的是,我们更关注的是用户及其 体验。 支持渠道、工单系统、开放的拉取请求、注册请求数量以及其他用户互动工具,对于理解适应情况非常有帮助。 这些数字的重要性会随着时间变化。 例如,申请访问平台的人数通常呈现出一个有两个平坦侧面的山丘形状。 在一侧,由于人们过于羞怯,不愿尝试新事物,因此采用初期会很慢。 而在山丘的另一侧,随着新项目较少地被加入平台,平台的采用增速变缓。 另一个例子可能是支持请求的数量。 这些通常会随着用户数量的增加而增加。 然而,随着平台转向自助服务,这些数字预计会随着时间的推移逐渐减少到最低水平,同时你的平台会变得更好,用户也会学会如何 使用它。
因此,第二点,你必须定义你平台当前 KPI 的上下文。 或许看起来显而易见,成熟平台可能会有不同的 KPI,或者你的利益相关者必须以不同的方式解读这些 KPI,但相信我,事实并非如此。 你需要让这一点变得透明、清晰并且易于理解 给每一个人。
重要提示
KPI 及其当前的成熟度背景需要被 明确指定。
举个例子,下面的图示展示了与平台集成不良的环境相比,KPI 可能会如何变化。 这些数字是基于从不同项目中收集的几个测量数据,并在此进行了概括。 在左侧,缺乏明显的改善方向,或者它们不断上下波动。 最明显的就是支持请求数量的增加。 将其放在较少应用使用平台的背景下来看,这是一种不良信号。 现在,在右侧,我们可以看到任何 KPI 都有所改善。 大多数数字不会降到零,但它们会保持稳定,无论是平台增长、最终用户增长,还是其他 长期影响。
图 2.14: 比较平台影响力(左:无平台,右:有平台)
这些数字 缺乏上下文。 当你通过平台的生命周期声明某个状态时,数字变得更加有意义。 在下一张图中,我们添加了一个 分段的示例。
随着首次采用支持请求的增加,每次变更的成本也在上升,而仅有少数应用程序被迁移。 在逐步推广阶段,可以看到新应用程序的数量迅速增加,同时,支持请求也在增加。 每次变更的成本趋于平稳。 在接下来的优化阶段,我们可以看到大规模的变更。 每次变更的成本和支持请求急剧下降。 新应用程序的数量已达到峰值,并且由于大多数能够使用平台的应用程序 已经迁移,所以数量开始缓慢下降。
在这三个阶段中,每个版本的发布时间逐渐减少。 这是自动化和自助服务的效果,并且在最后一个阶段开始趋于平稳。 这与几乎所有其他关键绩效指标(KPI)的稳定发展相似。 与其他图表相比,我们还改变了添加应用程序数量的表示方式。 现在,你可以看到每个季度迁移的应用程序数量以及总的 应用程序数量。
图 2.15:平台生命周期分段的示例 KPI
通常,当公司看到这些数字时,他们会停止在该领域的投资。 以产品思维来看,这些数字意味着你能够在固定的成本下提供持续的改进和创新,同时客户满意并能够进行 快速开发。。
定义平台采纳 KPI
我们 可以使用许多技术指标作为我们的 KPI 基础。 这些指标易于获取并且可以关联。 另一方面,这些数字的解读则取决于你。 服务请求的减少可能是因为你有出色的文档,但也可能是因为没有人使用这个平台。 如前所述,这就是为什么它需要提供完整的图景。 这可能变得复杂,因为每一个新的 KPI 可能会为数据添加更多的细节。 在某些时刻,你可能需要 删减这些内容。
对于开发人员和 DevOps 团队,已经有一些框架可以作为 基础使用。
DORA 指标
DevOps 研究和评估 (DORA) 是由 Gene Kim 和 Jez Humble 创立的初创公司,您可能从 《DevOps 手册》中了解到他们。 DORA 指标是一组四个关键绩效指标,可用于衡量 DevOps 团队的表现及其平台对用户的影响。 尽管它们并不总是完全适用,但它们被广泛采纳和使用,例如被诸如 GitLab [13]的系统所采纳。
DORA 指标如下:
-
部署频率:组织成功发布的频率 生产
-
变更的交付时间:从承诺到 进入生产
-
服务恢复时间:导致生产中失败的部署的百分比 时间
-
变更失败率:组织从失败中恢复所需的时间 时间
我经常看到组织将术语从 生产 更改为 发布 或 生产发布。这是因为大多数公司在发布面向最终用户之前都要经历多个阶段。 由于每个组织的具体情况不同,有多种解决方案来衡量这些数字,但最终通常会采用自己的方法 来实现。
要测量发布次数,您可以针对支持您发布的喜爱的源代码版本控制系统运行一个简单的 API 调用。 这些数字可以导入数据库并通过 Grafana 仪表盘进行可视化。
识别变更交付时间更为复杂。 为此,您必须跟踪提交并查看它们何时包含在发布中。 然而,这两个指标都很容易获取和计算。 另一方面,变更失败率变得更为棘手。 为此,您需要在整个部署和生产链中包含有关您的发布的信息,以便在发生故障时,您可以识别问题属于哪个发布的日志。 此外,您还必须确定是否真的是由发布引起的问题 还是其他原因。
恢复服务的时间与同样的过程一样。 你需要确定何时发生了事故,何时成功关闭,以及何时通过恢复解决。 你的事故管理系统可以提供这两个指标。 这些指标。
关于 DORA 指标的特殊之处在于,他们已经研究了成千上万家公司,为你提供了与其他公司相比的好坏概述。 [14]。
精英 | 高 | 中 | 低 | |
---|---|---|---|---|
部署 频率 | 按需 | 每天一次到每周一次之间 一周 | 每周一次到每月一次之间 每月 | 每周一次到每月一次之间 每月 |
变更 交付时间 | 少于 一天 | 一天到 一周之间 | 一周到 一个月之间 | 一周到 一个月之间 |
变更 失败率 | 5% | 10% | 15% | 64% |
失败的部署 恢复时间 | 少于 一小时 | 少于 一天 | 一天到 一周之间 | 一个月到 六个月之间 |
% 的受访者 | 18% | 31% | 33% | 17% |
表 2.3:不同成熟级别的 DORA 指标
DORA 指标的优点在于,大多数公司都能收集到这样的数据。 另一方面,作为平台工程师,我们缺少一些关于直接和个人反馈的方面。
下图是一个终端用户的 Grafana 仪表板,展示了实时数据。 在左上角,你可以看到部署到生产环境的应用程序数量为 105,总共的部署次数为 1.847\。 在右上角,显示了变更的周期时间(单位:小时),为 612.5,变更失败率为 0.522%,部署频率为 5.02,MTTR 为 199\。 底部的图表展示了每个应用程序的部署情况。
图 2.16:DORA 仪表板的实际示例(该图片仅供视觉参考,文本信息并非必需。)
如何开始使用 DORA? 一种方法 是利用 Keptn,一个 CNCF 孵化项目,提供自动化的可观察性,用于 Kubernetes 上的应用感知部署。 Keptn 可以通过命名空间级别的注释启用,并将创建 Prometheus 指标和 OpenTelemetry 追踪,便于观察部署。 这些指标包括部署的持续时间、部署成功和失败的情况,并为部署、应用、环境和版本提供维度。 开箱即用,这 为你提供了一些核心的 DORA 指标,你可以将它们展示在你的仪表板上,如下图所示:
图 2.17:Keptn 提供自动化的部署可观察性,报告一些 DORA 指标
Keptn 提供了额外的功能,比如基于 SLO 的部署成功验证,并且还会在多个阶段追踪部署,以识别额外的指标,例如,“部署在哪个环节停滞? 推广过程中遇到了什么问题?”
要了解更多信息,请查看 Keptn 网站和 DORA 教程 [15]。
SPACE 框架
SPACE 框架 结合了五个维度 用于评估开发团队的生产力。 GitHub、微软和维多利亚大学共同开发了这一方法。 它专注于团队协作的方式及其成果。 查看这五个维度 将使 这一点变得清晰:
-
满意度与福祉:收集有关整体满足感、幸福感以及与工作相关的心理健康的信息。
-
绩效:以输出为导向的指标,如已完成的任务或发布的版本
-
活动:关注如提交次数、合并请求或代码审查等活动
-
沟通与协作:我们将检查团队的沟通行为和对齐情况,以及使用审查和评论机制
-
效率与流动:开发和部署过程的顺畅程度
你可能会觉得可以使用部分或全部的 DORA 指标来回答性能、效率,甚至是活动,没错。
对于满意度和福祉,我们可以使用如下方法,如员工满意度得分(ESS)、员工净推荐值(eNPS)或员工参与指数(EEI)。在开始时,你可以通过简单的调查开始。
性能指标的想法包括交付时间、从事件到恢复的时间、发布次数和测试代码覆盖率。
要衡量活动,你可以参考以下内容:
-
部署频率
-
代码行数
-
提交次数
-
拉取请求数量
-
审查次数
-
完成的任务/问题
-
已解决的故事点
这些数字并不总是容易收集的。在一些国家,明确禁止评估个人的表现,且只允许对一定数量的人进行此类评估。这也常常使得 SPACE 受到批评。因此,如果你希望使用 SPACE,我们不得不承认,这些指标对于了解开发周期很有帮助,你的主要客户,你应该和负责数据保护的团队沟通,他们负责确保正确设置这些内容。
类似的问题 出现在沟通与协作维度上。 如果你不想查看通信工具的用户统计数据,合并请求、合并时间,或提交、合并请求或问题票中的评论数量,都是一个不错的开始。 你还可以通过征求反馈来评估会议的质量。
效率和流动性的最后一个维度可以再次基于 DORA 指标。 此外,评估开发周期中有多少次交接,开发人员真正专注的时间是多少,以及他们的 速度是多少,也是非常有帮助的。
SPACE 框架 可以在个人、团队和组织层面使用。 以下是迈克尔·考夫曼(Michael Kaufmann)书中的图示, 《通过 GitHub 加速 DevOps》,提供了进一步的 这些层级的示例。
图 2.18:迈克尔·考夫曼(Michael Kaufmann)提供的 SPACE 指标示例 [16]
通过 SPACE 框架,我们 现在可以深入洞察开发过程,这对我们作为平台工程师非常有帮助,并且它扩展了 DORA 指标。 我们可以将这些 指标相互叠加。 这使得产品负责人、架构师和平台工程师可以评估他们是否以正确的方式做正确的事情。 然而,我们缺少来自 开发者体验 (DevEx)专家的反馈。
DevEx 框架
SPACE 和 DORA 缺乏 DevEx 的视角。 此外,虽然 我们正在关注满意度和流动性,但它并没有给我们提供关于平台工程团队的完整画面。 这就是 DevEx 框架 发挥作用的地方 [17]。它将视角限制为仅仅三个维度。 它们共同构建了 DevEx 的基础,并能够扩展其 行动范围。
图 2.19:DevEx 的三个核心维度
从开发者的角度来看,流动状态是指他们 100%专注于编写代码,几乎忘记了时间、饥饿和其他需求。 流动状态越容易实现和维持, DevEx 就越好。
我们谈论了很多认知负担。 简而言之,一个良好的 DevEx 包含非常低的 认知负担。
最后,反馈循环衡量开发者在其行为上收到反馈的速度和质量。 反馈越好,开发者就能以最小的摩擦和损失,更加持续和可操作地做出反应。
要 衡量 这些是困难的,因为答案介于实际指标和 主观反馈之间:
-
心流状态:
-
指标:重新提交的提交记录,变更请求数量,每个 拉取请求的提交次数。
-
主观反馈:他们感到被打断的频率如何? 他们是否感到持续的压力或紧迫感? 他们的 决策自主权有多高?
-
-
认知负担:
-
指标:重新打开的缺陷数量,调试时间,组件的依赖项数量,以及解决 技术问题的时间
-
主观反馈:编码时间与其他任务的时间;他们组件的技术范围,以及他们需要多频繁地 切换上下文
-
-
反馈循环:
-
指标:周期时间,部署频率, 平均修复时间 (MTTR),缺陷数量,以及 测试自动化的水平
-
主观反馈:代码审查的有效性、内部团队沟通以及 反馈质量
-
正如你可以 从这些例子中看到的,解释的空间很大。 当你定义平台的 KPI 时,必须确保这些指标被清晰描述。 你需要特别准确地解释你如何解读哪些反馈和指标以及 如何解读。
重要提示
架构师必须理解这些 KPI 及其含义,以便能够有效地改进平台。
使用性能指标
通过 之前的框架,一些性能指标被突出了出来,例如关闭的问题数量或故事点。 这些指标在团队层面上运行良好,但也可能导致 许多误解。
然而,有一种方法并非抽象且不清晰,像故事点那样,而是一种万无一失的方式,用来与用户和利益相关者讨论平台及其效率:成本。 我知道这不是一个创新的 KPI,但许多团队往往做得不正确,产品负责人也对此感到畏惧。 我也并不意味着要报告团队的成本、基础设施或许可证。 你可以利用成本指标作为平台表现的证明,及其平台团队。
每次变更的成本
每次变更的成本 是一种 有效的方式,能够突出平台的改进和能力。 在云原生生态系统的最终用户社区中,我们可以看到这个 KPI 被用来讨论平台的运行情况。
让我们比较两种不同的每次变更成本: :
-
$500 每次变更:每月$15,000,或每年$180,000,基于每月 30 次变更
-
$80 每次变更:每月$2,400,或每年$28,800,基于每月 30 次变更
显然,两个数字之间有着巨大的差距。 但是你应该考虑到,最初你的成本会较高,而且变更次数很少。 理想情况下,平台团队和平台本身会随着时间的推移变得更好,从而稳定整体成本,但变更的次数会增加。 这将导致每次变更的成本降低。 下面的图表展示了这一点,便于更好地可视化。 虚线展示了整体成本的例子。 随着平台变得更加高效,整体成本略微增加。 深色实线表示每年的变更次数,虚线则表示每次变更的成本。 正如你所见,随着你能够进行的变更次数增多,每次变更的成本大幅下降。 这虽然是一个非常基础的数字,但它是你与平台及其平台工程团队沟通的有力工具。
图 2.20:每年总成本 示例
每个项目/产品的成本
相关的 成本每变更的成本是每个项目、产品或应用的成本。 我们可以从两个不同的方向来看这些成本:
-
总共享成本:仅关注团队成本、额外工作量或非因果关系成本等方面 不是因果关系基础的
-
平衡的因果关系成本:因果关系成本包括除共享成本外,项目所消耗的基础设施和服务的实际成本 项目
为了突出平台和团队的表现,我建议选择总共享成本。 这样的数字很容易获取,并且可以在项目之间进行分配,使得你能够在每月不费太多力气的情况下追踪它们。 另一种选择是 每个项目的引入成本。对于这个指标,你需要计算每个使用平台的项目在引导过程中的工作量。 然而,我个人不会在这些指标上花费太多精力。 它们更像是修辞数字,并不能提供关于平台性能的深入见解,而更多是关于你在引导过程中的效率 在 引导中的表现。
用户/产品的间接成本
间接成本的视角与每变更成本非常相似。 在这里,你将所有直接成本和共享成本相加,并仅减去消耗的资源。 因此,区分像代理、网络流量以及可能的与用户相关的个人成本与产品实际使用的资源是很重要的。 计算这一点比较复杂,但结果是平台及平台团队的性能指标。
这是一种分歧的指标。 一方面,它应该尽可能小,以便能在内部销售平台。 另一方面,这个数字代表了平台实际价值的成本。 因此,我认为我们需要隐藏一个既不太昂贵也不太便宜的中间地带。
总结
本章向你介绍了定义平台原则以及如何制定你的平台团队目标。 现在你手中拥有了文档化和设计平台架构的工具,接下来是针对不同平台的最终用户示例。 本章为你提供了关于平台能力的额外视角。 通过 TVP 的方法,你学会了如何专注于单一里程碑而非全局目标,并更快适应需求。 最后,我们向你介绍了几种衡量平台采用情况和 性能指标的方法。
在 第三章,我们将讨论为平台构建基础的细节。 首先,我们将向你介绍我们的参考公司和示例公司,并解释一些细节。 你将学习到基础设施基础、混合云以及 软件即服务 (SaaS)的挑战,以及创建基础架构的参考架构 。
进一步阅读
-
[1] 延迟成本:项目延迟的经济影响 交付: https://businessmap.io/lean-management/value-waste/cost-of-delay
-
[2] Evan Bottcher,关于平台的讨论: https://martinfowler.com/articles/talk-about-platforms.html
-
[3] Miro 模板,平台工程目标 画布: https://miro.com/miroverse/platform-engineering-purpose-canvas-template
-
[4] 团队 拓扑结构: https://teamtopologies.com/
-
[5] PlatformCon2023,平台即代码:通过参考架构简化开发者平台设计 架构: https://youtu.be/AimSwK8Mw-U?si=1CDAJb1gLtlCgeyH
-
[6] PlatformEngineering.ORG 工具: https://platformengineering.org/platform-tooling
-
[7] CNCF 景观: https://landscape.cncf.io/
-
[8] Wasm 示例源代码:
-
通用 页面: https://webassembly.org/
-
一个简单的 Kubernetes 运维程序用于运行 Wasm – kwasm: https://kwasm.sh/
-
一个企业级运维程序和开发工具包用于 Wasm: https://www.spinkube.dev/
-
-
[9] ThoughtWorks 平台技术战略 层次结构: https://www.thoughtworks.com/insights/blog/platform-tech-strategy-three-layers
-
[10] 精益 创业: https://theleanstartup.com
-
[11] ICE 打分模型 模型: https://www.productplan.com/glossary/ice-scoring-model/
-
[12] 谷歌创业课程 地图: https://www.startuplessonslearned.com/2010/09/good-enough-never-is-or-is-it.html
-
[13] GitLab DORA 指标: https://docs.gitlab.com/ee/user/analytics/dora_metrics.html
-
[14] DORA 报告 2023: https://cloud.google.com/devops/state-of-devops
-
[15] DORA 教程: https://keptn.sh/stable/docs/guides/dora/
-
[16] 迈克尔·考夫曼,《通过 GitHub 加速 DevOps》 GitHub: https://www.packtpub.com/product/accelerate-devops-with-github
-
[17] DevEx 框架: https://queue.acm.org/detail.cfm?id=3595878
第三章:构建支持 平台能力的基础
解决用户遇到的问题,设计良好的用户和开发者体验,避免技术复杂性,是成功产品和成功 平台工程的基础步骤。
实际上,许多项目失败的原因是忽视了这些基础原则:我们看到架构师迷失在技术细节中,忽略了他们需要解决的问题。 项目失败的一个常见原因是最终用户在新产品创建过程中的参与不够持续。 通常,架构决策是在没有考虑新产品如何适应现有生态系统、流程和组织技能的情况下做出的。 所有这些都导致了一个不稳定的基础,有限的成功潜力 。
在本章中,我们将详细讲解定义平台的坚实基础的必要步骤和流程,该平台可以从初始功能集成长为支持关键企业的 平台能力。
因此,我们将在本章中涵盖以下主要主题:
-
金融 One ACME——我们的 虚拟公司
-
通过找到 正确的视角 克服平台的复杂性
-
考虑现有流程并整合一个 新实施
-
设计 基础架构架构
-
探索多云、多 SaaS 以及能力的 碎片化
-
探索一个参考架构 用于我们平台
金融 One ACME——我们的虚构公司
在本节中,我们将学习 如何理解工程组织中用户的需求,如何平衡不同团队的需求,以及如何决定平台应该包含哪些能力,哪些 不应包含。
为了使这个更具应用性和实际性,我们将向您介绍 金融 One ACME。虽然它是一个虚构公司,但我们将在本书的其余部分中介绍并使用的历史、技术挑战和团队,都是我们过去几年在许多组织中看到过的。 拥有这样的组织可以帮助我们更好地解释如何将本书中所介绍的理论应用到 实际行动中。
让我们来看一些 关于 金融 One ACME的重要细节:
-
Financial One ACME 的简史:Financial One ACME 一直是金融服务市场软件解决方案的领导者。 公司成立于 2000 年代初期,采用经典的三层应用架构(Windows 富客户端、应用服务器和数据库),客户将其安装在自己的数据中心中,并且每年发布两次软件更新。 每年发布两次。
多年来,Windows 富客户端逐渐被 Web 客户端所替代。
-
提供自托管 SaaS 和本地部署的产品:2015 年,SaaS 托管版本的需求增加了。 与其重新设计产品以支持多租户的 SaaS 解决方案,决定是简单地为每个 SaaS 客户单独托管应用服务器和数据库,且所有服务都托管在现有的 Financial One ACME 数据中心中。 这解决了数据存储位置和数据访问的问题。 然而,这意味着随着 SaaS 客户数量的增长,运营开销也随之增加,因为每个客户(=租户)都在其 独立的虚拟机上部署了生产环境。
此外,IT 运维面临着不断扩展数据中心容量的挑战,以应对生产环境随业务增长而扩展的需求。 由于客户规模不同,他们还需要为每个租户进行单独的容量规划,以确保环境大小合适,避免 资源过度配置。
另一方面,开发团队负责预生产环境的管理。 这包括从开发工作站、构建服务器到预生产测试和 暂存环境的所有设施。
-
转向每月发布:从 2015 年到 2020 年,发布频率不断提升,最终形成了每月发布一次的周期。 这些发布由开发团队构建和测试,然后交给 IT 运维部门进行部署,既包括 SaaS 环境,也包括那些仍在本地部署软件的客户。 这种速度的变化导致了一个情况:并非每个客户都希望如此频繁地更新软件,因为更新必须与他们的内部变更请求流程对接。 一些客户的版本滞后了多达六个发布,这给开发团队增加了额外的负担,必须支持所有这些 较旧的版本。
-
迁移到云端:在 2020 年,业务扩展到新地区,随着扩展,要求也增加了,需要在新地区提供 SaaS 服务。 公司决定不再建设更多的数据中心,而是 将现有的生产架构迁移 到公共云提供商的计算服务上。
这一举措还包括了新的流程和指南,关于如何更新软件或访问这些云服务器上的数据——例如,如何访问属于 租户 Y 的 X 服务器日志!
-
被新兴金融科技公司超越:在疫情期间,涌现出了一些新兴的金融科技软件公司,这些公司是 为云端而生、为云端架构设计的!这给 Financial One ACME 带来了压力,因为这些新的仅 SaaS 公司的架构不需要维护一个支持 SaaS 和本地环境的传统架构。 它们的架构也是多租户和多云的,这使得它们比 Financial One ACME 的同类产品运行得更加高效和低成本。
领导层做出了 战略决策,重新架构他们的服务,同时提升开发、支持和运营现有平台的效率,直到每个客户都能迁移到未来的 仅 SaaS 服务。
现在我们到了 2024 年,是时候思考如何帮助这个组织实现现代化并重新创造自我。 Kubernetes 和云原生是未来的技术栈! 希望寄托在平台工程上,改善工程师们在未来技术栈上的工作,同时维护旧技术栈。 这就是我们发挥作用的地方——新成立的平台 工程团队!
现在,让我们了解一下我们未来的 内部开发平台(IDP)的潜在用户:
-
开发团队:管理所有预生产工具 和环境
-
IT 运维:谁在管理本地数据中心和云端 计算资源
-
DevOps:负责从应用层面部署和运营现有基于 SaaS 的生产环境的团队
-
质量工程师:专注于在软件推广到生产环境之前进行测试的团队 以保证质量
-
站点可靠性工程师(SRE):专注于弹性、可用性,帮助报告和执行服务水平协议(SLA) 和服务水平目标(SLO)
-
技术文档:负责准备面向最终用户的文档,配合每个发布版本,包括发布说明、新特性以及 使用指南
-
其他人:包括 数据库管理员, 项目经理, 产品负责人, ProdSec, 以及其他人
现在我们知道了团队是谁 我们 作为一个团队是谁以及 我们的 潜在用户是谁,接下来让我们看看 我们 需要如何构建一个平台来解决 他们的 痛点!
通过找到正确的视角来克服平台的复杂性
“我们花了几个月时间构建了我们的新平台。 开发人员讨厌它! 帮我 理解为什么!”
我们不想最终陷入 那种不得不在公共讨论论坛上发布这样的问题的情况。 这个标题——信不信由你——来自一篇真实的帖子。 如果你想了解更多关于这个故事的信息, 可以阅读 Reddit 上的帖子 [1] ,该帖子提供在 进一步 阅读 部分。
那么,为什么会发生这种情况呢? 许多平台工程项目失败的原因与其他产品开发项目失败的原因相同:有人有了一个好主意——建造了或者让别人建造了一个产品——但最终发现没有人看到这个新产品的好处,因为它没有解决任何人实际面临的问题。 人们所面临的问题。
许多人犯的错误是不通过潜在的最终用户来验证初步想法,这些用户可能会从该解决方案中受益。 如果你找不到一群具有需要的新产品能够解决的实际问题的用户,那么最好根本不构建产品,因为它注定会从 一开始就失败。
如果你曾从事过产品管理工作,你可能会想,“但这不就是产品管理 101 吗!” 完全同意! 然而,并非每个负责构建新平台的团队都拥有产品管理经验。 我们见过许多团队发现自己处于这样一种境地,他们可以开始构建一个平台,但并未意识到它与构建任何常规产品的相似之处。 在开始构建之前,构建产品的许多任务已经开始了 *产品。
应用基础的产品管理原则——“不要给用户更快的马”
成功的 平台工程方案应与过去成功的产品团队所做的相一致: 过去:
-
识别 一个拥有足够大用户群体的问题 基础
-
理解 为什么这是一个挑战以及目前它的负面影响是什么 现在
-
研究 为什么这个问题尚未 得到解决
-
如何 量化解决 这个问题 的好处
为了回答这些问题并超越基础,我们建议与你的潜在终端用户进行交谈。 让他们告诉你他们面临的实际问题,并给予他们自由,解释一种能使他们理想地完成工作的解决方案。
在倾听时,请确保你没有被当前知道的任何技术限制或挑战所局限。 有一句名言,归功于 亨利·福特 据说他曾说:“如果我问人们他们想要什么,他们会说更快的马。” 你会发现,用户通常能够轻松描述他们的问题。 然而,他们通常提出的解决方案受限于他们认为可能的范围,或者是当前意识到的技术限制。 。
现在,我们并不打算从一个需要数月或数年的革命性新软件工程方式开始。 然而,这种思维方式是重要的,因为它是朝着解决别人未能解决的问题迈进的一步,而这个解决方案 能够被广泛采用!
首先,我们来思考如何 解决这个问题 使用 最简单且最快速的 技术方案。 不要一开始就过度设计 试图提出最佳或最具革命性的技术实现方案。 虽然这是一个鼓舞人心的目标,但我们的初衷是 快速获取反馈并验证 我们提出的解决方案是否解决了潜在 最终用户的痛点。
为了获取这些快速反馈,你需要做 以下几件事:
-
快速交付 一个 解决方案
-
展示 给你的潜在 最终用户
-
获取 持续反馈
-
根据 反馈进行 优化
-
继续 通过这些步骤 进行迭代
这个过程 会一直持续,直到你能够证明用户愿意使用你的解决方案,因为它改善了他们完成 事情的方式。
避免“沉没成本谬误”
并非每个项目都会成功,不管你尝试和迭代多少次。 “ 沉没成本谬误 ”是一个在日常生活决策中经常见到的问题模式, 在软件工程中也同样存在。 它突出了一个问题,即组织在一个策略上继续投资,因为已经做出了投入,即使显然停止投资会更好,因为该策略不可能成功。 关于这一点有很多材料可以阅读,例如 沉没成本 的相关文章 [2]。
因此,明确何时停止迭代非常重要。 如果“用户喜欢这个解决方案”这一时刻在一定时间内没有发生,你必须准备好停止并终止这个项目。 为此,你需要为自己设定 达成这一 验证点 的里程碑。 记住,正如前面所提到的,你不希望最终出现类似“我们花了几个月时间构建这个平台。 开发者讨厌它! 帮我 理解为什么!”这样的结论。
构建用户需要的东西——一个实际的例子
让我们回到最初的步骤。 我们需要构建的是什么? 在产品管理中,有许多不同的思维方式(例如请参见 图 3**.1 ,这是其中一个例子),它们展示了构建的事物与用户需求之间的差异。 如果你搜索关于过度工程的思维方式,结果也是如此。 它们得出了相同的结论: 在没有先理解用户需求的情况下构建事物! 用户需求!
图 3.1:过度工程悖论
我们有可能避免一种情况 ,即最终构建出一个无法解决真正问题,或者以过于复杂的方式解决问题,并且没有投资回报的情况。 投资回报。
每个旅程中最难的步骤是第一步。 在我们的案例中,第一步是理解 谁 是我们潜在的平台注册用户,以及 平台可以解决的真正痛点是什么 。
为了看到这一点的实际应用, let’s walkthrough the several steps on how we would approach this for Financial One ACME!
第一步——理解用户的真正痛点
在走廊中的对话中,开发团队常常抱怨,分析软件中的问题,在生产环境中要比在预生产环境中容易得多。 他们可以完全访问构建服务器(Jenkins)和测试工具(Selenium 和 JMeter)的所有日志,以及他们部署软件的环境。 他们可以轻松增加日志级别,或者迅速部署一个包含更多日志输出的新版本,以更快地解决问题。 更快地解决问题。
当 生产环境中发现问题时,分析问题是完全不同的故事! 开发团队必须通过提交 Jira 工单来请求 IT 运维的许可,以便获取日志访问权限。 这有时需要几个小时,因为 IT 运维团队通常会被其他许多任务压得喘不过气。 IT 运维团队也没有关于软件记录日志的位置的内部知识,开发团队也不总是在初始工单中提供这些信息。 因此,通常需要多次迭代才能捕获到所需的日志,并将其上传到 IT 运维团队管理的工具,以便与其他团队共享生产相关数据。 更改日志级别也不是那么简单。 这种生产环境的更改必须遵循特别的变更审批流程。 作为软件变更,这由 DevOps 团队处理,这进一步拖慢了进程。 这使得事情变得更加缓慢!
对开发团队来说,这意味着他们不能像以前那样直接远程登录到服务器上即时访问和分析日志,而是必须通过 IT 运维的中央生产数据存储工具来分析这些日志。 然而,开发团队并不太熟悉这个工具,因为他们并不经常使用它。 该集中式工具还有自己独立的权限系统,最初是为了防止未授权的访问敏感数据而设置的。 由于该系统与开发团队使用的认证和访问控制系统并未集成,通常与当前团队的分配情况不同步。 这导致了个别工程师无法访问他们需要的日志,从而导致与 IT 运维和 DevOps 之间的额外沟通,或者通过一个未经批准的捷径,直接询问恰好有访问权限的开发同事来获取 日志!
如你所见,开发团队中充满了很多挫折和痛苦。 但正如你所想象的那样,IT 运维和 DevOps 团队也面临着大量的挫折。 他们在操作生产环境时,经常会被要求查找并提供生产数据访问权限,或批准日志级别的更改,这些任务不断打断他们的工作。 他们经常需要来回沟通,才能弄清楚请求的是什么数据,数据在哪里,谁应该有权访问 这些数据。
通常会有大量的讨论来理解完整的情况,理解双方的痛点,并且获取足够的细节,以便开始思考更好的解决方案。 当你在组织中应用这一方法时,计划足够的时间并提前通知你想要交流的人,以便他们可以整理思路来进行 这些对话!
为了开始提出解决方案,最好先组织各方痛点的概览,如下表所示: 在 以下表格中:
问题: 开发人员无法直接访问生产环境中的日志 日志 |
---|
痛点: 开发团队 |
需要创建 Jira 工单来请求访问生产环境中的日志。 |
大量时间都花费在工单上,直到 IT 运维团队找到正确的日志 来捕获。 |
在生产环境中进行故障排除时,很难更改日志级别。 但仍然需要创建更多变更请求工单。 |
低效的日志分析。 开发人员更习惯于使用预生产环境中的工具。 生产环境中的工具对他们来说不够直观,反而降低了 效率。 |
处理生产日志中的权限问题 分析工具。 |
表格 3.1:将问题及其痛点组织成易于理解的表格
现在我们有了 双方的痛点列表,是时候考虑这些痛点的解决方案了,分析该解决方案的影响、成本,以及其 投资回报率 (ROI) 将会是什么!
步骤 2 – 量化解决这些痛点的收益
我们从 量化一个解决方案的影响开始。 这是必要的,以证明构建一个能解决这些痛点的平台所投入的时间和精力。 虽然之前解释的痛点是真实的,但我们需要理解这些问题是偶发的还是经常发生的。 我们需要回答的问题是,是否值得投入数周时间来构建一个解决 这个问题的平台?
回到相同的团队,现在是时候量化那些列出的痛点的成本,按时间花费或实际的美元来计算。 我们可以通过有根据的猜测或从他们当前的时间追踪中获得这些数字(最佳选项)。 由于团队目前正在使用工单,我们应该能够获得这些工单的总时间花费 在两方之间。
这里是一个修订后的表格,包含了额外的 成本影响!
痛点/每月花费的时间 | 开发团队 | IT 运维 | DevOps |
---|---|---|---|
请求日志访问的缓慢过程 | 2 天 | 4 天 | |
更改 日志级别 | 0.5 天 | 0.5 天 | |
低效的 日志分析 | 2 天 | ||
解决权限问题的变通方法 | 0.5 天 | ||
总计 | 每月 5 天或每年 60 天 每年 | 每月 4 天或每年 48 天 每年 | 每月 0.5 天或每年 6 天 每年 |
表 3.2:量化每个痛点的收益,并提供一个简洁的概览
现在,这是一个 很好的概览,包含了一些有趣的统计数据,可以帮助我们更容易做出决策。 如果我们能够解决开发者目前面临的所有痛点,尤其是他们现在无法轻松访问生产环境中的必要日志文件,我们每年可以节省多达 114 个工程工作日。 这是一个很好的起点,也是一个强有力的理由,去投资提高团队效率,进而投资于 平台工程!
步骤 3 – 提出一个能改善开发者体验的解决方案
现在我们知道 我们每年最多可以节省 114 个 全职等效 (FTE) 工程师工作日,我们应该继续推进并提出一个解决方案,呈现给相关用户。 我们不能展示解决方案的技术细节,而是应该描述开发者将如何体验用户旅程。 开发者体验 是平台工程中经常讨论的 关键词。 所以,让我们提出一个能为开发者带来全新体验的解决方案,激发他们使用 我们的解决方案!
像产品工程一样,我们需要通过询问终端用户他们希望如何与未来的解决方案互动来让他们参与其中。 开发者通常偏好通过代码或简易使用的 命令行接口 (CLI) 来完成所有工作。 这里重要的一点是,我们希望提出一个适合当前工作流程和工具的解决方案,这样我们的用户就无需学习另一个工具或改变他们的工作方式。 。
在我们的场景中,提案采用了 代码化配置 的方法。 开发者可以在代码中指定日志级别、日志输出、所有权和通知渠道。 这可以是一个独立的 YAML 或 JSON 文件,也可以是 Kubernetes 部署定义的一部分。 开发者只需要将该文件提交到他们的 Git 仓库。 DevOps 和 IT 运维可以验证并批准 拉取请求 (PR), 确保所有数据准确无误。 如果有新的警报出现,或者有人请求日志,新的平台工程能力将获取该组件的正确日志文件。 然后,它使用所有权和通知信息联系开发团队,提供相关日志的链接或摘要,以便解决问题。 下图展示了提议的端到端工作流,并展示了它如何改善开发者、IT 运维和 DevOps 的体验:
图 3.2:通过代码化配置提升开发者、DevOps 和 IT 运维的体验
该 提议的解决方案解决了当前所有的痛点,同时确保只有拥有组件的团队可以查看他们的数据。 该解决方案还具有可扩展性,使其在生产和预生产环境中都能以相同的方式工作。 这是未来迭代的 这一能力!
步骤 4 – 你的第一个原型
如果提议的 解决方案被接受,就可以开始进行原型实现。 原型是获取快速反馈的好方法,虽然实现不必完美,但也能提供有效反馈! 就我们的目的而言,原型是最好的方法,因为我们需要验证用户想要解决的问题是否能够以他们愿意使用的方式解决 我们的解决方案。
原型的第一部分应聚焦于 接口 ,即我们正在构建的这一新功能的接口。 在我们的案例中,这就是我们之前讨论过的配置文件。 原型设计中的一个关键考量是决定解决方案是否应该在现有技术栈(云虚拟机上的三层应用)上解决问题,还是仅仅想在 Financial One ACME 将其新的云原生实现迁移时解决这一问题。 目标应该始终是提供相同的开发者体验,无论底层技术栈如何。 然而,在实施过程中,收益和投入的差异可能会 非常大。
为了验证这两个技术栈的原型,请参见以下的配置即代码文件:
表 3.3:两种声明性方式实现提议的解决方案
两种 选项都允许开发团队提供所有相关信息,并允许 DevOps 和 IT 运维人员 验证这些信息:
-
服务详情:名称、版本、所属组件 。
-
所有权:团队标识符、选择的通知工具和选择的频道 。
-
日志:日志级别是什么,日志写入到哪里 。
在这个 Kubernetes 示例中,你还可以看到我们如何通过已经标准化的注解来传递一些信息,例如app.kubernetes.io/name
、part-of
和version
。我们还可以看到像日志级别这样的信息可以传递给容器,因为在这个配置文件中改变日志级别也可以改变由已部署的容器产生的日志!
在进行任何实际实现之前,可以先获取关于该提议配置文件格式的早期反馈。 这给开发、DevOps 和 IT 运维团队提供了机会,他们可以提出额外的元数据需求,如 服务的优先级 。 这可以用来定义当生产环境出现问题警报时的升级流程,或用于发送更新邮件给 团队的备用邮箱地址。
我们提议的解决方案的核心不是配置文件,而是能够响应开发者请求的自动化过程,同时也可以由警报触发。 最简单的解决方案是一个简单的服务,它暴露一个 REST 端点,可以用于这两种使用场景的触发。 该服务需要在一个能够访问 以下内容的环境中运行:
-
持有“配置文件”的 Git 仓库或 K8s 集群
-
对目标环境中现有日志解决方案的 API 访问
-
访问 通知工具的 API
要开始 一个最小可行原型,我们可以定义只支持 K8s、只支持生产日志解决方案,并且只支持单一的通知工具,如 Slack。 这使我们能够通过原型证明其价值,并且有扩展到其他配置数据源、附加日志环境以及更多通知工具的选项。
剩下的就是 REST API 的定义。 在这里,我们应该与开发和 IT 运维团队进行咨询,因为他们是该 API 的主要使用者。 开发者使用它按需请求访问日志,而 IT 运维团队在问题被识别时调用该 API。 下表展示了适用于这两种使用场景的 REST API 定义示例:
表 3.4:新提议服务的 REST API 示例
尽管两种实现方式都试图为这些服务找到正确的日志文件,并将其发送到正确的团队,Notify API 还接收一个事件参考,这使得我们的实现能够在发送给 开发团队的消息中包含该信息。
作为这个解决方案原型的一部分,我们还可以做很多事情: 例如:
-
确定当前的 SDLC 效率,并学习如何衡量我们打算 带来的积极影响
-
提供 CLI、聊天机器人或 网页 UI
-
为新服务创建一个模板 Git 仓库,包含 新的配置
-
创建审计日志以跟踪谁在 请求数据
-
暴露指标以跟踪 API 的使用情况和性能
-
实现适当的身份验证以调用 REST API
-
实现速率限制,以避免在调用后台 Git、K8s API 或日志 平台 API 时出现问题
这个列表可能 还可以继续。
并不 需要在一开始就实现所有这些功能,以验证原型并证明 这些能力的价值。
将基本的产品管理技能应用到您的平台项目中
到目前为止,我们学到了如何理解用户的真实痛点,如何量化为这些痛点构建解决方案的益处,如何提出解决方案,以及如何通过构建 原型来保持用户的反馈循环!
现在我们明白,在构建新平台时,我们需要一种产品思维方式,是时候扩展我们的需求收集了。 通过了解平台如何融入现有的端到端流程和工具,超越开发者的需求,将为我们未来的采用和能力增长打下基础!
考虑现有的流程并整合新的实现方案
我们刚才讨论了如何识别用户的真实痛点,以及如何选择合适的候选项为首批原型进行早期反馈。 然而,需求不能仅仅来自最终用户。 我们必须超越仅通过聊天机器人、模板库或新的 CLI 提供自助服务的做法。
我们需要审视并分析整个价值创造过程,看看新平台的功能在其中如何定位。 我们需要确保我们理解当前的 软件开发生命周期 (SDLC)——这是组织在开发、发布、运行和退役新软件时遵循的过程。 有几个问题我们需要能够 回答:
-
引入变更到现有的 SDLC 的标准和流程是什么? ?
-
当前 SDLC 效率是如何衡量的,我们如何衡量我们预期的积极影响? 。
-
是否有我们需要遵守的关于新工具的监管要求?
-
组织内需要被通知或批准新工具的人员是谁?
-
新工具是否与现有系统进行集成,满足认证、访问控制、审计、可观察性、安全性 和弹性等要求?
第一步是了解现有流程,如何证明我们想要实现的积极影响,扩展它的需求,以及谁是关键的 决策者。
了解现有的 SDLC——“工件的生命周期”
作为 平台工程 旨在提供能够改善和改变开发人员在 SDLC 过程中执行特定任务的能力,了解组织中当前的 SDLC 是非常重要的。 特别是在大型企业中,很可能并非只有一个过程,而是许多在多年来不断演化的过程。 同样,很可能没有多少人——如果有的话——能从第一个需求和工件创建开始,一直到新软件在生产环境中投入运行,再到其被替换 或退役。 我们非常重要的一点是,不能犯假设我们——或者某一个人——了解现有端到端过程的错误。 即使是那些在公司工作多年的工程师,通常也会生活在自己的信息泡沫中,只对从一个新想法的诞生到代码发布、运行,最终被 退役的全过程有一个有限的了解。
工件生命周期实验——从想法,到 git 提交,到生产环境!
学习端到端过程的一种简单方法是做一个 小实验。
作为孩子,我曾 像你一样对河流(无论大小)感到着迷。 我相信你一定也曾将一块木头或一根树枝投入水中,观察它被水流带走的过程。 你可能会沿着河流跑,观看那根树枝一路流向它的最终生命周期阶段:大海! 因为所有的水流最终都会汇入大海。 作为孩子,我们并没有机会将那根树枝一路追踪到大海。 然而,作为工程师,我们有机会追踪一个工件的完整生命周期:从想法的诞生(需求工程工具中的一个工单)到开发者的第一次 git 提交,直到该工件部署到生产环境中,或者被更新或替换 在生产环境中。
我们有两种方式来理解这个工件生命周期。 首先,我们可以选择一个现有的服务或功能,通过分析所有工单、git 提交、管道运行、测试报告、电子邮件、变更请求和事件报告,进行一些取证,从中了解相关的过程、工具和 人员!
另一种方法是让其中一个开发团队创建一个“演示”或“无影响”的功能。 随着特性标志的流行,这可以是一个简单的功能,通过标志改变工件的某些运行时方面。 这样做的好处是,它不会对生产环境造成风险,但它允许我们了解关于当前 SDLC 的所有信息,并由此推导出该组织当前的工件生命周期!
关于工件生命周期的洞察
我曾 举办过几次“让我们理解工件生命周期”工作坊,和世界各地的不同组织合作。 结果是收获了很多关于人、流程和工具的洞察和学习。 这包括 以下内容:
-
什么 任务 从需求到第一次 git 提交,再到 生产部署
-
谁 参与了——不同的团队,负责开发、测试、验证和推动从开发到生产的变更 到生产
-
过程中使用的 工具 以及是否在 不同环境中为相同任务使用了不同的工具
-
过程中哪些任务是 手动 的,哪些是 已经 自动化 的
-
依赖 于其他任务或团队,以保持变更推进 到生产
-
每个任务的 时间 、整体时间、任务之间的等待时间 等等
这些洞察 使你能够创建完整的软件开发生命周期(SDLC)或完整的工件生命周期的可视化。 一旦我们进入讨论我们的平台功能如何影响当前 现有的流程时,这样的可视化将非常有用:
图 3.3:理解软件工件的生命周期rtifact
工件生命周期不仅限于初始交付
虽然 平台工程的工作通常旨在改善软件组件的初始入职或交付方面,但我们不能局限于此。 这就是为什么术语 制品生命周期 是一个很好的替代方案,因为生命周期并不会在初始开发过程结束时停止。 制品的生命周期还包括运营、发布更新、维护,甚至是替换或淘汰 该制品。
在我们从 Financial One ACME 的第一个例子中,我们讨论了一个运营生命周期阶段。 这涵盖了开发团队获取生产日志文件以排查当前问题的繁琐过程。 下图展示了这个生命周期阶段 和过程!
图 3.4:访问日志时事件响应的生命周期
相关团队的需求和现有流程
在我们的 练习中,为了理解制品在生命周期中的流转过程,我们学到了很多关于涉及的团队、现有工具和流程的知识。 我们还了解了未来平台能力可能需要集成的工具,哪些团队需要合作,另外——如果我们最终要替换或与现有工具集成——需要做哪些工作,以确保不会破坏当前工具 实现提供的功能。
以下是一些 发现的例子:
-
单点登录 (SSO):每个工具都需要与 中央 SSO
-
安全性:每个工具都需要通过软件供应链的 安全指南
-
审计:每个工具都需要创建特定的 访问日志
-
服务水平协议 (SLAs):每个关键工具都需要遵守公司范围内定义的可用性 SLAs,以确保 关键服务
到目前为止,我们已经 了解到理解软件制品的完整过程和生命周期是多么重要。 了解哪些团队参与其中,介绍新工具时需要经过哪些现有流程,这一点也非常重要。 我们还了解到,平台工程能力可以在未来的某些领域为软件交付和制品 生命周期提供显著改进!
引入生命周期事件 – 测量和提升 SDLC 的效率
我们已经讨论过工件生命周期的概念。 一个工件通常会经历一系列阶段:需求接受、实施开始、拉取请求、工件构建、安全扫描完成、测试完成、构建验证、工件提升、部署完成、功能发布、问题检测、配置更改、问题 解决、工件退役。 虽然每个组织的生命周期阶段或环节可能有所不同,但我们可以通过 DevOps 无限循环很好地可视化这一过程。 为了追踪工件在生命周期中的进展,我们建议你在每个步骤中记录为生命周期事件。 下图展示了这些事件的示例,以及相关工具和团队应添加的一些元数据,以更好地理解工件从最初的需求 到操作的完整流动:
图 3.5:SDLC 及其工件生命周期事件
生命周期事件的 概念并不新鲜。 许多组织正在通过不同的方式实施这一概念, 例如,在其 CI/CD 流水线的开始和结束时,添加日志输出,包括元数据,如时间戳、Git 仓库、流水线、创建的工件和成功状态。 持续交付基金会一直在进行 关于 CDEvents [3] 规范的工作,该规范定义了一套事件词汇,允许工具以互操作的方式进行通信。 CDEvents 是一个很好的起点,可以扩展以覆盖工件的完整生命周期,正如 之前所提议的那样。
标准化此类事件有许多好处 :
-
每个工件都可以通过其生命周期追溯:这意味着它可以回答诸如谁参与了 工件的创建!
-
发布管理:哪个版本的发布工件已经部署, 何时 由 谁 在 哪个 环境中?
-
生命周期阶段可以被衡量,这为我们提供了 DORA 指标:从初始需求到首次部署需要多长时间(交付时间),我们有多少次部署(部署频率),有多少次部署导致了生产中的问题(部署失败率),以及修复生产问题需要多长时间(恢复服务时间)?
-
合规性:它使我们能够识别某些工件是否跳过了重要步骤,如安全扫描、韧性测试等。
-
互操作性:这指定了我们是使用 Jenkins 还是 GitHub Actions 来构建工件。 如果所有工具都生成相同类型的事件,我们就能控制整个 生命周期。
在 你 定义生命周期事件之前,先查看开源社区中的现有倡议,以及交付、可观测性和 应用生命周期管理 (ALM)领域中的供应商在做什么。 没有必要重新发明轮子,因为目前标准正在制定中!
呈现改善现有 SDLC/DORA 的价值主张
理解 现有的 SDLC 并使工件的生命周期可见,将为我们正在进行的平台工程项目提供宝贵的洞察。 这对于所有工程团队成员来说也是一种启示,因为他们中的许多人很可能从未得到过这种概览,无法看到当前流程中有哪些地方可以 改进。
获得的洞察力使我们能够在价值主张上进行工作,这将改善不仅是开发团队的日常生活,理想情况下还包括 SDLC 过程中许多其他团队的工作。 这就是为什么理解和衡量当前流程如此重要——它不仅能让我们提出新的解决方案,还能呈现有力的数据支持的改进,例如提高交付时间、部署频率、部署失败率或恢复服务的时间(我们钟爱的 DORA 指标)。
基于迄今为止学到的所有内容,这在我们为 Financial One ACME 的日志访问用例中会是什么样子? 我们的初始用例仅关注在生产环境中出现错误时如何改善日志访问。 在了解了完整的端到端流程后,所有相关团队以及现有的流程、工具和组织需求,我们可以提出以下解决方案和 价值主张:
建议:将可观察性自动化作为非功能性 软件需求 |
---|
价值主张:提高 DORA 指标,减少认知负担,并扩展 最佳实践 |
关键绩效指标(KPI)和 受影响团队:通过自动将生产环境中的相关可观察性数据传递给开发团队,使服务恢复时间提高 50% 。这消除了 DevOps 和 IT 运维团队手动捕获和转发日志的需求。通过自动化捕获和分析早期生命周期阶段(开发、测试)的可观察性数据(日志、指标、追踪和事件),使交付时间缩短 50% 。分析将改善并自动验证新构建的工件,从而减少 质量工程师的手动工作量。通过自动捕获早期环境中的问题,并基于可观察性数据自动检测常见问题(新的关键日志、异常、应用程序缓慢等),使生产部署失败率降低 50% 。 |
高级用户旅程 和协作:作为一名 开发团队,我提交一个 可观察性即代码(Observability as Code) 配置文件,其中包含有关日志源(用于自动捕获)、所有权(用于自动路由)和重要日志模式(用于自动验证和 自动报警)的信息。作为一名 质量工程团队,我协作并扩展日志模式,以自动检测回归作为测试 结果分析的一部分。作为一名 DevOps 团队,我协作并基于其他 开发团队的经验和模式扩展日志模式检测规则。作为一名 IT 运维团队,我协作并验证正确的日志源,并基于开发 和 DevOps 定义的相关日志模式扩展生产问题检测。 |
表 3.5:以可展示的布局提出我们的解决方案建议
提出清晰的建议和价值主张
如果我们能够清晰地阐明 我们的平台的优势,成功的机会将更高。
现在我们已经 学会如何设计我们的提案,以包括所有参与软件交付和工件生命周期的人员,现在是时候考虑如何设计 拟议的解决方案。
设计基础架构体系
到 目前为止,我们已经推销了我们的提案。 一旦所有参与的团队和执行赞助商都同意,就是时候进入下一个阶段了:我们需要考虑如何设计解决方案以及如何与基础架构配合,以实现组织的所有非功能需求:弹性、可用性、审计能力、安全性、强制集成等等。
虽然我们在开始阶段不应过度设计,但了解对基础架构决策产生影响的所有要求至关重要。 以下是我们需要能够回答的几个问题:
-
我们如何、由谁以及在哪里部署、更新和运营 平台?
-
是否有组织要求在 特定基础设施上运行?
-
我们的解决方案是否需要在多个地理区域甚至多个 基础设施提供商之间运行?
-
是否有基础设施要求来访问和连接 其他系统?
-
我们的解决方案必须能够支持多少用户?
-
我们的基础架构以及 终端用户的 SLA 是什么?
-
我们如何进行 扩展和收缩?
在 第四章中,我们将 花更多时间深入探讨使用 Kubernetes 作为统一编排层来架构平台核心。 无论您最终决定使用 Kubernetes 还是其他工具作为底层抽象层,您都需要回答这些问题,因为它们将影响您的一些决策。 因此,让我们深入了解如何回答这些问题会影响我们的 架构决策!
避免象牙塔式的方法 – 我们拥有这个平台!
在回答影响基础架构的问题之前,让我们先回答基本的 所有权问题:
我们拥有这个平台!
平台工程团队负责整个平台的端到端产品!
由于我们的平台将提供使交付和操作软件组件更轻松的能力,我们的首要目标必须是遵循我们自己的最佳实践,并且最好能以相同的黄金路径和自助服务功能来部署平台,正如我们希望终端用户使用的那样。 一些组织称之为 做零号客户 或者 饮用自己的香槟。如果我们不遵循这一原则,只是建造一些东西,然后扔给别人希望他们操作,那我们就错失了现代产品思维在 平台工程中的真正意义。
我们需要感受到用户当前在软件交付和操作中所经历的痛苦。 这将成为我们构建平台能力的更大动力,使我们作为软件 团队的工作更轻松。
我们需要避免 象牙塔式的做法 ,这种做法是我们从上而下强迫最佳实践,而自己却没有遵循这些最佳实践。 如果我们这样做,可能会像本章开头提到的那个著名的 Reddit 帖子一样:“我们花了几个月建这个平台。 开发者讨厌它! 帮我 理解为什么!”
现在,让我们回过头来回答之前提到的一些基础设施问题,这些问题可能会影响我们的 架构决策。
组织约束——现有的基础设施要求?
我们需要 弄清楚是否有使用现有基础设施服务的限制。 对于企业来说,可能已经存在与基础设施或云服务提供商的合同。 如果存在这样的限制,它将影响我们的决策——例如,我们是否需要在本地运行和操作自己的 K8s 集群,还是可以利用供应商的托管服务? 如果我们被限制在某个云供应商,这也意味着我们可能只能使用他们的服务(存储、数据库、缓存, 等等)。
关于这一领域,我们还需要关注访问控制、流入和流出以及 成本约束!
在最终确定基础设施 和架构决策之前,了解所有此类组织约束是非常重要的!
连接性约束——互操作性要求?
我们的平台 功能将要求我们连接并与其他现有系统进行交互。 这包括访问 SSO、Git 仓库、CI/CD 管道、可观察性、编排层、云 API 等。
根据平台运行的位置——即本地部署与云部署——这将影响平台如何与所有这些工具连接,或者这些工具如何连接回 平台。
我们需要考虑防火墙、拉取与推送连接、API 限速和成本,因为我们需要确保所有系统之间具有弹性的互操作性。 这些系统。
在最终决定我们的平台基础设施及其与其他系统连接的位置之前,了解所有这些连接约束非常重要。 我们需要与之连接的系统。
弹性约束——SLA 和其他非功能性需求?
我们平台的 目标是改善各种内部用户(开发人员、DevOps、IT 运维、质量工程师、应用支持等)的日常工作。 这意味着我们的平台每次用户需要时都必须保持可用并正常工作。 在全球企业中,这可能意味着需要具备 24/7 的运营弹性和高可用性。 如果平台不可用,我们的工程师将无法进行关键工作,比如发布软件的新版本、修补安全漏洞或扩展工作负载以应对增加的最终 用户需求!
因此,我们需要了解平台上的非功能性需求,例如 以下内容:
-
可用性——例如,早上 9 点 到下午 5 点 在单一时区,或跨所有时区的周一至周五 时间段
-
用户体验——例如,必须保证系统的可接受的最终用户性能,支持最多 100 名并发工程师使用 平台功能
这些非功能性需求还意味着我们需要考虑如何进行横向和纵向扩展。 我们需要定义是否将平台集中提供,以便服务全球各个地区,还是需要将平台组件部署到不同地区,以更好地满足 可接受的性能的用户体验要求。
动态、水平或垂直扩展是我们将在本书后面详细探讨的话题,当我们深入研究如何使用 Kubernetes 作为统一层架构平台的核心时。
现在我们已经找到了所有这些问题的答案,我们将能够做出关于平台基础设施和架构选择的更明智决策,以及 它的能力!
探索多云、多 SaaS 和能力碎片化
当我们研究能力碎片化以及它如何与平台即服务和其可扩展性互操作时,有一件事是我们想到的一个关键功能,虽然一开始可能不太显眼:IDP 应该是多租户的,而不仅仅是多 用户。
作为我们平台能力之一的多租户和所有权
多租户通常是在生产应用程序的上下文中考虑的。 例如,我们的平台客户 Financial One ACME 正在寻求将其单租户产品转变为多租户产品。 这将帮助他们实现更高的利润率,减少运营开销,并带来一系列其他商业上的成功。 在高度监管的行业中,生产应用程序中存在的数据分离应该在应用程序生命周期的每个级别中都存在,包括 IDP。 这不仅是最佳实践,而且可能是获得某些安全性和 合规认证所必需的。
由于生产数据可能进入测试,确保平台上每个用户的默认访问级别尽可能限制,有助于确保如果数据最终被传送到不希望出现的地方,数据的暴露表面将尽可能小。 此外,多租户的完全隔离确保了每个团队可能需要的秘密彼此之间是隔离的,进一步降低了任何 风险表面。
我们将在 第七章中进一步探讨平台的安全性和多租户的优势。 然而,需要注意的是,多租户也有助于减少平台用户的认知负担。 我们将在 第六章中更详细地讨论这一点, 为开发者和 他们的自助服务构建。
多租户意味着我们做出的任何架构决策——我们在平台中包含的任何工具——也必须支持多租户。 以可观察性为例——如果我们捕获日志、度量、追踪和事件,既包括核心平台的,也包括用户部署的应用程序,我们需要确保这些可观察性数据能够“分离”到各个租户。 我们已经讨论过,在部署核心平台时,我们的平台必须包含所有权作为元数据,以及在为新应用提供自助服务模板时。 让我们考虑一个例子,部署定义中丰富了所有权元数据,如在 克服平台复杂性 一节中所讨论的。 这些所有权元数据 可以用来强制访问控制,限制对该部署所捕获的任何数据的访问,包括在查询度量或追踪时使用它作为过滤器: 或追踪:
service-abc.yaml (****Kubernetes 部署) | 使用所有权元数据 与可观察性结合使用! |
---|
| apiVersion: apps/v1``kind: Deployment``metadata:``**name: fund-transfer-service**
****namespace: prod-useast-01**
**spec:
…
**template:**
****metadata:**
****annotations:**
****owner.team: dev-team-backend**
****app.kubernetes.io/name: fund-transfer-service**
****app.kubernetes.io/part-of: backend-services**
****app.kubernetes.io/version: 2.34**
**…
**spec:**
****containers:**
**-
name: fund-transfer
**image: "financeoneacme/fund-transfer:2.34"**
…
********************** | 注释可以作为 PromQL 查询中的过滤器使用, 例如:sum(rate(container_cpu_usage_seconds_total{annotation_name="owner.team", annotation_value="dev-team-backend"}[5m]))
|
表 3.6:如何定义和使用所有权元数据
这些 所有权信息可用于向那些拥有该组件的团队授予可观察性数据的访问权限。 它允许我们将数据按租户进行划分,同时也允许我们平台的管理员或共享服务的所有者分析跨越单一租户的数据。 这也是一个重要的功能,因为我们很可能会遇到跨租户的问题。 因此,拥有适当上下文的全部可观察性数据将对于识别问题和 解决问题至关重要。
数据管道——无论是使用 OpenTelemetry Collectors 还是商业产品——也可以利用这些元数据将可观察性数据发送到不同的后端存储,甚至可以基于租户对这些数据的存储进行分离!
关于所有权和可观察性的话题将在后续章节中详细讨论,因为它是我们平台许多功能和属性的关键支持。
关于在多 X 环境中运行的考虑
在 组织约束 一节中,我们讨论了理解现有的任何约束, 例如我们是否有 业务或监管义务来运行多云、多 SaaS,或——正如一些人所称—— 混合云。
当金融公司 ACME 希望进入不同的全球市场时,可能需要将我们的软件从云服务商的最本地化区域运行——例如,EU-WEST 或 APAC-SOUTHEAST。 甚至可能需要跨多个区域或多个不同的供应商运行,以履行高可用性和弹性要求,同时遵守这些公司所需遵循的标准。 遵守这些标准。
出于竞争或监管原因,也有可能一些云服务商或 SaaS 供应商根本无法使用。 为什么会这样呢? 如果金融公司 ACME 的潜在客户将某个云服务商视为竞争对手,或者某个供应商不符合某些监管要求,他们将无法购买我们的 软件服务。
这听起来 像是有很多限制,几乎没有选择,不是吗? 幸运的是,有解决方案可以解决这个问题:在接下来的章节中,我们将深入讨论 Kubernetes 作为底层抽象层。 在这个抽象层之上构建将使我们更容易——但并非没有麻烦——将工作负载迁移到不同的区域和不同的 云服务商。
由于我们的平台及其所有组件不仅仅会运行在 Kubernetes 上,还将包括其他服务,因此创建一个全面的检查表是很重要的,以避免做出未来难以更改的架构决策。 例如,如果我们的平台需要一个文档存储服务,我们可以选择在 Kubernetes 上运行和操作 MongoDB 等文档数据库,或者从某个云服务供应商那里选择一个现有的 SaaS 服务。 选择托管服务将减少我们的工作量,但我们需要确保每个支持文档存储服务的潜在 SaaS 供应商提供相同的接口、类似的性能特征和类似的成本结构。 如果我们做出错误的架构决策,可能很难做出以下更改:
-
改变我们的实现方式,以便支持一个 不同的 API
-
对不同供应商的性能和可扩展性产生影响
-
增加我们软件的整体运营成本
集中式和分布式平台能力
在 接下来的章节中,我们将提供更多的例子,展示如何将我们的 IDP 及其组件作为一个中央服务、按环境、按区域,甚至按租户(如果有此需求)来运行。 关于组织需求的发现将影响这些决策,例如我们的客户是否要求他们在特定区域运行,或是否不允许使用某个 云服务提供商。
将组件集中化有一个很大的优势,那就是它们更容易管理。 想一想一个 集中式 持续集成 (CI) 系统,比如 Jenkins 或 GitLab,它通过拉取请求触发管道来构建新的工件。 这种 CI 系统的中央实例更容易操作、观察、安全性管理和维护。 另一方面,这意味着所有用户都共享同一个系统,这可能很容易导致瓶颈。 中央系统还需要 访问所有目标环境,这些环境可能位于不同的地理 区域,甚至是其他云服务提供商。 随着每个新团队接入他们的应用程序,或者每个新客户对 Financial One ACME 的需求都可能增加对这些云环境的依赖,尤其是当要求我们软件 特别部署时!
去中心化组件的优点在于能够自动封装数据捕获、存储和访问过程。 这些组件仅被特定区域或环境中的团队使用,这限制了诸如噪声邻居等模式的潜力,我们将在 第六章中详细讨论这一点。去中心化方法的缺点是,随着每个新环境的加入,组件数量的增加使得操作、监控、安全性和管理这些组件变得更加困难。 自动化可以在此提供帮助,但我们不能低估额外的复杂性。 想一想,如果我们的 CI 系统发现了漏洞。 我们需要在所有的去中心化服务中逐个修补,而不是在中央系统中修补一次!
现在我们已经了解了多重 X(包括多租户、云和 SaaS)将对我们的架构决策产生影响,因此,找到努力、收益和外部需求之间的良好平衡非常重要。 到目前为止,我们所获得的所有知识使我们能够提出一个好的平台参考架构,这是我们将在本章最后部分讨论的内容!
探索我们的平台参考架构
架构图是展示一个系统向最终用户提供什么功能及其内部工作原理的好方法。 在这一章中,我们讨论了构建未来平台基础的一些重要步骤以及 其能力:
-
我们平台的目的(解决最终用户的痛点):我们的最终用户是谁,我们需要解决什么问题?
-
用户界面/开发者体验:我们为最终用户提供的理想开发者体验,如何最好地融入他们的日常工作活动中?
-
核心平台组件:选择合适的组件,以适应现有的流程、工具、基础设施 和约束。
-
我们的平台作为产品:像任何软件产品一样,我们的平台必须具备可用性、韧性(即使在高负载下也能正常工作)和默认的安全性。
-
成功的关键绩效指标(KPI):利用可观察性来衡量并推动采纳、效率 和生产力。
一种不同的展示方式是 通过我们平台的高级架构图来呈现。 虽然以下图示并不完整,但它可以作为你将要构建的平台的一个很好的参考:
图 3.6:我们平台基础架构参考(该图像仅作为视觉参考;文本信息并非必需。)
我们的平台作为一个产品,必须有明确的目标!
在进入 技术细节之前,先可视化并阐明目标!
让我们一步步走过这张图,就像你向同事或终端用户展示时那样。 我们将从顶部开始,然后再深入探讨可观测性、可用性、弹性 以及 安全性。
目标——为终端用户提供自助服务
在 第二章中,我们举了一个平台原则的例子,我们称之为 自助服务优先。我们提供了详细的描述,声明:“我们将为客户提供每项平台功能作为自助服务,关注他们的用户体验,并支持自主决定的 软件开发。”
这正是前面图示顶部所反映的内容。 在这里,我们列出了我们了解到的所有潜在终端用户。 对于 Financial One ACME,我们的开发团队通过简化新应用程序的入驻过程或更便捷地访问生产中的遗留系统日志文件,支持他们的用例。 我们还通过提供自助服务功能,减少手动工作和认知负担,支持 DevOps、安全、 站点可靠性工程 (SRE) 和 应用支持团队。
像这样的高级架构图,空间有限。 我们可以通过文字添加不同终端用户自助服务功能的示例,或者——如果需要——为每个终端用户及其 自助服务功能提供这张图的更详细版本!
用户界面/开发体验
每个终端用户 群体都是不同的。 即使在终端用户群体内部,我们也可能会有不同的技能集,这在设计和实施平台的自助服务功能用户界面时需要考虑。 自助服务功能。
开发团队很可能更倾向于待在他们首选的集成开发环境(IDE)中,比如 Visual Studio Code 或 IntelliJ。 他们大概能接受编辑 YAML 文件来启用新的自助服务功能。 有些人可能会期待一个 IDE 插件,能够引导创建这些 YAML 文件,或者一个能提供代码补全和架构验证的插件,来减少像 拼写错误这类错误的发生。
另一方面,同一组织内的其他团队可能希望有一个漂亮的开发者门户 UI —— 比如 Backstage,它提供了很好的模板功能,便于创建 新组件。
然后是自动化工程师,他们既不习惯使用 IDE,也不愿意通过向导点击来创建新项目。 他们需要一个可以用来自动化任务的 API 或 CLI,因为这才是他们习惯的工作方式。 他们可能期待一些 Python 库来调用我们的 自助服务功能。
正如本章前面讨论的那样, 每个用户的技能不同,因此他们对良好的用户体验有不同的期望。 专注于提供良好的用户体验至关重要,因为这将是决定平台是否会被采纳的关键因素。 这就是为什么我们需要重点突出用户界面及其在我们参考架构中的样式,因为这将是用户与 平台互动的方式!
核心平台组件
虽然我们每个人 在选择工具和技术时都有自己的偏好,但选择核心平台组件的正确方法不应基于个人喜好。 它必须基于我们想要通过用户界面交付的自助服务用例(即我们平台的功能),这个界面要能提供良好的用户和 开发者体验。
在前面的图示中,我们将平台组件细分为以下几个领域: 以下几个领域:
-
交付服务:这些 是专注于软件交付的工具和服务,因为这是我们平台的主要用例。 CNCF 开源工具,如 Backstage、ArgoCD、Flux 和 OpenFeature,开源工具如 GitLab、Jenkins 和 K6,甚至商业工具如 Atlassian 的工具,都属于 这一类别。
-
平台服务:作为 一个关键的使用场景,将围绕交付和操作软件组件展开,我们的平台还应提供这些软件组件所需的核心平台服务。 这些服务可能包括缓存或数据库(Redis、MongoDB 或 Postgres)、消息传递和事件(Apache Kafka 或 RabbitMQ)、服务网格(Istio、Linkerd 或 Nginx),以及诸如机密管理、容器注册表、策略代理、自动扩展等核心服务 等等。
-
平台:虽然平台不一定要运行在 Kubernetes 上,但在本书的其余部分,我们将重点关注 K8s 作为基础平台编排层。 然而,Kubernetes 并不是解决所有平台工程问题的万能答案。 目标是选择合适的平台组件,以提供最终用户所需的自助服务。 这可能会导致你将平台运行在虚拟机上,甚至选择一个完全托管的现成 SaaS 解决方案!
-
X-as-Code:除了 优先自助服务外,我们还必须接受 一切皆代码 的理念。 无论 是定义部署、可观察性还是所有权,如 本章前面讨论过的,或者软件生命周期的任何其他方面,一切都应以某种形式的代码表达。 在这里,你应该选择像 Crossplane、Terraform 或 Ansible 这样的工具。 尤其是 Crossplane,它在云原生领域中逐渐成为在声明式方式下编排应用程序和基础设施的首选工具。 此外,确保根据工具在交付和平台服务中的 X-as-Code友好程度来验证你的工具选择,例如它们如何 定义 服务水平目标 (SLOs) 或使用所选 可观察性工具设置监控警报!
-
可观察性:你不会在没有任何遥测和关键指标(如速度、高度或燃油表)的情况下开车或飞行。 我们也不能在没有内置可观察性的情况下操作任何平台。 感谢像 OpenTelemetry 这样的标准以及 Prometheus 和 Fluent 等项目,我们在平台中使用的组件中有许多内置的可观察性信号。 无论是 Kubernetes 本身,还是像 Nginx、Reddis、Harbour、ArgoCD 或 Backstage 这样的工具,所有这些工具都会发出度量、日志、事件和/或追踪,我们可以收集这些数据并将其发送到我们的可观察性后端进行自动分析或警报! 虽然许多工具已经内置了这些功能,但并非每个我们选择的工具都会提供我们所需的所有数据。 因此,基于工具的可观察性状态验证你的工具选择非常重要。 对于某些工具,你可能需要从日志中提取度量。 对于其他工具,你可能需要一个扩展——例如,Jenkins 提供了一个 OpenTelemetry 插件,用于在每次 Jenkins 作业执行时发出追踪。 在其他情况下,你可能需要依赖基于代理的商业可观察性解决方案。
可观察性将在本书后续部分深入讨论,因为它是许多领域的关键基础,例如 SRE、自动扩展、事件响应 和故障排除。
一个可用、具备弹性且安全的平台
平台就是一种产品! 因此,一个我们希望内部用户使用的产品,必须在他们需要时可用,在大量用户同时使用时具备弹性,并且要确保安全,以便我们的用户信任 该产品。
为了确保我们的平台在用户需要时始终可用,我们需要应用与任何成功软件产品相同的架构原则。 对于我们的平台,这意味着所有关键组件也需要默认是可用的、具备弹性且安全的。 让我们来看看以下一些关于 这三个 支柱的示例和最佳实践。
可用性
我们的用户 认为我们的平台可用,当他们可以用它进行我们承诺的自助服务用例时。 还记得我们提议的 从生产环境自助请求日志 用例吗?来自 图 3**.2?如果开发团队遵循这个自助服务用例,他们期望我们的平台在任何时候,无论是周二中午还是周五晚上 11 点,都能按时交付请求的日志。
该用例涉及许多不同的工具,例如 Git(存储配置为代码文件的位置)和我们自家的解决方案,该解决方案通过 REST API 提供特定日志的请求。 这两个系统都需要可用。 这两个系统还需要在规定的时间内响应请求,并提供有效的响应。
确保可用性的最佳方法是遵循 两个步骤:
-
端到端用例监控:就像我们对业务关键应用程序所做的那样,我们可以设置合成测试来模拟该用例的端到端用户旅程。 在这种情况下,我们将通过执行虚拟提交和 PR 并验证响应时间和响应代码来验证调用 Git API 的正常工作。 我们还会对我们的 REST API 调用请求日志做同样的操作。 然而,这种情况还需要等待请求的日志被发送回开发团队的通信渠道,以便我们可以测量完整的 端到端时间。
这两种情况都可以通过自定义脚本完成,我们将其作为定期作业运行,或者我们可以利用 现有的合成测试解决方案。 您应该验证您的可观察性平台是否已经提供了合成测试,因为许多供应商在其产品组合中都有此功能。 这也将使我们第二步更加容易,即监控和警报关键 质量指标!
-
监控并警报关键可用性指标:作为平台团队,我们希望在最终用户开始抱怨之前,能够在平台自助服务用例不再按预期工作时得到通知。 这就是监控和警报关键指标的作用所在。 我们的合成测试已经提供了一个良好的指标,但我们应该设置自动警报,以防 Git API 或我们的 REST API 开始变慢或开始响应错误。 此外,我们还可以对其他领先指标进行警报。 我们平台中的许多工具——包括我们的 Git 示例——将生成日志以及健康指示器指标。 如果我们看到 任何错误 日志,我们希望能够收到通知。 这些日志可能是早期警告指标。 大多数系统还具有内部队列,例如请求队列。 如果这些队列不断增长,我们希望能够收到通知,因为这意味着过多的传入请求正在积压,最终会导致性能和 可用性问题。
我们还需要做很多其他工作,以确保系统保持可用。 我们将在下一节讨论这些内容,届时我们将 讨论 系统的弹性。
弹性
我们刚才 讨论了我们产品的可用性。 对于我们的最终用户而言,平台必须始终可用,无论他们是系统中唯一的用户,还是有 1,000 个其他用户在尝试执行自助服务任务。 他们还期望平台始终可用,无论我们的内部基础设施或云服务是否出现任何问题。 这就是我们讨论系统弹性的时刻。 它意味着尽管发生了任何意外事件(高负载、组件故障、连接问题等),系统本身 依然保持可用。
从 架构角度来看,我们可以做很多事情来提高系统的弹性,从而实现高可用性。 让我们再次考虑 REST API 的示例。 从部署的角度来看,我们可以做以下几点:
-
负载均衡复制部署:我们可以使用 ReplicaSets 部署我们的实现,这样多个实际的 Pod 实例就需要处理传入的请求。 请求可以通过轮询或其他更 复杂的算法进行负载均衡。
-
实现地理多样性:为了服务全球用户,我们可以将我们的实施方案部署到用户所在的地区。这减少了网络延迟的影响,并根据用户的分布来分配负载。
-
提供自动扩展:水平 Pod 自动扩展器(HPA)和Kubernetes 事件驱动自动扩展(KEDA)允许我们基于指标(如 CPU、内存、传入请求量,甚至用户体验)来扩展我们的实施方案。这使得我们能够避免大多数资源限制导致的弹性问题。
-
执行自动备份和恢复:并非所有问题都能预防。如果发生灾难,拥有自动备份和恢复选项非常重要,这样我们可以尽快恢复正常运行。
从软件架构的角度来看,我们的 REST API 有以下几种选择:
-
API 请求速率限制:我们可以限制整体或特定用户/团队的请求数量。这可以避免因某些人不小心(或故意)向我们的系统发送过多请求而导致的问题。
-
排队传入请求:事件驱动架构允许我们将请求排队,并在有工作者可用时处理它们。工作者可以根据队列长度进行扩展。虽然像请求队列这样的额外层可能会影响请求延迟,但这种架构模式将提高系统的稳定性、弹性和可用性。
-
重试和退避机制至下游系统:由于我们还需要调用 Git 或日志监控等后端系统,我们希望确保不会受到它们端问题的影响。为此,我们可以实现 API 重试(当 API 调用超时或失败时)并结合退避机制(增加 API 调用之间的时间间隔)。这些策略将提高我们的弹性,并且有助于我们的下游系统。
安全性
如果我们的平台 随时可用,那是一个很好的开始。 但是像任何软件产品一样,它也必须是安全的。 对于平台来说,这一点尤为重要,因为它使我们的最终用户能够创建、部署和操作新的软件应用程序,而这些应用程序也必须是安全的。 如果我们的平台运行在具有已知漏洞或容易被黑客攻击的堆栈和工具上,我们就会面临攻击者利用这些漏洞进行软件供应链攻击,或获取机密信息的风险。 让我们再看看我们的 REST API。 如果我们允许每个人使用该 API 从生产环境请求日志,最终可能会把关键日志信息发送给 黑客。
这就是为什么对我们的平台、我们使用的每一个组件和工具,以及我们开发的每一行代码应用最严格的安全指南是如此重要。 在 第七章中,我们将更详细地讨论构建合规和安全产品的主题,因此在这里我们只是简要概述。 我们必须将我们的平台视为公司中最关键的软件。 因此,我们不能部署任何具有已知漏洞的组件。 我们必须在所有自定义开发的 API 中实施适当的访问控制。 我们必须使用所有安全扫描和警报工具来保护我们的平台,这些工具也是我们在生产环境中运行任何业务关键软件时会使用的。 生产环境中。
我们是平台工程团队,这意味着我们对平台的安全负责,就像我们对平台的 可用性 和韧性负责一样!
成功的 KPI 和优化
“我们花了几个月时间构建我们的新平台。 开发人员讨厌它! 帮我 理解为什么!”
根据 到目前为止我们所学的内容,我们应该能处于一个良好的位置,以避免我们在本章开始时想要避免的问题。 我们希望构建一些用户喜爱的产品,因为它能让他们的生活变得更轻松。 但我们不能仅凭直觉或个别证据就宣称成功。 我们需要通过 KPI 来衡量我们的影响力,并根据 KPI 报告我们的成功,同时我们还需要利用平台内可以观察到的所有数据, 不断优化这些成功的 KPI,以及平台的运营方面。
我们已经详细讨论了可观察性,作为我们平台的自助服务功能以及我们的最终用户,以及使用可观察性来理解我们平台的内部工作原理,以确保可用性和弹性。 观察我们的平台组件,如 Backstage、ArgoCD、Jenkins、OpenFeature、Git、Harbor、Redis、Istio 和 Kubernetes 本身,为我们提供了关于平台及其功能成功的许多见解。 让我们深入了解一些关键指标,如何捕获它们以及我们可以用它们来优化我们的平台采纳 和效率。
活跃用户
如果像 Backstage 这样的工具成为我们平台的开发者门户选择,我们需要测量有多少用户每天或每周登录 Backstage。 我们希望衡量通过 Backstage 模板引擎创建了多少新项目,以及这导致了多少新的或更新的 Git 仓库。
对于我们在财务一 ACME 上的日志分析用例,我们可以测量我们的 REST API 被调用的频率。 由于团队应该使用团队标识符或令牌来识别,我们可以测量有多少团队正在使用我们的功能,以及使用频率。
这些都是当前采纳的良好指标。 如果我们看到采纳速度缓慢,我们可以提供更多教育或利用更多一对一会议来使更多团队能够采纳。 我们还可以利用各个团队的现有采纳情况,并创建内部成功案例来推广平台的功能给 其他团队。
然而,重要的是开始测量平台上有多少活跃用户,并将其用作基准,以便我们可以设置额外的行动并增加采纳!
服务水平目标 (SLOs) / DORA
平台 是通过自助服务模板推广最佳实践的机会。 这就是服务水平目标 (SLOs) 的用武之地。 我们不仅可以为我们的平台组件定义 SLOs,如可用性,还可以将这作为一个机会,在我们的平台上通过任何新模板为我们的最终用户创建的新软件项目中包含 SLO 定义 - 我们希望他们的软件也具有高可用性、弹性 和安全性。
此外,我们可以通过我们的平台,衡量有多少新的软件项目和发布得以创建和发布。 我们可以查看 Jenkins 作业的执行次数,执行时间,ArgoCD 同步的频率,以及有多少部署最终进入生产环境并满足其 SLOs。 这些都是帮助我们将 DORA 指标反馈给工程团队的关键指标。 他们需要这些指标来展示他们的工作效率。 这也帮助我们作为平台团队,突出展示随着我们的平台的帮助,团队在效率上的提升,假设一些指标,如 部署频率 或 变更的交付时间 将会有所改善。 我们将在 第五章中详细讨论 DORA, 届时我们将深入探讨 CI/CD 自动化!
利用率/FinOps
让大量用户采用我们的平台是一个伟大的目标,我们已经学会了如何衡量它。 将更多的团队带入一个集中式平台,可以让我们集中执行最佳实践,围绕部署的正确大小和正确扩展进行管理,并优化底层基础设施的利用,从而实现平台和所有通过该平台部署的应用的成本优化。 通过它。
在 第一章中,我们提到了一项新的欧洲法规,旨在报告碳排放。 这也可以作为平台能力进行集中处理,报告平台、每个平台服务以及通过平台部署和运行的每个应用的实际资源利用、成本和计算的碳影响。
还有许多这样的使用案例帮助我们优化底层基础设施的利用,以控制我们的成本。 总的来说,这意味着我们的平台是一个 非常适合我们 FinOps 倡议的地方!
我们希望你能将类似的方法应用于为你的平台工程项目创建架构参考架构。 从一个高层次的概述开始,展示平台的目标,概述其功能和用户界面,提供核心平台组件的见解,并指出你如何衡量平台工程计划的成功。
总结
在本章中,我们学到了如何以产品思维接近平台工程:找出我们需要解决的真正问题,提供一个简单快速的解决方案来验证我们的实现,确定我们的平台如何融入现有流程和组织要求,提出具有价值主张的解决方案,且不仅仅关注开发团队,然后设计时注重灵活性,而不是走向 过度工程化的道路!
我们最终得到了一个高级参考架构,您可以使用它向终端用户及任何需要支持该项目的利益相关者展示平台的目的。
在 第四章中,我们将深入探讨平台的架构细节,Kubernetes 的作用,如何将其与现有服务集成,以及如何将平台的能力提供给你的应用程序和服务 开发团队!
进一步阅读
-
[1] Reddit 帖子: https://www.reddit.com/r/devops/comments/stuep4/weve_spent_months_building_this_platform_devs/
-
[2] 沉没 成本: https://developerexperience.io/articles/sunk-cost
-
[3] CDEvents: https://github.com/cdevents/spec
第二部分 – 设计与打造平台
在 第二部分中,我们将讨论平台的技术基础,并引导你在设计过程中走过相关的决策点。 为此,你将学习平台的四个主要部分 – 由 Kubernetes 代表的核心组件和基础设施、平台所需的自动化、面向自服务、开发者友好的平台相关组件,以及构建安全且 合规环境所需的步骤。
本部分包含以下章节:
-
第四章, 平台核心架构 – Kubernetes 作为统一层
-
第五章, 集成、交付与部署 – 自动化无处不在
-
第六章, 为开发者及其自服务打造平台
第四章:平台核心架构 – Kubernetes 作为统一层
作为一个平台工程团队,你需要做出一个关键决定,关于你核心平台的底层技术栈。 这个决定将对你的组织产生长期影响,因为它将决定你需要哪些技能和资源来构建一个支持当前和未来自服务 用例的平台。
Kubernetes — 或简称 K8s — 不是解决所有问题的方案,但在构建平台时,Kubernetes 可以构建 基础。
在本章中,你将深入了解是什么使得 Kubernetes 成为许多平台工程团队的首选。 我们将解释 Kubernetes 所基于的承诺理论概念,以及它的实现方式带来的好处。
你将更好地理解如何导航 云原生计算基金会 (CNCF) 生态系统 ,因为在实施你自己的 平台时,选择合适的项目对你至关重要。
一旦你熟悉了 Kubernetes 及其生态系统的优势,你将学习在定义平台核心层时需要考虑的事项,例如统一基础设施、应用和服务能力。 你将学习如何为与新平台之外的核心企业服务实现互操作性而设计,并如何设计平台的灵活性、可靠性 和健壮性。
因此,我们将在 本章中涵盖以下主要内容:
-
为什么 Kubernetes 扮演着至关重要的角色,以及它为什么(不)适合所有人
-
利用和管理 Kubernetes 基础设施能力
-
为灵活性、可靠性 和健壮性设计
为什么 Kubernetes 扮演着至关重要的角色,以及它为什么(不)适合所有人
目前,我们将专注于 Kubernetes,但也有 其他方式可以提供运行工作负载的平台。 除了 Kubernetes 的许多不同版本(如 OpenShift)外,还有一些替代方案,如 Nomad、CloudFoundry、Mesos 和 OpenNebula。 它们都有存在的理由,但只有一个几乎被无处不在地采纳:Kubernetes!
除了这些平台,你还可以使用公共云提供商的虚拟机或服务来实现无服务器、应用引擎和简单的容器服务。 在许多情况下,平台在需要时也会利用这些服务。 采用纯 Kubernetes 策略可能需要更长的时间,因为组织需要一段时间才能完全投入其中。 不过,你可以观察到两个最近的趋势:
-
从 Kubernetes 管理虚拟机
-
迁移到虚拟集群和由集群管理的虚拟机,以防止由于 虚拟化许可证 带来的成本激增。
Kubernetes 拥有一个重要的生态系统和社区,许多其他组织实现的广泛用例,以及高度积极的贡献者来解决随 Kubernetes 出现的下一个挑战。 与 Kubernetes相关。
Kubernetes – 一个起点,但不是终极目标!
“Kubernetes 是一个构建平台的工具。 这是一个开始,但不是最终目标” 这句话来自于 Kelsey Hightower,他曾在 2014 年 Kubernetes 推出时为 Google 工作。 然而,尽管 Kubernetes 在构建现代 云原生平台中发挥着至关重要的作用,但这并不意味着它适合每一个人。 还记得平台工程的产品导向方法吗? 它从理解用户的痛点开始。 一旦我们了解了痛点,就可以着手实现用例,并决定采用哪些技术方案。 来做出选择。
请回顾 第一章 中名为 你真的需要一个平台吗?的部分,我们提供了一份问卷帮助你决定平台的核心内容。 答案可能是 Kubernetes,但也不一定非得是。 让我们从查看 Financial One ACME 的示例用例开始。
Financial One ACME 会选择 Kubernetes 吗?
如果我们考虑 Financial One ACME 的用例,“在生产环境中更容易访问日志以进行问题诊断”,使用提议的解决方案并不一定需要 Kubernetes 作为 底层平台。
如果我们的组织尚未使用 Kubernetes,且我们仅需要一个集成到不同日志解决方案中的新自动化服务,那么我们可能不想将 Kubernetes 提出作为底层核心平台。 这是因为它会为尚未具备所需经验的组织带来新的复杂性。 我们可以使用所有现有的工具和团队来实现并操作该解决方案;也许我们可以将其与其他已经拥有的工具一起运行,遵循相同的部署、升级、监控等操作流程。
另一方面,如果已经存在相关知识,或者 Kubernetes 已经可用,那么使用 Kubernetes 作为核心平台来编排这个新服务将解决很多问题,例如提供 以下内容:
-
新服务容器 作为 Pods
-
针对 这些服务的自动化健康检查
-
通过 ReplicaSets 等概念提供的弹性和可扩展性 以及自动扩展
-
通过 Ingress 控制器和 网关 API 实现外部访问
-
通过 Prometheus 或 OpenTelemetry 对这些服务的基本可观测性
然而,当我们仅仅需要部署一个简单服务时,是否真的需要运行我们自己的 Kubernetes 集群呢? 答案是否定的! 有一些替代方案,例如使用你首选的 云服务商的能力来运行实现:
-
无服务器:该解决方案可以作为一组通过 API 网关暴露的无服务器函数来实现。 状态或配置可以存储在云存储服务中,并可以通过 API 轻松访问。
-
容器:如果该解决方案在容器中实现,该容器可以是轻量级的,并且其端点可以通过 API 网关轻松暴露。 无需一个完整的 Kubernetes 集群,且无需某人进行 维护。
这个仅仅是 Financial One ACME 的一个使用案例 可能不会促使我们选择 Kubernetes 作为核心平台。 然而,在做出这个关于未来平台核心的关键决策时,我们还必须超越第一个使用案例来看待问题。 平台工程将通过为内部工程团队提供许多自服务功能来解决更多的使用案例,从而提高他们的 日常工作效率。
这是一个棘手且影响深远的决策,需要在前瞻性和过度工程之间找到良好的平衡。 为了让这个决策更容易做出,让我们来看看选择 Kubernetes 作为 核心平台的好处。
选择 Kubernetes 作为核心平台的好处
为了让做出选择未来平台核心的关键决策变得更容易,让我们来看一下为什么其他组织选择 Kubernetes 作为核心构建块。 理解这些原因、好处以及挑战,应该能让架构师更容易做出这个 重要决策。
声明性期望状态——承诺理论
传统的 IT 运维 使用 义务模型,这是 当一个外部系统指示目标系统执行某些操作时的情况。 这种模型需要将大量的逻辑嵌入到外部系统中,例如自动化管道。 一个脚本化的管道,无论是基于 Jenkins、GitHub Actions 还是其他解决方案,不仅需要将更改应用于目标系统。 该管道还需要处理外部系统未预测的结果和错误。 例如,如果部署新软件版本在一定时间内未成功,我们该怎么办? 我们应该回滚吗? 管道该如何 处理这种情况?
在《Kubernetes 纪录片第一部分》(https://www.youtube.com/watch?v=BE77h7dmoQU)中,Kelsey Hightower 用一个很好的类比解释了 Kubernetes 遵循的承诺理论模型。 它大致是这样的: 它是这样的:
如果你写了一封信,把它放进信封里,并贴上目的地地址和正确的邮票,那么邮局承诺会在一定时间内将这封信送达目的地。 无论这个投递过程涉及卡车、火车、飞机或其他任何形式的运输,对写信的人来说都无关紧要。 邮政服务会尽一切努力履行投递承诺。 如果一辆卡车发生故障,另一辆卡车将继续前行,直到信件送达最终目的地。
同样的原则适用于 Kubernetes! 在我们的类比中,信件是我们放入信封中的容器镜像。 在 Kubernetes 世界中,信封是某个 自定义资源定义 (CRD)。 为了传递镜像,这可以是一个 Deployment 的定义,其中包括镜像的引用、副本数、该镜像应部署到的命名空间,以及该镜像运行所需的资源要求(CPU 和内存)。 Kubernetes 然后会尽其所能,履行部署该镜像的承诺,通过找到合适的 Kubernetes 节点,确保其满足所有要求,以运行指定副本数的容器镜像,并具备所需的 CPU 和内存。
另一个例子是 Ingress ,它将已部署的服务暴露到外部世界。 通过注解,可以控制某些对象的行为。 对于 Ingress,这可能是为应暴露的域自动创建 TLS 证书,使得匹配的服务可以通过 HTTPS 访问。 以下是一个 Ingress 对象示例,用于 fund-transfer-service
通过特定域将对象暴露到外部世界,使用证书管理器——一个 Kubernetes 核心生态系统工具——来创建有效的 TLS 证书 来自 LetsEncrypt
:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fund-transfer-service
namespace: prod-useast-01
annotations:
cert-manager.io/issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- fundtransfer-prod-useast-01.finone.acme
secretName: finone-acme-tls
rules:
- host: fundtransfer-prod-useast-01.finone.acme
http:
...
完全工作的 Ingress 对象描述比这个清单中展示的内容稍微复杂一些 [1] 示例。 然而,这个示例很好地解释了 Kubernetes 如何将定义转换为预期的实际操作——从而 实现 承诺。
现在,问题是:“这一切魔法是如何运作的?” 为了回答这个问题,我们将从探讨控制器 和操作符的概念开始。
Kubernetes 控制器和操作符
Kubernetes 控制器本质上是实现 Kubernetes 承诺理论的控制循环。 换句话说,控制器自动化了 IT 管理员通常手动执行的任务:持续观察系统的当前状态,将其与我们期望的系统状态进行比较,并执行纠正措施,以确保 系统运行!
因此,Kubernetes 控制器的一个核心任务是 持续对账。 这种持续活动使其能够强制执行期望的状态,例如确保 在先前的 Ingress 定义示例中 匹配的 期望状态 与 当前状态。 如果期望状态或当前状态发生变化,表示它们 不同步。 然后,控制器尝试通过对管理对象进行更改来同步两个状态,直到当前状态再次与期望 状态一致!
以下插图显示了控制器如何监视 期望状态 (通过清单表达并存储在 etcd 中),将其与 当前状态 (在 etcd 中持久化的状态)进行比较,并管理 管理对象 (例如 Ingress、部署、SSL 证书等):
图 4.1:通过 Kubernetes 控制器实现的对账和自愈设计
这个图表已经显示了控制器的核心 概念和能力,强调了自动对账循环如何通过设计自动实现自愈。 然而,控制器还有其他功能:观察集群和节点健康,强制资源限制,按计划运行作业,处理生命周期事件,并管理部署的推进 和回滚。
现在,让我们讨论 Kubernetes 操作员。 操作员是控制器的一个子类,通常专注于特定领域。 一个很好的例子是 OpenTelemetry 操作员,它管理 OpenTelemetry 收集器和工作负载的自动插装。 该操作员使用相同的对账循环来确保始终应用 OpenTelemetry 的期望配置。 如果配置更改或当前的 OpenTelemetry 收集器或插装存在问题,操作员将尽最大努力保持承诺,确保期望状态是实际状态。 要了解更多信息,请访问 OpenTelemetry 网站 在 https://opentelemetry.io/docs/kubernetes/operator/。
操作器的其他使用案例通常涉及管理和自动化核心服务和应用程序,如数据库、存储、服务网格、备份与恢复、CI/CD 以及消息传递系统。
如果你想了解更多关于控制器和操作器的信息,可以查看 CNCF 操作器工作组及其白皮书,网址为 github.com/cncf/tag-app-delivery/tree/main/operator-wg
。另一个精彩的概述可以在 Kong Blog 上找到,标题为 What’s the Difference: Kubernetes Controllers vs Operators。这篇博客还列出了控制器和操作器的优秀示例:konghq.com/blog/learning-center/kubernetes-controllers-vs-operators
现在我们对控制器和操作器有了更多了解,接下来让我们看看它们是如何确保所有 Kubernetes 组件和部署具备内建弹性的!
由探针驱动的内建弹性
控制器持续验证我们的系统是否处于期望的状态,通过观察 Kubernetes 集群、节点和所有已部署的 Pod 的健康状况。如果其中某个组件不健康,系统会通过某些自动化操作将其恢复到健康状态。以 Pod 为例。如果 Pod 不再健康,它们最终会被重启,以确保整个系统的弹性。重启组件通常是 IT 管理员在“我们试试关掉再开看看会发生什么”方法下执行的默认操作。
就像 IT 管理员通常不会随意开关设备,Kubernetes 采取了更为复杂的方法来确保我们的 Kubernetes 集群、节点和工作负载的弹性。
Kubelet——Kubernetes 的核心组件之一——通过使用多种类型的探针(启动探针、就绪探针和存活探针)持续监控 Pod 的生命周期和健康状态。以下图示展示了根据启动、就绪和存活探针检查结果,Pod 可以处于的不同健康状态:
图 4.2:Kubelet 使用探针判断 Pod 健康状态
一旦 Pods 不再健康,Kubernetes 会尝试重新启动 Pods,并将 Pods 恢复到健康状态。 对于探针结果的评估和重启策略,有很多不同的设置,你必须熟悉这些设置,才能充分利用 Kubernetes 的内建弹性。 所有这些设置都在你的 Deployment 和 Pod 定义中声明。
如果你想了解 更多关于 Kubelet 如何管理不同的探针并查看一些最佳实践,我们建议查看一些博文,例如 Roman Belshevits 关于存活性 探针的文章: https://dev.to/otomato_io/liveness-probes-feel-the-pulse-of-the-app-133e
另一个很好的资源是官方 Kubernetes 文档,介绍如何配置存活性、就绪性和启动 探针: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
健康探针仅在 Kubernetes 内部有效
重要的是要理解,所有这些健康检查仅在 Kubernetes 集群内部进行,并不能告诉我们通过 Ingress 对外暴露的服务,从最终用户的角度是否也是健康的。 最佳实践是额外从“外到内”的角度进行健康检查和可用性检查,作为外部控制。 例如,你可以使用合成测试来验证所有暴露的端点是否可达并返回 成功响应。
现在我们已经了解了 Pod 的内建弹性,接下来讨论一些更复杂的构造,例如通常由多个不同的 Pods 和其他对象组成的应用程序,如 Ingress 和存储。
工作负载和应用生命周期编排
正如我们所学,Kubernetes 提供了 内建的 Pods 生命周期编排,如 前一节所述。 然而,我们在 Kubernetes 上部署的业务应用通常有多个依赖的 Pods 和工作负载,这些共同构成了应用。 以我们的 Financial One ACME 为例:支持客户的金融服务应用包含多个组件,如前端、后端、缓存、数据库和 Ingress。 不幸的是,Kubernetes 没有应用的概念。 尽管有多个倡议和工作组在定义应用,但我们目前必须依赖其他方法来管理应用,而这些应用是由 多个组件组成的。
在 批处理变更以应对依赖性 一节中, 第五章,我们将学习诸如 Crossplane 之类的工具。 Crossplane 允许你定义所谓的复合应用,这使得应用所有者能够轻松定义应用的单个组件,然后部署单独的实例,如 以下示例所示:
apiVersion: composites.financialone.acme/v1alpha1
kind: FinancialBackend
metadata:
name: tenantABC-eu-west
spec:
service-versions:
fund-transfer: 2.34.3
account-info: 1.17.0
redis-cache:
version: 7.4.2
name: transfer-cache
size: medium
database:
size: large
name: accounts-db
region: "eu-west"
ingress:
url: „https://tenantABC-eu-west.financialone.acme"
Crossplane 提供 应用程序 和基础设施编排,使用操作符模式持续确保每个应用程序实例——如在复合应用中定义——按预期运行 。
另一个我们将在 第五章 中深入了解的工具是 Keptn CNCF 项目。 Keptn 提供自动化的应用感知生命周期编排和可观察性。 它让你可以声明性地定义部署前后检查(验证依赖关系、运行测试、评估健康状况、强制基于 SLO 的质量门控等),而无需编写自己的 Kubernetes 操作符来实现这些操作。 Keptn 还提供自动化的部署可观察性,以更好地理解发生了多少次部署,成功了多少次,以及它们在哪些地方和为何失败,通过发出 OpenTelemetry 跟踪和指标,方便故障排除 和报告。
Kubernetes 提供了构建韧性系统的许多基础构件。 虽然你可以编写自己的操作员来扩展到你自己的问题领域,但你也可以使用现有的 CNCF 工具,例如 Crossplane 或 Keptn,因为它们提供了一种更简单的声明性方式,应用承诺理论的概念,来处理 更复杂的 应用程序和 基础设施组成。
重启组件是确保系统韧性的一种方式,但还有更多的方法。 让我们来看看自动扩展,它解决了动态环境中另一个关键的 问题!
自动扩展集群和工作负载
在大多数行业中,系统预期的负载在一年中的每一天分布不均。 总会有某种季节性波动:零售在黑色星期五和网络星期一有激增,税务服务在报税日有激增,金融服务通常在发放工资时会有激增。 我们的 Financial One ACME 客户也面临同样的情况。 作为一个金融服务组织,始终会有来自终端用户的基本流量,但在月初和月末 会有流量激增。
Kubernetes 提供了几种扩展应用 工作负载的方式:手动(例如,设置 ReplicaSets)或通过工具自动扩展,如 Horizontal Pod Autoscaler (HPA)、 Vertical Pod Autoscaler (VPA),或者 Kubernetes Event Driven Autoscaler (KEDA)。 这些 扩展选项允许你在工作负载的 CPU 或内存不足时、应用程序流量激增时,或者响应时间开始 增加时进行扩展!
除了工作负载,你还可以并且很可能需要通过工具来扩展你的集群和节点的规模, 例如 Cluster Autoscaler (CA) 或 Karpenter [2],或者通过你所使用的托管 Kubernetes 云服务提供的选项。
作为一个平台工程团队,你需要熟悉所有不同的选项,同时也要注意所有的 考虑因素:
-
设置限制:不要允许应用程序无限制地扩展。 您可以选择为每个应用程序、工作负载、命名空间等强制执行最大限制。
-
成本控制:自动扩展是很棒的,但也有其成本。 确保将成本报告给 应用程序所有者。
-
缩小规模:扩展很容易! 确保同时定义何时缩小规模的指标。 这样 可以保持成本 在可控范围内。
要了解更多信息,请查看以下 文档: https://kubernetes.io/docs/concepts/workloads/autoscaling/
现在我们已经了解了在 Kubernetes 环境中扩展的选项,那么如何扩展到其他Kubernetes 集群呢? 我们来看看这个问题。
一次声明——到处运行(理论上)
Kubernetes 作为开放标准的承诺是,任何声明的状态(Ingress、工作负载、机密、存储、网络等)无论是在单一集群中运行,还是在多个集群中运行以满足特定需求(如阶段分离:开发、预发布和生产,或区域分离:美国、欧洲, 亚洲等)都会表现相同。
无论是操作您自己的 Kubernetes 集群,使用 OpenShift,还是使用云供应商提供的托管 Kubernetes 服务,这一承诺在理论上都是成立的。 那么,在理论上是什么意思呢? 不同的服务之间确实有一些具体的技术差异,您需要加以考虑。 根据不同的服务,网络或存储可能会有所不同,因为底层实现依赖于云供应商。 某些服务还会提供核心 Kubernetes 服务、服务网格和运维工具的特定版本,这些都会随着托管安装一起提供。 一些服务要求您使用供应商特定的注释来配置某些服务的行为。 这就是为什么在不同供应商之间应用相同的声明式状态定义理论上是可行的——但实际上,您需要考虑一些小的差异,并进行一些 在理论上的调整,这些差异需要特定的供应商配置!
由于技术细节和差异不断变化,因此在本书中提供当前的对比并不合适。 您必须理解的是,尽管理论上您可以将任何 Kubernetes 对象部署到任何版本的 Kubernetes 上,但结果和行为可能会有所不同,具体取决于您部署的环境。 这就是为什么我们建议对所选的 Kubernetes 目标平台进行一些技术研究,并了解它与其他平台的差异,特别是当您想要进行多云/多 Kubernetes 部署时,因为相同的 Kubernetes 对象可能会有 细微的差异!
好消息是 全球社区正在通过提供更好的指导和工具来解决这个问题,使得 声明一次——随处运行 的承诺 成为现实。
我们已经看到了选择 Kubernetes 作为核心平台的许多好处。 然而,还有另一个重要的原因,解释了为什么 Kubernetes 在过去 10 年里自首次发布以来获得了如此广泛的采用:那就是全球社区和 CNCF!
全球社区和 CNCF
Kubernetes 于 2014 年 6 月由 Google 宣布,版本 1.0 于 2015 年 7 月 21 日发布。 随后,Google 与 Linux 基金会合作,并成立了 CNCF,将 Kubernetes 作为其初始项目! 自那时以来,社区和项目迅速席卷了全球!
十年后(在撰写本书时),CNCF 拥有 188 个项目,244,000 名贡献者,1660 万次贡献,成员遍布全球 193 个国家。 许多介绍 Kubernetes 和 CNCF 的演示文稿通常从展示 CNCF 的生态图开始: https://landscape.cncf.io/
虽然生态图令人印象深刻,但它也成为了许多关于如何在这个全球社区的所有项目中导航的艰难和复杂性的梗的来源。 然而,不要害怕。 全球 CNCF 社区是 Linux 基金会的一部分,其使命是为快速发展的云原生项目提供支持、监督和指导,包括 Kubernetes、Envoy、 和 Prometheus。
以下是您需要注意的一些事项,它们将帮助您在不断增长的 CNCF 项目列表中导航:
-
项目状态:CNCF 会积极跟踪每个项目的状态和活动。 贡献者和采纳者的数量,以及项目的开发活跃度,是你是否应该进一步关注某个项目的良好指标。 如果一个项目处于停滞状态,只有一个维护者,或者几乎没有采纳者,那么对于你选择长期支持的平台工具而言,可能并没有什么用处。 你的平台。
-
成熟度级别:CNCF 还指定了成熟度级别 ,包括沙箱、孵化、或已毕业,分别对应于《创新者的窄道》中的创新者、早期采纳者和早期多数这几个层级 的 跨越鸿沟 图表(https://en.wikipedia.org/wiki/Crossing_the_Chasm)。 已毕业的项目已经在多个行业广泛采纳,是其支持的用例的安全选择。 孵化中的项目已经从技术试验场跨越到获得良好采纳,并且维护者的数量也在不断增长,维护者的背景也日益多样化。 想了解更多关于 CNCF 成熟度的标准,并查看各项目处于哪个级别,可以访问官方站点 : https://www.cncf.io/project-metrics/。
-
ADOPTERS.md
文件,几乎每个 CNCF 项目在其 GitHub 仓库中都有。 如果你决定采纳这些项目中的一个,我们鼓励你通过提交拉取请求将你的名字添加到采纳者名单中。 这不仅有助于项目,也有助于其他组织决定该项目是否 值得追求!
Kubernetes 至关重要,因为它有一个强大的社区
尽管 Kubernetes 拥有强大的技术基础,但正是过去 10 多年中建立起来的社区和生态系统使 Kubernetes 成为平台工程师将其作为 核心平台的可行选择。
我们现在已经更清楚地理解了 Kelsey Hightower 所说的:“Kubernetes 是一个用来构建平台的平台。 这是一个开始,但不是最终目标。”选择 Kubernetes 作为核心平台有很多好处,特别是因为它是基于 承诺理论的概念构建的。Kubernetes 提供了组件的自动化弹性、扩展性和生命周期管理。 不断壮大的社区通过数百个开源 CNCF 项目为许多常见问题提供了解决方案,任何组织 都可以使用。
虽然我们通常关注 Kubernetes 在部署和编排应用程序方面的好处,但让我们看看如何利用 Kubernetes 将我们的基础设施能力提升到未来平台!
利用和管理 Kubernetes 基础设施能力
回顾一下 第二章,你已经了解了平台参考组件模型和能力平面。 当我们谈论将基础设施能力提升到 Kubernetes 时,最终用户在使用该平台时会意识到这些能力。 我们必须区分需要与 Kubernetes 集成的资源和那些通过规范配置并部署到 Kubernetes 的资源,以及在集群外部操作或创建的新资源。 在下图中,您可以找到需要坚实集成的资源整合和网络部分的示例;否则,它们会主动阻碍平台的有用性和 功能。
图 4.3:带有示例工具的能力平面
集成基础设施资源
我们将讨论平台底层技术的基本组件和你必须做出的设计决策。 首先,由于 Kubernetes 的强大,你在工具选择上更加灵活,并可以根据需要进行扩展。 当你调整平台能力以适应不同 的使用案例时,这尤其有帮助。
容器存储接口
该 容器存储接口 (CSI) 提供 对附加到集群或运行集群的节点的存储技术的访问。 在 CSI 开发者文档中 [3],你可以找到几乎所有存储提供商的驱动程序。 该列表包含 云提供商的驱动程序,例如 AWS 弹性块存储 (EBS),软件定义存储如 Ceph,或者商用解决方案如 NetApp 的连接器。 此外,CSI 驱动程序还支持特定工具的驱动程序,如 cert-manager 和 HashiCorp Vault。 简而言之,CSI 对于任何需要存活时间超过其所属容器且未存储在数据库中的数据至关重要,或者对于 数据库存储是必需的。
驱动程序的安装依赖于基础设施和存储技术。 例如,对于云提供商,你通常需要 以下内容:
-
服务帐户或 权限策略
-
启动污点 和容忍配置
-
预安装的 外部快照工具
-
驱动程序 安装本身
由于其复杂性,这些组件通常通过 Helm 或其他软件包管理工具进行部署。 有时,它们需要更多的节点权限,这在平台设计时可能带来安全隐患。 你还需要考虑如何访问存储:
-
ReadWriteOnce (RWO):一个 Pod 声明了一部分可用存储,它可以进行读写操作,而其他 Pods 不能访问该存储,除非原 Pod 释放 该存储。
-
ReadWriteMany (RWM):多个 Pods 可以声明一部分可用存储。 它们可以进行读写操作,并与其他 Pods 共享该存储。
-
ReadOnlyMany (ROM):多个 Pods 可以声明一部分存储,但只能从中读取数据。 从中读取。
-
ReadWriteOncePod (RWOP):仅能被一个 Pod 声明;没有其他 Pod 可以占用它,并允许读写操作。
CSI 驱动程序的概述提供了更多关于哪些访问模式被支持的信息。 由于没有一刀切的解决方案,你需要将你的选项透明地展示给用户,并解释如何 使用它们。
用户需要了解和理解的核心元素是 StorageClass
, PersistentVolume
,以及 PersistentVolumeClaim
。当一个 Pod/Deployment 需要一个卷时, StorageClass
将触发新卷的创建。 如果一个 Pod/Deployment 已经声明了一个卷,而且它没有被销毁,Kubernetes 控制平面将会重新分配该卷给 Pod/Deployment。
图 4.4:动态配置新的持久卷
你作为平台团队定义 StorageClass
,理想情况下,与存储专家合作。 以下示例突出显示了 StorageClass
的常见定义。配置中有很多容易出错的地方;例如,未设置 reclaimPolicy
将默认删除后创建并附加的卷。 然而, StorageClass
支持你为用户请求的 PersistantVolumeClaim
动态创建卷,因此是自助服务的强大推动力:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ultra-fast
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: csi-driver.example-vendor.example
reclaimPolicy: Retain # default value is Delete
allowVolumeExpansion: true
mountOptions:
- discard
volumeBindingMode: WaitForFirstConsumer
parameters:
guaranteedReadWriteLatency: "true" # provider-specific
这带来的缺点是你需要考虑人为错误,错误可能会导致存储被填满,从而使平台崩溃,直到 系统冻结。
CSI 挑战
CSI 的定义是 一种相对较新的方法,但存储管理和软件定义存储的底层技术比 Kubernetes 要早得多。 我们还可以在许多 CSI 驱动程序中发现这一点,它们不过是对传统代码的封装。 对于那些不太灵活且可扩展性较差的集群来说,这可能不是问题,但在你有大量活动的环境中,你可不想让 CSI 成为系统中的瓶颈。 一些 CSI 甚至有限制和约束,以防止它们在规模扩展时失败。 公平地说,我们通常会看到这种情况出现在本地安装和一些老旧的存储技术中。 对于这些,我们还可以考虑 逻辑单元号 (LUN) 的呈现和连接限制。 LUN 是 Pod 从存储空间请求并获取数据的方式。 物理服务器与存储之间的连接数量是有限制的。 同样,当您管理自己的存储 和 SAN 时,这一点尤为重要。
为什么有些 CSI 这么差劲? CSI 不仅仅是提供存储容量。 它与存储提供者进行通信,承诺提供所需的容量,并等待 RAID 控制器、备份和快照机制准备就绪。 在大规模存储系统中,我们还会看到更多:我们可以找到存储容量分配和 优化过程。
为了克服这些问题,您需要评估存储,特别是其云原生存储能力的存储驱动程序。 这将需要进行大规模的性能测试,并创建大量的卷。 CSI 驱动程序不应做任何削减或性能下降,创建的卷应处于 毫秒级别。
此外,确保驱动程序支持跨存储和跨云/基础设施迁移;提供不同基础设施之间的同步和异步复制;提供各站点之间的功能对等;并在需要时支持本地存储类型,例如 边缘场景。
一个好的 CSI 将使您的平台能够 在任何地方运行,并支持广泛的 使用场景。
容器网络接口
容器网络接口 (CNI) 可以成为平台最相关的组件,但它也是最被低估的组件。 在我们看到的许多项目中,一些平台团队并不关心他们使用什么作为 CNI,也不太利用网络策略、加密或细粒度的网络配置。 由于它对网络层的简单抽象,刚开始使用时并不会让人感到压倒性。 然而,另一方面,有许多用例中,最关键的组件就是 CNI。 我甚至见过因为 CNI 的动态特性与传统的网络实现方法不兼容而导致项目失败。
CNI 总是需要专门的实现,因为它是一套规范和库,用于编写插件来配置网络接口。 CNI 只关注 Linux 容器的网络连接,并在容器被删除时清除分配的资源。 由于这一点,CNI 有广泛的支持,并且其规范实现简单。 易于实现。
因此,我们作为架构师绝不应将 CNI 视为“Kubernetes 中的另一个对象。” 我们必须评估并引入它。 一些 CNI 旨在简化维护,提供一个坚固但简单的功能集。 例如,如果主要关注的是第 3 层,考虑使用 Flannel。 其他 CNI,如 Calico,是一个坚实的选择,功能丰富;Cilium 则引入了 eBPF 的使用,提供更快速、更安全的网络连接。 如果你因为可能有其他需求,如提供不同级别的网络,而仍然难以选择,可以选择社区为你提供的解决方案:Multus。 慢慢思考并探索你的选项。 有成百上千 种 CNI。
CNI 对 你的平台可能有重要影响:
-
安全性:CNI 可以提供不同的网络策略功能,以实现对网络的细粒度控制。 它们可以具备额外的加密功能,集成身份和访问管理系统,以及 详细的可观测性。
-
可扩展性:集群规模越大,网络中的通信就越频繁。 CNI 必须支持你的增长目标,即使在复杂的路由和数据传输下仍然保持高效。
-
性能:Pod 之间的通信有多快和直接? CNI 引入了多少复杂性? 它能多高效地处理通信? 它能处理很多复杂的 网络策略吗?
-
可操作性:高级 CNI 不是很侵入式,且易于维护。 强大的 CNI 理论上可以被替换,只要它们遵循 CNI 规范,但每个 CNI 都有自己的一套功能,这些功能往往是 无法替代的。
请注意,并非所有的 CNI 都支持这些功能。 例如,有些甚至不提供网络策略支持。 其他 CNI 是云提供商特定的,只与一个云提供商集成, 并且它们确实启用一些以云提供商为中心的功能。
架构挑战 – CNI 链式调用与多个 CNI
平台非常适合 CNI 链式调用。 CNI 链式调用 引入了多个 CNI 的顺序使用。 CNIs 的使用顺序及其目的在 /etc/cni/net.d
目录中定义,并由 kubelet 处理。 这允许平台团队处理网络的一部分,并提供一种受控的方法,而平台用户则可以在更高的层次上自由配置他们的网络。 例如,平台用户可以访问 Antrea 作为 CNI,以某种程度上配置他们的网络。 他们还可以应用网络策略和出口配置,以防止他们的应用与所有人进行通信。 在平台的另一侧,平台工程团队将通过 Cilium 管理全局跨集群通信以及网络加密,以执行安全最佳实践。 此外,Cilium 提供的可见网络数据将提供给运营和安全团队。 这些用例最适合与云提供商自己的 CNI 进行交互。 它们通常能够更好地将平台与云集成,但在另一面缺乏许多高级功能。 其他方面的功能则较少。
另一个待评估的方法是通过 Multus 或 CNI-Genie 为 Pod 分配多个网络接口。 通常,Pod 只有一个接口,但例如使用 Multus 时,这可以是多个网络接口。 什么时候这变得相关? 以下实例是它变得相关的示例: 变得相关:
-
将控制数据和操作数据与 应用数据分离
-
为极为 异构的工作负载提供灵活的网络选项
-
通过为每个租户分配完全不同的网络,将多租户架构提升到一个新水平
-
支持不寻常的网络协议和连接,例如在边缘场景或 电信环境中
-
网络功能虚拟化 (NFV),由于 其复杂性 ,需要多个网络
Multus CNI 是一种位于节点上的元插件,处于实际 CNI 和 Pod 网络接口之间,如下图所示。 它一方面将不同的网络接口连接到 Pod,另一方面处理与预期 CNI 的连接,以确保正确的网络接口。 的连接。
图 4.5:Multus 元 CNI 插件
这两种方法都必须进行充分评估。 它们对网络的复杂性、性能 和安全性有重大影响。
提供不同的 CPU 架构
Kubernetes 支持 多种 CPU 架构:AMD64、ARM64、386、ARM、ppc64le,甚至包括使用 s390x 的大型机。 目前许多集群运行在 AMD64 架构上,但在本文写作时,ARM64 正受到强烈关注,导致这一趋势的变化。 本讨论主要关于节省成本并获得一些额外性能,同时减少总体电力消耗。 至少在理论上,这是一个三赢的局面。 不仅 ARM64 是基础设施可能发生的变化,开源架构项目 RISC-V 也在加速发展,并且是第一个创建 RISC-V 服务 [4]的云服务提供商。
作为平台工程师,我们可以启用这些迁移和变化。 Kubernetes 集群可以同时运行多种架构——不是在同一个节点上,而是在不同的节点组中。 请记住,这一变化也需要容器构建方面的调整。 对于某些软件组件,你可以进行多架构构建;而对于其他组件,可能需要在为不同架构创建容器之前进行调整。 容器才可以创建。
要选择一个部署应该交付到的架构,你只需要在 nodeSelector
中像这样添加 Spec
部分到部署 YAML 文件:
nodeSelector:
kubernetes.io/arch: arm64
提供不同容器镜像的一个即将到来的替代方案是将软件编译为 WebAssembly (Wasm) 容器。
Wasm 运行时
作为一种替代容器格式,Wasm 的使用在过去一年中大幅增加。 Wasm 是一种面向栈式虚拟机的二进制指令格式。 可以把它看作是各种编程语言和多种不同执行环境之间的中间层。 你可以将用超过 30 种不同语言编写的代码编译成一个 *.wasm
文件,然后在任何 Wasm 运行时上执行该文件。
然而,名称 WebAssembly是具有误导性的。 最初设计是为了让代码在网页上快速运行,但今天它可以在任何地方运行。 我们为什么要使用它? Wasm 默认是安全且沙盒化的。 在一个 Wasm 容器中,除了经过编译的二进制代码外,没有操作系统或其他任何东西。 这意味着没有可以破坏或窃取上下文或服务帐户的东西。 Wasm 还具有极快的启动时间,其限制由运行时设置,而不是模块。 一些运行时声称启动速度可达到大约 50 毫秒。 相比之下,你的大脑需要超过 110 毫秒才能识别某物是否在你眼前经过。 此外,Wasm 容器的大小大约是 0.5 MB – 1.5 MB,而一个非常精简的容器可能约为 5 MB。 然而,我们在市场上看到的通常是 300 MB – 600 MB,1 GB – 3 GB,甚至有时超过 10 GB 的镜像大小。 通过减小镜像大小,Wasm 容器的存储占用也大大减少。 Wasm 还具有硬件独立性。 相同的镜像可以在任何地方运行,只要你有一个可用的 Wasm 运行时。
在 Kubernetes 环境中,OCI 和 CRI 运行时支持 Wasm。 这意味着你可以同时运行一个 Wasm 容器和一个常规容器。 如以下图所示,不需要做进一步的修改。 Wasm 应用镜像存储在节点级别,并由上层执行。
图 4.6:Wasm 在 Kubernetes 节点上
为了使 Wasm 可执行并在你的平台上可用,你需要指定一个运行时类并在部署/Pod 级别定义其使用。 在以下示例中,你可以看到左侧指定了 crun
作为 RuntimeClass
,右侧则是一个 Pod
定义,其中 spec.runtimeClassName
的值为 crun
。 对于 crun
,我们还需要添加一个注释来告知 crun
该 Pod
具有一个 Wasm 镜像:
| apiVersion: node.k8s.io/v1``kind: RuntimeClass``metadata:``name: crun``scheduling:``nodeSelector:``runtime: crun``handler: crun
| apiVersion: v1``kind: Pod``metadata:``**name: wasm-demo-app**
****annotations:**
****module.wasm.image/variant: compat**
**spec:
**runtimeClassName: crun**
****容器:**
****name: wasm-demo-app**
****image:**
**docker.io/cr7258/wasm-demo-app:v1**
************ |
表 4.1:定义一个将在 Wasm 运行时执行的 RuntimeClass 和 Pod
作为替代方案,像 SpinKube 这样的新开发 提供了一整套工具来以最佳方式利用 Kubernetes 资源 [5]。这种方法使得开发体验能够与 Wasm 容器化应用的部署和执行紧密集成。 下图展示了工作流以及不同组件如何协同工作。 它使开发过程变得简单直观,并为 平台带来了一个快速但稳定的新运行时环境。
图 4.7:SpinKube 概览 [6]
然而,Wasm 真的是业界已经采纳的技术吗? 以下是一些展示 其采用情况的示例和用例:
-
`互操作性:Figma 作为 Wasm 在 任何计算机上运行
-
插件系统:您可以为 Envoy 编写 Wasm 扩展
-
沙箱:Firefox 或 Chrome 浏览器可以在沙箱环境中运行 Wasm,以保护 您的系统
-
区块链:CosmWasm 或 ICP 使用 Wasm 的版本来 运行应用程序
-
容器:WasmCloud 执行容器的概念不同,或者是 SpinKube,一个具有 CLI 的 Wasm 运行时,提供简单的 开发流程
-
无服务器平台:Cloudflare Workers 或 Fermyon Cloud 运行您的 Wasm 应用
Wasm 目前还不能替代容器。 它是一个进化的步骤,非常适合一些特定的应用场景,但它尚未得到广泛采用,并且在调试过程中存在问题。 然而,这些障碍的解决只是时间问题。 它们会得到解决。
为 GPU 利用启用平台
类似于对不同 CPU 架构的支持,或者将容器运行时扩展到包括 Wasm,GPU 的支持 最近对用户变得极为相关。 必须安装特定于 GPU 类型的设备插件,如 AMD、Intel 或 Nvidia。 这将向 Kubernetes 及其用户暴露自定义可调度资源,如 nvidia.com/gpu
。 此外,我们不是 GPU 及其功能的专家;从平台工程的角度来看,不同的供应商为其插件开发了不同的功能集和扩展。
如果您的用户需要 GPU,我强烈建议开发或者寻找该领域的专家。 AI 和 LLM 的领域正在迅速扩展。 硬件和软件提供商每月都会推出新工具、新系统和新方法,以便充分利用这些技术。 任何场景都有其非常具体的需求。 训练一个模型需要大量的数据、GPU、存储和内存。 微调一个模型主要取决于您想训练的模型有多大。 一个七十亿参数的模型可以适应 14 GB 的 VRAM,但提高精度可能会轻松将其大小增加到 24 GB 或更多。 最后,为用户提供推理引擎以服务 LLM 需要大量的 网络通信。
重要提示
推理意味着向 LLM 发送一个提示。 大多数人认为 LLM 接着会像人类一样创建故事。 然而,实际上发生的是,在每个单词之后,LLM 必须将整个提示(包括新词)再次发送给 LLM,以便它能决定添加到句子中的下一个词。
作为经验法则,对于所需的 GPU VRAM(视频内存是 GPU 的内存),你需要将模型的大小翻倍。 以下是一些 更多的例子:
-
Llama-2-70b 需要 2 * 70 GB = 140 GB VRAM
-
Falcon-40b 需要 2 * 40 GB = 80 GB VRAM
-
MPT-30b 需要 2 * 30 GB = 60 GB VRAM
-
bigcode/starcoder 需要 2 * 15.5 = 31 GB VRAM
因此,需要平台与机器学习团队之间的合作。 根据他们想使用的模型,你选择的硬件可能不再符合 要求。
架构挑战
虽然 GPU 的集成非常简单,但它们有一些需要讨论的要素;你应该了解以下内容:
-
GPU 成本:便宜的 GPU,具有较低的 VRAM 和计算能力,可能不适合你的使用案例。 另外,你可能不会 24/7 使用 GPU,但你应该考虑更具动态性和灵活性的可能性。 然而,如果你的使用案例需要 GPU 计算能力,拥有 GPU 从长远来看可能比常规的 CPU 更便宜。
-
特权权限:许多机器学习工具需要进一步的定制和调整,特别是它们所要求的权限。
-
最终用户需求:除了模型大小外,数据科学家和机器学习工程师希望如何使用模型,很大程度上取决于他们想要实施的实际使用案例。 方法的任何小变化都可能使平台无法使用。 这必须在此类平台的架构中加以考虑,以提供最多的资源和最大的扩展 灵活性。
-
外部模型拉取到集群:与容器类似,从像 HuggingFace 这样的页面拉取模型是一种常见做法。 你可能会考虑在支持机器学习活动的平台网络中进行此操作。
为机器学习和大语言模型(LLM)操作创建适合的平台需要一种全新的优化层次。 未运行的 GPU 是浪费金钱。 使用不当的 GPU 是浪费金钱。 然而,从基础设施的角度来看,我们还必须确保更多:数据保护、平台安全性以及模型的专门可观察性。 在我看来,机器学习和大语言模型是一个令人兴奋的应用场景,提供了一个平台工程师的游乐场。
解决方案空间
为了优化 GPU 的使用,有一些 可用的方法:
-
多进程 服务器 (MPS)
-
多实例 GPU (MIG)
-
时间分片/共享
根据 GPU 驱动程序,您将会发现多种可能性,就像使用 Nvidia 一样。 其他 GPU 驱动程序可能还不够成熟或功能丰富,但 当然,您应该经常评估 这个问题。
时间分片是最差的选择。 虽然它比什么都没有好,但 MPS 至少会比时间分片方法高效两倍。 然而,MPS 有一个主要缺点:进程之间没有严格的隔离,这会导致切片之间出现相关故障。 这就是 MIG 的优势所在。 它提供了良好的进程隔离和 GPU 的静态分区。 在一个超级动态、可扩展且随时可调整的 Kubernetes 集群中,静态分区如何实现? 是的,因为机器学习训练不会仅仅运行几秒钟或 几分钟 [7]。
下图展示了 GPU 的内存分区,您可以将不同的工作负载分配到这些分区中。
图 4.8:将 GPU 拆分成三个 GPU 实例的示例
这些 GPU 实例可以由单个 Pod 使用,也可以由一个包含多个进程的 Pod(不理想)使用,或者通过使用 Nvidia 的 CUDA 等技术来使用。 CUDA 是一个 MPS,因此您可以结合不同的方法,如下图所示:
图 4.9:使用三个 GPU 实例并行的示例
从这里,我们可以看一下研究和实验部分,你可以结合 Kubernetes v1.26
,并且它仍处于 Alpha 阶段,因此每个版本可能会有 API 的重大变动。 有一些有趣的文章和演讲讨论了它。 根据你阅读的时间,它可能已经过时了。 date [8].
启用集群可扩展性
如前所述,在本 章节中,Kubernetes 集群的伸缩能力对于不同需求是有益的,包括从弹性、随工作负载增长到回退重新初始化,再到在数据中心和可用区提供最高可用性。 从本质上讲,我们区分水平和垂直自动扩展器,后者针对 Pods,水平 CA 则调整节点数量。
与许多功能一样,Kubernetes 提供了规范,但期望其他人来实现它。 这至少适用于 VPA 和 CA,它们至少需要在 集群级别运行一个指标服务器。
然而,HPA 功能丰富,支持基于指标的伸缩。 请看以下 HPA 的例子。 使用 stabilizationWindowsSeconds
,我们还可以告诉 Kubernetes 等待先前的操作,以防止抖动。 抖动的定义如下,根据 Kubernetes 文档: Kubernetes 文档:
在使用 HorizontalPodAutoscaler 管理一组副本的规模时,由于所评估的指标具有动态特性,副本数量可能会频繁波动。 这有时被称为抖动,或者说是振荡。 它类似于控制论中的滞后效应概念。
我们可以参考以下 HPA 的配置。 看起来很简单;你可以看到,根据策略,行为可以发生巨大变化。 例如,当在拥有数百个副本的大型部署中减少 10% 的 Pods 时,我们需要非常小心。 所示的缩小配置将防止同时删除超过两个 Pods:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
maxReplicas: 7 #maximum replicas to scale up to
minReplicas: 3 #expected minimum replicas
scaleTargetRef: ... targetCPUUtilizationPercentage: 75 #metric to react on
behavior:
scaleDown:
stabilizationWindowSeconds: 300 #wait 300 sec
policies:
- type: Percent
value: 10 #reducing 10% of Pods
periodSeconds: 60 #per minute
- type: Pods
value: 5 #reducing not more
periodSeconds: 60 #than 2 Pods per min
selectPolicy: Min
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 75 #increase the Pod by 75%
periodSeconds: 15 #every 15 seconds
selectPolicy: Max
VPA、HPA 和 CA 的问题
你需要考虑自动扩展器家族的一些约束:
-
HPA:
-
你 必须正确设置 Pods 的 CPU 和内存限制及请求,以防止资源浪费或频繁 终止的 Pods。
-
当 HPA 达到可用节点的限制时,它无法调度更多的 Pods。 然而,它可能会利用所有可用资源,这可能会导致 问题。
-
-
VPA:
-
VPA 和 HPA 不应基于相同的指标进行缩放。 例如,尽管两者都可以使用 CPU 利用率触发扩容,HPA 部署更多的 Pods,而 VPA 增加现有 Pods 上的 CPU 限制,这可能导致基于相同指标的过度扩展。 因此,如果同时使用 VPA 和 HPA,应将 CPU 用于其中一个,而将内存用于 另一个。
-
VPA 可能建议使用比集群或节点中可用资源更多的资源。 这会导致 Pod 变得不可调度。
-
-
CA:
-
CA 缩放基于 Pods 的请求和限制。 这可能导致许多未使用的资源、利用率低下以及 高成本。
-
当 CA 触发云提供商的缩放命令时,可能需要几分钟时间为集群提供新的节点。 在此期间,应用程序性能会下降。 最坏的情况是,它 变得无法服务。
-
解决方案空间
作为平台工程师,我们希望确保用户能够定义缩放行为,同时不会让平台面临风险。 利用 HPA、VPA 和 CA 需要完美的配置与控制,由策略引擎提供的保护措施以及密切的监控。 控制集群的扩展和缩减变得至关重要,同时要为 用户启用命名空间内的自动缩放。
在云提供商上管理 Kubernetes 集群的缩放需要你查看 CA 及其可用的各种云集成。 此外,如果你使用 Cluster API (CAPI),你 还可以利用其能力进行 集群 自动缩放。
网络能力和扩展
现在,让我们来看一下 Kubernetes 与底层基础设施的最终资源集成。 为此,我们将从集群的网络机制入手,逐步探讨 DNS 和负载均衡。 DNS 和负载均衡可以在集群内发生,并与 Kubernetes 运行的基础设施协同工作。
Ingress – 旧方式
Ingress 是旧版定义,描述了如何将外部集群的最终用户请求路由到系统中并传递给暴露的应用程序。 近十年来,它一直是定义传入网络流量的方式。 Ingress 通常由如 NGINX、HAProxy 或 Envoy 等 Ingress 控制器表示,举几个例子。 这些控制器本质上会采用 Kubernetes 定义的标准资源中的路由规则,并管理其余部分。 如下面的图所示,从那里开始,流量会被重定向到正确的服务,服务再将其转发到 Pod。 从物理层面上讲,流量将从网络接口流向 Ingress 控制器,再到 Pod,但正如 Kubernetes 是一位优秀的调度器一样,仍然有一些逻辑步骤 介于其中。
图 4.10:Ingress 控制器(来源:Kubernetes 文档)
虽然它具有可扩展性,适用于一些 最大规模的部署,但它也有 一些缺点:
-
Ingress API 仅支持 TLS 终止和简单的基于内容的 HTTP 流量请求路由
-
它的语法有限,保持 非常简单
-
它需要注解 以支持扩展性
-
它降低了可移植性,因为每个实现都有自己的方式来 执行此操作
构建多租户集群更加具有挑战性,因为 Ingress 通常是在集群级别,并且权限模型较弱。 此外,Ingress 支持命名空间配置,但不适合多团队和共享的 负载均衡基础设施。
Ingress 方法的优美之处在于它广泛的支持与其他工具的集成,例如用于证书管理的 cert-manager 和即将看到的外部 DNS 处理。 此外,Kubernetes 的维护者声称没有计划废弃 Ingress,因为它以一种简单的方式完美地支持简单的 web 流量。 方式。
Gateway API – 新方式
在 2023 年秋季,网关 API 正式发布。 网关 API 由多个资源类型组成,而不是单一资源,它遵循的模式在其他 关键集成中已被使用:
-
网关
: 进入集群的入口点 流入的流量 -
GatewayClass
: 定义将处理 网关的控制类型 -
*Route
: 实现从网关到 服务的流量路由:-
HTTPRoute
-
GRPCRoute
-
TLSRoute
-
TCPRoute
-
UDPRoute
-
将下图与 Ingress 方法进行比较时,我们可以看到流入的流量通过网关并被重定向的两个步骤 通过 *Route
。
图 4.11: 网关 API
它们如何比较? 如何比较?
Ingress | 网关 API | |
---|---|---|
协议 | 仅限 HTTP/HTTPS | L4 和 L7 协议 |
多租户 | 困难/自定义扩展 | 按设计支持多租户 设计 |
规范 | 基于注释,控制器特定 | 控制器独立,拥有 资源,标准化 |
定义/资源 | Ingress 资源 | 网关,GatewayClass,*Route 资源 |
路由 | 基于主机/路径 | 支持头部 |
流量管理 | 仅限于 供应商 | 内建/定义 |
表 4.2: 比较 Ingress 和网关 API
这一丰富的功能集对需要构建平台的人来说是一个游戏规则改变者。 此前,这些大多数功能必须通过商业购买,或通过一些变通办法和自开发工具强行集成到集群中。 然而,借助网关 API,支持广泛的协议,并且还附带了两个 有趣的额外 功能。
您可以使用网关 API 创建跨命名空间路由,可以使用 ReferenceGrant
或者 AllowedRoutes
。允许的路由通过引用绑定实现,需要网关所有者定义来自哪些命名空间的流量。 在实践中,网关配置将会扩展 如下:
namespaces:
from: Selector
selector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- alpha
- omega
这将允许来自 alpha
和 omega
命名空间的流量。
或者,我们可以使用 ReferenceGrant
,如下所述:
ReferenceGrant 可用于启用网关 API 内的跨命名空间引用。 具体来说,路由可以将流量转发到其他命名空间中的后端,或者网关可以引用另一个命名空间中的密钥。
它们听起来相似,但实际上不是。 让我们 来比较一下:
-
ReferenceGrant
:在命名空间 A 中的网关和路由授予命名空间 B 中的服务 转发流量 -
AllowedRoutes
:在命名空间 C 中配置了网关对命名空间 B 的路由。
除了这些跨命名空间的额外安全层外,网关 API 还具备自己的扩展能力。 通过它们,您可以定义自己的策略附加,如后端 TLS 策略,以验证正确的 TLS 使用方式 。
在我们继续之前的最后一件事。 网关 API 自带角色。 角色是预定义的角色,允许细粒度使用网关功能。 入口只有一个角色,即 用户,无论是管理员还是开发者。 以下表格显示了这些角色在 四层模型中的写权限:
图 4.12:高级四层模型的写权限
在这一点上, 网关 API 是解决无数夜晚正确处理入站流量并构建具有清晰和 透明方法的多租户平台的真正救星。。
ExternalDNS
该 ExternalDNS 项目由 Kubernetes 贡献者开发,是一个常在云提供的 Kubernetes 集群中使用的工具,但仍然没有作为一个重要实现被广泛强调。 然而,它弥合了集群中某些随机 IP 和一个公开或私有可访问的 DNS 之间的鸿沟,并将流量路由到平台内的应用程序。 ExternalDNS 支持几乎所有云环境以及类似云的环境,还支持如 CloudFlare 这样的流量和内容聚焦型服务。
然而,ExternalDNS 不是一个 DNS。 惊讶吧! 它从 Kubernetes API 读取资源,并配置外部 DNS 指向集群的公共端点。 你也可以说,ExternalDNS 让你能够通过 Kubernetes 资源以 DNS 提供商无关的方式动态控制 DNS 记录。
让我们来看看 ExternalDNS 如何与 Kubernetes 的 CoreDNS 和 Cloud DNS 服务一起工作。 在下图中,你可以看到左侧是托管的 Kubernetes。 在这种情况下,AWS EKS 和 CoreDNS 正在 Kubernetes 上运行,以解析内部 DNS 请求。 当部署了 ExternalDNS 后,它会观察网关、入口和服务资源。 当发生变化或有新服务 上线时,ExternalDNS 会更新云提供商或 DNS 服务提供商上的 DNS 记录。
图 4.13:ExternalDNS
然而,在寻找替代方案时,你通常只有两种备选方式:自己创建 DNS 条目(手动)或通过自定义控制器、函数,或者在基础设施 创建过程中实现自动化。
因此,看到 ExternalDNS 的限制令人非常痛苦:
-
缺少细粒度的控制;例如,ExternalDNS 会为来自 任何命名空间的所有服务和入口创建 DNS 记录
-
ExternalDNS 只提供 A 记录;如果你需要 TXT 或 CNAME 记录,你必须 手动完成
-
你将获得 DNS 名称的默认 DNS 配置;否则,你必须手动 定义它们
除此之外,你还可能会遇到更高的费用(由于其不合理的 DNS 记录创建行为)以及增加的复杂性或延迟。 我不能完全同意这些问题,因为这取决于你如何 处理它们。
仅当你已经掌握 Kubernetes 网络的其他部分,并能够进行精细化的网络策略和网关 API 管理,从而严格控制哪些服务可达及如何访问时,才考虑使用 ExternalDNS。 此外,考虑启用 DNSSEC 功能,并建立 DNS 监控。 监控。
负载均衡、EndpointSlices 和拓扑感知路由
最后,为了 总结 网络部分,我们将简要 讨论 负载均衡、 EndpointSlices,以及 拓扑感知路由。
负载均衡由 集群中的入口控制器或网关 API 处理。 这可以通过与云服务提供商结合,外包给外部负载均衡器。 所有主要的云服务提供商都有自己的方法,通常通过他们自己的控制器来管理托管服务的负载均衡器。 这些选项之间有什么区别? 在 Kubernetes 内部运行自己的负载均衡器意味着,首先,所有流量都会被路由到那个入口点。 通过正确的设置,这可以是多个节点,并且负载分配非常简单。 缺点是,如果某个节点由于某种原因超载,它仍然会处理并内部分配流量。 云负载均衡器会将负载分配到多个节点,并且根据集成方式,它会知道某个节点是否能够处理更多的负载,或者是否应将流量重定向到另一个节点。 一个 公共云负载均衡器的缺点是,你必须为此付费。 付费。
EndpointSlices 在大规模集群中变得至关重要。 Endpoint 是定义 IP 地址和端口列表的 API 对象。 这些地址属于动态分配给服务的 Pod。 当创建服务时,Kubernetes 会自动创建一个关联的 Endpoint 对象。 Endpoint 对象维护与服务选择器标准匹配的 Pod 的 IP 地址和端口号。 EndpointSlices 在 Kubernetes 1.16 中被引入。 它们提供了一种将网络端点分布到多个资源上的方式,减少了 Kubernetes API 服务器的负载,并提高了大规模集群的性能。 过去,单一的大型 Endpoint 对象会导致服务变慢,而现在创建了多个小型 EndpointSlice 对象,每个对象代表 一部分端点。
控制平面创建并管理 EndpointSlices,每个 slice 的端点数不得超过 100 个。 此限制可以调整,最大值为 1,000。 对于 kube-proxy 来说,EndpointSlice 是路由 内部流量的事实来源。
EndpointSlices 对于 拓扑感知路由也是必需的。 通过拓扑感知路由,你可以将流量保持在云提供商的 可用区 (AZ)内。 这有两个主要优点:它减少了 网络成本并提高了性能。 而不是让 AZ 1 中的 Pod 与 AZ 2 中的 Pod 通信并传输大量数据,AZ 1 中的 Pod 现在将与 AZ 1 中的一个副本(如果有)进行通信。 为了实现最佳效果,传入流量应通过外部负载均衡器均匀分配,并且每个区域应至少有三个端点。 否则,控制器将无法分配该端点,失败的概率约为 50%。
Kubernetes 作为平台控制平面的一部分
回顾第二章,我们可以在参考架构中看到,平台可以是高度分布式的,基于许多服务,从更高的角度来看,这些服务可能甚至不属于同一组。 我们讨论过,Kubernetes 很可能成为你平台的核心部分。 然而,它能变得有多核心呢? 如前所述,Kubernetes 不仅仅是用来运行工作负载的;它是一个平台(基于承诺理论和标准化模型及 API),用于构建平台。 这使得 Kubernetes 将一只脚迈入平台控制平面,并通过两种方式实现这一点:作为资源控制器和作为 平台或协调器。
从 Kubernetes 内部管理资源
开源的 Crossplane 项目是唯一一个不依赖于任何提供商的解决方案,可以在 Kubernetes 内部管理云资源。 最初为了在 Kubernetes 内部管理其他 Kubernetes 集群而创建,它迅速成为处理云资源的通用解决方案。 云资源作为 CRD 提供,可以通过规范文件将其定义为 Kubernetes 本地资源。 这使得用户可以选择定义他们所需的内容,并将资源创建交给 Kubernetes 的承诺理论来处理。 对于不同的云,有所谓的提供商可用,用于定义可用的资源。 用户可以创建单个资源或完整的组合,这些组合由多个资源组成,它们 属于同一组。
外部资源与内部定义资源的问题
管理资源的正确方法是什么? 是应该由基础设施团队定义,还是通过需求表单的输入来定义? 是否可以通过平台由用户来定义? 一切皆有可能,但没有简单的 答案。
让我们来看看这两种方法。 首先,让我们看看来自传统保守背景下的 Financial One ACME 的一个场景:基础设施团队在过去几年里一直在争取自动化和声明性方法。 虽然他们通过 Ansible 管理本地环境,但他们决定为云提供商使用更简单的工具:Terraform(或 OpenTofu)。 我们不会详细介绍整个技术栈,但直到我们进入 Kubernetes 平台之前,一切都通过经典的 CI/CD 推送原则和 IaC 通过 Terraform 来协调。
Financial One ACME 正在启动一个新项目,开发内部使用的定制软件。 团队将利用 ACME 平台并着手系统的基础架构工作。 他们已经确定需要某些文件存储、缓存、关系型数据库、通知服务和消息流服务。 由于平台提供了一些自助服务,团队可以将 Terraform 模块复制到他们的仓库中。 从这里开始,一个预定义的 CI/CD 管道将接管配置,并将资源部署到定义的环境中。 这些自定义的但仍然由外部管理的资源在某些方面是与系统的其余部分隔离的。 它们可能生活在同一个仓库或层级中,并由团队管理,但它们并没有 紧密集成。
另一方面,它们提供了一定程度的稳定性。 在平台用户空间内部,这些资源是不可见的,唯一存在的是一个发现服务。 当一个组织逐渐成熟时,管道可能会被定制,而无需通知所有者或用户。 然而,基础设施和应用程序是明确分离的,这是一个优势,因为它们的生命周期完全不同。 这些元素。
快进到现在,Financial One ACME 已经完成了云原生转型,最大限度地利用了平台工程和 IDP。 同样,他们计划开展一个新的内部项目,这个项目与之前的项目完全不同,但却有着完全相同的需求。 一些组织的行为永远不会改变。 这一次,项目团队在他们的开发者门户中创建了一个新项目。 所有基本需求会自动推送到一个新的 Git 仓库中。 所选的资源被选中并推送到平台,平台上的控制器决定在哪里部署这些资源。 一些资源最终成为云服务商的托管服务,另一些则由专门的团队放在共享服务账户中,还有一些则在项目的命名空间内。 过了一段时间,团队意识到他们选择了错误的配置,经过调整后,迁移到新服务的过程开始了。 这个场景与之前的场景一样真实,但具有 不同的影响。
团队需要更详细地了解他们的需求;另一方面,他们必须信任预定义的部署和配置。 平台工程团队与运营团队合作,定义了具有防护栏的最佳实践,确保可操作性,同时也几乎满足了用户的所有需求。 在集群用户空间内,团队可以找到所有部署的资源,并将它们视为平台内的服务,即使它们并未在其中运行。 然而,用户端的突然变化可能导致资源在其他地方突然增加或删除。 管理服务团队必须在没有警告的情况下处理这些变化。 总体而言,所有依赖资源都表现出极其不稳定和动态的特性,难以预测,也难以管理。 另一方面,项目可以完全专注于流程和进展,因为外部资源是通过集群内部处理的,而不是通过外部集群资源管理解决方案(如 IaC)。
两种方法都可以。 两者各有利弊,而对你的组织来说,最适合的方法往往更多地取决于人的因素,而不是技术因素。 然而,显而易见的是,内部定义的集群资源比外部定义的更加动态,并且更多地转移到了用户的责任范围内。 定义的资源。
最终,这变成了一场哲学讨论。 外部定义的资源更为传统,而内部定义的方法则是进步和面向未来的。 然而,我们没有太多选项来运行集群内的供应流程。 除了 Crossplane,我们还看到了许多元实现控制器,它们从集群中读取自定义资源并触发 CI/CD 流水线,例如。 如果有人问。
在本节中,我们研究了 Kubernetes 所需的基本能力,围绕它们的挑战,以及如何解决它们。 而且,它们不像你在会议上看到的那些疯狂的实现方式。 正确掌握这些基础知识将决定其他一切是否会带来愉快或痛苦。 接下来,我们将通过探讨找到合适的节点大小及其对灵活性 和 可靠性的影响来结束本章。
设计灵活性、可靠性和稳健性
在上一部分中,我们讨论了集群的可扩展性,以及 VPA、HPA 和 CA 如何协同工作。 这有助于创建一个灵活、可靠且强大的系统。 其中一个关键部分是允许定制,前提是不会对系统造成损害。 集群的各个组件必须无缝协作,但也必须在需要时能够互换。 这非常敏感:一方面,你有一个随着时间推移而不断扩展和收缩需求的集群;另一方面,你还需要集群周围的所有扩展功能,以便为你的使用案例提供最佳的功能集。 你还拥有一个不断发展的开源社区,该社区经常发布更新和新功能,这些更新和新功能应该集成并为你的用户提供。 正如我们之前所说的,这就是为什么你必须拥有产品思维——为你的用户构建最佳的平台。 扔掉不需要或过时的东西,但要保持整个系统作为 你的核心。
现在,出于某种原因,我们仍然看到关于是否应该将所有工作负载放在 Kubernetes 上,以及它是否可靠的讨论。 我们必须从两个角度来看这个讨论:从 Kubernetes 的基础设施和核心职责的底层视角来看,以及从用户可以看到并体验到的顶层视角来看。
优化资源消耗与留出足够的头部空间
正如我们在 第二章中学到的那样,Kubernetes 集群及其管理的工作负载之间有着一种有趣的关系,相互影响。 有些应用程序需要更多的 CPU 能力,其他应用程序则会扩展,而其余的则根据需求运行。 找到合适的匹配并不容易,但理想的目标是高资源消耗,因为它优化了可用资源的使用,从而降低 成本。
如何使集群达到合适的大小
评估集群的合适大小 始终是一个挑战。 找到合适的解决方案显然是一个 看情况而定的问题。让我们以 Financial One ACME 为例,它必须提供一个新的集群。 我们对预期的工作负载了解不多,只知道它需要一些内存,并且有几个不那么需要资源的活动组件。 因此,我们可以选择以下 选项之一:
-
1x 16 vCPU,64 GB 内存
-
2x 8 vCPU,32 GB 内存
-
4x 4 vCPU,16 GB 内存
-
8x 2vCPU,8 GB 内存
出于可用性原因,第一个选项会是个坏主意。 如果你在集群中进行更新,整个系统崩溃或你需要提供一个新的节点,你需要将所有应用程序迁移并关闭旧节点。 选项 4 包含许多节点。 由于其小巧的尺寸,这可能会导致资源短缺,因为一些基础组件将消耗部分 CPU 和内存。 此外,根据云提供商的不同,可能会有其他限制,比如可用的 IP 地址、带宽和存储容量。 另外,如果你有一个需要 1 vCPU 的应用程序,并且可能扩展到 1.5 – 2.0 vCPU,它可能会导致一个 完整节点崩溃。
然而,Kubernetes 每个节点使用多少资源呢? 默认情况下,对于 CPU,我们可以使用以下规则:
-
前 6% 核心
-
第二核心的 1% 核心
-
接下来 0.5% 两个核心
-
从第 5 个核心起的 0.25%
我们也有一些关于 内存的粗略规则:
-
前 25% 4 GB
-
以下内容的 20% 4 GB
-
接下来 10% 8 GB
-
接下来的 112 GB(最多 128 GB)的 6%
-
任何超过 128 GB 的 2%
-
如果节点内存小于 1GB,那么是 255 MiB
此外,每个 节点有一个 100 MB 的驱逐阈值。 如果资源完全利用且超出阈值,Kubernetes 将开始清理一些 Pods,以防止内存完全耗尽。 在 learnk8s,你可以找到一篇非常详细的博客 关于它(https://learnk8s.io/kubernetes-node-size)。
让我们可视化 这些数字:
2 vCPU 8 GB RAM | 4 vCPU 16 GB RAM | 8 vCPU 32 GB RAM | |
---|---|---|---|
Kubelet + 操作系统 vCPU | 70 m 或 0.07 vCPU | 80 m 或 0.08 vCPU | 90 m 或 0.09 vCPU |
Kubelet + 操作系统内存 | 1.8 GB | 2.6 GB | 3.56 GB |
驱逐阈值 | 100 MB | 100 MB | 100 MB |
可用 vCPU | 1930 m 或 1.9 vCPU | 3920 m 或 3.9 vCPU | 7910n 或 7.9 vCPU |
可用内存 | 6.1 GB | 13.3 GB | 28.34 GB |
表 4.3:不同节点大小的资源消耗和可用资源
从这些可用的 资源中,你还需要减去任何用于集群的资源,例如 DaemonSet
用于日志记录和监控, kube-proxy
,存储驱动,等等。
进行计算,如果有许多小节点,所使用的基础资源层比大节点的要大。 让我们来看一个例子。 两个 2vCPU 8 GB RAM 节点总共需要 140 m 的 CPU 和 3.6 GB 的 RAM,而一个 4vCPU 16 GB RAM 节点只需要 80 m 的 CPU 和 2.6 GB 的 RAM。 这些是为了提高可用性所付出的成本,但在仅对比可用资源时,单个节点需要分配给 Kubernetes 的资源较少。 然而,理想情况下,节点应该达到其最大使用率;我们并不是运行一个虚拟机,标准情况下有 80% 的未使用资源! 至少 80% 的节点利用率应该是实现最佳性能/价格比的目标。 这也是因为服务器的能源比例性。 CPU 所需的能源并不会与 工作负载呈线性比例增长。
假设一台服务器最多可以消耗 200 瓦特。 那么,大多数服务器在 CPU 利用率达到 50% 时需要消耗 150 到 180 瓦特之间的电量。 这意味着,服务器的利用率越高,能源/CPU 的利用率越好,这也是更加具有成本效益 和可持续的。
在选择集群节点大小时,我们必须考虑其他因素: 集群:
-
如果你提供的是少数几个大节点,但用户定义了反亲和性,使得每个节点上只能运行一个副本的 Pod,并且由于预期副本数过高导致节点不足,那么一些 Pod 将变得无法调度。
-
大节点往往资源利用率低,因此你会花更多的钱在一些你 并不使用的东西上。
-
如果你有太多小节点,并且不断遇到待处理的 Pods,每个集群都必须进行扩展,这可能会让用户不满,因为扩展新节点总是需要时间。 此外,如果应用程序持续运行在 资源限制下,也可能会影响其服务性。
-
许多节点会导致更高的网络通信量、容器镜像拉取和每个节点上重复的镜像存储。 否则,在最坏的情况下,你总是需要再次从注册表拉取镜像。 对于小型集群来说,这不是问题,但对于大量节点来说,这会变得 非常频繁。
-
节点越多,它们之间以及控制平面之间的通信就越频繁。 在某些请求量级下,这意味着需要增加 控制平面的节点大小。
找到合适的节点和集群大小本身就是一门科学。 极端的两端都不好。 首先,考虑一下你期望的工作负载类型。 如果不确定,可以从中等大小的节点开始,并根据需要调整节点大小。 此外,始终考虑预留一些额外的内存。 Kubernetes 每个节点的核心组件不需要大量的 CPU,但需要至少 2 到 3 GB 的内存,以及所有其他 默认组件。
解决方案空间
大多数公共云提供商都支持运行多种实例类型的 Kubernetes 节点。 这对于不同的使用场景非常有帮助,从隔离工作负载到优化利用率,或者从一种 CPU 架构迁移到另一种架构。 我们还讨论了 GPU 利用率,在这种场景下,你可以结合使用 CPU 节点运行非 GPU 工作负载,同时在 GPU 实例上进行模型训练。
为此,你必须正确管理和标记节点,并为用户提供透明的方法和支持,帮助定义他们的 亲和性设置。
为了确定合适的节点大小,learnk8s 还为你提供了一个实例计算器(https://learnk8s.io/kubernetes-instance-calculator),你可以在开始制作自己的 Excel 表格进行计算之前先考虑使用它。
此外,在定义集群规模时,尽管规模看似不那么相关,但通过选择合适的节点大小,你可以直接影响成本、利用率、应用可用性、用户体验,以及为弥补 潜在缺点所需做的额外实现。
总结
在这一章中,我们更深入地了解了 Kubernetes 的一些相关组件,它们构成了你平台的基石。 我们首先考察了 Kubernetes 是否是正确的选择,并且探讨了它为何通常是一个明智的选择。 Kubernetes 以承诺理论为核心,具有许多强大的功能来运行和扩展平台,因此它几乎是平台的完美基础。 从这里,我们探讨了 Kubernetes 的一些基本元素:存储、网络、CPU 架构和 GPU 支持。 在此背景下,我们了解了一些设计考量和在实现 Kubernetes 时可能遇到的问题。 尽管 Kubernetes 作为基础在不同环境下可能有所不同,但完全可以创建一个统一的体验。 然而,这将带来一些主要的缺点,比如失去某些云服务提供商的功能、灵活性, 以及可定制性。
接下来,我们讨论了如何在一个非常严格与高度灵活的系统之间找到平衡。 两者都可以被视为强大且可靠,但它们带来的是非常不同的挑战和问题。 因此,我们进行了一个简短的思维实验,以确定正确的集群大小和节点类型,然后我们通过讨论如何在用户空间实施保护措施来结束这一部分内容。 这帮助我们在用户空间内提供灵活性,但通过防止用户的不当行为和错误配置的服务,保护了平台。 我们在 这一章中学到了这些内容。
在下一章中,我们将重点关注平台的自动化。 除了基础设施,自动化是平台的关键组件,正如你稍后将看到的,它在长期中可能成为瓶颈和成本驱动因素。 你将学习如何设计一个适当的发布流程,如何在 CI/CD 和 GitOps 中实施该流程,并且如何使用这种组合来管理平台工件的生命周期。 我们还将向你展示如何有效地观察 这个过程。
进一步阅读
-
[1] Kubernetes 中的对象: https://kubernetes.io/docs/concepts/overview/working-with-objects/
-
[2] Karpenter: https://karpenter.sh/
-
[3] CSI 驱动程序 索引: https://kubernetes-csi.github.io/docs/drivers.html
-
[4] RISC-V Kubernetes 节点在 Scaleway 上: https://www.scaleway.com/en/docs/bare-metal/elastic-metal/how-to/kubernetes-on-riscv/
-
[5] SpinKube: https://www.spinkube.dev/
-
[6] SpinKube 概述: https://www.spinkube.dev/docs/overview/
-
[7] Nvidia – 改善 GPU 在 Kubernetes 中的利用率: https://developer.nvidia.com/blog/improving-gpu-utilization-in-kubernetes/
-
[8] DRA 与 GPU:
第五章:集成、交付和部署 — 自动化无处不在
第二章 包括了一个平台的参考架构,突出了诸如 开发者体验、 自动化与编排和 可观察性平面等层次。 第三章 从一个不同的角度结束了该参考架构,采用自上而下的方法,从 目的、 用户界面、 核心平台组件、 平台作为产品以及 成功的 KPI等方面进行了阐述。
大多数平台的构建目的是为了使开发团队能够更容易地发布软件,而无需处理构建、部署、测试、验证、安全、操作、发布或扩展软件的复杂性。 在本章中,我们将深入探讨平台的各个层次和组件,以便理解如何集中化并自动化发布软件所需的专业知识,并将其提供为 自助服务。
在本章结束时,我们将学习如何为软件制品定义端到端的发布过程,将 持续集成/持续部署 (CI/CD)过程与制品生命周期阶段对齐,使用 CI、CD 和持续发布工具自动化这些阶段,将这些工具集成到现有流程中,观察自动化效果,并通过 IDP 实现扩展。
因此,我们将在本章中覆盖以下主要内容:
-
介绍 持续 X
-
GitOps:从推动到拉取期望状态
-
理解容器和制品注册表的重要性,作为 入口点
-
定义发布过程 和管理
-
实现可持续的 CI/CD 流程用于 DevOps — 应用生命周期编排
-
内部开发者平台 (IDP) — 平台中的自动化巨兽
持续 X 介绍
如果这是你第一次听说 关于 持续集成 (CI) 或 持续交付 (CD),那么我们建议你查看一些关于这些基本概念的优秀文献。 Jez Humble 是 https://continuousdelivery.com/ 的维护者,并且是 《持续交付》 一书的共同作者, Dave Farley。如果你需要这个主题的速成课程,请查看他们的资料。 也有一些录制的讲座,提供了很好的概述,比如题为 持续交付听起来很棒,但它行不通 这里: https://vimeo.com/193849732。
为了确保我们有相同的理解,让我们快速回顾一下构建模块是什么以及为什么它在 软件交付中很重要。
持续 X 的高层次定义
自动化软件交付的 基本基础是正确的 配置管理 ,包括构建、部署、验证、操作和扩展我们系统所需的所有资产:代码、测试、部署和基础设施定义、依赖关系、可观测性、所有权等。 将所有这些资产放入版本控制中,可以生成可重复且可靠的输出,提供审计能力,允许我们回滚破坏性更改,并提供灾难 恢复能力。
Git,或任何 类型的 Git 版本,今天最有可能在软件组织中用于版本控制。 根据你使用的 Git 解决方案,你将看到额外的内建功能,如跨团队协作(问题跟踪、解决合并冲突等)、自动化(提交检查、交付流水线等)或报告(效率或 DORA 指标)。
现在我们已经建立了基础,让我们深入探讨 持续 X。
CI
持续集成 是 一种强调频繁和自动化地将代码变更集成到共享代码库中的实践。 当多个开发人员在同一个代码库上工作时,定期将这些变更合并非常重要,以验证代码是否能良好集成,并生成可以部署到环境中的工件(如容器镜像或二进制文件)。 持续集成的关键方面包括 以下内容:
-
自动化构建:代码 提交到版本控制系统时,会触发一个自动化构建过程,该过程会编译代码、运行测试,并且 生成工件
-
测试自动化:单元测试、集成测试以及其他检查会在构建 过程中执行 如果有任何测试失败,构建会被标记为失败 。
-
反馈回路:这为 开发人员提供了快速反馈,以便快速修复问题,从而提高整体代码质量 和 稳定性
持续测试和可观察性的验证
尽管持续集成已经强调了 自动化单元测试和集成测试的重要性,但我们要强调的是,在生命周期早期进行更多自动化测试和验证,将带来更好的质量和稳定性。 假设构建的软件提供了 REST API 或 UI 接口,应该针对这些接口进行基本验证,以确保功能的准确性(例如,API 是否返回正确的响应,使用无效参数时是否返回正确的 HTTP 状态码,API 调用是否有超时,或是否返回任何 HTTP 错误?)
可观察性 对验证系统健康至关重要 ,并提供了更快排查问题所需的数据。 作为持续验证 过程的一部分,我们必须验证已测试的构建是否生成有效且预期的可观察性数据。 我们应该验证是否生成了所有预期的指标、日志或跟踪数据,并且在运行这些基本的单元测试、集成测试或 API 测试后,是否没有明显的异常或离群值。
我们一直在强调, 可观察性必须是现代软件的非功能性要求。 这就是为什么持续集成应该验证是否生成了预期的数据。 如果没有,那么这就像是一个失败的单元或集成测试,你应该将构建 标记为失败!
对于 Financial One ACME 及其关键财务服务,我们应验证 以下内容:
-
API 是否能正确验证调用者的访问控制(例如,不能查询 其他用户的财务数据)?
-
API 是否不会记录任何机密数据,如信用卡号、用户名 或令牌?
-
API 是否能正确生成失败尝试的指标,以便在生产中用于警报潜在的 黑客攻击?
持续交付
在持续交付网站上定义(https://continuousdelivery.com/),“持续交付是将所有类型的变更——包括新特性、配置更改、错误修复和实验——以安全、快速且可持续的方式投入生产,或交到用户手中的能力。”目标是消除 部署中的失败恐惧。 与其进行不频繁的大规模发布,部署应该成为常规操作,持续发生。 此外,采用新的部署模式,如蓝绿部署、金丝雀部署或特性标记,可以进一步降低风险。 这些将在关于部署 与发布的章节中进一步讨论!
持续交付的一些方面如下:
-
自动化部署:来自 CI 的新 工件与其他更改捆绑,并自动部署。 部署定义是声明式的并且受版本控制,因此允许在任何环境中以更可预测、可重复和低风险的方式进行更新。 环境。
-
部署流水线:与 CI 相比,流水线允许更高级别的测试和部署验证。 在此,执行性能、安全性、可扩展性、韧性和用户体验等测试。 这不仅验证单一工件,而是整个部署 变更集!
-
质量门控与晋升:在部署流水线的末尾,所有测试结果充当质量门控 ,然后将变更晋升到下一个环境:从开发 到 质量保证 (QA),从 QA 到暂存,从暂存 到生产。
-
回滚与向前滚动:如果生产环境中的质量门槛未通过,可以通过回滚至先前版本控制的部署配置来触发回滚。 另一种策略是向前滚动,意味着问题被修复, 并且由于自动化部署,修复可以迅速部署,以避免 需要回滚。
持续部署 – 将部署与发布解耦
CD 以自动化和快速的方式部署更改。 然而,仍然存在更改导致失败的风险,需要回滚或向前滚动,正如 前面所解释的那样。
持续部署 更进一步,采用了新的部署模式,倾向于将更改的部署与向最终用户发布新功能集分离。 目前已经成熟的模式如下:
-
蓝绿部署:新的 部署(通常标记为 蓝色)将与现有部署(通常标记为 绿色)并行进行。 通过负载均衡器,可以将流量切换到蓝色。 如果蓝色出现问题,流量可以切换回仍在运行的实例,从而避免了回滚,并最小化了对 最终用户的影响。 如果一切顺利,绿色将变为蓝色,直到下次部署 来临。
-
金丝雀部署:类似于蓝绿部署,但在更精细的层次上。 这是新版本与旧版本并行部署的做法。 首先,它会部署到一小部分用户或一部分流量。 如果一切正常,分阶段的推出将继续,直到所有用户流量都使用新版本。 如果在阶段过程中出现问题,流量将切换回旧版本。
-
特性标志:与旧版本和新版本的负载均衡并排部署不同,特性标志 允许开发者“隐藏”新代码 在开关/切换后面。 在部署期间,新版本会覆盖旧版本,但不会执行隐藏的新代码。 通过细粒度的配置,可以为单个用户、用户类型、地理区域或任何其他服务消费者属性启用特性。 如果某个特性有问题,只需进行一次运行时配置更改,代码就会 重新变为非活动状态。
将部署与发布解耦,使团队能够更好地控制新功能的推出,从而最小化风险。 还有更多关于实现细节以及挑战的内容,但超出了本书的范围。 如果你有兴趣了解更多,可以查看 OpenFeature [1],一个 云原生计算基金会 (CNCF)孵化的项目。 OpenFeature 提供了一种标准的、特性标志管理、供应商无关的方式供开发者实现特性标志。 围绕它的社区也有很多关于渐进式交付的最佳实践,其中包括前面讨论的所有 模式 。
基础设施的持续 X
持续 X 不仅仅与应用程序代码或配置相关。 相同的概念应适用于任何基础设施定义。 作为平台,我们将需要某些基础设施组件。 无论是虚拟机、Kubernetes 节点、负载均衡器、域名系统 (DNS)、文件存储、数据库、虚拟网络、无服务器计算,还是其他任何允许我们运行核心平台以及将通过我们的 平台自服务部署、操作和管理的应用程序的组件。
就像应用程序代码一样,我们希望将基础设施需求配置为代码,进行版本控制,并应用相同的 CI 和持续测试、验证、 和交付。
GitOps 也是近几年出现的一个术语,它专注于自动化根据声明性定义的期望状态,通过 Git 进行版本控制来配置基础设施的过程。 我们将在本章的后续部分更详细地介绍 GitOps。 首先, 让我们从 基础设施即代码 (IaC)开始,讨论一些基础知识。
IaC
有许多不同的工具可以实现 IaC ,而且你很可能已经在你的组织中使用了一个或多个工具:Terraform、Ansible、Puppet、Chef、CloudFormation 以及 AWS 云开发工具包 (CDK)仅举几个例子。
这是一个非常简单的 Terraform 代码片段,用于创建一个类型为 c5.large的 EC2 实例
:
resource "aws_instance" "finoneacme_demoserver" {
ami = "ami-01e815680a0bbe597"
instance_type = "c5.large"
tags = {
Name = "IaCTFExample"
}
}
这里有一个使用 Ansible 创建 AWS S3 存储桶的例子:
- name: provisioning S3 Bucket using Ansible playbook
hosts: localhost
connection: local
gather_facts: false
tags: provisioning
tasks:
- name: create S3 bucket
S3_bucket:
name: finoneacme_bucket_dev
region: us-east-1
versioning: yes
tags:
name: bucketenv
type: dev
IaC 使我们能够定义我们期望的 IaC 状态。 这段代码可以进行版本控制,类似于应用程序代码,一旦部署,就会实现期望的基础设施配置。 与应用程序代码类似,我们可以使用 以下内容:
-
CI:用来验证我们所有的 IaC 是否有效。 IaC 工具通常具有“干运行”功能,可以验证是否没有错误,所有配置文件是否没有冲突或 依赖问题。
-
测试和部署验证:在 IaC 部署后,我们可以验证我们是否真正达到了期望的状态(例如,确保 EC2 实例已启动并运行,S3 存储桶可以 访问,等等)。
-
回滚或恢复:IaC 让我们可以回滚更改或恢复到先前的版本,因为一切 都被版本控制!
关于 IaC 的更多细节,包括版本控制策略(IaC 的存储位置),作者建议参考现有的书籍和博客,了解 该主题。
Crossplane – 平台和应用的 IaC
基础设施即代码(IaC)不仅仅局限于核心平台服务,它还适用于我们允许开发团队通过平台自服务部署的应用程序。 一个新的应用程序可能需要文件存储、数据库和公共 DNS,或需要部署第三方解决方案;这取决于其虚拟机,而该虚拟机可以从部署的应用程序访问,这些应用程序可能会驻留在 K8s 上。
你可以为 Terraform、Ansible 和 CDK 提供模板,开发人员可以将其添加到自己的代码库中,然后作为其应用程序持续 X 的一部分进行应用。
在云原生领域中,出现了一种工具来覆盖应用程序和基础设施编排,它就是 CNCF 项目 Crossplane [2]。除了提供许多不同的云供应商或甚至 Terraform 的提供程序外,它还提供了 Compositions。 Compositions 是一个用于创建多个托管资源作为单一对象的模板。 这使得平台团队能够为常见的应用程序架构定义这样的模板,然后让应用程序团队仅使用该模板来实例化正确的基础设施并部署 应用程序。
我们在 第二章 中讨论了一个自服务使用案例——自动化性能测试环境的配置。 我们可以定义一个组成模板,使开发团队能够像使用下面示例中的模板一样轻松使用它: 这个例子:
apiVersion: composites.financialone.acme/v1alpha1
kind: PerformanceTestCluster
metadata:
name: ptest-devteam1
spec:
clusterSize: "medium"
targetApp:
repoUrl: "https://financialone.acme/our-app-repo"
targetRevision: "2.5.1"
chart: "ourapp"
loadProfile: "spike-load"
observability: true
notifySlackOnReady: "#devteam1"
leaseTime: "12h"
这个 Composition 的定义是 PerformanceTestCluster
由平台工程团队与那些知道如何安装负载测试和可观察性工具的专家共同创建。 在前面的例子中,一个新的中型 K8s 集群将被配置,所请求的应用程序将使用引用的 Helm chart 进行安装,捕获可观察性数据(例如,配置 Prometheus 和日志抓取),并且将部署负载测试工具,以便能够运行尖峰负载场景。 一旦一切准备就绪,带有环境详情的 Slack 通知将发送给团队。 最后但同样重要的是,该环境将在 12 小时后根据强制性 leaseTime
字段的规定自动关闭。
前面的例子已经展示了将 IaC 集成到我们的持续 X 工作中时的强大功能!
Continuous X 作为我们平台中的系统关键组件
我们可以从许多不同的工具中选择来实施 Continuous X:Jenkins、GitLab、Tekton、Argo CD、Flux、Keptn、Crossplane、Selenium 和 k6
,仅举几例。 无论我们选择哪种工具,这些工具需要始终可用、具备弹性并且安全,因为它们是我们平台的支柱。 这些工具与任何支撑我们业务的软件同样重要。 想想 Financial One ACME。 如果开发人员需要发布一个修复,解决其金融软件中的关键生产问题,他们需要 Continuous X 能够完美运行。
为了确保在需要时这些组件能够可用,我们需要应用与我们业务关键应用相同的最佳实践:
-
默认安全:如果攻击者能够进入我们的 Continuous X 工具包,他们将打开进入我们平台管理的任何系统的大门。 由于这一关键性, 第七章 完全致力于构建安全和 合规的产品。
-
测试我们所做的每一个更改:假设我们使用 GitLab 作为我们的 Git 和 CI 工具之一。 我们必须对 GitLab 的部署配置进行版本控制,并通过相同的 Continuous X 流程运行,以验证每个新版本或配置更改。 如果必要,我们将回滚或前进,以防更新 引发问题!
-
部署高可用性:遵循这些工具的部署指南,以确保高可用性。 如果我们有全球分布的团队,我们需要确保将某些组件尽可能部署到离终端用户最近的地方。 另外,考虑零停机时间的升级选项并 遵循这些选项。
-
观察每个组件:每个工具都提供某种类型的遥测数据,用以指示健康状态。 例如,Argo CD 会暴露 Prometheus 指标,用于工作队列长度、Git 请求和同步活动。 这些数据能指示 Argo CD 是否仍然能够正常工作。 持续增加的工作队列深度表明 Argo CD 无法跟上所有请求,需要 进行排查。
-
服务级协议(SLAs)和故障警报:我们——平台团队——必须在最终用户报告问题之前就知道存在问题。 这就是为什么我们需要为每个组件设置 SLAs,并在系统未按预期工作时配置适当的警报。 实现这一点的最简单方法是设置针对每个工具的关键 API 端点的合成检查(例如,通过每五分钟运行一次的合成检查来验证 Jenkins UI 是否响应,这为我们提供了一个提前警告信号,以防 Jenkins 在其他人注意到之前就开始出现问题)。
以下是一个示例仪表板,突出显示了工具的关键健康指标,例如 Argo CD。 同样的原则必须应用于构成我们核心平台能力的所有其他工具!
图 5.1:监控并观察平台中每个工具的情况
平台组件与您的关键应用程序同样至关重要
平台的核心将围绕将更改部署到各种环境展开。 所有支持这些用例的工具——尤其是那些用于连续 X 的工具——需要具有高度可用性、弹性和安全性。 确保将相同的工程最佳实践应用于平台的所有组件!
现在我们已经回顾了连续 X(集成、测试、验证、交付、部署和发布)的核心概念,并讨论了所有配置(代码、部署配置、基础设施、可观测性等)必须遵循相同的原则,接下来是时候探讨如何利用这些概念为通过平台实现自主服务的团队提供支持。
GitOps – 从推送到拉取期望状态的转变
CI/CD 已经存在多年。 《持续交付》一书最初发布于 2010 年——容器(通过 Docker 推广,从 2013 年开始)和容器编排平台(如 Kubernetes,从 2014 年开始)出现之前的几年。
快速推进到 2024 年,当本书首次发布时;我们生活在一个以下情况的世界: 现状是:
-
Git 是事实的源头 。 它包含了所有的内容作为代码:源代码、测试、基础设施和部署定义、可观测性、所有权等。
-
CI/CD 是构建、测试并 打包容器 / 开放容器倡议 (OCI) 镜像 来自源代码 Git 仓库,并将它们发布到工件注册中心(例如,Harbor)。
-
GitOps 持续尝试应用部署 Git 仓库(例如 Helm 图表)中声明的最新期望状态,并将任何额外的配置(例如,观测性)推送到 相应的工具中。
上述描述并不是适用于所有情况的统一模型。 GitOps 在每个组织中的实施方式都会有所不同。 如果你搜索 什么是 GitOps?
,你会找到许多不同的变体,如 以下内容:
-
分离 CI 和 CD:CI 负责发布容器,CD 负责发布打包后的工件,如 Helm 图表
-
单一 Git:将一切作为代码(源代码、测试、部署、观测性等)存储在单一的 Git 仓库中
-
推送 GitOps:通过流水线或工作流推送期望的状态,而不是通过 GitOps 操作员 将更改拉入目标环境
在接下来的章节中,我们将重点介绍一种偏向于 拉取 (将配置拉入目标环境 ) 模型,而不是 推送 (从外部工具推送配置到目标环境 ) 模型,该模型可以使用 CNCF 工具(如 Flux 或 Argo CD)实现,如下图所示,该图取自 Codefresh 学习中心的 GitOps:https://codefresh.io/learn/gitops/。
图 5.2:GitOps 流程基础,如 Argo CD 或 Flux 等 GitOps 工具所推广的那样
是否选择使用自动化流水线或工作流推送更改到目标 Kubernetes 环境是由你决定的! 为了让决策更容易,我们将深入探讨每个阶段的更多细节,并了解 在每个阶段中应该遵循的一些最佳实践!
阶段 1 – 从源代码到容器镜像
迁移到 GitOps 并不 改变我们如何从 Git 中版本控制的源代码构建应用程序。 在构建新工件之前,一个好的做法是自动化依赖检查和更新。 例如, Renovate Bot [3]工具可以与 Git 集成,并在发现过时的依赖、第三方库或其他依赖项时创建 Pull Requests。 这些工具非常有用。
一旦我们在 Git 中更新了代码, CI 就有了和以前一样的任务:它创建一个工件,最有可能是一个容器镜像,并将其推送到容器注册表! 这个 CI 可以根据需要触发,或者在某些分支的提交、Pull Requests,或者任何其他合理的触发条件下触发。 这是对组织来说非常重要的!
有很多不同的工具可以完成这项工作:Jenkins、GitLab、Azure DevOps、GitHub Actions、Bitbucket Pipelines 等等。 也有各种容器注册表选项。 稍后我们将在本书中讨论容器注册表及其重要性,它们和我们平台的所有组件一样,都是我们 未来平台成功的关键!
一旦 CI 成功编译源代码、执行单元测试,并对代码进行任何额外的质量检查后,便可以将二进制文件打包成容器镜像。 构建容器镜像有很多最佳实践;从只将单个服务打包到容器中、谨慎使用公共基础镜像,到构建尽可能小的镜像。 遵循所有这些规则将提高容器镜像从 CI 到生产环境的成功率。 这里有两个显而易见但非常重要的实践,我们希望在本章提到(更多信息,请参阅之前提到的书籍):
-
finoneacme/fund-transfer:2.34.3
,其中finoneacme/fund-transfer
是名称,2.34.3
是标签。 在你构建镜像时,标签由你决定,但你应该遵循一致的命名规则。 行业中有两种常见的做法:-
MAJOR.MINOR.PATCH
。每个次要或修补版本号必须是向后兼容的更改。 结合版本名称latest
,这是指向最新可用版本的默认值,你可以方便地访问特定的镜像(例如,X.Y.Z
)。 它还允许选择特定次版本的最新修补版本(例如,X.Y
)或某个主版本的最新次版本和修补版本(例如,X
)。 -
git commit
哈希作为版本,而不是跟踪语义版本。 提交哈希 在镜像上也立即告诉我们是哪个源代码提交负责生成了这个容器镜像。 对于我们的镜像,这可能意味着它被标记为 这样:financeoneacme/fund-transfer:d85aaef
。
-
-
log4j
漏洞。 这就是为什么安全性必须超越 CI 流程中的静态检查,并贯穿整个工件的生命周期。 这一点将在 第七章中进一步探讨 ,该章节深入探讨了构建和运营 安全产品。
现在我们已经将容器镜像上传到我们的 容器注册表中,我们可以在 部署定义中使用它。
第 2 阶段 – 从容器镜像到富含元数据的部署工件
容器镜像现在需要找到进入部署定义的路径。 看 Kubernetes,我们需要一个清单文件来定义我们的部署定义,之后可以将其应用到我们的 K8s 集群。
在 第三章中,我们强调了将 元数据(所有权、可观察性级别、通知)添加到我们的部署文件中,将有利于许多自助服务用例,例如 作为自助服务请求日志 的用例。 因此,除了我们的源代码,我们还需要对我们的部署定义进行版本控制,并且应强制执行最小元数据集。 这使我们的平台自助服务用例(例如,谁部署了哪些服务,属于哪个应用程序)以及遵循一般基础设施最佳 实践(例如,定义请求和资源限制)成为可能,正如你在以下清单中看到的:
mainfest.yaml(Kubernetes 部署文件)
apiVersion: apps/v1
kind: Deployment
metadata:
name: fund-transfer-service
spec:
…
template:
metadata:
annotations:
# team ownership
owner.team: dev-team-backend
app.kubernetes.io/name: fund-transfer-service
# app-context
app.kubernetes.io/part-of: backend-services
app.kubernetes.io/version: d85aaef
# log level
observability.logs.level: info
# log source
observability.logs.location: stdout
…
spec:
containers:
- name: fund-transfer
image: "financeoneacme/fund-transfer:d85aaef"
imagePullPolicy: IfNotPresent
resources:
# specify limits
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "100Mi"
cpu: "2"
…
部署定义可以是一个清单文件或一组清单文件,因为我们可能还需要额外的 K8s 资源,例如服务或 Ingress 定义。 与我们将清单文件组织在与服务源代码相邻的子文件夹中的做法不同,我们可以选择使用模板化或 打包框架和工具,如 Kustomize [5] 或 Helm [6]。
考虑如何组织和版本控制所有“作为代码”的文件是非常重要的。 有几种策略,每种策略都有优缺点,并且都有很好的文档支持。 如果想深入了解,作者建议阅读关于仓库和目录结构的不同模式。 以下部分只是对你在深入研究这一主题时必须知道的内容的简要概述!
单一仓库与多仓库
在设计 Git 仓库结构时,你有两个常见的选择,业界称之为单一仓库(monorepo) 或多仓库(polyrepo);一个 Git 仓库或多个由团队 或职能划分的仓库:
图 5.3:单一仓库与多仓库——两种模式的优缺点
-
单一仓库:在单一仓库模式中,所有配置文件(代码、基础设施、可观察性等)都存储在一个 Git 仓库中。 这种模式适用于每一个潜在的环境(开发、测试、预发布、生产),也就是说,所有配置都在同一个 Git 仓库中,通过文件夹来区分。
单一仓库的好处是所有内容都集中在一个地方。 缺点是,这个仓库最终会变得非常庞大,当像 ArgoCD 或 Flux 这样的工具需要不断扫描整个仓库以检查变化时,可能会引发性能问题。 它还使得应用程序和基础设施拥有者之间的关注点分离变得更加困难。
-
多仓库:在多仓库模式中,我们有多个仓库,这使得分离关注点变得更加容易。 这些可以是按团队划分的仓库(例如,应用团队 A、B、C),按环境划分的仓库(开发、测试、生产等),按租户划分的仓库(在多租户系统中),或者按组织边界划分的仓库(如应用团队、平台团队等)。
好处在于更好的关注点分离。 缺点是,最终我们会管理大量的 Git 仓库,这使得在所有配置文件中确保一致性和整体有效性变得更加困难。这些配置文件分布在多个仓库中,组成了整个配置。
目录结构 – 遵循 DRY 原则
无论是单一仓库还是多仓库,每个 Git 仓库都需要有良好的目录结构。 通常,我们会看到它反映了组织结构,将开发团队与管理基础设施或平台的团队分开。 一个好的实践是遵循 不重复自己(DRY) [7] 原则。 这个理念的核心是找到最佳结构,以避免在不同位置复制/粘贴常见的 YAML 设置。 这就是像 Helm 和 Kustomize 这样的工具提供帮助的地方。
以下结构示例灵感来源于一篇来自 Red Hat [8] 的 GitOps 博客,展示了平台管理员的配置和应用团队使用像 Kustomize 这样的模板工具的配置。 该结构可以用于一个单一的 monorepo,通过文件夹进行分离,或者按照 polyrepo 模式 进行配置。
平台团队 | 开发团队 |
---|
| ├──
bootstrap``│ ├──
base``│ └──
overlays``│ └──
default``├──
cluster-config``│``**├──**
gitops-controller
**│ ├──
identity-provider
│ └──
image-scanner
└──
components
**├──**
applicationsets
**├──
applications
**└──**
argocdproj
**** | └──
deploy``├──
base``└──
overlays``├──
dev``├──
prod``└──
stage
|
表 5.1:Kustomize 等工具可能使用的目录结构
现在我们已经了解了组织仓库和目录结构的不同方式,接下来需要学习新容器镜像如何从 第一阶段 进入我们仓库中的部署定义!
用新容器镜像版本更新清单
最后一步是 第二阶段 将 CI 构建出的新镜像版本推广到我们的部署文件中。 这可能是更新部署清单,如前面的示例所示,或者在使用 values.yaml
文件时更新 Helm 图表。
由于这些配置文件是版本控制的,存储在 Git 仓库中(无论是单一仓库还是多个仓库,都没有关系),我们应该遵循常规的 Git 工作流,创建一个 Pull Request 来推广镜像的更新。 我们可以通过以下两种方式自动创建该 Pull Request:
-
CI 流水线中的步骤:作为 CI 的最后一步,可以打开一个 Pull Request,将新镜像标签推广到相应的部署定义仓库和正确的目录结构(例如,如果这是一个新构建的镜像,则更新开发覆盖目录中的值)
-
容器注册表中的 Webhook:当注册表接收到 CI 的新镜像时,它可以触发一个 Webhook,允许 我们在部署 Git 仓库中创建相同的 Pull Request
以下是我们在前面示例中看到的 Kubernetes 部署 的更新版本信息。 你可以将其与之前示例中的值进行对比:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fund-transfer-service
spec:
…
template:
metadata:
annotations:
owner.team: dev-team-backend
app.kubernetes.io/name: fund-transfer-service
app.kubernetes.io/part-of: backend-services
app.kubernetes.io/version: a3456bc
observability.logs.level: info
observability.logs.location: stdout
…
spec:
containers:
- name: fund-transfer
image: "financeoneacme/fund-transfer:a3456bc"
…
现在,我们知道了由 CI 构建的新镜像是如何进入我们各自仓库中的部署定义的。 我们之前已经看到过 GitOps 操作符在本章中的图像。 现在,是时候深入了解 GitOps 操作符如何将我们在 Git 中的目标状态应用到我们的 目标集群了!
第三阶段 – GitOps – 保持你期望的部署状态
现在我们已经将所有内容作为代码存储在 Git 中,是时候讨论如何将这些配置应用到目标环境了。 一种方法是使用与 CI 相同的推送模型。 我们可以在 Git 仓库发生更改时触发交付管道,然后将最新版本的清单或 Helm 图表应用到目标环境。 然而,在本章中,我们专注并倾向于使用拉取模型,这由 GitOps 操作员或 GitOps 代理实现,例如 Argo CD、Flux 以及一些 商业产品。
GitOps 操作员简介 – 将 Git 同步到 K8s
GitOps 操作员 持续调和并确保我们在仓库中声明的期望状态与目标环境中实际运行的状态匹配。 如果 Git 中的状态与目标环境中的状态不匹配,操作员会检测到一个 不同步 的情况。 这种情况可能发生在 Git 配置更改时(例如,新的镜像可用并导致清单更新)。 也可能发生在有人修改了 K8s 中的配置时(例如,通过手动更新或使用不同的自动化工具)。 以下是一个高层次的概述,展示了 GitOps 操作员的角色: GitOps 操作员:
图 5.4:GitOps 操作员将 Git 中的期望状态与 K8s 中的实际状态同步
根据 GitOps 操作员工具的不同,调和过程中的配置选项也会有所不同。 值得查看云原生生态系统中两个重要工具的文档: Argo CD [9] 和 Flux [10]。另外,请熟悉配置元素,因为一些工具有项目和应用程序的概念,而其他工具则只有 源的概念。
理解调和 – 保持 K8s 与 Git 的同步
从本质上来说,GitOps 操作会从源头(如 Git 仓库、Git 仓库中的文件夹、OCI 仓库、S3 存储桶等)获取项目或应用的期望状态,并与目标 K8s 集群上当前运行的状态进行比较。 这两个状态可能是 同步的 或 不同步的。当状态是 不同步时,GitOps 操作有不同的选项来同步这些状态——即:将当前状态调整为匹配 期望状态:
-
手动同步:通过 命令行接口 CLI (CLI) 或者 用户界面(UI),可以触发 GitOps 操作来同步这 两个状态。
-
自动同步:一旦 系统出现不同步,GitOps 操作会尝试 自动同步。
-
同步计划/窗口:可能会有一些时候你希望或者不希望同步发生。 这时,同步计划或同步窗口就发挥作用了,它们可以阻止或允许同步 发生。
-
同步失败:有时 GitOps 操作可能无法应用期望的状态。 这可能是由于配置文件错误(例如,引用了无效的镜像)。 也可能是因为基础设施问题(例如,K8s 的资源不足)。 还可能是由于工具之间的冲突(例如,自动扩展工具,如 HPA/KEDA,正在更改副本或资源限制)。 重要的是要意识到这种状态并正确处理。 请参阅最佳实践部分以获取一些 额外的建议!
现在我们了解了同步的基本知识,接下来让我们看看不同的 GitOps 操作 部署 模式吧!
GitOps 操作模式 – 单一模式、中心辐射模式和多对多模式
最简单的版本 – 也是许多人开始时可能会使用的模型 – 是 monorepo 方法,GitOps 操作从单一目标环境中拉取,如我们在 图 5**.3 中看到的两种模式。 虽然单一目标是一个常见模式,特别是当你刚开始使用 GitOps 时,我们还有其他模式需要 快速介绍一下。
GitOps 也可以设置为一个中心 GitOps 操作员,它将 Git 中声明的期望状态保持一致,并通过推送这些更改与多个目标环境进行同步。 这被称为 集线器-辐射模型 hub-and-spoke model。另一种选择是 多对多模型,其中每个目标环境都有自己的 GitOps 操作员,它会不断地将期望状态与其自身集群中的状态进行同步,如下图所示:
图 5.5:集线器-辐射模式和多对多 GitOps 操作模式
现在我们已经讨论了 GitOps 中的主要阶段,让我们总结一下,并快速看一下几个 最佳 实践。
GitOps 最佳实践
这个列表并不完整,但应该能为你在定义 GitOps 过程时提供一个良好的起点:
-
将配置与源代码库/文件夹分离:建议将实际的源代码与部署定义分开。 可以放在不同的仓库中,或者在同一仓库中放在不同的文件夹里。 为什么要这么做? 这是为了清晰地分离关注点和访问权限。 这使得 Git 触发的操作变得更加简便,因为在部署配置文件中的更改应该触发与源代码文件中不同的操作。 如果 CI 修改相同的仓库,它避免了可能的无限循环变化! 仓库越小,GitOps 工具扫描所有文件以确定期望状态所需的工作就越少。
-
正确的同步设置 – 拉取与 Webhook:GitOps 工具提供不同的同步设置,用于扫描源系统(如 Git)和调度触发同步。 对于扫描,了解默认的拉取频率非常重要(例如,默认情况下,Argo CD 每三分钟拉取一次所有 Git 仓库)。 Argo CD 和 Flux 还可以配置为接收来自 Git 系统的 Webhook,这样就将拉取机制替换为推送机制! 这非常重要,因为随着源系统(Git、工件仓库、S3 存储桶等)数量的增加,从 GitOps 工具到这些系统的 API 调用次数也会增加。 因此,最好监控 GitOps 工具到外部系统的调用次数,以便在行为发生剧烈变化时及时收到警报。 行为的变化可能是由于不小心更改了默认设置的配置。 大多数工具提供 Prometheus 或 OpenTelemetry 指标,可以通过你的 可观测性工具进行观察!
作者见过由于 GitOps 工具在同步过程中产生过多负载,导致 API 限制甚至使 Git 系统崩溃的配置! 同步!
-
并非每个配置都必须在清单中:由于 GitOps 跟踪期望状态与实际状态,因此重要的是将一些可能由不同工具管理的配置排除在清单之外。 以副本为例。 如果你使用 HPA 或 KEDA 等工具来自动扩展 Pod,则不希望在清单中设置静态的副本数。 这会导致 GitOps 工具在 HPA/KEDA 进行任何更改时检测到不同步状态。 因此,导致两个自动化工具彼此竞争。 彼此竞争。
-
GitOps 通知以处理同步状态:GitOps 工具在同步状态变化时提供通知。 这通常发生在 GitOps 检测到不同步状态时,完成同步时,或当同步失败时出现问题。 在这些情况下,收到通知非常重要,因为你需要确保能够处理同步失败或在最新更新成功同步时将信息反馈给开发团队。 团队。 已 同步。
为了接收通知,GitOps 工具会创建 Kubernetes 事件,你可以将这些事件导入到你的可观测性解决方案中,并对其进行反应/警报。 GitOps 工具通常还提供某种类型的本地通知功能,当发生特定事件时,可以触发外部工具。 例如,Flux 提供了警报功能,而 Argo CD 提供了通知的概念。 这两者都允许你在某些需要关注的事件发生时,发送 Slack 消息或触发其他外部工具!
GitOps——从推送到拉取的转变
GitOps 将 Git 的力量从应用代码扩展到一切皆代码。 虽然 CI/CD 仍然侧重于构建工件,但 GitOps 提供了一种优雅的方式,将所需的部署状态拉取到软件开发生命周期中的任何目标环境。 系统的变更只能通过 Git 完成,借助变更可追溯性、回滚到先前版本的功能,并通过 Pull Requests 强制执行审查流程。
现在我们已经覆盖了 GitOps 的基础内容,我们应该看看它如何在构建现代平台中对我们有所帮助。 作为平台团队,我们可以通过在 CI/CD 中使用自动化以及容器注册表来集中执行最佳实践(版本控制、策略等),从而减少错误变更请求的可能性。 通过 Git,每个变更和部署都可以追溯到 Git 提交,这使得故障排除变得更加容易,同时它还提供了额外的自服务功能(例如,当你的变更已经同步到目标环境时,通知开发团队,或通过 git commit
哈希值 通知他们最新版本存在问题)。
现在,让我们花些时间讲解容器注册表,因为没有它们,我们将无法发布或分发开发团队利用平台作为自服务所生成的任何镜像。 一个自服务平台。
理解容器和工件注册表作为入口点的重要性
容器和工件注册表值得单独成章,因为它们是现代云原生平台的核心构建块之一。 然而,我们将尽量提供相关知识,以帮助你跟随本书后续章节的内容。
我们区分公有注册表和 私有注册表:
-
公共注册中心 通常由个人或小团队使用,这些用户希望尽可能快速地启动并运行他们的注册中心。 然而,某些时候,值得考虑 私有注册中心。
-
私有注册中心 提供 几个关键功能,如高效的镜像存储、漏洞扫描、将镜像复制到其他注册中心、在拉取镜像时强制执行访问控制以及通知其他工具有关更新的内容,最终目标是使镜像能够快速、轻松地被需要它们的环境部署。
虽然私有容器注册中心通常仅在内部访问,用于推送新的构建并由我们的 GitOps 工具拉取,但我们也可以将注册中心开放给 公众。这里的 公众指的是 允许第三方供应商将他们最新的镜像或部署构件推送到此公共端点。 随着组织依赖于第三方软件——想想你自己部署的任何现成软件产品——我们可以在这些软件部署到内部系统之前,利用相同的漏洞扫描、复制和访问控制过程。 例如,金融公司 ACME 可能允许他们的第三方供应商为开发票务系统推送新版本到该公共端点。 一旦扫描并验证完毕,这些版本就可以部署到运行所有开发工具的内部 K8s 集群中。
以下图示展示了容器注册中心如何集成到端到端交付过程中,该过程从推送一个新的构件(第三方或 CI/CD)开始,直到该新构件被部署到目标环境:
图 5.6: 容器注册中心 – 我们平台的心跳
虽然从不同的开源和商业注册中心供应商那里可以获得大量深入的信息,但我们想简要概述一下注册中心如何融入我们的平台工程架构、为什么某些概念很重要,以及我们如何最好地将容器注册中心作为易于使用的自助服务提供给终端用户!
从容器到构件注册中心
在深入探讨 过程之前,我们需要快速指出,容器注册表——尽管名字有这种暗示——并不局限于容器镜像。 大多数容器注册表通常支持 OCI [11] 镜像标准。 在过去的几年中,容器注册表扩展了对非容器工件的支持,如 Helm Charts、压缩版的 Manifests 或基于 Kustomize 的模板。 这种扩展还带来了工件注册表名称的变化,因为这些工具管理的是一般的工件,而不仅仅是 容器镜像。
但这还不是全部。 工件注册表、开源软件或 SaaS (即 软件即服务) 服务,通常还具备其他功能,如访问控制、区域复制、审计日志、策略执行、安全扫描、通知等 更多功能。
构建和推送工件到注册表
上传 (推送) 和 下载 (拉取) 镜像可以使用 Docker(或其他工具,如 Podman)命令来完成。 在此之前,你需要先对注册表进行身份验证(无论是私有还是公共注册表,例如 Docker Hub)。 一旦认证通过,推送和拉取就非常简单,如以下代码所示:
通过 docker 命令与容器注册表交互
export REGISTRY=registry.finone.acme
# Authenticate
docker login -u YOURUSER -p YOURPASSWORD $REGISTRY
# Build an image
docker build -t $REGISTRY/financeoneacme/fund-transfer:1.2.3 . # Push an image
docker push $REGISTRY/financeoneacme/fund-transfer:1.2.3
# Pull an image
docker pull $REGISTRY/financeoneacme/fund-transfer:1.2.3
在 CI 过程中构建新镜像时,遵循最佳实践来处理镜像标签和元数据非常重要。 因此,OCI 也在其镜像规范中列出了建议的注释 [12] ,如 created
, authors
, url
,以及 documentation
(所有前缀为 org.opencontainers.image.
)。
除了 API 接口外,注册表通常还提供 一个 用户界面 (UI),使得查看已上传的镜像、它们占用的空间以及根据注册表的管理功能提供所有这些功能的配置方面(例如,创建项目、管理用户、指定策略、配置webhooks 等)变得更加容易。
管理已上传的工件
注册表通常 管理项目中的工件。 项目可以是注册表中的私有或公共,意味着公共项目中的工件可以被任何能够访问注册表的人访问,而私有项目只能由授权用户访问。 这引出了用户管理和访问控制,可以在项目级别定义,也可以输入访问日志。 注册表会为每次推送和拉取创建日志,以便有良好的审计追踪。 CNCF Harbor 是一个非常流行的容器注册表,它还提供了关于所有这些功能的良好文档。 我们建议你阅读公开的文档 [13] 以了解所有这些功能。 需要记住的关键是如何将你的镜像组织到项目中,以及你将谁授予访问权限。 如果我们还希望允许外部第三方将他们的镜像推送到我们的注册表中,可以为他们创建特定的用户,允许他们上传到相应的容器项目 !
漏洞扫描
第七章 更详细地讨论了安全性,但在这里需要提到的是,像工件注册表这样的中央组件,每个工件都必须通过它,是进行静态漏洞检查的完美场所。 注册表通常提供开箱即用的 扫描功能,或者允许根据工件类型集成额外的工具。 这些工具将在上传新镜像时调用,以扫描镜像的到达,或者按计划进行扫描,以确保镜像被扫描并在持续的基础上重新扫描。
订阅注册表中工件生命周期事件
一个 artifact 通常会经历 从初始上传(推送)、安全扫描、复制到其他仓库、下载(拉取)到最后被删除的不同生命周期阶段,直到它不再需要为止。 以 Harbor 为例,我们也可以使用 Webhook 订阅这些生命周期阶段。 这为许多有趣的使用案例提供了可能,例如 以下几点:
-
安全:通知安全团队关于 新的漏洞
-
存储:清理旧镜像以防存储配额 达到限制
-
部署:从 CI/CD 或 第三方供应商上传的新镜像
-
审计:跟踪谁在拉取 哪些 artifact
作为参考,以下是可以与 Webhook 一起使用的完整生命周期事件列表: artifact deleted
, artifact pulled
, artifact pushed
, chart deleted
, chart downloaded
, chart uploaded
, quota exceeded
, quote near threshold
, replication finished
, scanning failed
, 和 scanning finished
。
保持性和不可变性
仓库可能会迅速 膨胀—如果没有良好的清理策略, 可能会导致存储成本高昂。 因此,建议清理不再需要的旧镜像。 因此,一些仓库提供开箱即用的保留策略,您可以定义匹配特定规则的镜像以及它们将保留多长时间。 一旦镜像超过保留期,它们将 被删除。
另一个使用案例是, 默认情况下,每个人都可以上传一个新镜像,且具有相同的 镜像标签,导致之前的镜像版本没有标签。 为了防止这种情况,某些仓库提供不可变性规则,禁止标签 从 现有镜像中移除!
监控我们的仓库
由于注册表是我们平台的核心 ,我们需要确保它们保持健康。 如果注册表停止处理推送或拉取请求,无法执行漏洞检查,无法复制到其他注册表或发送通知,那么我们就会遇到问题! 这意味着关键更新无法及时进入目标环境。 这可能意味着一个安全漏洞——尽管通过新镜像修复——无法得到修复,因为新镜像无法传送到受感染的环境。 这也可能意味着我们无法在承诺的时间内 发布新特性。
为了观察我们的注册表健康状况,我们可以监控它们的各种健康指标。 在自托管的注册表中,这些指标通常通过 Prometheus 或自定义的 REST API 暴露。 在 SaaS 托管的注册表中,这些指标通常通过供应商的监控服务 API 暴露,例如 AWS CloudWatch。
一个好的参考是 Harbor,这是我们之前提到的 CNCF 项目。 它通过 Prometheus [14],暴露了许多重要的指标,包括项目和项目内仓库的数量、使用的存储、执行和排队的任务数量,以及 Harbor API 的性能指标(例如,推送或拉取工件需要多长时间)。
图 5.7:监控我们的注册表,及早识别潜在问题
Harbor 也是 OpenTelemetry 的早期采纳者,这是一个 CNCF 项目,最初为云原生社区引入了分布式追踪的标准。 Harbor 提供了 一个 OpenTelemetry 导出器 [15],生成带有更详细信息的追踪数据,既可用于健康监测,也可用于故障排除!
注册表——我们所有工件的核心中心
容器或工件注册表 是所有构建和部署的软件的核心 中心。 它 提供了一种集中管理、扫描、分发和执行访问控制的方式,确保所有软件在部署前进行管理。 因此,它必须被视为一个高度关键的组件,必须进行监控以确保可用性和弹性,同时本身必须是安全的!
现在我们已经讨论了我们的工件注册表的重要性,让我们继续看看所有这些组件是如何与我们的 发布流程 集成的。
定义发布流程和管理
我们已经涵盖了构建和部署软件所需的所有基本模块。 CI 构建新的容器镜像或工件,并将其推送到注册表。 我们知道注册表具有扫描漏洞、复制到其他注册表、通知其他工具的能力,以及强制 访问控制。
我们还了解了 GitOps 及其通过拉取方法确保目标环境中定义的期望部署状态(清单文件、Helm 图表、Kustomize 等)。
我们现在讨论的是如何定义和执行完整的端到端发布流程,以及如何管理从初始创建、初始部署、推广到其他阶段,以及更新到新版本,直到软件不再 需要为止!
下图突显了将新镜像推送并复制到其他注册表只是问题的一部分。 镜像的新版本还需要在管理单一或多个 Git 仓库中的部署定义中进行引用。 在拥有多个阶段(开发、QA、生产)和在每个阶段拥有多个区域或集群时,我们还需要在每个阶段和 每个区域/集群之间推广该新版本!
图 5.8:发布流程 – 从初始推送到部署和推广,从阶段到阶段
在上图中, values.yaml
代表 Helm 图表的值。 当然,这只是简化版,因为我们之前讨论过的值会有更多(例如所有权、应用上下文、日志级别、 git commit
哈希、容器 注册表等)。
现在,让我们看看如何完成这些部署更新,各个阶段之间应该发生什么,我们有哪些推出选项,以及如何最好地跟踪实时库存,了解是谁在何时部署了哪些内容!
将部署更新到新版本
有 多种方法可以更新 values.yaml
文件,用于初始开发阶段,并且它如何被推广到下一个阶段。 根据组织使用的工具和成熟度或流程,这可以通过不同的方式实现。 选项如下: 如下:
-
作为 CI 一部分更新:一旦 CI 将新镜像发布到注册表,它可以打开一个 Pull Request,更新第一个阶段仓库中的版本。
-
通过注册表 Webhook 更新:当一个新镜像上传到注册表时,可以使用 Webhook 打开一个 Pull Request。 这种方法类似于让 CI 来完成。 然而,它解耦了这个过程。 这也能够独立于使用哪个工具(例如,CI)或哪个创建者(例如,第三方供应商)推送新 镜像版本。
-
计划更新:每小时、每天早上 8 点,或者任何其他时间表都可以用来创建一个 Pull Request,拉取注册表中的最新版本信息。 这提供了持续更新和按计划批量更改的功能。 按计划进行。
-
手动 Pull Requests:作为自动化 Pull Requests 的前奏,或强制 强制手动批准,版本更新也可以 通过手动完成。
批量变更以应对依赖关系
当处理可以独立部署的简单应用程序或微服务时,更新单个文件可能已经足够。 通常,我们必须将多个变更合并为一个变更请求。 这通常发生在变更依赖于其他组件的变更时。 例如,我们的 Finance One ACME 可能会出现新版本的 fund-transfer
服务也需要新版本的 account-info
服务,而这个服务又需要基础设施更改。 你可以看到,它很容易变得复杂,而且完全自动化变得更加困难。 自动化变得更加困难。
这些变更很难自动化,通常会回退到某个发布管理团队,由他们手动解决这些 依赖关系。
还有其他解决方法。 一种方法是使用包管理器,例如 Helm,其中 Helm 图表可以 包含部署整个应用所需的所有配置。 Helm 图表 还可以成为上传到 注册表的工件。
我们在前面的章节中看到的另一种方法是使用像 Crossplane 这样的工具,它 提供了基础设施即代码(IaC)和应用程序即代码。 以下是使用组合来部署一个包含多个组件的应用程序的示例,组件包括业务逻辑、缓存、入口和 数据库:
apiVersion: composites.financialone.acme/v1alpha1
kind: FinancialBackend
metadata:
name: tenantABC-eu-west
spec:
service-versions:
fund-transfer: 2.34.3
account-info: 1.17.0
redis-cache:
version: 7.4.2
name: transfer-cache
size: medium
database:
size: large
name: accounts-db
region: "eu-west"
ingress:
url: "https://tenantABC-eu-west.financialone.acme"
正如我们所看到的,有多种方法可以解决这个应用程序 依赖性问题。
当我们面临跨应用程序依赖或依赖于独立部署和更新的共享服务时,这将变得更加棘手。 遵循最佳实践来定义这些服务之间良好而清晰的 API,并确保主要版本之间的向后兼容性,将使这变得更加容易。 要了解更多信息,可以查看现有的文献或 CNCF TAG 应用交付 [16] 在管理 应用程序和部署依赖方面所做的工作。
部署前和部署后的检查
一旦我们准备好并提交了所有的部署配置更改到 Git,我们就可以准备将其部署到目标集群。 如本章前面所解释的,我们可以选择使用推送模型(例如,交付管道部署我们的更改)或拉取模型(例如,GitOps 操作员将 Git 中的最新版本同步到 目标集群)。
无论是哪种方法,我们都希望进行一些部署前和部署后的检查,以回答如下问题:
-
部署前:我们准备好部署 该更改了吗?
-
所有的 外部 依赖是否已准备好?
-
所有新的镜像是否已成功扫描并且没有 漏洞?
-
目标环境中是否没有正在进行的维护或部署隔离?
-
所有需要批准部署的人 都批准了吗?
-
-
部署后:部署 是否成功?
-
更新的服务是否可用并成功 处理请求?
-
系统是否满足所有功能性和 非功能性要求?
-
所有服务是否符合它们的 SLA 并且 服务级别 目标 (SLOs)?
-
是否每个人都已被通知 关于部署的情况?
-
该部署是否可以升级到 下一阶段?
-
预部署检查可以通过多种方式实现。 其中一些可以作为 Pull Request 流程的一部分来实现(例如,当所有预部署检查未完成时,Pull Request 不能合并)。 它们也可以在部署管道中进行验证,或者作为 GitOps 工具中的预同步钩子来实现。
后部署检查可以在部署完成后通过部署管道或 GitOps 工具中的后同步钩子来实现。
一个能够在 Kubernetes 上独立于部署方式实现预部署和后部署检查的工具是 CNCF 项目 Keptn。 Keptn 可以在预部署检查未成功时停止 Kubernetes 部署。 Keptn 还可以执行后部署检查,例如执行测试、评估 SLOs,或 通知团队。
要了解更多关于 Keptn 的信息,请查看 文档 [17]。
部署通知
一旦我们得知新的 部署是否成功或失败,并完成了部署后的检查,我们可以利用这些信息,通知那些能够从中受益的人或工具。 无论是使用像 Keptn 这样的工具、GitOps 工具的通知,还是通过管道来实现,以下是一些发送 此类信息的示例:
-
一个聊天:在他们的 Slack 渠道中通知开发团队,他们的最新部署要么已准备好,要么已经未通过 检查
-
一个票证:通过更新 Pull Request 或 Jira 票证,告知关于 部署状态
-
一个状态页面:保持部署状态页面的更新,显示版本、环境和健康信息 直到最新
-
一个可观察性后端:将部署事件发送到你的 可观察性后端
部署事件到可观察性后端
可观察性工具不仅跟踪度量、日志和追踪信息,还可以跟踪诸如 部署或 配置变更事件等。 许多可观察性工具(如Dynatrace, Datadog, New Relic等)可以利用这些事件并将它们与 应用行为的变化进行关联。 这可以显著提高事件响应,因为它可以 与特定的 部署变更相关联。
阶段之间的晋升
假设我们在开发中有一个新的 版本成功通过了所有后部署检查,那么这些更改是如何从开发环境推广到 QA,然后再进入生产环境的呢? 每个更改是否都必须推广到生产环境,还是有其他更好的策略? 新的发布是否会一次性推出到所有生产环境,还是有更好的策略 呢?
以下是我们在过去与之合作的组织中看到的一些策略。 对于所有这些策略,意味着创建一个 Pull Request,将新的部署定义从较低阶段的 Git 位置推送到较高阶段的 Git 位置:
-
自动化:通常出现在开发环境和 QA 环境之间。 每当后部署检查成功时,它可以触发一个自动化的 Pull Request。 这确保了所有通过基本检查的开发更改能够迅速推送到一个用于更 全面测试的环境中。
-
计划性:例如,每天一次,将开发或 QA 中的最新版本推广到一个特殊的测试环境(例如,性能测试环境)。 这确保团队每天都能获得他们更新在过去 24 小时内的性能行为变化反馈。
-
受控/手动:通常越接近生产环境,这种情况越常见。 那些成功通过 QA 和性能测试的更改将被标记为可以安全推送到更高级别的环境中。 实际的晋升通常是手动进行的,而 Pull Request 本身可能已经自动创建(但不会自动批准),例如,从那些成功通过性能 测试阶段的版本中创建!
-
生产环境中的多阶段部署:当有多个生产集群时,通常的做法是先将更改部署到一个集群。 然后,验证一切正常后,再继续发布到其他集群。 第一个集群可能仅供内部使用,或者仅供那些知道他们会首先接收到更新的用户(例如,亲朋好友或早期访问小组成员)。 当在多个区域或多个 SaaS 服务商中部署时,建议定义清晰的 顺序(例如,先在欧洲部署,再在 美国发布)。
在我们不同的生产环境中使用多个质量门控阶段和分阶段发布策略的 目标是:减少 部署失败的风险!
蓝绿部署、金丝雀发布和功能标志
虽然使用多个 阶段进行发布前和发布后检查已经 降低了风险,但我们还可以为每个单独的部署做更多的工作: 渐进式交付 策略,如 蓝绿部署、金丝雀发布或功能标志。 我们已经在 持续部署—将部署与 发布解耦 章节中详细讨论了这些内容。
在发布过程中,这是一个重要话题,因为存在几个 悬而未决的问题:
-
谁将接收到新版本? 是某个特定百分比的用户,还是 某个特定群体?
-
如何衡量和验证发布 是否成功?
-
谁负责做出前进或 回滚决策?
就像通过发布后检查验证部署一样,我们也可以通过渐进式交付进行相同的操作。 开源 工具,如 Argo Rollouts [18] 和 Flagger [19],或商业工具提供自动化分析,比较渐进式发布各阶段之间的差异。 通常通过查询可观察性平台的数据来实现;例如,使用 Prometheus,验证新版本是否在关键服务级别指标(如请求失败率、响应时间、内存和 CPU 消耗)上与现有版本没有差异。 有关更多详情,建议查看相应工具的文档,这些工具用于 渐进式发布。
发布清单
自动化部署和发布过程的价值主张在于,工程团队可以更频繁地将更新发布到各个目标环境中。 我们越是通过平台简化这种自动化的可用性,团队就会更频繁地发布 更多版本。
发布清单可以帮助 我们跟踪当前在哪些环境中、由哪些团队发布了哪些版本。 从平台工程的角度来看,我们可以强制执行对这些信息的一致定义:版本、环境和所有权。 如果一切都以代码配置,这意味着这些信息可以在 Git 中获取。 当使用 Kubernetes 作为目标平台时,我们还可以将这些信息作为注释添加到我们的 Kubernetes 对象(部署、 Pod、Ingress)上。
在之前的示例中,我们已经强调了一些标准的 K8s 注释。 这里是一个 部署定义的片段:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fund-transfer-service
spec:
…
template:
metadata:
annotations:
owner.team: team-us
app.kubernetes.io/name: fund-transfer
app.kubernetes.io/part-of: backend-tenant-2
app.kubernetes.io/version: 1.5.1
在 K8s 中,我们 可以简单地查询每个 K8s 集群的 K8s API,以获取这些标签下所有部署的概览。 然后我们可以获得如下概览 :
集群 | 发布 | 部分 | 所有者 |
---|---|---|---|
dev |
fund-transfer:1.5.1 |
backend |
team-at |
dev |
account-info:1.17.2 |
backend |
team-ae |
qa |
fund-transfer:1.4.9 |
backend |
team-at |
qa |
account-info:1.17.1 |
backend |
team-ae |
prod-eu |
fund-transfer:1.4.7 |
backend-tenant-1 |
team-de |
prod-eu |
account-info:1.17.1 |
backend-tenant-1 |
team-de |
prod-us |
fund-transfer:1.4.6 |
backend-tenant-2 |
team-us |
prod-us |
account-info:1.17.0 |
backend-tenant-2 |
team-us |
表 5.2:基于部署工件元数据的发布清单
可观察性工具通常提供此功能,因为它们已经拉取 K8s 的 API 来观察集群的健康状况、事件和对象,包括这些元数据。
另一种方法是直接从 Git 解析这些信息。像 Backstage 这样的工具正是做的这个,它们通过这些信息提供一个易于搜索的软件目录。
发布管理——从启动到任务控制
对于许多组织来说,发布过程的结束标志是将新版本部署到生产环境中,随后运维团队接管所有保持软件运行的工作。这两个阶段通常也被称为启动和任务控制,这两个术语来源于 NASA 如何管理其太空任务。
多年前,DevOps 运动受到了许多著名例子的推动,例如 AWS 平台,它推广了你构建它,你运行它!的方法。这意味着开发者的责任不仅仅是构建一个工件,然后将其扔到所谓的“运维墙”那边。开发者必须对他们的代码从开发到生产的全过程负全责并拥有所有权。他们需要处理事件和更新,直到他们的代码最终被淘汰。
当我们看今天复杂的环境时,拥有每一个方面(代码和基础设施)是非常困难的。通过平台工程,我们尝试通过提供自服务来减少复杂性,并给团队提供更多控制权,使他们能够拥有自己软件的端到端生命周期。
在构建未来平台时,我们的重点必须是提供自服务功能来编排整个工件的生命周期。实际上,将整个应用程序的生命周期编排成一个单一的工件,通常只是应用程序的一部分。生命周期编排包括构建、部署和发布,但也包括生产支持所需的所有用例。这包括通过扩展性实现的弹性、事件管理、获取正确的可观察数据以进行故障排除,以及自动化交付以推动修复和更新。
在接下来的部分,我们将深入探讨生命周期编排,如何通过使我们的生命周期可观察来提高透明度,以及事件驱动模型如何帮助减少管道和编排代码的复杂性。
实现可持续的 CI/CD for DevOps – 应用生命周期编排
从构建,到发布,再到推广、部署、发布、修复。 这是需要自动化的很多任务,用来协调我们工件和应用程序的整个生命周期。 我们在那些负责端到端的团队中看到的挑战是,大多数这些脚本处理的是 以下内容:
-
触发某个(硬编码)工具
-
在某个(硬编码)环境中执行
-
然后,等待工具完成 它的工作
-
解析特定的 结果格式
-
根据解析结果,决定接下来做什么 (硬编码)
这些脚本代码往往会被复制粘贴到不同的项目中,稍作修改并适配。 对单个脚本进行修复或调整很难推广到所有其他变种,因为没有简单的跟踪这些脚本的方法。 这导致了高昂的维护成本,使得改变这些脚本中使用的过程或工具变得不灵活,并且浪费了工程师用于做自己 常规工作的时间。
使用管道脚本的强大功能没有问题。 许多管道自动化工具还提供了抽象、通过库实现的代码复用以及其他特性,以减少代码重复并增加可重用性。 因此,根据您使用的工具,确保遵循这些 最佳实践!
存在一种适合现代云原生应用及 云原生环境的不同方法: 生命周期 事件驱动编排!
在接下来的部分,我们将深入探讨这一方法,因为它提供了大量的灵活性和集中式可观察性,并将导致更可持续的 CI/CD 自动化 以及运维!
工件生命周期事件可观察性
事件驱动编排的基础是生命周期事件,如在 第三章中讨论的那些。它涉及观察工件的完整生命周期:从代码的初始 Git 提交、容器镜像构建并推送到注册表,再到每个阶段的发布,直到工件更新或退休,包含所有的中间步骤。
到目前为止,在本章中,我们讨论了许多生命周期步骤,并且强调了如何提取其中一些生命周期 事件:
-
CI/CD 流水线 可以在它们 构建 或 部署
-
工件注册表 在容器被 推送, 拉取, 或 扫描
-
PreSync
,Sync
,PostSync
, 和SyncFailed
-
Git 工作流可以用于在代码 更改 或 被 提升
-
像 Keptn 这样的工具提供了每个部署的前后事件,以及 复杂应用程序的事件
-
容器 平台,如 Kubernetes,暴露了有关 部署健康
CDEvents [20],一个来自连续交付基金会的项目,扩展了 CloudEvents 规范,后者已经是一个成熟的 CNCF 项目,并且在生态系统中得到了广泛的应用。 CDEvents 最初的目标是为构建、测试和部署的所有生命周期阶段标准化事件。 最近,它已扩展到覆盖运营中的部署生命周期阶段,如 生产事故。
这个想法是,遵循这些事件标准的工具更容易与生态系统中的所有其他工具集成。 不需要管理和维护工具之间的硬编码集成,这些工具通过开放标准的 API 和事件进行通信。 工具可以触发这些事件;例如,Jenkins 创建了一个新工件,其他工具可以订阅这些事件(例如,GitLab 可以订阅并触发一个工作流来扫描并发布容器)。 这个生命周期阶段也会生成一个 标准化事件。
所有事件都有最基本的属性集,用于识别阶段、工件以及涉及的工具,此外,还有必填的附加属性(例如,初始 git 提交
,版本,环境,和 责任团队)
当所有这些事件被发送到一个中央事件中心时,它就能实现对工件生命周期的完全事件驱动编排。 工件的生命周期。
例如,如果你决定通知开发团队有关其 Slack 中的新部署,你可以轻松地订阅部署事件,并将该事件转发到 Slack。 如果你更改聊天工具为其他工具,只需更改该事件订阅。 无需查找所有管道中当前发送通知的代码,因为这通过简单的事件 订阅变更完成。
总的来说,在所有工具和阶段中使用一组定义良好的生命周期事件,使我们的平台 工程方法具备了许多能力:
-
可追溯性:我们可以追踪每个工件从初始创建到生命周期结束的全过程。 这让我们能够看到工件的位置(发布库存)、它们卡住的位置,以及谁负责将工件放入 某个环境。
-
度量:我们可以衡量多少工件在生命周期中流动以及花费了多长时间。 这是报告 DORA 效率指标的基础!
-
互操作性:我们可以轻松地集成新工具或替换它们,只要它们都遵循相同的标准(例如,切换通知工具只是更改一个 事件订阅)的问题。
-
灵活性:像替换工具一样,我们可以通过让额外的工具在特定事件上添加工作,轻松地调整我们的交付流程(例如,在某些环境中为部署添加强制性的安全扫描,可以通过安全 扫描工具的事件订阅实现)。
让我们快速回顾一下与创建和维护冗长复杂的自动化脚本相比,这种事件驱动系统的构建块。 过程 逻辑。
与事件一起工作
第一步是我们使用的工具必须在工件生命周期中发出这些事件。 观察 CDEvents(它扩展了 CloudEvents),在生态系统中已经有一些工具提供了开箱即用的支持,例如 Jenkins、Tekton、Keptn、Tracetest、Spinnaker 等。
那些尚未集成的工具,可以使用现有的 SDK 轻松发布这些事件。 以下是一个使用 Python 的代码示例,它创建了一个带有元数据的管道运行完成事件(cdevents.new_pipelinerun_finished
_event),标识管道、制品或所有者:
import cdevents
event = cdevents.new_pipelinerun_finished_event(
context_id="git-abcdef1231",
context_source="jenkins",
context_timestamp=datetime.datetime.now(),
subject_id="pipeline_job123",
custom_data={
"owner": "dev-team-backend",
"part-of": "backend-services",
"name" : "fund-transfer-service"
},
subject_source="build",
custom_data_content_type="application/json",
pipeline_name="backendBuildPipeline",
url="https://finone.acme/ci/job123",
)
# Create a CloudEvent from the CDEvent
cloudevent = cdevents.to_cloudevent(event)
# Creates the HTTP request representation of the CloudEvent in structured content mode
headers, body = to_structured(event)
# POST it to the event bus, data store
requests.post("<some-url>", data=body, headers=headers)
前面的 Python 代码示例创建了一个新的 pipelinerun_finished_event
,表示管道执行完成。 附加的上下文数据指示了哪个管道以及它何时构建,它允许我们提供附加的元数据,例如所有权、制品或该管道属于哪个应用程序。
无论你是使用 CDEvents 标准提案,还是发送你自己的生命周期事件, 基于 CloudEvents 是个不错的主意,因为该项目已经有许多行业集成,可以发布或消费 CloudEvents,Knative 就是 其中一个例子!
通过订阅事件来进行协调
一旦我们的所有工具都 发布标准化事件,我们就可以通过让我们希望参与该过程的工具订阅它们想要 执行的事件,从而更容易地协调我们的制品生命周期过程。
我们在本章之前讨论过相同的概念,当时我们谈到使用工具(如 Argo CD 或 Harbor)的 webhook 功能来在新制品可用时或新部署成功同步时执行操作。
标准事件模型的好处是,工具不再需要订阅特定工具的特定 webhook(例如,Argo CD 的 webhook)。 相反,我们可以订阅一个接收所有涉及工具的标准化生命周期事件的中央事件中心,如下图所示:
图 5.9:所有工具都发布并订阅标准化的生命周期事件
将一切 基于事件标准,消除了点对点工具集成或管道脚本中硬编码的工具集成需求。 举个例子,我们可以简单地订阅 dev.cdevents.service.published
事件,获取关于该服务的详细信息并将其转发到我们的 聊天工具。
如果今天该工具是 Slack,将来我们决定切换到另一个工具时,我们只需更改 该订阅。
另一个用例是将不同的工具作为流程的一部分,在不同的环境中处于活动状态。 由于这些标准化的事件包含大量的元数据(例如,某个服务被部署到哪个环境),我们可以订阅某个特定环境的事件。 以下是展示 一些示例的表格:
源工具 | 事件属性 | 被订阅者 | 操作 |
---|---|---|---|
Argo | service.deployed``staging``fund-transfer:2.3``team-backend |
K6 当 environment == "``staging" |
执行 简单负载 |
. | Slack 当 所有者 == team-backend |
向团队的后端 Slack 频道发送通知 | |
OTel Collector | 收集所有事件并转发到 可观测性后端 |
表 5.3:来自 Argo 的相同事件可以被不同的工具订阅,以执行不同的操作
一些工具已经提供了 开箱即用的支持,能够订阅 CloudEvent 源或提供一个可以消费 CloudEvent 的 API 端点。 对于其他工具,则需要构建一个轻量级的集成层,该层订阅这些事件并将其转发给目标工具。 也可以通过支持 CloudEvents 的事件总线系统来实现。
分析事件
现在我们知道了事件是如何 发送的,以及其他工具如何订阅这些事件,我们可以讨论如何利用它们来分析我们的生命周期过程 实际上是如何运作的。
定义良好的 事件拥有时间戳、生命周期阶段定义(=事件类型
)以及一些上下文信息(工件、环境、所有者),可以用于分析以下问题: 例如:
-
在某个 特定环境中发生了多少次部署?
-
一个特定应用程序或租户的部署次数是多少? 或者租户的部署情况如何?
-
一个团队的活跃程度如何,基于 所有权信息?
-
有多少工件从开发环境 到达生产环境?
-
哪些工件需要很长时间,并且在到达生产环境的过程中被阻塞了? 到生产环境的路径上在哪里出现了阻塞?
-
是否有某些制品比其他制品更容易引发安全漏洞或生产问题?
-
在流程中,哪些工具消耗了大部分 时间?
-
有哪些工具通常是导致端到端流程缓慢的原因?
我们可能还有更多的问题,可以通过分析 这些事件来解答。
我们如何分析这些事件? 你可以将所有这些事件流式传输到数据库或你的可观察性平台。 在 图 5**.9中,我们包含了 OpenTelemetry,因为事件可以直接被摄取并转发到你的可观察性后台,并 在那里进行分析。
通过事件可观察性为 CI/CD 带来透明性
将所有事件与所有元数据以及明确定义的表示生命周期阶段的类型放在一个地方,使我们能够获得关于集成、交付和运维过程的很多透明性。 这些数据使我们能够优化流程,从而实现 更高的可持续性。
为 CI/CD 和运维构建自动化通常会导致大量自定义代码,需要在所有被复制的项目中进行维护。 我们在这一节中学到的是,转向事件驱动的方法来管理制品生命周期,可以解决很多复杂性问题,这些问题通常隐藏在自定义脚本或硬编码的 工具对工具集成中。
在 第九章中,我们将讨论如何通过做出正确的 架构决策来减少我们平台组件中的技术债务。
IDP – 平台中的自动化克拉肯
到目前为止,在这一章中 我们已经学到了许多关于自动化端到端构建、交付、部署和发布过程的基本构建模块。 我们讨论了通过 GitOps 部署期望状态的新方法,其中期望状态是从目标环境中拉取的,而不是从外部工具(如 管道)推送过来。
我们讨论了从第一次提交到将软件发布给最终用户的端到端发布流程。 最后,我们讨论了将事件驱动方法应用于编排我们的工件生命周期,这提供了一个集中的事件中心,使所有发生的事情更加透明和可观察。 这还使我们更灵活,因为我们可以将工具集成和流程定义从流水线或 bash 脚本转移到事件订阅和 事件驱动工作流中,从而消除复杂性。
在这最后一部分,我们想简要了解哪些概念可以通过您可能已经拥有的现有工具实施,哪些新方法存在以解决我们讨论的一些挑战,以及您可能希望查看的一些新工具,这些工具在过去几年中已经出现 - 无论是开源还是商业 - 都可以减轻一些工作 的负担。
我们将通过将自己置于我们的用户、开发人员或开发团队的鞋子中来实现这一目标,因为他们将需要适应新的工作方式。
为更轻松的启动提供黄金路径模板!
我们尝试推行许多新实践,例如确保部署的正确元数据(版本、应用上下文、所有权等),或者在每个构建流水线中包含安全漏洞检查。 在平台工程社区中,这些也被称为 黄金路径。 为了确保新项目团队能够轻松遵循这些实践,我们需要使它们易于获取 和采纳!
最简单且最有影响力的方法是提供软件或存储库模板。 这些是形式为清单文件、流水线、自动化脚本等的模板,开发人员可以在模板存储库中找到,然后应用到 他们的项目中。
虽然这种方法有效,但并不强迫工程师真正使用这些模板;此外,这是一个额外的手动步骤,也可能导致错误。
使这变得更加简单和自动化的一种方法是提供一个 CLI 或 UI 来初始化新的或更新现有的 git 存储库,使用最佳实践模板。 Backstage,这是一个由 Spotify 捐赠给 CNCF 的 项目。
Backstage 软件模板 功能 旨在将 Golden Path 模板作为每个开发人员构建新软件组件时的入口点。 模板可以由了解如何正确配置流水线、启用自动化测试和部署以及强制 安全检查的专家定义。
一旦定义了模板,它们就可以通过易于使用的向导进行访问,提示开发人员提供一些关键输入数据,例如他们实现的服务类型、所有权信息、可观察性或安全性的要求等 - 所有这些输入都将影响使用来自 模板的文件和配置创建新存储库或更新现有存储库。
要了解更多关于模板的信息,请查看 Backstage 网站上的详细文档和示例。 Backstage 网站 [21]。
通过 Crossplane 的抽象
另一种简化和强制最佳实践的方法是在定义您的 应用程序或服务时提供额外的抽象层。 在 K8s 中,我们必须定义我们的部署、服务、Ingress, 持久卷索赔 (PVCs),以及在需要部署依赖服务时更多的软件组件,如数据库、缓存或其他所需的 软件组件。
在 IaC 的早期部分,我们介绍了 CNCF 项目 Crossplane。 Crossplane 通过代码编排 同时管理基础设施和应用程序部署,并提供了所谓的 复合体的概念。在这里,我们不会花更多时间,因为我们之前已经提供了几个示例,展示了如何使用复合体来提供性能测试环境以及定义金融后端应用程序类型,开发人员只需指定要部署的服务版本即可。 以下是复合体定义的前两行。 在 Crossplane – 用于平台和 应用程序的 IaC 部分中查看其余内容:
apiVersion: composites.financialone.acme/v1alpha1
kind: FinancialBackend
在提供抽象时,使开发人员了解这些抽象的重要性至关重要。 这可以通过提供教育材料或简单地通过与前面讨论的相同模板方法(如使用 Backstage 等工具)来实现。
一切 Git-flow 驱动
嗯,Git 作为我们的事实来源应该不是什么惊讶的事情——我们在本章早期就已经确立了这一点。 然而,大多数 Git 解决方案提供了额外的功能,我们可以利用这些功能来加强标准和流程(例如,GitHub 工作流)。 工作流可以根据计划触发,也可以作为 Git 驱动过程中的许多不同事件的一部分(例如, 推送, 拉取 请求, 发布)。
这使我们能够在构建和推送工件之前也强制执行我们的标准;例如,验证我们期望每次部署时必需的元数据文件(例如,所有者信息)。 我们还可以使用它自动进行代码扫描并生成得分卡,或者我们可以用它来验证所有依赖项是否安全且没有已知的 安全漏洞。
根据选择的 Git 工具,你通常会找到一个市场或最佳实践目录,列出可以为特定类型的项目执行的工作流和操作。 确保你熟悉所有基于你的 工具选择可能实现的功能。
软件目录
一旦我们使开发人员能够构建更多遵循所有流程的软件,我们希望会看到大量新服务的开发。 这些是其他开发人员也需要了解的服务,以避免开发团队构建重复的服务,并鼓励开发人员在现有服务 和 API 的基础上构建更多功能。
一个 软件目录 提供所有可用服务和 API 的概述,并理想情况下还提供一些文档,这是我们 追求的目标。
由于 Git 是事实来源,我们可以直接从 Git 中提取大部分信息。 根据我们选择的 Git 解决方案,软件目录可能已经是提供的一部分。 然而,软件目录中还有更多服务和 API,组织可以拥有并基于它们进行开发(例如,部署在本地的外部 API 或第三方软件)。
Backstage,这个 也提供了前面讨论过的模板功能的工具,还附带一个软件目录。 它通过解析 Git 仓库中的特定元数据文件获取数据,但也允许外部数据源提供实体信息。 以下插图来自 Backstage 博客,展示了 Spotify 在 Backstage 中的软件目录的样子: 样子:
图 5.10:从 Git 中提取的实体元数据的软件目录
从前面的截图中我们可以看到,软件目录是了解一个组织内有哪些软件组件、它们是什么类型、谁拥有它们、在哪里可以找到源代码以及 其他信息的强大工具。
像 Backstage 这样的工具 并不是完整的 IDP;然而,它们代表了一个门户——一个图形化界面——可以访问大多数 IDP 用户相关的数据。 IDP。
虽然 Backstage 是一个选项,但市场上还有许多其他选择。 从自家开发到其他开源或商业工具,如 Cortex、Humanitec、Port, 或 Kratix。
总结
在这一章中,我们了解了从最初创建到投入生产的整个过程中的底层自动化和流程。 对于现代平台,采用 GitOps 方法,尤其是通过拉取而非推送的方式,应当是一个重要的考虑因素。 我们学习了 Git 作为真实来源,以及将业务逻辑作为容器或 OCI 合规的镜像放入我们的 目标环境中的方式。
随着组织的成长,推行良好的流程和最佳实践变得尤为重要。 为了确保执行效果,它需要易于访问,并且应当作为自助服务提供端到端的支持,以免影响工程师们的创造力流动。 工程师的创造力。
这也引出了下一章的主题。 在 第六章中,我们深入探讨了专注于自助服务功能的重要性,这些功能真正解决了目标用户——我们的开发人员——的需求。 我们将讨论如何将这些黄金路径的最佳实践引入我们的平台,从而显著改善开发人员完成工作的方式。
进一步阅读
-
[1] OpenFeature – https://openfeature.dev/
-
[2] Crossplane –
www.crossplane.io/
-
[3] Renovate Bot –
github.com/renovatebot/renovate
-
[4] 语义化版本控制 –
semver.org/
-
[5] Kustomize –
kustomize.io/
-
[6] Helm –
helm.sh/
-
[7] 务实程序员 – DRY 原则 –
media.pragprog.com/titles/tpp20/dry.pdf
-
[8] 如何设置 GitOps 目录结构 –
developers.redhat.com/articles/2022/09/07/how-set-your-gitops-directory-structure#directory_structures
-
[9] Argo CD –
argo-cd.readthedocs.io/en/stable/
-
[10] Flux –
fluxcd.io/flux/
-
[11] 开放容器倡议 –
opencontainers.org/
-
[12] 建议的容器注解 –
github.com/opencontainers/image-spec/blob/main/annotations.md
-
[13] Harbor –
goharbor.io/
-
[14] Harbor Prometheus 指标 –
goharbor.io/docs/2.10.0/administration/metrics/
-
[15] Harbor 分布式追踪 –
goharbor.io/docs/2.10.0/administration/distributed-tracing/
-
[16] CNCF TAG 应用交付 –
tag-app-delivery.cncf.io/
-
[17] Keptn –
keptn.sh/
-
[18] Argo Rollouts –
argoproj.github.io/rollouts/
-
[19] Flagger –
flagger.app/
-
[20] CDEvents –
github.com/cdevents
-
[21] Backstage –
backstage.io/
第六章:为开发人员及其自助服务构建
没有可访问功能的平台不是一个平台。 作为规则,自助服务应该包含在内,并且必须提高可访问性,以增加价值。 在构建平台的过程中,通常没有区分基础设施层和应用层。 内部开发者门户(IDPs)的集成为此带来了新的复杂性,它解决了一些需求 同时也留下了一些未解答的问题。 将社区引入你的平台并对他们的贡献持开放态度是很重要的。
在本章中,我们将回顾一些概念,并分享一些最佳实践的思路。 到本章结束时,你应该了解如何构建一个具有韧性、灵活性并能够满足用户需求的平台。 他们所处的位置。
在本章中,我们将涵盖以下 主要主题:
-
软件开发与平台开发——避免 混淆
-
减少 认知负荷
-
自助服务 开发者门户
-
落地、扩展和集成 你的 IDP
-
平台可观察性的架构考虑 在平台中
-
为社区开放你的平台 并促进合作
技术要求
在本章中,将会有一些技术示例,包括 .yaml
文件和命令。 虽然你不需要设置集群来跟随本章内容,但如果设置集群,可能会加深你的理解。 我们使用了以下技术来开发我们的示例 和解释:
-
kind – 测试版本为 kind
v0.22.0 go1.20.13
-
我们使用本指南设置了一个三节点的 集群: https://kind.sigs.k8s.io/docs/user/quick-start/#configuring-your-kind-cluster
-
Docker(建议使用 Docker 无根设置) 推荐使用
-
The
kubectl
命令行工具 -
一个 GitHub 仓库
代码示例可以在 Chapter06
文件夹内找到 这里: https://github.com/PacktPublishing/Platform-Engineering-for-Architects。
在本章中,我们将进行一些小型教程。 尽管并非每个代码片段都需要在 Kubernetes 集群中运行,但建议设置一个本地的 Kind 集群,其中至少包含一个控制平面节点和三个工作节点,以便充分体验教程的价值。 Kind 集群的配置可以在 GitHub 上的章节仓库中找到。
软件与平台开发——避免混淆
在我们迈向 平台开发的过程中,我们必须明确区分平台开发和其他形式的 软件开发。 请记住,平台的目的是为了使开发和运维团队能够进行开发和运营;它并不是客户直接体验的东西,尽管客户确实 间接受益。
平台的价值在于将构建和交付应用程序所需的所有工具、服务和应用程序统一在一起,呈现在用户面前。 简而言之,平台是开发者用来将软件应用程序交付给最终用户的一系列服务。 你可以 使用现有的软件应用程序,因此也可以在不编写 任何代码的情况下开发 一个平台。
那么,如何开发一个平台,如何开发软件呢? 它们在哪些地方交集,在哪些地方不同?
平台生命周期与软件生命周期
在 许多 方面,平台的生命周期看起来与任何 软件开发生命周期 类似 (SDLC) (SDLC)。
图 6.1:一个 SDLC
在前面的章节中,我们讨论了强大规划阶段的好处,并将平台视为产品的处理方式。 虽然在此过程中避免过度设计平台很重要,但你仍然会最终得到一个具有多方面特性的系统。 因此,由于平台的相对规模和复杂性,平台的发布可能不像按下按钮并让你的更改在一些服务器或集群中传播那样简单。 这在很大程度上是因为平台内部有大量的动态组件。 例如,创建一个新的集成以交付 软件材料清单 (SBOM)将比在现有的策略引擎中调整策略以使其 更具限制性对用户造成的干扰更小。
如果我们看看 一个典型平台的结构及其 用户考量,理解平台生命周期如何与 软件应用程序 有所不同会更加容易。
图 6.2:IDP 组件和功能区域示例
在这个平台示例中,预生产环境位于左侧,生产环境则位于右侧,客户面向的应用程序将在其中部署(应用程序着陆区)。 无论是单一 Kubernetes 集群、多集群还是多架构,平台是一个紧密结合的整体,包含这两种范式。 平台应该将架构从用户中抽象出来,让他们能够 独立工作。
在这里,DevOps 工程师关心的是应用程序如何构建和发布。 诸如渐进式发布、DORA 指标(https://dora.dev/)以及其他 DevOps 实践是平台可能需要支持的内容。 同样,如果您的组织在云原生之路上已经走得很远,那么与生产应用程序操作相关的数据将是站点可靠性工程师关心的内容。 还有其他相关的考量因素,比如安全检查、基础设施即代码以及文档。 这些方面的每一项都可以在平台内单独控制,并对所有或部分 角色可用。
图 6**.2 假设有专门的角色在工作;然而,如果没有专门的 DevOps 团队,那么开发人员和质量工程师很可能会在这方面分担职责。 这个整体概念仍然适用于即便是小型组织,在这些组织中,一个人可能需要同时承担多个角色的用户故事。 即使是理论上划分的角色,也应该有助于在进行平台生命周期管理时提供帮助。
平台 不仅仅是软件;它还是与工作负载协同调优的过程,以确保其对用户的性能。 虽然用户需求确实会影响平台的大小、范围和功能,但它应该尽可能设计得不偏不倚,以确保一个用户的黄金路径不会优先于 另一个用户。
可靠性与可维护性
一个平台的 可靠性 和 可服务性 是开发者在 组织中使用该平台的最大因素。 但这些因素到底意味着什么呢? 这些概念各自到底意味着什么呢?
从本质上讲,可靠性 涵盖了平台的整体可用性以及用户与平台成功互动的能力。 平台的可靠性应假设已经在平台中实现了多租户(多个用户之间完全隔离)。 这是因为这个概念仍然可以在不需要的地方应用,但你的平台仍然支持具有各自需求的多个用户。 尽管多租户对于内部工具而言并不是直观的做法,但高度监管的团队可能更愿意在一个隔离的环境中工作,以确保没有安全性和合规性违规的风险。 即使所有用户都是内部客户,他们仍然不应该暴露于或受到其他 用户工作负载的影响。
为了实现平台的可靠性,你应该利用调优和策略,帮助你确保工作负载隔离,并保持平台的完整性。 在 Kubernetes 环境中,一个节点可以是虚拟机或真实硬件。 与任何机器一样,每个节点都有分配给它的固定内存和 CPU。 然而,与标准虚拟机不同,当你超额分配 CPU 时,节点可以使用原本属于其他节点的 CPU。 这一点对内存不适用,虽然内存也可以被过度订阅,且 内存不足 (OOM) 会 导致节点重启。 这样可以通过保证在 CPU 周期可用时,资源能够使用这些 CPU 周期,从而降低整体资源需求;然而,另一方面,由于软件错误导致的 CPU 占用过多的恶劣进程,可能永远无法将 CPU 周期返回给节点池,以便再次使用。 其他因素,例如 Pod 优先级、请求、限制和 Pod 中断预算,会影响调度器如何决定在哪个地方以及如何调度 Pods,是否会驱逐已有的工作负载。 有关这些主题的更多信息,重要的是定期查看 Kubernetes 文档,因为新特性和最佳实践不断涌现,而该项目也在持续 发展(https://kubernetes.io/docs/home/)。
虽然这些设置的最终调优和编排需要 由平台工程团队来决定,但我们通常建议,这些调优应该成为规划阶段的一部分。 在平台生命周期内,你应该持续不断地进行评估。 你应该在平台的生命周期中持续重新评估。 平台。
平台的可服务性类似于可靠性,不同之处在于可服务性更多关注的是需要利用平台的用户的黄金路径。 为了使平台具有可服务性,它需要满足用户的需求。 与自助服务不同,自助服务是用户在不依赖管理团队的情况下,能够合理完成所有合理操作的能力,可服务性则从整体上看待所有用户需求,并在用户所在的位置满足这些需求。 可靠性可以视为可服务性的衡量标准,但满足用户需求的目标比单纯的可靠性更加全面。 可服务性是平台产品思维的核心, 平台的核心。
类似于关键用户旅程, 黄金路径映射了系统用户的更关键使用场景。 一般来说,这是用户可以采取的步骤序列,并期望达到 预期结果。
对于利用 IDP 的应用程序开发者来说,黄金路径可能是这样的:
图 6.3:使用 IDP 的黄金路径示例
开发者推送提交后, 持续集成 (CI) 系统 自动执行其魔法。 应用程序或应用程序的升级 进入生产环境后,它会将数据发送回日志记录和可观察性工具,供开发者利用,从而使他们能够获得关于应用程序生产状态 和性能的洞察。
结论
考虑到这一点,我们现在可以看一下,平台工程的 SDLC 与一个平台运行的应用程序开发之间的分歧。 在平台的黄金路径中,用户不再是与一个完成所有工作的服务或不透明系统交互,而是与由多个服务组成的系统交互。 这些服务中的一些或全部可能是自家研发的,但更可能是这些服务是内部工具与开源技术的混合,共同协作,构建出我们所说的 平台。
由于平台由多个组件构成,你还可以基于每个组件来拆解平台的 SDLC。 然而,与任何系统一样,你必须警惕那些需要多个组件一起发布的相互依赖关系。 这些组件必须协调发布。
你的开发人员需要能够透明和不透明地使用这些系统。 这意味着,虽然平台中的每个工具的使用应该自动化融入工作流程中,但开发人员应能够看到发生了什么,并在必要时调试各个部分的问题。 例如,如果一个 Tekton CI 任务失败,我们示例中的应用开发人员需要能够看到失败的任务,并深入了解任务失败的原因,从而帮助他们修复应用或 CI 任务。 CI 任务。
了解如何有效管理平台生命周期,同时尽量减少对用户的影响,可能就像是在“喝自家酿的香槟”,意味着平台团队使用他们提供给用户的相同流程和技术。 平台的构建和生命周期管理可以利用相同的 DevOps 工具; 代码即配置(star () as code)* 等模式,如 文档即代码(docs as code)、 基础设施即代码(infrastructure as code)和 配置即代码(configuration as code) 都对平台的构建和发布有重要影响。 然而,与软件应用程序不同,对于平台的最终用户来说,这些声明式的内容对他们的影响最大,尤其是与诸如银行应用等完全封闭的 DevOps 方面相比,后者对最终用户完全是黑盒处理。 这些 DevOps方面对于平台最终用户来说完全是黑盒的。 客户的身份会极大影响软件工程的某一方面相对于其他方面的重要性,因此区分了 软件开发和 平台开发。
以用户为中心的设计同样是理解平台成功所需优先考虑的事项的关键。 虽然用户自身会在很大程度上提供相关信息,但随着本章的推进,我们将重点强调我们认为对成功平台最为重要的方面。 成功的平台。
通过对平台的 SDLC 有了新的理解,我们可以开始利用这些知识来阐明将平台构建为产品的意义。
减少认知负荷
在 第一章中,我们提到了 减少认知负担的重要性。随着技术和系统的发展,它们变得更加复杂。 高度的复杂性意味着理解系统的每个部分将成为更大的心理负担。 曾经的单体应用程序时代已经过去, Linux, Apache, MySQL, PHP (LAMP) 堆栈 已经成为历史。 现在,微服务、云、虚拟网络等已成为软件应用程序日常操作的组成部分。
在现代软件架构中,完整栈软件工程师不再需要理解运行应用程序代码的服务器或操作系统。 平台为他们处理了这些问题。 然而,尽管平台可以抽象出许多细节,开发人员仍然需要对底层技术有一定的意识和理解,特别是当这些技术与他们的应用程序相关时。 因此,平台必须在简化用户操作和提供所需信息之间找到平衡,避免提供那些不相关的“噪音”。
如果我们对比 和对照不同角色的优先级, 我们可以将平台的范围与这些用户的需求进行对照。
平台团队 | 双方 | 开发团队 |
---|---|---|
租户应用程序 | 平台可用性 | 应用程序可用性 |
用户管理 | 团队 RBAC | 应用程序 SLO/SLI |
用户认证 | 安全 | 通过/失败 CI 测试 |
网络 | 合规性 | CD 工作成功 |
平台 SLO/SLI | 日志聚合 | 应用程序升级 |
平台升级 | 错误和 异常追踪 | 应用程序 资源基准测试 |
平台扩展性 | 政策违规 | |
平台架构 | 配额消耗 | |
平台可维护性 | 应用程序性能 |
表 6.1:对比开发团队与平台团队的优先级
正如你所看到的,即使有平台团队 负责许多应用程序交付给最终用户的方面,应用程序的开发团队仍然有许多需要考虑的问题。 平台可以处理共享的考虑因素,例如安全性和合规性,但平台的工作并不是保证开发团队生产的应用程序具有良好的性能。 你可以将你的 IDP 视为一种托管服务,这对于 该产品来说并不是一个坏的定义。
作为一种托管服务,旨在实现“做”和“启用”之间的平衡,前面的表格成为了构建成功平台的路线图。 例如,通过在可观察性和可靠性工具方面进行投资,平台可以支持开发团队管理他们的考量,同时抽象掉它能抽象掉的其他一切。 一个不必关心聚合日志如何存储的开发人员,但知道如何在需要时访问它们,可以显著简化日常工作流程,从而让更多的精力投入到功能开发中,而不是运营负担。 这种认知负荷的减少可以导致团队的上下文切换更少,从而减少错误和压力。 通过这种方式,对平台的投资就是对开发团队生产力和健康的投资。 开发团队。
如果我们回想一下我们虚构的公司,Financial One ACME,我们可以想象在公司进行云转型战略时,认知负荷会变得更加重要。 由于公司并不是从零开始构建一切,开发人员不仅要维护遗留系统和架构,还要进行新的时代重构。 支持这一工作的平台可以让开发人员学得更少。 他们只需要知道平台在他们进行重构和 迁移战略时对他们的期望。
平台对减少认知负荷的承诺本质上是向开发人员做出的承诺,即你将帮助他们更高效地工作。 减少上下文切换、迅速直接的反馈循环和易用性,所有这些都有助于提高开发人员的工作满意度。 快乐的开发人员是压力更小的同事,有助于创造更加积极的工作环境,这对开发团队和 平台团队来说都是双赢的局面。
在平衡认知负荷的同时使用平台
尽管平台的许多功能是为了构建应用程序,但它同样必须能够支持应用程序的运行。 尽管平台的需求应当有详细的文档记录且易于理解,但人类往往容易出错;因此,平台必须强制执行其规范。 幸运的是,Kubernetes 在一定程度上本身就支持这一点,通过准入控制器 和 基于角色的访问控制 (RBAC),但你也可以利用工具如策略引擎来确保那些不符合规范的工作负载或操作被拒绝,并给用户返回错误信息。 越早收到反馈,用户的认知负担越轻,因为他们不需要切换上下文来响应 平台。
那么,如何通过内部 开发者平台来减少工程团队的认知负担呢?
如果你不知道从哪里开始,考虑到实现细节时,从同理心出发。
假设你是使用该平台的工程师之一。 现在是凌晨四点,你正处于慌乱中,而没有其他人能帮助你。 问问自己:“这会容易使用吗?” 当你对自己的方法感到满意时,找一位工程师来验证 你的假设。
每个细节都应与这一原则保持一致。 如果核心功能简单且能简化用户的操作,用户就无需承担认知负担。 在考虑其他方面时, 比如集成,遵循相同的方法。 直接的架构往往是最有效的。 如果“为什么会有这个架构存在?”这个问题无法用“因为它帮助了用户”来回答,也许它根本不应该存在。 完全没有必要。
通过微调 IDP 的操作,仍然可以取得显著的提升。 此外,设定平台上应用程序的标准,你的团队可以为它们定义一个成熟度模型。 这个成熟度模型将帮助定义一个应用程序需要具备哪些条件,才能成为平台环境中的成功成员,这意味着开发者可以以此为检查表进行工作,而无需进行实验、向人请教, 或猜测。
前期生产与生产阶段
一个平台既涵盖 预生产 环境,也涵盖生产环境,用于软件应用程序。 平台中应用程序 所在的部分被称为 应用程序 着陆区。
尽管平台必须考虑这两者,但这两者永远不应混合,因此平台必须在环境之间强制执行逻辑分隔。 它可以通过架构或策略来实现。 然而,架构是最好的,也是最安全的模型,我们将在 第七章 中进一步讨论。在当前章节中,尽管我们可能会提到安全最佳实践和分隔策略,但详细的解释可以在 第七章 中找到。本章中关于这些环境的重要要点应是它们如何与认知负荷互动,以及平台如何旨在减少这两个范围内的负荷。
身份验证与租户管理
随着平台 的扩展和用户的增加,它实际上变成了多租户平台。 这是由于在安全环境中确保最小权限策略的重要性,以及帮助确保用户之间不会互相干扰。 在 多租户环境下,仍然需要提供单租户的体验,这意味着其他用户和租户的存在必须对 每个用户隐藏。
你将添加到平台的第一个集成通常与身份验证和用户管理相关。 大多数 OpenID Connect (OIDC) 提供商 可以集成到常见的身份提供者(IDP)工具链中,因此选择正确的提供商不会很困难。 从那时起,用户管理就变得相对直接,尽管不同的工具之间有所不同。 为了减少认知负荷,越少越好,因此一种跨平台进行身份验证的方式意味着用户需要记住的登录流程更少,每天的登录次数更少,密码也更少,同时提供与其他工具相同的统一体验, 这对于公司内部的其他工具也适用。
让我们在一个更现实的场景中来看这个问题。 Financial One ACME 是一家银行。 这意味着它们处于一个高度监管的行业,需要保护大量非常敏感的数据。 控制谁能访问哪些数据,以及如何访问这些数据,是公司安全性和合规性方面最重要的内容之一。 期望用户每次都完美地记住并执行所有这些安全和合规实践,实在是过于苛刻。 因此,平台需要程序化地强制执行 这些规则。
满足任何行业的这些要求,无论是否高度监管,最终都归结于RBAC —— 如何将用户相互隔离,并确保他们只拥有需要的权限,而没有不必要的访问权限。 请查看下图,了解一个 多租户身份提供者(IDP)的示例。
图 6.4:IDP 集群中的多租户
在这个例子中,两个租户的工作负载可以共享一个节点的空间,但通过 RBAC,租户无法看到在同一节点上运行的其他工作负载。 另一种选择是确保每个节点上只能运行一个租户的工作负载。 然而,这种隔离程度可能会带来负面影响,因为它可能会影响 高可用性 ,或者需要更多节点来确保租户之间完全隔离并保持高可用性。 我们在 第三章讨论了平台的高可用性,这些相同的原则也适用于你的开发团队正在开发的应用程序。 与平台一样,高可用性是应用程序韧性的一项特征。 这可能意味着它通过扩展来处理负载的能力,或者仅仅是其整体的正常运行时间。 影响高可用性的因素之一是位置。 虽然你可能在 一个 副本集(ReplicaSet) 中有三个 Pod,但如果这三个 Pod 都在同一个节点上,并且该节点被重启(例如平台升级时),那么应用程序将会停机;因此,它就不是高可用的。 通常,高可用的应用程序会将副本集分布在多个节点上,如果这些节点位于不同的可用区,则该应用程序会增加一层额外的韧性。 遵循云原生平台最佳实践的开发人员通常会期望他们的应用程序具备高可用性,这意味着至少有两个副本集在 不同节点上运行。
RBAC
无论租户模型如何,Kubernetes 系统中的 RBAC 将成为平台自服务的骨架。 你将使用 RBAC 限制用户对 IDP 的访问,仅限于他们的工作负载将落在 IDP 中的命名空间和环境。 此外,你将在面向最终用户的环境中也有类似的 RBAC 策略,应用程序将在该环境中运行。 生产环境中的 RBAC 策略将比开发环境中更为严格,因为需要对这些权限进行更高程度的审查。 你将在 第七章中学习更多关于此的内容。
进一步阅读
Kubernetes 的官方文档可以在此找到 : https://kubernetes.io/docs/reference/access-authn-authz/rbac/。
借用官方文档中的一个示例,我们可以看到一个 RBAC 示例,在该示例中,分配给用户的角色使其能够读取机密,但不能编辑或 删除它们:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: secret-reader
rules:
- apiGroups: [""]
#
# at the HTTP level, the name of the resource for accessing Secret
# objects is "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]
与这个示例类似,通过利用 RBAC,你可以为用户设置适当的守护措施,让他们成功地在平台上导航。 那么,如何确定一个强健的 RBAC 策略需要具备哪些条件,以便在访问、操作和限制之间找到平衡呢? 这时,自助服务成为核心。 让我们看看你的用户可能会在平台上期望的工作流。
图 6.5:IDP 用户的示例工作流
在图 6**.5中展示的示例中,我们有多个团队,因此,团队内可能有多个用户,他们依赖于同一平台中的相同系统。 有时,他们会在同一时间依赖于相同的系统。 根据你选择的 CI/CD 系统,最佳实践可能有所不同;然而,一般而言,平台团队的目标是确保为 IDP 所需的组件能够扩展 到用户。
用于 Kubernetes 集群的身份验证系统也可以用于类似 Argo CD 的预生产工具,允许用户在同一个 Argo CD 实例中利用独立的项目。
虽然这不是最佳实践,但技术上可以对生产级访问实施相同的访问模式;然而,当访问生产环境时,最佳做法是进行更严格的身份验证,并且一旦验证通过,权限集应更加有限。 我们将在 第七章中进一步讨论, 构建一个 安全平台。
防止“噪音邻居”问题
噪声邻居如何影响认知负荷可能不是显而易见的,但防止这种事件的发生有助于避免生产事件,这些事件可能很难排查和解决,尤其是在多租户环境成为标准的情况下。 它还帮助用户排查他们的应用程序,或帮助平台团队排查生产问题,通过排除随着更多保护措施到位而变得不太可能的场景。 由于你的开发团队应当预期有限访问 IDP 的生产端,并且无法访问其他团队的指标和工作负载,因此所有防止噪声邻居的工作都有助于确保这些工作负载不太可能进入每个团队的 认知负荷。
在 IDP 的上下文中,噪声邻居指的是与其他工作负载在同一环境中运行,并且占用资源的工作负载,无论是通过大量的网络流量、CPU 或内存使用,还是其他不太邻里友好的行为。 就像城市会有条例防止邻居封锁街道或播放过大声的音乐一样,平台可以实施规范,确保一个工作负载不会对另一个工作负载产生负面影响。 影响。
在许多实际场景中,一个 Kubernetes 集群被用于多个环境。 对于云支出预算较为有限的组织,开发、QA、预生产和生产环境可能都位于同一个集群上。 从技术角度讲,整个 IDP 可以是一个集群。 虽然这不是我们认为的最佳实践,但它是一个极其常见的做法,用于降低成本。 正是在这些场景中,噪声邻居最有可能发生,但即使在生产环境完全隔离的情况下,也有可能发生,原因可能是某个工作负载遇到软件漏洞,或因为有效原因导致资源使用率高于正常水平。 容器化工作负载在防范这些共享环境中的噪声邻居场景方面有优势,因为这些 Pod 和容器定义可以持有资源定义,工作负载的封装性是一种优势,特别是在资源管理方面。 虽然绝大多数防范噪声邻居场景的保护应当作为软件应用的防御性编程的一部分,另外还应通过准确地对这些应用程序的资源需求进行基准测试和识别,但平台本身也有能力进行硬化。 硬化。
你如何实现这种硬化? 以下是实现方法:
-
即使在强制实施工作负载隔离最佳实践的情况下,也要注意资源共享,例如 etcd、API 服务器和 网络栈
-
采取预防措施避免 CPU 饥饿
-
集群自动扩展——自动添加(或移除) 节点
etcd 的维护和管理
etcd 是一个键值存储,用于保存集群中所有 Kubernetes 对象的定义——如 Pod 定义、作业定义、StatefulSets、DaemonSets, CustomResourceDefinitions (CRDs) 等等,这些都保存在 etcd 中。 这意味着,随着平台使用量的增加,etcd 可能会变得相当满。 etcd 项目 建议为 etcd Pods 分配一组最小资源,以确保生产环境中的顺利运行。 详细的推荐可以在 etcd 文档中找到(https://etcd.io/docs/v3.6/op-guide/hardware/),其功能领域包括磁盘、网络、CPU 和内存。 随着集群使用量的增加,etcd 的需求也会增加。 通常,etcd 的最佳实践包括确保其优先使用 CPU、网络和专用磁盘,具有高吞吐量和低延迟。 如果可能,应该使用固态硬盘 (SSD) 来存储 etcd 数据。 etcd 文档中有一些关于 etcd 调优的建议,我们这里不再重复。 保持跟进项目本身的最新建议非常重要,因为技术会随着时间不断演进。 由于 etcd 是 Kubernetes 集群成功运行的关键组件,因此必须仔细监视其可观察性,并主动应对磁盘压力或其他资源问题的迹象,这对于保证 平台的可用性至关重要。
即使在优化了 etcd 配置之后,etcd 的实际使用或过度使用仍然可能导致资源竞争。 例如,云原生 CD 系统 Argo CD 将作业存储为 CRD 条目,而不是 Kubernetes 作业。 随着 Argo CD 中团队和部署数量的增加,etcd 中的条目数也会增加。 如果 etcd 填满,Kubernetes API 服务器将会宕机。 为防止这种情况,必须确保 etcd 健康并且不会 过度填充。
一种方法是确保你从 etcd 中修剪掉旧的 CRD 条目。 在 Argo CD 的情况下,这个功能是原生集成到应用中的。 在标准 Kubernetes 作业的情况下,可以通过在作业定义的 spec
字段中应用所需的时间,来使用生存时间(TTL)机制。 这样,在作业成功或失败后经过一定时间,垃圾回收机制将运行并移除该条目,从而管理 etcd 的大小和健康状态。
类似地,其他 Kubernetes 类型也有各自的清理机制。 对于部署(Deployments),有一个规范是 revisionHistoryLimit
,它决定了将保留多少个旧版本的部署。 如果该数字为 0,那么在生产问题发生时,部署将无法回滚,但 etcd 会保持干净,像 口哨一样清晰。
如何调整这些清理措施,将取决于用户数量以及这些用户生成的 etcd 条目数量。 etcd 的大小也是一个因素,而这个因素可以通过增加控制平面节点的大小来扩展。 因此,何时扩展,何时修剪,何时考虑向 IDP 环境中添加集群,将是一个成本管理、投资回报率(ROI)和平台生命周期的基本部分。 它还需要成为你为 IDP 选择的集成工具的一个因素,但我们会在 第八章中更详细地讨论成本管理。
理解 CPU 和调度器以避免饥饿现象
在集群范围内共享的下一个资源是 CPU,无论租户模型如何。 如前所述 ,在讨论可靠性时,我们提到过,在 Kubernetes 中,每个节点对象可以使用的内存是有限制的。 所有 Pod 及其内存请求都会被汇总,如果总内存超过分配给节点的内存,Pod 将被调度到有足够空间的其他节点,或者调度失败。 如果 Pod 尝试使用超出节点可用内存的内存,将会被终止并重新调度。 内存分配在节点之间有严格的边界。 CPU 看起来也具有相同的功能,但实际上,所有 CPU 都可以被所有节点使用。 这意味着,虽然应用性能可能主要是开发团队的考虑因素,但开发人员不必担心平台是否能够支持应用的需求。 虽然开发人员需要告知平台工作负载的需求,但平台需要能够 支持这些需求。
Kubernetes 中的 CPU 管理的实际实现是 完全公平调度器 (CFS),它 与 Linux 内核 使用的是相同的调度器。 从功能上来说,这意味着,如果 CPU 是可用的, 节点上的工作负载可以使用超出节点分配的 CPU 资源。
虽然这种超额分配 CPU 的能力在某些情况下可能有用,因为它可以在技术上支持“二进制打包”,允许平台支持更多的工作负载,只要它们不会同时运行,并且不需要保留更多的虚拟硬件(或者如果你在裸金属上运行,就是实际硬件),但也有一些原因,你可能希望防止部分或所有 CPU 以这种方式被使用,即使它在 技术上是可用的。
例如,在生产环境中,可能会有一些工作负载必须始终保持高性能,或者特别敏感于 CPU 缓存,而其他操作则可以稍微慢一些,或者更自由地重新调度到新节点。 对于这些情况,你可以通过更改 CPU 管理策略 更好地保证 CPU 可用性。
要了解 Kubernetes 中 CPU 管理的完整实现, 请阅读文档 此处: https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/。
该平台需要在 kubelet 配置中启用 CPU 管理策略,以便将 CPU 从池中排除。 你需要定义预留多少 CPU,但可以保留所有 CPU,减去运行 Kubernetes 关键工作负载所需的部分。 为了达到预期的结果,即保证某个 Pod 具有所需的 CPU,而其他 Pod 则牺牲部分资源,开发者需要在 Pod 配置中指定请求(requests)和限制(limits),并且这两个值 必须匹配。
这是一个示例 Pod 配置:
spec:
containers:
- name: myCPUPrivilegedPod
image: docker-registry.nginx.com/nap-dos/app_protect_dos_arb:1.1.1
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "100Mi"
cpu: "2"
一旦你的节点按这种方式配置,它将被平台认定为 保证的 ,因此能够使用独占 CPU。 该 kube-scheduler (调度器 简称为调度器) 会优先安排它运行在支持 Pod CPU 需求的节点上。 如果只有一个节点符合条件,那么调度器会优先选择它;然而,如果你配置了多个或所有节点以使用独占 CPU,则虽然 Pod 的优先级较高,因而不太可能被迁移(除非遇到极端情况),它仍然可能被放置到 新的节点上。
为了确保始终选择特定节点,或者始终选择某种类型的节点,你可以为节点添加标签。 这样做有助于调度器将 Pod 匹配到在 Pod 配置中定义标签的节点上。 这进一步确保了 Pod 的正确放置,除了防止“吵闹邻居”外,还可用于其他场景。 。
如果你决定使用本地 kind 集群并且还没有设置,先让我们来设置这个集群。 首先,克隆本书的 GitHub 仓库,并切换到 第六章:
git clone https://github.com/PacktPublishing/Platform-Engineering-for-Architects.git
//asuming Linux
cd Platform-Engineering-for-Architects/Chapter06
kind create cluster --config kind-config.yaml --name platform
创建好命名为集群的平台后,将上下文设置为本章的 kind 集群。 这样就无需在每个 kubectl
命令末尾添加 --context kind-platform
了:
kubectl config set current-context kind-platform
现在我们让工作变得更简单一些,接下来可以开始 Pod 标签演示。
这是节点标签命令和 示例输出:
$ kubectl label nodes platform-worker2 reserved=reserved
node/platform-worker2 labeled
运行此命令查看节点的标签: 该节点的标签为:
$ kubectl get nodes platform-worker2 --show-labels
NAME STATUS ROLES AGE VERSION LABELS
platform-worker2 Ready <none> 3d23h v1.29.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=platform-worker2,kubernetes.io/os=linux,reserved=true
我们的最终 Pod 看起来像这样:
apiVersion: v1
kind: Pod
metadata:
name: my-app-nginx
labels:
env: prod
spec:
containers:
- resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "100Mi"
cpu: "2"
name: my-app-nginx
image: docker-registry.nginx.com/nap-dos/app_protect_dos_arb:1.1.1
imagePullPolicy: IfNotPresent
nodeSelector:
reserved: reserved
重要提示
为了让 CPU 管理策略在之前和新的工作负载上得到执行,你必须清空节点并 重新启动它。
除了 CPU 外, 调度器还会考虑其他几个不同的因素来决定如何优先调度或移动 Pods,包括明确的 Pod 优先级。 如果 Pod 优先级没有得到充分理解,或者平台的租户情况不明确,工程师可能不会立刻明白为什么不应该把应用 Pod 的优先级设得很高。 就开发人员而言,他们的生产应用程序可能是最重要的工作负载。 然而,如果使用不当,这可能会导致“吵闹的邻居”问题。 一个被过度优先的 Pod 看似无害。 实际上,它就像是一个不断加剧的拒绝服务攻击,随着规模扩大而变得更严重。 如果 Pod 被分配到一个副本集,并且随着集群的扩展,其实例数量增加,那么被过度优先的工作负载也会逐渐增多。 如果这个问题扩展到多个 Pod,那么问题会加剧。 这会导致平台服务退化,增加底层硬件的压力,并使故障排除变得更加困难。 因此,平台可能需要限制或防止这种分配。 这可以通过执行配额或 准入控制器来实现。
我们将在本章后面讲解配额和准入控制器。 但是,如果你希望在平台中使用 Pod 优先级,以下是启用它的一般步骤:
-
首先,创建一个 优先级类。 这种类型不限于命名空间,而是更广泛地在集群中可用。 通过将以下内容保存到一个 YAML 文件 中来创建该类,文件名为
priority.yaml
:apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: critical-priority value: 1000000 preemptionPolicy: Never globalDefault: false description: "This priority class should be used for platform Pods only."
-
接下来,应用 优先级类:
$ kubectl apply -f priorityclass.yaml priorityclass.scheduling.k8s.io/critical-priority created
-
现在,当我们创建一个 Pod 时,可以选择该优先级。 将以下内容保存为 文件
pod.yaml
:apiVersion: v1 kind: Pod metadata: name: my-app-nginx labels: env: prod spec: containers: - resources: limits: memory: "200Mi" cpu: "2" requests: memory: "100Mi" cpu: "2" name: my-app-nginx image: docker-registry.nginx.com/nap-dos/app_protect_dos_arb:1.1.1 imagePullPolicy: IfNotPresent nodeSelector: reserved: reserved priorityClassName: critical-priority
-
保存 Pod 后,使用
kubectl create
创建它:$ kubectl create -f pod.yaml pod/my-app created
在 Kubernetes 中有许多其他调整选项,可以确保调度器做出你所希望的决策。 也可以运行额外的调度器配置文件,或者完全用一个遵循 不同规则的客户配置文件替换默认的调度器。
这是一项相当高级的操作,因为它需要深入理解调度器的工作原理,以及如何根据需要进行更改以优化平台。 然而,如果您确实决定这是必要的,我们建议在替换默认配置之前,先从额外的配置文件开始。 替换默认配置。
重要说明
对于 OpenShift 用户,尽管最终结果相同,但在 OpenShift 中实现独占 CPU 的过程略有不同,并且使用了机器集(machine sets)。 请查阅最新的 OpenShift 容器平台文档,以获取有关该产品的最新操作指南。 产品相关的最新信息。
速率限制和网络健康
速率限制 是对服务响应请求者请求的网络流量或 HTTP 请求的限制。 每个响应都有计算成本,因此速率限制作为一种保护措施,防止端点被恶意利用,无论是故意的还是由于 错误导致的。
系统的常见摧毁方式是 分布式拒绝服务 (DDoS)攻击。 虽然此类攻击可以由恶意行为者发起,但也可能由于人为错误或软件缺陷而意外发生。 DDoS 是最经典的噪音邻居示例。 计算成本高,它可以通过大量请求淹没网络层,消耗计算资源,或者通过请求返回天文数字般庞大或计算上昂贵的数据来执行。 这些请求可能导致的数据返回需要非常庞大或计算量极大的资源才能获取。 获取这些数据的代价极高。
可以实现速率限制,以防止大量连续请求造成噪音邻居现象。 可以在集群入口(例如 nginx)中处理此问题。 但在 API 服务器中也有一些独立的 Kubernetes 功能可以利用。 同样可以使用。
在 API 服务器本身,您的团队可以设置 API 优先级和公平性规则,帮助防止流量泛滥。 如果噪音邻居问题来自内部源(即集群入口不再是一个因素),这尤其重要。 例如,如果备份作业配置错误,并且它试图从集群内部查询数据来创建备份,或者过于频繁地推送备份,应用速率限制可以帮助防止该作业淹没网络层。 默认情况下,启用了基本的优先级和公平性规则,尽管如果需要,完全可以更改或禁用这些规则。 然而, 不推荐这么做。
集群扩展和其他策略
还有哪些策略可以防止“吵闹邻居”情况? 自动扩展 是作者们最明显的选择,尽管肯定还有更多策略。 所有的调优和规划,如何将工作负载压缩到平台上,最终都无法避免集群需要扩展(或缩小)规模。 最经典的“吵闹邻居”场景,往往是由于平台规模过小,无法满足 用户的需求。
平台中的资源约束将决定集群如何扩展。 如果需要为在工作节点上运行的非系统组件提供额外资源,那么向集群中添加一个节点就可以解决问题。 然而,如果尽管已经采取了先前的措施,etcd 仍然填满,或者 API 服务器遇到约束,则需要更大的控制平面 来解决。
可观测性数据是 衡量何时以及如何扩展集群的最佳标准。 这些数据可以通过工作负载的计算资源利用率和工作负载性能来收集。 随着利用率的增加,如果剩余资源的阈值被达到或超过,则可以触发集群扩展事件。 您的可观测性数据也可以被更加创造性地使用。 一个常见的技术面试问题是排查问题。 解决方案总是一个定时任务(cron job)占用了过多资源,导致系统崩溃。 有了可观测性,您不需要人工去发现这一点;您可以使用数据。 无论是在 Pod 级别还是节点级别,资源利用的显著波动都可以被捕获并发出警报。 但如果您需要响应警报,那么这也是一个自动化的机会。
最后,如果所有方法都失败了,您可以使用事件驱动系统,基于可观测堆栈收集的数据反应,重新启动故障 Pod。 这能有效工作,并保持关键任务组件的运行,但它不能永久替代人为解决导致资源利用问题的根本原因。 最初的原因。
简而言之,保持集群健康和适当的用户与工作负载隔离是确保平台用户能够专注于最重要事项的关键,帮助我们为成功平台的下一个方面—— 自助服务。
启用自助服务开发者门户
我们已经明确指出,如果自助服务不是平台设计的核心要素,那么它就不是一个真正的平台。 现在,是时候理解这意味着什么,以及它如何与减轻认知负担的承诺相关。 平台团队不可能时刻在场来审批所有事情。 因此,将一些控制权交到最终用户手中是至关重要的,同时也不能增加他们的认知负担。 这种二分法需要找到一个小心的平衡点,这必须通过利益相关者之间的讨论和协商来达成成功。 平台团队如果过于紧密地控制一切,会阻止平台随着用户的发展而扩展,因为它将始终受限于团队的规模和位置。 这意味着现在是时候思考如何帮助用户自助。 开发者或团队应该能够在无需更高审批级别的情况下做什么? 哪些操作应该被限制? 如何实现合理的自助服务,又如何强制执行合理的限制? 我们已经讨论过将平台视为产品的重要性,但从功能上讲,这个产品类型是服务。 任何以“服务”形式销售的产品,不仅需要保证功能,还要保证公司提供的参与度和体验。 这适用于软件即服务(SaaS) 或 平台即服务(PaaS) 。即便你没有销售你的 PaaS,你也应该采纳服务心态,以确保用户始终处于你的 可用性考虑的核心。
简而言之,自助服务 意味着用户可以以合理的方式完成合理的操作,而不会对无关的用户或团队产生负面影响。 理解自助服务如何适应你的平台,首先需要了解用户的需求。 了解你的用户所在的工作环境非常重要。 如果你的用户是那种整天都待在命令行界面上的人, 那么 命令行界面(CLI) 可能是他们最需要的工具来完成工作。
然而,如果你的 IDP 被用于文档作为代码,而不是代码作为代码,那么使用它的团队可能会从 UI 中受益,而不是 命令行界面(CLI)。
最终用户希望如何实现他们的目标? 最终用户希望如何从平台作业中获取反馈? 当我们接近自助服务时,这些是我们需要问的问题。 一般来说,平台团队应该有一些开发者可以操作的参数。 例如,你可以保证某些工作负载只会落在特定的位置,并且可以将资源(内存和 CPU)限制绑定到你的 Kubernetes 集群中的命名空间。
提示
一旦你开始调整工作负载如何分配和消耗资源,这将开始人工影响集群容量。 在真正需要时,或者在创建了全面的集群扩展和 容量规划后,才应实施此策略。
强制执行配额
用户或团队 应该能够将他们的应用程序部署到应用程序着陆区。 然而,他们不应该在没有合理限制的情况下这样做。 这些限制,如资源消耗,可以在每个命名空间的基础上进行限制。 这可以通过指定 ResourceQuota 类型来完成。 你需要创建一个 YAML 文件,并将其应用到命名空间。 继续阅读,看看它将 是什么样的。
首先,创建一个 YAML 文件;我们称其为 your-dev-quota.yaml
:
apiVersion: v1
kind: ResourceQuota
metadata:
name: your-dev-quota
spec:
hard:
requests.cpu: "2"
requests.memory: 1Gi
limits.cpu: "4"
limits.memory: 6Gi
persistentvolumeclaims: "5"
services.loadbalancers: "1"
services.nodeports: "0"
保存文件,现在将 YAML 应用到命名空间。 你的命令看起来像这样: 类似这样:
kubectl apply -f your-dev-quota.yaml --namespace=your-dev-namespace
现在,你的开发者可以根据需要在他们的命名空间中安排任何 Pods,但工作负载必须保持在分配给他们的配额边界内。 为命名空间创建配额有助于在平台中实现多租户。 将资源分配给用户的方式可以在不超负荷集群的情况下利用集群容量,从而允许用户独立操作,但仍然在 合理的边界内。
配额也可以通过 GitOps 来管理,从而简化用户增加或更改配额的请求。 如果用户调整配额的过程是发起一个 拉取请求 (PR),然后 等待批准,这样可以使他们更容易与平台互动。 易用性将是平台在组织内成功的一个重要因素。 易用性将是平台在 组织内成功的关键因素。
简单的可重复工作流
我们谈到了 简化工作流对于减少认知负担的重要性,实际上,这个话题我们可以在本章的任何地方提到。 然而,这最符合自服务功能,因为它是核心特性。 为了让平台实现自服务的承诺,它必须易于使用,而且使用方式必须容易 记住。
我们再次提到我们的黄金路径。 一个开发人员构建应用程序并提交了一个提交。 该更改通过 CI/CD 系统直到最终,应用程序安装或升级被推送到应用程序落地区。 该 PR 的整个过程必须对开发团队来说易于理解、直观且可预测。 如果团队需要更改 CI 工作或与其应用程序特定的 CD 逻辑,那么他们应该能够轻松访问管理系统来进行 这些操作。
让我们重新回到 Financial One ACME 团队。 我们可以进一步扩展这个例子,再次设想公司包含多个角色或用户类型。 如果我们考虑一个开发团队和一个相应的文档团队,那么每个团队从 CI 系统中需要什么呢?
可以肯定地说,两个团队都需要能够在系统内完成他们的工作,了解工作是否成功或失败,并知道整体 CI 管道是否成功或失败。 然而,他们可能在与 CI 系统交互时有不同的需求。 开发人员或 DevOps 工程师可能更倾向于使用 CLI,而文档团队或非技术团队可能更喜欢使用 图形用户界面 (GUI) 来利用 这个系统。
开源 CI/CD 项目 Tekton 和 Argo CD 都配备了 GUI。 这两个系统还可以利用专门设计的 CLI 工具来与项目进行交互。 对于这些系统的用户,只要他们获得了正确的访问权限,他们可以根据自己的需求选择与这些系统交互的方式,并决定哪种方式更容易,保持本地 CLI 更新,还是偶尔使用 GUI。 通过灵活性,这些工具和你的 IDP(如果你采用它们)能够让用户根据 他们的便利性自助服务。
然而,对于像 CI 管道这样的任务,可能需要很长时间才能完成,其他选项,如 API 调用或 Webhook 集成到 Slack 等常用工具中,可能更容易满足用户需求。 就像输入一样,平台应该在输出时满足用户需求,因为用户需要频繁切换上下文的次数越少,他们的认知负担就越轻,自助服务目标就越容易实现。
重要提示
CI 管道 通常是一系列任务;其中一些同步运行,一些异步运行,以根据最近的更改构建和验证应用程序。 这可能是每次一个 PR(CI 的核心),但也可能不那么频繁,比如每天。
即使是高度技术化的用户也喜欢一个好的 GUI,如果他们已经在使用像 GitHub 或 GitLab 这样的工具进行 PR 审查,他们可能不想再改变范式。 因此,尽管普通用户可能永远不需要看到他们交互的 Kubernetes 集群的内部细节,但我们确实说过,平台应该“自酿香槟”。 这包括一些最佳实践,比如在用户所在的位置与他们会面。 一些 Kubernetes 解决方案,如 OpenShift,随附 GUI 层。 对于那些没有 GUI 的解决方案,Lens(https://k8slens.dev/)提供了一个 易于使用的 GUI。
图 6.6:连接到本地 kind 集群的 Lens GUI,代表一个 IDP
总之,开发者自助服务是关于在用户所在的位置与他们会面,并为他们提供成功使用平台的工具。 这意味着理解他们的需求,同时还要赋予他们将平台与他们工作方式整合的能力,而不是让他们的工作方式适应平台。 平台。
落地、扩展和集成你的 IDP
如果你按照我们在 第三章中的建议,已经采访了用户,已经绘制了用例,并且设计了你的平台。 现在是有趣的部分——将它变成 现实。
让我们来看一下最基础的 IDP。 它是做什么的? 最重要的是,用户可以通过适当的访问权限和权限来认证它。 它具有 CI 系统、CD 系统,至少一些基本的安全工具,如扫描功能,现如今还可以生成 SBOM。 那么开发人员编写的软件的生产位置在哪里呢? 它在哪里? 促销流程是怎样的? 让我们再看一下之前提到的 IDP 黄金路径 图 6**.3。
这个图中有很多组成部分,软件开发应该是迭代性的,这意味着我们不会一次性完成所有看到的内容。 所以,让我们来看看如何实现一个 IDP。 最简单可行的平台 是什么样的?
它可能是这样的 :
-
身份验证 和 RBAC
-
安全检查
-
CI/CD
-
一个应用 登陆区
从技术上讲,CD 系统并不是必须的,只要有某种促销过程将应用特性和增强功能推向生产环境。 采用这种精简的方法,或者最简可行平台,我们可以很容易地看到如何在一个组织中实现 IDP。 新产品的吸引力甚至可能促使用户去尝试,并帮助推动初期的采纳。 然而,保持这些用户的参与,并吸引更多犹豫不决的用户,需要扩展初始的提供内容,并确保用户使用的东西是易于理解的 且高效的。
不过,扩展你的 IDP——这看起来是什么样的? 扩展可能看起来像是容量规划,但随着平台采用的增长,你如何调整和配置你的资源? 在 Kubernetes 中,这意味着要确定集群的大小和/或 集群数量。
扩展还将包括特性和增强功能的添加,以及不仅是开发和推出的规划,还要考虑这些增强功能的运营。 增加另一个功能或集成会对平台产生什么影响? 如何衡量增强功能的成功,并且如何利用数据来确定其投资回报率? 投资回报率如何?
强制执行平台特定的标准
如果我们回顾本章开头,我们提到过,平台特定的标准对于终端用户来说应该容易理解,以帮助确保低认知负荷,并且这必须包括对 这些标准的强制执行。
没有一种固定的方法来做这件事,因为不同平台的规范可能会有所不同。 然而,我们发现有一些在过程和技术上都行之有效的做法,适合你 的组织。
成熟度模型
成熟度模型更倾向于认知负荷减少的社会技术方面。 为软件应用定义成熟度模型,并将其引入平台,可以帮助开发者确保他们以正确的方式构建应用。 这还可以确保认知负荷的减少不会由于忽略或遗忘某些内容而导致终端用户体验变差。 从本质上讲,它确保了双方的利益从 平台的角度是对齐的。
一个成熟度标准的例子是,新的应用程序只有在经过高可用性和灾难恢复配置后,或者有单元测试,或者团队已经编写并提交了他们的 Prometheus 查询和 Grafana 仪表盘以供应用可观测时,才会引入平台。 这些类型的模型可以通过 CI 作业进行强制执行,检查平台认为适当的内容是否存在,或者它们可以简单地作为指南发布给团队 进行遵循。
通过常见平台集成扩展平台
将一个平台从最薄的可行平台扩展到一个强大的系统,将需要利用新的集成并更好地利用最初的功能集。 一些常见且有用的集成在 本节中进行了概述。
静态分析
用户如何知道自己是否与预期的成熟度模型和 平台规范相匹配? 如果不匹配,他们如何获得反馈? 一种与用户建立这种执行和反馈循环的方法是通过静态分析。 当开发团队提交 PR 时,CI 作业会启动。 在第一次 CI 作业时,静态分析工具处于最佳位置;这样,如果 PR 失败,计算资源就不会浪费在运行测试或生成 SBOM 上,反馈循环也能更快地将错误信息反馈给最终用户。 减少反馈时间有助于保持开发人员的工作效率高,并降低认知负荷,因为开发人员不需要记得在较长延迟后再回去检查平台。
例如 可以在 PR 时使用的静态分析工具包括安全审计,这些审计将镜像版本与已知的关键漏洞利用 (CVE)进行比较, 以确保不会推送有漏洞的代码。 截至目前, Snyk 是一个流行的选择,因为它是开源的,并且提供免费的 选项。 CodeQL 是另一个流行的选择,也对开源项目免费,因此很容易找到它的使用示例。 这两款工具都很受欢迎,因为它们与 GitHub 工作流集成得很好。
然而,对于规范的执行,也有一些静态 分析工具,用于验证应用程序是否符合云原生最佳实践。 来自 StackRox 社区, KubeLinter 就是一个 很好的例子。 它带有多种检查,但可以配置为跳过不需要的平台检查,或者接受 自定义检查。
上述提到的静态分析工具都利用了 GitHub 工作流,这是一种声明式 YAML 格式,用于定义当 PR 被提交时,应该在仓库中执行哪些操作。 例如,要在 GitHub 工作流中对仓库的主分支运行 KubeLinter, .github/workflows
目录会被添加到 GitHub 仓库中,然后一个 YAML 文件会被提交到该目录。 该 YAML 文件的内容大致如下 kubelint.yaml
文件:
name: StaticValidation
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
lint:
runs-on: ubuntu-latest
# Checks-out your repository
- uses: actions/checkout@v4
- name: Scan yamls
id: kube-lint-scan
uses: stackrox/kube-linter-action@v1
with:
directory: app-of-apps
#config: .kube-linter/config.yaml
准入控制器和策略代理
Admission 控制器 是 Kubernetes 的另一个特性。 它们有两种形式,验证型和 变更型 Webhook。 两者都能减少认知负担,并启用自助服务。 然而,它们是相对高级的主题,可能不是你平台 MVP 的一部分,甚至可能不在前五名集成中。 随着平台的成熟,这些特性可以 被采纳。
如果变更型 Webhook 允许平台强制执行某个规范,例如对用户透明地设置环境变量,这让用户可以减少对平台本身的关注,更多地关注应用程序。 然后,如果环境变量发生变化,开发者在他们这边不需要做任何不同的操作。 验证型 Webhook 检查所应用的对象是否符合预期的参数。 你可以将这两种类型的 Webhook 用于工作负载,通常情况下,如果你使用变更型 Webhook,你应该始终使用验证型 Webhook,以确保 Webhook 响应后没有其他地方错误地修改对象。 已响应。
Admission 控制器是 强大的,但有些复杂。 它们需要设置服务器来运行控制器,并编写 Webhook 和响应。 由于它们的体积和复杂性,相较于其他平台规范强制执行方法,它们不适合新的 IDP,但对于拥有大量用户的大型组织,它们可以成为一个 强大的工具。
Admission 控制器可以是定制的,但开源 策略代理 例如 Open Policy Agent (OPA) 或 Kyverno 利用 相同的特性,并能帮助简化它们的使用。 利用基于策略的规范可以提升平台性能,并防止工作负载之间的冲突。 工作负载之间的冲突。
观察性
在下一节中将更深入地讨论,观察性 是 IDP 从第一天开始就应该具备的特性,并且可以扩展。 观察性应该被视为一个持续的集成过程,意味着它始终在增长和修改,以匹配 平台的生命周期。
一些常见的可观察性工具包括Prometheus、Grafana 和 Loki,它们可以单独使用,但与OpenTelemetry (Otel)及 Otel 协议结合使用时效果更佳。 当你将这些工具与 Thanos 结合用于长期存储时,你可以获得更多的数据来进行分析,这对于理解可观察性数据的上下文和历史非常有帮助。
这些工具分别专注于对 Kubernetes 对象的指标查询以及这些指标的分析、指标的可视化和日志聚合。 此外,这套工具可以被多个用户的单租户使用,或者作为一个多租户工具使用。 可以利用 IDP 的用户认证和 RBAC,就像我们之前讨论的 CI/CD 解决方案一样。
平台的 MVP 和黄金路径应该是初始可观察性实施的主要焦点。 可观察性应该以衡量用户满意度的方式进行,且 服务级目标 (SLOs)直接 映射到用户在平台上的成功。 因此,作为最佳实践,请确保黄金路径的核心组件可用,并为成功和延迟设定目标,同时确保平台团队能够利用可观察性来做出数据驱动的决策,包括跟踪某些功能的使用情况以及其中的失败 率。
举个例子,假设 Financial One ACME 最初发布了一个新的平台 MVP,包含了 Tekton 用于 CI,Argo CD 用于 CD。 但是,借助可观察性堆栈,平台团队发现只有 1%的用户在使用 Tekton 集成。 这表明 CI 需求可能在其他地方得到了满足。 凭借这些数据,团队可以决定弃用 Tekton 部署并回收一些集群容量,同时让用户继续以他们喜欢的方式工作。 另外,他们也可以深入理解为什么这个平台功能没有被使用,并寻求帮助用户采纳新的 CI 系统。
集成你的平台
平台集成 不仅仅是将各个组件集成在一起;它还意味着将 IDP 融入到公司的工作方式中。 这意味着服务优先的文化、以用户为中心、为运营卓越提供反馈回路,并且要与用户保持一致。
再一次,我们回到 Financial One ACME。 这家公司需要平衡现代技术栈和遗留技术栈。 在这里有一个机会,通过将现有的身份验证工作流整合到新平台中,来满足这些用户的需求。 如果他们已经在使用 GitHub 或 GitLab,那么他们很可能已经在使用某种单点登录,这应该与任何 基于 Kubernetes 的 IDP 兼容。
融入公司的工作方式可能意味着某些解决方案无法在当前满足用户的需求。 例如,如果 Financial One ACME 开发团队中 Tekton 采用率低的原因是因为 GitHub 工作流与 Argo CD 足以处理他们所有的 CI/CD 需求,那么与 Tekton 的整合可能是一个回报较低的努力。 正在重构单体应用的开发者,可能会发现能够使用共享 CI 工具会更有利。 这将帮助他们在旧的单体系统与新系统之间创建一组共同的测试和检查,确保在过渡过程中保持一致性。 在 Tekton 采用率低的例子中,事前分析决策可能防止了浪费时间,而这段时间可能更好地用于集成数据转换工具,如 Apache Airflow 或 Argo Workflows。
由于成功的 IDP 采用将依赖于数据驱动,一个关键方面是 可观察性。平台的 可观察性将有助于推动其对消费者和必须 维护该平台的团队的价值。
平台中可观察性的架构考虑
自助服务与认知负载——我们已经暗示过这些话题中可观察性的重要性。 没有优先考虑可观察性的情况下,你的平台无法为用户提供服务。 换句话说,你应该期望监控和 衡量一切。
可观察性 有两种类型;第一种是为平台团队利益而设的可观察性。 这就是平台团队如何收集数据和信息以帮助提高其可靠性。 通过使用 SLO 的站点可靠性实践,平台团队可以通过为平台设定 SLO 来衡量客户满意度,并通过创建可观察性来支持 这些目标。
好消息是,在 Kubernetes 世界中,由于对象和微服务,这一切变得更容易。 坏消息是,由于微服务,现在需要衡量的事物比以往多得多,而且很难知道自己是否已经捕获了所有信息。 你的可观察性应该有一个生命周期,像你的软件一样。 从良好的开始,逐步改进,并意识到可观察性是一个活的东西,可能永远不会达到一个 完成的状态。
在 Prometheus、Grafana、Loki 及其他 自由开源软件 (FOSS) 项目中,有一个标准的可观察性工具箱 ,它帮助观察和衡量工作负载,并提供对其操作的洞察。 通常,使用可观察性数据的消费者是 站点可靠性工程师 (SREs) 或 DevOps 工程师,但任何关心其应用程序运营卓越性的用户,都应能够轻松地审查和使用 这些信息。
同样重要的可观察性工具,适用于故障排查生产问题的工具包括网络追踪、存活探针和堆栈跟踪。 一个平台应该提供该工具链的实现和验证,并以受限方式向用户提供,确保安全性和合规性 得到维护。
自然,日志和遥测数据的保留策略引入了自身的扩展性问题,而日志记录和备份系统可能是最严重的噪声邻居行为的制造者。 因此,这些系统的护理和运维工作由平台团队负责,以确保其顺利集成到预生产和生产环境中。 关于可观察性的其他最佳实践,如清理日志并将平台和应用日志隔离开来,这些都是重要的,但我们将在 第七章 中更详细地讲解这些话题, 当构建一个 安全平台时。
平台中的可观察性
可观察性简单来说就是你 衡量平台的方式。 在基于服务的方法中,SRE 实践中的 SLO 或多个 SLO 的设计旨在维护黄金路径。 如果你考虑一下你的 SDLC,这条路径应该在规划阶段已经定义好。 一个支持用户的 SLO,确保他们在黄金路径上顺利前行,代表了一个观察性策略的最佳实践。
如果我们回顾一下 Financial One ACME,以及在这个组织中为成功的 IDP 定义的黄金路径,我们可以看到观察性策略开始发展。 平台团队可以衡量工作流中最关键项目的 Pod 健康状况。 如果 Argo CD 发生崩溃循环,或者某个组件 Pod 未准备好,那么你就知道 CD 相关的用户期望未能 得到满足。
如果任务因安全检查或 SBOM 生成失败,那么安全性和合规系统需要关注。 这可能会阻止 PR 合并,打断开发人员的工作流,他们需要弄清楚为什么必要的检查没有通过。 在整个黄金路径上,总有一些可以衡量的内容,能够帮助指导平台健康和 开发人员的满意度。
平台中的可观察性应该支持 SLO。 这些通过收集和分析数据来衡量客户满意度。 当我们讨论平台演进中的数据驱动决策时,在 第一章中,我们倡导它作为维持平台产品思维的一种方式。 SLO 是一个具体的数据驱动方式,能够创建一个反馈循环,你的团队可以 在此基础上进行迭代。
如果你的组织无法满足 SLO,那么用户可能会不满意。 然而,即便你满足了 SLO 目标,SLO 仍然可以改进。 开始时设置一个 SLO,例如“任务 80%的时候会成功”,然后将这个数字提高到 99.99%。 这样,你依然可以不断改进平台,而不会增加用户的认知负担。
重要提示
100%是一个糟糕的 SLO 目标,因为这是一个不可能达成的指标;然而,可以应用三九、四九甚至五九的可用性概念。
关于 SLO 的进一步阅读,我们推荐 《站点可靠性工程:谷歌如何运行 生产系统》。
集中式可观察性——你什么时候以及为什么需要它
当平台 遭遇重大故障时,会发生什么? 开发人员、DevOps 团队、SRE 或任何负责响应生产环境 故障的人如何弄清楚发生了什么? 立即登录生产系统进行调试和故障排除是一种选择,但这不是最安全的做法。 最终,可能有必要这么做,但它应当被视为最后的手段,而不是 第一步。
你怎么知道你的可观察性堆栈没有出现故障呢? 在单一集群的 IDP 中,这些问题很容易回答。 但是在多集群或多云的 IDP 中,这些问题变得更加复杂。 这就是为什么集中式可观察性堆栈对于任何应用程序的成功至关重要,包括 IDP 本身。 集中式可观察性堆栈使得环境无关的可观察性成为可能。 集中式系统可以连接到 IDP 集群并通过探针验证其存活状态,或者它可以仅仅在没有数据时发出警报。 如果 IDP 集群停止连接主系统或停止发送日志,那是一个很好的指示,说明存在 问题。
重要指标
由于平台可以对工作负载的运行方式和位置做出规范,它也可以对工作负载的测量方式做出规范。 例如,DORA DevOps 指标可以被收集并用于提供服务质量或服务健康状况的指示。 平台可以通过提供服务来支持这一点,以暴露必要的数据点,或者它可以通过强制收集必要的指标来执行指标计算,因为它是环境的控制实体。 的环境。
虽然 SLO 和服务级别指标不能完全标准化跨多个服务,平台应该支持收集、聚合和保留任何关键应用程序指标的策略。 这些指标必须易于获取,并且具备适当的 RBAC 权限控制。 这使得用户可以自助定义指标并排除软件中的问题。 软件。
为开发人员提供的可观察性服务
平台的消费者的可观察性 与平台团队可能看到和操作的情况略有不同。 尽管工具箱大体相同,但所需的数据有所不同。 除了他们的应用程序性能和正常运行时间,平台上的开发人员更关心失败的 CI 任务、DORA DevOps 指标、应用程序日志和异常。 他们需要能够查看所有的可观察性数据,而不包括平台级别的可观察性或其他租户的可观察性。 这不仅有助于维持安全性和合规性,还减少了认知负担,因为来自所有源的数据可能让开发人员难以理解他们需要什么数据,以及如何 解读这些数据。
有几种方法可以实现将清晰的信号传递给开发人员。 Thanos 的可观察性服务专门设计为 Prometheus 指标的长期存储解决方案,原生支持多租户。 查询语言仍然是 Prometheus 的 PROMQL,这为 Thanos 中与可观察性数据的交互提供了一种熟悉的方式。 然而,它确实需要一定量的专用云存储来支持 其操作。
当我们深入探讨在平台中存储和提供可观察性数据的含义时,平台扩展性的问题会呈现出新的形式。 我们将在后续章节中更具体地讨论成本管理。 不过,现在我们可以先看一下几个高层次的考虑因素。
由于设置 Thanos 需要创建对象存储并允许它与平台之间的访问,它为平台增加了另一层复杂性。 必须设置存储,还必须创建并维护将其与平台其他部分连接的网络路径。 对于需要长期存储可观察性数据的大规模操作而言,这样做是有回报的,但对于较小的 IDP 或平台的早期版本来说并不理想。 相反,Prometheus 也可以以相同的方式使用,但它还可以通过本地模式将数据保存在持久卷中,或者根本不持久化数据,这意味着如果 Prometheus Pod 重新启动,它曾经知道的指标将 丢失。
在一个 IDP 内,团队可能对指标的长期保存有不同的需求;因此,使用联邦的多个 Prometheus 实例是一种相当常见的模式。
联邦本质上是一个 Prometheus 实例从另一个实例抓取所需的数据。 这些实例可以位于同一集群中,也可以跨不同集群。 在多租户身份提供者(IDP)的使用场景中,Prometheus 联邦是一个合乎逻辑的选择。 平台拥有应用程序的着陆区,因此它将持有其可观察性中的资源使用真实数据源。 然而,如果开发团队也使用 Prometheus 通过金丝雀路由和合成探针等方式来衡量其应用程序的健康状况,那么将这一数据开放给所有用户既不明智也不安全。 开发人员可能希望将这些数据与平台拥有的数据结合起来,以全面了解其应用程序性能。 通过允许开发人员的 Prometheus 实例以联邦方式访问所需的数据,开发人员可以在不超出其 RBAC 的范围内获取这些信息。 此外,这种模型通过创建有用的信噪比 来保持低认知负荷。 信噪比 是开发人员接收到的所有信号数量与可操作信号和噪声的比例。 开发人员暴露的噪声越少,他们就能越有效地解析所需的数据 进行审查。
实际实现并不困难;只需在 Prometheus 的 YAML 文件中添加几行新代码。 关键要点是,应用程序可以从平台和任何必要的共享服务中获取信息,但不能相互获取信息。 由于应用程序级别的可观察性数据更有可能包含个人识别信息或其他敏感数据,因此这有助于平台团队确保遵循安全性和合规性最佳实践或要求 。
这个例子的推荐配置是跨服务联邦。 在这种情况下,最常见的模型是拥有一个中央 Prometheus 服务器进行联邦操作,并由其他可能不支持联邦的 Prometheus 服务器抓取数据。
图 6.7:Prometheus 联邦模型
平台 Prometheus 实例的 YAML 看起来会像这样:
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
primary: 'platform-prometheus'
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 5s
static_configs:
- targets: ['api.example.com:3000']
- job_name: 'federation'
scrape_interval: 60s
honor_labels: true
metrics_path: '/federate'
params:
'match[]':
- '{job="github-exporter"}'
- '{job="namespace-cpu-usage"}'
static_configs:
- targets:
- "prometheus.tenant-1-app:9090"
- "prometheus.tenant-2-app:9090"
在 图 6**.7 示例中,存在一个共享服务,理解哪些服务对多个应用程序的可观察性栈很重要可能会很困难。 一个使用场景可能是 DevOps DORA 指标。 在 DORA 中,如果团队希望实施 DORA 成熟度模型,他们会衡量四个关键领域:
-
恢复的平均时间:从故障中恢复所需的时间
-
变更失败率:部署导致失败的频率
-
变更的提前期:变更到达生产环境所需的时间
-
部署频率:变更部署的频率
要获取这些数据,需要查询 CI/CD 系统,或者可以配置它们通过 Webhook 发送数据,并使用 Prometheus 来获取和标准化这些数据,从而使所有比较的数据以相同格式存在,这样可以简化计算。 其实现细节将涉及一个 Prometheus 导出程序。 Prometheus 导出程序是一个服务,它从可能不是立即支持 Prometheus 的系统中查询数据,然后将其转换为兼容的数据格式。 例如,为了计算变更的提前期,Prometheus 导出程序会从 CI 系统获取数据,标准化后,通过 Prometheus API 查询或联合查询使数据可用。
无论你最终使用何种可观察性实施路径,数据及其重要性都应对所有能够利用它的人开放,以支持平台和它所支持的产品的成功。 在这样做时要小心,同时也要理解数据是用户的一个关键资产。 对于用户来说,数据至关重要。
为社区和协作开放你的平台
我们经常使用 术语 开源,但开源并不总是意味着免费,反之亦然。 然而,当我们讨论 FOSS(自由开源软件)时,软件最重要的方面不是技术,而是其背后的社区。 开源社区促进协作,并帮助以革命性的方式自然发展软件, 从而改变整个行业。
当我们之前讨论了解用户时,主要是在启动阶段的背景下,但你需要保持相同的以用户为中心的思维,以确保产品持续运行并保持相关性。 充分利用用户的最佳方式是将他们带到团队的桌面上,创建一个像 FOSS 项目那样的社区,邀请合作、贡献和交流。 通过这种方式,你可以拥有一个数据驱动、用户主导的平台,随着服务的组织的不断发展而不断演化。 许多时候,一个项目会起步,但其后续的路线图却模糊不清。 随着初期推进 IDP 的兴奋感消退,失去动力和变得自满的风险随之而来。 社区的作用应该是充当燃料,保持 车辆前行。
在这一点上,你可能会问,我们该如何创建这个社区呢? 我们如何在不破坏我们为平台所努力减少的认知负担的情况下,将人们吸引到一起呢? 我们的平台该如何实现这一目标?
对此并没有一种魔法般的解决方案,而是可以通过几种方式同时进行,例如在代码库中添加贡献者指南,设定对期望的贡献类型以及如何评审贡献的明确要求。 贡献将如何被审查。
另一个方面是,你可能会在平台中加入一些工具,以帮助吸引合作。 例如,策略引擎能够吸引合作,因为用户可以提出新的策略或对现有策略进行调整。 通过设立一个面向最终用户开放的策略库,你可以邀请用户为政策提出 PR,提出新的权限或调整现有权限。 正如我们在自助服务中讨论的那样,这利用了 Git PR 和 同行评审的已知有效工作流程。
类似地,环境配额的使用是一种可以通过 GitOps 进行管理的配置;通过 PR 请求更多配额,并利用一些 GitHub 工作流帮助开发者申请配额。 在这两个例子中,利用 Git 仓库和 PR 允许用户以更直观的方式与平台互动,更符合他们的工作流程。 再一次,站在“客户”的角度帮助减少他们的认知负担,同时吸引合作是确保平台能够继续满足他们需求的必要条件。
一个平台应该自饮 自己的香槟。 如果产品安全团队对应用程序提出了平台必须支持的要求,比如生成 SBOM 或自动化渗透测试工作,那么平台本身也应该按照相同的安全标准进行评估。 这样做不仅是一次智力诚实的实践,确保安全标准的执行方式合理,并且不会妨碍服务性和自助服务,而且还是与内部社区互动的途径,推动平台的开发和加固。 平台。
一位有洞察力的读者可能会问,“你如何调和用户认知负担的减少与他们在 平台体验中的参与之间的矛盾?”
这是一个重要问题,因为无休止的反馈请求可能导致沉默,尤其是当用户认为反馈不会被采纳时,生成反馈的心理负担可能会非常重。 被采纳。
没有一种保证能成功的单一方式来获取社区参与,但当我们回顾 FOSS 社区中的一些最佳实践时,我们可以看到 一种趋势。
开放式规划
定期的计划会议和社区会议,由平台团队参与,并且对任何人开放,帮助创建和培养围绕平台建立开放协作的社区文化。 在一个 Kubernetes 项目中,大多数项目会议都在公开日历上,个人可以订阅邮件列表,及时了解每个项目的最新消息。 定期的产品更新和计划通过邮件发送,且公司内部公开透明,有助于保持兴趣和参与,希望能带来必要的积极反馈,以确保 IDP 的长期成功。
接受贡献
接受贡献可能是部署和扩展 IDP 过程中最具后勤难度的社会技术性方面之一。 然而,一些开源 IDP 项目非常适合这种范式。 最近的开源宠儿 Backstage 提供了许多其他功能,其中包括一个支持插件的架构。 换句话说,当一个团队发现缺少某些功能或能从一些额外的生活质量特性中受益时,通过将 Backstage 作为 IDP 框架来铺设,他们可以利用插件框架通过 PR 向内部 IDP 项目提议新功能。
虽然你不必使用这个精确的框架来实现相同的结果,但 Backstage 所展示的模式能够让用户轻松贡献,并通过动手贡献而不是仅仅向平台团队的待办事项列表发送请求,提供一种标准化的变更提案方式。 团队的待办事项列表。
总结
在本章中,我们探讨了如何为开发人员构建平台以及认知负荷的重要性。 保护用户免受不良结果的影响,有助于减轻他们的认知负荷,减少他们的担忧。如果出现问题,减少需要调试的内容,可以帮助开发人员保持高效工作。 此外,我们还探讨了认知负荷与自助服务之间的关系,并审视了一些常见的工具和模式,以提供一个能够满足用户需求的平台。 如果你正在使用集群并跟随学习,可能还会获得一些关于这些建议的实践经验。 虽然我们简要提到了每个主题的安全方面,但在下一章中,我们将深入探讨这些内容以及更多话题。 下一章。
第三部分 – 作为产品的最佳实践平台
在上一部分,我们将为你提供优化平台以实现成本效率的工具,从而帮助用户也能做到这一点。 我们将通过概述在你的成本环境中建立透明度所需的简单步骤,并为你提供减少基础设施费用的最佳实践来实现这一目标。 从基础设施成本开始,我们将讨论技术债务,如果不正确处理,它们可能会对所选技术栈的维护成本产生负面影响。 你将学习如何使用工具和框架评估技术债务,以及记录决策的重要性。 最后,我们将展望未来。 你将了解到变革的必要性,以及为什么你必须成为推动变革的积极一员。 你可能会发现一个关于你的黄金道路的新视角,以及一些关于未来相关技术的想法。 相关技术。
本部分包含以下章节: 以下章节:
-
第七章, 构建安全合规的产品
-
第八章, 成本管理与最佳实践
-
第九章, 选择技术债务以避免平台崩溃
-
第十章, 为未来打造平台产品
第七章:构建安全和合规的产品
在数字化时代,网络犯罪日益增多。 虽然并非每个组织都有银行或政府机构那样严格的合规要求,但那些高度监管环境的安全标准和最佳实践可以并应该被推广到你的平台。 每一层次的安全性都能帮助防止安全漏洞的发生, 从而减少风险。
在本章结束时,你应该能更好地理解安全标准、框架和趋势。 这包括理解和 利用 软件材料清单 (SBOM),了解开源项目如何提升平台安全,以及理解策略引擎技术(包含示例和用例)。 你应该能够利用这些学习,定义正确的行动来保障平台安全,同时不限制你的功能,并确保应用交付过程提供加固且安全的 软件/容器包。
因此,本章将涵盖以下主要内容:
-
调和安全左移与 零信任
-
理解平台安全——如何构建既安全又灵活和 开放的系统
-
查看 SBOM 实践
-
理解流水线安全——你需要考虑的事项,以确保你的 持续集成/持续交付 (CI/CD) 流水线
-
理解应用安全——制定并 执行策略
-
自由和开源软件 (FOSS) 在平台安全中的应用以及如何 使用它
调和安全左移与零信任
安全左移 和 零信任 是当前网络安全领域的 流行术语。 这些术语——或者说是流行短语——无疑会逐渐消失,但它们所代表的实践将在未来几年继续作为最佳实践。
安全左移 将构建和交付软件的过程视为一个从左到右的线性流程图。 该流程图可能会像这样:
图 7.1: 简单的应用程序开发工作流
在这个简化的示例中,开发人员编写代码,代码随后被放入源代码管理,最终作为应用程序供用户使用。 查看右侧的这个安全工作流程,尤其是在应用层面本身,虽然重要,但为时已晚。 已经有三个明显的地方,缺乏安全性可能会造成漏洞,这些漏洞可能 被利用。
在人员层面解决安全问题是向左安全的核心,但这并不是安全的终点;它只是安全的起点。 安全必须贯穿于流程图的每个步骤,以便当我们进入更现实的示例时,能够看到安全如何随 业务范围的扩展而扩展:
图 7.2:扩展的开发和交付工作流程
在前面的 图中,你已经可以看到在用户尝试与应用程序及其支持基础设施互动时,实施的一些常见安全最佳实践。 然而,开发团队和开源依赖项层面的安全性,源代码管理中的安全性,CI/CD 中的安全性,以及应用程序本身的安全性并未被解决。 即便是密钥和机密的存储,虽然代表着一种最佳实践,也需要对访问这些密码的过程应用安全措施。 向左的安全帮助你从产品生命周期的开始,到将完成的应用程序交付给最终用户的全过程中构建安全故事。 在平台工程中,这可能会在最安全的平台和实现完美自助服务的平台之间产生不和谐感。 由于平台需要支持开发人员的自助服务,因此平台完全拥有安全故事的做法开始与自助服务所带来的灵活性相冲突。 因此,它可能会施加足够的限制,使得平台显得有很高的准入门槛,从而危及平台的采纳以及 开发人员的幸福感。
在过去, 信任,但要验证 将是解决这个问题的安全模型。 其含义不言自明。 你信任开发人员已经做了所有必要的工作,以维持应用程序所需的安全态势,但在平台团队那边,你并不拥有端到端的安全控制。 该平台会尽最大努力验证所有正确的措施已经到位,而不会干扰 自助服务。
如今,安全最佳实践发生了变化, 零信任 成为了主流。 零信任 本质上假设每个人都是恶意行为者(无论是否故意都不重要)。 为了保持这种安全姿态,平台需要遵循最佳实践,但它不能承担应用程序的责任。 换句话说,平台需要为开发团队和利益相关者提供所有必要的支撑,以支持一个安全且合规的产品。 例如,如果需要使用 Python 语言,那么可以在镜像注册表中提供一个经过安全处理的 Python 二进制文件,无论是内部加固的,还是来自受信任的供应商,所有平台的用户和应用程序都可以访问。 使用来自 Docker 注册表的 Python-Slim 镜像也是一个更安全的选择,并且更容易获得。 对于大多数使用场景,Slim 镜像应该是足够的。 从这里出发,一个合理且自服务的限制是拒绝那些不使用已知安全来源镜像的工作负载。 在 CI 管道中可以处理这个检查。 尽可能将检查推到最左边可以节省每个人的时间,同时也避免了在不符合安全姿态的更改上浪费计算资源。 然而,在这一部分添加检查可能会有点痛苦,因为它需要编写一个作业来扫描、分析,然后根据存储库中所有 Dockerfile 的内容做出决策。 这些文件可能嵌套在子目录中,虽然这不是不可能完成的挑战,但确实可能比较麻烦。 此外,将这种功能写入 CI 管道应该被视为超出普通 平台团队的职责范围。
从平台的角度来看,另一种方法是使用策略引擎和准入 Webhook 来拒绝任何未使用受信任镜像源的 Pod 定义。 这虽然没有达到理想的最左边,但希望开发团队及其遵循的流程能够避免这种情况的发生。然而,在零信任环境中,这项政策将作为最后的防护措施,确保只有正确的软件被推广到生产环境。 可以认为,来自更多公共来源的镜像对于 IDP 中的原型设计是可接受的,因此,防护措施仅对生产环境是必要的。 这使得平台可以继续为团队服务,而不会不必要地 妨碍他们。
另一个例子是,只有当提交被签名时,才能接受将提交推送到 GitHub 仓库。 签名表明作者及代码自签名后未被篡改。 这可以通过 GitHub 仓库中的 webhooks 来强制执行,尽管平台团队对任何公司的 GitHub 组织的影响力可能有限;如果有的话,安全团队可能会要求此项操作。 尽管这是“零信任”与左移安全性共同作用的一个很好的例子,但这很可能超出了 平台团队的范围。
虽然这些短语听起来像是空洞的格言,但如果团队忽视它们所代表的基本原则,就会失败。 做得好的安全性是公司可以做出的最佳投资之一。 将安全性左移意味着尽早进行测试并频繁地进行测试。
每一个重大的安全漏洞和风险都可以通过测试被发现。 有各种类型的测试,包括一些由安全专业人员执行的测试,但无论是渗透测试还是仅仅是质量工程过程中的基础负面测试,都应该定期测试安全性和合规性,以确保没有意外的表面区域。 这可能表现为以意外的方式使用软件,或者仅仅是验证组织内角色的权限设置 是否正确。
现在我们已经介绍了左移安全性和“信任但验证”这两个概念,接下来让我们看看如何构建一个既安全又灵活的系统 和灵活的系统。
理解平台安全性——如何构建一个既安全又灵活、开放的系统
平台并不是组织安全态势的全部;它只是方程式的一部分。 在评估如何将网络安全或 DevSecOps 集成到平台时,必须保持平衡。 将安全性左移有助于减少平台团队需要投入的努力,但明确和清晰的安全范围有助于每个人理解自己在 安全故事中的角色。
将问题拆解成可消化的小块
安全性和灵活性也可能让人觉得是两个完全对立的词。 良好的安全性本质上是僵化的;然而,对于 IDP(身份验证平台)的成功来说,平衡这两者是可能且必要的。 我们如何实现这一点呢? 第一步 是 定义安全范围。
范围界定的第一步是了解所需的最低安全水平。 显然,我们总是应该做得比最低要求更多,因此,如果你想了解最高安全水平可能是什么样的,虽然这不是一个坏主意,但它可能会人为地增加范围,使你无法看到整体的全貌。 因此,我们建议从一个狭窄的重点开始。 一旦你知道平台必须执行的最低安全级别后,就可以开始考虑平台 必须支持的最低安全级别。
当你走上安全这条道路时,很容易迅速意识到互联网世界有多么危险,从而产生过度修正。 这些过度修正可能会增加认知负担,并为使用平台设置进入障碍。 如果一个优秀的开发人员懒惰,那么平台应当帮助他们懒惰,而不是引入额外的复杂性。 正因为如此,尽管平台不能承担组织整个网络安全姿态的责任,但它在这一姿态中扮演着至关重要的角色。 安全是好的;但安全表演 却不是。
了解多少是过多,多少是恰到好处,是一种随着时间推移而不断磨练的技能,并且在什么时候一个安全措施过多或恰到好处,没有明确的界限。 答案永远是: 视情况而定。例如,确保一个环境不对公共互联网开放,可能会妨碍项目人员在家办公,但如果这个项目涉及航天飞机或核反应堆,那么这种“空气隔离”环境是正确的,而不是 过度修正。
许多安全专家花费了多年时间研究安全,并定义了什么是安全和合规的明确标准。 美国 国家标准与技术研究院 (NIST) 是美国商务部下属的一个部门,汇聚了这样的专家,他们定期发布新的标准并随着 行业的发展更新现有标准。
了解这些组织的工作有助于你在发展对安全性和灵活的 内部开发者 平台 (IDP) 交集的理解。 由于这些机构通常发布针对与政府合作的公司的标准,值得注意的是,它们的出版物是针对特定类型的受众,并不能替代与在你 特定行业中有经验的网络安全专家的交流。
解决 OWASP 十大安全问题
如果你仍然不确定从哪里开始,另一个组织发布了可以作为界定安全范围的极好起点的指南。 这个 开放全球应用安全项目 (OWASP)(owasp.org)是一个 专注于网络安全的非盈利组织。 作为一个受人尊敬的安全专家团体,他们的 十大安全问题 列表已成为预见并防止软件安全问题的重要指南。 他们会定期重新发布此列表,2021 年出版的版本就是他们的 当前列表:
-
A01:2021–访问控制 破坏
-
A02:2021–加密失败
-
A03:2021–注入攻击
-
A04:2021–不安全的设计
-
A05:2021–安全配置错误
-
A06:2021–脆弱和 过时的组件
-
A07:2021–身份识别和 认证失败
-
A08:2021–软件和数据 完整性失败
-
A09:2021–安全日志记录和 监控失败
-
A10:2021–服务器端请求 伪造(SSRF)
OWASP 更进一步,2022 年还推出了针对 Kubernetes 的 十大安全问题 列表:
-
K01: 不安全的 工作负载配置
-
K02: 供应链漏洞
-
K03: 过于宽松的 RBAC 配置
-
K04: 缺乏集中式 策略执行
-
K05: 日志记录不足 与监控
-
K06: 破损的 认证机制
-
K07: 缺失的网络 分段控制
-
K08: 机密管理失败
-
K09: 配置错误的 集群组件
-
K10: 过时和易受攻击的 Kubernetes 组件
这些列表大致匹配,但都适用于基于 Kubernetes 的身份提供平台(IDP)。 这些列表不应被视为全面的安全姿态指南,而应被认为是 IDP 安全姿态中需要解决的最基本事项,但仍然是启动你的 范围项目的一个全面起点。
实施威胁建模
在你定义安全范围后,第二步 是 威胁建模。威胁模型是对可能影响你的应用程序或在本案例中平台安全的所有因素的表示。 执行威胁建模是如何得出正确结论的一个典范,这对于组织的安全姿态至关重要。 你可以使用这些 十大 列表来引导你关于威胁建模的讨论。 根据 《威胁建模宣言》(threatmodelingmanifesto.org)的作者,威胁模型应当回答以下 四个问题:
-
我们正在 做什么?
-
可能会 出什么问题?
-
我们将如何处理 这个问题?
-
我们做得够好 吗?
例如,你可以从以下开始: 我们正在研究用户如何向 IDP 进行身份验证。然后,接着讨论 过于宽松的 RBAC 配置可能会出什么问题? 并逐一处理 Kubernetes 列表中的每个 前十名 项目。 这些问题看似简单,但随着第二个问题(可能会出什么问题?)和第一个问题(我们在做什么?)的比例是多对一的,第三个问题(我们要如何解决?)与第四个问题也有类似的关系。 无论如何,如果 我们做得够好吗? 的答案不是一个决定性的“是”,那么这些问题的循环应该继续。 成功的威胁模型和应对计划是通过与所有 平台利益相关者的协作进行的。
这种在安全方面的协作是成功将安全置于中心的一项重要策略,且不牺牲可用性、接受贡献的能力或自助服务。 正是通过协作的威胁建模过程,可以解决安全方面的社会技术风险。 “安全向左”不仅意味着到达开发者的电脑,甚至包括开发者本人。 确保他们采取适当的预防措施,了解恶意行为者如何试图操控情况以窃取凭证,并且通常遵循最佳实践——例如,不要将公司笔记本电脑留在车里,以免被盗。
常见的安全标准和框架
进入安全标准的世界,首先需要尝试弄清楚一系列缩略语的含义。 目标并不是一夜之间成为安全专家,而是了解自己所需的安全级别,并确保平台做出一切必要措施来符合该安全标准。 解决这个问题的一个简单方法是,查看你所在公司所服务的行业以及相关的安全框架。 例如,美国的医院或大型医疗集团需要 遵守 健康保险流动性与责任法案 (HIPAA)合规性要求。 因此,任何与类似机构合作的供应商,无论其所在地如何,都需要能够遵守相同的标准。 通过了解最终用户和开发团队的需求,平台团队可以确定超越标准最佳实践之外所需的安全性和合规性级别。
让我们快速概述一些安全标准。 这并不是一个详尽无遗的列表,但涵盖了一些更常见的标准: 常见标准:
标准 | 地区 | 级别 | 描述 | 备注 |
---|---|---|---|---|
PCI DSS | 国际 | 1-4 | 支付卡行业数据安全标准。定义了安全性和合规性要求。 适用于任何处理、传输或存储信用卡信息的公司。 级别基于 交易 量。 | 由信用卡品牌创建,而非 政府机构。 |
DPDPA | 印度 | 不适用 | 数字个人数据保护法。定义了个人数据的处理方式。 | 印度政府在 2023 年通过了该法案,虽然它与 通用数据保护条例 (GDPR)相似,但也有显著的差异。 |
FedRAMP | 美国 | 中等, 高 | 联邦风险与授权管理程序。定义了提供软件和服务给 联邦政府所需的安全性 和合规性。 | 美国 联邦政府;与 州政府不同。 |
HIPAA | 美国 | N/A | 健康保险可携带性与责任法案 。 | 这是 90 年代设定的标准,随着技术的发展,必须不断演变 以适应新需求。 |
DGA | 欧洲 联盟 (欧盟) | N/A | 数据治理法案。定义了 欧盟在数据使用 和共享方面的政策。 | 适用于 公共部门数据和数据利他主义的背景,以及什么可以共享,什么不能共享。 填补了 GDPR 标准中的空白。 |
GDPR | 欧盟 | N/A | 个人数据如何使用 以及如何不使用。 | 这是一次历史性的举措,彻底改变了全球的数据 处理方式。 |
表 7.1:安全性与合规性框架说明
虽然这些框架有所不同,并且它们旨在实现不同的目标,但从核心上讲,它们是相同的。 由应用程序捕获和存储的数据必须在传输过程中和静止时都得到保护,并且它必须仅进入预期的位置,只有预期的用户可以访问。 这一点部分通过 RBAC 实现,但仅靠 RBAC 不足以保障安全。 诸如 PCI DSS 这样的安全标准包括对硬件、服务器及其物理设备和存放位置的实际检查,才能获得合规认证。 合规性和安全性通常是紧密相关的,但实际上是不同的。 一个系统可以是安全的,但不合规,反之亦然。 尽管我们在此不会深入讨论这些差异,因为它们超出了 IDP 本身的范围,但了解安全性不仅仅是勾选清单上的框是非常重要的。 这些框应该有助于指导威胁建模的工作应在系统中扩展到何种程度,以及你如何在任何 受监管行业中开发平台的角色。
资产保护
坚持数字空间的范式,我们讨论的资产是你服务的数据库。 大多数安全性和合规性法规关注数据处理。 你应当理解为,数据是普通组织最有价值的资产,应该被 珍视。
你的数据有三种状态:它要么在传输中,要么在使用中,要么处于静止状态。 因此,针对它的安全性必须涵盖所有状态。 由于数据存储在平台上,所以平台有责任确保这一部分的安全性。 这是一个极少数不需要极高安全保护的数据产品,因此,确保数据安全几乎不可能过度。
静态数据保护
你的数据将 大部分时间处于静止状态。 数据设计和整体数据库安全需要非常具体的关注和定期审查,但静态数据的高级概念 归属于 以下几个类别:
-
分类
-
这是什么类型的数据,它包含了什么信息?
-
它有多重要? 重要性如何?
-
根据监管需求或 业务重要性进行数据的物理隔离
-
数据如何与系统交互可以 为分类提供信息
-
-
加密
- 每一层的加密,包括物理层和 数字层
-
盐值 和哈希
-
不仅仅是加密, 还有压缩
-
如果你希望数据 保持人类可读性,这就不太理想
-
在内存中可能会大幅扩展,并可能创建 一个意外的 分布式拒绝服务 攻击 (DDOS)
-
-
-
限制访问
-
可以根据 分类进行调整
-
应该有正式的审查流程, 角色与职责
-
-
冗余备份
-
三是高可用和 低可用系统的魔法数字
-
数据的冗余不需要完全一致;可以根据数据丢失的成本来设计策略 数据
-
-
数据 保留政策
-
你 到底需要它保存多久? 是否有 相关法律 约束?
-
那 监管链 是什么 关于 那个 决策的?
-
与冗余相同;不需要为 所有数据制定统一政策
-
-
你首先需要它吗?
-
挑战 每一部分:
-
膨胀发生在当人们认为数据 是必要的
-
不确定性让人们要求比 他们需要的更多
-
展示数据如何增加价值和责任,并使得后续使用和 成本合理化变得容易:
-
积极地减少 没有正当理由的部分
-
记录系统如何以及为什么被创建的历史 过程
-
员工入职 变得容易
-
回答关于系统的问题变得容易,包括 不公平的问题
-
公共汽车编号失去 其重要性
-
-
保留 这些数据的商业理由是什么?
-
它会 带来利润吗?
-
这能 节省资金吗?
-
我们会从中 学习吗?
-
做这件事 是正确的吗?
-
我们面临哪些风险 如果我们保留它的话?
-
-
-
操作使用案例是什么? (例如:故障排除项目、访问日志、 审计跟踪)
-
-
-
-
热 存储 和 冷存储
-
热 存储更 容易访问
- 需要 访问规则
-
冷存储则不 那么容易访问
-
不需要频繁关注但仍然重要的旧数据进入 冷存储
-
更难访问,通常由更高级别的 权限控制
-
-
数据主权
许多 国家正在采纳数据主权 法律,本质上规定在该国边界内由人们创建的数据不得离开该国的物理边界。 这并不总是意味着数据不能在国外查看(使用中的数据),而是数据存储必须保持在区域边界内。 这解决了静态数据合规性问题,但并没有解决 安全性问题。
保障数据在传输中的安全
当 数据在传输过程中,它正被微服务之间传输,这意味着它暴露在平台的网络和/或外部端点中。 数据在传输过程中需要加密,但最终,数据需要被使用,接收端点将在某个时刻解压数据:
-
限制存储在内存中的数据及其存储时间—这能保护平台的健康和数据安全(要聪明地管理 缓存)
-
不要在广泛开放或特权的端口上传输数据
-
仅记录数据交易中绝对必要的信息,而不是交易本身的数据(参见日志清理)
-
通过 清理输入 来防止注入攻击
-
使用网络安全和加密的最佳实践来防止 中间人 (MITM)攻击 以及其他各种类型 的攻击
使用中的数据
使用中的数据 正如其字面意思所示:系统正在查看或 更改的数据。 数据一旦存储并在使用中,它要么从存储中检索,要么被缓存。 它可能保存在内存中,或者通过各种读取和缓存技术直接读取。 数据在初次进入系统时也会处于使用状态。 这包括注册新用户或存储新的日志行。 通常,处于这种状态的数据也可能正在经历数据转换,如聚合或清理操作,以确保数据不能用于注入攻击,甚至可能被删除。 保护使用中的数据,只是我们在数据传输或静止时所应用的相同原则的另一种体现。
保护你的网络安全
Kubernetes 具有可插拔架构,虽然它默认没有网络栈,但某些 Kubernetes 平台选项会有自己的默认设置。 例如,OpenShift 容器平台采用了默认的 容器网络接口 (CNI),并称其为 开放虚拟网络 (OVN)。 除了 OVN,还有其他更安全、可观察性更强的解决方案适用于 Kubernetes 网络。
**
除了网络技术外,网络拓扑在网络安全中也扮演着重要角色。 像防火墙、VPN、VLAN、路由器、交换机等网络工具可能不会直接部署在 Kubernetes 集群上,但它们在集群安全中起着非常重要的作用。 无论最终的网络拓扑如何,集群如何与公共互联网交互(或者可能根本没有交互!),为了进行适当的威胁建模和合规性,你需要能够观察并记录 你的网络。
预生产环境与生产环境之间的隔离
一般的最佳实践 是无论安全性和合规性需求如何,你的系统都应该将生产数据和访问权限与其他环境隔离,以确保数据受到保护。 数据是大多数公司最有价值的资产,因此保护和隔离数据是确保资产安全的最佳方式,能够保证所有公司的安全性和合规性。 数据保护是安全性和合规性的核心。 生产数据绝不能离开生产环境,且必须严格控制对这些数据的访问,确保没有恶意行为者——无论是内部、外部、故意还是意外——能够访问生产数据。 我们再次引用我们的平台架构,来自 第二章:
图 7.3:平台参考组件
每个白盒,即便 是与安全相关的盒子,都必须有自己的安全网关。 例如,组织的 RBAC 管理能力不能开放给任何人修改。 很容易看出,这种情况如何迅速演变成一个问题,并催生了一个专家领域。 我们不会在本书中取代他们的知识和专业技能;然而,我们会分享一些我们认为最重要的 IDP 安全方面,帮助你走上 正确的道路。
任何组织的一个简单胜利是将暂存、开发和生产环境完全隔离开来。 这包括拥有完全不同访问规则的独立数据库,而不是一个包含不同表格和不同 访问规则的数据库。
可以使用单一集群,并通过基于网络策略的隔离、适用于特定命名空间的 RBAC,以及创建类似于多个集群的隔离体验,但集群的 API 服务器、审计日志、 etcd
、网络及其他集群范围资源仍然代表着潜在的单点故障,可能影响 隔离的安全性。
因此,出于 安全和合规性的考虑,最好将环境完全隔离。 通过拥有两个独立的集群,可以保证生产数据的隔离,并减少人为错误对安全性的影响。 从这里开始,你还可以拥有不同的网络配置,例如有不同允许规则的防火墙规则,甚至完全与 公共互联网断开连接的环境。
机密和令牌管理
在 Kubernetes 中,Secret 是应用程序可能需要访问的敏感数据,如密码、认证令牌、环境变量、API 密钥等,以确保其正常功能或完成某项任务。 机密管理 成为在依赖自动化如此重的系统中,像 IDP 一样工作的最关键挑战之一。 幸运的是,有一些设计好的模式和技术可以帮助 应对这一挑战。
作为一个基于 Kubernetes 的平台,内建的 Kubernetes 功能用于秘密管理的安全性是关键的起点。 需要明确的是,默认行为并不安全。 秘密信息与 ConfigMaps 的存储方式类似,且没有加密。 它们是编码存储的,但编码仅仅是 base64
。这种方式适用于开发环境,但对于生产环境并不安全。 然而,你可以在不安装任何第三方应用的情况下对静态的秘密数据进行加密。
有关静态数据加密的进一步阅读以及可以应用于测试集群的示例,请参阅 Kubernetes 文档中的 数据加密 部分: https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/。
秘密管理的范围通常相当广泛,一旦你有多个集群或多个环境,管理支持应用程序所需的所有秘密变得非常困难。 因此,使用诸如 HashiCorp Vault 这样的秘密存储软件,或如 Bitwarden 和 1Password 这样的密码管理器,已经成为行业标准。 一个标准的最佳实践是,将秘密信息在集群中自动部署的方式是:在代码中存储对秘密的引用,然后通过某种逻辑查找该引用并为应用获取相应的秘密。 因此,应用程序通过引用拉取秘密信息的常见模式是利用 侧车模型。
在侧车模型中,Pod 中的一个容器专门负责在 Pod 启动时获取并提供 供主应用容器使用的秘密信息。 这些秘密信息随后被放入存储卷中,当应用需要时 它会读取这些信息。
侧车模型 如下所示,其中 Pod 同时包含应用程序和 侧车容器:
图 7.4:容器侧车模型
但是,除非 sidecar(或其他服务)在循环中运行,否则这只解决一次,不能解决密钥可能定期更改的环境。 这就是开源软件解决方案是一个绝佳选择的地方。 外部秘密操作员 (ESO)是一个 开源软件,可以实现这一点。 该项目是 Linux Foundation 的财产。 有关其工作原理的详细概述,请访问 external-secrets.io。
他们的参考架构图如下,几乎与我们的 sidecar 参考图几乎完全一样:
图 7.5:ESO 的参考架构
基本上,秘密 存储在集群外的密码存储中,然后操作员可以访问它们。 秘密引用存在 自定义资源 (CRs),操作员知道如何解释和执行检索。 如果秘密发生变化,则操作员会自动将新秘密应用于集群,这意味着秘密在 IDP (SOT)中只有一个 真实来源。
当然,必须首先提供存储的身份验证秘密给操作员才能工作。 这可能有点困难,因为设置此操作员的人需要能够提供凭据以访问集群外的 秘密存储。
ESO 在高度监管的环境中特别有用,例如证书和令牌等秘密经常轮换。 这允许在源头进行轮换,但自动传播到 必要的环境。
ESO 并非集群上秘密管理的唯一开源项目;CNCF 正在孵化其他几个项目,都值得审查。 选择适合您组织的正确工具需要在权衡一系列利弊之后进行,但这些工具利用的模式代表了您应该追求的最佳实践。
日志清理
平台生成的任何日志都应该 进行清理。 应用程序也应该如此;然而,这超出了平台的边界。 日志是数据的一部分,因此是平台安全需要保护的资产。 除了我们之前讨论的存储和传输问题,数据清理是确保即使恶意行为者获得日志数据访问权限,他们也无法利用这些数据进一步危害系统的关键部分。 像 SonarQube(https://docs.sonarsource.com/sonarqube/latest/)这样的代码质量检查工具非常适合检测任何敏感数据是否被发送到错误的位置,从而在发生安全事件之前进行修复。 以防发生安全事件。
已清理的日志不应包含密码或令牌。 例如,在记录 API 请求时,请求的身份验证方式(例如,持有者令牌)可以被记录,但实际的令牌本身需要被清除。 在捕获或存储敏感数据的地方,应进行加盐 并哈希处理。
这些平台日志 也应该尽可能避免包含 个人身份信息 (PII)。 这种类型的数据通常不需要,因此存储它会带来不必要的风险 暴露面。
日志清理也包括保留政策。 就像应用数据在其 生存时间 (TTL) 到期后应进行生命周期管理和销毁一样,日志数据也应如此。 随着平台的不断运行,日志对平台团队的帮助逐渐减少,但其中包含的信息可能在某些情况下仍有价值,尤其是在由于业务需求无法剥离个人身份信息(PII)时。 因此,保留可能对恶意行为者有价值的数据会带来不必要的风险,甚至使用冷存储也无法完全规避这一风险。 何时销毁数据最终是一个业务决策,如果有充分的理由保留平台指标数据,可以对数据进行转换进一步匿名化,以便仅销毁 PII。 保持平台指标 永远存储。
安全访问
我们已经讨论过几次 RBAC 了。 它被明确列为 OWASP 十大之一,因此,确保访问控制正确对组织的安全性至关重要。 确保这一点的方法之一是创建服务账户。 这些账户是非人工身份账户,可以被系统上的工作负载使用。 与人工账户一样,服务账户的认证需要一个令牌,并且这个令牌应定期更换。 通过将访问类型分为人工和非人工,你可以利用 最小权限原则 (PoLP)来确保人工用户或工作负载仅拥有其所需的权限,而不会拥有它们 不需要的权限。
最小权限不仅应适用于工作负载,也应适用于人员。 在评估用户应该拥有的权限时,有几点需要注意。 接下来,我们将为您定义一些高层次的最佳实践供您调查:
-
单用户,多 RBAC:
-
用户在预备环境和生产环境中有独立的 RBAC 角色。
-
预备环境应与生产环境保持一致,以便在一个环境中执行的操作能帮助用户在另一个环境中执行相同的操作。
-
可能有破玻璃程序来获取更高的访问权限。 这种访问权限(如果存在)必须是可审计的,这意味着所有相关信息 都必须被记录。
-
-
GitOps 用于 安全:
-
可以 管理 RBAC。
-
减少直接授予访问 集群的需要。
-
Doe变成 单点故障(SPOF)。
-
中心 的安全性已被移除。
-
审计日志
什么是审计日志? 审计日志是 Kubernetes API 服务器看到的操作记录。 这意味着 Kubernetes 集群中的每一个变更,无论是自动化的还是人工发起的,从登录到 Pod 调度,都记录在审计日志中。 如果存在标识信息,它将在审计日志中记录。 这是因为审计日志是了解谁在何时、何地做了什么操作的重要路径,审计日志对于事件解决和安全性 PUT
或 PATCH
等有效载荷也应被记录。 凭据不应作为个人身份信息(PII)记录,应在绝大多数情况下省略。 案例。
利用审计日志 来确定异常行为可以通过一些基础的可观察性实现和相应的警报来完成。 在定义平台时,你应该已经构思出用户故事和关键用户流程。 在这些练习中,你会分析用户将会做什么并期待成功的操作。 但是你是否考虑过用户不会做或不应做的事情,并期待成功呢?
审计日志中发现的行为,如果偏离平台用户的正常行为,是定义潜在异常警报的最简单方法。 一个例子是出现大量的 403
错误,或者来自于 特定 无类域间路由 (CIDR)范围外的 IP 地址的请求数量显著增加。
自动化检测可能会查找的其他项目包括 以下内容:
-
检测异常或无效的用户代理 或机器人
-
来自 不同位置的多个用户登录或会话
通常,违反已知规范的事件应该自动地引起人工注意。 然而,大多数情况下,这些事件仍然需要人工审核,因为它们不一定表示安全事件。 它们可能指示一个软件问题或一个必要的事件,但在设计警报时并未考虑到。 警报不应过于频繁。 虚假信号会造成伤害,尤其是当它们在半夜打扰到你的团队时。 如果虚假信号触发得过于频繁,工程师们可能会很快忽视它们,以维持较低的 认知负荷。
到目前为止,我们已经介绍了安全的基础知识,还有很长的路要走。 现在我们已经学习了一些一般性话题,接下来让我们更具体一点。
查看 SBOM 实践
开源工具、编程语言中的库、包管理器和容器镜像是现代应用程序的构建块,同时在安全 你的 软件供应链方面也带来了独特的挑战。这就是我们亲切地称之为供应链安全难题的原因。 当你并不拥有所有需要 被保护的代码时,你如何保持良好的安全态势呢?
如果我们以可视化的方式表示供应链,它将包含一些未知的人(我们称之为参与者)为开源依赖项做出贡献,以及另一个可能已知的参与者更直接地为你的代码库做出贡献。 这是一个极度简化的图示(这里可能缺少了 10 个框),但它应该有助于你理解 重点:
图 7.6:示例供应链
你的软件供应链就是一切,所有参与者都在发布你的应用程序中发挥作用。 当我们考虑如何维护安全时,我们必须拆解我们的应用程序和基础设施拓扑结构。 SBOM 是跟踪和管理项目风险的重要工具。
在美国政府于 2021 年发布行政命令后,这些文档对于许多公司来说变得强制性。 其基本前提是,公司知道它们构建的软件依赖于什么,以及这些软件的依赖项来自何处。 SBOM 会在一款软件及其所有依赖项捆绑发布时生成。 虽然对于每个公司来说并非绝对必要,但作为最佳实践,当与扫描工具配合使用时,它们有助于审计并理解风险的表面区域,特别是在像 Heartbleed 或 Log4j 这样的 Day 0 漏洞再次被发现时。
SBOM 通常是在 CI 流水线的构建过程中生成的。 Syft 是一个 相当常见的 SBOM 生成工具,通常与扫描器 Grype 配合使用, 因为它们都是 Anchore 提供的免费开源工具。 思科的开源项目办公室最近还发布了一个 名为 KubeClarity (https://github.com/openclarity/kubeclarity),该工具可以协同使用多个 SBOM 和扫描工具,提供软件及其表面区域的最完整视图 以便于风险评估。
SBOM 生成工具仍然相对较新,因此它们还不完美。 可能某个工具未能检测到一个包,而另一个工具则能检测到,反之亦然。 为了了解你的安全态势,少即不等于多,因此获得尽可能最完整的视图是保持安全的最重要部分。 安全优先。
如何使用 SBOM
SBOM 不仅仅是客户或美国政府要求清单上的一个勾选项。 它还是一个有效的漏洞检测和响应工具。 在 图 7**.6中,我们展示了你的应用程序如何继承代码,从而继承了可能利用的开源依赖和库中的漏洞。 这些依赖关系很难跟踪,这也是为什么 SBOM 可以作为你系统的账本。 这意味着,如果后续发布了重大安全漏洞通报,你可以快速将该通报与 SBOM 进行交叉引用,并及时了解你的软件是否存在漏洞。 这可以通过查看已创建的报告,也可以通过重新生成报告来完成。 如果报告生成与扫描工具配合使用,扫描工具应该在漏洞被录入到关键 漏洞注册表后立即检测到新的漏洞。
获取 GitHub 仓库的 SBOM
查看 GitHub 仓库的 SBOM 的一种简单方法是使用 curl 命令调用你要调查的仓库的 GitHub API。 为了快速演示,我们将介绍如何执行此操作以及如何解读 结果。
GitHub SBOM 采用一种被称为 SPDX 的格式;你可以在 这里了解更多关于该格式的信息 : https://spdx.github.io/spdx-spec/v2.3/introduction/。
要获取 SBOM,请在终端中使用以下代码块来调用 GitHub API。 你不需要进行身份验证就可以运行此命令,但如果需要的话,也可以进行身份验证。 将 $REPOSITORY
和 $OWNER
变量替换为你所需的仓库信息。 为了示例,我们将查看 tag-security
CNCF 仓库:
curl -L \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/$OWNER/$REPOSITORY/dependency-graph/sbom
返回的 JSON 会比较长,所以我们只看从 curl 请求中收到的 SBOM 的一部分(https://api.github.com/repos/cncf/tag-security/dependency-graph/sbom):
{
"sbom": {
"SPDXID": "SPDXRef-DOCUMENT",
"spdxVersion": "SPDX-2.3",
"creationInfo": {
"created": "2024-08-16T05:37:53Z",
"creators": [
"Tool: GitHub.com-Dependency-Graph"
]
},
"name": "com.github.cncf/tag-security",
"dataLicense": "CC0-1.0",
"documentDescribes": [
"SPDXRef-com.github.cncf-tag-security"
],
"documentNamespace": https://github.com/cncf/tag-security/dependency_graph/sbom-0a6b74785f7954ee,
输出的顶部包含了一些关于 SBOM 的基本信息,包括它是如何生成的、何时生成的以及相关的数据许可信息。 这只是告诉你关于你分析的仓库的高层次信息。 接下来的部分是 包
,它包含了所有的软依赖项以及它们的关系:
"packages": [
{
"SPDXID": "SPDXRef-npm-babel-helper-validator-identifier-7.22.20",
"name": "npm:@babel/helper-validator-identifier",
"versionInfo": "7.22.20",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"licenseConcluded": "MIT",
"supplier": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceLocator": "pkg:npm/%40babel/helper-validator-identifier@7.22.20",
"referenceType": "purl"
}
],
"copyrightText": "Copyright (c) 2014-present Sebastian McKenzie and other contributors"
},
只看 SBOM 中的一个包,你可以看到包信息、使用的版本、许可和版权信息。 MIT 许可证意味着该包是开源的,但版权信息表明谁在维护该包,并且从本质上防止其他软件项目使用该包的名称。 IBM 在此解释了版权的原因: https://www.ibm.com/topics/open-source。输出中还包括了标签供应商和下载位置。 在这个示例中,这两个字段的元数据显示 NOASSERTION
。正如 SPDX 文档中所解释的那样(https://spdx.github.io/spdx-spec/v2.3/package-information/),这应该在以下情况下使用: 以下情况:
-
SPDX 文档创建者已尝试但无法做出合理的 客观判断
-
SPDX 文档创建者未尝试确定 此字段
-
SPDX 文档创建者有意没有提供任何信息(不应因此而意味着任何含义) 。
对于其他软件包,这些字段可能包含有关软件包的其他数据,也可能没有任何断言。 在软件包列表之后,按照相同顺序列出软件包与 存储库的关系:
"relationships": [
{
"relationshipType": "DEPENDS_ON",
"spdxElementId": "SPDXRef-com.github.cncf-tag-security",
"relatedSpdxElement": "SPDXRef-npm-babel-helper-validator-identifier-7.22.20"
在这个例子中,这是 非常直接的,因为它说主要元素,标签安全库,依赖于 npm:@babel/helper-validator-identifier
软件包版本 7.22.20
。在这种情况下,SBOM 创建者可以提供更多元数据,但目前没有。 有关这些关系的更多信息可以在这里找到: https://spdx.github.io/spdx-spec/v2.3/relationships-between-SPDX-elements/#111-relationship-field。
再次提醒,SBOM 是一个非常简单的工具;它创建一个分类账,列出应用程序或代码库的组成部分。 单独使用时,它并没有太多作用,但作为工具链的一部分,它对理解系统及其依赖的漏洞风险表面有很大帮助。 依赖。
保持在漏洞的顶端
软件漏洞 通常称为 公共漏洞及曝光,或 CVEs。美国 国土安全部 (DHS) 维护公共 CVE 注册表 (https://www.cve.org),您可以了解已知的曝光情况,并可与您的软件和应用进行对比,检查已知的 CVE 和您的风险。 尽管平台团队可以构建专门服务来确定系统中是否存在 CVEs,但无需如此,因为存在大量的 FOSS 工具可以为您完成这项工作。
您可以通过 GitHub 检查 CVEs,方式如下: 以下:
-
Dependabot,一个 GitHub 机器人,扫描您的存储库,并提出拉取请求,通过提升软件包到已知的安全版本来积极解决 CVE 问题
-
Snyk,验证拉取请求以确保不引入任何 新的漏洞
-
CodeQL,类似于 Snyk,评估拉取请求的内容,确保它们不会 引入漏洞
为了跟踪运行时的漏洞,定期扫描图像注册中心,如 Harbor,或使用 Trivy 等工具,至少针对你最关键的环境进行扫描,将帮助你及时掌握环境中的漏洞。
由于 SBOM 生成和漏洞检测通常是 CI 管道的一部分,我们接着讨论管道的其余部分,及如何确保你的CI/CD 过程中的安全性。
理解管道安全——你需要考虑的事项,以确保你的 CI/CD 管道的安全
假设平台团队对 GitHub 或公司使用的其他源代码控制仓库具有影响力或管辖权,那么 CI/CD 管道的安全性 从头到尾将成为身份提供平台(IDP) 安全态势的关键部分。
保护你的仓库
代码仓库的安全性 是“安全向左移”的一个很好的例子。 通过早期强制执行安全规范并将其融入项目的工作方式,组织可以防止问题在后续发生。 一个安全的仓库采用了多个 最佳实践:
-
写保护 主分支
- 如果需要额外的安全性,你可以使用私有 Git 仓库和自托管的 Git 来增强安全性
-
要求 签名提交
- 这验证了 提交作者的身份
-
提交前 Webhook
- 用于验证没有机密被 意外提交
-
强制性 同行评审
- 包括由 代码所有者的签字
-
自动验证 拉取请求
-
依赖 安全扫描
-
测试——应包括安全性 和访问权限的验证
-
-
持续扫描
- 扫描 以防止密码、令牌或其他 机密数据的意外提交
这份清单并非详尽无遗,但它应该为任何组织提供最佳的起点,以确保其源代码能防止恶意行为者和 人为错误的风险。
保护 GitOps
GitOps 已在 第 第五章中详细介绍,因此由于其对平台安全的重要性,我们将简要回顾一下。 GitOps 被松散地定义为一种自动化过程,用于验证 SOT(Git 或其他版本控制系统),以确保期望状态与实际状态匹配。 它通过一次性部署或更改来完成此操作,但也通过一个自动化的调和循环,主动检查期望状态的变化,并对实际状态采取行动,使其与期望状态匹配,或检测到实际状态的变化并对系统进行更改,将其恢复到期望状态。 当前最大的开源 GitOps 项目是 Argo CD,这是一个属于 CNCF 的 CD 工具。 在 Argo CD 和其他 GitOps 模式中,有一些最佳实践需要注意,以确保更 安全的环境。
对于 GitOps,有两种传播更改的模型;一种是推送模型,另一种是拉取模型。 在推送模型中,GitOps 系统将更改作为推送到其 API 端点的方式接收。 不言而喻,这些推送应当经过适当认证,但我们还是要强调,以确保没有混淆。 接收到推送后,GitOps 系统处理更改,然后采取行动。 这个行动可能是将新的软件包推向生产环境,但也可能是配置更改。 然而,在这种模型中,推送源通常不会被验证,Argo CD 也没有配置来验证它。 这就创建了一个攻击向量,因为如果凭证被泄露,攻击者可以通过 CD 系统推送更改,这可能会打开额外的 入口点。
拉取方法正好相反。 GitOps 系统使用其认证机制访问 SOT,然后从端点读取更改。 由于源是已知的安全源,通过自动化应用这些更改被认为是一个安全的操作。 这并不是说拉取模型没有风险。 推送和拉取模型都可能遭受 MITM 攻击,但在正确安全的网络和适当的加密实现下,这些风险应该 被大大限制。
当你的 GitOps 系统采取行动时,它应该以最安全的方式进行。 我们已经讨论过服务账户的使用和价值,因此你应该不会感到惊讶,GitOps 系统应该利用服务账户。 这些服务账户应当只具备完成 GitOps 实施目标所需的最小访问权限, 同时仍能在平台上执行任务。
安全性和 GitOps 可能是选择适合你 IDP 的 GitOps 工作模式时一个重要的因素,我们希望这些指南在为你的组织构建 GitOps 时能发挥作用。
现在我们已经覆盖了应用交付的安全性,让我们在 安全基础上继续深入,看看 应用安全。
理解应用安全性——设定并执行政策
安全性是一个动态目标,随着技术的进步,攻击方式增多,攻击者变得越来越复杂。 因此,团队在安全方面保持的流程和仪式比技术本身还要重要。 这并不是因为技术不重要,而是因为良好的纪律和强有力的流程习惯能够使技术根据行业的发展灵活地替换和调整。
良好纪律的一部分是维护准确的文档和架构图。 如果应用架构发生了重大变更,那么这可能会改变风险面和攻击向量。 例如,对一个库或网络端口的未记录或记录不全的依赖,可能导致暴露在一个更难以识别的漏洞面前。从而增加发现的难度。
基础应用安全
在 第五章中,我们讨论了 构建和交付镜像及工件的方法。 所描述的应用程序语义版本方法代表了创建软件的最佳实践,但不适用于其使用。 当使用 Git 进行源代码控制构建发布时,发布除了一个人为定义的版本外,实际上还会获得一个 SHA-256 签名,这得益于 Git 的现代功能。 与可以重复使用的版本号不同,安全哈希算法 (SHA)是该软件确切构建的签名,并且它始终是唯一的。 因此,对于安全最佳实践来说,使用平台所使用的镜像的完整 SHA 地址,而不是 镜像版本,是非常重要的。
以下是使用镜像版本 docker pull
命令的示例,以及 SHA:
docker pull quay.io/keycloak/keycloak@sha256:520021b1917c54f899540afcb0126a2c90f12b828f25c91969688610f1bdf949
除了镜像版本,还有一种称为 latest
的标识方法,但它们几乎可以是任何东西,因为只有行业规范,没有技术限制。 由于标签可以稍后重新指向任何发布镜像,因此这不是拉取依赖项的安全方法。 使用浮动标签的风险 包括 以下几点:
-
无意的 和未经测试/审查的更新至 应用程序组件。
-
限制了立刻了解正在运行的内容以及运行地点的能力 和时间。
-
未经审查的外部软件包可能会自动传播,为恶意行为者创建可利用的后门。 如果一个 Pod 重启并且
latest
被用于该 Pod 的 Dockerfile 中 ,这种情况是常见的。
有几个原因可以选择使用浮动标签,或者使用语义版本发布标签,而不是精确的 SHA。 如果你定期测试最新版本的构建,测试依赖项的持续可支持性会更容易。 在开发环境中,始终使用已知良好包的最新版本所带来的灵活性可能是需要的,然后在转向生产环境时,可以将其固定到精确的版本标签或 SHA。 针对浮动标签进行验证,还可以使团队在应对安全漏洞时更加灵活,因此应该对每个依赖项进行风险评估,并制定政策以确定应用程序的规范 是什么。
设置策略的典型方式是使用开源策略代理等工具来捕捉任何偏离已定义规范的行为,并防止这些偏离进入受限 环境。 开放策略代理 (OPA)和 Kyverno 都是免费使用的开源选项。 对于这两个工具,你可以利用 代码即策略 (PaC),它可以方便地审查并保存在 源代码管理中。
OPA 和 Kyverno 是两个可以用于安全的开源软件示例,但它们并不是开源生态系统中唯一的工具。
用于平台安全的 FOSS 及其使用方式
FOSS 项目,无论是在 Linux 基金会还是 CNCF 内部外部,都有大量的项目可以帮助你管理平台的安全态势。 前面提到的项目如 Harbor 和 Trivy 只是其中的两个。 它们只是众多项目中的一部分。
在比较你的安全需求时,比如确保已涵盖 OWASP 十大 针对可用的开源项目,你会发现有工具可以帮助你解决列表上的每一项。
管理安全的模式与工具
平台的作用仅限于管理公司安全态势。 因此,它需要通过提供有用的集成、采取安全优先的策略,并且如前几章所讨论的,保持对支持的开发者社区的贡献开放,来为安全提供坚实的基础。 当你确定了所需的合规级别后,就可以开始查看哪些错误可能导致合规性和安全态势的失败,通过进行基于流程的安全审查和基于技术的 安全审查:
-
一个 基于流程的审查 意味着有人在审查和评估合规性与安全性。 这可能是为了寻求认证而进行的审计,也可能是定期进行的内部审查,以确保最佳实践仍在实施,并且指南是最新的。 。
-
一个 基于技术的审查 将 利用软件自动并可能持续地审查和验证安全性与合规性。 SBOM 和 CVE 扫描是基于技术的软件构建审查的例子,像 OPA 或 Kyverno 这样的策略引擎可以帮助自动治理 IDP。 技术解决方案还可以进一步帮助检测可能代表安全事件的异常和事件。 CNCF 项目 Falco (https://falco.org) 就是这样做的。 它有几个关键的 特点,但 重要的是,它能检测是否存在权限提升,这可能代表着不良的安全和合规态势,或者可能代表一个已经获得系统访问权限的不法分子。 该系统的安全性可能已经被破坏。
任何公司如果希望展示符合某一治理框架的安全性和合规性,那么该合规框架将有助于定义过程执行的频率,并将 这些指导与定期审计相结合,以帮助确保平台 不会出现任何问题。
我们的虚构公司会怎么做呢?
我们的虚构公司“Financial One ACME”是一家长期存在的金融机构,正在进行云原生转型,以便在与年轻的金融科技公司竞争中保持竞争力。 作为一家金融机构,他们有一个固有的目标——最小化风险。 他们还受到监管限制,包括 PCI DSS。
此外,由于他们是一家银行,必须保护货币资产和客户数据,他们已经建模了可能威胁到其系统的潜在风险。 在众多物理和非物理风险中,许多安全行动项目被交给平台团队,在 IDP 的实施过程中解决。 这些问题可能已经得到解决,但由于这个 IDP 是一个全新的应用程序(或者是一个全新的项目),它需要重新处理合规性 要求。
平台团队需要实施的一个事项是确保他们所有新版本的软件包都已 妥善安全。
妥善安全意味着 以下内容:
-
签名 并验证
-
安全存储
-
安全地检索
-
严格的 变更控制
-
审计跟踪
平台团队采取了之前概述的步骤来确保他们的代码库和 GitOps 系统的安全,但这些措施不足以满足这一要求。 因此,平台团队决定利用一个内部镜像注册表,其中所有软件提交和包都会进行签名。 CI/CD 管道基于这些工件创建镜像,并将它们放入私有注册表中,平台应用程序 从中获取镜像。
镜像注册表的选择可能看起来像是以下几种选项之一:
-
支付给一个镜像注册表供应商,帮助他们通过扫描来维持安全姿态,并为他们提供 可信 通用基础 镜像 (UBI)
-
托管他们自己的镜像注册表,如 Harbor,这是一个 CNCF 毕业项目,拥有一个镜像注册表,该注册表 签名、 存储和 扫描 容器镜像(https://goharbor.io/),并且它们将被填充为 已批准的镜像
-
如果他们使用的是 OpenShift,那么集群拓扑中已经有一个镜像注册表,平台团队会用 扫描工具 来补充它。
他们进一步添加了一个准入 webhook,以确保没有容器基于未批准的镜像。 这个用例非常简单:一个验证 webhook 检查容器镜像是否符合定义的预期。 如果不符合,工作负载将被拒绝,Pod 将无法启动。 虽然团队可以自己构建一个准入 webhook 服务,但他们可能会选择 OPA。 虽然它不是唯一提供此功能的开源项目,但它是唯一一个毕业项目,使其成为生产环境中最安全的选择。 在生产中使用。
Sysdig 创建了一个开源版本的此类 webhook,它还会更改或修改 Pod 配置,以便其镜像使用完整的镜像 SHA,而不是发布标签,详细信息见: https://github.com/sysdiglabs/opa-image-scanner。这两个 webhook 都代表了安全性和合规性的最佳实践,是任何平台 工程团队在安全方面轻松获胜的明智之选。
然而,尽管这些限制至关重要,它们可能通过限制 IDP 能力的灵活性而对创新产生负面影响。 因此,平台团队决定将这些限制限制在生产环境中。 这样做允许开发团队在其开发环境中进行实验,而无需担心任何不应意外进入 生产环境。
管理依赖项的一种策略是使用供应。 供应是将代码从你原本会导入代码库的开源库复制过来的过程。 在供应过程中,可能会对该代码进行更改以 增强其安全性,例如启用 联邦信息处理标准 (FIPS) 模式。 FIPS 合规性规定了数据通过 安全套接层 (SSL**) 的加密强度,通常是最佳实践。
平台团队可能会有的另一个行动项目是在疑似安全漏洞事件中成为第一联系点。 这意味着需要立即对问题报告做出反应。 由于平台对公司基础架构的责任很大,从开发到生产能力,团队需要能够迅速响应,以减轻 恶意行为者造成的损害。
应当制定并定期测试这样的 IR 计划 (IRPs)。 与 灾难恢复计划 (DRP) 类似,定期 测试计划及其响应真实安全漏洞的能力可以 减轻损害。
早期检测是该平台所需的另一个关键能力。 对审计日志的分析是理解谁进入了系统(或谁的凭证被泄露)以及恶意行为者在获得访问权限后进行了什么操作的关键。 审计日志还可以用于主动检测,因为它们可以被 用于 机器学习 (ML)模型进行异常检测,这可以比人类更快地发现安全漏洞。 此外,使用像 Cilium 这样具有高度可观察性的云原生网络解决方案可以帮助识别和追踪恶意行为者。 尽管一些硬编码的可观察性实现能够达到相同的结果,但它们需要手动维护,而机器学习模型由于其 自学习的特性,可能具有更多的内在灵活性。
两种方法都不是完美的,因此,在决定如何围绕威胁检测实现自动化时,组织需要对收益、权衡和团队能力做出判断。 在我们虚构公司的情况下,选择前进的路径将是一个关于“自建与购买”的讨论,这可能会因为任务的规模和复杂性以及它们所需维护的高度安全环境而最终做出购买的决策。 为了维持这一点。
谈到这个高度安全的环境,为了向审计员展示组织使用新 IDP 的 PCI 合规性,我们的平台团队需要能够提供网络架构图,并向 审计员解释该图的内容。
任何合规性审计员都希望验证开发和生产环境是否得到充分隔离,无论这是否在物理上得到实现,或者通过 网络实现来完成。
最后,虽然 Financial One ACME 平台团队将确保他们遵循所有已知的构建时最佳实践,但他们也会实施工具,确保他们的运行时同样安全。 很可能,生产环境中唯一有权限创建 Pod 的用户将是那些服务帐户用户,确保 Git 保持为 SOT,并且可以利用 GitOps 来规范平台的 安全态势。
这些示例响应仅涵盖了金融机构(如银行)所需的一部分安全性和合规性要求;它们还可能会受到额外政府法规的约束,这将需要进一步的安全性和合规性风险缓解措施。 正如您的团队所需要的,我们虚构的公司也需要解决其所遵循的合规框架中的每一项条目,并且最 重要的是,创建协作仪式来维持最佳安全性 他们可以达到的水平。
总结
总之,安全性和合规性是一个广阔的领域,许多专家已经发布了专门的著作。 本章不应被视为包罗万象,但应帮助您迈出正确的步伐,为您的 IDP 定义并执行网络安全策略。 了解如何追踪漏洞并在您的组织中建立仪式和工具,以便捕捉和发现 IDP 及其所承载的应用程序中的漏洞是非常重要的。 它所托管的应用程序。
尽管安全性和灵活性并非天生是合作伙伴,但专注于关键安全需求而不阻碍创新的智能实现是为开发人员提供他们成功所需工具和他们所需保护的关键。 保持安全。
请记住——安全事件的成本可能极其昂贵,甚至可能导致破产或诉讼。 虽然日志存储和其他安全要求可能需要费用,但这些费用是可以管理的,并且永远不会比未能保护系统所带来的成本更高。 关于如何管理平台成本的更多信息,我们继续阅读 第八章。
第八章:成本管理和最佳实践
在过去几年里,云迁移一直是许多 IT 组织的首要任务,并且在未来几年仍然是一个战略性、相关的方向。 自建数据中心仍然是一个有价值的选择;相比于云,自建数据中心的设计和成本通常经过更深思熟虑。 在本章中,我们将深入探讨这一问题,并反思云成本管理 和优化的必要性。
你将更多地了解标签策略,以及它们为何是获取云支出可见性和透明度的可行资源。 我们还将探讨如何定义标签策略、最佳实践以及实际方法,确保它们得到妥善设置。 以此为基础,我们将深入探讨成本优化的四大支柱: 流程、 定价、 使用、 以及 设计。
在本章的最后,我们将分享一些实用的建议,帮助你优化平台并长期降低成本,同时为你的用户提供节省成本的价值。 。
总体而言,我们将重点讨论有效的成本管理,以及作为平台工程师,如何实现这一目标。 以下是你可以期望学到的内容:
-
理解成本格局——云是否是最佳选择 ?
-
实施标签策略以揭示 隐藏成本
-
审视成本 优化策略
-
自动扩展、冷存储以及其他成本优化 技巧
了解成本格局——云是否是最佳选择?
成本管理 在平台工程中的应用始于对成本驱动因素的深入理解 ,特别是了解基础设施中哪些平台组件可能会影响这些因素。 成本驱动因素也是直接影响你运营总成本的因素。 理解并随后识别这些因素,有助于做出明智决策,从而提供以成本为导向的 优化平台。
是否选择云——这是一个问题
过去几年,几乎没有办法绕过云采纳战略。 在这些年里,这一运动面临了来自一些成功进行“反迁移”的公司压力,这些公司声称他们的本地部署比云更便宜、更好,并且满足他们的一切需求。 反迁移 就是这个过程的名称。 做这种选择的人数并不十分明确,且很大程度上取决于什么算作其中的一部分。 它变得非常著名,作为 HEY/Basecamp/37signals,他们的首席技术官 David Heinemeier Hansson 曾表示 如果他们不使用云计算,他们将在未来五年节省超过 700 万美元 。 他们购买的服务器与每年在云上花费 190 万美元之间进行的愚蠢对比,显示出他们的购买服务器花费了 50 万美元,这让数学变得非常清晰 [1]*。
当然,Basecamp 或 HEY 与企业是不具可比性的,对吧? 根据我的经验,我可以告诉你,运行大型公司的硬件在自有数据中心比迁移到云端要便宜。 一个关键因素是数据量以及基础设施需要多么动态和可扩展。 计算以拍字节为单位的数据时,云服务会迅速变成一个无尽的金钱黑洞。 相对静态的工作负载也会大大减少所包含的好处,而且还有许多其他的缺点。 另一方面,云市场每年都在持续增长,似乎没有尽头。 因此,要做出决定,选择正确的方式,你需要考虑许多不断变化的因素,从简单的成本对比到可用技能以及你业务的实际需求。 对云是否适合你的全面评估本身就可能是一个项目。 但我们想给你一些关于应该考虑的标准,以便你能在 这个阶段做出决策:
-
成本:评估本地部署的初始资本支出(CapEx)或一次性费用,和云服务的运营支出(OpEx)或持续性订阅费用,并考虑长期财务影响,同时还要意识到,如果你提前支付 1 年或 3 年的费用,你在云计算上将节省最多的钱。 这几乎就像是在购买 硬件。
-
可扩展性:评估你在快速变化情况下动态扩展资源的需求。 这里有个提示:如果峰值增长太快,静态服务器可能比等待几分钟直到新 实例启动更适合用户体验。
-
可靠性:评估正常运行时间保证、冗余和故障转移能力,以保持持续运营。 查看停机历史。 一些云提供商经常 出现问题。
-
合规性:确保遵守法律和监管要求,重点关注数据驻留和主权问题。 在决定之前,你必须明确对主权的观点。 不幸的是,这个术语的涵盖面比明确的要求清单 更广泛。
-
集成:确保与现有系统和软件的兼容性,并提供 API 和集成工具。 提供商应支持 基础设施即代码 (IaC),尤其是 能够通过编程创建账户、用户 和权限。
-
支持:评估技术支持的可用性和质量,以及 服务水平协议 (SLA)。 研究其他用户对所提供支持的满意度;仅仅因为你有合同,某人负责保持系统运行并帮助你,并不意味着该服务 也一定好。
-
新服务的开发:评估提供商向你提供的新功能的速度和数量。 考虑到每月更新过多的情况,也要注意是否几乎没有 新功能。
-
地理考虑:考虑数据中心与最终用户的距离以及服务的区域可用性。 如果你的公司开发全球 可用的 软件即服务 (SaaS),那么构建在公共云上可能比集成多个 区域提供商更为容易。
-
技能与专业知识:评估管理基础设施所需的专业人员和培训要求。 考虑到你仍然需要任何 IT 领域的人才来完成这项工作。 短期计划是什么样的? 长期需要什么? 别再认为云计算很简单了;大多数企业之所以苦苦挣扎,是因为他们没有让 团队接受培训。
-
环境影响和可持续性:考虑提供商的能源消耗、碳足迹和可持续性实践。 他们如何处理废水? 如果他们购买碳补偿,记住你需要为此付费,因为理论上这会增加 电费。
云通常有一个非常强大的优势:几乎没有什么能阻止你立刻开始。 你可以找到大量的模板、蓝图和示例。 在短时间内,你就能开始运行,拥有你的第一个环境作为 平台。
当我们选择云时——我们必须考虑它的隐藏成本
由于我们本书主要聚焦云端,我们暂时假设选择裸金属方式的用户本能上具有更高的成本意识 和敏感度。
云提供商为我们提供了很多即用的服务, 通常带有许多 内置的功能。它们通常包括备份解决方案、可扩展性、 高可用性 (HA),以及集中管理的服务。 在规划云基础设施时,大多数情况下,我们会发现硬性事实和最佳猜测。 硬性事实是指像需要多少个 CPU 或服务器,应该使用哪种数据库,是否是单节点还是高可用等信息。 然而,你应该意识到,几乎每一个你选择的公共云提供商选项都会对成本产生一些 影响。
常见的大成本驱动因素包括 以下几点:
-
负载均衡器:几乎在每个架构中都需要它们,你也不例外。 正如前面所解释的,我们有机会将应用服务从 Kubernetes 命名空间扩展到云端,甚至控制网络——这是一个主要的 成本陷阱。
-
API 网关:负载均衡器的更为邪恶的双胞胎,作为最佳实践时,变得非常昂贵,特别是对于频繁通信的系统和路由 密集型通信。
-
数据:无论是静态还是在传输过程中,数据 迅速成为云账单中最大的成本块。 虽然一切都可以扩展,但你的数据必须 存放在某个地方。
-
备份和快照:它们对于一个成熟的平台非常必要;你需要有一个非常 好的备份策略,既精简 又可靠。
-
可扩展的 托管服务:如果你在使用无服务器架构、消息流或预训练的 AI 模型——任何可以无限扩展的东西——设定限制至关重要,以避免产生 意外成本。
理想的云项目——我们必须强调的是,我们在谈论的是有截止日期的事情——是非常有明确意见的,遵循云服务提供商的最佳实践。 几乎每个云项目都不理想,因定制实现的需要而导致迁移过程中的摩擦,这些实现必须以某种方式使旧世界和云服务提供商能够互相协作。 这些隐性成本通过技能的缺失而积累,因为所需的技能到底从哪里来呢? 从外部技能提供商那里获得帮助意味着他们必须先了解你,才能真正帮助你。 然而,若没有 外部支持,大多数云项目会在更早的阶段就被认为是失败的。
简而言之,我们可以说大多数服务在成本方面可能会反过来对你不利。 作为平台工程师,我们必须能够控制这些元素,建立保护措施和限制。 为了实现这一点,我们需要透明度 关于成本。
在哪里可以找到透明度
与此同时,我们有很多选择可以获取关于我们花费在哪些地方的信息,以及这些开销是否基于利用率等指标被认为是浪费。 所有云服务提供商都提供了探索(甚至细分)成本的可能性,但由于重点关注基础设施,尤其是在应用层上,细节往往缺失。 从以下 AWS 成本浏览器的截图来看,我们得到的是不同服务类型的消费图表。 现在我们需要应用过滤器来获得更多见解,并分析这些成本的根源:
图 8.1:AWS 成本浏览器
像 Apptio Cloudability 这样的商业解决方案 可以从多个不同账户、云服务提供商和环境中收集成本信息,集中在一个工具中。 它们在给定的数据上应用 FinOps 逻辑,并提供预定义的仪表板,比如以下截图中的单位成本和节省仪表板:
图 8.2:Apptio Cloudability 关于单位成本的详细仪表盘
微软定义 单位成本如下:“衡量单位成本是指计算一个业务单元成本的过程,该业务单元能够展示云计算的业务价值。” 单位的具体定义由你决定。 它可以是金融系统中的交易、媒体平台的用户,或你能将不同部分的 基础设施映射到的任何其他内容。
现在,在我们的平台和开源世界中,我们也可以找到一些解决方案来获得更多的透明度,例如 Kubecost(带有商业计划)和 OpenCost。 两者都可以在集群内分配成本,并与云服务提供商资源结合使用。 不幸的是,两者都缺乏良好的分析能力。 因此,我们常常看到自实施的解决方案与 商业智能 (BI) 工具,或 Prometheus 和 Grafana 实现结合,加入一些成本数据。 这些解决方案的效果取决于所投入的时间和 资金。
FinOps 和成本管理
近年来,FinOps 获得了一些 人气,并试图将自己与传统的成本管理区分开。 成本管理通常被描述为一种短期的、孤立的、单一目的的活动,旨在迅速实现成本节省。 当你作为平台团队,具备成本意识并积极管理你的成本时,这也将是一个可持续且持久的解决方案。 但 FinOps 成功之处在于它的整体方法,包括采购部门、 业务部门 (BUs),以及 财务部门,明确目标是实施对云成本及其动态的组织理解。 。
以下 FinOps 框架概述表明,它采取了与平台工程团队相似的方法。 我们可以看到需要与你的原则对齐的原则;需要集成和启用的能力,这将需要你的输入;以及角色——如平台工程师,他们 合作 [2]:
图 8.3:由 FinOps 基金会提供的 FinOps 框架
作为平台团队,您应该与 FinOps 团队合作,并积极管理他们对您平台的影响。 潜在的成本节省必须与您的原则、用户满意度和开发者体验相平衡。 基于数据的建议需要具备架构资格,并考虑替代方案和建议以实现 成本节省。
在接下来的部分,我们将讨论如何实施标签策略。 标签是为云和 Kubernetes 资源提供附加信息的一种简单解决方案,并且能创建某种程度的透明度。 此外,许多成本管理和 FinOps 工具需要标签才能提供 更好的洞察。
实施标签策略以揭示隐藏的成本
标签 和标签 对您来说可能并不陌生,因为它们在各种工具、公有云和 Kubernetes 中都有应用。 我们将使用标签这个词来表示标签,但当使用标签这个词时,我们实际上是专门指 Kubernetes 标签。
应用和使用标签本身可以变成一门艺术。 如果标签过多,可能会让它们应附加到服务的哪些信息变得不清楚。 当然,没有标签显然是没有帮助的。 在涉及多个不同业务单元和部门以及复杂发布机制的组织中,标签可能会被过载,或者只是一些缩写的集合。 关键是,我们需要标签来获得服务的透明度,明确它们属于谁,可能还会指示不同的服务级别或安全等级,最终成为将这些服务与成本结构和资源使用情况匹配的锚点。 因此,标签允许更精确地分析与成本相关的资源 和 成本原因。
使用标签的目的
标签 可以针对不同的目标群体和用户。 根据您询问的是哪个子域,它们应该包含组织、操作、安全甚至架构信息。 我们还可以考虑一些对我们作为 平台工程师 可能有帮助的标签。
组织信息的标签可以定义哪个部门、角色或团队负责该服务,是否是面向内部或外部的解决方案,以及是否存在任何国家、合规或 治理限制。
在操作层面,常见的属性包括服务级别、调度时间和维护窗口,但也有一些可能由第三方工具引入的非常技术性的资讯;例如,通过实现特定云 策略 [3]。
对于特定领域,如安全或平台团队,我们可以将标签和标签看作定义所需的隔离或数据保护,或者类似于典型的扩展模式、上下峰值,或识别微服务架构及其归属于某些组件和 结构。
我们的重点将放在相关的成本 标签上。 这些标签通常与组织标签相似,或与前面提到的操作性或领域特定的标签相关。 明确组织归属或提供有关某事为何如此剧烈扩展的见解是成本管理的关键推动因素。 这些标签有助于为需要提升 成本意识的项目和团队提供清晰的视图。
一些来源还指出,使用标签进行访问管理。 出于多种原因,这需要谨慎处理。 如果你有 基于属性的访问控制 (ABAC) 和权限 管理,标签可以成为实现这一目标的可行且简单的方式。 但这也带来了一个缺点,即你将一个纯粹的信息源变成了一个安全关键元素,需要在使用时进行保护和双重检查。 将其与信息特性混合使用可能会使标签的使用变得复杂,并且很可能会成为正确使用的障碍。 我们之前说过,标签可以用来传递安全信息。 然而,在提供诸如风险分类或安全级别等信息与提供 访问授权能力 方面,存在差异。
因此,像我们的平台一样,定义标签的目的至关重要。 随意地使用并设置标签虽然总比什么都不做好,但当标签是否重要变得不清晰时,未来可能会引发问题。
标签和标签限制
不幸的是,我们遇到了两个 普遍存在 的问题:
-
标签没有标准,这意味着它们的长度、标签数量、允许的字符或 大小写敏感性 等存在很多变体。
-
组织往往在许多不同的提供商 和平台上拥有多个环境
所以,在接下来的步骤中,定义标记策略时,你必须了解你可以可靠构建的最小数量。 标记策略将引入一个标准,必须适用于任何平台。 为每个平台定义多种不同的方法将导致混淆 和错误。
举个例子,我们将 比较不同的提供商 和平台:
AWS | GCP | Kubernetes | Azure | |
---|---|---|---|---|
每服务的最大标签数量 限制 | 50 | 64 | 没有 指定限制 | 50 |
最大 字符标签 名称长度 | 128 | 63 | 63 | 512 |
最大 字符标签 值长度 | 256 | 63 | 63 | 256 |
特殊字符 | 字母数字字符、空格以及 + - = . _ : / @ | 字母数字字符、- 和 _ | 字母数字字符、- . _ | 字母数字字符、<, >, %, &, , ?, / 不允许 |
区分大小写 | 是 | 是 | 是 | 是 |
表 8.1:云提供商和 Kubernetes 的标签限制
虽然 63 个字符 作为一个常见的方向看起来很好,但我们在实际应用中发现,对于许多组织来说,甚至 256 个字符也不足够。 这只是另一个例子,说明为什么标记策略必须经过 深思熟虑。
此外,一些结构在不同的提供商之间无法通用。 假设我们的 Financial One ACME 公司使用 AWS 和 GCP,包括其托管的 Kubernetes 服务作为平台,为什么 Financial One ACME 会达到允许的最大标签数量限制呢? AWS 团队首先发现了这个问题,并发现它可以使用更长的标签和一些特殊字符,从而允许某种嵌套结构。 于是,他们开始将不同的标签合并成一个,得出了 以下结果:
department-responsible=financial-one-ACME/domain-sales+marketing/stream-customer-management/squad-frontend/operations-squad-lazy-turtle
该值有 112 个字符,包含了 /
来表示层次步骤和 +
来表示 和。团队对结果感到满意,并将这个新标签推送到共享存储库。 一段时间 后,平台团队收到投诉,在销售和营销 IT 部门中,由于 一些标签的问题,最新的发布没有得到推出。
此外,您会发现其他限制,如每个账户 或订阅的最大标签数。
定义标记策略
正如 之前所解释的,标签可以具有许多不同的用途;主要是要在技术标签和业务标签之间保持平衡。 业务标签可以分为组织标签和成本标签。 最终,许多标签往往具有重叠的信息。 因此,为了找到正确的方法,通常有必要为定义设定一些基本规则和边界。 在第二步中,您必须在操作、平台和开发之间进行协调,从技术方面找到所需的标签集,然后再设置组织标签。 这个方向是有道理的,因为通常运营所需的标签也包含了关于 组织的信息。
让我们从一些共同的 基本规则开始:
-
标签名称和值 必须 要短于 63 个字符。
-
标签只能是 字母数字组合。
-
标签只能包含
-
和_
. -
请关注标签值及其内容,因为您通常会对此进行筛选和搜索。
-
标签名称对于排序 和分组更为相关。
-
不要将 个人可识别信息 (PII)写入 标签中。
-
建立一个大小写样式。 企业可能更喜欢帕斯卡样式,而在技术中,烤串样式更为常见。
-
使用更多的标签比使用更少的标签更好,但尽量保持 10%至 20%的可用 标签。
根据这些规则,我们可以创建下一个相关的元素。 首先,我们需要考虑为这些元素打上标签,使其比我们之前讨论的更加细化。 一个好的做法是考虑以下分类: 以下分类:
-
所有权——部门、团队、 组织、流。
-
环境——无论你们的预发布 环境是什么。
-
项目或产品——项目或产品集群,用于识别哪些组件 属于同一组。
-
成本中心——谁是 负责人? 是否有任何共享成本需要 额外考虑?
-
合规性——监管限制和要求、政策,及 合规规则。
-
操作——生命周期、备份、 维护窗口。
从这里开始,我们必须制定命名约定。 如前所述,重要的是不要过度加载单个标签。 遵循给定的规则和限制,并结合许多软件和系统组件的特性,标签约定可能如下: 如下所示:
-
department=public-relations-content-management
-
owner=department-public-relations
-
data-classification=personal-identifiable-information
在一些语言中,推荐使用 Pascal(Hello-My-Name-Is-Pascal
)或驼峰命名法(i-Am-A-Camel
)。 将所有字母都写成小写的风格称为 kebab 风格。 你需要非常明确地定义这些命名模式。 名字应该有多少个字符?包含多少个单词? 是更具描述性,还是仅仅匹配已经确定的内容? 同样也适用于值。 确保这些约定已被文档化、沟通,并且在工程师的入职或培训中被包括。
现在,最后一步是开始建立一个 带有这些基本规则的标签目录。建议创建这些目录,以便你有空间描述标签的含义、目的以及面向的群体。 这还可以帮助你保持标签简短,因为你不需要再为标签的值添加更多描述。 下表是一个简单的标签目录示例。 它可以根据组织的独特需求进行扩展。 重要的是将任何你可能需要包含在标签中的信息转移到目录中,以免使标签显得过长 并且难以阅读:
# | 标签名称 | 预期值 | 含义 | 目的 | 标签利益相关者 |
---|---|---|---|---|---|
1 | 部门 |
部门处理或代码;例如, DE-22-P |
应用程序所属的 BU 或区域 | 明确应用程序的所有者 | 应用程序 |
2 | 应用程序 |
应用程序名称;例如, internal-cms |
应用程序的名称 | 识别 该应用程序 | 应用程序所有者; 运营;架构师 |
3 | 维护-允许 |
可以执行维护的日期和时间;例如, sunday-0800-1230 |
该标签标识了日期或日期范围,以及以 0800 表示的时间段,意味着 上午 08:00 |
定义应用程序何时可以关闭以更新/修补或发布 新版本 | 运营; 一级支持 |
表 8.2:标签目录示例
标签自动化
这将是不可能的 手动标记所有资源,尤其是在有许多活动组件的 IDP 上。 某些工具可以帮助我们在资源创建时自动标记它们,甚至在事后“修补”它们。 正如我们之前所见,在平台上,我们必须区分平台运行的基础设施和用户在其上运行或管理的工作负载 集群。
当您使用 Terraform/OpenTofu 等工具管理基础设施时,您可以在代码中给组件打标签 如下所示:
resource "aws_ec2_tag" "example" {
resource_id = aws_vpn_connection.example.transit_gateway_attachment_id
key = "Owner"
value = "Operations" }
对于某些提供商,您甚至可以给所有资源使用相同的标签。 这需要小心处理,因为您不希望错误地标记其他资源,尤其是当这些资源触发第三方集成或导致 错误计费时:
provider "aws" { # ... other configuration ... default_tags {
tags = {
Environment = "Production"
Owner = "Ops" } } }
几乎所有的基础设施即代码(IaC)解决方案都提供这种方法,并且可以适配任何类型的基础设施,无论是云端 还是本地部署。
这也可以应用于 Kubernetes 资源。 我们在这里跳过常规部署文件,因为这些文件很可能会以不同的方式处理,例如使用 Helm。 以下示例展示了 Helm 如何从 Helm chart 值文件中获取标签的值。 模板 值由一个 _helper.tpl
模板文件动态创建,而.Release
值则是内建信息。 另一种选择是从用户提供的 values.yaml
文件中读取值:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{template "grafana.fullname".}}
labels:
app: {{template "grafana.fullname".}}
chart: {{template "grafana.chart".}}
release: {{.Release.Name}}
heritage: {{.Release.Service}}
Helm 的一个优点是我们可以将其与简单的 if
语句结合使用。 通过这种方式,我们可以根据部署的目标位置或任何 其他触发条件传递预定义的信息。
注意
在您的 Backstage 模板中使用预定义的标签,强制用户至少定义一些基本的 标签。
这两种方法都是高度声明式的,并且可能会因为实施或被忽视而失败。 当然,您可以在 CI/CD 管道中检查标签的正确使用,但这可能导致许多部署失败。 这引出了策略引擎。 一方面,策略引擎可以用于测试部署中的标签,但像 Kyverno 这样的工具 也可以在需要时添加这些信息。 以下是一个 Kyverno 策略示例,它为任何 Pod foo=bar
添加标签 或服务:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-labels
annotations:
policies.kyverno.io/title: Add Labels
…
policies.kyverno.io/subject: Label
spec:
rules:
- name: add-labels
match:
any:
- resources:
kinds:
- Pod
- Service
mutate:
patchStrategicMerge:
metadata:
labels:
foo: bar
我们可以以非常精细的方式选择并修补这些信息,并且在以后需要时可以随时更新。 如果需要。
合并与分开的成本和计费报告
在许多 组织中,我们看到有合并成本和账单报告的需求。 虽然从组织的角度看,这是理解基础设施支出的最快方式,但它也需要许多标签来确保适当的拆分和细节级别。 当组织规模过大,且业务标签数量大于技术标签时,这是一个明显的信号,表明某些事情出了问题。 从激进的角度来看,此时做合并成本和账单报告就成了一种反模式。 那么我们该怎么办呢? 例如,企业通常使用字母数字编码来识别部门。 因此,部门如果不是叫做 财务和会计,可能会有类似 B-2-FA-4
的编码。这种编码是消除许多其他组织或业务标签并将其放入匹配表或数据库的完美基础,尤其是当你希望在其他地方进行编程匹配时。 需要考虑的其他事项包括,例如,暂存环境和集群。 大多数时候,开发、集成和生产系统是独立的。 这意味着,如果你的应用运行在 曼哈顿
集群上,这可以被翻译为 prod-1234-eu
,那么我就不需要另一个标签来表示 stage=production
或 region=eu
。需要明确的是,你应该限制自己不要将过多信息添加到 单一值中。
然而,故事中有一个很大的 但是 这让我们回到了话题的起点。 从成本管理的角度来看,你需要拥有许多良好的标签,以便能够进行分析 和研究。
仅有标签本身是不够的;它们是成本优化的一个重要部分,但你需要采取哪些优化措施呢? 在接下来的部分,我们将探讨一些通用的优化策略,这些策略不仅包括调整规模和 减少基础设施。
查看成本优化策略
减少云和平台成本的最快方法是关闭那些你不需要的部分。 最大的问题是,参与该过程的每个人可能都有一些理由,为什么他们需要现有的基础设施。 但从现在开始,我想要转换你对这个话题的视角: 成本优化不是我们应该事后引入的东西。以下是你应该考虑的另一个原则: 在平台设计时要具备成本意识和高效性 。
注意
平台中发现的成本优化潜力越大,说明我们作为平台工程师 和架构师做得越差。
我们可以在平台的任何部分发挥潜力。 然而,这本身就可以写成一本书来覆盖所有相关方面。 因此,我们将讨论适用于平台内任何组件的原则。
精简流程
流程、业务和技术方面,都会 导致更高的成本。 我将给你举一个导致不必要成本的模式/反模式的例子。 Kubernetes 的发布分为三个主要版本,在其中的几周里,你会获得补丁、bug 修复和安全补丁。 Kubernetes 发布团队自动化了发布过程中的大部分工作,包括夜间构建,并且持续不断地 进行所有的测试 在 Kubernetes 基础设施上运行几千个测试。 当这种方法被引入时,社区将其视为顶级科技公司的圣杯。 突然间,每个人都想要他们容器的夜间构建,常常跳过测试部分,认为测试过程 太过繁琐。
这种方法的缺点是,大多数夜间构建从未被部署。 它还阻止了构建服务器在晚上关闭。 容器注册表一直保持忙碌,包括 CVE 扫描,企业容器通常较为庞大,增加了传输和存储成本。 大多数组织没有理解的是,Kubernetes 是一个全球性项目。 此外,如果在某个地方是夜晚,其他地方的人仍然在工作,开发新功能并推送到 Kubernetes 仓库。 最终,Kubernetes 是全球最大的开源项目之一,由全球最大的 数字原生公司 Google 发起。
好好想想!
仅仅因为别人都在做,考虑清楚它是否真的 有意义!
回到流程。 几乎每个流程都可以得到改善,因为它们通常是历史上逐步积累的。 在平台空间内,定期引入的工具替代了脚本和自定义开发的工具。 更好的流程不仅可以降低成本,还可以提高 效率,减少风险,最小化错误,并 提供一致性。
经过验证的流程优化方法可以在 精益 和 六西格玛 方法论中找到。 为了改善一个流程,它们教你执行以下操作: 以下内容:
-
定义/识别 流程的问题。
-
衡量 交易的性能和时间。
-
分析效率低下 和依赖关系的表现。
-
改进流程以 解决效率低下问题。
-
通过引入 金丝雀部署 来控制和审查新流程。
平台的有趣之处在于,大多数流程可以在 CI/CD 流水线和 GitOps 实现中找到。 这些将我们的目标转化为技术步骤,以确保实现预期状态。 复杂的地方在于,当我们在 Kubernetes 内以及其上方的组件中有流程依赖关系时。 作为一个最终一致的事件驱动系统,识别哪些依赖关系导致了哪种行为是一项挑战。 有时候,在不造成 进一步影响的情况下,我们无法改变组件的反应。
在技术世界中,流程常常听起来像是一堆脚本一起运行。 理想的状态是采用云原生的方法,利用 Kubernetes 标准化的 API、控制器 功能和资源定义。 它解耦了不同的功能,并通过这些流程,使它们更容易优化。 此外,它使得引入新的、历史上积累的 流程垃圾变得困难。
寻找最优惠价格的最佳交易
一种 优化成本的直接方式是比较列表价格,并选择最便宜的选项。 便宜 因此是相对的,因为你可能会降低性能、吞吐量、可用的 IP 地址等。 成本和利用率是很好的指标,但必须评估它们的二级影响。 例如,减少实例大小可能会降低吞吐量和性能,这会导致你不得不引入另一个实例。 这样,你可能会用两个实例达到 80%-90% 的利用率,但仍然支付和使用更大实例的费用相同,甚至更多。 即使是利用率更低。
如果你不受地域限制,某些地区比其他地区便宜;即便如此,它的影响不大。 目前一个强有力的选择是利用 ARM 服务器。 如果你无法在其上运行你自己或用户的工作负载,至少可以将其用于托管服务,例如数据库。 这可以节省 20%-30% 或更多 的成本。
这些都是非常明显的步骤。 找到并识别在正确区域中不被过度配置的正确资源。 如前所述,当你保持资源精简时,通常能实现更好的成本效益。 并且清晰明了。
有很多工具,如 Cloudability 或 Flexera,可以帮助你找到更便宜的选项,有些工具甚至建议改变架构来降低成本。 这些工具非常有助于入门,但它们通常也有很高的价格,而它们所编码的知识并不是你不能通过一些自己的研究或参加免费的成本 优化 课程获得的。
设计以实现最高利用率和最低需求
随着平台工程师减少 基础设施,它很可能会影响用户体验。 平台变得越灵活,它就越能适应给定的情况。 你可以引入主动组件,简化系统并在可能的情况下减少系统大小,同时教育用户了解增加或减少工作负载的可能性。 将他们的利用率与账单匹配可以激励用户负责任地使用资源。 责任使用。
之前,你了解了许多小节点与少数大节点之间的困难。 我们还发现,我们可以轻松支持不同的 CPU 架构,并动态分配资源。 所有这些因素在定义平台核心时都起着重要作用。 我总是脑海中浮现出蚂蚁巢穴的图景。 在它们的中心,有很多活动发生,但核心是一个稳定的结构,里面包含着大部分蚂蚁。 当巢穴,也就是我们的平台,发展壮大时,我们将扩展这一部分。 但有时,我们并不知道这是否只是暂时的。 在整本书中,你已经看到不同的方法来处理动态工作负载,在接下来的章节中,我们将展示一些扩展的最佳实践。 扩展。
那么,理想的图景是什么样的呢? 除了取决于你的需求之外,平台应尽可能利用其资源,同时没有额外的基础设施开销。 简单来说,作为一个平台,这并不容易做到。 工作负载并不完全掌握在你手中,它可能从静态的长时间运行的资源密集型软件,到成千上万个不断变化的无服务器容器。 我们可以从中得出的结论是,你可以尽情地设计平台, 但最终,一切 都归结为一个持续的工作过程:分析、反应和调整。 随着时间的推移,你可以在平台中构建一个自动反应的基础层,覆盖大多数情况,但你仍然需要继续优化。
在本章的最后部分,我们将通过一些具体的例子来看一下如何进行扩展和优化。 你将学习到反应性和预测性扩展,以及 成本意识 工程。
自动扩展、冷存储和其他成本优化的技巧
在 第四章中,在 自动伸缩集群和工作负载 一节中,我们已经讨论了 Kubernetes 带来的一个关键好处和 核心功能。 K8s 内置了多种不同的工具和机制来实现伸缩,例如配置 ReplicaSets(我们希望每个工作负载的实例数量)或使用可观察性数据来驱动自动伸缩 决策,利用 水平 Pod 自动伸缩器 (HPA), 垂直 Pod 自动伸缩器 (VPA),或 Kubernetes 事件驱动自动伸缩 (KEDA)。 有 一个很棒的免费教程,详细介绍了所有 Kubernetes 提供的自动伸缩选项, Is It Observable 提供的。这里是 YouTube 教程链接,里面还包含了 GitHub 教程的链接: https://www.youtube.com/watch?v=qMP6tbKioLI。
自动伸缩的主要用例 是确保工作负载拥有足够的计算、内存和存储,以实现特定的可用性目标。 以我们的金融公司 ACME 为例,这可能意味着他们使用自动伸缩来确保其金融交易后台能够在 100 毫秒的响应时间内处理 1000 个并发交易。 尽管自动伸缩可以帮助我们实现可用性目标,但它也有成本,因为伸缩资源意味着需要为额外的计算(CPU)或内存付费。 不当的自动伸缩——伸缩过多、从不缩减,或在错误的时间伸缩——也会导致计划外的成本激增,同时无法实现自动伸缩的真正目标!
正确的自动伸缩 是我们所期望的。 正确地做自动伸缩不仅可以帮助我们实现业务和技术目标,还能让我们利用自动伸缩来控制成本。 让我们来看看平台工程师应该了解的几个自动伸缩话题,以及我们还能做些什么来优化成本。 请记住,接下来我们讨论的一些实践也适用于任何类型的工作负载:无论是云工作负载还是其他。
自动伸缩的多种形式
云计算和 Kubernetes 作为核心平台的可扩展性是平台工程师手中非常强大的能力。 然而,扩展的方法有很多种。 有不同的触发点可能导致系统进行扩展,而且,扩展不仅仅是一个方向(通常是向上),我们还必须考虑将系统缩小——甚至缩小到零,以避免浪费资源! 以至于毫无意义!
向上扩展——不仅仅是基于 CPU 和内存
大多数 工程师都熟悉基于 CPU 和内存进行扩展。 你会发现的多数 HPA 示例通常是根据该 Pod 的某个平均 CPU 利用率来扩展 Pod 的副本。 这就是我们通常所说的响应性扩展,因为我们是基于达到某个阈值来进行扩展的 响应。
然而,基于 CPU 或内存进行扩展并不总是最佳选择。 这也不是我们唯一的选择,尽管大多数扩展框架最初都是基于 CPU 和内存进行扩展的,因为这两个是可以在 Pods 或命名空间中设置的关键限制。
对于针对特定吞吐量优化的服务来说,例如,基于并发请求来进行扩展会更有意义。 同样,对于 CPU,除了根据平均 CPU 利用率进行扩展,还可以在 Kubernetes 开始限制 Pod 的 CPU 时进行扩展,这可能会更好。 在前面的章节中,我们提到了像 KEDA 以及 云原生计算基金会 (CNCF) Keptn 项目,它们可以提供来自各种可观测性源(如 Prometheus、Dynatrace、Datadog、New Relic)的任何类型的度量,用于事件驱动的自动扩展。 要查看这个如何工作,请查阅 Keptn 文档中的完整示例: https://keptn.sh/stable/docs/use-cases/keda。
关键要点是,并不是每个工作负载都受限于 CPU 或内存,并且 Kubernetes 不仅仅限制你根据这两个关键属性来定义扩展规则。 根据真正使工作负载更高效地执行的因素来定义扩展规则,还会导致更高效的资源利用,从而带来一个在成本上也得到了 优化 的系统!
预测性扩展与响应性扩展
通常认为 自动扩展是瞬时工作的。 但事实并非如此! 想想金融公司 ACME。 如果在发薪日,每个人都想查看他们的新账户余额,这可能意味着当天前几个小时内流量会激增 10 倍。 然而,云服务提供商不能保证所有这些资源能够立即提供,因为你正在与许多其他组织竞争,这些组织也在尝试同时请求云资源。 此外,工作负载本身也不能立即处理传入的请求,因为许多 Pod 在准备好之前都有一定的启动时间。
这个问题可以通过预测性扩展来解决。 与反应性扩展——即在达到某个阈值时进行扩展,如前一节所述——相比,预测性扩展是基于潜在的未来情境并在达到阈值之前进行反应。 预测性并不意味着我们需要一个魔法玻璃球来告诉我们未来。 它可以像在我们预期流量激增前几小时启动扩展那样简单;例如,在发薪日之前或在某个特定时间开始的营销活动前。 具体时间。
其他预测可能更为动态:
-
季节性:可以通过查看历史数据来基于季节性做出预测。 电子商务是一个很好的例子,一年四季中总会有一些日期流量激增,例如黑色星期五或网络星期一。 这些激增很容易 预测 到!
-
相关数据源:另一个预测方式是查看其他数据源。 保险公司通常会查看恶劣天气数据。 当预报有暴风雨并且这些暴风雨有可能造成损害时,预测性扩展是合理的,特别是对于客户提交保险索赔时所使用的服务。 在这种特定情景下,您甚至可以在接近 暴风雨的特定区域进行扩展。
-
系统依赖:在复杂系统中,基于系统其他部分的负载行为来扩展依赖组件也是一个可选方案。 以酒店行业为例。 如果我们看到更多人搜索航班,因为他们想在长周末出游,我们也可以预测性地扩展提供酒店、租车或其他可预订活动推荐的后端服务。 前往 旅游目的地的推荐。
用例 – 预测性存储扩展以优化成本和可用性
现在 我们已经了解了不同的预测性扩展方法,接下来让我们将其应用于一个非常高成本的 例子:存储!
我们的数字系统生成的数据比以往任何时候都要多,而且预计这一趋势将持续下去。 存储 —— 尽管看似有充足的空间 —— 对许多组织来说仍然是一个重要的成本因素。 对我们金融一号 ACME 公司来说也是如此,假设我们需要存储系统处理的每一笔财务交易的所有细节。 作为一个组织,我们需要确保可以始终保存所有记录,但同时我们也希望确保不为目前不需要的存储空间支付费用。 因此,我们希望将空闲磁盘空间保持尽可能小,以避免为不需要的磁盘付费。 另一方面,我们还需要确保有足够的空闲磁盘空间,以防交易量激增,因为我们无法承受交易丢失的风险。 考虑到大规模扩展磁盘不能瞬间完成,可能需要几个小时,我们可以应用预测性存储扩展来满足所有 我们的需求!
以下截图帮助我们理解这一过程是如何工作的。 它展示了随时间变化的磁盘空闲空间百分比。 我们写入的数据越多,空闲磁盘空间就越少。 与其在某个固定阈值上扩展我们的云存储,我们可以使用预测模型,在预计会在扩展所需的时间内达到某个低阈值时进行扩展——从而确保我们始终拥有足够的空闲磁盘空间,而不会为 过多的空间支付费用:
图 8.4:云存储的预测性扩展,以优化成本和可用性
前面这个例子来自一个真实的使用案例,通过持续根据预测性扩展方法调整存储大小,实现了显著的成本节约!
缩放到零——在不需要时关闭系统
虽然我们运营的许多系统需要 24/7 全天候可用,但有些系统并没有这个要求。 这些可能是仅在正常工作时间由员工使用的系统,或是仅在一天、一个月或一年中特定时间需要执行某些任务的系统。 我敢肯定,我们都能想到一些经常处于空闲状态但仍消耗宝贵资源的系统,尽管它们当前并不 被需要。
虚拟机 (VM) 是缩放到零的一个很好的例子。 多年来,我们一直在做这件事:在不需要时关闭虚拟机,并在需要继续工作时将其重新启动。 许多组织通过自动化的方式实现这一点:在工作日结束时自动关闭用于日常业务任务的虚拟机,并在第二天早上再次启动它们。 单单这一点就带来了巨大的成本节省机会,因为许多虚拟机可以在晚上 和周末关闭!
在 Kubernetes 中,我们也有机会将工作负载缩放到零。 我们可以使用前面讨论过的 KEDA,也可以考虑像 Knative(可以运行无服务器工作负载)或 kube-green
这样的工具。后者旨在减少 K8s 工作负载和集群的 CO2 足迹,并且能够在不需要时将工作负载、节点或集群置于休眠状态。 想了解更多关于 kube-green
的信息,请访问以下 网站: https://kube-green.dev/。
我们仍然需要回答一个问题:哪些工作负载可以缩放到零,以及可以缩放多久? 我们从这些工作负载的所有者那里获得这些数据,明确它们何时以及需要多长时间。 另一种方法是直接利用可观察性数据,查看一天中的哪些时段使用了哪些工作负载,并基于此创建一个 kube-green
休眠配置来将工作负载缩放到零。 这种实现的一个示例可以在 可持续性研讨会 Henrik Rexed 的讲座中找到: https://github.com/henrikrexed/Sustainability-workshop。
从工作负载扩展到集群
到目前为止,我们讨论了很多关于调整规模或扩展工作负载的内容,以确保我们拥有足够的资源来满足业务目标,同时也避免过度配置,以便我们可以优化 成本。
随着我们扩展工作负载的规模, 底层 Kubernetes 集群也需要相应地调整大小。 这时,集群自动扩展功能就派上用场,它会扩大集群的节点数量,确保有足够的资源来运行所有工作负载,同时还会在节点未被充分利用且工作负载能够分配到剩余节点时,缩减节点。 这确保了底层集群节点机器的优化,从而最终 节省了成本。
Kubernetes 文档网站上已有大量现有文档: https://kubernetes.io/docs/concepts/cluster-administration/cluster-autoscaling/。
有一些特定的自动扩展工具,比如 Karpenter——最初由 AWS 开发——它帮助调整 Kubernetes 集群的规模,同时控制成本。 Karpenter 与你的云服务提供商的 API 集成,能够配置处理特定工作负载所需的适当节点大小。 如果不再需要,它还会缩减节点。
除了像 kube-green
(之前提到过)这样的工具外,Karpenter 是一个很好的选项,可以在考虑成本的同时扩展你的集群。 要了解更多关于 Karpenter 的信息,请访问 https://karpenter.sh/。
作为平台工程师, 了解所有不同的扩展选项非常重要。 其中许多可以配置来调整工作负载和集群的规模。 对于一些情况,与工程团队和工作负载所有者密切合作,定义适合特定工作负载的扩展策略也非常重要。 总体而言,自动扩展——无论是计算、内存还是存储——是成本效益优化 平台工程的关键推动力之一!
成本感知工程
现在我们已经了解了如何在平台中构建内容,以实现适当规模和自动扩展来节省成本,我们还需要讨论如何让工程团队从一开始就更具成本意识。 最好的成本优化从组织中的每个人意识到他们的行为对成本的影响开始,因此他们会默认地构建和设计更具成本效益的系统。 基于标签的成本报告是一种让团队意识到他们的成本的方法。 这种策略在本章前面已经讨论过。
让我们来看看一些我们认为每个人都应该考虑的额外选项,因为它们有可能 带来 成本意识的工程实践!
只需要你所需的请求方式
在云计算初期,许多组织给予其工程团队完全访问云门户的权限。 这种便捷的“自助服务”提升了生产力,因为每个人都可以轻松地创建新的虚拟机、存储服务,甚至是 Kubernetes 集群。 然而,这种“西部荒野”式的做法导致了许多组织的成本爆炸,因为用户只是创建新服务,却没有考虑基本问题,例如:我究竟需要多大的虚拟环境,且需要多长时间? 我需要它多久?
作者曾与之合作的一个组织是一家金融机构。 他们没有给每个人完全访问云门户的权限,而是构建了自己的自助服务门户,允许工程团队创建新的虚拟机、数据库、集群等。 作为自助服务门户的一部分,团队必须定义他们需要资源的应用程序和环境,以及需要多长时间使用这些机器;例如,仅在工作时间内。 该结果是,通过自动关闭不再需要的服务,成本减少了 60%。 下图显示了工程师如何请求他们所需的资源。 右侧还可以看到详细的报告以及该组织正在实现的整体优化目标:
图 8.5:一款报告并降低成本的自助平台
在这个用例中,报告不仅关注成本,还包括碳足迹,这对大多数组织来说是一个重要话题。 通过中央平台提供这一自助服务,使得该组织可以从一开始就让工程师更加关注成本,同时也展示了他们的行动所带来的正面影响。 tive impact their actions have。
租赁模式与固定费用资源
前面的例子 非常好,但要求团队在一开始就考虑清楚他们确切需要哪些资源、需要多长时间。 另一种做法是采用 租赁模式。这意味着什么呢?
当 A 开发团队请求一个资源——比如他们需要用来做开发工作的 Kubernetes 集群——他们可以简单地请求一个默认的时间段;例如,1 周。 这个 1 周的时间段就成为了他们对该资源的“初始租期”。 时间段和团队所有权将通过创建的资源标签进行管理。 通过自动化,在租期结束前 1 天,可以通过电子邮件或聊天消息提醒团队他们的租期即将到期。 消息还可以提供给他们一个选项, 延长租期 一天或一周,并提醒他们这段额外时间会带来的费用。
这种方法已在多个组织中实施,并确保任何团队仍然可以通过自助服务获得他们需要的资源。 它还确保那些被遗忘或不再需要的资源会被关闭,而无需事先指定资源需要的确切时间。 exactly how long a resource will be needed.
现在我们已经讨论了如何仅在资源真正需要时运行它们以节省成本,接下来让我们讨论一下工程师如何优化 他们的代码以带来 成本影响!
绿色工程 – 优化你的代码
高效 的代码通常不仅执行更快;它通常还需要更少的 CPU、内存,甚至可能需要更少的磁盘存储或网络带宽。 一切资源的减少也意味着成本的降低。 那么,为什么不是每个人一开始就编写高效的代码呢?
工程师们往往面临着时间压力,需要交付新功能,或者组织没有投资能够测试并提供优化建议的工具,作为软件交付生命周期的一部分。 作者们过去曾与许多软件组织合作,识别出一些非常常见的模式,这些模式导致了低效的、因此成本高昂的代码。 以下是 一些例子:
-
请求过多数据:开发人员不是利用查询语言只请求特定操作所需的数据,而是检索更多数据,然后在内存中进行过滤和处理。 这导致了更多的网络流量来传输数据,更多的内存用于存储数据,以及更多的 CPU 用于在 客户端代码中进行迭代和过滤。
-
低效使用库或算法:许多软件库存在是为了完成某些任务;例如, 对象关系映射器 (ORMs) 用于将数据库中的数据映射到 开发语言中的对象。 然而,开发团队并不总是有时间来正确测试或配置这些库,以便为特定的使用案例进行优化。 因此,导致了低效的使用,进而导致更高的 CPU、内存、网络和 磁盘访问。
-
过度日志记录:软件工程师使用日志框架来记录代码执行过程中的信息。 日志通常用于分析、诊断和故障排除失败或有问题的代码执行。 然而,日志经常被过度创建或重复记录,且没有适当的格式或足够的上下文信息;例如,未设置日志级别。 这导致了在创建日志时的开销,同时也导致了当这些日志被摄取、转换和由 可观察性平台分析时的开销。
在软件工程中,还有许多 模式会导致性能或可扩展性问题。 除了检测这些模式外,应用程序的架构评审还可以通过更高效的架构或重写代码来实现成本降低。 一个显著的例子是亚马逊 Prime Video,它放弃了基于 AWS 的分布式无服务器架构,转而使用被描述为 单体架构 的视频质量分析,这样做将基础设施成本降低了 90% [4]。最终,这些模式也意味着低效的代码执行,从而导致更高的成本。 作为平台工程团队,我们有机会利用现代可观测性工具分析这些模式,并将这些信息反馈给工程师,提醒他们不仅要关注代码带来的成本,还要告诉他们在哪里可以开始进行优化,正如下一张截图所示。 这两张图表展示了每个服务创建了多少日志,并突出了哪些日志没有正确配置;例如,没有设置日志 级别:
图 8.6:为团队提供关于过度日志等模式的简单洞察
这将引导我进入本节的最后部分,即为工程师提供教育机会,让他们从 编写的第一行代码 开始就意识到成本!
教育机会
尽管这可能不会 成为平台工程团队的主要职责,但由于工程团队使用我们的平台作为自助服务来部署他们的应用程序,我们可以利用这个平台来教育每个人,提醒他们在使用平台部署软件 服务时所产生的成本影响。
在之前的部分中,我们已经强调了使用案例,比如向工程团队发送成本和使用报告,或识别和突出低效的代码模式。 实现这一目标的关键是正确的标签(例如,谁拥有基础设施和应用程序的哪一部分),以及良好的可观测性(例如,哪些系统使用了多少 CPU、内存、网络等)。 拥有这些信息,平台工程团队可以主动将这些数据推送给各团队,并借此持续展示他们的应用程序带来的成本影响。 持续进行这一过程还将产生教育效果,使工程师能够更清楚地了解 他们行为的成本影响。 行为的成本影响。
总结
在本章中,你应该已经培养了对成本的敏感度,并且有了如何在你的平台上处理这个话题的想法。 优秀的平台为用户提供透明度,并提供灵活的选项以根据不同触发条件调整工作负载。 此时,你应该能够结合前几章学到的方法,如动态资源分配与 GPU 配合使用,以实现高利用率和最佳 成本分配。
记住,仅仅从成本角度来看并不足以降低整体平台成本,因为一些服务器尺寸的减少可能会因为云提供商的其他限制而增加对多个小型服务器的需求。 标签策略为控制和透明度奠定了基础。 看似简单的事情可能最终会导致许多组织讨论。 为了优化成本,你还可以利用其他元素,如流程,并达成长期承诺,以获得更好的 定价优惠。
最后,我们为你的平台提供了一些实际的示例和最佳实践。 我们回顾了不同的扩展方法,以及预测性扩展与反应性扩展之间的区别,并重点讨论了除 CPU 之外的其他扩展因素,如内存 和存储。
总结一下,当你理性思考并将你在平台上花费的钱视为自己的钱时,你就能变得非常具有成本效益。 作为一个平台工程团队,你还可以制定一个度量标准,定义平台的效率,以便与你的管理层达成一致,使用这部分免费预算进行进一步的投资和优化。 记住,尽管云给我们提供了一个 无限的 资源量,我们不必仅仅因为这些资源 存在就全部使用它们。
让我们直接进入最后一章。 正如我们之前所说,唯一的不变就是不变。 在我们最后一章中,我们将讨论持续变化以及如何在轻量架构和可持续理念的推动下生存,并探索变革的黄金路径。 为了结束本章,我们敢于展望未来,讨论一些可能或可能不会在 未来几年内变得相关的 技术趋势。
进一步阅读
-
[1] 我们为何离开云端 – 大卫 海内梅尔·汉森:
-
[2] FinOps 框架高清 海报: https://www.finops.org/wp-content/uploads/2024/03/FinOps-Framework-Poster-v4.pdf
-
[3] 云端 托管人: https://cloudcustodian.io/
-
[4] Prime Video 成本 优化: https://www.thestack.technology/amazon-prime-video-microservices-monolith/
第九章:选择技术债务以避免平台崩溃
什么是 技术债务 以及它是如何避免平台崩溃的? 技术债务是对一项软件的持续维护成本。 这包括在运行环境上的实际支出、用于操作和更新的时间以及客户满意度。 就像货币债务一样,技术债务也会不断积累。 随着债务的滚雪球效应,团队无法再承受新的债务或交付新的功能,因为他们的所有工作时间都花费在缓解 当前的问题上。
一个团队被技术债务淹没的迹象很容易发现:你的团队是否在试图维护极其过时的软件? 你是否有操作脆弱且/或容易崩溃的系统? 你是否在使用不必要的 复杂软件?
如果你对这些问题的回答是肯定的,那么就有相应的成本。 例如,更新、安全性和操作过时的软件从非常昂贵到不切实际不等。 操作上脆弱的软件需要频繁的人工干预,因此会消耗你的运维团队的时间,并增加团队倦怠的风险。 最后但同样重要的是,过于复杂的软件通常很难理解。 它很难阅读和维护,这意味着持续的开发和 维护成本。
没有任何软件能够做到完全没有债务,但当将其与功能进行评估时,可以帮助保证平台更加成功的运行状态。 在这一章中,我们将涵盖以下主题:
-
有意识地承担技术 债务
-
利用数据驱动 设计决策
-
维护和重构 技术债务
-
重写与重构 – 一份 实用指南
-
架构决策记录 – 为 后世文档化
有意识地承担技术债务
“明智地选择你的技术债务,”你的作者 Hilliary 总是这么说。 但是,选择你的技术债务意味着什么呢? 难道我们不是应该解决问题并 识别解决方案吗?
是的。 但是这两者 并不互相排斥。 很可能没有一个现成的完美解决方案。 通常,产品是根据特定的使用场景设计的,但这些场景并不涵盖所有情况。 没有任何产品能够覆盖每个用户潜在需求和工作流的每种组合。 事实上,在你的平台生命周期的过程中,情况也将是一样的。 当我们承诺接受一个解决方案或技术的不足时,意味着我们承诺以另一种方式来弥补这些不足。 这种补偿应该被管理,以避免我们为平台团队或开发者创造额外的工作负担或手动工作流,进而导致无法持续的辛苦劳动。 过多的技术债务会削弱平台的投资回报率。
技术决策应该是协作进行的。 团队达成共识是很重要的,这样可以确保前进的方向至少是大多数人都感到舒适的。 要有意识地承担技术债务,必须能够评估它并理解以下 重要问题的答案。
以下是一个 技术债务评估,供你 试用:
-
你今天解决了哪些问题? 正在解决什么问题?
-
你目前没有(还没有) 什么问题?
-
这将需要我们多少时间来构建 或采用?
-
这将花费我们多少 来运行?
-
我们需要什么来 维持它?
-
我们的 预期回报是什么?
-
团队能否维持 它 按原样?
-
如果不是,团队是否准备好提升技能, 并且有足够时间?
-
如果是开源软件,社区有多强大? 团队如何加入 这个社区?
-
-
任何新加入的团队成员需要什么才能尽快上手? 快速上手需要什么?
-
我们什么时候能知道是时候 扩展了?
-
我们将如何 扩展?
-
我们的弃用标准是什么 对于这个解决方案?
将这些问题的答案可视化为一个会计账簿。 在你将所有内容加总后,你是领先还是落后? 像你的账本一样,这些数值必须平衡。 团队或组织的技术债务平衡更多的是社会技术层面的,而非单纯的技术层面。 工程的艺术性可能会导致一种情感上的联系,从而产生盲点和缺陷,影响解决方案的长期可持续性。 例如,由充满激情的工程师编写的前沿工具,如果没有以邀请团队协作的方式编写,且技术部分排除了不少没有必要技能来维护这些工具的团队成员,它们更可能会被弃用或遭到破坏。 这些工具。
一个解决方案可以尽可能聪明、优雅且简单,但如果不适合团队或问题所在的环境,它仍然可能是不可持续的。 例如,如果解决方案是用 Rust 编写的,而团队至今一直在使用 Ruby,那么如果设计和实现该解决方案的人离开,团队不太可能能够维持该解决方案。 例如,解决方案是用 Rust 编写的,而团队至今一直在使用 Ruby,那么如果设计和实现 该解决方案的人离开,团队不太可能能够维持该解决方案。
这就是为什么在所有问题中,最后一个问题变得最为重要。 弃用标准告诉我们何时该退役或替换一个工具、一个系统或任何一段代码。 所有事物最终都会变成遗留软件,因此弃用计划或评估弃用标准是维持 技术债务的重要方面。
可持续地超越最薄可行平台(TVP)
当我们构建一个 TVP 时,我们 承诺于 足够好的概念,但随着发展的推进,曾经足够好的东西可能会开始变得不合适。 这是平台技术债务的第一次迭代。 技术债务是增长和演化的标志。 它有时可能是回顾起来犯了错误决策的结果,但同样也常常是超越当初决策合理性时的结果。 在某种程度上,技术债务是做生意的成本,而在其他方面,它可以看作是成长的痛苦。 无论你怎么看,它都是一种需要管理 或减轻的症状。
当平台需要超越最初的薄弱阶段,并开始朝着成为一个强大的解决方案的方向前进,支持一个规模化的组织时,我们开始重新评估过去的决策。 我们是否扩展现有功能? 我们是否替换任何组件? 我们是否淘汰那些未能按预期表现的功能? 这次过渡到下一代 IDP 是由与定义 TVP 相同的数据推动的。
关键用户旅程
最初在 IDP 的发现或规划阶段定义, 关键用户旅程 定义了用户与 IDP 之间交互的预期工作流,以及他们如何期望在其中 获得成功。
然而,随着组织的发展,这些关键用户旅程也在不断变化。 过去对组织至关重要的事物,可能不再重要,或随着时间的推移变得不如其他新兴事物重要。
此外,如果你遵循了 TVP 模式,那么初始阶段构建平台时可能排除了一些用例和潜在的关键用户旅程。 定期审查这些旅程,并有意识地决定添加新的旅程或扩展现有的旅程,将影响你的平台如何成长,以及你的 团队如何创新。
关键用户旅程是围绕用户故事构建的。 对于 Financial One ACME,将需要满足某个特定的地理区域,因此一个用户旅程将这样描述:“作为平台的用户,我希望部署一个仅能在北美访问的应用程序。” 一旦平台团队接受了用户故事,应该创建相应的用户旅程。 并不是每个提议的用户故事都应该立即被接受。 例如,在前述的故事中,访问的区域边界需要支持基础设施。 如果这个用例不是业务关键性的,且可以在不增加额外工作的情况下满足,即使是次优的,或者可以在平台外支持,那么可能应该先处理一个更重要的用户故事。
假设用户旅程“仅在北美可访问的应用程序部署”被接受,我们可以使用诸如 Crossplane 和复合特性等工具来实现,它允许平台工程团队定义一个模板,以便在特定地区部署应用程序。 然后,用户在他们的旅程中只需创建该模板的一个实例,指定所有值,并提交到 Git。 其余的由平台的核心交付工具处理。 最终结果将是仅能从指定地区访问的已部署应用程序。 从技术角度来看,这可以通过特定的入口路由规则来实现,针对区域特定的域名,如下所示 示例定义:
apiVersion: composites.financialone.acme/v1alpha1
kind: FinancialBackend
metadata:
name: tenantABC-us
spec:
service-versions:
fund-transfer: 2.34.3
account-info: 1.17.0
redis-cache:
version: 7.4.2
name: transfer-cache
size: medium
database:
size: large
name: accounts-db
region: "us-east"
ingress:
url: "https://tenantABC.financialone-us.acme"
有关 Crossplane 的更多详细信息,请参阅 工作负载和应用生命周期编排 部分中的 第四章。
评估每个用户故事的重要性和价值有助于平台的可持续增长。 团队不可能一蹴而就地做完所有事情,因此,保持项目范围不会以团队无法承受的速度扩展是很重要的。 不能承受的。
这同样是所有软件工程中应遵循的原则,以这种方式,平台和面向最终用户的应用程序 没有什么不同。
避免过度工程化
什么是 过度工程化? 过度工程化 通常表现为创建一个非常强大且功能完备的系统,试图解决每一个可能出现的问题,即使这些问题还不存在。 过度工程化是一个 容易但危险的陷阱。 我们评估中的前两个问题帮助我们避免陷入过度工程化的解决方案。 本质上,我们通过这些问题来定义每个决策的目标和非目标。 理解我们解决的是什么问题和用例,并明确我们不解决的是什么——或者至少暂时不解决——有助于指导我们的决策,并保持项目 范围的合理性。
例如,在一个可观察性解决方案中,随着时间的推移,预期会有大量数据。 很容易陷入规划无限数据并相应构建基础设施的反模式。 相反,应该提问 有针对性的问题:
-
多少数据是 真正有用的?
-
我们值得保存 这些数据多久呢?
-
我们需要保留 多久 数据?
-
谁需要访问 数据?
通过根据你的答案定义参数,你将使数据保持可管理的状态。 如果没有必要将数据保留超过一个月或一年,那么解决方案就不应该是编写一个能够处理大量数据的系统。 更好的解决方案是一个处理小量数据的系统,并定期清除不需要的数据。 或者,如果完全清除不符合你的用例,或者觉得过于极端,那么数据聚合可能是正确的方法。 例如,几秒钟内收集的遥测数据可以汇总成天、周、月甚至年。 数据聚合可以让覆盖长时间段的数据依然存在,但无需为大量数据设计存储和检索机制。 如果你觉得自己正经历回忆 第七章,观察得很敏锐。 帮助我们防止过度工程化的相同问题,也帮助我们在许多情况下理解我们的安全态势。
避免过度工程化并不意味着从不为你还没有遇到的问题进行规划。 未能为未来做好规划也可能导致创建需要完全替换的解决方案。 现在,当我们考虑可组合系统 架构和微服务的力量时,这可能是有益的。 有时,完全更换一个组件是获得最佳结果的最佳方式。 然而,这通常不是一件容易或快速的事情,所以如果这是所需的,它仍然需要谨慎地规划并按照计划执行。 避免过度工程化的普遍建议是解决今天的问题,并为明天的问题制定路线图。 保持系统简洁可以让它们更容易维护,并帮助确保产品在发展过程中更加灵活。 产品不断发展。
构建与购买——构建决策树
在 添加新组件并评估技术债务的过程中,最重要的问题之一是我们应该构建它还是购买它。 这通常是社会技术性问题多于技术性问题,因为它涉及平衡内在的构建欲望与如果购买更合适时的需求。 对于团队来说。
工程师希望自己设计事物是很自然的。 我们的身份、优势、经验和兴趣使我们偏向于做出与这些因素一致的决策。 然而,关键在于从情感和智力上脱离对问题解决的执着,以尽可能不带偏见的眼光看待这些选项。
以下是一个关于是否自建或购买解决方案的决策树图。 请注意, 购买 并不总是意味着花费金钱;它也可能指的是采用一个免费的或开源的 工具:
图 9.1:自建与购买决策树
遵循这个 决策树应当由团队共同完成。 一些问题可能有更主观的答案,或者不同团队成员对此有不同的看法。 协作是推动这些 决策可持续发展的关键。
让我们一起分析一个决策树的例子。 在这个例子中,我们虚构的公司 Financial One ACME 希望现代化其旧有的技术栈。 在他们的目标下,他们可能需要解决以前没有遇到过的技术挑战,或者考虑整合一些新的功能,而这些功能在没有使用更 现代架构的系统中是无法实现的。
一个新功能或不同功能的例子可能是评估数据转换工具的采用。 随着更多数据能力在云原生架构中得到实现,产品团队、平台团队或两者可能会考虑利用这样的工具来使原始数据 更加有用。
例如,如果平台团队想要使用 Prometheus 收集平台的使用指标,并帮助将其转化为 DORA 指标,那么在这个工作流中,数据转换将会很有用。 同样,数据转换的其他使用场景在公司内也可能有用,因此实施时需要考虑多个用户需求。 。
如果我们对这个例子进行过度简化,我们将按照决策树进行操作 如下:
Q:我们想解决的是什么问题 ?
A:我们需要启用自动化 数据转换。
Q:是否有现有的 解决方案?
A:是的 – Apache Airflow 和 Argo Workflows。
请注意,在这种情况下,团队已经识别出了两个潜在的解决方案。 从这里开始,接下来的问题应当针对这两个解决方案进行。 它符合要求吗? 团队能维持它吗? 我们能负担得起吗? 由于这两个解决方案都是开源的,负担得起的问题更可能是关于运行的相对成本,而这可能并不容易直接判断。 在评估开源时,最重要的问题是,当一个工具符合要求时,团队是否有能力维护该解决方案。 团队的能力不能在空白中回答,需要团队 权衡他们的能力和对新集成的舒适度。 如果需要提升技能,并不意味着技术债务不可接受,而是必须在时间表中添加时间,让每个人都能跟上进度,以便得出一个结论 这在潜力上是可持续的 ,而不是 这 是不可持续的。
团队认同的重要性
虽然许多 平台架构师会参与 IDP 的日常运行,但同样有可能他们不会。 因此,负责日常管理和维护的人需要充分投入 IDP 的方向及其产品。 这通常是平台团队,但如果你构建的是一个鼓励自服务并与整个工程组织广泛合作的平台,那么团队的范围将会更大 。
可能会出现无法达成 100%共识的情况,在这种情况下,就必须 不同意 并且 承诺。团队共识可以减少倦怠,但必须与确保 IDP 沿着 路线图继续发展的需求相平衡。
在需要团队提升某种技能的决策中,团队认同至关重要,例如学习工具本身或新的编程语言。 如果团队不愿意或无法适应技术解决方案,那么无论它有多少其他 可取之处,最终该解决方案都不会提供价值。
现在我们已经熟悉了技术债务的概念,我们可以通过 利用数据来研究缓解该债务的策略。
使用数据驱动设计决策
到目前为止,我们已经 讨论了可观察性数据对自服务和开发者满意度的重要性,但并没有涉及 IDP 所收集的遥测数据的其他应用场景。 数据是一个强大的资产,可以帮助决策,并在成本和时间节约方面发挥作用。 例如,当你面对两个解决方案 ,它们在解决方案契合度和技术债务方面似乎各占一半时,很难知道该如何选择。 这时,你可以利用数据来做出决策,并跟踪你的 技术债务。
你团队需要用于决策的几乎所有数据,要么已经存在于你的可观察性解决方案中,要么可以轻松添加到其中。 这些数据将非常容易获得。
可观察性至关重要
经过 花费大量时间和精力搭建可观察性堆栈、收集和保存数据之后,如果仅将这些数据用于事件管理和响应,那几乎是不负责任的做法。 幸运的是,平台团队很少被指责为不负责任,因此你很可能已经知道,这些数据可以以更有意义的方式使用。 将可观察性数据应用于决策,并利用它来识别和管理技术债务,是你的可观察性堆栈的关键应用场景,虽然有时这些场景会被忽视或强调不足。 在 第六章中,我们讨论了可观察性如何以及在哪里应用,但现在让我们来看一下同样的方面,应用于 技术债务。
操作数据和繁琐工作
我们简要 介绍了 服务级别目标 (SLO)的概念,在 第三章中我们曾提到过它。我们在 第六章中再次讨论了它,作为确保客户满意度的关键因素。 然而,SLO 数据也可以用来检查和识别 技术债务。
SLO 是一个目标。 通常,这个目标在初期并不会被定义为最理想的状态,因为在新项目启动时很难立刻实现这一点。 对于新项目来说,一个好的 SLO 定义会更加保守,但会有一条通向最终目标的路线图。 好的 SLO 是那些随着系统 演变而不断变化的 SLO。
例如,如果理想的 SLO 是 28 天内 99%的应用部署将成功,那么最初的目标可能低至 60%。在一切都是绿地(从零开始)的情况下,设置一个更高的目标是合理的,但如果我们看看Financial One ACME,这是一个正在迁移大量遗留产品的公司,你会知道那里团队的起点处于不利位置。 因此,设定合理可实现的目标,并在达成这些目标后定义下一个目标,是一种更可持续的方法。 这样可以让团队优先处理阻碍成功的问题,并展示成果。 你可能会问自己,“这就是游戏化吗?” 是的。 是的,就是游戏化。然而,正向反馈循环已被证明能有效减少倦怠并提高开发人员的幸福感。 团队应该被设置为成功,而不是失败。
然而,SLO 并不是唯一重要的操作数据。 完全有可能通过手动干预而不是自动化来维持 SLO。 这正是可靠性工程师通常开始讨论辛劳概念的地方。 辛劳是必须定期或手动完成的无聊、重复的任务。 即使是部分自动化的过程,如果它仍然需要人类按下按钮启动,那么它仍然是辛劳。
有些辛劳是可以接受的,比如为一个还没有准备好全面实施 CI/CD 的组织手动启动发布流水线。但有些辛劳是不可接受的,比如每当需要升级时,工程师手动执行软件每个组件的发布。由于辛劳是由人类完成的,因此它更难追踪,因为追踪辛劳本身就是辛劳。 然而,为了管理技术债务,保持对辛劳的意识并制定减少辛劳的行动计划对确保团队有可接受的认知负载水平和持续创新能力至关重要。
团队和开发人员的辛劳必须根据具体情况和整体情况进行审视,以避免千刀万剐的折磨。 就像我们审查每个存储的数据或每个新增的组件一样,每一项手动任务都必须接受相同的审查:
-
为什么我们需要做这件事?
-
它需要多长时间?
-
我们需要做这个多久?
-
我们能否自动化它?
如果“我们能自动化吗?”的回答是“是的,”那么必须立即将其优先级排序,并加入团队待办事项的积压中,最好是高优先级,因为开发者的时间是组织中最宝贵的资产之一,仅次于数据。 每个团队对劳作的容忍度会有所不同,这取决于团队的组成和他们的技能。 Google SRE (https://sre.google/sre-book/table-of-contents/)一书将劳作的最大目标定义为 50%,但团队会根据团队的性格和其他 组织因素,拥有自己的容忍度。
让我们看一个非常耗时但只需要做一次的任务。 假设是你之前做过的任务;它不一定是某个特定任务。 一些工程师看到手动任务后会直接把它做掉。 其他人则会评估任务,并将其自动化,即使这是一个一次性任务,哪怕这样会让过程 变得更长。
这可能是因为经过同行评审的自动化使用起来比那些可能导致人为错误的步骤更安全,或者因为他们希望永远不再做这件事,他们宁愿不冒着任务是一次性任务的错误风险。 这也可能仅仅是因为对所做的事情进行编码化可以确保它不会随着时间流逝而被遗忘,或被部落知识所遗弃。 无论是执行任务还是为其创建自动化,都是劳作的例子,但尽管花费的时间更长,自动化过程更好地处理了技术 债务。
找到你对劳作的实际容忍度将是一个反复试探的过程,但保持对劳作掌控的一个策略是为其设定服务水平目标(SLO)。 通过将花费在劳作上的时间视为 SLO,当该 SLO 被突破时,比如说我们花费在劳作上的时间不超过 50%,那么就应该进行根本原因分析,找出为什么花费更多时间,识别出纠正措施,以将劳作恢复到预期范围内。 然而,团队应该感到在正确地做事时是安全的,即使这意味着劳作的错误预算在一个 报告期内已被消耗。
DORA 指标
如果我们回顾 我们在 第二章中定义的 DORA 指标和评级,就很容易看出这些指标如何帮助你保持对技术债务的关注:
精英 | 高 | 中 | 低 | |
---|---|---|---|---|
部署 频率 | 按需 | 每天一次到每周一次之间 | 每周一次到每月一次之间 | 每周一次到每月一次之间 |
变更 交付时间 | 少于 1 天 | 1 天到 1 周之间 | 1 周到 1 个月之间 | 1 周到 1 个月之间 |
变更 失败率 | 5% | 10% | 15% | 64% |
失败部署 恢复时间 | 少于 1 小时 | 少于 1 天 | 1 天到 1 周之间 | 1 个月到 6 个月之间 |
表 9.1:DORA 指标能力
如果你已经在你的 IDP 中实施了诸如Keptn之类的 DORA 指标收集解决方案,那么你可能已经开始在 IDP 中的每个组件上跟踪这些指标。随着你随着时间的推移跟踪这些指标,你可能会注意到某些趋势。
例如,如果在 6 个月后更改的交付时间或部署失败率仍处于低到中等范围内,那么值得调查背后的原因,看看是否有未解决的技术债务。
应用程序性能数据
与 DORA 指标相关,但在其他方面相邻的是 IDP 中应用程序和组件的性能数据。尽管平台团队不负责最终用户应用程序,但平台知道应用程序使用的 Pods 和容器的状态。它还可以了解 API 调用的成功与响应时间。平台团队必须使他们服务的开发人员也能理解这些状态,以便他们能够评估这些指标,并确定是否存在问题。这将使开发人员能够管理他们的技术债务。这是平台的可服务性和自助服务的关键方面,对公司持续的成功至关重要。记住,一旦平台投入使用,它是公司中最关键的应用程序;它需要支持自己的成功以及面向最终用户的应用程序的成功。
一个应用程序可以以高度可用的方式架构,充分利用 K8s 平台的强大功能,但这也可能使其无意中掩盖一些可能令人担忧的问题或模式。 因此,应用程序的性能应定期进行衡量和展示。 例如,在一个为高可用性设计的系统中,一个拥有三个 ReplicaSets 的 Pod 可能允许应用程序在没有中断的情况下运行,或者看起来像没有中断,即使偶尔发生 OOM 或其他崩溃。 然而,平台会知道这些崩溃,并且可观察性可以用来确保拥有该应用程序的开发人员也能意识到这些问题 。
在 第六章中,我们讨论了平台如何支持可观察性,在必要时提供一些规范,并允许开发人员自助服务。 让我们来看一个例子,看看 IDP 如何在测量内容时提供一些规范,因为应用程序的状态对平台来说最容易看到。
识别其他关键数据
在 第二章中,我们 定义了 KPIs,以帮助推进 IDP 的采用。 那些推动采用的相同数据点和指标,也可以被用来推动创新并管理技术债务。 此外,在衡量采用时,自然的结果是,你已经衡量了围绕采用的任何失败。 这些失败是一个关键数据点,可以帮助你的团队了解开发人员如何与 平台进行互动。
在 第六章中,重点介绍了如何将平台整合到公司工作的方式,并举例说明未能采用 Tekton 而选择 GitHub Workflows。 这是一个管理技术债务的例子,同时也是一个未充分利用的组件,可能没有提供 足够的 投资回报率 (ROI)来证明持续的维护成本是合理的。 例如登录次数、或在 Tekton 的案例中,每天、每周、每月计划的作业数等使用指标是重要的衡量标准。 如果平台或平台的某个组件未能随着时间的推移看到利用率的增长,特别是与整体平台的比较,则是时候评估 该组件了。
组件是否提供投资回报,是否减少认知负荷,是否达到 SLO 目标,开发人员是否满意? 如果平台或组件在这些方面出现问题,那么它在技术债务方面的负担比回报更重。
数据保留是技术债务
虽然 可观察性数据对未来平台的决策非常有用,但保留的数据量只需保持所需的最低限度。 随着存储数据量的增加,您可能会发现保留数据的回报递减,而维护成本却很大。 系统所需的数据存储越多,数据存储设计就越关键,因为存储数据量的增加可能开始对系统性能产生负面影响。 此外,由于数据是最关键的资产,在安全事件发生时最容易受到威胁,因此仅保留必要的数据有助于管理任何数据泄漏事件的风险表面和相关工作。 虽然我们不能保证您需要保留多少数据来保持应用程序性能指标,但谨慎地决定保留数据并遵循最佳实践,我们在 第七章 中所阐述的内容应有助于确保保留的数据是可管理的。 需要记住的是,平台、用户和技术每年都会有很大变化,因此保留时间较长的数据在平台随行业现代化的同时会变得越来越不适用。 最好确保任何保留的数据都有充分的理由存在;否则,它只是债务表上的另一个标记。
现在我们更了解如何利用数据来识别和管理技术债务,让我们更实际地应用它到 我们的平台。
维护和重构技术债务
创建 IDP 的每一个方面到目前为止都代表着技术债务。 理想主义者 会告诉你,如果你做出了明智的决策,就不会有技术债务,但坦率地说,这并不准确。 曾经繁荣的开源项目可能会毫无预警地关闭,或者意外的 CVE 可能会出现。 无论如何,尽管做出了最大努力,总会存在债务。 对于组织健康而言,关键在于能够应对这类事件的后果,并保持合理的创新步伐。
拥有你的技术债务
拥有你的 架构意味着要承担它所产生的技术债务。 虽然这听起来像是一个抽象的概念,但当我们开始评估架构的各个组件时,很容易意识到其影响的广度。 记住来自 第二章的重要提示?
重要提示
你为平台选择的环境会自动规定平台的一些部分,无论你是否喜欢! 增加云和基础设施提供商的数量会成倍增加平台设计的挑战。
支持 IDP 所需的基础设施 可能是技术账本上最庞大的项目之一。 拥有意味着债务及与之相关的风险表面面积是明确定义和理解的。 有时候,这种风险可能意味着安全或合规相关的风险,但通常在技术债务的背景下,我们指的是影响团队维护和交付产品能力的风险,而不是使团队陷入过度劳累的风险。 平台架构师负责体现这种所有权,但他们不能在这里成为超级英雄。 技术债务的所有权,包括应对意外情况,是一项 团队共同努力的任务。
技术债务与团队仪式
让整个团队共同承担技术债务的最直接方法是将其融入团队参与的仪式中。 这一点的最终形式应该由团队设计。 在敏捷工作环境中,这可以包括在每个迭代或项目的规划阶段专门评审技术债务,也可以表现为每个迭代中专门用于减少技术债务的时间,如减少重复性劳动或处理已识别的改进机会 。
如果你的平台团队采用看板(Kanban)方式工作,确保将待办事项按优先级堆叠,以便技术债务项目能在其他工作中被拾取,这也是一个 不错的策略。
重要提示
这些项目可能会因其他事项的优先级更高而被不断地降级,这是一个非常现实且严重的风险。 然而,这会产生滚雪球效应:解决技术债务所需的努力程度会随着工作 推迟的时间越长而变得更大。
制定不可变的方式以控制技术债务非常重要。 许多组织采用不同的策略来解决意外问题。 重要的是掌握组织的社会技术方面,确保平衡不被打破。 不偏离平衡。
平台可组合性
平台的可组合性使其非常适合进行重新设计。 IDP 的组件应该是相对独立的,几乎没有(如果有的话)相互依赖。 因此,团队应该有权决定利用平台的这一特性,并在适当的情况下弃用并替换组件。 适当时进行替换。
团队需要谨慎做出这一决策,因为替换现有组件比仅仅添加一个新组件要复杂一些。 除了回答评估新技术债务的问题外,还需要回答另一组关于退役 旧技术债务的问题。
以下是一些需要考虑的弃用标准:
-
我们是否会永久失去我们需要或 关心的功能?
-
这将如何影响 用户?
-
过渡计划是什么? 过渡计划是什么?
-
时间线是什么? 有成本差异吗?
-
我们是要替换它还是仅仅 移除它?
-
原组件和 替代组件的重叠时间是多少?
-
如果有,是什么? 如果是,具体是什么?
-
新的维护要求是什么?
依赖关系
管理依赖关系 是管理技术债务的关键领域之一。 虽然接纳一些团队无法控制的解决方案可能在最初对某些人来说是反模式,但现实是,管理依赖关系的状态通常比尝试在合理的时间内编写一个完全功能的定制 IDP 更容易。 的时间。
如果我们查看我们在第二章的依赖关系矩阵, 我们可以将其应用于平台维护及其债务的讨论:
图 9.2: 依赖关系矩阵
你的平台可能有几种不同类型的依赖关系。 以下是一些 明显的例子:
-
开源 组件
-
网络和 网络设备
-
数据库
-
付费组件
-
内部或 定制服务
在一个理想的世界里,IDP(身份提供者)在任何部署环境中都会是完美统一的。 然而,在多云或多架构的部署环境中,基础设施的依赖关系很可能是 相似但又有所不同的。 因此,你可能需要构建并维护几个不同版本的矩阵,以便准确反映环境。 无论你决定做什么,都需要确保这些文档保持最新,以避免未来出现错误或安全相关的问题。
安全性
通常 被称为一个动态目标,安全最佳实践会随着行业的发展而演变和变化。 当这些变化发生时,你的软件就会落后,需要赶上时,这将成为你技术债务账单上的一项。 例如,如果我们观察主要云服务提供商的工作负载身份,我们可以看到一个行业转型的实时例子,以及一波新的技术债务 的出现。
过去,应用程序使用密钥是常态,而这些密钥的风险则被接受。 虽然缓解这些风险属于技术债务的一部分,但当时并没有更好的替代方案。 然而,工作负载身份的发明消除了应用程序与云服务提供商交互时需要使用密钥的需求。 取而代之的是,它们拥有基于角色的身份,可以使用短期令牌,这类似于人类用户 所使用的。
现在,随着这一技术被所有三大云服务提供商采纳,整个行业正在持续努力现代化应用程序,并利用工作负载身份替代密钥。 对于某些应用程序,通过重构就可以实现这一目标,但对于其他应用程序,这可能意味着需要添加新的组件或进行全面的重写 。
并非所有技术债务的严重性相同
之前提到的安全性子标题可能是最明显的例子,但并非所有技术债务的严重性都相同。 任何妥协系统完整性的因素,如安全性和合规性,都会被赋予较高的优先级,尽管其他可能伴随高时间成本的项目也可能会排在 清单的顶部。
例如,虽然一个新应用程序的发布可能是手动完成的,第一次或第二次发布时可能如此,但很快就会自动化。 从那里开始,自动化接管了,它启动的时间更短,维护也更容易。 这是减少管理软件所需工作量的一个快速且显而易见的方式,但自动化仍然需要维护,这也被视为技术债务。 然而,由于它不太可能频繁更改,因此与如果将发布过程保持为手动过程相比,这项债务的重量要轻得多。
这就是为什么定期回顾并了解团队在非创新时间都花在哪里,或者平台中已识别的差距,变得如此重要。 平台的提供内容。
现在我们已经讨论了如何应对技术债务,让我们进一步扩展,了解何时该对这些债务进行重构 或重写。
重写与重构数据 – 一份实用指南
当技术债务 不断积累时,团队将花费越来越多的时间来解决这些债务或由此带来的后果,而减少了创新的时间。 这在功能上看起来像是更高程度的操作性繁琐、更长的新人加入和培训周期,以及团队的疲惫。 保持对技术债务的管理对于确保开发者的幸福感和创新的持续至关重要,它们应该是平台团队的核心关注。 平台团队。
有时,这种雪球效应可能表现为事物不再按预期工作,或者所需时间超过了预期。 另外,也可能是曾经被认为是可以接受的工作量,运行平台的工作量,阻碍了平台和团队在用户基础增长时的扩展。 无论是哪种情况,当这些技术债务项目超出了正常预期时,团队需要考虑如何回到正常状态,或者找到一个新的 良性状态。
对此的选项可能是重构一些平台。 重构可能允许进行一个相对快速的变化,以获得长期的收益。 例如,当 Golang 改变了其依赖关系的版本处理方式时,许多团队不得不进入并调整他们的导入,以匹配新的、更好的方法。 这里有一些其他重构的理由 :
-
提高效率
-
提高安全性
-
添加或更改 接口
然而,当 无法利用当前的代码库或组件列表达到期望的状态时,可能会考虑进行重写。 我们已经确定,在其基础层面,系统应该是可组合的,意味着很少—如果有的话—是作为辅助功能的无服务器函数或脚本。 重写服务的一个原因是,如果继续使用该服务会导致需要增加那种 类型的辅助工作负载。 Envoy Proxy 就是这种辅助工作负载的一个例子。 它的文档将其描述为“一款高性能的 C++分布式代理,专为单一服务和应用程序设计,也是为大型微服务‘服务网格’架构设计的通信总线和‘通用数据平面’,它与每个应用程序并行运行,并通过以平台无关的方式提供常见功能来抽象网络。”(来源: https://www.envoyproxy.io/)。 然而,作为 这个例子,尽管 Envoy Proxy 功能强大,如果实施它的唯一原因是为了支持一个与其他服务使用不同语言编写的单一服务,那么与其增加 Envoy Proxy 层,不如重写那个异类服务可能更有意义。
虽然增加 Envoy Proxy 是一个优雅且现成的解决方案,但它是一个相对复杂的层,添加到 Kubernetes 平台中,而仅为一个服务添加这个层需要相较于重写该服务的情况进行合理化。
判断是否需要重写
假设 你在一个平台团队中,正在审视一个组件并试图决定它的未来。 是什么因素让你决定是时候告别这个当前状态的组件了? 其中一个原因可能是许可证的变化。 如果你使用的工具曾经是开源的,但现在更改了许可证类型,那么可能是时候替换它了。 毕竟,开源版本可能不会收到任何安全相关的更新,而你的团队可能没有足够的技能来维持这个 工具的现状。
重写组件的决策 树应该与选择构建还是购买的决策树几乎相同。 当你觉得必须重写时,构建还是购买的对话需要再次进行。 如果存在开源解决方案或可购买的廉价解决方案,你的重写可能只需要实施一个新的集成。 但如果情况并非如此,那么可能需要创建一个定制的服务。
然而,如果你希望扩展功能或实现某些性能提升,这种情况非常不可能,并且需要完全重写。 除非组件非常旧或自编写以来一直没有得到维护,否则你很可能最好重构你的 代码库。
以一个例子来检查外部因素对重构的影响
有时, 重构的需求并不是来自内部,而是由于生态系统中工具或技术的变化。 不幸的是,这些依赖项的维护者做出的决策直接影响你的团队的技术债务,并可能导致需要重构该工具的实现。
重构的一个好例子是在从使用 Helm (https://helm.sh/)版本 2
过渡到 Helm 版本 3
进行 Kubernetes 包管理时。 Helm 是一个 Kubernetes 部署的包管理工具。 它是一个开源工具,使用一种名为 charts 的打包格式来协调在集群上安装和升级应用程序的过程。 Chart 不是一个文件,而是定义运行应用程序所需 Kubernetes 资源的文件集合。 例如,Argo CD 可以通过 Helm charts 安装到 Kubernetes 集群中。
Helm 2 是 Helm 的一个受欢迎版本,直到 2019 年 11 月发布 Helm 3,但它在 处理 客户资源定义 (CRDs) 方面存在一些缺点。 虽然今天的 Helm 仍然不支持升级或删除 CRD,但新方法帮助 Helm 更好地为使用 CRD 的用户服务。
那么,Helm 3 解决了什么问题呢? 首先,让我们来看一下 CRD 是什么。 这在 第四章中有更详细的解释,但如果你忘记了,CRD 是扩展 Kubernetes API 的。 从功能上讲,这意味着你的 Kubernetes 集群现在具有了自定义资源的概念。 在 Helm 2 中,CRD 的处理是通过 crd-install hook
方法完成的。 Helm 中的 hook 机制允许一个 chart 在运行之前与其他依赖项进行交互并利用它。 虽然理论上这个方法应该是有效的,但它仍然导致了 Helm 无法识别 CRD 的情况,因此 Helm 无法验证 chart 中的 API 扩展。 如果安装后添加了新的 CRD,升级将失败,因为 crd-install hook
方法不会被执行。 这意味着如果 CRD 发生变化,之前版本的 chart 必须被完全删除并替换。 这对于 Helm 用户来说是非常具有破坏性的,许多团队不得不在其 CI/CD 管道中实现临时解决方案。
Helm 3 通过弃用以前的 crd-install hook
方法,并移除了支持该功能的底层功能,改变了用户体验。 现在,需要一个 CRD 目录。 该目录路径嵌套在 chart 中,使用这个名为 crds/
的目录,允许 Helm 在将 CRD 添加到集群后暂停,之后再继续执行 chart。 Helm 现在允许进行 CRD 安装,而以前是无法做到的。 本质上,Helm 现在在进行 chart 验证时了解新的 API 功能,确保用户能够享受到更流畅的操作体验。
虽然底层的 Helm 变化相当重大,但对于 Helm 用户来说,这意味着需要重构现有的 chart 并升级正在使用的 Helm 版本。 然而,这也使得许多用户能够去除他们实现的任何临时解决方法,转而利用 Helm 内建的升级功能。 根据所使用 chart 的大小和复杂性,这些重构可能需要相当大的努力,但远不及迁移到另一种处理软件安装和 升级方式的工作量。
要了解重构变化的大致范围,我们来看一下今天使用 Helm 3 的 Argo CD Helm Charts (https://github.com/argoproj/argo-helm/tree/main/charts)。在该仓库中,有一系列图表,每个图表作为整体 Argo CD 项目的一部分被安装。 在 Helm 中,目录结构非常重要;没有正确的结构,图表 无法正常工作。
如果我们查看其中一个图表,我们会看到新的 CRD 目录 已经存在:
图 9.3:Argo CD Helm Chart 目录结构
如果我们查看其中一个图表的 values.yaml
文件,我们会发现 CRD 的使用 非常简单:
图 9.4:Helm Chart values.yaml 文件中的 CRD 配置
从开发的角度来看,利用 CRD 的主要工作在于 CRD 定义本身。 在这里,定义了 CRD 注释以及任何其他所需的逻辑,如资源策略, 也都在此声明:
图 9.5:来自 Argo CD 项目的 CRD
在看到 CRD 如何与 Helm 图表一起使用后,产生的一个自然问题是:“之前是怎样的?” 答案是“这取决于。” 在 crd-intall
钩子被添加到 Helm 之前,解决这个问题的常见方法是将 CRD 分解成单独的图表,然后使用 bash 脚本确保图表按特定顺序安装。 例如 Istio
项目就是采用这种方式的例子。 这种方法有效,但增加了维护负担,因为确保正确安装的脚本成了 Helm Chart 维护者需要持续更新的另一项任务 。
在 Helm 添加安装钩子后,废弃此类脚本的功能变得可能。 在保持相同 CRD 文件的同时,用户可以添加 crd-install
钩子注释。 然后,Helm 的底层组件 tiller 会确保在其余图表 被执行之前,触发 CRD 的创建。
Helm 2 文档进一步解释了这一点(https://v2.helm.sh/docs/charts_hooks/#hooks)。 需要了解的关键点是,包含 crd
钩子的 YAML 文件最初看起来 是这样的:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
version: v1
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
重写后,它会 看起来 像这样:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
annotations:
"helm.sh/hook": crd-install
spec:
group: stable.example.com
version: v1
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
从本身来看,这是一个非常小的变化。 正在使用的 Helm 版本也需要更新,但总体来说,这是一个非常小的工作量,允许许多自定义的 bash 脚本 从维护 Helm Charts 的人员的工作中解脱出来 ,这些人员为他们的应用程序维护 Helm Charts。
有时,重构的过程非常简单。 它可能只是识别一些低效的区域,或者是发现代码可能遇到竞争条件的地方,并对代码进行修改,以改进这些情况。 其他时候,重构可能更加全面,例如替换一个库,或者将语言版本更新为带有一些破坏性更改的版本。 无论重构的原因是什么,它通常比 重写投入的努力要少,但回报更高。
审视一次著名的重写
如果仍然 不清楚何时重写可能是合适的选择,我们可以通过开源软件项目来看一些著名的重写案例。 在 Linux 内核中,以及容器技术本身,因此也包括 Kubernetes,我们可以在 cgroups 中找到一个完美的例子。 Cgroups,也被称为控制组,是存在于 Linux 内核中的一种技术,用于管理计算进程。 Cgroups 管理资源分配并通常控制进程。 然而,最近为了响应社区对性能提升的呼声,进行了一次重写,cgroupsv2 应运而生。
这个例子特别有趣,因为它产生了连锁反应。 虽然 cgroupsv2 通常被认为优于 v1,并且大多数应用会看到性能提升,但遗留应用可能会受到影响。 在 Kubernetes 中,必须指定一个 cgroups 版本,而遗留应用的问题就显而易见。 特别是,旧版的 Java 和 Node.js 应用将与 cgroupsv2 的内存查询功能不兼容,并且可能会 遭遇 内存溢出 (OOM) 错误,导致工作负载崩溃。 对于使用较旧应用的公司,建议升级技术栈。 这可能意味着需要重写或重构,具体取决于遗留代码的滞后程度以及需要做什么才能将其更新为与 新的 cgroupsv2 兼容的版本。
那些对 Kubernetes 有更多了解的人可能会注意到,截至当前版本 1.30 的 K8s,Kubernetes 仍然包含使用 cgroups v1 的功能。 这是事实,任何不希望进行现代化的组织可以利用这一功能。 然而,依赖 cgroupsv1 继续存在而非采用重写版本是不明智的,因为更可能的情况是 v1 会被弃用,而不是同时维护两个版本。 如果不希望进行现代化,那么与其新增技术负担,不如将指定 cgroupsv1 的集群配置添加到团队账簿中。 所有新的集群必须在支持 cgroupsv1 的期间内创建,或者在遗留应用没有现代化之前,使用 cgroupsv1 的规范。 如果创建集群有自动化过程,那么也需要对其进行重构,并将其添加到额外的配置中。 此外,虽然现代化的应用能够在 cgroupsv1 下运行,但它们的性能可能不如在 cgroupsv2 下的表现。 无论是决定重构集群创建,还是对运行在旧版语言上的工作负载进行现代化,这次重写的影响波及到了 整个行业。
重写及随之而来的 cgroupsv2 成为默认设置的一个最终后果是,一些新的原生 Kubernetes 特性仅与更新的版本兼容。 换句话说,坚持使用 cgroupsv1 会限制你的能力,错过一些 Kubernetes 增强功能,特别是在 工作负载管理方面。
重写后的过渡
你已经完成了重写(或可能是替换),现在是时候进行切换了。 不幸的是,这类过渡很少是干净的——如果不是永远——通常需要精确的规划和执行。 过程越混乱,原始组件必须存在的时间越长,团队就需要平衡与两者相关的运营成本。 为了促进更健康的过渡,尽早定义过渡计划,并尽可能测试过渡计划,确保没有遗漏。 此外,编写和测试过渡计划还有另一个好处。 完成的计划和测试成为事件记录和未来可能需要发生的迁移蓝图。 虽然我们总是希望重写只是发生一次,但现实是,任何长期存活的企业都会经历多次重写。 通过记录这个 过程,你有助于减少重写的技术债务。 这将帮助未来的工程师理清系统的历史,并节省他们在 未来生成过渡计划的时间。
架构决策记录 – 为未来的世界而写
一个 架构决策记录 帮助 平台的未来拥有者了解不仅仅是什么做了,而且为什么这么做。 维护准确的架构图和文档对理解任何软件系统至关重要,但这些决策和最终状态背后的推理帮助未来的领导者知道如何在 未来使用该平台。
为什么要记录软件架构?
软件架构应该 出于几个原因进行记录。 首先,它有助于新团队成员和用户了解项目。 一份好的架构文档帮助这些人理解系统如何使用,以及数据是如何流动 的。
其次,它有助于安全性和合规性,因为所有安全性和合规性审计都需要详尽的软件架构文档 和图表。
最后,它为你提供了一个起点,从这里开始成长。 通过记录当前状态,你为规划提供了一个参考文档。 如果未来的架构师没有很好地理解平台的现状,怎么能知道平台应该走向哪里呢? 它在哪里?
良好的技术文档应该是什么样的?
一个 好的架构文档将包含图像和描述的结合。 根据文档的粒度,它应当展示用户如何与系统互动、系统的依赖关系、数据如何流动、数据如何存储、数据在哪里被更改(如果有的话),以及交互的预期结果。 换句话说,文档应当捕捉系统的功能以及它是如何 实现这些功能的。
文档的另一个功能是记录的不仅仅是“做了什么”和“怎么做”,还要记录“为什么”。 比做什么和怎么做可能更为重要的是,每个决策背后的“为什么”帮助指导未来的决策过程。 例如,未来的架构师需要了解有关 所做决策的关键信息:
-
是否存在某种限制因素导致了 次优的解决方案?
-
替代解决方案需要达到什么标准才能 被考虑?
-
当时已知的风险和缓解措施是什么? ?
-
目标和非目标是什么 以及为什么?
-
每个组件对 另一个组件有何影响?
通过确保记录决策的这些方面,你是在构建遗产,而不是 遗留软件。
然而,尽管架构决策记录非常重要,它们并不是你平台上唯一重要的文档。 本章和书中提到的其他类型的文档也应当编写并维护,以便你能跟踪系统所带来的技术债务。 虽然管理文档的组织结构和保持文档更新可能会感觉像是苦差事,确实是一种债务,但通过这样做所节省的时间随着时间的推移会带来回报。 可搜索、结构良好的文档是使系统经得起时间考验的关键。 时间考验。
我们虚构的公司——最后一瞥
ACME Financial One 是一个 专为我们的目的设计的公司——旨在让其看起来像许多正在现代化技术栈的真实公司。 在我们的示例中,他们正在处理遗留系统以及新的或绿色场地系统。 像任何真实的公司一样,这将导致初期技术债务的重复。 为了支持工程师度过这段两套系统并存的过渡期,一个设计良好的平台将提供所需的灵活性,以便在这 两个环境之间实现平稳过渡。
ACME Financial One 需要通过最精简的可行平台来支持这一点,该平台能够扩展并发展,以便在用户所在的地方为他们提供支持。 清晰的平台文档和利用平台的流程将帮助开发人员在过渡过程中顺利过渡并采纳新的 环境。
总结
如果从这一章关于技术债务的内容中能学到一课,那就是在正确的时间提出正确的问题,能够帮助你的团队构建一个旨在持久的平台。 这个平台可能永远不会 完成。总会有需要改进的地方,也总能取得进展。 然而,一个精细的管理平台的流程和相应的文档,将使团队能够在保持 团队健康的同时持续维护平台。
在处理技术债务时,请记住我们在本章开头讨论的示例指标和问题。 识别技术债务并了解相关成本,将帮助你与需要处理这些问题的团队更好地沟通并优先解决这些问题。 虽然解决技术债务本身既费时又昂贵,但它将帮助你为一个能够持久并为 未来变化做好准备的平台奠定基础。
在下一章也是最后一章,我们将讨论变化,以及你的平台如何作为你 IT 组织的稳定基础提供这些变化。 你将了解可持续和轻量架构的概念,以及如何使用户能够采用这些方法。 我们将挑战“黄金路径”这一以用户为导向的术语的视角,并质疑作为平台工程师和架构师的我们,是否也需要一条黄金路径。 最后,我们将以一些关于下一个 趋势话题的思考结束本书。
第十章:为未来打造平台产品
一个项目 和一个产品的区别在于它的生命周期。 项目是有结束的。 如果它们错过了截止日期,组织将重新分配资源、资金和时间,导致系统的退化。 一个 平台作为一个产品必须建立在能够随着需求变化调整、演化和成长的基础上。 平台不仅仅是一个技术解决方案。 它们需要一个团队、一个思维方式,以及一个完整的有机体,才能为 公司带来纯粹的价值。
在最后一章中,我们希望找到一些激励的话语,概括本书的学习和洞见。 我们将讨论变化的延续性,以及你的平台如何为你的 IT 组织提供这种延续性,成为一个稳定的基础。 接着,我们将探讨可持续和轻量架构的原则,以及你如何鼓励你的用户遵循这些方法。 作为平台工程师,解锁这些能力并 增添价值,取决于你。
我们将通过挑战“黄金路径”作为一个以用户为中心的术语,来结束本书的话题,并质疑作为平台工程师和架构师的我们,是否也同样需要一个 黄金路径 。 黄金路径。
在本章的最后一些思考中,我们将讨论下一个趋势话题,然后让你继续或开始着手你的平台。 你可能已经意识到,我们经常写到“你的平台”——作为产品的平台始于你的思维方式。 你对其质量和给用户带来的吸引力负责。 你定义了它的成功,它也不是那种事后可以被丢弃的东西。 这就是为什么它是 你的 平台。
在我们的最后一章中,你将 学习到:
-
持续变化——学习如何老去 与适应
-
考虑可持续和轻量架构 以及支持
-
黄金路径 为变化指引
-
一瞥 未来的景象
持续变化——学习如何老去与适应
持续变化的主要挑战在于 作为平台团队,你需要自己采纳并体现前瞻性思维,同时还要引导和激励你的用户采纳相同的方法。 如果你做不到这一点,在最好的情况下,你的用户将会要求并迫使你的解决方案和团队做出适应。 在最坏的情况下,他们会放弃你的平台,寻求其他解决方案。 所以,最终要依赖你为变化和 成长提供一个坚实但灵活的基础。
变化的必要性
我们之前讨论了技术 是如何快速发展的,以及主要的技术里程碑周期是如何每 10 到 15 年发生一次的。 由于 Kubernetes 刚刚满 10 岁,我们可以预见未来几年将会有一次革命。 然而,更小的变化也使得平台有必要适应新的需求和创新。 平台架构师必须找到一种平衡平台稳定性与市场创新的方法。 这两者之间的紧张关系可能令人不堪重负。 它将质疑你平台的概念、你的 IT 组织的决策,以及 你用户的设计选择。
麻省理工学院斯隆管理学院定义了这种持续变化的基本结构。 他们发现,看似混乱的变化其实只是以下图所示的四个周期的延续。 他们花了 15 年的研究才理解这些变化并非线性发生,而是不断地从头开始。 我们应该从这个周期的哪一点开始呢? 这取决于我们已经身处其中,实际上,每种技术、方法论或框架都有自己的周期,且略微影响着其他周期。 以平台工程作为一个概念为例。 它真正开始的地方很难说;它是从 DevOps 演变过来的吗? 这个术语是随着像 Backstage 这样的工具出现的吗? 还是有人在哲学上思考 IT 世界,认为这是个好主意? 最有可能的是,平台工程的自我推动力是由这些因素共同作用而产生的。 它引发了新工具和架构视角的出现。 DevRel (即 开发者关系) 专业人员 与他人讨论并展示它的工作原理,最终促使我们写了一本关于它的书 。
图 10.1:MIT 持续变革的基础结构
如果你看到这种 在四个领域中平衡的自我推动行为,那么它很可能会被 每个人采纳,你可以迈出第一步,成为 首个推动者。
培养变革文化
我们从 变革的命令中能学到什么? 我们无法避免变革;如果你这样做,你就在回避自己的未来和平台的未来。 作为一个平台团队,你处于一个理想的位置来培养变革的文化。 是的,又一个让你忙碌的议题,虽然它和工程无关,但正是这种职责让平台工程师的角色如此特别。 你处于一个理想的位置,因为平台和你的工作是连接 IT 环境、开发团队和 业务之间的纽带。
承担这种责任并成为思想领袖并不是一件容易的事。 平台工程师的角色要求他们设定未来的愿景,并引领团队走过变革,这需要制定策略来建设一个接受变革的团队文化,包括沟通、激励和领导支持。 正确的文化方法取决于你的团队和组织中的个人,这是一本书中几乎无法覆盖的内容。 我们可以推荐的做法是创造一种邀请用户进行实验的环境,例如沙箱环境或试点项目。 但你需要做得更多;你也需要为自己的团队创造这样的环境,并且需要鼓励你的平台工程师测试新技术。 你的结果必须被记录下来,并供整个团队查看,这样可以避免重复工作,并且你还可以比较所测试技术的成熟度 随时间的变化。
因此,你必须实施一种惯例,以同样的方式庆祝成功和失败。 在实践科学中,失败是 研究的重要组成部分。 像 SpaceX 这样的公司,一次次让火箭爆炸,但他们仍然视其为成功,因为每一次,他们离目标更近一步。 也许不要让你的数据中心爆炸,但至少要创造一个安全的环境,在这里你可以为失败投资资金。 组织每天都会浪费资金在看似毫无意义的活动上。 如果你能做一些事情,使你的团队和技术准备水平得到提升,那就远比 这样做更好。
迭代演化和不断发展的策略
有了一个安全的环境来测试新技术,你将能够识别哪些技术可以引入到平台中 ,甚至可能替代掉旧的技术。 寻找 稳定性和创新之间的平衡会因情况而异。 我们见过一些平台,它们总是一次性推出一个全新的环境,并且在自动迁移工作负载到新平台方面变得非常专业。 然而,许多人选择逐步引入新技术和功能的方式。 通过使用功能开关并以金丝雀发布的方式为用户提供访问权限,你可以慢慢但安全地引入更改。 通过良好的可观察性和反馈循环,你还将有机会提高新功能的质量,并让用户参与到 演化过程中。
演化不需要依赖重大里程碑;持续进行以下工作同样重要: 的操作:
-
重构代码并提高其质量 。
-
考虑功能请求 的因素
-
反思技术债务,看看它们是否已经 变得可以解决
-
频繁更新第三方 集成
在这种情况下,思考如何淘汰某些实现也是非常重要的。 有些工具可能经历了大量的炒作,但在实现后不久就死掉了,因为缺乏可持续的贡献和开发。 其他工具则可能随着时间的推移被新版本所替代。 因此,设置一个废弃策略也是你平台演化的一部分。 它需要清晰地传达对用户的影响和时间表。 随着废弃,你还将能够识别平台组件的模块化程度以及它们是否松耦合,以及用户是否建立了某种 依赖关系。
采用新技术是一段旅程,也是一个心态。 作为平台工程师,你处在正确的位置来实现这一点。 建立一个持续学习的环境、频繁的反馈循环和事后复盘,以便快速学习并加速 成功的演变。
在接下来的部分,我们将简要介绍可持续发展中的一个最新趋势。 我们将重点关注环境 可持续性,这在近年来已成为一个强有力的驱动力,而 生成式人工智能 (GenAI)的迅猛增长使得这一话题更加引起了关注。 因此,作为平台工程师,我们可以做以下几件事,为可持续的未来贡献自己的一份力量。
考虑可持续和轻量级的架构与方法
信息技术已经成为全球顶级 二氧化碳 (CO2)的排放源之一,与建筑和航空业并列。 关于它的最佳研究,遗憾的是已经有几年历史,但我们可以总结出 2018 年的研究结果:
-
全球信息技术消耗了约 2% 的电力
-
这导致了全球 CO2e 的 1.5%–2.4%(e 代表 等效 ——并非所有气体都来自 CO2) 的排放
-
这个范围与印度尼西亚(1.48%)、加拿大(1.89%)或 德国(2.17%) 的 CO2e 份额相当
-
2018 年的预测认为这些数字到 2025 年将翻倍;一些不太可靠的来源甚至预测 CO2e 将在 2025 年增加 12% 。
我们不必关闭一切,重新像穴居人一样生活,但至少我们应该意识到我们的 无限 计算资源对环境的影响。 任何形式的计算资源,在生产、使用(通过其所需的电力)以及退役和销毁时,都会导致 CO2e 的排放。 。
因此,我们能做的最好的事情是 如下:
-
减少:减少就是要在我们消费的每一件事上都保持可持续性。 在我们的日常生活中,我们不必拿塑料袋,但那数字资源该怎么办呢? 其实很简单——我们真的需要一个超高可用的分布式 web 服务器,跨越五大洲,并每小时进行跨地域备份吗?
-
重用:在 IT 领域,重用有两个方面。 如果一台服务器退役,可能不应将其销毁,而应将其卖给专门运营遗留服务器的供应商。 其次,我们应尽可能长时间地继续使用该服务器。 这也是一种重用的形式。 作为一个调度器,Kubernetes 使这一过程变得极其简单,并且当出现 故障时,能够提供经过验证的负载均衡。
-
修复:我们大多数人没有能力修复服务器。 通常,个别组件如内存模块或网络 设备会发生故障。
-
适配大小:调整请求的计算能力,无论是在本地还是云端,以适应你的工作负载。 服务器上 80%的空闲空间的时代已经结束。 那些计算资源利用率不到 90%的人,实际上是在 伤害地球。
-
拒绝:不要与那些未致力于实现可持续性目标的供应商合作。 你有责任将这一标准纳入你的供应商 选择流程中。
-
重新平台化:注意,如果一个供应商或平台没有承诺或不支持 可持续性目标。
现在,你可能会说,“可持续性”的商业案例在经济上并不太有吸引力。 你甚至可能会有这种感觉,认为我们之前讨论的行动已经足够了。 减少成本通常伴随着优化你的 CO2 足迹。 因此,优化你的足迹也会对你的成本产生积极影响。 此外,越来越多的法规正在制定,以应对全球变暖增长放缓的问题。 遵守这些法规总比 支付罚款要好。
启用轻量化架构
启用轻量级架构应该是平台的主要目标。 它不仅对可持续性有益,还提高了系统的健壮性和容错能力。 当应用程序的架构没有硬性依赖,使用较少的组件,部署灵活(包括可扩展性),并采用恰当的技术时,它便是轻量级的。 一个很好的例子是 Apache Kafka,它在流处理的背景下成为了一种事实上的标准。 然而,我看到更多的系统仅仅将其用作传输日志和事件并汇总数据流。 现在有数十种更轻量、高效的工具,提供相同的功能,但没有规模、资源需求和复杂性的开销。 这种模式非常普遍。 我们更倾向于选择一种能够满足我们需求并能提供更多功能的解决方案,尽管我们并不需要它,而其他人可能需要,所以它必须是 正确的选择。
无论我们选择什么,为了支持轻量级架构作为平台团队,我们必须提供技术选项,能够为需求提供两到三个更好的答案,而不是错误的答案。
为用户适应提供支持
我们现在如何支持用户 并允许他们在未来优化他们的系统? 该如何做?
我们让他们了解自己的影响,展示平台在可扩展性、平衡性和 无服务器运行方面的潜力,并提供替代的轻量级技术。 这可以成为我们内部开发平台 (IDP) 和可观测性的一部分。
为了展示碳足迹的透明度,开源工具如 Kepler、Scaphandre 以及商业解决方案提供了对系统组件造成的 CO2e 排放的洞察。 下面是来自 Kepler 的截图,展示了一个简单的 Grafana 仪表盘,显示了每个组件的能耗。
图 10.2:一个显示各个组件能耗的 Kepler 仪表盘(该图像仅作为视觉参考;文本信息并非必须。)
目前,这些工具仍在开发中,经常出现错误且难以处理,但它们是一个起点。 它们是否成功将取决于教育用户的能力。 我们必须改变用户的观点,将其从在一个环境中构建软件转变为让平台为他们完成这项任务。 软件需要在平台上构建,以便本地使用所有平台的能力。 这听起来可能像是我们想要构建和引入依赖关系,从而打破我们关于模块化的概念。 但这正是产品的难点;你 必须找到正确的方法来发展和启用新方法,同时保持向后兼容性。 如果你的新特性或旧特性的替代方案带来了价值,用户将会适应 它。
重要提示
适应新方法的最佳方式是通过开放性,这种开放性促进了透明度 和教育。
在接下来的部分,我们将通过黄金路径总结我们的平台之旅。 我们将再次从不同的角度审视平台工程中的常见话题,将黄金路径视为平台的一个特性。 最终,作为平台工程师,我们创建黄金路径以创造更多的黄金路径。
变更的黄金路径
术语 黄金路径 在我们的书中已经被多次使用。 它的首次使用之一是在 Spotify 的博客文章中,发表于 2020 年 [1]。黄金路径是为了引导工程师通过支持的方式完成任务(例如,使用特定的服务或创建具有特定目的的新代码)。 平台工程的核心是提供易于使用的 黄金路径 ,以减少各个团队在创建、编译、测试、部署和 操作软件时的工作量。
再次看看我们的虚构公司 Financial One ACME! 在本书的早些时候,我们已经识别出几个用例,可以作为 自助黄金路径 提供给我们作为平台工程团队支持的工程师。 提供良好支持的关键方面是适当的反馈渠道,这也是我们接下来将讨论的内容。
提供反馈渠道
支持我们的平台意味着 确保平台上提供的所有黄金路径始终按预期工作。 如果某些功能未按预期工作,平台工程团队需要确保尽快修复所有故障,减少对 最终用户 的负面影响。
理想情况下,我们能够自动检测到事物未按预期工作,而无需等到用户抱怨。 在 第三章中,在 一个可用、弹性且安全的平台 部分,我们讨论了观察我们的平台及其所有组件的重要性,以便自动检测到事物未按预期工作。 与任何其他软件产品一样,我们可以并且必须利用可观察性来确保我们的平台及其所有组件达到其 服务级别目标 (SLOs)。 所以,如果我们的可观察性 告诉我们某些东西不可用、变慢,或突然出现错误,那么我们必须采取行动将其恢复到 正常工作状态。
通过可观察性获得自动化反馈的另一种方式是观察有多少用户在积极使用平台,以及他们遵循哪些黄金路径。 如果需要复习此主题,请回顾 成功 KPI 和优化 在 第三章中。 如果我们看到,突然之间,用户改变了使用行为,那么这表明有些事情发生了变化,我们需要处理。 可能是一些黄金路径出现了故障(例如,开发人员无法访问工具,如 Backstage、Git 或 ArgoCD),也可能是一些路径已经过时(例如,一个 Backstage 模板不再工作,因为它使用了过时的配置)。 也可能是组织中的需求、规定或首选技术栈发生了变化,而我们的平台当前没有提供反映这些变化的新用例的黄金路径。
为了保持黄金路径的最新状态,我们需要提供一个 反馈渠道 ,使我们的用户可以积极地向我们反馈他们的新需求。 这就像在任何软件产品中请求新功能一样。 了解这些新功能需求的最佳方式是与我们的最终用户直接沟通,这些最终用户就是我们的内部工程师。 这可以通过内部聊天来实现(例如,可以设立一个专门的 Slack 频道,如 #goldenpath-suggestions
),也可以提供一个电子邮件分发列表(例如, goldenpaths@yourcompany.com
),或者可以在平台本身内构建一个 自助反馈黄金路径 ,供用户提供他们希望平台做的下一步详细信息。
反馈是最终 驱动我们不断改进平台的新功能或优化现有黄金路径的关键。 对于最终用户来说,这意味着他们不断从平台中获得新功能,使他们的日常生活更加轻松,即使他们的需求和交付软件的过程发生变化。软件 可能会发生变化。
黄金路径是我们平台的功能
正如本章开头所提到的,平台是一个产品,因此它永远不会完成,这与有最终截止日期的 项目不同。 平台是不断发展的,因为在新的黄金路径出现或现有黄金路径需要更新以继续工作时,将有新的使用场景需要添加进来,这些变化是由于 需求的变化。
因为我们像构建产品一样构建平台,所以我们也可以将黄金路径视为我们产品的功能。 这些功能会有不同的形式,取决于我们决定如何构建平台以及我们为最终用户提供哪种类型的用户界面。 在 第三章 的 平台参考架构 部分,我们讨论了平台的不同形式的用户界面—— 命令行界面 (CLI),REST API,一个 Git 仓库中的特殊配置文件,或一个列出所有可用用例的漂亮开发者门户 UI。 提供自助服务访问可用黄金路径(即功能)列表的用户界面示例如下所示:
图 10.3:一个包含所有可用黄金路径(即功能)的自助门户(该图像仅作为视觉参考;文本信息并非关键。)
上面的截图来自一个金融机构(类似于我们的 Financial One ACME),该机构提供了一个涵盖各种使用场景的自助黄金路径列表,这些场景要么是平台工程团队识别的,要么是最终用户多年来请求的。 大多数 在截图中可见的黄金路径集中在请求云资源(例如,EC2 或 Azure 虚拟机)、请求 云服务(例如,AWS 关系数据库服务 (RDS)或 Azure 负载均衡器),或根据模板创建新内容(例如,创建 Chef 策略或 Azure DevOps 管道)。
当点击其中一个黄金路径时,通常会提示用户输入一些信息,这些输入将触发一些自动化操作来完成请求。 一些不能或尚未自动化的黄金路径可能仅提供一份操作指南,解释如何完成这个特定的 请求。
为了回顾这些黄金路径在现实生活中的影响,请回到 第八章,该章专注于成本优化。 在 仅请求你所需的方式 部分中,我们解释了在 请求 Azure 虚拟机 黄金路径背后发生了什么。
前面的示例已经很好地概述了平台如何演变。 但我们并不是一开始就有那么多黄金路径(即功能)。 我们从小开始,然后需要准备好扩展。 但是 我们如何 做到这一点呢?
从小开始,预期增长,并且不要成为瓶颈
在 第二章的关于 最小可行平台 (TVP)的部分中,我们讨论了如何从一小组我们认为会产生影响的使用场景开始。 从那里开始,我们利用反馈进一步了解额外的使用场景,并用它们来扩展 平台。
让我们来看一下 Financial One ACME。 在本书的前几章中,我们讨论了几个自助服务用例,这些用例将改善 Financial One ACME 中开发人员、DevOps、ITOps 或测试人员的工作。 其中包括以下用例,如 自助访问生产日志以进行故障排除, 提供合规环境, 执行性能和韧性测试,以及 新应用程序的上线。一旦这些用例得以实施,并且证明它们能使工程师的工作更加轻松,那么更多的 黄金路径请求 将随之而来。 因此,我们需要预期随着构建成功产品而带来的增长。
随着需要支持的用例集不断增长,问题在于这需要更多的测试和维护工作,从而减少了为平台添加新用例或核心功能的时间。 我们有可能陷入瓶颈,因为我们在努力使平台及其功能与用户的所有需求保持同步。 解决这个问题的一种方法是提供“黄金路径”来构建更多的黄金路径,从而简化平台的扩展——不仅是由专家来完成,而是由每个人!
构建黄金路径来创建黄金路径
在整本书中,我们讨论了提供黄金路径以减少工程师完成工作所需的认知负担。 现在的问题是,当我们设计平台时,是否也能提供黄金路径 以使平台工程团队能够更高效地通过新特性、功能和用例来扩展平台? 我们能否构建一个黄金路径,让平台工程团队能够创建更多的黄金路径? 而且,这个黄金路径是否还可以被我们的最终用户(即我们组织中的工程团队)使用,以实现他们的新需求,而无需平台工程团队的帮助? 这几乎让人联想到 2010 年的科幻电影 《盗梦空间》,由 莱昂纳多·迪卡普里奥主演。
好消息是,我们不需要好莱坞来实现这样的事情。 在 第五章中,在 提供模板作为 Golden Paths 以简化起步 部分,我们讨论了 Backstage 中的模板功能。 Backstage 中的模板是一种定义 Golden Paths 的方式。 这些模板通常由平台工程团队创建和维护,因为它们的创建 需要一定的专业知识。 以下片段展示了这样的 模板定义的摘录:
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: self-service-app-onboarding
title: Onboarding of a new App to Financial One ACME
description: Golden Path to onboard a new App
spec:
owner: financialoneacme/platform-engineering
type: service
# Input parameters needed when using this golden path
parameters:
- title: What programming language
properties:
language:
title: Programming Language
type: string
description: Which language do you want to use? owner:
title: Owner
type: string
description: Which team owns this new app? - title: Choose a target Git location
... # Steps when Golden Path gets executed
steps:
- id: fetch-base
name: Fetch Base
action: fetch:template
input:
url: ./template
values:
language: ${{ parameters.language }}
owner: ${{ parameters.owner }}
- id: publish
name: Publish
action: publish:github
- id: register
name: Register
action: catalog:register
# Output to show back to the end user
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
要查看完整的示例,可以查看 fetch-base
步骤,它会获取一组在 git 仓库中指定的文件,这些文件将在有人选择 Java 时使用。 然后发布步骤会根据所选语言发布新的 git 仓库。 后台还会进行一些额外的处理,但本质上,这就是创建 Golden Path 模板的 全过程。
通过这个例子应该能看出,创建 Golden Paths 其实并不难。 当然,这里有一些细节是我们无法在这本书的简短章节中涵盖的,但它展示了像 Backstage 这样的工具已经存在,使得创建或更新 Golden Path 模板变得非常容易。 这也使得平台工程团队能够让其他团队帮助创建和更新他们自己的 Golden Paths,从而减少了核心平台工程团队成为 瓶颈的机会。
虽然我们以 Backstage 为例,但市面上还有其他工具也提供类似的灵活性。 你应该熟悉开源领域中可用的工具,并且也要关注商业化产品。 另一个值得关注的开源项目是 Kratix – 开源平台工程框架 [3]。Kratix 提供了一个框架,用于 构建可组合的 内部开发平台 (IDP)。 它采用了一种有趣的方式,使用所谓的 Promises,这实际上类似于 Golden Paths。Kratix 鼓励所有用户创建并贡献自己的 Promises 到平台,从而使得平台中的每个用户都能利用任何人在 组织中创建的 Golden Paths。
现在我们已经讨论了黄金路径不仅仅是平台的起点,而且还会不断演变,以及如何提供黄金路径来构建新的黄金路径,从而确保平台的未来增长,接下来是时候考虑一些我们在打造适合未来的平台产品时需要关注的未来趋势了!
对未来的展望
接下来的几行可能是本书中最危险的一段文字。 它们可能完全是胡说八道,或者仅仅是一个 梦,或者也许是我们日常工作未来的样子。 构建平台意味着在我们周围的事物发生变化时,保持平台的更新。 这包括密切关注市场,观察发生了什么,并评估其中某些趋势是否可能持续下去。 最重要的是回答一个问题:这些新趋势是否对你的平台用户和组织有益。
超管虚拟机的替代
第一个需要哲学思考的话题是 超管虚拟机(hypervisors)是否可以被 Kubernetes 替代。 我们之前提到过这个话题,特别是探讨了仍然使用 超管虚拟机的历史和原因。 要使这种方法成为一个值得考虑的方式,如何平息 首席信息安全官 (CISO)的忧虑呢? 需要解决的问题包括以下内容:
-
容器隔离 和加密
-
网络隔离 和加密
-
存储隔离 和加密
简而言之,我们必须将每个部分隔离并加密,同时保持容器编排器的完全灵活性,以提供平台所需的所有功能。 但也许我们在以过于传统、老派的方式思考这个问题。 显然,我们可以将容器加固到一个程度,使其成为一个完全封闭的黑盒。 对于一些应用,我们可以考虑将 WebAssembly 作为一个非常安全的编译目标。 如果这还不够,我们可以将机密计算加入整个过程,或者等待开源社区找到更好的解决方案。 就好像没有人能突破虚拟机的上下文一样 [4]。隔离 网络也不是万能的解决方案。 诸如 扩展伯克利数据包过滤器 (eBPF) 的技术被用来“动态地为高效的网络、可观察性、追踪和安全编程内核。“ eBPF 通过允许在内核空间中执行自定义的沙箱程序,来增强安全的网络通信,而无需修改内核代码。 这一能力使得对网络数据包的处理、过滤和监控进行细粒度控制,从而有助于执行安全策略并实时检测恶意活动。 最后的问题是存储,但在这个领域,我们依靠软件定义的存储。 总体而言,用 Kubernetes 替代虚拟机是可行的。 当然,我们跳过了一些细节,但在云原生领域,每个问题都有解决方案。
平台的 AI
在写作时,没有任何话题像 AI,或者更准确地说,是 GenAI,变得如此无处不在。 每一场会议、每一个媒体来源, 每一本专业杂志都被 GenAI 涵盖。 此外,开源社区并没有停滞不前,开发了 K8sGPT 作为回应。 K8sGPT 是一个扫描 Kubernetes 集群的工具,用简单的英语诊断和处理问题。 这是现在的情况,但我们将在未来看到的内容更加有趣。 正确调整大小、调整规模、配置负载均衡以及其他任何操作任务的实际问题,都是 AI 处理的完美问题。 我们已经有了标准化的架构和通信模式。 因此,找到最合适的节点或 Pod 的正确配置(以其资源消耗为标准)的方法应该是简单的。 令人惊讶的是,目前我们还没有看到任何市场主导的解决方案。 但也许问题在于,使用 AI 太过简单。 相反,我们看到随着人口结构的变化,IT 领域受到许多不同因素的影响,许多公司和市场中存在技能差距和成本压力,这可能会加速 AI 在运营领域的应用。 这可能会推动 AI 领域的加速发展。 在运营方面的应用。
OCI 注册表作为存储和 RegistryOps
我们不断改变我们的操作方式。 自从 DevOps 的出现以来,我们将一切都操作化——机器学习、安全、成本管理等。 其中的另一个新增内容是 RegistryOps。 这里的想法是 将所有部署所需的工件存储在同一个位置,而不是将它们分散在各种其他存储解决方案中。 为此,开放容器倡议定义了多个标准,以供注册表遵循,例如分发规范、运行时规范和镜像规范。 回到 第五章, 容器和工件注册表作为入口点的重要性,我们曾对此进行过深入讨论。 这使得注册表成为了一个事实与失败的单一来源。 这个 OCI 注册表作为存储 (ORAS) 项目 在这些标准的基础上构建。 它允许使用容器镜像,构建工件之间的关系,并附加,例如,将一个 软件物料清单 (SBOM) 附加到一个容器上。 当然,SBOM 存储在同一个注册表中。 GitOps 工具随后将注册表用作期望状态存储,并处理向集群的部署。 由于大多数注册表都符合这种方法,ORAS 容易扩展,提供了非常精细的权限规范,并提高了供应 链安全性。
供应链攻击越来越多,供应链成为了一个相关的攻击面。 通过一个位于 CI 和 CD 之间的注册表,你为这些链条建立了一个期望的断点。 这减慢了扫描工件以查找 漏洞的过程,并确保它们由正确的人签名。 最终,RegistryOps 仍然是 GitOps,但它使用的是注册表而不是 Git。
容器化管道作为代码
一个相对较新的概念或想法是定义一个容器中的 CI/CD 管道,这样你可以在本地运行它,也可以在 GitHub Actions 或 Jenkins 等工具中运行。 通过这样做,你可以协调自动化步骤,并 确保它可以在任何地方运行。 这个想法是由开源工具 Dagger 引入的,Dagger 是由 Docker 的前 CTO 和创始人 Solomon Hykes 发起的。 除此之外,你实际上是编写管道代码,这使得它独立于 CI/CD 系统本身的逻辑。 以下代码仅是 Dagger 文件中的一个函数,执行应用程序的构建。
func (m *HelloDagger) Build(source *dagger.Directory) *dagger.Container {
build := m.BuildEnv(source). WithExec([]string{"npm", "run", "build"}). Directory("./dist")
return dag.Container().From("nginx:1.25-alpine"). WithDirectory("/usr/share/nginx/html", build). WithExposedPort(80)
}
这种方法对于那些工具和流程碎片化的公司来说可能是一个游戏规则改变者,尤其是那些每年都倾向于将自己的技术环境迁移到不同平台的公司。
然而,一些批评者也指出,可能没有必要在本地执行 CI/CD 组件,因为一个主要趋势是将所有内容远程推送,以优化资源利用率。 像 GitHub Actions 这样的平台提供可重用的步骤,只需要填写 值。 让我们看看是否将在 未来的管道中使用 Dagger。
平台——与它们共建更好的未来?
通过平台、产品 思维模式和强大的平台工程团队,我们可以提供一个基础设施和集成层,来协调和赋能软件开发与执行。 我们的挑战是找到我们提供的环境稳定性和安全性与创新驱动力之间的平衡,确保我们的平台不断进化,确保它们衰老但 不退休。
但这取决于我们自己。 正如生活中的许多事情一样,如果我们不相信它并且让它成真,没人会帮我们实现。 平台化方法并不是唯一的选择,但它确实拥有一个快速增长的专家团队、充满动力的贡献者和一个充满活力的社区。 我们可以在每一个重要的活动中看到,最终用户讲述他们引入平台和平台团队的成功故事,以及它如何成为 他们组织的游戏规则改变者。
展望未来,平台站在我们技术未来的最前沿,承诺简化复杂性并促进创新。 通过专注于平台工程,架构师可以设计出不仅强大而且能够适应技术快速变化环境的系统。 整合平台伴随着持续的学习与改进,通过小而深思熟虑的迭代可以带来显著的提升。 我们必须鼓励一种协作与共同成长的文化。 尽管面临诸多挑战,但精心设计的平台带来的潜在回报是不可否认的,正如多次所示,它为构建具有韧性和可扩展性的系统提供了坚实的基础。 通过精心规划和执行,平台工程可以为更高效、更有效的系统解锁新的可能性。 通过对我们的原则和目标的承诺与专注,我们可以创造出未来-proof、且为变化做好准备的理想环境。 迎接变化。
总结
在本章中,我们讨论了变化的必要性,以及作为平台工程师的我们如何积极地拥抱变化。 通过渐进的演化步骤,我们引导平台度过其生命周期,不是通过拆解它,而是通过让它成熟。 我们保持平台的未来-proof,并提供创新的解决方案。 我们还强调了启用可持续方法和架构的重要性,并指出,对于平台来说,为整个组织集中提供优化是容易的。 整个组织。
我们讨论了黄金路径、它的反馈循环,以及作为功能来看待它的不同视角,而不是仅仅一种主观设定。 我们强调了你的平台将经历的增长,并且需要定义黄金路径来帮助它通过新的能力不断成长。 我们通过探讨如何使用现有的黄金路径来创造新的黄金路径来结束讨论。 黄金路径。
最后,我们强调了当前的趋势和话题,这些趋势和话题可能会,也可能不会影响我们云原生宇宙的未来几年。 谁知道下一次大爆炸会在何时发生,它会将我们带向何方呢? 未来。
这本书为您提供了许多不同的视角、工具和方法,用于设计您自己的平台架构。作为一名工程师或决策者,您了解了构建平台的实际复杂性,远远超出了纯技术的范畴,并且了解了为用户和组织带来的价值和好处。作为一名平台架构师,这些步骤和考虑因素将支持您的旅程,帮助您创建一个不断发展的产品,并对实施和使用它的人们产生积极影响。
接下来,如何利用您所学到的知识将由您自己决定。如果您处于定义参考架构和目标架构的阶段,可以回到Miro或draw.io模板[5]。我们还建议加入不同的平台工程社区,例如 CNCF 平台工作组[6],他们经常举办知识分享会,并且其他用户也会提供关于他们方法的见解。
我们祝愿您在旅程中一切顺利,并且在您的平台上取得更多成功。
深入阅读
-
[1] Spotify 关于黄金路径的博客:
engineering.atspotify.com/2020/08/how-we-use-golden-paths-to-solve-fragmentation-in-our-software-ecosystem/
-
[2] 后台模板文档:
backstage.io/docs/features/software-templates/writing-templates
-
[3] Kratix:
www.kratix.io/
-
[4] 与虚拟机相关的安全事件,其中研究人员和可疑人员成功突破了虚拟机的限制:
-
VENOM 漏洞(2015 年):VENOM(虚拟化环境忽视操作操控)漏洞在 QEMU 虚拟机的软盘控制器中被发现。它允许攻击者从来宾虚拟机中逃逸,并在主机上执行任意代码:
en.wikipedia.org/wiki/VENOM
-
CVE-2017-5715(Spectre)和 CVE-2017-5754(Meltdown):这些漏洞利用了 CPU 设计中的缺陷,影响了几乎所有现代处理器。 它们通过利用投机执行,允许攻击者跨虚拟机边界读取敏感数据: https://meltdownattack.com/
-
CVE-2019-11135:被称为 ZombieLoad 的漏洞,存在于 Intel 的 CPU 中,攻击者可以利用该漏洞泄露同一物理 CPU 上运行的其他进程或虚拟机的数据: https://nvd.nist.gov/vuln/detail/CVE-2019-11135
-
Blue Pill(2006):Joanna Rutkowska 演示了 Blue Pill 攻击,展示了如何实时安装基于虚拟化监控程序的 rootkit,实质上创建了一个无法检测到的虚拟机逃逸: https://en.wikipedia.org/wiki/Blue_Pill_(software)
-
Cloudburst(2009):这是一个 VMware 漏洞,允许从客户机内执行代码到宿主机: https://nvd.nist.gov/vuln/detail/CVE-2009-1244
-
以及更多与虚拟机相关的安全事件: https://en.wikipedia.org/wiki/Virtual_machine_escape
-
-
[5] 创建你自己的 架构工作坊:
-
[6] CNCF 平台工作组 : https://tag-app-delivery.cncf.io/wgs/platforms/