Openshift-和-Kubernetes-混合云应用-全-

Openshift 和 Kubernetes 混合云应用(全)

原文:zh.annas-archive.org/md5/5135ff927e52366fe42034a2158d7193

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

今天,软件已经成为每个行业的重要组成部分。从加速业务数字化转型的 Web 和移动应用程序,到支持业务增长的更高效的后端系统,再到作为日常生活中设备一部分的嵌入式技术,容器已经确立了它们作为软件交付和运行的基本方面。

推动这些转型的运营和应用团队看到他们的应用足迹在多个地点、云、数据中心和边缘快速增长。组织越大,足迹越广,挑战也越复杂。随着云成为应用部署的一个组成部分,组织也希望现代化他们现有的足迹,并加速他们的开发团队。

那么是什么推动了这些趋势?开源技术促进了标准化,导致生态系统利益和方法的爆炸式增长,从而需要新的标准化。作为最成功的开源生态系统之一,Kubernetes 在如何构建和运行应用程序方面产生了重大变革。

关于如何平衡速度和灵活性——扩展、整合和简化——同时增强团队交付价值能力,这可能是你心中的一个关键问题。你的组织可能受到应用团队的推动,在云端足迹中有机地采纳新服务。在这种情况下,你很可能关注如何在尽量减少对团队干扰的同时,为绿地项目带来操作关注点的一致性。

你的组织可能有更广泛的关注——积极跨越计算的整个光谱,包括边缘设备、网关和数据中心中的本地应用处理、可视化以及云端的存档。这些真正的“混合”应用在每个位置都有独特的需求,但正是这些位置特定的行为加强了保持操作关注点一致性的需求。

最后,你的组织可能运行与大部分企业应用类似的工作负载,这些应用正在真正成为云无关的。借助现代应用开发框架、工具和应用基础设施如 Kubernetes,基础设施的细节对应用的可移植性和可用性的阻碍程度降低了。对于一个希望成为云无关的组织来说,你可能正在构建一个平台来整合和自动化你业务的生命周期。支持混合云的现代平台必须解决诸多严峻挑战,如安全性、数据整合、成本管理以及提高开发者速度以服务于运行业务的应用程序。

作为 Kubernetes 项目的创始成员之一和 OpenShift 主架构师,过去七年来,我致力于帮助大型企业和小团队找到这种折衷之道,并确保在云、数据中心和边缘之间提供一致的应用环境,以促使客户平台取得成功。

随着 Kubernetes 的成熟,用户成功的复杂性和规模也在增加。增长的曲线已将我们带入广泛采用阶段,现在是审视挑战、模式和技术的正确时机,这些可以帮助控制这种增长。

本书深入探讨了最成功的企业 Kubernetes 团队遇到的问题和解决方案,并为您提供了可信赖的跟随路径。视角的融合至关重要——从 Brad 在社区推动跨开源项目一致性和为用户找到共同点的工作,到 Jake 在 IBM Kubernetes Service 和 IBM Red Hat OpenShift Service 构建和运行云规模 Kubernetes 的专业知识,再到 Michael 作为 Red Hat Advanced Cluster Management 产品架构师,专注于帮助组织整合和保护多集群 Kubernetes。

他们共同代表了大量实际经验,展示了如何有效地在规模上利用 Kubernetes,以帮助组织支持其日益增长的应用程序群,无论是云为先、多云还是连接到边缘。

随着我们继续标准化和简化跨多种环境构建和部署应用的方式,开源和开放生态系统帮助组织朝着共同目标进行协作。Kubernetes 和我们生态系统中的其他工具是实现这一目标的基础构建模块,而本书将帮助您在这条道路上探索权衡和机会。

克莱顿·科尔曼

Red Hat 高级杰出工程师

架构师,容器化应用基础设施(OpenShift 和 Kubernetes)

序言

OpenShift,作为 Kubernetes 的红帽发行版,正在迅速成为容器化应用程序的首选平台。随着企业试图扩展和运营 OpenShift,必须理解许多实践方法,以管理资源、为应用团队暴露这些资源、治理这些资源,并持续向这些环境交付变更。

基于我们在管理和支持成千上万的裸金属和虚拟机集群方面积累的专业知识,我们将重点介绍如何使用特定技术和示例来使您的组织有效地运营基本的 Kubernetes 和 OpenShift。

我们为什么写这本书

尽管有很多关于如何开始使用 Kubernetes 和 OpenShift 的内容,我们的目标是专注于涵盖更高级概念的书籍,例如有效管理集群资源和可应用于确保业务连续性的可用性模型。此外,我们希望深入探讨在生产环境中成功操作 Kubernetes 和 OpenShift 所必需的主题、工具和最佳实践。我们特别关注安全性、高级资源管理、持续交付、多集群管理和高可用性等主题。此外,本书探讨了支持混合云应用程序的最佳实践,这些应用程序集成了来自多个云环境以及传统 IT 环境的最佳功能和功能。在本书中,我们将我们对这些生产级主题的深入知识和经验提供给更广泛的 OpenShift 和 Kubernetes 社区。

本书适合对象

本书适合 DevOps 工程师、Kubernetes 和 OpenShift 平台运维工程师、站点可靠性工程师、网络运维工程师、云原生计算应用开发人员和 IT 架构师。此外,本书对那些创建和管理 Kubernetes 或 OpenShift 集群,或者使用这些平台交付应用程序和服务的人尤为感兴趣。

本书的组织方式

本书的结构旨在帮助运维人员和开发人员深入理解运行 Kubernetes 和 OpenShift 的高级概念。第一章概述了基本的 Kubernetes 和 OpenShift。然后讨论了 Kubernetes 的基本概念并描述了 Kubernetes 的架构。在第二章中,我们介绍了 OpenShift 和 Kubernetes 之间的关系,并描述了如何启动多种 Kubernetes 和 OpenShift 环境。第三章深入探讨了高级资源管理主题,包括专业调度、资源预留、专用节点类型以及容量规划和管理。第四章涵盖了在单个集群内支持高可用性所需的关键基础知识。在第五章中,我们介绍了跨多个企业集群进行生产级持续交付和代码推广的概述。第六章和 7 章聚焦于多个生产集群的使用。在这些章节中,我们详细描述了几个混合云使用案例,并深入讨论了多集群配置、升级和策略支持等高级主题。这些概念在第八章得到了进一步强化,我们提供了一个多集群应用交付的工作示例。最后,在第九章中,我们讨论了 Kubernetes 和 OpenShift 的未来,并列出了关于在生产 Kubernetes 和 OpenShift 环境中运行应用程序的各种有用主题的额外参考资料。

本书中使用的约定

本书使用以下印刷约定:

斜体

指示新术语,URL,电子邮件地址,文件名和文件扩展名。

常量宽度

用于程序清单,以及段落内引用程序元素,如变量或函数名,数据库,数据类型,环境变量,语句和关键字。

常量宽度粗体

显示用户应直接输入的命令或其他文本。

常量宽度斜体

显示应由用户提供值或由上下文确定的值的文本。

提示

此元素表示提示或建议。

注意

此元素表示一般注释。

警告

此元素表示警告或注意事项。

使用代码示例

补充材料(代码示例,练习等)可在https://github.com/hybrid-cloud-apps-openshift-k8s-book下载。

如果您有技术问题或使用代码示例时遇到问题,请发送电子邮件至bookquestions@oreilly.com

本书旨在帮助您完成工作任务。一般来说,如果本书提供了示例代码,您可以在自己的程序和文档中使用它。除非您复制了代码的大部分内容,否则无需联系我们以获得许可。例如,编写一个使用本书中几个代码片段的程序不需要许可。销售或分发来自 O’Reilly 书籍的示例代码则需要许可。引用本书并引用示例代码来回答问题也不需要许可。将本书中大量示例代码合并到您产品的文档中则需要许可。

我们感谢,但通常不要求署名。署名通常包括标题,作者,出版商和 ISBN。例如:“使用 OpenShift 和 Kubernetes 的混合云应用,由 Michael Elder,Jake Kitchener 和 Dr. Brad Topol(O’Reilly)编写。版权所有 2021 Michael Elder,Jake Kitchener,Brad Topol,978-1-492-08381-8。”

如果您认为您使用的示例代码超出了合理使用或上述许可的范围,请随时通过permissions@oreilly.com与我们联系。

O’Reilly 在线学习

注意

40 多年来,O’Reilly Media已为公司提供技术和业务培训、知识和见解,帮助其取得成功。

我们独特的专家和创新者网络通过书籍、文章和我们的在线学习平台分享他们的知识和专业知识。O’Reilly 的在线学习平台为您提供按需访问的实时培训课程、深度学习路径、交互式编码环境以及来自 O’Reilly 和其他 200 多家出版商的大量文本和视频。有关更多信息,请访问http://oreilly.com

如何联系我们

请将对本书的评论和问题发送至出版商:

  • O’Reilly Media,Inc.

  • 1005 Gravenstein Highway North

  • Sebastopol,CA 95472

  • 800-998-9938(美国或加拿大)

  • 707-829-0515(国际或本地)

  • 707-829-0104(传真)

我们为本书设有一个网页,列出勘误表,示例和任何额外信息。您可以访问https://oreil.ly/hybrid-cloud查看此页面。

发送电子邮件至bookquestions@oreilly.com以发表评论或询问有关本书的技术问题。

有关我们的书籍和课程的新闻和信息,请访问http://oreilly.com

在 Facebook 上找到我们:http://facebook.com/oreilly

在 Twitter 上关注我们:http://twitter.com/oreillymedia

在 YouTube 上观看我们:http://youtube.com/oreillymedia

致谢

我们要感谢整个 Kubernetes 和 OpenShift 社区,他们对这些开源项目的热情、奉献精神和巨大承诺。如果没有多年来为这些项目贡献代码的开发者、代码审阅者、文档作者和运维人员,Kubernetes 和 OpenShift 将不会拥有今天它们享有的丰富功能集、强大的采用率和庞大的生态系统。

我们要向为本文提供宝贵反馈的技术审阅者表示诚挚的感谢:Dan “Pop” Papandrea、Daniel Berg、Joshua Packer、Scott Berens 和 Burr Sutter。

我们要感谢我们的 Kubernetes 同事 Clayton Coleman、Derek Carr、David Eads、Paul Morie、Zach Corleissen、Jim Angel、Tim Bannister、Celeste Horgan、Irvi Aini、Karen Bradshaw、Kaitlyn Barnard、Taylor Dolezal、Jorge Castro、Jason DeTiberus、Stephen Augustus、Guang Ya Liu、Sahdev Zala、Wei Huang、Michael Brown、Jonathan Berkhahn、Martin Hickey、Chris Luciano、Srinivas Brahmaroutu、Morgan Bauer、Qiming Teng、Richard Theis、Tyler Lisowski、Bill Lynch、Jennifer Rondeau、Seth McCombs、Steve Perry 和 Joe Heck,感谢多年来的精彩合作。

我们还要感谢贡献给 Open Cluster Management 项目及其相关项目的贡献者,包括 OpenShift Hive 项目、Open Policy Agent 和 ArgoCD。

我们要感谢许多开源贡献者,他们使得 Kubernetes 以外的项目和示例得以实现,特别是例子 PAC-MAN 应用的作者和发起者,包括 Ivan Font、Mario Vázquez、Jason DeTiberus、Davis Phillips 和 Pep Turró Mauri。我们还要感谢许多示例 Open Cluster Management 策略的作者和贡献者,包括 Yu Cao、Christian Stark 和 Jaya Ramanathan。

我们还要感谢我们的编辑 Angela Rufino,在一个非常动态的年份和全球性流行病期间耐心地参与书写过程。此外,我们要感谢我们的副本编辑 Shannon Turlington,她对我们的工作进行了细致的审查,并提出了大量建议改进。

特别感谢 Willie Tejada、Todd Moore、Bob Lord、Dave Lindquist、Kevin Myers、Jeff Brent、Jason McGee、Chris Ferris、Vince Brunssen、Alex Tarpinian、Jeff Borek、Nimesh Bhatia、Briana Frank 和 Jake Morlock,在此过程中给予我们的所有支持和鼓励。

—Michael、Jake 和 Brad

第一章:Kubernetes 和 OpenShift 概述

过去几年来,Kubernetes 已经成为管理、编排和提供基于容器的云原生计算应用的事实标准平台。云原生计算应用基本上是由一组较小的服务(微服务)构建而成的应用程序,并利用云计算环境通常提供的开发速度和可扩展性能力。随着时间的推移,Kubernetes 已经成熟到可以提供管理更高级和有状态工作负载所需的控制,如数据库和 AI 服务。Kubernetes 生态系统继续经历爆炸式增长,并且该项目因为是一个基于多供应商和技术精英的开源项目,背靠坚实的治理政策以及为贡献者提供公平竞争的平台而受益良多。

尽管有许多 Kubernetes 发行版供客户选择,但 Red Hat OpenShift Kubernetes 发行版特别引人注目。OpenShift 已经在各行各业广泛采用;全球超过一千家企业客户目前使用它来托管其业务应用程序并推动数字转型努力。

本书侧重于让您成为在生产环境中运行传统 Kubernetes 和 OpenShift Kubernetes 发行版的专家。在本章中,我们从广义上概述了 Kubernetes 和 OpenShift 的历史起源。然后,我们回顾了使 Kubernetes 和 OpenShift 成为创建和部署云原生应用程序的主导平台的关键特性和能力。

Kubernetes:编排容器化应用的云基础设施

Docker 在 2013 年的出现向许多开发人员介绍了容器和基于容器的应用程序开发。容器被提出作为创建自包含部署单元的虚拟机(VM)的替代方案。容器依赖于 Linux 操作系统的先进安全性和资源管理功能,以在进程级别提供隔离,而不是依赖于 VM 来创建可部署的软件单元。Linux 进程在启动应用程序镜像或创建新的镜像快照等常见活动中比 VM 轻量得多,效率更高几个数量级。由于这些优势,开发人员青睐容器作为创建新软件应用程序的自包含部署软件单元的理想方法。随着容器的流行,对于在集群中提供、管理和编排容器的共同平台的需求也在增长。十多年来,谷歌一直支持使用 Linux 容器作为其云中部署应用程序的基础。^(1) 谷歌在规模上编排和管理容器方面拥有丰富的经验,并开发了三代容器管理系统:Borg、Omega 和 Kubernetes。作为基于 Borg 和 Omega 经验教训重新设计的最新一代容器管理系统,Kubernetes 作为开源项目提供。Kubernetes 提供了几个关键特性,显著改进了开发和部署可扩展的基于容器的云应用程序的体验:

声明式部署模型

Kubernetes 发布之前存在的大多数云基础设施采用了基于脚本语言(如 Ansible、Chef、Puppet 等)的过程化方法来自动化将应用程序部署到生产环境。相比之下,Kubernetes 使用了声明式方法来描述系统的期望状态。Kubernetes 基础设施负责在需要时启动新的容器(例如容器失败时)以达到所声明的期望状态。声明式模型在传达所需的部署操作方面更加清晰,与试图读取和解释脚本来确定所需部署状态相比,这一方法是一个巨大的进步。

内置的副本和自动伸缩支持

在一些存在于 Kubernetes 之前的云基础设施中,应用程序的复制和自动扩展能力并不是核心基础设施的一部分,并且在某些情况下,由于平台或架构限制,从未真正实现。自动扩展 是指云环境能够识别应用程序的使用情况增加,并自动增加应用程序的容量,通常是通过在云环境中的额外服务器上创建更多应用程序副本来实现的。自动扩展功能作为 Kubernetes 的核心特性提供,显著改进了其编排能力的健壮性和可用性。

内置的滚动升级支持

大多数云基础设施不提供应用程序升级支持。相反,它们假设操作员将使用像 Chef、Puppet 或 Ansible 这样的脚本语言来处理升级。与此不同的是,Kubernetes 实际上提供了内置支持,用于应用程序的滚动升级。例如,Kubernetes 的升级可配置为利用额外资源进行更快的无停机滚动,或者执行较慢的滚动来进行金丝雀测试,通过将软件释放给少数用户来减少风险并验证新软件,确保应用程序的新版本是稳定的。Kubernetes 还支持暂停、恢复和回滚应用程序的版本。

改进的网络模型

Kubernetes 将单个 IP 地址映射到一个 pod,这是 Kubernetes 最小的容器部署、聚合和管理单元。这种方法使网络身份与应用程序身份保持一致,并简化了在 Kubernetes 上运行软件的过程。^(2)

内置的健康检查支持

Kubernetes 提供了容器健康检查和监控功能,降低了识别故障发生时间的复杂性。

尽管 Kubernetes 提供了许多创新功能,但许多企业公司仍然对采用这项技术持谨慎态度,因为它是由单一供应商支持的开源项目。企业公司对于愿意采纳的开源项目非常谨慎,他们希望像 Kubernetes 这样的开源项目有多个供应商参与贡献;他们还期望开源项目是基于精英治理并且具有公平竞争的政策。2015 年,云原生计算基金会(CNCF)成立以解决 Kubernetes 面临的这些问题。

CNCF 加速 Kubernetes 生态系统的增长

2015 年,Linux 基金会发起了 CNCF 的创建。^(3) CNCF 的使命是使云原生计算普及化。^(4) 为了支持这个新基金会,谷歌将 Kubernetes 捐赠给 CNCF 作为其种子技术。以 Kubernetes 为生态系统核心,CNCF 已经发展到拥有超过 440 家成员公司,包括谷歌云、IBM 云、红帽、亚马逊网络服务(AWS)、Docker、微软 Azure、VMware、英特尔、华为、思科、阿里云等。^(5) 此外,CNCF 生态系统已经托管了 26 个开源项目,包括 Prometheus、Envoy、gRPC、etcd 等。最后,CNCF 还培育了几个早期项目,并已有八个项目被接纳到其用于新兴技术的 Sandbox 计划中。

在厂商中立的 CNCF 基金会的支持下,Kubernetes 每年的贡献者已经超过 3,200 人,来自各行各业。^(6) 除了托管几个云原生项目外,CNCF 还提供培训、技术监督委员会、治理委员会、社区基础设施实验室以及几个认证项目,以促进 Kubernetes 和相关项目的生态系统。由于这些努力,目前已经有超过一百个经过认证的 Kubernetes 发行版。其中,最受欢迎的 Kubernetes 发行版之一,尤其是企业客户,就是红帽的 OpenShift Kubernetes。在接下来的部分中,我们将介绍 OpenShift,并概述它为开发人员和 IT 运营团队提供的关键优势。

OpenShift:红帽的 Kubernetes 发行版

尽管许多公司都为 Kubernetes 做出了贡献,但红帽的贡献尤为引人注目。红帽从 Kubernetes 作为开源项目诞生之初就成为其生态系统的一部分,并且一直是 Kubernetes 的第二大贡献者。基于对 Kubernetes 的实践经验,红帽提供了自己的 Kubernetes 发行版,称为OpenShift。OpenShift 是企业中部署最广泛的 Kubernetes 发行版。它提供了一个符合标准的 Kubernetes 平台,并通过各种工具和功能来提高开发人员和 IT 运营的生产力。

OpenShift 最初发布于 2011 年。[⁷] 那时,它有自己的平台特定的容器运行时环境。[⁸] 2014 年初,红帽团队与谷歌的容器编排团队会面,了解到了一个新的容器编排项目,最终成为 Kubernetes。红帽团队对 Kubernetes 非常印象深刻,因此 OpenShift 被重写以使用 Kubernetes 作为其容器编排引擎。由于这些努力,OpenShift 能够在其 2015 年 6 月的版本 3 发布中交付一个完全符合标准的 Kubernetes 平台。[⁹]

红帽 OpenShift 容器平台是 Kubernetes 带有额外支持功能的实现,使其符合企业需求。Kubernetes 社区为发布提供长达 12 个月的修复补丁支持。OpenShift 通过提供长期支持(三年或更长时间)的主要 Kubernetes 发布版本、安全补丁以及覆盖操作系统和 OpenShift Kubernetes 平台的企业支持合同,使其在各个分布中区别于其他发行版。红帽企业 Linux(RHEL)长期以来一直是各大小组织的事实上 Linux 发行版。红帽 OpenShift 容器平台在 RHEL 的基础上构建,以确保从主机操作系统到集群上所有容器化功能的一致 Linux 发行。除了所有这些好处之外,OpenShift 通过提供各种工具和能力来增强 Kubernetes,重点是提高开发人员和 IT 运营的生产力。以下各节描述了这些好处。

开发者的 OpenShift 好处

虽然 Kubernetes 在为容器镜像的供应和管理提供了许多功能,但它并未提供太多支持来自基础镜像创建新镜像、将镜像推送到注册表或识别新版本可用的功能。此外,Kubernetes 提供的网络支持使用起来可能相当复杂。为了填补这些空白,OpenShift 为开发者提供了超越核心 Kubernetes 平台所提供的几个优势:

源到镜像

当使用基本 Kubernetes 时,云原生应用程序开发人员负责创建自己的容器镜像。通常,这涉及找到合适的基础镜像,并创建一个包含所有必要命令的 Dockerfile,以便将基础镜像与开发人员的代码合并,创建一个 Kubernetes 可以部署的组装镜像。这要求开发人员学习一系列用于镜像组装的 Docker 命令。通过其源码到镜像(S2I)能力,OpenShift 能够处理将云原生开发人员的代码合并到基础镜像中。在许多情况下,可以配置 S2I,使开发人员只需将其更改提交到 Git 仓库,S2I 将看到更新的更改,并将其与基础镜像合并以创建新的组装镜像以供部署。

推送镜像到注册表

当云原生开发人员在基本 Kubernetes 中使用时,必须执行的另一个关键步骤是将新组装的容器镜像存储在像 Docker Hub 这样的镜像注册表中。在这种情况下,开发人员需要创建和管理仓库。相比之下,OpenShift 提供了自己的私有注册表,开发人员可以选择使用该选项,或者可以配置 S2I 将组装后的镜像推送到第三方注册表。

图像流

当开发人员创建云原生应用程序时,开发工作会导致大量配置更改,以及对应用程序容器镜像的更改。为了解决这种复杂性,OpenShift 提供了图像流功能,该功能监视配置或图像更改,并根据更改事件执行自动构建和部署。这一功能减轻了开发人员在发生更改时手动执行这些步骤的负担。

基础镜像目录

OpenShift 提供了一个基础镜像目录,其中包含大量有用的基础镜像,适用于各种工具和平台,如 WebSphere Liberty、JBoss、PHP、Redis、Jenkins、Python、.NET、MariaDB 等。该目录提供了来自已知源代码打包的受信内容。

路由

在基础 Kubernetes 中配置网络可能会相当复杂。OpenShift 具有路由结构,与 Kubernetes 服务进行交互,并负责将 Kubernetes 服务添加到外部负载均衡器中。路由还为应用程序提供可读的 URL,并提供多种负载均衡策略,以支持蓝绿、金丝雀和 A/B 测试部署等多种部署选项。^(10)

虽然 OpenShift 对开发人员有很多好处,但其最大的差异化优势在于它为 IT 运维提供的好处。在下一节中,我们描述了运行 OpenShift 在生产环境中自动化日常运营的几个核心能力。

OpenShift 对 IT 运维的益处

2019 年 5 月,Red Hat 宣布发布 OpenShift 4。¹¹ Red Hat 收购了 CoreOS,后者对管理 Kubernetes 的生命周期行为具有非常自动化的方法,并且早期倡导“运营者”概念。这个新版本的 OpenShift 完全重写,借鉴了 CoreOS 创新的管理实践和 OpenShift 3 在可靠性方面的声誉,大大改善了 OpenShift 平台的安装、升级和管理方式。^(11) 为了提供这些重要的生命周期改进,OpenShift 在其架构中大量使用了最新的 Kubernetes 创新和最佳实践来自动化管理资源。因此,OpenShift 4 能够为 IT 运维带来以下好处:

自动化安装

OpenShift 4 支持创新的自动化安装方法,可靠且可重复。^(12) 此外,OpenShift 4 安装过程支持完全堆栈的自动化部署,并能处理安装完整基础设施,包括 DNS 和虚拟机等组件。

自动化操作系统和 OpenShift 平台更新

OpenShift 与轻量级的 RHEL CoreOS 操作系统紧密集成,后者专为运行 OpenShift 和云原生应用程序进行了优化。由于 OpenShift 与特定版本的 RHEL CoreOS 紧密耦合,OpenShift 平台能够在其集群管理操作中管理操作系统更新。对 IT 运维的关键价值在于,它支持自动化、自我管理的远程更新。这使得 OpenShift 能够支持云原生和无人值守的运维操作。

自动化集群大小管理

OpenShift 支持自动增减其管理的集群大小。与所有 Kubernetes 集群一样,OpenShift 集群有一定数量的工作节点,用于部署容器应用程序。在典型的 Kubernetes 集群中,增加工作节点是一个 IT 运维必须手动处理的操作。相比之下,OpenShift 提供了一个称为机器操作器的组件,能够自动向集群添加工作节点。IT 运维人员可以使用MachineSet对象声明集群所需的机器数量,OpenShift 将自动执行新工作节点的供应和安装,以达到所需状态。

自动化集群版本管理

OpenShift,像所有的 Kubernetes 发行版一样,由大量组件组成。每个组件都有自己的版本号。为了管理这些组件的更新,OpenShift 依赖于 Kubernetes 创新称为operator construct。OpenShift 使用集群版本号来标识正在运行的 OpenShift 版本,该集群版本号表示需要安装哪些个别 OpenShift 平台组件的版本。通过其自动化的集群版本管理,OpenShift 能够在集群更新到新版本时自动安装所有这些组件的适当版本,以确保更新正确进行。

多云管理支持

许多使用 OpenShift 的企业客户拥有多个集群,这些集群部署在多个云中或多个数据中心中。为了简化多个集群的管理,OpenShift 4 引入了一个新的统一云控制台,允许客户查看和管理多个 OpenShift 集群。^(13)

正如我们将在本书后面看到的那样,当到达运行和 IT 运营人员需要解决运营和安全相关问题的时候,OpenShift 及其提供的功能变得非常显著。

摘要

在本章中,我们概述了 Kubernetes 和 OpenShift,包括这两个平台的历史起源。然后,我们介绍了 Kubernetes 和 OpenShift 提供的主要优势,这些优势推动了这些平台在全球范围内的巨大增长。这帮助我们更加重视 Kubernetes 和 OpenShift 为云原生应用开发者和 IT 运营团队提供的价值。因此,毫不奇怪这些平台在多个行业中经历了爆炸式增长。在第二章,我们将建立一个扎实的基础概述 Kubernetes 和 OpenShift,介绍 Kubernetes 架构,讨论如何在生产环境中运行 Kubernetes 和 OpenShift,并介绍几个关键的 Kubernetes 和 OpenShift 概念,这些对成功运行至关重要。

^(1) Brendan Burns 等,“Borg、Omega 和 Kubernetes:从十年三个容器管理系统的经验中汲取教训”,ACM Queue 14(2016 年):70–93,http://bit.ly/2vIrL4S

^(2) Brendan Burns 等,“Borg、Omega 和 Kubernetes:从十年三个容器管理系统的经验中汲取教训”,ACM Queue 14(2016 年):70–93,http://bit.ly/2vIrL4S

^(3) Steven J. Vaughan-Nicholls,“云原生计算基金会寻求在云和容器领域实现统一”,ZDNet(2015 年 7 月 21 日),https://oreil.ly/WEoE0

^(4) Linux Foundation,CNCF Chapter(更新于 2018 年 12 月 10 日),https://oreil.ly/tHHvr

^(5) CNCF 会员页面提供了 CNCF 成员增长的更多详情。

^(6) 查看Kubernetes 公司表格仪表板以获取当前列表。

^(7) Joe Fernandes,“为什么 Red Hat 选择 Kubernetes 作为 OpenShift”,红帽 OpenShift 博客(2016 年 11 月 7 日),https://oreil.ly/r66GM

^(8) Anton McConville 和 Olaph Wagoner,“Kubernetes、OpenShift 和 IBM 的简史”,IBM Developer 博客(2019 年 8 月 1 日),https://oreil.ly/IugtP

^(9) “Red Hat 提供 OpenShift Enterprise 3 以支持新的 Web 规模分布式应用平台” [新闻稿],红帽(2015 年 6 月 24 日),https://oreil.ly/jlane

^(10) 欲了解更多有关 OpenShift 路由的详情,请参阅使用基于路由的部署策略的 OpenShift 文档。

^(11) Joe Fernandes,“介绍 Red Hat OpenShift 4:企业级 Kubernetes”,红帽 OpenShift 博客(2019 年 5 月 8 日),https://oreil.ly/yNb8s

^(12) Christian Hernandez,“OpenShift 4.1 Bare Metal Install Quickstart”,红帽 OpenShift 博客(2019 年 7 月 31 日),https://oreil.ly/yz4pR

^(13) Fernandes,“介绍 Red Hat OpenShift 4:企业级 Kubernetes”。

第二章:OpenShift 和 Kubernetes 入门

在本章中,我们涵盖了多个主题,这些主题构成了对 Kubernetes 和 OpenShift 的基础理解。我们从 Kubernetes 架构概述开始,然后描述了几种部署选项,这些选项可以帮助您快速建立基本的 Kubernetes 环境和 OpenShift 环境。接下来,我们介绍了用于与 Kubernetes 和 OpenShift 交互的命令行工具 kubectloc。然后,我们简要回顾了 pods、deployments 和 service accounts 的基本 Kubernetes 概念。在本章的后半部分,我们介绍了 OpenShift 提供的几个增强概念,超越了传统 Kubernetes。最后,我们讨论了在生产环境中运行 Kubernetes 或 OpenShift 时经常使用的更高级的主题。

Kubernetes 架构

Kubernetes 架构在高层次上相对简单。它由一个 master 节点 和一组 worker 节点 组成。节点可以是物理服务器或虚拟机。Kubernetes 环境的用户使用 CLI (kubectl)、API 或 GUI 与 master 节点进行交互。master 节点负责在 worker 节点上调度工作。在 Kubernetes 中,被调度的工作单元称为 pod,一个 pod 可以包含一个或多个容器。存在于 master 节点上的主要组件包括 kube-apiserverkube-schedulerkube-controller-manageretcd

kube-apiserver

kube-apiserver 提供了用于操作 Kubernetes 环境的 Kubernetes API。

kube-scheduler

kube-scheduler 组件负责选择在其上创建 pods 的节点。

kube-controller-manager

Kubernetes 提供了几个高级抽象来支持 pods 的副本、节点管理等。每个抽象都由一个控制器组件实现,我们将在本章后面描述。kube-controller-manager 负责管理和运行控制器组件。

etcd

etcd 组件是一个分布式键值存储,是 Kubernetes 控制平面的主要数据存储。该组件存储和复制 Kubernetes 环境中所有关键信息状态。etcd 的关键特性是支持 watchWatch 是一种远程过程调用(RPC)机制,允许在键值创建、更新或删除操作时回调函数。Kubernetes 出色的性能和可伸缩性特性依赖于 etcd 作为高效的数据存储机制。

工作节点负责运行调度在其上的 pods。存在于工作节点上的主要 Kubernetes 组件包括 kubeletkube-proxycontainer runtime

kubelet

kubelet 负责确保每个 Pod 中的容器被创建并保持运行状态。在识别到容器异常终止或未通过用户定义的其他健康检查时,kubelet 将重新启动容器。

kube-proxy

Kubernetes 的关键优势之一是其为容器实现的网络支持。kube-proxy 组件提供连接转发、负载均衡以及将单个 IP 地址映射到 Pod 的网络支持。kube-proxy 的独特之处在于它提供了关键的分布式负载均衡能力,这对 Kubernetes 的高可用架构至关重要。

容器运行时

容器运行时组件负责运行每个 Pod 中存在的容器。Kubernetes 支持多种容器运行时环境选项,包括 Docker、rkt、CRI-O 和 containerd。^(1)

图 2-1 显示了 Kubernetes 架构的图形表示,包括一个主节点和两个工作节点。

图 2-1. Kubernetes 架构的图形表示

如图 2-1 所示,用户可以使用 GUI 或 kubectl CLI 与 Kubernetes API 服务器进行交互。这两者都使用 Kubernetes API 与 Kubernetes 主节点上的 kube-apiserver 进行交互。Kubernetes 主节点的 kube-scheduler 组件调度 Pod 在不同的工作节点上运行。每个 Pod 包含一个或多个容器,并分配了自己的 IP 地址。在许多实际应用中,Kubernetes 部署同一个 Pod 的多个副本(运行副本)以提高可伸缩性并确保高可用性。Pod A1 和 A2 是仅在分配的 IP 地址上有所不同的 Pod 副本。同样,Pods B1 和 B2 也是同一个 Pod 的副本。位于同一 Pod 中的容器允许使用标准的进程间通信(IPC)机制进行通信。

在下一节中,我们将介绍几种运行 OpenShift 和 Kubernetes 环境的方法。

Kubernetes 和 OpenShift 的部署选项

Kubernetes 和 OpenShift 都已经达到了令人难以置信的流行水平。因此,目前有几种可用于部署基本 Kubernetes 或 Red Hat 的 OpenShift Kubernetes 分发的选项。在接下来的几节中,我们总结了当前可用的不同部署选项,包括 Red Hat 的 CodeReady Containers、IBM Cloud 和几种 OpenShift 部署选项。

Red Hat 的 CodeReady Containers

Red Hat 提供了一个名为CodeReady Containers的最小预配置的 OpenShift 版本 4 集群,可以在您的笔记本电脑或台式电脑上运行。CodeReady OpenShift 环境旨在用于开发和测试目的。CodeReady Containers 在您的本地机器上提供了一个完全功能的云开发环境,并包含了您开发基于容器的应用程序所需的所有工具。

IBM Cloud

IBM Cloud 提供给用户选择传统的 Kubernetes 集群或 Red Hat OpenShift 集群。IBM Cloud 的 Kubernetes 提供的是作为服务的云服务,提供所有标准的 Kubernetes 功能,包括智能调度、自愈、水平扩展、服务发现与负载平衡、自动发布和回滚以及秘密和配置管理。此外,IBM Cloud 的 Kubernetes 服务还包括集群部署、更新和扩展的自动化操作,专家安全性、优化配置以及与 IBM Cloud 基础设施平台的无缝集成。它在 6 个区域和 35 个数据中心提供高可用的多区域集群。IBM Cloud 提供了一个免费的 Kubernetes 集群,包含超过 40 个免费服务,并提供按使用付费的选项。

IBM Cloud 也为用户提供高可用、完全托管的OpenShift 集群。IBM 的 OpenShift 产品实施了独特的安全性和生产力功能,旨在消除更新、扩展和配置所需的大量时间。此外,IBM Cloud 的 OpenShift 提供了抗击意外激增并防范可能导致财务和生产力损失的攻击的弹性。除了按使用和订阅选项外,IBM Cloud 还提供了一个免费的预配置 OpenShift 版本 4.3 环境,可以免费使用四小时。

OpenShift 部署选项

Getting Started with OpenShift网站上定义了多种 OpenShift 的部署选项。所描述的选项包括在您的笔记本电脑上安装 OpenShift 版本 4,将其部署在您的数据中心或公共云中,或让 Red Hat 为您管理 OpenShift。此外,Red Hat 还提供了面向 OpenShift 的实践教程和 OpenShift 游乐场环境,供非结构化学习和实验使用。图 2-2 展示了可用的 OpenShift 部署选项的多样性。

图 2-2. OpenShift 部署选项详见Get Started with OpenShift

在下一节中,我们将介绍用于与这些平台交互的命令行工具。

Kubernetes 和 OpenShift 命令行工具

如 第一章 所述,OpenShift 提供了一个符合 Kubernetes 平台标准的平台,并通过各种工具和能力来提高开发人员和 IT 运营的生产力。在本节中,我们首先介绍 kubectloc,它们分别是与 Kubernetes 和 OpenShift 交互的标准命令行工具。我们介绍了几个 OpenShift 用来表示其在传统 Kubernetes 上提供的增强功能的概念。我们描述的 OpenShift 概念包括认证、项目、应用、安全上下文和镜像流。

在介绍了 Kubernetes 的一些核心概念之后,接下来的章节将以 YAML 文件的形式给出几个示例。对于所有 Kubernetes 环境,可以使用标准的 Kubernetes 命令行工具 kubectl 运行所包含的样本。许多 Kubernetes 环境,包括本章前面提到的环境,都描述了如何安装 kubectl。一旦您的 Kubernetes 环境运行起来并安装了 kubectl,则以下几节中的所有 YAML 文件样本都可以通过首先将 YAML 保存到文件(例如 kubesample1.yaml),然后运行以下 kubectl 命令来运行:

$ kubectl apply -f kubesample1.yaml

正如之前讨论的那样,OpenShift Kubernetes 分发增加了几项新的增强功能和能力,超越了传统 Kubernetes 的使用方式。OpenShift 通过扩展 kubectl 的功能来提供对这些功能的访问。为了明确表明 OpenShift 版本的 kubectl 具有扩展功能,OpenShift 将其 kubectl 版本重命名为一个新的命令行工具叫做 oc. 因此,以下命令等效于之前的 kubectl 命令:

$ oc apply -f kubesample1.yaml

除了对所有 kubectl 命令提供一对一的匹配支持外,oc 还添加了用于管理用户和组的角色和角色绑定等管理功能的命令。

要了解 OpenShift oc CLI 提供的命令的广度,请参阅 OpenShift 命令行文档

Kubernetes 基础知识

Kubernetes 具有几个与其管理容器模型相关的概念。在本节中,我们简要回顾了关键的 Kubernetes 概念,包括 pod、部署和服务账户。

什么是 Pod?

因为 Kubernetes 支持容器的管理和编排,您可能会认为 Kubernetes 支持的最小可部署单元是一个容器。然而,Kubernetes 的设计者从经验中学到,最小的可部署单元最好是能够容纳多个容器的单元。在 Kubernetes 中,这个最小的可部署单元称为 pod。一个 pod 可以容纳一个或多个应用程序容器。位于同一个 pod 中的应用程序容器具有以下好处:

  • 它们共享一个 IP 地址和端口空间

  • 它们共享相同的主机名

  • 它们可以使用本地 IPC 相互通信

相比之下,运行在单独 Pod 中的应用容器保证拥有不同的 IP 地址和不同的主机名。实质上,不同 Pod 中的容器应被视为运行在不同的服务器上,即使它们最终在同一个节点上结束。

Kubernetes 提供了一组强大的功能,使得 Pod 容易使用:

易于使用的 Pod 管理 API

Kubernetes 提供kubectl命令行界面,支持对 Pod 进行各种操作,包括创建、查看、删除、更新、交互和扩展 Pod。

文件复制支持

Kubernetes 让在本地主机和在集群中运行的 Pod 之间复制文件变得非常简单。

从本地计算机到 Pod 的连接性

在许多情况下,您可能希望从本地主机到集群中运行的 Pod 的网络连接。Kubernetes 支持端口转发,通过安全隧道将本地主机上的网络端口连接到集群中运行的 Pod 上的端口。这是一个非常有用的功能,可帮助调试应用程序和服务,而无需将它们公开。

卷存储支持

Kubernetes Pod 支持挂载远程网络存储卷,以使 Pod 中的容器能够访问持久存储,即使 Pod 和最初利用该存储的容器的生命周期结束后仍然保留。

基于探针的健康检查支持

Kubernetes 提供基于探针的健康检查,以确保容器的主要进程仍在运行。此外,Kubernetes 还执行活跃性检查,确保容器实际上正在运行并能够执行真正的工作。借助这种健康检查支持,Kubernetes 可以识别出您的容器是否崩溃或变得无法使用,并代表您重新启动它们。

我如何描述我的 Pod 中有什么?

Kubernetes 和其他所有由 Kubernetes 管理的资源都使用 YAML 文件进行描述。以下是描述基本 Pod 资源的简单 YAML 文件:

apiVersion: v1
kind: Pod
metadata:
 name: nginx
spec:
 containers:
 - name: nginx
   image: nginx:1.7.9
   ports:
   - containerPort: 80

此 YAML 文件包含以下字段和部分:

apiVersion

此字段用于声明使用的 Kubernetes API 架构的版本。Kubernetes 在功能和功能上继续快速增长。通过支持其 API 的多个版本,Kubernetes 管理其增长能力所导致的复杂性。通过设置apiVersion字段,您可以控制资源使用的 API 版本。

kind

使用kind字段来识别 YAML 文件描述的资源类型。在上述示例中,YAML 文件声明它描述的是一个Pod对象。

metadata

metadata 部分包含有关 YAML 定义的资源的信息。在上述示例中,metadata 包含一个名为 name 的字段,声明了此 Pod 的名称。metadata 部分还可以包含其他类型的标识信息,例如标签和注释。我们将在 “Deployments” 中更详细地描述这些内容。

spec

spec 部分提供了此资源的期望状态的规范。如示例所示,此 Pod 的期望状态是具有名称为 nginx 的容器,该容器由标识为 nginx:1.7.9 的 Docker 镜像构建。容器共享其所在 Pod 的 IP 地址,而 containerPort 字段用于分配此容器可用于发送和接收网络流量的网络端口(在本例中为 80)。

提示

apply 命令将创建一个资源或更新任何现有的匹配资源。还支持 create 命令,该命令将假定尚不存在由 YAML 文档描述的资源。通常情况下,您可以在任何需要 create 的地方使用 apply。在某些情况下,例如特殊的 generateName 属性,仅支持 create

要运行前面的示例,请将文件保存为 pod.yaml。现在可以通过以下方式运行它:

$ kubectl apply -f pod.yaml

运行此命令后,您应该会看到以下输出:

pod/nginx created

要确认您的 Pod 是否真正运行,请使用 kubectl get pods 命令进行验证:

$ kubectl get pods

运行此命令后,您应该会看到类似以下的输出:

NAME  READY STATUS  RESTARTS AGE
nginx 1/1   Running 0        21s

当 Pod 运行时,您还可以使用 logs 命令查看 Pod 内运行进程的日志(如果有多个容器,请使用 -c 选项选择要查看的特定容器):

$ kubectl logs nginx

如果需要调试正在运行的容器,可以通过以下命令在容器内创建一个交互式 shell:

$ kubectl exec -it nginx – bash

此命令指示 Kubernetes 为名为 nginx 的 Pod 中运行的容器启动一个交互式 shell。因为此 Pod 只有一个容器,所以即使您没有指定容器名称,Kubernetes 也知道您要连接的容器。通常认为在运行时访问容器的交互式 shell 是一种不良实践。然而,在学习或调试应用程序部署到生产之前,交互式 shell 可能是有用的。运行前述命令后,您可以与容器的运行时环境进行交互,如下所示:

root@nginx:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin selinux 
srv sys tmp usr var
root@nginx:/# exit

如果您的 Pod 包含多个容器,则在 kubectl exec 命令中还需要包含容器名称。为此,请使用 -c 选项,并在 Pod 名称之外包含容器名称。以下是一个示例:

$ kubectl exec -it nginx -c nginx -- bash
root@nginx:/# exit
exit

要删除刚刚创建的 Pod,请运行以下命令:

$ kubectl delete pod nginx

您应该看到已删除 Pod 的以下确认:

pod "nginx" deleted

在使用 Kubernetes 时,您可以期望在集群中运行大量的 pod。在接下来的部分中,我们描述了如何使用标签和注释来帮助您跟踪和识别您的 pod。

部署

部署 是 Kubernetes 的高级抽象,不仅允许您控制实例化的 pod 副本数目,还支持新版本的 pod 的逐步发布。部署是可配置的,以便利用额外的资源进行无 downtime 的快速发布,或者进行包含金丝雀测试的较慢发布。较慢发布的优势在于它可以通过将软件发布给一小部分用户来降低风险并验证新软件,从而确保新版本的应用程序稳定。部署依赖于 ReplicaSet 资源来管理 pod 副本,然后在此能力之上添加 pod 版本管理支持。部署还允许将新发布的 pod 版本回滚到先前的版本,如果新版本的 pod 出现问题。此外,部署支持两种升级 pod 的选项,即 RecreateRollingUpdate

Recreate

Recreate pod 升级选项非常直接。在这种方法中,部署资源修改其关联的 ReplicaSet,以指向新版本的 pod。然后,它继续终止所有的 pod。ReplicaSet 然后注意到所有的 pod 都已终止,因此生成新的 pod 以确保所需副本数目正在运行。Recreate 方法通常会导致您的 pod 应用程序在一段时间内不可访问,因此不建议对需要始终可用的应用程序使用此方法。

RollingUpdate

Kubernetes 的部署资源还提供了 RollingUpdate 选项。通过 RollingUpdate 选项,您的 pod 会逐步被新版本替换。这种方法会导致旧版本和新版本的 pod 同时运行,从而在维护期间避免使您的 pod 应用程序不可用。每个 pod 的就绪状态会被测量,并用于通知 kube-proxy 和入口控制器,哪些 pod 副本可用来处理网络请求,以确保在更新过程中不会丢失任何请求。

以下是一个使用 RollingUpdate 选项的部署的 YAML 规范示例:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
 labels:
  app: webserver
 annotations:
  deployment.kubernetes.io/revision: "1"
spec:
 replicas: 3
 selector:
  matchLabels:
   app: webserver
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
  spec:
   containers:
   - name: nginx
     image: nginx:1.7.9
     ports:
     - containerPort: 80

先前的部署示例包含了我们在ReplicaSets 和 Pod 中看到的许多特征。在其元数据中,它包含标签和注释。对于部署,一个以deployment.kubernetes.io/revision为键,值为1的注释提供了此部署内容的第一个修订版信息。与ReplicaSets 类似,部署声明了其提供的副本数量,并使用matchLabels字段声明用于识别其管理的 Pod 的标签。

提示

标签匹配是 Kubernetes API 中非常常见的一个方面。如果需要组织或分类资源,请添加描述性标签,作为轻量级元数据。您还可以使用kubectl getkubectl patch中的-l选项查询或查找资源。

ReplicaSets 类似,部署(deployment)既有一个用于描述部署的spec部分,也有一个嵌套在template内部的spec部分,用于描述由此部署管理的 Pod 副本的容器。

部署资源中新的并且特定的字段是strategy字段及其子字段typeRollingUpdatetype字段用于声明正在使用的部署策略,目前可以设置为RecreateRollingUpdate

如果选择了RollingUpdate选项,则还需要设置maxUnavailablemaxSurge的子字段。选项的使用如下:

maxSurge

maxSurge RollingUpdate选项在滚动更新期间允许分配额外的资源。此选项的值可以设置为数字或百分比。作为一个简单的例子,假设一个部署支持三个副本,并且maxSurge设置为2。在这种情况下,在RollingUpdate期间将有总共五个副本可用。

在部署的高峰期,将会有三个使用旧版本 Pod 的副本和两个使用新版本 Pod 的副本运行。在这一点上,需要终止一个旧版本 Pod 副本,然后可以创建另一个新版本 Pod 的副本。在这个阶段,将会有总共五个副本,其中三个具有新修订版,两个具有旧版本 Pod。最终,当达到新版本的正确数量的 Pod 副本可用时,两个旧版本的 Pod 可以被终止。

maxUnavailable

RollingUpdate选项用于声明在更新期间可能不可用的部署副本 Pod 的数量。可以将其设置为数字或百分比。

以下的 YAML 示例展示了一个已更新以启动一个滚动更新的部署:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
 labels:
  app: webserver
 annotations:
  kubernetes.io/change-cause: "Update nginx to 1.13.10"
spec:
 replicas: 3
 selector:
  matchLabels:
   app: webserver
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
  spec:
   containers:
   - name: nginx
     image: nginx:1.13.10
     ports:
     - containerPort: 80

请注意,已添加了一个新的带有键kubernetes.op/change-cause的注释标签,并且该值表示容器中运行的 nginx 版本已更新。还请注意,在spec部分中用于容器的镜像名称已更改为nginx:1.13.10。这个声明实际上驱动了由部署管理的 Pod 副本现在在升级发生时具有新版本的容器镜像。

为了演示部署的功能,让我们运行前两个示例。将第一个部署示例保存为deploymentset.yaml,将第二个示例保存为deploymentset2.yaml。现在,您可以通过以下方式运行第一个部署示例:

$ kubectl apply -f deploymentset.yaml

运行此命令后,您应该会看到以下输出:

deployment.apps/nginx created

要确认您的由部署管理的 Pod 副本是否实际在运行,请使用kubectl get pods命令进行验证:

$ kubectl get pods

运行此命令后,您应该会看到类似以下的输出:

NAME                  READY STATUS  RESTARTS AGE
nginx-758fbc45d-2znb7 1/1   Running 0        68s
nginx-758fbc45d-gxf2d 1/1   Running 0        68s
nginx-758fbc45d-s9f9t 1/1   Running 0        68s

通过部署,我们有了一个名为kubectl get deployments的新命令,它在它们更新其镜像时提供部署状态。请按以下方式运行此命令:

$ kubectl get deployments

运行此命令后,您应该会看到类似以下的输出:

NAME  READY UP-TO-DATE AVAILABLE AGE
nginx 3/3   3          3         2m6s

现在让事情变得有趣,让我们通过应用我们在deploymentset2.yaml中保存的第二个部署示例来更新部署中的镜像。请注意,我们本可以只更新我们在deploymentset.yaml中保存的原始 YAML,而不是使用两个单独的文件。我们通过以下步骤开始更新:

$ kubectl apply -f deploymentset2.yaml

运行此命令后,您应该会看到以下输出:

deployment.apps/nginx configured

现在,当我们重新运行kubectl get deployments命令时,该命令会提供有关部署状态的信息,因为它们更新其镜像,我们将看到一个更加有趣的结果:

$ kubectl get deployments
NAME  READY UP-TO-DATE AVAILABLE AGE
nginx 2/3   3          2         34s

如此输出所示,当前部署有三个正在运行的 Pod 副本。其中三个 Pod 副本已经更新为运行更新后的 nginx 镜像。此外,总共有三个 Pod 副本,其中两个可用于处理请求。在一段时间后,当滚动更新完成时,我们达到了希望的状态,即有三个更新后的 Pod 副本可用。我们可以通过重新运行kubectl get deployments命令来确认,查看输出现在与我们的期望状态匹配:

$ kubectl get deployments
NAME  READY UP-TO-DATE AVAILABLE AGE
nginx 3/3   3          3         46s

要删除刚刚创建的部署,请运行以下命令:

$ kubectl delete deployment nginx

您应该会得到以下确认,即部署已被删除:

deployment.apps "nginx" deleted

部署还包括命令来暂停部署、恢复部署以及回滚图像更新。如果您对正在部署的新图像有一些值得调查的顾虑,或者确定正在部署的更新图像有问题并且需要回滚到先前的版本,这些命令将非常有帮助。详见Kubernetes 部署文档,了解如何使用这些部署功能的更多信息。

在接下来的章节中,我们将探讨在安全的 Kubernetes 生产级环境(如 OpenShift)中运行先前示例所需的额外步骤。

在 OpenShift 上生产环境中运行 Pod 和部署示例

在前几节中呈现的 Pod 和部署示例非常适合教学目的以及在本地开发环境中运行。但在像 OpenShift 这样的高度安全的 Kubernetes 平台上运行时,需要考虑其他因素。首先,在先前示例中使用的 nginx 容器镜像被配置为以特权根用户运行。默认情况下,像 OpenShift 这样的安全生产 Kubernetes 平台配置为不允许容器镜像以根身份运行。这是因为以根身份运行容器镜像会增加恶意代码可能对主机系统造成损害的风险。^(3) 为解决这个问题,我们将在本章前面创建的pod.yaml示例中使用一个不需要以特权根用户运行的图像版本,来替换先前使用的 nginx 容器。Bitnami 提供的 nginx 容器镜像作为非根容器运行,可以在生产 OpenShift 环境中使用。以下示例是我们之前创建的pod.yaml的更新版本,使用了 Bitnami 的非根 nginx 容器镜像:

apiVersion: v1
kind: Pod
metadata:
 name: nginx
spec:
 containers:
 - name: nginx
 image: bitnami/nginx:1.18
 ports:
 - containerPort: 80
注意

请记住,所有资源都是集群范围,意味着集群中只能存在一个该类资源,或者是命名空间范围,意味着这些资源与集群上的其他类似资源隔离。在 OpenShift 中,您可能还会看到项目这个术语,它在 Red Hat 与社区合作通用化为命名空间的概念之前就已存在。项目命名空间是同义词,OpenShift 将对get projectsget namespaces作出响应。您可以将命名空间类比为文件系统中的文件夹,用于分配给协作创建一组文件的用户组。我们将在“OpenShift 增强”中进一步讨论命名空间或项目。

我们早期 Pod 和部署示例的另一个问题需要解决的是,在创建它们时,我们没有通过创建一个专用于我们资源的 Kubernetes 命名空间来将我们的资源与其他资源隔离开来。相反,早期示例将我们的资源放置在 Kubernetes 默认命名空间中。为了鼓励应用程序的正确隔离,安全的生产 Kubernetes 平台(如 OpenShift)将强制执行您的 Kubernetes 资源不在默认命名空间中创建,而是在提供所需隔离的用户定义命名空间中创建。要创建正确配置的命名空间,OpenShift 提供了 oc new-project 命令。OpenShift 的项目功能在 “OpenShift Enhancements” 中有更详细的描述。然而,目前我们将使用 oc new-project 命令来创建一个名为 book 的新项目,这将提供所需的隔离以便运行我们的 Pod 示例。我们通过运行以下命令来创建我们的新项目:

$ oc new-project book

我们随后可以使用 oc apply -f 命令,传入我们更新后的 pod.yaml 文件,并使用 -n 选项声明我们希望在 book 命名空间中创建我们的资源:

$ oc apply -f pod.yaml -n book
pod/nginx configured

现在我们已经使用了非根容器镜像,并且不再使用默认命名空间,OpenShift 将允许我们的 Pod 示例在生产环境中运行。我们可以通过使用 oc get pods 命令来确认这一点:

$ oc get pods
NAME  READY STATUS  RESTARTS AGE
nginx 1/1   Running 0        63s

我们可以使用 oc delete pod 命令清理并移除 Pod 示例:

$ oc delete pod nginx
pod "nginx" deleted

我们用于 Pod 示例的相同技术也可以应用于部署示例。只需更新用于 deploymentset.yaml 中的 nginx 镜像,并确保在执行 oc apply 命令时使用 book 命名空间。在下一节中,我们将介绍 Kubernetes 平台的另一个基本概念,称为 服务账户,它们用于为 Kubernetes 平台的关键部分提供身份验证。

服务账户

当您与您的集群交互时,您通常将自己表示为用户身份。在 Kubernetes 的世界中,我们将智能构建到系统中,以帮助它与其世界进行交互。许多时候,Pod 可能使用 Kubernetes API 与系统的其他部分交互或者生成作业。当我们部署一个 Pod 时,它可能与卷存储交互,与主机文件系统交互,与主机网络交互,或者对赋予它的操作系统用户对文件系统访问权限敏感。在大多数情况下,您希望限制给定 Pod 的默认权限,以免其超出绝对基础功能。基本上,Pod 在集群、主机操作系统、网络层和存储层中被赋予访问权限越少,可以被利用的攻击向量就越少。

为了让 pod 与系统交互,它被分配了一个服务账户。可以将其视为功能性身份。服务账户是可以通过令牌与 kube-apiserver 进行身份验证并被授权执行某些行为的主体。

在某些 Kubernetes 系统中,投影到 pod 中的服务账户可能具有 Kubernetes 之外的身份。一个强大的使用情况是在 Kubernetes 中使用开源Istio服务网格项目。在这种情况下,Istio 身份通过服务账户进行投影,这允许一个 pod 在进行服务请求时与另一个 pod 进行身份验证。一些云提供商和其他安全工具也允许将服务账户身份投影到 pod 中,这允许与这些外部平台进行身份验证。

在 OpenShift 中,服务账户还用于将一组安全权限与每个 pod 关联起来。OpenShift 用于创建专门的安全权限组合的对象称为安全上下文约束。在下一节中,我们将更详细地讨论安全上下文约束,以及 OpenShift 提供的其他重要增强功能,以补充基本的 Kubernetes。

OpenShift 增强功能

OpenShift 引入了几个新概念,用于简化开发和运维。OpenShift 特有的方法包括认证、项目、应用程序、安全上下文和镜像流。

认证

安全对于 OpenShift Kubernetes 平台至关重要。因此,所有用户必须对集群进行身份验证才能访问它。OpenShift 支持各种常见的认证方法,包括基本认证(用户名和密码)、OAuth 访问令牌和 X.509 客户端证书。OpenShift 提供了oc login命令用于执行身份验证,通过以下方式运行:

$ oc login

在基本认证使用情况下,当运行此命令时,用户将被要求输入 OpenShift 容器平台服务器的 URL 以及是否需要安全连接,然后用户将被要求输入他们的用户名和密码。此外,OpenShift 的可配置 OAuth 服务器允许用户将 OpenShift 身份与外部提供者(如 LDAP 服务器)集成。

项目

标准的 Kubernetes 提供了 命名空间 的概念,允许您为 Kubernetes 资源定义隔离。命名空间使集群资源可以在大量用户之间分割,通过它们所管理的作用域所产生的隔离,防止用户因命名冲突而意外使用其他人的资源。命名空间非常有用,OpenShift 已经调整了命名空间以用于应用程序分组。OpenShift 将 Kubernetes 命名空间添加特殊的标准注释列表,将其称为 项目。OpenShift 使用项目作为分组应用程序的机制。项目支持访问权限的概念。这使您可以添加一个或多个具有对项目访问权限的用户,并使用基于角色的访问控制设置不同用户在访问项目时具有的权限和能力。

使用 oc new-project 命令创建项目,并提供项目名称、描述和显示名称,如下所示:

$ oc new-project firstproject --description=”My first project” 
--display-name=”First Project”

OpenShift 通过使用 oc project 命令轻松切换项目。在这里,我们切换到名为 secondproject 的不同项目:

$ oc project secondproject

要查看您有权访问的项目列表,可以使用 oc get projects 命令:

$ oc get projects

关于项目使用的更多信息,请参阅 OpenShift 项目文档

应用程序

在使用基本的 Kubernetes 环境时,云原生应用程序开发人员需要执行的更繁琐的步骤之一是创建自己的容器镜像。通常情况下,这涉及查找合适的基础镜像,并创建一个 Dockerfile,其中包含将基础镜像与开发人员的代码组合以创建可由 Kubernetes 部署的装配图像所需的所有命令。OpenShift 引入了 应用程序构造,极大地简化了在 Kubernetes 环境中创建、部署和运行容器镜像的过程。

应用程序是使用 oc new-app 命令创建的。该命令支持多种选项,可以多种方式构建容器镜像。例如,使用 new-app 命令,应用程序镜像可以从本地或远程 Git 存储库构建,或者可以从 Docker Hub 或私有镜像注册表拉取应用程序镜像。此外,new-app 命令支持通过检查存储库的根目录来确定创建应用程序镜像的正确方法。例如,OpenShift 的 new-app 命令将在您的存储库根目录中查找 JenkinsFile,如果找到此文件,将使用它来创建应用程序镜像。此外,如果 new-app 命令没有找到 JenkinsFile,它将尝试通过查看存储库中的文件来检测您的应用程序使用的编程语言。如果能够确定所使用的编程语言,new-app 命令将找到适合该编程语言的基础镜像,并将用它来构建您的应用程序镜像。

以下示例说明了如何使用 oc new-app 命令从 OpenShift 示例 ruby hello world 应用程序创建新的应用程序镜像:

$ oc new-app https://github.com/openshift/ruby-hello-world.git

这个命令将把应用程序创建为最近为用户选择的 OpenShift 项目的一部分。有关 new-app 命令支持的应用程序镜像创建选项的更多信息,请参阅OpenShift 应用程序创建文档

安全上下文约束

在 OpenShift 中,安全始终是首要考虑的。但是增强的安全性可能会带来额外的复杂性和麻烦。如果使用了增强的安全性而容器没有获得适当的安全选项,则会失败。如果放宽安全性以避免问题,则可能导致漏洞。为了使用户能够在减少麻烦的同时利用增强的安全性,OpenShift 包括了一种称为安全上下文约束的安全结构。

安全上下文约束标识出一组安全特权,保证 pod 的容器在执行之前知道将获得什么安全特权。以下是安全上下文约束提供的常见安全特权选项列表:

允许 pod 运行特权容器

安全上下文约束可以声明 pod 是否被允许运行特权容器或者仅能运行非特权容器。

需要安全增强型 Linux (SELinux)

SELinux 是 Linux 的安全架构,为系统上的应用程序、进程和文件定义访问控制。SELinux 提供了比标准 Linux 更多的额外保护。安全上下文约束提供了 MustRunAs 属性值,用于声明 pod 的容器是否必须运行 SELinux,以及 RunAsAny 属性值,用于声明 pod 的容器是否可以运行标准 Linux 或 SELinux。

将 pod 的容器作为特定用户或非 root 运行

以 root 身份运行的容器比以非 root 身份运行的容器具有更大的漏洞足迹。安全上下文约束提供了 MustRunAsNonRoot 属性值来表示不允许 Pod 的容器以 root 身份运行。此外,安全上下文约束使用了 RunAsAny 属性值,允许 pod 的容器以 root 或非 root 用户运行。最后,安全上下文约束管理了 MustRunAsRange 属性值,允许 pod 的容器在用户 ID 在特定用户 ID 范围内时运行。

允许 pod 的容器访问文件系统组块存储

安全上下文约束可用于限制 pod 的容器访问的块存储。通过文件系统组标识符标识块存储部分。安全上下文约束提供了 RunAsAny 属性值,允许 pod 的容器访问任何块存储的文件系统组,并提供了 MustRunAs 属性值,用于表示 pod 的块存储必须在安全上下文约束中列出的文件系统组 ID 范围内。

OpenShift 包括几个内置安全上下文约束配置文件,可供重复使用。要查看您有权访问的项目列表,可以使用 oc get scc 命令:

$ oc get scc

NAME             AGE
anyuid           182d
hostaccess       182d
hostmount-anyuid 182d
hostnetwork      182d
node-exporter    182d
nonroot          182d
privileged       182d
restricted       182d

如上所示,OpenShift 为常见场景贡献了安全上下文约束配置文件,如特权、受限或以非 root 运行。要查看安全约束配置文件的所有单独功能设置,请使用 oc describe scc 命令并传递您想要更多详细信息的配置文件名称。例如,如果您想要更多了解特权约束配置文件的强大功能,可以调用 oc describe scc 命令如下:

$ oc describe scc privileged

运行此命令将列出与此配置文件关联的大量约束属性。以下是一些较为有趣的内容:

Settings:
 Allow Privileged: true
 Allow Privilege Escalation: true
 Default Add Capabilities: <none>
 Required Drop Capabilities: <none>
 Allowed Capabilities: *
 Allowed Seccomp Profiles: *
 Allowed Volume Types: *
 Allowed Flexvolumes: <all>
 Allowed Unsafe Sysctls: *
 Forbidden Sysctls: <none>
 Allow Host Network: true
 Allow Host Ports: true
 Allow Host PID: true
 Allow Host IPC: true
 Read Only Root Filesystem: false
 Run As User Strategy: RunAsAny
 SELinux Context Strategy: RunAsAny
 FSGroup Strategy: RunAsAny
 Supplemental Groups Strategy: RunAsAny

为了比较目的,我们可以在受限制的配置文件中运行相同的命令。如下输出所示,约束属性值比特权配置文件中的要严格得多:

$ oc describe scc restricted

Settings:
 Allow Privileged: false
 Allow Privilege Escalation: true
 Default Add Capabilities: <none>
 Required Drop Capabilities: KILL,MKNOD,SETUID,SETGID
 Allowed Capabilities: <none>
 Allowed Seccomp Profiles: <none>
 Allowed Volume Types: configMap,downwardAPI,emptyDir,persistentVolumeClaim,
projected,secret
 Allowed Flexvolumes: <all>
 Allowed Unsafe Sysctls: <none>
 Forbidden Sysctls: <none>
 Allow Host Network: false
 Allow Host Ports: false
 Allow Host PID: false
 Allow Host IPC: false
 Read Only Root Filesystem: false
 Run As User Strategy: MustRunAsRange
 SELinux Context Strategy: MustRunAs
 FSGroup Strategy: MustRunAs
 Supplemental Groups Strategy: RunAsAny

这里的关键点在于安全上下文约束配置文件能够组织和封装大量的能力属性,并确保在允许 Pod 执行之前满足所有这些属性。这减少了设置能力属性不当的机会,减少了由于安全设置不正确而导致的意外 Pod 失败的可能性。

安全上下文约束配置文件通过使用 Kubernetes 服务账号对象与 Pod 相关联。有关使用安全上下文约束的更多信息,请参阅OpenShift 安全上下文约束文档

镜像流

在部署云原生应用程序的关键步骤之一是从仓库中检索正确的容器应用程序镜像。在生产环境中,此检索过程可能存在几个潜在问题。首先,容器镜像是通过标记标识符检索的,但可能会发生容器镜像被覆盖的情况,因此标记引用的镜像可能会发生变化。如果这种变化未被注意到,可能会导致部署的云原生应用程序中引入意外错误。其次,在生产环境中,镜像检索过程还需要支持自动化构建和部署,而许多镜像仓库在支持此自动化方面的能力有限。第三,在某些情况下,容器镜像需要具有多个标签与之关联,因为在不同环境中,容器镜像用于不同目的。不幸的是,许多镜像仓库不支持将多个标签与容器应用程序镜像关联的能力。

为了解决所有这些问题,OpenShift 引入了镜像流的概念。^(5) 镜像流旨在为标记的镜像提供更稳定的指针。镜像流维护了一个 SHA-256 安全哈希函数,指向其所指向的镜像,以确保镜像不会被错误地更改。镜像流还支持为镜像提供多个标记,以更好地支持在多个环境中使用它们。此外,镜像流包括触发器,当更新镜像流时可以自动启动构建和部署。此外,镜像流不仅可以引用外部仓库中的容器镜像,还可以安排定期重新导入外部容器镜像,以确保始终具有最新更新的容器镜像副本。

创建和更新镜像流相对来说是比较简单的。使用oc import-image命令来创建镜像流。在下面的示例中,使用oc import-image命令创建了一个名为nginx的初始镜像流,其导入的镜像流标签的值为1.12

$ oc import-image nginx:1.12 --from=centos/nginx-112-centos7 --confirm

如本例所示,被导入到 nginx 图像流中的初始容器图像是位于 centos/nginx-112-centos7 的图像。confirm 选项指示,如果尚不存在,则应创建图像流。

创建图像流后,我们可以使用 oc describe 命令来查看它。在以下示例中,is 值是输入流资源的简称。我们要描述的特定输入流是名为 nginx 的输入流:

$ oc describe is/nginx

此命令的输出如下所示:

Name: nginx
Namespace: default
Created: 52 seconds ago
Labels: <none>
Annotations: openshift.io/image.dockerRepositoryCheck=2020-06-12T20:16:15Z
Image Repository: default-route-openshift-image-registry.apps-
crc.testing/default/nginx
Image Lookup: local=false
Unique Images: 1
Tags: 1

1.12
 tagged from centos/nginx-112-centos7

*centos/nginx-112-
centos7@sha256:af171c38298e64664a9f999194480ce7e392858e773904df22f7585a1731ad0d

我们可以通过使用 oc tag 命令为此图像添加额外的标签。我们通过以下方式向现有的 nginx:1.12 标签添加一个 nginx:latest 标签:

$ oc tag nginx:1.12 nginx:latest

最后,我们可以通过调用 oc tag 命令,从外部仓库标记一个图像,并安排定期重新导入该图像。如下例所示,我们引用外部仓库中的图像,并将其与图像流标签关联,然后添加定期更新选项以表示应定期更新该标签:

$ oc tag docker.io/nginx:1.14 nginx:1.14 --scheduled

有关图像流使用的更多信息,请参阅 管理图像流文档

Kubernetes 和 OpenShift 高级主题

当在生产环境中运行 Kubernetes 或 OpenShift 时,经常会使用几个高级概念。在本节中,我们讨论这些高级主题,包括 Webhooks、准入控制器、基于角色的访问控制和运算符。

Webhooks

Webhook 是一个 HTTP 回调。基本上,Webhook 在发生有趣事件时允许向外部实体推送信息。通常,使用 HTTP Post 操作推送事件信息,并且事件信息最常以 JSON 负载表示。在 Kubernetes 中,Webhook 用于各种与安全相关的操作。例如,Kubernetes 可以使用 Webhook 查询外部服务,以确定用户是否具有执行特定操作的正确权限。

Webhook 在 OpenShift 中也被用作 触发构建的机制。使用 Webhook,您可以配置 GitHub 仓库,在仓库发生更改时发送警报。此警报可用于启动新构建,并在构建成功后执行部署。

Webhook 在 Kubernetes 的准入控制器中也被大量使用,下一节将对其进行描述。有关 Kubernetes 中 Webhook 使用的更多信息,请参阅 Kubernetes 文档中的 Webhook 模式

准入控制器

保持 Kubernetes 平台安全的关键是防止可能造成损害的请求。准入控制器是 Kubernetes 使用的一种机制,用于保护平台免受有害请求的影响。在某些情况下,准入控制器会阻止请求创建 Kubernetes 对象。在其他情况下,准入控制器会允许请求被处理,但会修改请求以使其更安全。例如,如果收到一个启动 Pod 的请求,并且请求没有指定 Pod 应以特权模式还是非特权模式启动,准入控制器可以修改请求,以确保 Pod 以非特权模式启动。

在 kube-controller-manager 中内嵌了多个准入控制器,默认情况下很多准入控制器都是启用的,以确保 Kubernetes 平台的安全性。然而,在某些情况下,管理员需要超出这些内置准入控制器的限制。Kubernetes 允许管理员通过注册 Webhook 来添加额外的准入控制器,以处理 Kubernetes 对象上的请求。我们将在第三章详细讨论准入控制器。

基于角色的访问控制

Kubernetes 的授权功能已集成到平台中。Kubernetes 授权使用基于角色的访问控制(RBAC)模型,并提供一个功能齐全的授权平台,允许操作员通过 Kubernetes 对象 ClusterRoleRole 定义各种角色,并通过 ClusterRoleBindingRoleBinding 将其绑定到用户和组。可以把 RBAC 想象成在文件系统上设置权限的一种方式,但在 Kubernetes 的情况下,它是在 Kubernetes 对象模型上设置权限。我们将在第四章详细讨论如何使用 RBAC 以及如何在其周围构建最佳的多租户模型。

操作员

Kubernetes 具有内置的抽象层,例如部署(deployments),非常适合无状态应用程序。此外,Kubernetes 基于控制循环的优雅设计使其能够支持声明式编程模型,并且在大规模环境中即使故障频发,平台仍能健壮执行。

为了支持复杂的有状态应用程序,Kubernetes 需要一个可扩展的模型,使用户能够添加自定义资源并执行这些资源的生命周期管理。此外,理想情况下,这个可扩展模型还应支持 Kubernetes 平台内广泛使用的控制循环架构。Kubernetes 包含操作员模式,为满足所有这些需求的自定义资源提供了一个可扩展模型。

运算符支持创建自定义资源。这意味着您可以通过创建自定义资源定义在 Kubernetes 中定义新的资源类型,并且这些新资源可以像任何标准 Kubernetes 资源一样存储在 Kubernetes etcd 数据库中。此外,您还可以为您的资源创建自定义控制器,该控制器执行与标准 Kubernetes 控制器相同类型的控制循环行为。自定义控制器可以监视您的有状态应用程序的实际状态,将其与期望状态进行比较,然后采取行动尝试实现应用程序的期望状态。例如,假设您为特殊类型的数据库创建了一个操作符,这是一个有状态的应用程序。操作符及其控制器可以确保运行的数据库副本数量与所需的副本数量相匹配。此外,由于操作符具有自定义控制器,因此可以将用于启动新数据库副本或更新现有数据库副本的任何自定义生命周期管理代码添加到控制器中。

操作符模式设计良好,其关键优势之一是其无缝性。与操作符关联的自定义资源通过 kubectl 命令行工具进行管理,在管理角度上看,与标准 Kubernetes 资源几乎没有区别。为了简化操作符的创建,存在操作符软件开发工具包,可以生成所需的自定义资源定义以及运行操作符控制循环所需的大部分控制器代码。由于操作符框架的清晰架构设计以及丰富的工具支持,使用操作符作为添加有状态应用程序的手段继续增长其流行度。现在有一个 Operator Hub,其中托管了大量用于管理各种 Kubernetes 平台应用程序的现有和可重复使用的操作符。我们将在 第七章 中更详细地讨论操作符及其在 Kubernetes 中的使用情况。

总结

在本章中,我们涵盖了广泛的主题,为您提供了 Kubernetes 和 OpenShift 的广泛基础和坚实介绍。我们涉及了一些对于生产环境中运行至关重要的主题,并且我们将在本书的后续章节中更详细地探讨这些主题。此外,本章还帮助说明了 Kubernetes 和 OpenShift 生态系统如何成熟为提供大量企业级功能和灵活性的平台。在 第三章 中,我们涵盖了一个关键的生产主题:在生产环境中运行时 Kubernetes 资源的高级管理。

^(1) Lantao Liu 和 Mike Brown,"Kubernetes Containerd Integration Goes GA," Kubernetes 博客(2018 年 5 月 24 日),https://oreil.ly/SlHmh

^(2) Brendan Burns 等人,"Borg、Omega 和 Kubernetes: 从三种容器管理系统的十年经验中学到的教训," ACM Queue 14 (2016): 70–93,http://bit.ly/2vIrL4S

^(3) Tomas Pizarro Moreno,"在 OpenShift 上运行非根容器," Bitnami 工程部(2017 年 10 月 27 日),https://oreil.ly/pxSGf

^(4) OpenShift 认证文档 提供了更多关于支持的认证方法的详细信息。

^(5) 镜像流文档 提供了更多信息。

^(6) Maciej Szulik,"如何在 Kubernetes 中使用 OpenShift Image Streams 简化容器镜像管理," 红帽 OpenShift 博客(2017 年 3 月 23 日),https://oreil.ly/JEV4u

^(7) Wikipedia 提供了 Webhooks 概述

第二章:使用 OpenShift 和 Kubernetes 入门

在本章中,我们涵盖了多个主题,这些主题提供了对 Kubernetes 和 OpenShift 的基础理解。我们首先概述了 Kubernetes 的架构,然后描述了几种部署选项,这些选项将使您能够同时启动基本的 Kubernetes 环境和 OpenShift 环境。接下来,我们介绍了用于与 Kubernetes 和 OpenShift 交互的命令行工具kubectloc。然后,我们简要回顾了 Pods、Deployments 和 Service Accounts 等基本 Kubernetes 概念。在本章的后半部分,我们介绍了 OpenShift 相比传统 Kubernetes 提供的几个增强概念。最后,我们讨论了在生产环境中运行 Kubernetes 或 OpenShift 时常用的更高级的主题。

Kubernetes 架构

Kubernetes 架构在高层次上相对简单。它由主节点和一组工作节点组成。节点可以是物理服务器或虚拟机。Kubernetes 环境的用户可以使用 CLI(kubectl)、API 或 GUI 与主节点交互。主节点负责在工作节点上安排工作。在 Kubernetes 中,调度的工作单元称为Pod,一个 Pod 可以包含一个或多个容器。存在于主节点上的主要组件包括kube-apiserverkube-schedulerkube-controller-manageretcd

kube-apiserver

kube-apiserver 提供了用于操作 Kubernetes 环境的 Kubernetes API。

kube-scheduler

kube-scheduler 组件负责选择应在其上创建 Pod 的节点。

kube-controller-manager

Kubernetes 提供了几个支持 Pod 副本、管理节点等高级抽象。每个抽象都是通过控制器组件实现的,我们稍后在本章中描述。kube-controller-manager 负责管理和运行控制器组件。

etcd

etcd 组件是一个分布式键值存储,是 Kubernetes 控制平面的主要数据存储。该组件存储并复制您的 Kubernetes 环境的所有关键信息状态。etcd 的关键特性是其支持观察功能。观察是一种远程过程调用(RPC)机制,允许在键值的创建、更新或删除操作时进行回调函数。Kubernetes 的优秀性能和可伸缩性特性依赖于 etcd 作为高效的数据存储机制。

工作节点负责运行在其上调度的 Pod。存在于工作节点上的主要 Kubernetes 组件包括kubeletkube-proxy容器运行时

kubelet

kubelet 负责确保每个 Pod 中的容器被创建并保持运行状态。当 kubelet 发现容器异常终止或者未通过用户定义的其他健康检查时,它将重新启动容器。

kube-proxy

Kubernetes 的关键优势之一是它为容器实现的网络支持。kube-proxy 组件提供连接转发、负载均衡以及将单个 IP 地址映射到 Pod 的网络支持。kube-proxy 的独特之处在于,它提供了分布式负载均衡能力,这对 Kubernetes 的高可用架构至关重要。

容器运行时

容器运行时组件负责运行每个 Pod 中存在的容器。Kubernetes 支持多种容器运行时环境选项,包括 Docker、rkt、CRI-O 和 containerd。^(1)

图 2-1 展示了 Kubernetes 架构的图形表示,包括一个主节点和两个工作节点。

图 2-1. Kubernetes 架构的图形表示

如同 图 2-1 所示,用户可以通过 GUI 或者 kubectl CLI 与 Kubernetes API 服务器进行交互。这两者都使用 Kubernetes API 与运行在 Kubernetes 主节点上的 kube-apiserver 进行交互。Kubernetes 主节点的 kube-scheduler 组件负责将 Pod 调度到不同的工作节点上。每个 Pod 包含一个或多个容器,并分配有自己的 IP 地址。在许多实际应用中,Kubernetes 部署同一个 Pod 的多个副本以提高可扩展性并确保高可用性。Pod A1 和 A2 是不同 IP 地址的 Pod 副本。同样,Pod B1 和 B2 也是同一个 Pod 的副本。同一个 Pod 中的容器允许使用标准的进程间通信(IPC)机制进行通信。

在下一节中,我们将介绍几种启动和运行 OpenShift 和 Kubernetes 环境的方法。

Kubernetes 和 OpenShift 的部署选项

Kubernetes 和 OpenShift 都已经达到了令人难以置信的流行水平。因此,现在有多种选择可以部署基本的 Kubernetes 或者 Red Hat 的 OpenShift Kubernetes 发行版。在接下来的几节中,我们将总结当前可用的不同部署选项,包括 Red Hat 的 CodeReady Containers、IBM Cloud 和多种 OpenShift 部署选项。

Red Hat 的 CodeReady Containers

Red Hat 提供了一个名为 CodeReady Containers 的最小预配置 OpenShift 4 版本集群,您可以在笔记本电脑或台式计算机上运行。CodeReady OpenShift 环境旨在用于开发和测试目的。CodeReady Containers 在您的本地机器上提供了一个功能完整的云开发环境,并包含您开发基于容器的应用程序所需的所有工具。

IBM Cloud

IBM Cloud 提供用户选择传统的 Kubernetes 集群或 Red Hat OpenShift 集群的选项。IBM Cloud 的 Kubernetes 提供的是作为服务的云服务,提供标准的 Kubernetes 功能,包括智能调度、自愈能力、水平扩展、服务发现和负载平衡、自动发布和回滚以及秘密和配置管理。此外,IBM Cloud 的 Kubernetes 服务包括集群部署、更新和扩展的自动化操作,专家级安全性,优化的配置以及与 IBM Cloud 基础设施平台的无缝集成。它在 6 个区域和 35 个数据中心提供高可用的多区域集群。IBM Cloud 提供一个免费的 Kubernetes 集群,包括超过 40 个免费服务以及按使用量付费的选项。

IBM Cloud 还为用户提供高可用的完全托管的 OpenShift 集群。IBM 的 OpenShift 提供了独特的安全性和生产力功能,旨在减少更新、扩展和配置的大量时间。此外,IBM Cloud 的 OpenShift 具备处理意外涌入并保护免受可能导致财务和生产力损失的攻击的韧性。除按使用量付费和订阅选项外,IBM Cloud 还提供一个免费的预配置 OpenShift 4.3 环境,可免费使用四小时。

OpenShift 部署选项

Getting Started with OpenShift 网站上定义了几种 OpenShift 的部署选项。所描述的选项包括在笔记本电脑上安装 OpenShift 4 版本,在数据中心或公共云中部署,或让 Red Hat 为您管理 OpenShift。此外,Red Hat 还提供了实验性学习和实验的 Playground OpenShift 环境以及操作 OpenShift 的实际教程。Figure 2-2 展示了可用的多样化 OpenShift 部署选项。

图 2-2. 在 Get Started with OpenShift 上提供的 OpenShift 部署选项

在下一节中,我们将描述与这些平台交互所使用的命令行工具。

Kubernetes 和 OpenShift 命令行工具

正如在第一章中讨论的那样,OpenShift 提供了一个符合 100%标准的 Kubernetes 平台,并通过各种工具和功能增强了开发人员和 IT 运营的生产力。在本节中,我们首先介绍了kubectloc,这两个是用于与 Kubernetes 和 OpenShift 交互的标准命令行工具。我们介绍了 OpenShift 用来表示其对传统 Kubernetes 的增强的几个概念,包括身份验证、项目、应用程序、安全上下文和镜像流。

在介绍了 Kubernetes 的一些核心概念后,接下来的章节以 YAML 文件的形式给出了几个示例。对于所有的 Kubernetes 环境,可以使用标准的 Kubernetes 命令行工具kubectl来运行所包含的样本。许多 Kubernetes 环境,包括本章节早前提到的那些,描述了如何安装kubectl。一旦你的 Kubernetes 环境准备就绪并且安装了kubectl,可以通过首先将 YAML 保存到文件(例如kubesample1.yaml),然后运行以下kubectl命令来运行以下章节中的所有 YAML 文件样本:

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: web
    image: icr.io/sample/web:v1
    env:
    resources:
      requests:
        memory: "50Mi"
        cpu: "150m"
        ephemeral-storage: “50Mi”
  - name: logging
    image: icr.io/sample/logging:v2
    resources:
      requests:
        memory: "40Mi"
        cpu: "100m"
        ephemeral-storage: “200Mi”

如前所述,OpenShift 对 Kubernetes 的分发增加了几个新的增强功能和能力。OpenShift 通过扩展kubectl的功能来提供对这些功能的访问。为了明确 OpenShift 版本的kubectl具有扩展功能,OpenShift 将其版本的kubectl重命名为一个称为oc的新命令行工具。因此,以下命令等同于之前的kubectl命令:

resources:
   limits:
     memory: "200Mi"
   requests:
     memory: "200Mi"

除了所有kubectl命令的一对一匹配支持外,oc还添加了用于管理用户和组的角色和角色绑定等管理功能的命令。

欲了解有关 OpenShift oc CLI 可用命令的详细信息,请参阅OpenShift 命令行文档

Kubernetes 基础知识

Kubernetes 有几个特定于其容器管理模型的概念。在本节中,我们简要回顾了关键的 Kubernetes 概念,包括 Pods、Deployments 和 Service Accounts。

什么是 Pod?

因为 Kubernetes 支持容器的管理和编排,你可能会认为 Kubernetes 支持的最小部署单元应该是一个容器。然而,Kubernetes 的设计者从经验中学到(参见 2),将最小部署单元定义为能够容纳多个容器的东西更为优化。在 Kubernetes 中,这个最小的部署单元称为Pod。一个 Pod 可以容纳一个或多个应用容器。处于同一个 Pod 中的应用容器具有以下优势:

  • 它们共享一个 IP 地址和端口空间。

  • 它们共享相同的主机名

  • 它们可以使用本地 IPC 进行通信。

相反,运行在单独 pod 中的应用程序容器保证具有不同的 IP 地址和不同的主机名。实际上,即使它们最终在同一节点上运行,不同 pod 中的容器应被视为运行在不同的服务器上。

Kubernetes 提供了一系列强大的功能,使得 pod 的使用变得非常简单:

易于使用的 pod 管理 API

Kubernetes 提供 kubectl CLI,支持对 pod 进行各种操作,包括创建、查看、删除、更新、交互和扩展 pod。

支持文件复制

Kubernetes 可以非常方便地在本地主机和集群中运行的 pod 之间传输文件。

从本地主机连接到 pod 的网络

在许多情况下,您希望从本地主机到集群中运行的 pod 的网络连接。Kubernetes 支持端口转发,通过安全隧道将本地主机上的网络端口连接到集群中运行的 pod 上的端口。这是一个极好的功能,有助于在不公开暴露应用程序和服务的情况下进行调试。

支持卷存储

Kubernetes pod 支持将远程网络存储卷附加到使 pod 中的容器能够访问持久存储的 pod,这些存储可以在 pod 和最初使用存储的容器的生命周期之后继续存在。

基于探针的健康检查支持

Kubernetes 提供了探针形式的健康检查,以确保容器的主要进程仍在运行。此外,Kubernetes 还提供了活跃性检查,以确保容器实际上正在运行并能够执行真正的工作。借助这种健康检查支持,Kubernetes 可以识别您的容器是否已崩溃或变得无法正常工作,并代表您重新启动它们。

我如何描述我的 pod 内容?

Pod 和 Kubernetes 管理的所有其他资源都使用 YAML 文件进行描述。以下是描述基本 pod 资源的简单 YAML 文件示例:

resources:
   limits:
     memory: "200Mi"
   requests:
     memory: "100Mi"

这个 YAML 文件包含以下字段和部分:

apiVersion

此字段用于声明正在使用的 Kubernetes API 模式的版本。Kubernetes 在功能和功能上持续快速增长。通过支持其 API 的多个版本,它管理了由于功能增长而产生的复杂性。通过设置 apiVersion 字段,您可以控制您的资源使用的 API 版本。

kind

使用 kind 字段来识别 YAML 文件描述的资源类型。在上述示例中,YAML 文件声明正在描述一个 Pod 对象。

metadata

metadata 部分包含有关 YAML 定义的资源的信息。在上述示例中,metadata 包含一个名称字段,声明了此 pod 的名称。metadata 部分可以包含其他类型的标识信息,例如标签和注释。我们将在 “部署” 中更详细地描述这些内容。

spec

spec 部分提供了此资源的期望状态的规范。如示例所示,此 pod 的期望状态是具有名称为 nginx 的容器,该容器是从标识为 nginx:1.7.9 的 Docker 镜像构建的。该容器共享所在 pod 的 IP 地址,并且 containerPort 字段用于为此容器分配一个网络端口(在本例中为 80),以便它可以用于发送和接收网络流量。

提示

apply 命令将创建一个资源或更新任何现有的匹配资源。还有一个支持的 create 命令,它将假定 YAML 文档描述的资源尚不存在。通常可以在任何使用 create 的地方使用 apply。在某些情况下,例如特殊的 generateName 属性,只支持 create

要运行前面的示例,请将文件保存为 pod.yaml。现在可以通过以下方式运行它:

resources: {}

运行此命令后,您应该看到以下输出:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   run: cpu-use
 name: cpu-use
spec:
 replicas: 1
 selector:
   matchLabels:
     run: cpu-use
 template:
   metadata:
     labels:
       run: cpu-use
   spec:
     containers:
     - command:
       - stress
       - --cpu
       - "5"
       image: kitch/stress
       imagePullPolicy: Always
       name: cpu-use
       resources:
         limits:
           cpu: 1000m
         requests:
           cpu: 200m
     nodeSelector:
       kubernetes.io/hostname: "<worker node>"

要确认您的 pod 是否真正在运行,请使用 kubectl get pods 命令进行验证:

$ kubectl top pods
NAME                      CPU(cores)   MEMORY(bytes)
cpu-use-ffd7fd8f8-b2wds   998m         0Mi
cpu-use-ffd7fd8f8-cw6lz   999m         0Mi
cpu-use-ffd7fd8f8-wcn2x   999m         0Mi

运行此命令后,您应该看到类似以下的输出:

$ kubectl top pods
NAME                       CPU(cores)   MEMORY(bytes)
cpu-use-575444f9c6-2fctp   264m         0Mi
cpu-use-575444f9c6-4x2w6   264m         0Mi
cpu-use-575444f9c6-89q8z   263m         0Mi
cpu-use-575444f9c6-bw6fl   265m         0Mi
cpu-use-575444f9c6-dq4pn   265m         0Mi
cpu-use-575444f9c6-g968p   265m         0Mi
cpu-use-575444f9c6-jmpwl   265m         0Mi
cpu-use-575444f9c6-ktmbp   264m         0Mi
cpu-use-575444f9c6-lmjlz   265m         0Mi
cpu-use-575444f9c6-rfvx6   264m         0Mi
cpu-use-575444f9c6-rg77n   264m         0Mi
cpu-use-575444f9c6-skt25   263m         0Mi
cpu-use-575444f9c6-srhhf   264m         0Mi
cpu-use-575444f9c6-svz9z   264m         0Mi

当 pod 运行时,您还可以使用 logs 命令查看 pod 内运行的进程的日志(如果有多个容器,请使用 -c 选项选择要查看的特定容器):

$ kubectl top pods
NAME                         CPU(cores)   MEMORY(bytes)
cpu-noise-6575cc6657-2qhl8   3724m        0Mi

如果您需要调试正在运行的容器,可以使用以下命令创建一个在容器内部运行的交互式 shell:

$ kubectl top pods
NAME                         CPU(cores)   MEMORY(bytes)
cpu-noise-6575cc6657-2qhl8   2491m        0Mi
cpu-use-679cbc8b6d-95bpk     999m         0Mi

此命令指示 Kubernetes 为名为 nginx 的 pod 运行一个交互式 shell。因为此 pod 只有一个容器,Kubernetes 知道您要连接的容器即使您没有指定容器名称。在运行时访问容器以进行交互修改通常被认为是一种不良实践。然而,在学习或调试应用程序在部署到生产环境之前时,交互式 shell 可能会很有用。运行上述命令后,您可以与容器的运行时环境进行交互,如下所示:

$ kubectl top pods
NAME                         CPU(cores)   MEMORY(bytes)
cpu-noise-6575cc6657-2qhl8   7m           0Mi
cpu-use-679cbc8b6d-6nnkp     850m         0Mi
cpu-use-679cbc8b6d-n6gwp     844m         0Mi
cpu-use-679cbc8b6d-rl7vv     863m         0Mi
cpu-use-679cbc8b6d-z7hhb     865m         0Mi

如果您的 pod 中有多个容器,您需要在 kubectl exec 命令中同时包含容器名称。为此,请使用 -c 选项并在 pod 名称之外包含容器名称。以下是一个示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: memory-use
  name: memory-use
spec:
  replicas: 1
  selector:
    matchLabels:
      app: memory-use
  template:
    metadata:
      labels:
        app: memory-use
    spec:
      containers:
      - command:
        - stress
        - --cpu
        - "1"
        - --vm
        - "5"
        - --vm-keep
        image: kitch/stress
        imagePullPolicy: Always
        name: memory-use
        resources:
          limits:
            cpu: 10m
            memory: 1290Mi
          requests:
            cpu: 10m
            memory: 1290Mi
      nodeSelector:
        kubernetes.io/hostname: "10.65.59.69"

要删除刚刚创建的 pod,请运行以下命令:

$ kubectl top pods
NAME                          CPU(cores)   MEMORY(bytes)
memory-use-66b45dbd56-j4jj7   3m           943Mi
memory-use-774b6549db-bqpj5   9m           1280Mi
memory-use-774b6549db-k9f78   9m           1280Mi
memory-use-774b6549db-qmq62   9m           1280Mi
memory-use-774b6549db-vtm96   9m           1280Mi
memory-use-774b6549db-wwj2r   9m           1280Mi

您应该看到以下确认 pod 已被删除:

$ kubectl top pods
NAME                            READY   STATUS    RESTARTS   AGE
memory-noise-85df694f5d-2szg2   0/1     Evicted   0          12m
memory-noise-85df694f5d-598mz   1/1     Running   1          3m49s
memory-noise-85df694f5d-7njvb   1/1     Running   1          7m23s
memory-noise-85df694f5d-7pjjc   0/1     Evicted   0          12m
memory-noise-85df694f5d-8vl8h   0/1     Evicted   0          12m
memory-noise-85df694f5d-7njvb   0/1     OOMKilled 1          8m23s
memory-use-774b6549db-bqpj5     1/1     Running   0          59m
memory-use-774b6549db-k9f78     1/1     Running   0          62m
memory-use-774b6549db-qmq62     1/1     Running   0          62m
memory-use-774b6549db-vtm96     1/1     Running   0          62m
memory-use-774b6549db-wwj2r     1/1     Running   0          62m

在使用 Kubernetes 时,您可以预期在集群中运行大量的 Pod。在下一节中,我们将描述如何使用标签和注释来帮助您跟踪和识别您的 Pod。

部署

部署 是 Kubernetes 的高级抽象,不仅允许您控制实例化的 Pod 副本数目,还支持滚动部署新版本的 Pod。部署是可配置的,以便利用额外的资源进行更快的无停机滚动,或者执行能够进行金丝雀测试的更慢滚动。较慢的滚动部署的优势在于,它可以通过向一小部分用户发布软件来降低风险,并验证新软件,从而确保新版本的应用程序是稳定的。部署依赖于 ReplicaSet 资源来管理 Pod 副本,然后在此基础上添加 Pod 版本管理支持。部署还使新部署的 Pod 版本可以回滚到先前的版本,如果新版本的 Pod 存在问题。此外,部署支持两种升级 Pod 的选项,RecreateRollingUpdate

Recreate

Recreate Pod 升级选项非常直接。在这种方法中,部署资源修改其关联的 ReplicaSet 以指向 Pod 的新版本。然后,它终止所有的 Pod。然后 ReplicaSet 察觉到所有的 Pod 都已终止,因此生成新的 Pod 以确保所需副本数目在运行中。Recreate 方法通常会导致您的 Pod 应用在一段时间内无法访问,因此不推荐需要始终可用的应用程序使用此方法。

RollingUpdate

Kubernetes 的部署资源还提供 RollingUpdate 选项。使用 RollingUpdate 选项时,您的 Pod 将逐步被新版本替换。这种方法导致旧版本和新版本的 Pod 同时运行,并因此避免在维护期间使您的 Pod 应用程序不可用。每个 Pod 的可用性会被测量,并用于通知 kube-proxy 和入口控制器哪些 Pod 副本可用于处理网络请求,以确保在更新过程中不会丢失任何请求。

以下是使用 RollingUpdate 选项的部署的示例 YAML 规范:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: hello
  name: hello
spec:
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - image: kitch/hello-app:1.0
        name: hello
        resources:
          requests:
            cpu: 20m
            memory: 50Mi
---
apiVersion: v1
kind: Service
metadata:
  labels:
    run: hello
  name: hello
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    run: hello

前述的部署示例包含了许多我们在ReplicaSet和 pod 中看到的特征。在其元数据中包含标签和注释。对于部署,具有deployment.kubernetes.io/revision作为键和1作为其值的注释提供了有关此部署内容的第一个修订版的信息。类似于ReplicaSet,部署声明了它提供的副本数量,并使用matchLabels字段声明用于标识其管理的 pod 的标签。

提示

标签匹配是 Kubernetes API 中非常常见的一个方面。如果需要组织或分类资源,请添加描述性标签作为轻量级元数据。您还可以使用 kubectl 中的-l选项查询或查找资源,例如kubectl getkubectl patch

ReplicaSet类似,部署既有用于部署的spec部分,也有一个嵌套的spec部分,位于template内部,用于描述由此部署管理的 pod 副本中包含的容器。

针对部署资源新添加并具体的字段是strategy字段及其子字段typeRollingUpdatetype字段用于声明正在使用的部署策略,目前可以设置为RecreateRollingUpdate

如果选择了RollingUpdate选项,则还需要设置maxUnavailablemaxSurge的子字段。这些选项的使用如下:

maxSurge

maxSurge RollingUpdate选项允许在升级过程中分配额外的资源。此选项的值可以设置为数字或百分比。举个简单的例子,假设一个部署支持三个副本,并且maxSurge设置为2。在这种情况下,在RollingUpdate期间将总共有五个副本可用。

在部署的高峰期,将有三个使用旧版本 pod 运行的副本和两个使用新版本 pod 运行的副本。在此时,需要终止一个旧版本 pod 副本,然后可以创建另一个新版本 pod 的副本。在此阶段,将会有总共五个副本,其中三个具有新修订版,两个具有旧版本 pod。最终,达到正确数量的具有新版本的 pod 副本可用后,可以终止两个具有旧版本的 pod 的副本。

maxUnavailable

RollingUpdate选项用于声明在更新期间可能不可用的部署副本 pod 的数量。可以将其设置为数字或百分比。

以下的 YAML 示例显示了一个已更新以启动滚动升级的部署:

$ kubectl autoscale deploy hello --min=1 --max=5 --cpu-percent=80
deployment.apps "hello" autoscaled

$ kubectl get hpa hello
NAME      REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS  
hello     Deployment/hello   0%/80%    1         5         1

请注意,已添加一个带有键为kubernetes.op/change-cause的新注释标签,其值表示容器中运行的 nginx 版本已更新。还请注意,容器在spec部分中使用的映像名称已更改为nginx:1.13.10。这个声明实际上是驱动由部署管理的 Pod 副本,在升级发生时现在具有容器映像的新版本。

为了演示部署的能力,让我们运行前两个示例。将第一个部署示例保存为deploymentset.yaml,第二个示例保存为deploymentset2.yaml。您现在可以通过执行以下操作来运行第一个部署示例:

$ kubectl top pods -l run=hello
NAME                     CPU(cores)   MEMORY(bytes)
hello-7b68c766c6-mgtdk   43m          6Mi

运行此命令后,您应该看到以下输出:

$ kubectl get hpa hello
NAME      REFERENCE          TARGETS    MINPODS   MAXPODS   REPLICAS  
hello     Deployment/hello   215%/80%   1         5         1        

要确认由部署管理的 Pod 副本实际上正在运行,请使用kubectl get pods命令进行验证:

$ kubectl get hpa hello
NAME      REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hello     Deployment/hello   86%/80%   1         5         3          10m

运行此命令后,您应该看到类似以下的输出:

$ kubectl get hpa hello
NAME      REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hello     Deployment/hello   62%/80%   1         5         4          15m

使用部署,我们有一个新命令叫做kubectl get deployments,它会提供有关部署状态的信息,当它们更新其映像时。按以下方式运行此命令:

$ kubectl get hpa hello
NAME      REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hello     Deployment/hello   0%/80%    1         5         1          45m

运行此命令后,您应该看到类似以下的输出:

apiVersion: poc.autoscaling.k8s.io/v1alpha1
kind: VerticalPodAutoscaler
metadata:
  name: hello-vpa
spec:
  selector:
    matchLabels:
      run: hello
  updatePolicy:
    updateMode: Auto

现在让事情变得有趣,让我们通过应用我们在deploymentset2.yaml中保存的第二个部署示例来更新部署中的映像。请注意,我们本可以直接更新我们在deploymentset.yaml中保存的原始 YAML 文件,而不是使用两个单独的文件。我们开始更新,执行以下操作:

$ kubectl top pods -l run=hello
NAME                     CPU(cores)   MEMORY(bytes)
hello-7b68c766c6-mgtdk   74m          6Mi

运行此命令后,您应该看到以下输出:

resources:
      requests:
        cpu: 80m
        memory: 50Mi

现在,当我们重新运行kubectl get deployments命令时,该命令会提供有关部署正在更新其映像的状态,我们将看到一个更加有趣的结果:

spec:
  containers:
  - command:
    - /cluster-proportional-autoscaler
    - --namespace=kube-system
    - --configmap=coredns-autoscaler
    - --target=Deployment/coredns
    - --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,
"preventSinglePointFailure":true}}
    - --logtostderr=true
    - --v=2

如输出所示,当前部署有三个正在运行的 Pod 副本。其中三个 Pod 副本已经更新到最新的 nginx 映像,两个副本可以处理请求。经过一段时间后,当滚动映像更新完成时,我们达到了希望的状态,即有三个更新后的 Pod 副本可用。我们可以通过重新运行kubectl get deployments命令来确认,查看输出现在与我们的期望状态匹配:

- /pod_nanny
    - --config-dir=/etc/config
    - --cpu=100m
    - --extra-cpu=1m
    - --memory=40Mi
    - --extra-memory=6Mi
    - --threshold=5
    - --deployment=metrics-server
    - --container=metrics-server
    - --poll-period=300000
    - --estimator=exponential
    - --use-metrics=true

要删除刚创建的部署,请运行以下命令:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false

您应该收到以下确认,部署已被删除:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: "pod-policy.example.com"
webhooks:
- name: "pod-policy.example.com"
  rules:
  - apiGroups:   [""]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    resources:   ["pods"]
    scope:       "Namespaced"
  clientConfig:
    service:
      namespace: "example-namespace"
      name: "example-service"
    caBundle: "xxxxxxxxxx"
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  timeoutSeconds: 5

部署还包括暂停部署、恢复部署和回滚映像更新的命令。如果您对新的映像部署有疑虑,这些命令将非常有帮助,或者如果确定正在部署的更新映像存在问题,需要回滚到先前版本。有关如何使用这些部署功能的更多信息,请参阅 Kubernetes Deployment documentation

在下一节中,我们将详细探讨在安全的 Kubernetes 生产环境(如 OpenShift)中运行先前示例所需的额外步骤。

在 OpenShift 上生产环境中运行 Pod 和 Deployment 示例

在先前章节中呈现的 Pod 和 Deployment 示例非常适合教学目的和在本地开发环境中运行。但是,在像 OpenShift 这样的高度安全的 Kubernetes 平台上运行生产环境时,需要考虑其他因素。首先,在之前示例中使用的 nginx 容器镜像被配置为以特权根用户身份运行。默认情况下,安全的生产 Kubernetes 平台(如 OpenShift)配置为不允许容器镜像以 root 用户身份运行。这是因为以 root 用户身份运行容器镜像会增加恶意代码对主机系统造成危害的风险。^(3) 为解决这个问题,我们将用一个不需要以特权根用户身份运行的镜像版本替换本章早期使用的 nginx 容器。Bitnami 的 nginx 容器镜像以非 root 容器形式运行,可以在生产 OpenShift 环境中使用。以下示例是我们先前创建的 pod.yaml 的更新版本,使用了 Bitnami 的非 root nginx 容器镜像:

[PRE27]

注意

请记住,所有资源要么是 集群范围 的,意味着集群中只能存在一个此类资源,要么是 命名空间范围 的,意味着这些资源与集群中的其他相似资源隔离开来。在 OpenShift 中,您还可能会看到术语 项目,这是在 Red Hat 与社区合作通用命名空间概念之前的旧术语。项目命名空间 是同义词,OpenShift 可以响应 get projectsget namespaces。您可以将命名空间想象成文件系统中的文件夹,用于分配给一组协作文件的用户。我们将在 “OpenShift Enhancements” 中详细讨论命名空间或项目。

我们之前的 Pod 和部署示例还存在另一个需要解决的问题,那就是它们创建时并没有通过创建一个特定于我们资源的 Kubernetes 命名空间来将我们的资源与其他资源隔离开来。相反,早期的示例将我们的资源放置在 Kubernetes 的默认命名空间中。为了鼓励应用程序的正确隔离,安全的生产环境 Kubernetes 平台如 OpenShift 将强制执行,要求您的 Kubernetes 资源不要在默认命名空间中创建,而是在提供所需隔离的用户定义命名空间中创建。为了创建一个正确配置的命名空间,OpenShift 提供了oc new-project命令。OpenShift 的项目功能在“OpenShift 增强”中有更详细的描述。然而,现在,我们将使用oc new-project命令创建一个名为book的新项目,这将为我们的 Pod 示例提供所需的隔离。我们通过运行以下命令来创建我们的新项目:

[PRE28]

然后,我们可以使用oc apply -f命令,传入我们更新的pod.yaml,并使用-n选项声明我们要在book命名空间中创建我们的资源:

[PRE29]

现在我们使用了非 root 容器镜像,并且不再使用默认命名空间,我们的 Pod 示例将被 OpenShift 允许在生产环境中运行。我们可以通过使用oc get pods命令来确认这一点:

[PRE30]

我们可以通过使用oc delete pod命令来清理和删除 Pod 示例:

[PRE31]

我们可以将用于 Pod 示例的相同技术应用于部署示例。只需更新deploymentset.yaml中使用的 nginx 镜像,并确保在执行oc apply命令时使用book命名空间。在接下来的章节中,我们将介绍另一个名为服务账户的基本 Kubernetes 概念,用于为 Kubernetes 平台的关键部分提供认证。

服务账户

当您与集群交互时,通常会以用户身份表示自己。在 Kubernetes 的世界中,我们向系统中构建智能,帮助它与外部世界进行交互。许多时候,Pod 可能会使用 Kubernetes API 与系统的其他部分交互或生成作业。当我们部署一个 Pod 时,它可能会与卷存储交互,与主机文件系统交互,与主机网络交互,或者依赖于分配给它用于文件系统访问的操作系统用户的敏感性。在大多数情况下,您希望限制给定 Pod 的默认权限,以免其做更多的事情。基本上,给予 Pod 在集群、主机操作系统、网络层和存储层的访问权限越少,可以利用的攻击向量就越少。

为了使 pod 能够与系统交互,它被分配了一个服务账户。可以将其视为功能身份。服务账户是可以通过令牌与 kube-apiserver 进行认证并授权特定行为的主体。

在某些 Kubernetes 系统中,投射到 pod 中的服务账户可能具有 Kubernetes 之外的身份。一个强大的用例是在使用开源的Istio服务网格项目与 Kubernetes 时。在这种情况下,通过服务账户投射 Istio 身份,这允许一个 pod 在进行服务请求时与另一个 pod 进行认证。一些云提供商和其他安全工具也允许将服务账户身份投射到 pod 中,这样可以与这些外部平台进行认证。

在 OpenShift 中,服务账户也用于将一组安全权限与每个 pod 关联起来。OpenShift 用于创建特定安全权限组合的对象称为安全上下文约束。在下一节中,我们将详细讨论安全上下文约束以及 OpenShift 提供的其他重要增强功能,以补充基本的 Kubernetes 功能。

OpenShift 增强

OpenShift 引入了几个新概念,用于简化开发和运维。OpenShift 特有的方法包括认证、项目、应用程序、安全上下文和镜像流。

认证

安全对于 OpenShift Kubernetes 平台至关重要。因此,所有用户必须对集群进行认证才能访问它。OpenShift 支持多种常见的认证方法,包括基本认证(使用用户名和密码)、OAuth 访问令牌和 X.509 客户端证书。^(4) OpenShift 提供了oc login命令用于执行认证,操作如下:

[PRE32]

在基本认证用例中,当运行此命令时,用户将被要求输入 OpenShift 容器平台服务器 URL,以及是否需要安全连接,然后用户将被要求输入其用户名和密码。此外,OpenShift 的可配置 OAuth 服务器允许用户将 OpenShift 身份与外部提供者(如 LDAP 服务器)集成。

项目

标准的 Kubernetes 提供了命名空间的概念,允许您为 Kubernetes 资源定义隔离。命名空间使得集群资源可以分配给大量用户,并且由于它们管理的作用域而产生的隔离,使用户不会因为命名冲突而意外使用他人的资源。命名空间非常有用,OpenShift 已经为应用程序的分组适应了命名空间。OpenShift 通过采用 Kubernetes 命名空间并向命名空间添加特殊的标准注解列表来实现此目的。OpenShift 将这种特定类型的命名空间称为项目。OpenShift 使用项目作为其分组应用程序的机制。项目支持访问权限的概念。这使您可以添加一个或多个能访问项目的用户,并使用基于角色的访问控制设置用户在访问项目时拥有的权限和能力。

项目是使用oc new-project命令创建的,并提供项目名称、描述和显示名称,如下所示:

[PRE33]

使用oc project命令,OpenShift 可以轻松地在项目之间进行切换。在这里,我们切换到名为secondproject的不同项目:

[PRE34]

要查看您有权限访问的项目列表,可以使用oc get projects命令:

[PRE35]

有关项目使用的更多信息,请参阅OpenShift 项目文档

应用程序

在使用基本的 Kubernetes 环境时,云原生应用程序开发人员需要执行的比较繁琐的步骤之一是创建自己的容器镜像。通常,这涉及查找适当的基础镜像,并创建包含所有必要命令的Dockerfile,以将基础镜像和开发人员的代码组合成一个可以由 Kubernetes 部署的组装镜像。OpenShift 引入了应用程序构造,大大简化了在 Kubernetes 环境中创建、部署和运行容器镜像的过程。

应用程序是使用oc new-app命令创建的。此命令支持多种选项,可以多种方式构建容器镜像。例如,使用new-app命令,应用程序镜像可以从本地或远程 Git 存储库构建,或者可以从 Docker Hub 或私有镜像注册表拉取应用程序镜像。此外,new-app命令支持通过检查存储库的根目录来确定创建应用程序镜像的正确方式。例如,OpenShift 的new-app命令将在存储库的根目录中查找JenkinsFile,如果找到此文件,它将用于创建应用程序镜像。此外,如果new-app命令未找到JenkinsFile,它将尝试通过查看存储库中的文件来检测应用程序所使用的编程语言。如果它能够确定所使用的编程语言,new-app命令将找到适用于所使用编程语言的合适基础镜像,并用于构建应用程序镜像。

以下示例说明了如何使用oc new-app命令从 OpenShift 示例 ruby hello world 应用程序创建新的应用程序镜像:

[PRE36]

此命令将在最近选择为用户当前上下文的任何 OpenShift 项目中创建应用程序。有关new-app命令支持的应用程序镜像创建选项的更多信息,请参阅OpenShift 应用程序创建文档

安全上下文约束。

在 OpenShift 中,安全始终是首要考虑的。但增强安全性可能带来额外的复杂性和烦恼。如果使用增强的安全性但未为容器提供适当的安全选项,则会导致失败。如果放宽安全性以避免问题,则可能会导致漏洞。为了使用户能够在减少烦恼的情况下利用增强的安全性,OpenShift 包含了一种安全构造称为安全上下文约束

安全上下文约束确定了 Pod 容器保证执行的一组安全特权。因此,在 Pod 容器开始执行之前,它就知道将获得哪些安全特权。以下是由安全上下文约束提供的常见安全特权选项列表:

允许 Pod 运行特权容器。

安全上下文约束可以声明是否允许 Pod 运行特权容器,或者它只能运行非特权容器。

要求启用安全增强 Linux(SELinux)。

SELinux 是用于 Linux 的安全架构,为系统上的应用程序、进程和文件定义访问控制。SELinux 提供了比标准 Linux 更多的额外保护。安全上下文约束为声明是否必须由 pod 容器运行 SELinux 提供了 MustRunAs 属性值,以及声明 pod 容器可以运行标准 Linux 或 SELinux 的 RunAsAny 属性值。

将 pod 容器作为特定用户或非 root 用户运行

以 root 用户身份运行的容器比以非 root 用户身份运行的容器更容易受到攻击。安全上下文约束提供了 MustRunAsNonRoot 属性值,以指示 Pod 的容器不得以 root 身份运行。此外,安全上下文约束使用 RunAsAny 属性值,允许 pod 容器以 root 或非 root 用户身份运行。最后,安全上下文约束管理了一个 MustRunAsRange 属性值,允许 pod 容器在用户 ID 在特定用户 ID 范围内时运行。

允许 pod 容器访问文件系统组块存储

安全上下文约束可用于限制 pod 容器可以访问的块存储。块存储部分通过文件系统组标识符来识别。安全上下文约束提供了 RunAsAny 属性值,允许 pod 容器访问任何块存储的文件系统组,以及一个 MustRunAs 属性值,用于指示 pod 的块存储必须在安全上下文约束中列出的文件系统组 ID 范围内。

OpenShift 包含几个可重复使用的内置安全上下文约束配置文件。要查看您有权访问的项目列表,可以使用 oc get scc 命令:

[PRE37]

如示所示,OpenShift 为常见场景(如特权、受限或作为非 root 运行)提供安全上下文约束配置文件。要查看安全约束配置文件的所有单独功能设置,请使用 oc describe scc 命令,并传递您想要详细了解的配置文件名称。例如,如果您想要详细了解特权约束配置文件的功能强大程度,可以如下调用 oc describe scc 命令:

[PRE38]

运行此命令将列出与此配置文件关联的大量约束属性。以下是其中几个比较有趣的属性:

[PRE39]

为了比较目的,我们可以对受限配置文件运行相同的命令。如下输出所示,约束属性值比特权配置文件中的约束属性值严格得多:

[PRE40]

关键点在于安全上下文约束配置文件能够分组和封装大量的能力属性,并确保在允许执行 Pod 之前满足所有属性要求。这降低了错误设置能力属性的可能性,并减少了因不正确的安全设置导致的意外 Pod 失败的机会。

安全上下文约束配置文件通过使用 Kubernetes 服务账户对象与 Pod 关联。有关安全上下文约束使用的更多信息,请参阅OpenShift 安全上下文约束文档

镜像流

部署云原生应用的关键步骤之一是从仓库中检索正确的容器应用镜像。在生产环境中,这个检索过程可能会遇到几个潜在的问题。首先,容器镜像是通过标签标识符检索的,但容器镜像可能会被覆盖,因此标签引用的镜像可能会发生变化。如果这种变化未被注意到,可能会导致部署的云原生应用引入意外错误。其次,在生产环境中,镜像检索过程还需要支持构建和部署的自动化,但许多镜像仓库在支持这种自动化能力上有所限制。第三,有些情况下,一个容器镜像需要与多个标签关联,因为在不同的环境中,这个容器镜像可能有不同的用途。不幸的是,许多镜像仓库不支持将多个标签与容器应用镜像关联起来的能力。

为了解决所有这些问题,OpenShift 引入了 镜像流 的概念。镜像流旨在为标记的镜像提供更稳定的指针。镜像流通过维护一个 SHA-256 安全哈希函数与其指向的镜像关联,以确保不会错误更改镜像。镜像流还支持为镜像添加多个标签,以更好地支持在多个环境中使用这些镜像。此外,镜像流包括触发器,允许在更新镜像流时自动启动构建和部署。此外,镜像流不仅可以引用外部仓库中的容器镜像,还可以安排定期重新导入外部容器镜像,以确保始终具有最新更新的镜像副本。

创建和更新镜像流相对直接。使用 oc import-image 命令创建镜像流。在以下示例中,使用 oc import-image 命令创建一个名为 nginx 的初始镜像流,并为导入的镜像创建一个初始镜像流标签,其值为 1.12

[PRE41]

如此示例所示,导入到nginx图像流中的初始容器图像是位于centos/nginx-112-centos7的图像。confirm选项指示如果图像流不存在,则应创建该图像流。

创建图像流后,我们可以使用oc describe命令来检查它。在以下示例中,is值是输入流资源的简称。我们想要描述的特定输入流是名称为nginx的流:

[PRE42]

此命令的输出如下所示:

[PRE43]

我们可以使用oc tag命令为此图像添加额外的标签。我们通过以下方式向现有的nginx:1.12标签添加一个nginx:latest标签:

[PRE44]

最后,我们可以通过调用oc tag命令为来自外部仓库的图像打标签,并安排定期重新导入此图像。如以下示例所示,我们引用外部仓库中的图像,将其与图像流标签关联,然后添加定期更新选项以指示应定期更新标签:^(6)

[PRE45]

有关使用图像流的更多信息,请参阅管理图像流的文档

Kubernetes 和 OpenShift 高级主题

在生产环境中运行 Kubernetes 或 OpenShift 时经常使用几个高级概念。在本节中,我们讨论这些高级主题,包括 Webhooks、准入控制器、基于角色的访问控制和运算符。

Webhooks

Webhook 是 HTTP 回调。^(7) 本质上,Webhook 在发生有趣事件时将信息推送给外部实体。通常使用 HTTP Post 操作推送事件信息,并且事件信息通常以 JSON 负载表示。在 Kubernetes 中,Webhooks 用于各种安全相关操作。例如,Kubernetes 可以使用 Webhook 查询外部服务,以确定用户是否具有执行特定操作的正确权限。

OpenShift 也使用 Webhooks 作为触发构建的机制。通过 Webhooks,您可以配置 GitHub 仓库,在仓库发生更改时发送警报。此警报可用于启动新的构建,并且如果构建成功,还可以执行部署。

Webhooks 也被 Kubernetes 准入控制器大量使用,下一节将对其进行描述。有关 Kubernetes 中使用 Webhooks 的更多信息,请参见Kubernetes 文档中的 Webhook 模式

准入控制器

保持 Kubernetes 平台安全的关键在于保护免受可能造成伤害的请求。准入控制器 是 Kubernetes 用来防止平台受到有害请求的机制之一。在某些情况下,准入控制器将阻止请求创建 Kubernetes 对象。在其他情况下,准入控制器将允许请求被处理,但会修改请求以使其更安全。例如,如果有一个请求进来要启动一个 Pod,但请求没有指定 Pod 应该以特权模式还是非特权模式启动,准入控制器可以修改请求,在这种情况下要求 Pod 以非特权模式启动。

多个准入控制器嵌入在 kube-controller-manager 中,默认情况下在 Kubernetes 中启用以保持平台安全。在某些情况下,管理员需要超出包含的准入控制器范围的强制执行。Kubernetes 允许管理员通过注册 Webhook 添加额外的准入控制器来处理 Kubernetes 对象上的请求。我们将在 第三章 中更详细地讨论准入控制器。

基于角色的访问控制

Kubernetes 的授权已集成到平台中。Kubernetes 授权使用基于角色的访问控制(RBAC)模型,并提供了一个完全功能的授权平台,允许操作员通过 Kubernetes 对象 ClusterRoleRole 来定义各种角色,并使用 ClusterRoleBindingRoleBinding 将它们绑定到用户和组。将 RBAC 视为在文件系统上设置权限的一种方式,但在 Kubernetes 的情况下,它是在 Kubernetes 对象模型上设置权限。我们将在 第四章 中详细讨论如何使用 RBAC,并围绕它建立多租户模型的最佳实践。

操作符

Kubernetes 具有内置的抽象概念,如部署,非常适合无状态应用程序。此外,Kubernetes 基于控制循环的优雅设计使其能够支持声明式编程模型,并允许平台在大规模甚至在常见失败时执行稳健地。

为了支持复杂的有状态应用程序,Kubernetes 需要一个可扩展的模型,使用户能够添加自定义资源并对这些资源进行生命周期管理。此外,如果可扩展模型还能支持 Kubernetes 平台内广泛使用的控制循环架构将是理想的。Kubernetes 包括 操作符模式,为满足所有这些要求的自定义资源提供了一个可扩展性模型。

运算符支持创建自定义资源。这意味着您可以通过创建自定义资源定义在 Kubernetes 中定义新的资源类型,并且这个新资源可以像任何标准 Kubernetes 资源一样存储在 Kubernetes etcd 数据库中。此外,您可以为您的资源创建一个自定义控制器,执行与标准 Kubernetes 控制器执行相同类型的控制循环行为。自定义控制器可以监视您的有状态应用程序的实际状态,将其与期望状态进行比较,然后采取行动尝试实现应用程序的期望状态。例如,假设您为一种特殊类型的数据库创建了一个运算符,这是一个有状态应用程序。运算符及其控制器可以确保正在运行的数据库副本的实际数量与所需的副本数量匹配。此外,由于运算符具有自定义控制器,可以将启动新数据库副本或更新现有数据库副本所需的任何自定义生命周期管理代码添加到控制器中。

运算符模式设计良好,其关键优势是无缝。与运算符关联的自定义资源使用kubectl命令行工具进行管理,从管理角度看,与标准 Kubernetes 资源完全相同。为了简化运算符的创建,存在一个运算符软件开发工具包,用于生成所需的自定义资源定义和运行运算符控制循环所需的大部分控制器代码。由于运算符框架的清晰架构设计以及可用的广泛工具,通过创建新的运算符作为添加有状态应用程序的手段的做法继续受到欢迎。现在有一个运算符中心,其中托管了大量现有和可重用的运算符,用于管理各种 Kubernetes 平台上的应用程序。我们将在第七章中更详细地讨论运算符及其在 Kubernetes 中的使用。

总结

在本章中,我们涵盖了广泛的主题,为您提供了对 Kubernetes 和 OpenShift 的广泛基础和扎实介绍。我们涉及了一些对于在生产环境中运行至关重要的主题,并且我们将在本书的后续章节中更详细地探讨这些主题。此外,本章有助于说明 Kubernetes 和 OpenShift 生态系统如何发展成为提供大量企业级功能和灵活性的平台。在第三章中,我们将涵盖一个关键的生产主题:在生产环境中运行时 Kubernetes 资源的高级管理。

^(1) Lantao Liu 和 Mike Brown, “Kubernetes Containerd 集成正式发布,” Kubernetes 博客 (2018 年 5 月 24 日), https://oreil.ly/SlHmh.

^(2) Brendan Burns 等人, “Borg、Omega 和 Kubernetes: 从十年三个容器管理系统的经验中学到的教训,” ACM Queue 14 (2016): 70–93, http://bit.ly/2vIrL4S.

^(3) Tomas Pizarro Moreno, “在 OpenShift 上运行非根容器,” Bitnami Engineering (2017 年 10 月 27 日), https://oreil.ly/pxSGf.

^(4) OpenShift 认证文档 提供了有关支持的认证方法的更多细节。

^(5) 关于镜像流的文档 提供了更多信息。

^(6) Maciej Szulik, “如何通过 OpenShift Image Streams 简化 Kubernetes 中的容器镜像管理,” Red Hat OpenShift 博客 (2017 年 3 月 23 日), https://oreil.ly/JEV4u.

^(7) Wikipedia 提供了对 webhooks 的概述。

第四章:单个集群可用性

每个单独集群的坚实基础对您的应用程序和服务的可用性至关重要。即使拥有这些先进的分布式应用程序能力,某些系统也不适合多集群选项。大多数有状态应用程序和数据库无法进行地理分布,并且在多个集群上进行适当操作非常复杂。许多现代数据存储解决方案要求低延迟多区域解决方案,这使得将数据集群分散到高延迟环境中变得不太理想。还要考虑到许多数据平台具有StatefulSet解决方案来帮助操作它们,需要单集群架构。我们认为理解系统“可用”和“高度可用”的含义对于理解 OpenShift 和 Kubernetes 的价值和架构至关重要。请注意,我们将讨论如何利用多集群架构实现最高的可用性和灵活性,以及在第五章中准备好您所需的知识,以便能够就您的可用性目标做出明智的决策并利用 Kubernetes 和 OpenShift。

系统可用性

在现代服务和应用交付中,我们通常根据应用或服务的可用和正常响应时间的百分比来谈论系统可用性。技术服务行业的一个常见标准是使用“9”的术语来描述服务水平目标(SLOs)。我们经常听到“四个 9”或“五个 9”,以指示服务可用的时间百分比。四个 9 表示 99.99%,五个 9 表示 99.999%。我们将在本章节中沿用这些标准。这些 SLOs 是服务提供商与其利益相关者或客户之间的 SLA 中的协议。这些 SLOs 通常是按月度或有时是按年度进行衡量。

测量系统可用性

在他的日常工作中,其中一位作者,Jake,负责交付IBM 云 Kubernetes 服务Red Hat OpenShift on IBM Cloud。这些是全球服务,提供了管理 Kubernetes 和 OpenShift 解决方案的简单 API 驱动、用户友好的云体验。对于这些服务,站点可靠性工程(SRE)团队每月都会按客户计费周期测量服务水平目标(SLOs)。考虑到这些目标,思考与这些“9”相关的停机时间量是很有帮助的。我们可以在表 4-1 中看到,一旦进入更高可用性级别,我们的中断预算或允许的停机时间可以非常低。在进行这种分析时,您的团队需要仔细考虑愿意承诺的内容。

表 4-1. 可用性表;基于平均每月 730 小时计算

可用性百分比 每月停机时间
99%(两个九) 7 小时 18 分钟
99.5%(两个半九) 3 小时 39 分钟
99.9%(三个九) 43 分钟 48 秒
99.95%(三个半九) 21 分钟 54 秒
99.99%(四个九) 4 分钟 22.8 秒
99.999%(五个九) 26.28 秒

一个团队将努力改进的两个关键绩效指标(KPI)是平均修复时间(MTTR)和平均故障间隔时间(MTBF)。这两个指标直接影响可用性:

Availability = MTBF MTBF + MTTR

对于我们如何达到期望的可用性百分比,进行一些思考会很有帮助。假设我们希望达到 99.99% 的可用性。在表 4-2 中,我们比较了一些可以帮助我们达到这种可用性的不同 MTTR 和 MTBF 数字。

表 4-2. 四个九(99.99%)可用性计算

MTBF MTTR
1 天 8.64 秒
1 周 60.48 秒
1 月 4 分钟 22.8 秒
1 年 52 分钟 33.6 秒

对于那些在家跟随的人来说,这意味着要在一个每天会发生一次故障的系统中,你需要在不到九秒的时间内识别和修复该故障才能达到四个九的可用性。为了实现平台稳定性和软件质量的显著提升 MTBF,需要大量的工程投入。对许多客户来说,四个九的成本太高。要达到这种可用性水平需要硬件、工程和支持人员。作为对比,让我们来看看表 4-3 中两个九的比较。

表 4-3. 两个九 99% 可用性计算

MTBF MTTR
30 分钟 18.18 秒
1 小时 36.36 秒
1 天 14 分钟 32.4 秒
1 周 1 小时 41 分钟 48 秒

这些数据告诉我们什么?首先,如果你的目标是四个九,那么你需要一个异常可靠的系统,如果要达到目标,故障发生的频率应该在一年内仅有一次。即使是两个九,如果每天有一次故障,你也需要在不到 15 分钟的时间内识别和修复该故障。其次,我们需要考虑这些故障发生的可能性。你说每天发生一次故障?谁每天都会有故障?好吧,请考虑一下:单个 VM 实例的 SLA 通常约为 90%,这意味着每月将有 73.05 小时的停机时间。一个单独的 VM 平均每月将停机三天。如果你的应用程序仅依赖于 10 个系统,那么至少其中一个系统每天都会停机。

这幅图片看起来令人沮丧且难以逾越吗?如果不是,那么你一定是位极具乐观主义精神的人。如果你觉得这看起来具有挑战性,那么你已经意识到用标准的监控和恢复手册来达到这些故障率和恢复时间是不太可能的。那么,请进入高可用系统的世界吧。

什么是高可用系统?

简而言之,高可用性(HA) 指的是一个系统具有没有任何单点故障的特性。任何一个“组件”都可以承受故障,整个系统不会出现可用性损失。这种属性有时被称为能够将个别组件视为牲畜而非宠物。这个概念最初由微软的 Bill Baker 在他的演讲 “Scaling SQL Server 2012” 中引入^(1),后来在 CERN 的 Gavin McCance 讨论 OpenStack 时得到了普及^(2)。只要整体群体健康,我们就不需要关心个体的健康状况。我们喜欢保持所有宠物的健康和良好状态,但这需要耗费大量时间和金钱。如果你只有少数几只宠物,这种做法是可持续的。然而,在现代云计算中,我们通常看到数百甚至数千只宠物。这个星球上的 SRE 工程师根本不足以保持整个群体的健康。

在存在单点故障的系统中,即使是最精心维护的组件偶尔也会发生我们无法预防的故障。一旦通过监控发现,总会需要一些时间来使它们恢复健康。希望我们对系统可用性的介绍能让您意识到 MTBF 和 MTTR 的挑战。

对于我们来说,有一个最简单的高可用系统的图示是非常有帮助的。一个简单的无状态 Web 应用程序和负载均衡器就是一个很好的例子,如 图 4-1 所示。

图 4-1. 带有负载均衡器的高可用无状态 Web 应用程序

在这个最简单的高可用系统中,如果这两个 Web 应用实例中的任何一个失败,负载均衡器可以检测到该故障,并停止将流量路由到失败的实例。使用者不了解个别实例的情况;所有的交互都是与负载均衡器进行的。负载均衡器甚至能在一个实例的请求失败或者响应不够快时进行重试。

显然,负载均衡器本身也可能发生故障,但负载均衡器通常比您典型的 Web 应用程序具有更高的可用性数字。典型的云负载均衡器具有 99.99%的 SLA,这将是运行在该负载均衡器后面的任何应用程序的可用性上限。如果云负载均衡器不可用,您可以部署成对的虚拟 IP 和虚拟路由冗余协议(VRRP),它可以在两个负载均衡器实例之间移动单个 IP 地址。公共云提供商可以通过其应用程序和网络负载均衡器服务简化所有这些操作,这些服务能够处理负载均衡器本身的所有高可用性方面。

完成了吗?嗯,并不完全如此;现代应用程序和服务平台中有许多组件和服务需要运行。根据我们的经验,许多大型云服务可能包括多组五到六个微服务。再加上您的应用程序或服务可能是全球分布和/或复制的事实。考虑我们自己在 Red Hat OpenShift on IBM Cloud 中的服务可用性在图 4-2 中展示。

图 4-2. IBM 云上的 Red Hat OpenShift 位置

在全球六个多区域地区以及全球其他 14 个数据中心。我们的团队在每个多区域地区运行超过 40 个微服务,在单区域数据中心运行大约一半的微服务。这些数字很快就会增加。想象一下,如果我们必须在每个位置手动部署每个微服务并为它们中的每一个配置负载均衡器,那我们将需要一支大军的工程师来编写定制配置数据。这就是 OpenShift 和 Kubernetes 的用武之地。

OpenShift 和 Kubernetes 应用程序与服务可用性

Kubernetes 和 OpenShift 中的许多组件都有助于整个系统的可用性。我们将讨论如何保持应用程序的健康状态,如何处理网络问题以及如何解决计算节点可能出现的问题。

应用程序

对于我们的无状态应用程序,简单的负载均衡器是保证高可用性的关键。然而,正如我们讨论过的那样,如果我们依赖手动部署和配置负载均衡器和应用程序,那么保持它们的更新就不会太有趣了。在第二章中,我们讨论了 Kubernetes 部署。这种优雅的结构使得部署多个应用程序副本变得简单。让我们来看看livenessProbe,这是一种高级能力,也是部署-容器规范中更为关键的特性之一。livenessProbe定义了一个探测器,将由kubelet针对容器执行,以确定目标容器是否健康。如果livenessProbe在配置的failure​Th⁠reshold之后未能成功返回,则kubelet将停止容器并尝试重新启动它。

注意

为了处理容器在提供请求的能力上的瞬态问题,您可以使用一个readinessProbe,它将确定 Kubernetes 服务的路由规则,但如果它失败了,不会重新启动容器。如果readinessProbe失败,Pod 的状态将标记为NotReady,并且将从服务端点列表中移除该 Pod。探测将继续运行,并在成功时将状态设置为Ready。如果一个 Pod 有一个可能暂时无法访问的外部依赖项,这将非常有用。重新启动此 Pod 将无济于事,因此使用livenessProbe来重新启动 Pod 没有任何好处。有关 Kubernetes 和 OpenShift 中可用探测器的更多详细信息,请参见社区文档

以下示例显示了一个包含具有常见参数的livenessProbe的简单部署:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: webserver
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      labels:
        app: webserver
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
          - containerPort: 80
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 3
          periodSeconds: 3
          timeoutSeconds: 2
          failureThreshold: 2

让我们更仔细地查看我们在liveness​P⁠robe示例中选择的选项,以及它们如何转化为真实世界的行为:

httpGet

将执行的检查类型。您还可以使用tcpexec命令来执行测试,以测试容器是否健康。

initialDelaySeconds

kubelet在尝试第一个探测之前将等待多长时间。

periodSeconds

kubelet将测试探测的频率。

timeoutSeconds

kubelet客户端将等待响应的时间长度。

failureThreshold

kubelet标记探测失败之前,必须运行的连续失败次数。对于我们的livenessProbe,结果是重新启动此容器。

提示

一定要彻底测试您的livenessProbe和定时设置。我们听说过许多情况,其中应用程序启动缓慢导致CrashLoopBackOff场景,其中 Pod 由于从未有足够的initialDelaySeconds完全启动而从未成功启动。livenessProbe永远不会成功,因为kubelet开始检查生存状态之前,应用程序尚未准备好开始接收请求。对于滚动更新来说,设置非常长的初始延迟设置可能会很痛苦,但可以提高系统的稳定性,特别是在初始生存时间存在大量变异性的情况下。

总的来说,我们具备传统负载均衡器健康检查的所有要素。HAProxy 是一种流行的传统软件负载均衡解决方案。如果我们查看标准的 HAProxy 配置,我们会看到许多相同的关键健康检查组件:

frontend nginxfront
 bind 10.10.10.2:80
 mode tcp
 default_backend nginxback

backend nginxback
 mode tcp
 balance roundrobin
 option httpchk GET /
 timeout check 2s
 default-server inter 3s fall 2 
 server 10.10.10.10:80 check
 server 10.10.10.11:80 check
 server 10.10.10.12:80 check

这里需要注意的有趣之处是,我们在传统基础设施中的负载均衡器配置和 Kubernetes 中的部署中查看的是相同的配置设置。事实上,执行的实际检查是相同的。两者都通过端口 80 发起 HTTP 连接并检查 200 响应代码。然而,最终的行为却是非常不同的。在 HAProxy 的情况下,它只确定是否应将网络流量发送到后端服务器。HAProxy 不知道后端服务器是什么,也无法更改其运行时状态。相比之下,Kubernetes 并不直接改变网络路由。Kubernetes 只是更新部署中每个 pod 的状态(ReadyNotReady),如果 pod 未通过 livenessProbe,它将重新启动该容器。重新启动 pod 不能解决导致无响应的所有问题,但通常可以解决一些小问题。

提示

livenessProbe 不仅适用于确保容器健康且准备好接收流量。对于那些厌倦了清理 Java 虚拟机(JVM)泄漏线程和内存以至变得无响应的实用操作员来说,它是一个极好的可编程解决方案。它是我们在第三章中设置的资源限制的一个很好的备份,以尝试限制这些资源泄漏。

值得注意的是,将这些健康检查配置与系统的运行时组件关联起来而不是网络功能是一个有意为之的选择。在 Kubernetes 和 OpenShift 中,livenessProbe 是由 kubelet 执行而不是由负载均衡器执行。正如我们在图 4-3 中所看到的,这个探针在每个包含部署 pod 的节点上执行。

图 4-3. kubelet 执行 livenessProbe 到本地 nginx pod

每个 kubelet(每个工作节点一个)负责执行节点上运行的所有容器的所有探针。这确保了一个高度可扩展的解决方案,分发探针工作负载,并在整个机群中维护 pod 和容器的状态和可用性。

基础设施

Kubernetes 的网络是从哪里来的?这是 Kubernetes 服务的工作。这里的事情变得有趣起来。让我们看看流量如何到达我们的 nginx pod。首先,我们的服务定义:

apiVersion: v1
kind: Service
metadata:
 labels:
 app: webserver
 name: nginx-svc
spec:
 ports:
 - port: 80
 protocol: TCP
 targetPort: 8080
 selector:
 app: webserver
 type: ClusterIP
 clusterIP: 172.21.102.110

这个服务定义告诉 Kubernetes 应该将发送到集群中 172.21.102.110 端口 80 的任何流量路由到与选择器 app=webserver 匹配的任何 pod 上的端口 8080。这里的优势在于,当我们扩展或更新我们的 web 服务器应用程序并且 pod 被替换时,服务会保持端点的最新状态。如果我们删除所有的 pod 并让我们的部署自动替换它们,可以看到端点恢复的一个很好的例子:

$ kubectl get endpoints nginx-svc
NAME      ENDPOINTS                                          AGE
nginx-svc 172.30.157.70:80,172.30.168.134:80,172.30.86.21:80 3m21s

$ kubectl delete pods --all
pod "nginx-65d4d9c4d4-cvmjm" deleted
pod "nginx-65d4d9c4d4-vgftl" deleted
pod "nginx-65d4d9c4d4-xgh49" deleted

$ kubectl get endpoints nginx-svc
NAME      ENDPOINTS                                          AGE
nginx-svc 172.30.157.71:80,172.30.168.136:80,172.30.86.22:80 4m15s

这种自动管理服务端点的功能开始展示了 Kubernetes 与传统基础设施的一些优势。无需更新 HAProxy 中的服务器配置。Kubernetes 服务会完成所有工作,以保持我们的“负载均衡器”最新。为了组装端点列表,Kubernetes 服务会查看所有处于 Ready 状态的 pod,并匹配服务定义中的标签选择器。如果容器失败了探测,或者托管 pod 的节点不再健康,那么该 pod 将被标记为 NotReady,服务将停止将流量路由到该 pod。在这里,我们再次看到 livenessProbe(以及 readinessProbe)的作用,它的作用不是直接影响流量路由,而是通知 pod 的状态,从而影响用于路由服务流量的端点列表。

在我们的应用程序有多个副本分布在多个工作节点的情况下,我们有一个服务的解决方案,即使一个工作节点发生故障,我们也不会丢失任何可用性。Kubernetes 足够智能,能够意识到来自该工作节点的 pod 可能不再可用,并将在整个集群中更新服务端点列表,停止向故障节点上的 pod 发送流量。

我们刚刚讨论了当计算节点发生故障时会发生什么:服务本身足够智能,能够绕过在该节点上的任何 pod 进行流量路由。事实上,部署将会在重新调度后,将来自故障节点的那些 pod 替换为另一个节点上的新 pod,这对处理计算故障非常方便。那么为服务提供“负载均衡器”的需求呢?Kubernetes 在这个问题上采取了非常不同的方法。它使用 kube-proxy 组件提供负载均衡功能。kube-proxy 的有趣之处在于,它是一个完全分布式的组件,提供了一个将客户端负载均衡器的分布式特性和传统负载均衡器的前端融合在一起的负载均衡解决方案。并不是每个客户端实例都有一个独立的客户端负载均衡器,而是每个计算节点有一个。

请注意,kube-proxy 负责将服务定义及其端点转换为每个工作节点上的 iptables^(3) 或 IPVS^(4) 规则,以适当地路由流量。为简单起见,我们在图中没有包含 iptables/IPVS 规则本身;我们用 kube-proxy 来代表它们,如 图 4-4 所示。

图 4-4. kube-proxy 将流量路由到数据访问端点

在 图 4-4 的示例中,请注意,Web 服务器(客户端)将流量路由到本地 kube-proxy 实例,然后 kube-proxy 将流量发送到运行在本地和/或远程节点上的数据访问服务 Pod。因此,如果其中任何一个节点失败,那么客户端应用程序和负责该节点负载均衡的 kube-proxy 将一同失败。因此,kube-proxy 是在 Kubernetes 系统中提供简化和分布式高可用解决方案的关键组件。

我们现在审查了 Kubernetes 如何设计解决方案,以确保系统在任何单个计算节点、服务实例或“负载均衡器”(kube-proxy)失败时仍能保持系统的正常运行时间,从而使我们能够自动维持整个系统的可用性。无需 SRE 干预即可识别或解决故障。然而,我们时常看到 Kubernetes 托管的系统尽管如此仍会遭遇一些故障。这通常是由于编写不良的探针导致无法应对某些边缘情况下的应用程序故障所致。Kubernetes 在提供平台和最佳可用性指南方面做得非常好,但它并非魔法。用户需要带来良好设计和适当配置的应用程序和服务,才能看到这种潜力。

还有其他网络解决方案用于负载均衡。kube-proxy 很棒,但正如我们所述,它只在从集群中的客户端路由流量时才真正起作用。对于从集群外部的客户端请求路由,我们将需要一种略有不同的方法。诸如 Ingress Controller、OpenShift Router 和 Istio Ingress Gateway 等解决方案能够与 Kubernetes 服务概念集成,但提供一种方式将流量从集群外部路由到集群内运行的服务和应用程序。需要注意的是,您将需要将这些 Layer 7 路由解决方案与外部 Layer 4 负载均衡器结合使用,以保持适当的可用性。图 4-5 显示了外部负载均衡器如何与 OpenShift Router 结合使用,将来自集群外部的请求发送到运行在集群内的 Web 服务器。当使用这些网络解决方案时,请注意它们不会通过 kube-proxy 路由其流量;它们会直接向服务的端点进行自己的负载均衡。

图 4-5. 外部负载均衡器和 OpenShift Router

所有我们迄今为止讨论过的冗余性和可用性规则和逻辑在这里同样适用。外部负载均衡器就像我们在本章前面提供的 HAProxy 示例一样。在网络领域,根本无法逃避这些规则。要从外部世界将流量传送到集群中,仍然需要一个高可用解决方案。这里的优势在于,我们不需要为集群中的每个应用程序设置单独的负载均衡器。OpenShift Router、入口控制器和 Istio 入口网关都可以为集群中的许多不同应用程序提供服务,因为它们支持第 7 层路由,而不仅仅是第 4 层。第 7 层路由器可以检查 HTTP(S) 标头以理解目标 URL,并使用该信息将流量发送到正确的服务。重要的是要注意,OpenShift Router 和入口控制器仍然依赖于 Kubernetes 服务来执行 liveness 和 readiness 探针工作,以确定 Router 或入口控制器应将流量发送到哪些 pod。

虽然这些是更复杂的解决方案,但我们只需有一些允许将外部流量路由到我们集群中托管服务的东西即可。我们已经将外部负载均衡器解决方案所依赖的 NodePort 概念的讨论留给读者从 Kubernetes 文档 中了解。

结果

回顾我们最初的可用性讨论,Kubernetes 提供了一种识别和修复问题的机制。根据我们的应用程序 livenessProbe 设置,最多需要六秒来识别问题,然后需要一些时间重新路由流量(减少任何失败请求),然后自动通过重启修复失败的容器。在高效应用中,容器重启过程可能非常快,甚至亚秒级别。即使保守估计为 10 秒,对于我们的 MTTR 看起来也相当不错。我们可以通过调整设置更加积极,每两秒执行一次 livenessProbe,在单个故障时执行重启,将 MTTR 缩短至不到五秒。

关于 livenessProbereadinessProbe 设置的一点快速说明:请记住,kubelet 将会向整个集群中的这些容器的每个实例发出请求。如果您运行了数百个副本,并且它们每两秒都受到探针的影响,那么您的日志或指标解决方案可能会变得非常啰嗦。如果您的探针命中的是相对轻的端点,这些端点可能会处理用户请求中的数百甚至数千次,那么这可能不是问题,但无论如何,这也值得注意。

现在,我们对于 Kubernetes 在我们的应用程序中实现的 MTTR 有了一些了解,让我们来看看这对我们的可用性计算中的一些 MTBF 有何影响,以确定我们系统中的 表格 4-4。

表格 4-4. MTTR 计算为 MTBF

MTTR 99% 可用性的 MTBF 99.99% 可用性的 MTBF
5 秒 8.26 分钟 13 小时 53 分钟
10 秒 16.5 秒 27 小时 46 分钟
1 分钟 99 秒 6 天 22.65 小时
10 分钟 16 分钟 30 秒 69 天 10.5 小时

通过这些数据,我们可以看到,仅仅通过简单的 Kubernetes 部署概念,我们可以得出一些合理的可用性计算,我们可以持续维护这些计算。

做完了,结束了,对吧?不要那么快。不同的故障模式对 Kubernetes 和 OpenShift 可能会产生不同的结果。接下来我们将看看这些故障。

失败模式

在您的 Kubernetes 和 OpenShift 系统中可能会遇到各种类型的故障。我们将涵盖许多这些故障,如何识别它们,应用程序或服务的影响以及如何预防它们。

应用程序 Pod 失败

单个应用程序 Pod 的失败(图 4-6)可能导致几乎没有停机时间,正如我们早些时候讨论过的探测和 kubelet 自动重新启动失败的 Pod。

图 4-6. 应用程序 Pod 失败

值得注意的是,更长的探测间隔可能会导致更长的 MTTR,因为这会增加检测故障的时间。有趣的是,如果您使用 readinessProbes,则需要一些时间才能使就绪失败在 Kubernetes 或 OpenShift 系统中传播。恢复的总时间可能是:

(readinessProbe Internal * failureThreshold) + iptables-min-sync-period

iptables-min-sync-period 是我们尚未讨论的新配置设置。这个设置(或在 IPVS 模式下的 ipvs-min-sync-period)是 kube-proxy 决定在两次更新 kube-proxy 的 iptables 规则之间可以经过的最短时间。^(5) 如果集群中的一个端点在 t0 更新,然后另一个端点在五秒钟后更新,但 min-sync 设置为 30 秒,那么 kube-proxy(iptables/IPVS)仍将继续将流量路由到可能无效的端点。这个设置的默认值是一秒,因此影响最小。然而,在非常大的集群中,有大量的服务(或大量的端点),可能需要增加这个间隔,以确保 iptables/IPVS 控制平面的合理性能。这是因为随着要管理的服务越来越多,iptables 或 IPVS 规则集的大小可能会变得非常庞大,轻松地达到数万条规则。如果不调整 iptables-min-sync-period,那么 kube-proxy 可能会因不断尝试更新规则而不堪重负。记住这种关系是很重要的。集群的大小可以影响整个系统的性能和响应能力。

工作节点故障

在任何 OpenShift 或 Kubernetes 集群中最常见的故障之一是单个工作节点故障(图 4-7)。单节点故障可能由多种原因引起。这些问题包括磁盘故障、网络故障、操作系统故障、磁盘空间耗尽、内存耗尽等。让我们更仔细地看看当 Kubernetes 系统中存在节点故障时会发生什么。恢复单个节点的过程将直接影响我们对系统可用性的计算。

图 4-7. 工作节点故障

响应工作节点故障时发生的时间间隔如下:

T0

节点发生故障。

T1 = T0 + nodeLeaseDurationSeconds

Kubernetes 和 OpenShift 现在使用租约系统来跟踪工作节点的健康状况。每个 kubelet 向控制平面注册一个自己的租约对象。如果该租约过期,节点将立即标记为 NotReady。一旦节点标记为 NotReady,服务控制器将指示该节点上的任何 pod 可能已失败,并且应该移除与该节点上的 pod 相关的任何服务端点。这是 Kubernetes 决定停止路由流量到故障节点上的 pod 的方式。其结果类似于 pod 的 livenessProbe 失败,但会影响到故障节点上的所有 pod。nodeLeaseDurationSeconds 配置在 kubelet 上,决定租约的持续时间,默认为 40 秒。

T2 = T1 + iptables-min-sync-period

这是可能需要的额外时间,用于在集群中同步 kube-proxy 配置的整个飞机队。此行为与我们应用程序 pod 故障场景中讨论的行为完全相同,其中需要将服务的端点列表中的任何更改同步到集群中的所有 kube-proxy 实例。一旦同步完成,所有流量将被正确路由,任何来自故障节点的问题都将得到解决,但减少的容量除外。默认的最小同步周期为 30 秒。

T3 = T2 + pod-eviction-timeout

这个设置配置在 kube-controller-manager 上,决定在节点标记为 NotReady 后,控制器将等待多久才会尝试终止 NotReady 节点上的 pod。如果有控制器管理正在终止的 pod,通常它们将启动新的替代 pod,就像部署控制器逻辑的情况一样。这不会影响到流量路由到故障节点上的 pod;这个问题在 T2 已经解决。然而,这将恢复与来自故障节点的 pod 相关的任何降低的容量。默认超时时间为五分钟。

如我们所见,节点故障的影响程度类似于应用容器故障。请注意,在此,我们计算的是达到 T2 时的最坏情况。某些节点故障可能会被检测并迅速报告,因此 T1 时间可以更短,因为我们不必等待租约到期才能将节点标记为 NotReady。另请记住,iptables 可能会同步得更快,这只是最坏情况下。

工作区故障

工作区故障的处理方式与节点故障相似。在完全工作区故障(图 4-8)的情况下,例如网络故障,我们通常期望在一个相对较短的时间窗口内将所有节点标记为 NotReady

第 4-8 图:工作区故障

如果单个区域中故障节点的数量超过 unhealthy-zone-threshold 百分比,KubernetesOpenShift就可以“检测”到区域故障。如果检测到区域故障,则服务端点评估过程没有任何变化。所有位于故障节点上的容器都会被移除作为任何关联服务的端点。unhealthy-zone-threshold 将决定 pod 驱逐过程的变化。如果集群低于 large-cluster-size-threshold(定义为节点的数量),则该区域的所有 pod 驱逐将停止。如果 unhealthy-zone-threshold 超过大集群大小,则 pod 驱逐开始,但节点的处理速度会显著减慢,如 secondary-node-eviction-rate 所定义。这是为了防止 pod 调度风暴,并允许系统在“过度反应”到暂时故障之前获得一些额外的恢复时间。

控制平面故障

有各种不同的控制平面故障场景。存在工作节点和控制平面之间的网络相关故障,也存在控制平面组件故障。稍后在本节中,我们将详细介绍各种控制平面组件的高可用性特性。在我们开始这些细节介绍之前,值得谈一谈当出现任何形式的完全控制平面故障时,会发生什么。集群整体如何响应如图 4-9 所示的故障?

图 第 4-9 图:完全控制平面故障

好消息是,在此场景中,运行在工作节点上的所有工作负载不受影响。所有的 kube-proxy 配置、入口控制器、Pod、DaemonSets、网络策略等继续像控制平面故障发生时一样正常运行。坏消息是,在此状态下无法进行任何更改或更新系统。此外,如果在此场景中工作节点发生故障,则无法检测或更新集群以弥补故障。幸运的是,如果控制平面正确实现,这种故障的可能性很低。现在我们将介绍一些控制平面的高可用特性。

相比让所有人等待,我们将从控制平面中最复杂、潜在最具挑战性且易于最关键的组件开始。称为“最关键”的组件是 etcd。Kubernetes 和 OpenShift 使用开源键值存储 etcd 作为其持久性存储解决方案。etcd 数据存储是系统中所有对象持久化的地方。与节点、部署、Pod、服务等相关的所有数据和信息都存储在 etcd 中。

etcd 失败

您可能会遇到 etcd 的许多不同问题。有些问题比其他问题更容易缓解。从 kube-apiserver 到 etcd 的连接故障可能是最常见的问题。现代的 etcd 客户端内置了负载均衡和弹性。请注意,如果您有一个传统的负载均衡器位于您的 etcd 实例前面,那么大部分情况下这并不适用,因为您可能已经为 etcd 注册了单个终端节点。不过,kube-apiserver 将使用 --etcd-servers 设置来确定 etcd Go 客户端配置的 IP(s) 和/或主机名(s)。社区中有关于负载均衡行为的优秀文档。值得注意的是,大多数现代 Kubernetes 和 OpenShift 版本现在都在其客户端中使用 clientv3-grpc1.23 变体。该客户端版本始终维持活动的 gRPC 子连接,以确定与 etcd 服务器端点的连接状态。

etcd 客户端的故障恢复行为使系统能够处理 kube-apiserver 与 etcd 之间的通信故障。不幸的是,etcd 客户端无法处理像同行的性能降级或网络分区等问题。可以通过对 etcd 同行进行出色的就绪检查在某种程度上缓解这些问题。如果您的系统允许就绪检查(例如,在 Kubernetes 上托管的 etcd),您可以配置就绪检查,这些就绪检查可以查找网络分区或严重性能降级,例如使用以下内容:

etcdctl get --consistency=s testKey --endpoints=https://localhost:port

使用此代码将确保本地对等体能够正确执行序列化读取,并且是一个更强的检查,以确保此对等体不仅在侦听,还可以处理获取请求。可以使用 put 测试(也确保对等体连通性),但这可能是一个非常昂贵的健康检查,引入大量关键修订,并可能对 etcd 集群产生不利影响。我们正在进行持续调查,以确定 etcd 性能指标是否是对对等体正在遭受性能退化的可靠指标,并应从集群中移除。在此时点上,我们只能建议使用标准监控和 SRE 警报来观察性能问题。目前还没有强有力的证据表明,由于性能问题,您可以安全地终止对等体而不会可能对集群的整体可用性产生不利影响。

注意

如果讨论如何改善 etcd 的可用性而不提及为 etcd 设置适当的灾难恢复过程将是不合适的。定期进行自动化备份是必须的。确保定期测试 etcd 的灾难恢复流程。关于 etcd 灾难恢复的额外提示:即使 etcd 集群的法定人数已经中断,如果 etcd 进程仍在运行,您仍然可以从该对等体获取新的备份,即使没有法定人数也可以确保最小化从上次定期计划备份 etcd 开始的任何时间间隔的数据丢失。始终尝试从剩余的 etcd 成员获取备份,然后再进行恢复。Kubernetes 文档 提供了关于 etcd 备份和恢复选项的优秀信息。

对 etcd 最重要的关注点是维护 etcd 集群的法定人数。只要少于大多数对等体健康,etcd 就会中断法定人数。如果法定人数中断,则整个 Kubernetes/OpenShift 控制平面将失败,并且所有控制平面操作将开始失败。增加对等体的数量将增加可以容忍的对等体故障的数量。请注意,拥有偶数个对等体不会增加容错能力,并且可能损害集群检测到拆分脑网络分区的能力。^(7)

kube-apiserver 失败

kube-apiserver 负责处理 Kubernetes 或 OpenShift 集群中对象的所有 创建、读取、更新和删除(CRUD)操作的所有请求。这适用于最终用户客户端应用程序以及集群的内部组件,如工作节点、kube-controller-manager 和 kube-scheduler。与 etcd 持久存储的所有交互都通过 kube-apiserver 处理。

kube-apiserver 是一个主动/主动的扩展应用程序。它不需要领导者选举或其他机制来管理 kube-apiserver 实例的群体。它们都以活动模式运行。这个特性使得在高可用配置中运行 kube-apiserver 变得简单。集群管理员可以运行尽可能多的 kube-apiserver 实例,合理地将负载均衡器放置在 apiserver 前面。如果配置正确,单个 kube-apiserver 失败将不会对集群功能产生影响。参见 图 4-10 的示例。

图 4-10. kube-apiserver 单个故障而无可用性影响

kube-scheduler 和 kube-controller-manager 故障

这两个组件具有非常相似的可用性特性。它们都始终以单个活动实例运行,并使用相同的领导者选举特性。它们利用租约对象来管理集群的领导者。如果任一组件的跟随者实例失败,则不会对整体集群功能产生影响。如果领导者失败,则在 --leader-elect-lease-duration 时间内将没有领导者。然而,没有任何组件直接与调度器或控制器管理器交互。这两个组件在集群中异步地对对象状态变化进行交互。

kube-controller-manager 失去领导者可能导致从节点故障的恢复延迟,例如。如果某个 pod 因某种原因失败,则控制器管理器将无法响应更新相关服务的端点。因此,虽然同时发生领导者故障和其他系统故障可能会导致系统的平均修复时间延长,但不会导致长时间的故障或停机。

失败的 kube-scheduler 领导者对可用性几乎没有影响。当领导者宕机等待新选举时,将不会将新的 pod 调度到节点上。因此,尽管由于并发失败节点的 pod 调度不足可能会导致容量减少,但在此期间仍会有对集群的服务和路由更新。

网络故障

网络故障有许多不同的类型。我们可能会看到路由表更新失败导致数据包被错误路由。在其他情况下,我们可能会看到负载均衡器故障。也可能是网络交换机故障。潜在问题的列表是无穷无尽的。为了简化我们对这个主题的讨论,我们将网络故障分为两种常见类型:南北向网络故障,即来自 Kubernetes 或 OpenShift 集群外部的流量无法进入或离开,以及 东西向网络故障,描述了集群中节点之间无法传输网络流量的情况。

南北向网络故障

此故障场景指的是集群本身可能是健康的,控制平面和数据平面也可能是正常的,但可能存在来自外部源的流量路由问题。在图 4-11 中,我们遇到了外部负载均衡器到工作节点的连接失败的情况。

图 4-11. 南北网络故障

结果是,负载均衡器对从连接到失败的工作节点暴露的任何NodePort的健康检查将失败,负载均衡器将停止将流量路由到该工作节点,生活还在继续。事实上,在这里的 MTTR 将非常好,因为只需外部负载均衡器失败健康检查来停止将流量路由到失败的链接,或者如果连接时间限制和重试被集成到这个负载均衡器解决方案中,它可能会有零停机时间。负载均衡器就是为这种情况而生的。

如果负载均衡器与工作节点之间的所有连接都失败,那么来自集群外部的所有访问都将失败。结果是任何旨在从集群外部访问的服务都将真正停机。好消息是,我们可以实现多区域集群以减轻这种灾难性故障的可能性。在第八章中,我们将讨论使用全球基于 DNS 的负载均衡来进一步改善减轻地理网络中断的方法。此外,这些南北故障只影响从集群外部访问服务。在集群内运行的所有内容仍可正常运行,没有任何服务中断。

东西网络故障

我们还可能遇到节点失去跨区域连接或网络分区的情况。这在多区域集群中最常见,当区域之间发生网络故障时。在图 4-12 中,我们看到了一个完整区域的故障。

图 4-12. 东西总故障

在这种情况下,我们面临着一个“干净”的区域失败,其中 C 区域被完全分区。该区域的工作节点将继续尝试向 C 区域的控制平面节点更新其租约。如果成功,将导致问题。然而,C 区域的控制平面将退出集群并停止响应请求。发生这种情况有两个原因。首先,C 区域的 etcd 将开始失败,因为它将无法与其他区域的 etcd 联系以参与 etcd 集群的仲裁。结果,C 区域的 kube-apiserver 也将开始报告为不健康。C 区域的工作节点将无法续约其与控制平面的租约。结果,它将保持静止并且什么也不做。kubelet 将重新启动任何失败的容器。但是,正如我们所看到的,C 区域的工作节点与其他区域之间没有连接。任何应用程序通信将开始失败。

好消息是 A 区域和 B 区域一切都将安好。控制平面 etcd 将保持仲裁(在 A 或 B 中选举新的领导者,如果之前的领导者在 C 中),所有控制平面组件将保持健康。此外,C 区域的工作节点租约将到期,因此此节点将被标记为 NotReady。将移除所有 C 区域的服务端点,并更新 kube-proxy 停止将流量路由到 C 区域的 Pod。

可能有多种情况会使 Kubernetes 或 OpenShift 平台略微失调。我们将专注于一种暴露 Kubernetes 设计弱点的场景。这种失败只在工作节点之间的东西向西流量中发生失败并不罕见,可以在工作节点和控制平面之间提供一些网络隔离,这可能导致控制平面连接性和工作节点连接性之间的不一致性。我们在 图 4-13 中有一个例子。

图 4-13. 东西向数据平面失败

这里的结果非常有趣。所有工作节点能够向控制平面报告正常。工作节点之间没有通信验证。这也包括 kube-proxy,因为它不是传统的负载均衡器。Kube-proxy 不执行任何健康检查,无法验证工作节点之间的连接。结果是所有 Pod、服务和端点都保持完好无损。听起来很棒,但问题在于,如果任何 Pod 尝试跨失败的区域边界路由请求,该请求将失败,kube-proxy 不会进行重试。这可能很麻烦,需要检测和/或调试。

一种方法是在工作节点级别添加一些跨区域网络验证组件。我们还建议监控和提醒您的应用程序 Pod,以便您可以检测并报警。

在这场战斗中还有另一个秘密武器。如果您不依赖 kube-proxy,而是选择包括诸如断路器和超时自动重试等功能的解决方案,则可以在不修改应用程序或 Kubernetes 或 OpenShift 的情况下克服这些情况及其他情况。像IstioRed Hat Service Mesh这样的服务网格解决方案向每个 Pod 引入了一个完整的 sidecar 容器网格。这些 sidecar 容器运行 Envoy,一个小型、轻量级、高效的负载均衡器,包括先进的断路、负载均衡和重试功能。在这种类型的服务网格配置中,每个 sidecar 都足够智能,能够停止将流量路由到任何与其通信失败的终端 IP。

摘要

现在我们已经有了一个坚实的基础,可以理解如何测量和计算系统的可用性。此外,我们清楚地了解了 Kubernetes 如何提供工具和架构来实现高可用的应用程序和服务平台。了解系统如何运作以及其故障模式对开发团队正确设计他们的应用程序和定义 Kubernetes 和 OpenShift 资源至关重要。SRE 和运维团队应熟悉这些故障模式,并相应地计划监控和警报。

拥有这些 Kubernetes 工具,我们应该准备好构建一个正确架构的 Kubernetes 或 OpenShift 集群,并提供高可用的服务。我们还有必要的方程式来推导基于我们所做选择的服务水平目标(SLO)。凭借本章概述的分析和工具,您有能力对 SLO 做出明智的决策,并创建符合您目标的实现方案。

^(1) 不幸的是,PASS 已经停止运营,并且 2012 年初的演讲内容不可用。

^(2) Gavin McCance 在 2012 年 11 月 19 日的演讲“CERN 数据中心演进”中提到,https://oreil.ly/F1Iw9

^(3) iptables 的 Wikipedia 条目提供了更多信息。

^(4) IP Virtual Server 的 Wikipedia 条目提供了更多信息。

^(5) 查看Kubernetes 文档关于 kube-proxy以获取更多信息。

^(6) 查看Kubernetes 文档关于节点可靠性以获取更多信息。

^(7) 更多信息,请参阅etcd FAQ

第五章:跨集群的持续交付

云原生应用有可能颠覆整个行业。其中一个关键原因是它们支持持续交付。当市场环境发生变化,应用程序需要更新以应对迅速出现的现实约束时,持续交付使应用程序能够快速适应以解决这些新出现的问题。以下是容器镜像、Kubernetes 和 OpenShift 如何支持 DevOps 原则以促进持续交付的简要概述:

小批量更改

所有更改都应该是增量和有限的。当发生故障时,小批量更改通常比大规模、破坏性的更改更容易恢复。

对所有事物进行源代码控制

了解已经进行的所有更改对于理解已经进行的更改以及识别代码库或配置中回归原因非常有帮助。

类似生产的环境

开发人员应该能够访问与生产环境相对应的环境和工具。生产环境通常比开发或质量保证(QA)环境规模更大,配置更复杂。这种差异可能意味着在早期阶段正常工作的功能在生产环境中无法正确运行,而这是它们真正重要的地方。

运维实践的左移

我们应该在开发过程的早期阶段就暴露出健康管理、日志收集、变更管理等行为。

更改的持续集成

所有更改都应该持续构建和部署在一起,以便识别各种更改相互交织导致意外问题或 API 不兼容性的情况。

高度自动化的测试与持续反馈

为了管理速度,我们必须自动化我们的测试和验证工作,以便我们始终可以进行测试(ABT)。

在本章中,我们概述了支持跨集群生产环境中持续交付的各种工具和方法。我们首先讨论了 Helm,这是一个用于 Kubernetes 应用程序的流行打包工具。接下来我们介绍了 Kustomize,它提供了重新利用现有 Kubernetes YAML 文件以实现多种目的和配置的能力,同时避免使用模板。然后,我们描述了几种支持持续交付流水线的流行方法,包括 GitOps、Razee 和 Tekton。最后,我们通过对 OpenShift 流水线和 Open Cluster Management 项目进行广泛讨论来结束本章,这些项目用于在多个集群中部署应用程序。

Helm

Helm 是 Kubernetes 的包管理器。它使您能够创建一个包含构成应用程序的所有资源的多个模板的软件包。您可以期望在 Helm 软件包中找到的常见 Kubernetes 资源包括ConfigMaps、部署、PersistentVolumeClaims、服务等等。

Helm 提供其自己的CLI,该 CLI 将生成一组模板文件集合,并支持将变量值传递到这些模板文件中。Helm 生成的模板文件集称为 Helm 图表。然后,CLI 将从模板文件生成完整的 YAML 文件,打包所有相关文件,并部署该图表。

使用helm create命令通过传递图表名称来创建新包。因此,要创建名为firstchart的新图表,我们执行以下操作:

$ helm create firstchart

此命令将创建以下文件和子文件夹:

  • Chart.yaml

  • charts/

  • templates/

  • values.yaml

templates 文件夹包含一些生成的模板,用于表示关键的 Kubernetes 抽象,如部署和服务账户。

  • NOTES.txt

  • _helpers.tpl

  • deployment.yaml

  • hpa.yaml

  • ingress.yaml

  • Service.yaml

  • serviceaccount.yaml

  • tests/

_helpers.tpl 文件包含部分模板,用于生成诸如 Kubernetes 选择器标签和用于服务账户的名称等值。然后,诸如 deployment.yamlserviceaccount.yaml 的模板文件使用这些部分模板获取这些值。模板文件还从 values.yaml 文件中提取值。该文件包含用户可以更改的变量,例如 replicaCount。这些变量也传递到诸如 deployment.yamlserviceaccount.yaml 的模板文件中,并用于设置所需值。

一旦您拥有适当的模板文件集和为应用程序设置适当的变量,就可以打包与此 Helm 图表相关的所有内容了。使用helm package命令即可完成此操作。在我们的示例中,我们将如下调用helm package

$ helm package firstchart

Successfully packaged chart and saved it to:
/Users/bradtopol/go/k8s.io/helmexample/firstchart-0.1.0.tgz

如示例所示,运行helm package命令会生成一个包含与 Helm 图表相关所有内容的归档文件。现在,如果我们想要安装 Helm 图表,只需运行以下helm install命令:

$ helm install firstchart_release1 firstchart-0.1.0.tgz

helm install命令在其最基本的形式中需要两个参数,即发布名称和归档文件。helm install命令非常灵活,可以以多种不同的方式安装 Helm 图表及其内容。此外,已安装的 Helm 发布可以轻松使用helm update命令进行更新。有关 Helm 及其功能的更多信息,请参阅Helm 文档

Kustomize

在生产环境中运行 Kubernetes 时,通常会有大量用于部署和配置应用程序的 YAML 文件。此外,通常会将这些配置文件存储在某种形式的源代码控制中,以实现持续交付。生产环境的运行经验表明,出于各种原因,配置 YAML 文件通常需要定制。例如,可能需要根据开发和生产环境之间的差异进行配置更改。或者可能需要自定义前缀名称以区分类似的应用程序。为了解决这些需求,Kubernetes 的 kubectl CLI 已经整合了一个名为 Kustomize 的最新开发工具。Kustomize 工具提供了一种无模板的方法来定制 Kubernetes 配置。^(1) Kustomize 工具具有几个关键特性,既减少了管理生产部署的大量 Kubernetes 配置的复杂性,又促进了持续部署方法的使用。Kustomize 提供的主要功能包括 generators、composition、patches 和 overlays。

生成器

Kubernetes 部署的最佳实践建议,配置和秘密数据不应硬编码到应用程序中。相反,这些信息应存储在像 ConfigMaps 和 secrets 这样的 Kubernetes 资源中。Kustomize 提供了 generators 的概念,可以创建 ConfigMaps 和 secrets 来简化部署的这一方面。Kustomize 使用 YAML 文件描述应生成的内容。以下 Kustomize YAML 用于生成一个 Kubernetes secret:

secretGenerator:
- name: sample-secret
 literals:
 - username=admin
 - password=letbradin

要运行上述示例,将文件保存为 kustomization.yaml,并放入一个名为 generateSecret 的新文件夹中。现在可以通过以下步骤运行它:

$ kubectl kustomize ./generateSecret

在运行以上命令后,Kustomize 将生成一个如下所示的 Kubernetes secret 资源:

apiVersion: v1
data:
 password: bGV0YnJhZGlu
 username: YWRtaW4=
kind: Secret
metadata:
 name: sample-secret-dh2bm897df
type: Opaque

作为替代方案,Kustomize 可以生成一个 secret,其中用户名和密码存储在一个单独的文件中。例如,创建一个名为 password.txt 的文件,并将以下键值对存储在文件中:

username=admin
password=letbradin

现在 password.txt 中有了这些值,您可以创建一个 Kustomization 文件来生成一个 secret,并从 password.txt 中提取用户名和密码,如下所示:

secretGenerator:
- name: sample-secret2
 files:
 - password.txt

要运行上述示例,将文件保存为 kustomization.yaml,并放入一个名为 generateSecret2 的新文件夹中。现在可以通过以下步骤使用此文件运行 Kustomize:

$ kubectl kustomize ./generateSecret2

运行此命令后,Kustomize 将生成一个类似于以下显示的 Kubernetes secret 资源:

apiVersion: v1
data:
 password.txt: CnVzZXJuYW1lPWFkbWluCnBhc3N3b3JkPWxldGJyYWRpbgo=
kind: Secret
metadata:
 name: sample-secret2-77bf8kf96f
type: Opaque

在接下来的部分中,我们讨论 Kustomize 的另一个关键功能:将单个 Kubernetes 资源文件聚合到单个部署 YAML 文件中。

合成

Kubernetes 部署通常是从大量的 YAML 文件创建的。这些 YAML 文件本身可能被用于多种目的。Kustomize 支持组合的概念,这使得可以选择并将单个 YAML 文件聚合到一个部署 YAML 文件中。组合功能还减少了复制 YAML 文件然后对这些重复文件进行轻微修改以用于不同目的的需求。

为了展示 Kustomize 组合的功能,让我们创建一些我们想要聚合的 Kubernetes YAML 资源文件。首先,在一个名为composition的新文件夹中创建一个名为deploymentset.yaml的文件,并将以下内容存储在其中:

kind: Deployment
metadata:
 name: nginx
 labels:
  app: webserver
 annotations:
  deployment.kubernetes.io/revision: "1"
spec:
 replicas: 3
 selector:
  matchLabels:
   app: webserver
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
  spec:
   containers:
   - name: nginx
     image: nginx:1.7.9
     ports:
     - containerPort: 80

接下来,在composition文件夹中的一个名为service.yaml的文件中存储以下服务资源 YAML:

apiVersion: v1
kind: Service
metadata:
 name: nginx
 labels:
  app: webserver
spec:
 ports:
 - port: 80
   protocol: TCP
 selector:
   app: webserver

现在,您可以创建一个自定义文件,通过以下方式能够组合deploymentset.yamlservice.yaml

resources:
- deploymentset.yaml
- service.yaml

要运行此示例,在之前创建的composition文件夹中将文件保存为kustomization.yaml。现在,您可以通过以下方式使用此文件运行 Kustomize:

$ kubectl kustomize ./composition

运行此命令后,Kustomize 将生成如下所示的聚合部署 YAML:

apiVersion: v1
kind: Service
metadata:
 labels:
  app: webserver
 name: nginx
spec:
 ports:
 - port: 80
   protocol: TCP
 selector:
   app: webserver
---
apiVersion: apps/v1
kind: Deployment
metadata:
 annotations:
  deployment.kubernetes.io/revision: "1"
 labels:
  app: webserver
 name: nginx
spec:
 replicas: 3
 selector:
  matchLabels:
   app: webserver
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
  spec:
   containers:
   - image: nginx:1.7.9
     name: nginx
     ports:
     - containerPort: 80

在接下来的部分中,我们将介绍 Kustomize 的补丁功能,这使我们能够避免在需要对现有 YAML 文件进行小改动时复制整个 Kubernetes YAML 文件。

补丁

在许多情况下,用于部署的 YAML 文件可能与现有的 YAML 文件非常相似,只需要对其中一个资源进行小改动。例如,也许一个 YAML 文件中唯一需要更改的是要创建的副本数量。对于这种情况,Kustomize 提供了补丁的概念,能够对描述在 YAML 文件中的资源进行小修改。这比不得不复制完整的 YAML 文件然后对副本文件进行小修改的方法要好得多。

为了展示补丁的功能,让我们创建一个接近我们想要的但需要进行小改动的 Kubernetes YAML 资源文件。首先,在一个名为patch的新文件夹中创建一个名为deploymentset.yaml的文件,并将以下内容存储在其中:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
 labels:
  app: webserver
 annotations:
  deployment.kubernetes.io/revision: "1"
spec:
 replicas: 3
 selector:
  matchLabels:
   app: webserver
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
  spec:
   containers:
   - name: nginx
     image: nginx:1.7.9
     ports:
     - containerPort: 80

上面显示的部署 YAML 文件接近我们想要的,但现在我们需要一个新版本的此文件,需要提供六个副本而不是三个。与复制整个deploymentset.yaml资源文件然后更改replicas字段值不同,我们可以创建一个更小且更易于维护的补丁文件。

这是一个简单的补丁文件,可用于创建一个带有修改后replicas字段值的重复deploymentset.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
spec:
 replicas: 6

请注意,此小文件标识资源类型、资源名称和我们希望修改的属性字段。此小文件为 Kustomize 提供了足够的信息来识别需要打补丁的字段。要使用此补丁文件,请将其保存在您刚刚创建的patch文件夹中的新文件update_replicas.yaml中。

创建了补丁文件后,现在我们可以创建一个kustomization.yaml文件,标识我们希望修改的deploymentset.yaml文件,并将update_replicas.yaml文件标识为包含要修改的字段信息的文件:

resources:
- deploymentset.yaml
patchesStrategicMerge:
- update_replicas.yaml

要运行前面的例子,请将文件保存为kustomization.yaml,保存在先前创建的patch文件夹中。现在可以通过以下方式使用此文件运行 Kustomize:

$ kubectl kustomize ./patch

运行此命令后,Kustomize 将生成如下所示的修补部署 YAML 文件:

apiVersion: apps/v1
kind: Deployment
metadata:
 annotations:
  deployment.kubernetes.io/revision: "1"
 labels:
  app: webserver
 name: nginx
spec:
 replicas: 6
 selector:
  matchLabels:
   app: webserver
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
  spec:
   containers:
   - image: nginx:1.7.9
     name: nginx
     ports:
     - containerPort: 80

在下一节中,我们将描述叠加能力,这是 Kustomize 提供的最强大的功能之一。

叠加

在前几节描述的功能基础上,overlays功能是用户从 Kustomize 中看到的最重要的功能之一。Overlays 提供了一种方法,用户从一个包含一组 Kubernetes 部署 YAML 文件的基础文件夹开始,并使用增强和定制部署 YAML 文件的叠加文件夹来构建基础文件夹。在本节中,我们演示了如何使用 Kustomize 叠加来管理在开发、暂存和生产 Kubernetes 环境中使用部署 YAML 文件时发生的差异。

为了演示 Kustomize 如何以这种方式使用,让我们首先创建一个名为base的新文件夹,并将我们在“组合”中创建的deploymentset.yamlservice.yaml文件复制到该文件夹中。有了这些文件在base文件夹中,我们现在可以在base文件夹中创建一个kustomization.yaml,引用部署 YAML 文件,如下所示:

resources:
- deploymentset.yaml
- service.yaml

接下来,我们将创建一个叠加文件夹,为开发环境定制基础部署文件。我们首先创建一个名为development的新文件夹。在此文件夹内,如下所示创建一个新的kustomization.yaml

commonLabels:
 env: development
bases:
- ../base
namePrefix: dev-
patchesStrategicMerge:
- update_replicas.yaml

kustomization.yaml所示,Kustomization 文件首先定义了一个名为env的新标签,其中包含值development。由于此标签作为commonLabels字段的一部分进行定义,其效果是该标签将设置在包括在kustomization.yaml中的所有资源上。然后,Kustomization 声明其基础将是位于base文件夹中的部署 YAML 文件。接下来,Kustomization 定义了一个namePrefix,其值为dev-。此声明的效果是为所有资源名称和引用添加前缀。在kustomization.yaml的最后部分,声明了一个补丁,该补丁包含了对位于base目录中的部署 YAML 文件的修改。补丁包含在文件update_replicas.yaml中。update_replicas.yaml的内容如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
spec:
 replicas: 2

update_replicas.yaml文件包含一个补丁,将开发环境的副本数量修改为2。要运行此示例,请确保将该补丁保存在名为update_replicas.yamldevelopment文件夹中。现在,您可以通过以下步骤运行 Kustomize,使用kustomization.yamlupdate_replicas.yaml文件:

$ `kubectl` `kustomize` `./development`

在执行此命令后,Kustomize 将生成如下的修补部署 YAML:

apiVersion: v1
kind: Service
metadata:
 labels:
  app: webserver
  env: development
 name: dev-nginx
spec:
 ports:
 - port: 80
   protocol: TCP
 selector:
  app: webserver
  env: development
---
apiVersion: apps/v1
kind: Deployment
metadata:
 annotations:
  deployment.kubernetes.io/revision: "1"
 labels:
  app: webserver
  env: development
 name: dev-nginx
spec:
 replicas: 2
 selector:
  matchLabels:
   app: webserver
   env: development
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
    env: development
  spec:
   containers:
   - image: nginx:1.7.9
     name: nginx
     ports:
     - containerPort: 80

在审查生成的部署文件时,我们看到了几个由使用kustomization.yamlupdate_replicas.yaml文件导致的变化。首先,请注意现在在服务和部署资源以及用于创建容器副本的template部分中都存在一个新的env标签,其值为development。还要注意,部署的 YAML 中列出的所有名称现在都有一个前缀dev-。最后,开发环境的部署 YAML 中副本的数量已经修改,以表示只需创建两个副本。

接下来,我们将创建kustomize.yamlupdate_replicas.yaml文件,用于为分期环境定制基本部署 YAML 文件。在我们的情况下,分期环境应该在所有资源上具有一个env标签,其值为staging,所有资源的名称应该以staging-为前缀,分期环境使用的副本数量为五。与之前用于开发环境自定义的示例类似,我们首先创建一个名为staging的新文件夹。在该文件夹内,我们创建一个如下所示的新kustomization.yaml

commonLabels:
 env: staging
bases:
- ../base
namePrefix: staging-
patchesStrategicMerge:
- update_replicas.yaml

在此示例中,我们将env定义为一个commonLabel,并将其值设置为staging。我们创建了一个新的namePrefix,其值为staging-。我们还再次创建了一个update_replicas.yaml文件,该文件将作为补丁文件,在基本部署 YAML 文件在分期环境中使用时修改副本数量。update_replicas.yaml的内容如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
spec:
 replicas: 5

再次,update_replicas.yaml 文件包含一个补丁,用于修改副本数量。对于暂存环境,我们选择使用五个副本。要运行此示例,请确保将补丁保存在 staging 文件夹中,命名为 update_replicas.yaml。现在,您可以通过以下方式运行 Kustomize,使用 kustomization.yamlupdate_replicas.yaml 文件:

$ kubectl kustomize ./staging

运行此命令后,Kustomize 将生成如下的补丁部署 YAML:

apiVersion: v1
kind: Service
metadata:
 labels:
  app: webserver
  env: staging
 name: staging-nginx
spec:
 ports:
 - port: 80
   protocol: TCP
 selector:
  app: webserver
  env: staging
---
apiVersion: apps/v1
kind: Deployment
metadata:
 annotations:
  deployment.kubernetes.io/revision: "1"
 labels:
  app: webserver
  env: staging
 name: staging-nginx
spec:
 replicas: 5
 selector:
  matchLabels:
   app: webserver
   env: staging
  strategy:
   rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1
   type: RollingUpdate
  template:
   metadata:
    labels:
     app: webserver
     env: staging
   spec:
    containers:
    - image: nginx:1.7.9
      name: nginx
      ports:
      - containerPort: 80

再次,我们看到由于使用了我们的 kustomization.yamlupdate_replicas.yaml 文件所导致的几个更改。首先,请注意现在服务和部署资源以及用于创建容器副本的 template 部分中都存在一个新的 env 标签,其值为 staging。还请注意,部署 YAML 中列出的所有名称现在都具有 staging- 的前缀。最后,请注意,开发部署 YAML 的副本数量已被修改,表示应创建五个副本。

对于我们的最终示例,我们将使用相同的技术修改用于生产环境的基础部署 YAML 文件。在我们的生产环境中,所有资源的标签应具有值为 productionenv 标签,所有资源的名称应具有 prod- 的前缀,并且生产环境中应使用 20 个副本。我们首先创建一个名为 production 的新文件夹。在此文件夹内,我们创建一个新的 kustomization.yaml,如下所示:

commonLabels:
 env: production
bases:
- ../base
namePrefix: prod-
patchesStrategicMerge:
- update_replicas.yaml

我们再次创建一个 update_replicas.yaml 文件,用作在将基础部署 YAML 文件用于生产环境时修改副本数量的补丁文件。update_replicas.yaml 的内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
spec:
 replicas: 20

要运行此示例,请确保将补丁保存在 production 文件夹中,命名为 update_replicas.yaml。现在,您可以通过以下方式运行 Kustomize,使用 kustomization.yamlupdate_replicas.yaml 文件:

$ kubectl kustomize ./production

运行此命令后,Kustomize 将生成带有正确标签、前缀和副本数量值的补丁部署 YAML,如下所示:

apiVersion: v1
kind: Service
metadata:
 labels:
  app: webserver
  env: production
 name: prod-nginx
spec:
 ports:
 - port: 80
   protocol: TCP
 selector:
  app: webserver
  env: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
 annotations:
  deployment.kubernetes.io/revision: "1"
 labels:
  app: webserver
  env: production
 name: prod-nginx
spec:
 replicas: 20
 selector:
  matchLabels:
   app: webserver
   env: production
 strategy:
  rollingUpdate:
   maxSurge: 1
   maxUnavailable: 1
  type: RollingUpdate
 template:
  metadata:
   labels:
    app: webserver
    env: production
  spec:
   containers:
   - image: nginx:1.7.9
     name: nginx
     ports:
     - containerPort: 80

正如我们在这个全面的示例中展示的那样,Kustomize 能够管理用于开发环境、暂存环境和生产环境的部署 YAML 文件,并减少了我们需要维护所有这些环境的部署 YAML 文件的数量。我们避免了大量的文件复制粘贴,并最终得到了一个更易管理的解决方案。在下一节中,我们将展示如何使用 Kustomize 直接部署其生成的部署文件。

Kustomize 生成的资源文件的直接部署

在所有之前的 Kustomize 示例中,我们生成了修改后的部署 YAML,并将它们输出,以清晰展示 Kustomize 生成的内容。Kustomize 提供了另一种选项,即可以自动将生成的部署 YAML 文件提交给 Kubernetes 进行处理。这样可以避免首先生成所有部署 YAML 文件的步骤,而是直接使用 Kustomize 来部署生成的内容。要使用 Kustomize 直接部署到 Kubernetes,我们使用 kubectl apply -k 选项,并传入所需的自定义目录。例如,如果我们想直接部署我们刚刚讨论过的生产示例,我们将执行以下操作:

$ kubectl apply -k ./production/

此外,Kustomize 还提供了查看和删除其生成和部署的 Kubernetes 资源对象的命令。关于这些功能的更多详细信息可以在 Kustomization 文档 中找到。在下一节中,我们介绍 GitOps 的概念,这是一种流行的云原生方法论,用于直接从 Git 存储库驱动自动化持续交付。

GitOps

GitOps 是使用 Git 管理基础设施即代码的理念。这个理念最初由 Weaveworks 提出,^(2) 随着更多面向容器化的软件交付方法的流行,GitOps 已成为行业内的热门话题。基本的 GitOps 流程始终如一:对源代码的更新触发自动化流程,最终验证潜在的新变更或将新变更交付到正在运行的环境中。

应用 GitOps 包括以下步骤:

  1. 容器化应用程序以声明性基础设施表示(可通过 Kubernetes API 以 YAML 表示轻松实现),并存储在 Git 中。

  2. 所有变更均源自您的源代码控制修订系统,通常是 GitHub、GitLab、Bitbucket 等基于 Git 的系统。

  3. 像拉取请求或合并请求这样的代码审查技术允许您应用审查流程,甚至自动验证以确保更改在广泛推出之前能够正确运行。

  4. Kubernetes(或更广义地说是软件代理)随后用于在将更改合并到存储库中的适当分支时应用和协调所需的状态。

这个流程的前半部分实际上关乎于您的团队和组织文化。您的组织是否有足够的纪律性和适当的技能集,可以通过 Git 间接进行所有更改,而不是直接操作目标基础设施?在应用数量、交付阶段数量和实际集群数量方面,您是否有规模,可以管理库和分支的增加?与任何文化变革一样,寻找在您的组织中适应新思想并且可以成为广泛组织良好代表例的团队。在尝试更广泛地重新组织团队交付实践之前,专注于一个特定的应用程序或一组相关应用程序应用这种方法的端到端。

流程的后半部分是您拥有各种工具,这些工具已经发展出来,可以简化从 Git 采纳更改、将更改应用于运行环境并验证更改的管理。我们将在接下来的章节中审查一些可以帮助您从 Git 采纳更改并将其应用于您的 Kubernetes 环境的项目。

使用拉取请求/合并请求(PR/MR)可以利用功能分支完成代码库的增强和修复,并在与主要交付分支集成之前验证这些更改。这些 PR/MR 分支通常会对代码质量的几乎所有方面进行自动化验证,包括安全扫描、代码检查或团队或组织要求的最佳实践、自动化单元测试、自动化功能测试、自动化集成测试以及团队其他成员的审查,以确保更改与系统其余部分的目标和用例一致。

GitOps 在管理较少独立应用程序和较少活动环境的系统中效果良好(例如软件即服务,或 SaaS)。当您的交付产出包括必须打包和版本化的软件时,GitOps 仍然可以增加价值,但可能成为更大故事的一部分。

考虑一个使用 Git 存储库中托管源代码管理的单个应用程序。对于发布流程的每个阶段,您将创建一个不同的 Git 分支。在最基本的版本中,您持续部署单个“主”分支,在您的终端用户可访问的生产应用程序中。在其他情况下,您的发布流程可能需要额外的较低环境,因此需要像“主”(用于活跃开发的代码)、“候选”(用于用户验收测试中的代码)和“稳定”(用于生产中运行的代码)这样的相应分支。例如,您可能有“主”和“稳定”分支,“主”分支积累开发中的更改,“稳定”跟踪在生产中部署的代码和配置版本。许多组织可能还有其他层次的验证,包括质量工程、用户验收测试、性能和规模测试等。您希望控制的每个独特阶段可能意味着在您的 Git 存储库中另一个分支。

在接下来的部分,我们介绍 Razee,这是我们在本章中调查的几种适用于 Kubernetes 的生产质量持续交付系统之一。

Razee

Razee 是一种开源持续部署系统,最初由 IBM 开发,用于解决在支持数万个 Kubernetes 集群的极限规模环境中出现的问题。Razee 自动化了在大量集群和环境中部署 Kubernetes 资源,并提供了几个关键功能,以解决管理大量集群时出现的问题。

与其他部署系统相比,Razee 采用拉取式部署方法,使 Kubernetes 集群能够自我更新。此外,Razee 还具有动态库存创建功能,允许其确定它管理的所有 Kubernetes 集群中正在运行的内容。通过此功能,操作员可以了解他们的集群中运行的应用程序和版本。Razee 保留了此库存数据的历史记录,并提供警报,以帮助解决集群中的问题。

Razee 提供了大量基于规则的支持,用于集群分组,有助于简化管理大量集群。Razee 还使用基于规则的方法来编排其拉取式更新部署模型。所有这些功能的结合使 Razee 能够支持在多个可用性区域内自动化部署和管理数万个集群,无需手动干预。有关 Razee 扩展性方法和提供的关键功能的更多详细信息,请参阅Razee 文档

Argo CD

Argo CD是一个声明式持续交付系统,将 Git 仓库的内容与 Kubernetes 集群持续对比。Argo CD 拥有多种灵活的访问控制模型,从每个命名空间到每个集群的部署,或者您可以通过 Kubernetes secrets 为多个集群提供凭据以管理多个集群。

Argo CD 很好地适应使用 Helm 图表、Kustomizeksonnet对资源进行模板化更改的 Kubernetes 应用程序。一旦部署和配置完成,Argo CD 将响应仓库的提交挂钩或持续轮询的变更,并将这些变更应用到集群中。在许多方面,它类似于 Puppet 或 Chef 风格的收敛模型。如果在集群外进行更改,最终会恢复到期望的状态。

就我写作时而言,Argo CD 已经开始关注其他 API 种类,包括ApplicationSet,这些 API 允许将资源部署到多个集群。一个提议的路径将使 Argo CD 能够利用来自 Open Cluster Management 的集群库存,自动为机群成员配置 GitOps。

想了解更多有关 Argo CD 的信息,我们建议从项目网站的入门指南开始。

Tekton

Tekton 是一个基于 Kubernetes 的云原生应用程序的持续集成和持续交付(CI/CD)系统。由于 Tekton 作为基于 Kubernetes 的应用程序运行,它被构建为可扩展的云原生应用程序。这是与传统的非云原生 CI/CD 系统相比的一个重大优势,后者更容易出现故障或性能瓶颈。

Tekton 最初是 Knative 无服务器工作负载平台的构建系统。因为它作为通用的 CI/CD 平台提供了价值,所以在 2019 年 3 月被转换为独立项目,并捐赠给 Continuous Delivery Foundation。^(3)

Tekton 提供了 CLI 和仪表板用户界面,用于管理其 CI/CD 工作负载。它还支持事件触发器和 Webhook。您可以在Tekton Pipeline GitHub 站点IBM Developer Tekton 教程页面找到几篇入门 Tekton 的优秀教程。在接下来的几节中,我们将介绍 Tekton 的基本概念:任务和流水线。

任务

任务代表应按特定顺序执行的命令或工具集合。每个命令或工具被表示为一个步骤,定义要执行的命令或工具以及包含该命令或工具的容器镜像。以下示例展示了一个简单的任务,其中包含两个独立的步骤,每个步骤都运行一个 echo 命令:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
 name: simple-task-example
spec:
 steps:
 - name: echo
   image: ubuntu
   command:
   - echo
   args:
   - "This is step one of our first simple task example"
 - name: echo2
   image: ubuntu
   command:
   - echo
   args:
   - "This is step two of our first simple task example"

要运行此任务,请先将前一个示例保存为名为simpleexample.yaml的文件。接下来,您需要创建一个TaskRun资源,该资源将用于在 Kubernetes 集群上以独立方式运行任务。以下是我们将使用的TaskRun资源的 YAML:

apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
 name: simple-task-example-run
spec:
 taskRef:
  name: simple-task-example

正如我们示例的TaskRun YAML 中所示,我们创建一个TaskRun资源并为其命名。然后在taskRef字段中,我们提供要在 Kubernetes 集群上运行的任务的名称。要部署TaskRun资源,请将前一个示例保存为simpleexamplerun.yaml

在安装了 Tekton 的 Kubernetes 集群上,运行以下命令:

$ kubectl apply -f simpleexample.yaml
$ kubectl apply -f simpleexamplerun.yaml

运行这些命令后,您应该看到如下输出:

task.tekton.dev/simple-task-example created
taskrun.tekton.dev/simple-task-example-run created

要确认任务是否正确运行,我们可以使用tkn taskrun logs --last -f Tekton 命令查看刚刚运行的simple-task-example的日志:

$ tkn taskrun logs --last -f
[echo] This is step one of our first simple task example

[echo2] This is step two of our first simple task example

如果您需要更多关于任务执行的详细信息,可以使用tkn taskrun describe命令获取一个更大的信息列表:

$ tkn taskrun describe simple-task-example-run
Name: simple-task-example-run
Namespace: default
Task Ref: simple-task-example
Timeout: 1h0m0s
Labels:
 app.kubernetes.io/managed-by=tekton-pipelines
 tekton.dev/task=simple-task-example

Status

STARTED      DURATION   STATUS
1 minute ago 16 seconds Succeeded
 Input Resources
No input resources

Output Resources
 No output resources
 Params

 No params

 Steps

 NAME    STATUS
 ∙ echo  Completed
 ∙ echo2 Completed

Sidecars

No sidecars

尽管在上一个示例中未显示,但 Tekton 具有大量内置功能,用于常见的集成步骤,如从 Git 存储库中提取内容和构建容器镜像。此外,Tekton 为任务提供了一个名为工作空间的新功能,这是一个共享的持久卷。工作空间提供了一个共享存储区,任务可以在其中共享文件,并与正在协作处理它的其他任务一起使用。有关工作空间的更多详细信息,请参阅Tekton 工作空间文档

在接下来的部分中,我们将描述管道,这是 Tekton 用于启用多个任务共同进行构建集成和部署活动的构造。

管道

Tekton 提供了管道的概念作为其创建任务集合并排序执行任务组的机制。在某些情况下,任务依赖于其他任务,并且必须声明它们在依赖的任务之后执行。在其他情况下,任务可能不依赖于彼此,并且这些任务可以并发运行。管道构造管理其任务集合以及它们执行的顺序,以及每个任务有权使用的共享工作空间。为了更好地理解管道的工作原理,我们需要至少两个任务。在前一部分中,我们定义并部署了simple-task-example任务。现在我们将创建第二个任务称为simple-task-example2。此任务如下所示:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
 name: simple-task-example2
spec:
 steps:
 - name: echo
 image: ubuntu
 command:
 - echo
 args:
 - "This is step one of our second simple task example"
 - name: echo2
 image: ubuntu
 command:
 - echo
 args:
 - "This is step two of our second simple task example"

要部署此任务,请将前一个示例保存在名为simpleexample2.yaml的文件中。通过运行以下命令来部署此任务:

$ kubectl apply -f simpleexample2.yaml

您可能注意到,对于我们的第二个任务示例,我们没有提供相应的TaskRun来运行它。我们之所以没有第二个TaskRun,是因为我们将这两个任务分组到一个管道中,管道将负责创建其需要运行其任务的任何TaskRun对象。

此示例使用的流水线命名为 simple-pipeline,声明其管理两个任务,分别称为 simple-task-examplesimple-task-example2

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
 name: simple-pipeline
spec:
 tasks:
 - name: simple-task
   taskRef:
    name: simple-task-example
 - name: simple-task2
   taskRef:
    name: simple-task-example2

要运行此流水线,请首先将前面的示例保存到名为 simplepipeline.yaml 的文件中。使用以下命令部署此流水线:

$ kubectl apply -f simplepipeline.yaml

接下来,我们将创建一个 PipelineRun 对象,负责在 Kubernetes 集群上运行此流水线:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
 generateName: run-simple-tasks
spec:
 pipelineRef:
  name: simple-pipeline

将上一个文件保存为 simplepipelinerun.yaml。然后,您可以通过运行以下命令在集群上运行 simple-pipeline 示例:

$ kubectl create -f simplepipelinerun.yaml

要确认流水线是否正确运行,请使用 tkn pipelinerun logs --last -f Tekton 命令,并查看刚刚运行的 simple-pipeline 示例的日志:

$ tkn pipelinerun logs --last -f
[simple-task2 : echo] This is step one of our second simple task example
[simple-task : echo] This is step one of our first simple task example

[simple-task2 : echo2] This is step two of our second simple task example

[simple-task : echo2] This is step two of our first simple task example

在审查日志输出后,我们注意到两个任务并行运行。这与我们定义任务的方式一致。如果两个任务有依赖关系,并且我们需要第二个任务在第一个任务完成后才能执行,Tekton 流水线支持通过在第二个任务中添加 runAfter 声明来实现这一点。以下是示例:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
 name: simple-pipeline
spec:
 tasks:
 - name: simple-task
   taskRef:
    name: simple-task-example
 - name: simple-task2
   runAfter: 
   - simple-task
  taskRef:
   name: simple-task-example2

注意,runAfter 字段明确引用了名为 simple-task 的任务,在允许 simple-task2 执行之前必须完成该任务。

Tekton 具有许多有用的功能,超出本书的范围。有关 Tekton 及其功能的更多信息,请参阅 Tekton Pipeline documentation。此外,Tekton 流水线是 Red Hat 提供的 OpenShift Pipelines 的基础。

OpenShift Pipelines

OpenShift Pipelines 是 Red Hat 提供的基于 Tekton 的完全支持软件产品。使用操作员范例简化了 Tekton 的配置,并帮助您更快地从生态系统中获得价值。

让我们看一个使用 OpenShift Pipelines 的端到端示例,该示例可以从与本书关联的 GitHub 组织 获取。在此示例中,您将看到任务和流水线的 Tekton 概念,用于构建一个简单的应用程序,让您在浏览器中玩 PAC-MAN。

通过从 OpenShift Container Platform 网页控制台(4.4 或更高版本)中的 OperatorHub 目录安装操作员来配置 OpenShift Pipelines:

  1. 转到 Operators > OperatorHub,并搜索“OpenShift Pipelines”,如 Figure 5-1 所示。

    图 5-1. 根据“OpenShift Pipelines”查询筛选的 OperatorHub 目录
  2. 单击瓷砖以显示有关操作员的信息。滚动到页面底部,下载适合您平台的命令行版本(参见 Figure 5-2)。

    图 5-2. 下载适用于您平台的 Tekton CLI (tkn) 的命令行版本
  3. 点击安装。

  4. 选择适合你的 OpenShift 版本的更新频道。例如,如果你正在运行 OCP 4.4.x 版本,请使用 ocp-4.4。

  5. 点击订阅。

您可以通过导航到运算符 > 已安装的运算符并将项目过滤为“openshift-operators”,如 图 5-3 所示,确认安装成功。

图 5-3. 确认成功安装 OpenShift Pipelines 运算符

在使用 Tekton Tasks 组装您的持续交付解决方案时,您可以访问广泛的社区库存的现有任务:

让我们重新利用公共目录中的一些任务来组合我们的工作示例。安装运算符后,您还将获得一组可用的任务:

$ tkn clustertasks ls
NAME DESCRIPTION AGE
buildah 25 minutes ago
buildah-v0-11-3 25 minutes ago
git-clone 25 minutes ago
jib-maven 25 minutes ago
kn 25 minutes ago
maven 25 minutes ago
openshift-client 25 minutes ago
openshift-client-v0-11-3 25 minutes ago
s2i 25 minutes ago
s2i-dotnet-3 25 minutes ago
s2i-dotnet-3-v0-11-3 25 minutes ago
s2i-go 25 minutes ago
s2i-go-v0-11-3 25 minutes ago
s2i-java-11 25 minutes ago
s2i-java-11-v0-11-3 25 minutes ago
s2i-java-8 25 minutes ago
s2i-java-8-v0-11-3 25 minutes ago
s2i-nodejs 25 minutes ago
s2i-nodejs-v0-11-3 25 minutes ago
s2i-perl 25 minutes ago
s2i-perl-v0-11-3 25 minutes ago
s2i-php 25 minutes ago
s2i-php-v0-11-3 25 minutes ago
s2i-python-3 25 minutes ago
s2i-python-3-v0-11-3 25 minutes ago
s2i-ruby 25 minutes ago
s2i-ruby-v0-11-3 25 minutes ago
s2i-v0-11-3 25 minutes ago
tkn 25 minutes ago

现在,我们将创建一个流水线来构建我们的图像,并发布到集群内部的注册表中。首先,在您的 OpenShift 集群中创建一个项目来保存我们即将创建的资源:

$ oc new-project pipelines-tutorial
注意

所有以下示例假设您正在同一命名空间pipelines-tutorial中工作。如果由于某种原因您的KUBECONFIG引用了不同的命名空间,则可以使用oc project命令更新您的上下文:

$ oc project pipelines-tutorial

或者,您可以在oc applyoc createoc get等命令后添加-n pipelines-tutorial标志,例如:

$ oc get pipelines -n pipelines-tutorial

要构建并发布图像,必须授予默认服务帐户pipeline将图像推送到目标注册表的授权。例如,本示例使用 Quay.io 注册表,但任何注册表都可以正常工作。

要为pipeline服务帐户启用授权,您必须创建一个docker-registry密钥,并更新pipeline服务帐户。这些步骤与 Tekton 没有直接关系,但与我们的示例相关:

  1. 创建名为quay.io/*<username>*/pacman的 Quay.io 镜像仓库,以您的用户名命名,按照 Quay.io 文档 进行操作。

  2. 从 Quay.io 下载 Kubernetes 密钥。您可以从“生成加密密码”页面下的设置页面访问此密钥。

  3. 应用该密钥(确保更新*<username>*-pull-secret的默认名称为quay-registry-secret),或使用kubectloc命令行创建密钥:

    $ kubectl create secret docker-registry \
     --docker-server="quay.io" \
     --docker-username="YOUR_USERNAME" \
     --docker-password="YOUR_PASSWORD" \
     --docker-email="YOUR_EMAIL" \
     quay-registry-secret
    
  4. quay-registry-secret补丁到pipeline服务帐户中。OpenShift pipelines 运算符会自动在集群的每个命名空间中创建pipeline服务帐户。通过更新pipelines-tutorial命名空间中的服务帐户,您可以允许任何 Tekton TaskRun 利用此授权来推送图像:

    $ oc patch sa pipeline -p '{"secrets":[{"name":"quay-registry-secret"}]}'
    

我们将开始创建一个流水线,用于构建图像并将其推送到您刚刚定义的图像仓库中:

pipelines/01-pipeline-build-pacman.yaml 

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
 name: build-pacman
spec:
 workspaces:
 - name: shared-workspace
 resources:
 - name: source-repo
 type: git
 - name: image
 type: image
 params:
 - name: dockerfile-path
 type: string
 description: The path to your Dockerfile
 default: "Dockerfile"
 tasks:
 - name: build-image
 taskRef:
 name: buildah
 kind: ClusterTask
 resources:
 inputs:
 - name: source
 resource: source-repo
 outputs:
 - name: image
 resource: image
 params:
 - name: TLSVERIFY
 value: "false"
 - name: DOCKERFILE
 value: "$(params.dockerfile-path)"

build-pacman流水线定义了一个使用buildah ClusterTask的单个任务。输入需要一个带有所需的Dockerfile和相关源文件以及要构建的镜像详细信息的 Git 仓库。

我们使用oc命令行工具创建流水线:

$ oc apply -f 01-pipeline-build-pacman.yaml 
pipeline.tekton.dev/build-and-deploy-pacman created

应用流水线定义后,我们可以验证其是否存在:

$ oc get pipelines
NAME                    AGE
build-and-deploy-pacman 8s

tkn命令行工具为 Tekton 资源提供了一组特定的操作。除了类似于getdescribe的命令等价物外,还有直接的命令来查看任务日志和其他 Tekton 特定的行为:

$ tkn pipelines ls
NAME                    AGE            LAST RUN STARTED DURATION STATUS
build-and-deploy-pacman 21 seconds ago ---      ---     ---      ---

Tekton 流水线定义驱动其行为的参数。为了简化参数的管理,Tekton 还定义了代表在所需流水线行为中经常出现的不同类型对象的PipelineResources。

以下是定义的PipelineResource类型

git

GitHub 仓库

storage

存储 blob

image

容器镜像元数据

cluster

具有访问凭据的 Kubernetes 集群描述

pullRequest

一个 GitHub 拉取请求

cloudEvent

云事件

gcs

由 GCS blob/目录支持的 GCSResource

build-gcs

添加了 BuildGCSResources 以与 Knative 构建兼容

我们将创建PipelineResources,这些资源将成为流水线的输入,并满足输入参数的所需值:

pipelines/02-resource-git-repo-pacman.yaml

apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
 name: pacman-git
spec:
 type: git
 params:
 - name: revision
 value: master
 - name: url
 value: https://github.com/hybrid-cloud-apps-openshift-k8s-book/k8s-example-apps/

pipelines/03-resource-pacman-image.yaml

apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
 name: pacman-image
spec:
 type: image
 params:
 - name: url
 value: quay.io/mdelder/pacman

我们将应用这些资源,然后在我们的PipelineRun中引用它们:

$ oc apply -f 02-resource-git-repo-pacman.yaml \
 -f 03-resource-pacman-image.yaml

现在我们有一个流水线,我们有输入(我们的 Git 仓库和我们要构建的期望镜像)。让我们通过创建一个PipelineRun来触发流水线:

pipelines/04-pipelinerun-build-pacman-01.yaml

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
 generateName: pipelinerun-build-pacman-
spec:
 serviceAccountName: pipeline
 pipelineRef:
 name: build-pacman
 resources:
 - name: source-repo
 resourceRef:
 name: pacman-git
 - name: image
 resourceRef:
 name: pacman-image
 workspaces:
 - name: shared-workspace
 emptyDir: {}
 params:
 - name: dockerfile-path
 value: "pacman-nodejs-app/docker/Dockerfile"

PipelineRun将使用单个buildah任务执行两个操作:克隆 Git 仓库,然后构建镜像并将其发布到您之前创建的 Quay.io 注册表中。

要运行流水线,使用oc create

$ oc create -f pipelines/04-pipelinerun-build-pacman-01.yaml
注意

我们在这里使用oc create而不是oc apply,因为PipelineRun使用generateName而不是name属性。oc apply命令需要name属性,而oc create支持额外的行为来自动生成名称的后缀。

您可以使用tkn命令行工具查看正在运行的流水线:

$ tkn pipelinerun ls
NAME                           STARTED        DURATION STATUS
pipelinerun-build-pacman-qk5lw 23 seconds ago ---      Running

你可以使用tkn命令行跟踪PipelineRun的执行过程:

$ tkn pipelinerun logs -f

输出应该类似于以下内容:

[build-image : git-source-pacman-git-s2mxf]
{"level":"info","ts":1598817082.1290805,"caller":"git/git.go:105","msg":
"Successfully cloned https://github.com/hybrid-cloud-apps-openshift-k8s-book/
k8s-example-apps/ @ master in path /workspace/source"}
...

[build-image : build] STEP 1: FROM node:boron
[build-image : build] Getting image source signatures
[build-image : build] Copying blob 
sha256:3b7ca19181b24b87e24423c01b490633bc1e47d2fcdc1987bf2e37949d6789b5

...

[build-image : push] Getting image source signatures
[build-image : push] Copying blob 
sha256:ec62f19bb3aa1dcfacc9864be06f0af635c18021893d42598da1564beed97448

...

[build-image : push] Writing manifest to image destination
[build-image : push] Copying config 
sha256:854daaf20193c74d16a68ba8c1301efa4d02e133d383f04fedc9532ae34e8929
[build-image : push] Writing manifest to image destination
[build-image : push] Storing signatures

...

在这个例子中,我们构建了容器镜像。让我们进一步将更改应用到集群中。在这一步中,我们将创建一个具有三个不同阶段的流水线:

  1. 构建应用程序镜像。

  2. 获取包含我们部署清单的 Git 仓库(使用 Kustomize)。

  3. 应用 Kustomization 部署清单。

第一步的工作方式与以前完全相同。额外的步骤引入了一些新的想法:

  • 我们将创建一个PersistentVolumeClaim来为我们的 Git 仓库内容提供可用存储空间。否则,在第 2 步中从 Git 检索的文件将无法在第 3 步中使用。

  • 我们将需要为我们的pipeline服务账户添加额外的权限,以允许将部署清单应用到此集群上的应用程序命名空间。

让我们创建PersistentVolumeClaimPersistentVolumeClaim应请求足够的容量,以满足流水线中所有任务所需的所有持久文件系统存储。如果PersistentVolume在任务之间被回收或重新利用,可能会丢失重要状态,并且流水线运行可能会失败。另一方面,如果相同的PersistentVolume在许多流水线运行之间被重复使用,最终可能会耗尽所有可用空间。如果预计相同的PersistentVolume将支持多个并行流水线运行,请确保将accessMode设置为ReadWriteMany

pipelines/00-pvc-shared-workspace.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: shared-workspace
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 1Gi

$ oc apply -f 00-pvc-shared-workspace.yaml 
persistentvolumeclaim/shared-workspace created
注意

随着时间的推移,工作区的状态管理可能成为一个问题。Tekton 0.12 引入了volumeClaimTemplate,可以简化这个过程。否则,您可能会为每个PipelineRun始终创建PersistentVolumeClaimPersistentVolume。对于通过自动化创建的任何资源,请确保定义回收策略,以销毁或允许任何不必要的资源适时过期。

在我们的第一个流水线中,我们更新了system:serviceaccounts:pipelines-tutorial:pipeline服务账户,以允许使用额外的秘钥授权我们的服务账户将镜像推送到我们的 Quay.io 镜像注册表中。在我们的第二个流水线中,我们的服务账户将向运行流水线的相同集群应用部署清单,并需要授权到应用程序命名空间:

$ oc adm policy add-role-to-user edit --namespace pacman \
 system:serviceaccount:pipelines-tutorial:pipeline

通过edit ClusterRoleBindingpacman命名空间,服务账户将能够创建、修改和查看大多数 Kubernetes API 对象,包括部署、服务和 OpenShift 路由。我们选择的示例应用程序在其部署清单中创建了每一个这些对象。

要验证你已经正确应用权限,可以使用can-i命令,它会打印一个简单的“yes”或“no”答案:

$ oc auth can-i get deployments \
 --namespace pacman \
 --as system:serviceaccount:pipelines-tutorial:pipeline

现在我们将创建我们的新流水线:

pipelines/05-pipeline-deploy-pacman.yaml

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
 name: build-and-deploy-pacman
spec:
 workspaces:
 - name: shared-workspace
 resources:
 - name: source-repo
 type: git
 - name: image
 type: image
 params:
 - name: kustomization-path
 type: string
 default: kustomization
 - name: kustomization-git-repo-url
 type: string
 - name: kustomization-git-revision
 type: string
 default: master
 - name: dockerfile-path
 type: string
 description: The path to your Dockerfile
 default: "Dockerfile"
 tasks:
 - name: build-image
 taskRef:
 name: buildah
 kind: ClusterTask
 resources:
 inputs:
 - name: source
 resource: source-repo
 outputs:
 - name: image
 resource: image
 params:
 - name: TLSVERIFY
 value: "false"
 - name: DOCKERFILE
 value: "$(params.dockerfile-path)"
 - name: fetch-repository
 taskRef:
 name: git-clone
 kind: ClusterTask
 workspaces:
 - name: output
 workspace: shared-workspace
 params:
 - name: url
 value: "$(params.kustomization-git-repo-url)"
 - name: subdirectory
 value: ""
 - name: deleteExisting
 value: "true"
 - name: revision
 value: "$(params.kustomization-git-revision)"
 runAfter:
 - build-image
 - name: apply-config
 params:
 - name: kustomization-path
 value: "$(params.kustomization-path)"
 workspaces:
 - name: source
 workspace: shared-workspace
 taskSpec:
 params:
 - name: kustomization-path
 default: "kustomization"
 workspaces:
 - name: source
 steps:
 - name: apply-kustomization
 image: quay.io/openshift/origin-cli:latest
 workingDir: /workspace/source
 command: ['/bin/bash', '-c']
 args:
 - |-
 echo "Applying kustomization in DIR \"$(params.kustomization-path)\""
 oc apply -k $(params.kustomization-path)
 runAfter:
 - fetch-repository

$ oc apply -f 05-pipeline-deploy-pacman.yaml

我们不需要任何额外的PipelineResource来运行这个流水线。实际上,您可能会注意到在此流水线中管理两个相关的 Git 仓库的细节有所不同。在消耗不同任务或定义自己的任务时,您可能会发现如何组装任务以实现您的目标存在轻微的不一致。具体来说,社区git-clone任务不使用git类型的PipelineRes⁠ource,而是接受需要识别仓库 URL 和修订版本的组件部分。

就像之前一样,我们将创建一个PipelineRun并监视其进度:

$ oc create -f 06-pipelinerun-build-and-deploy-pacman-01.yaml 
pipelinerun.tekton.dev/pipelinerun-build-and-deploy-pacman-cjc7b created

再次,你可以使用tkn命令行工具查看所有PipelineRun

$ tkn pipelinerun ls
NAME                                      STARTED        DURATION  STATUS
pipelinerun-build-and-deploy-pacman-cjc7b 3 minutes ago  2 minutes Succeeded
pipelinerun-build-pacman-qk5lw            57 minutes ago 2 minutes Succeeded

您也可以查看或跟随日志。请注意,如果您在PipelineRun完成后运行此操作,则日志顺序将被反转:

$ tkn pipelinerun logs -f pipelinerun-build-and-deploy-pacman-cjc7b

首先定义和排查任务可能会有些容易出错。使用 API 参考,并且不要害怕删除或重新创建初始管道和管道运行以解决引用问题。

现在,我们可以通过使用 Web 浏览器打开路由确认pacman是否成功部署(图 5-4):

$ oc get route pacman --namespace pacman \
 -ojsonpath="{.status.ingress[0].host}"

图 5-4. PAC-MAN 应用程序的成功部署

开放集群管理应用程序

开放集群管理项目是跨一个或多个 OpenShift 集群管理应用程序的新方法。该方法将本地 GitOps 方法应用于将 Kubernetes 对象附加到 Git 存储库。让我们以开源 PAC-MAN 应用程序为基础进行一个简单的示例。

开放集群管理项目专注于管理 Kubernetes 集群的几个方面,包括创建和升级集群、分布式交付和应用程序管理、集群配置的同步以及维护托管集群的合规性和治理的可见性。hub集群运行多集群控制平面,而在托管集群上以一组 Pod 形式运行的轻量级代理则将所需状态应用于所有受管理的集群,并提供健康、搜索索引和合规性的反馈循环。在下一个示例中,我们将仅关注应用程序管理的概念。

开放集群管理应用程序模型依赖于以下概念:

Application

一组相关资源的分组,用于为消费者提供逻辑服务。

Channel

部署所需的应用程序部件的来源。当前支持的通道包括 Git 存储库、对象存储桶和 Helm 存储库。

Subscription

将应用程序的部分连接到一个或多个集群的通道。订阅从发布分支(例如“latest”,“stable”,“production”等)消费一系列版本。

PlacementRule

将订阅链接到一个或多个集群。

Red Hat 高级集群管理 (RHACM)是一个基于开放集群管理项目的完全支持的软件提供。类似于 OpenShift Pipelines 简化了采用 Tekton 和其他项目的设置和生命周期的方式,RHACM 为 Kubernetes 简化了采用开放集群管理项目的方式(图 5-5)。

图 5-5. 在 OpenShift 中安装 Kubernetes 高级集群管理

要安装 Kubernetes 的 RHACM,请按照以下步骤操作:

  1. 通过名称搜索运营商,然后单击安装。

  2. 安装运营商后,创建MultiClusterHub API 的实例:

    $ oc new-project open-cluster-management
    
    $ oc create -f - <<EOF
    apiVersion: operator.open-cluster-management.io/v1
    kind: MultiClusterHub
    metadata:
     namespace: open-cluster-management
     name: multiclusterhub
    spec: {}
    EOF
    
  3. 从 OpenShift 容器平台 web 控制台的应用程序列表中,单击新项目以打开 RHACM web 控制台,如 图 5-6 所示。您可能需要刷新您的网页浏览器以使此新链接出现。

    图 5-6. 在 OpenShift 中打开 RHACM web 控制台

示例假定您在高级集群管理中创建或导入了两个集群,并具有以下标签:

Cluster 1:
 apps/pacman: deployed
 environment: dev
 region: us-east

Cluster 2:
 apps/pacman: deployed
 environment: dev
 region: europe-west3

作为参考,我们假设以下两个托管集群:“cluster1-aws-east”和“cluster3-gcp-europe-west3”。请注意,一个集群(“cluster1-aws-east”)位于北美亚马逊上,而第二个(“cluster3-gcp-europe-west3”)位于欧洲谷歌上(参见 图 5-7)。因此,在此示例中,我们正在向由 OpenShift 支持的多集群和多云平台部署我们的应用程序!

图 5-7. 使用 RHACM web 控制台管理亚马逊和谷歌提供的集群

我们还可以从命令行显示这些托管集群:

$ oc get managedclusters -o yaml
apiVersion: v1
items:
- apiVersion: cluster.open-cluster-management.io/v1
 kind: ManagedCluster
 metadata:
 labels:
 apps/pacman: deployed
 cloud: Amazon
 clusterID: 7de6ab45-58ac-47f7-897d-b742b7197653
 environment: dev
 name: cluster1-aws-east
 region: us-east
 vendor: OpenShift
 name: cluster1-aws-east
 spec:
 hubAcceptsClient: true
 leaseDurationSeconds: 60
 status:
 ...
 version:
 kubernetes: v1.18.3+b0068a8
- apiVersion: cluster.open-cluster-management.io/v1
 kind: ManagedCluster
 metadata:
 labels:
 apps/pacman: deployed
 cloud: Google
 clusterID: 9e170dd8-a463-44c7-a59f-39b7459964ec
 environment: dev
 name: cluster3-gcp-europe-west3
 region: europe-west3
 vendor: OpenShift
 name: cluster3-gcp-europe-west3
 spec:
 hubAcceptsClient: true
 leaseDurationSeconds: 60
 status:
 ...
 version:
 kubernetes: v1.18.3+b0068a8
kind: List
metadata:
 resourceVersion: ""
 selfLink: ""

我们首先定义我们的应用程序,并引用将组成应用程序的Subscription类型:

apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
 name: pacman-app
 namespace: pacman-app
spec:
 componentKinds:
 - group: apps.open-cluster-management.io
 kind: Subscription
 descriptor: {}
 selector:
 matchExpressions:
 - key: app.kubernetes.io/name
 operator: In
 values:
 - pacman

应用程序提供了一种将一组相关部件组合成逻辑单元以便进行管理的方式。根据当前项目准备情况,该应用程序用于理解将部件交付给不同管理集群的情况。目前正在进行工作,以便使用该应用程序汇总健康信息,并总结部署应用程序或其部件的所有支持集群的准备情况。

现在让我们定义频道和订阅,以将我们的应用程序附加到一个或多个集群。该频道简单地引用我们应用程序的 Git 存储库:

apiVersion: apps.open-cluster-management.io/v1
kind: Channel
metadata:
 name: pacman-app-latest
 namespace: pacman-app
 annotations:
 apps.open-cluster-management.io/github-path: kustomization
spec:
 type: GitHub
 pathname: https://github.com/hybrid-cloud-apps-openshift-k8s-book/
openshift-pipeline-example-pacman.git
 # secretRef:
 # name: github-credentials

然后订阅引用频道,包括有关申请变更的分支的详细信息,并在 Git 存储库中隔离相关目录结构。订阅可以通过指定timeWindows进一步限制允许或阻止对源存储库中识别的集群的更改的部署时间。

在这里,我们看到pacman-app的订阅,并参考先前定义的频道:

apiVersion: apps.open-cluster-management.io/v1
kind: Subscription
metadata:
 annotations:
 apps.open-cluster-management.io/git-branch: main
 apps.open-cluster-management.io/github-path: kustomization
 name: pacman-app
 namespace: pacman-app
 labels:
 app.kubernetes.io/name: pacman
spec:
 channel: pacman-app/pacman-app-latest
 placement:
 placementRef:
 kind: PlacementRule
 name: pacman-dev-clusters
 # timewindow:
 # windowtype: blocked
 # location: America/Toronto
 # weekdays: ["Monday","Tuesday","Wednesday","Thursday","Friday"]
 # hours:
 # - start: "06:00AM"
 # end: "05:00PM"

订阅还提供通过packageOverrides向 Kustomization 项目或 Helm charts 提供部署信息的能力。

然后通过PlacementRule将订阅与托管集群匹配。PlacementRule使用匹配选择器来识别应该托管应用程序的管理目标集群。

在下面的示例中,PlacementRule定义了一个选择子句,以选择最多两个具有us-eastus-westeurope-west3区域值并包含标签environment=devapps/pacman=deployed的集群:

apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: pacman-dev-clusters
 namespace: pacman-app
spec:
 clusterConditions:
 - status: "True"
 type: ManagedClusterConditionAvailable
 clusterReplicas: 2
 clusterSelector:

 matchExpressions:
 - key: region
 operator: In
 values:
 - us-east
 - us-west
 - europe-west3
 matchLabels:
 environment: dev
 apps/pacman: deployed

我们可以应用我们示例项目中的所有这些 API 资源:

$ git clone git@github.com:hybrid-cloud-apps-openshift-k8s-book/
openshift-pipeline-example-pacman.git
$ cd openshift-pipeline-example-pacman
$ oc new-project pacman-app
$ oc apply -f deploy/pacman-app.yaml

现在让我们看看如果我们管理两个集群会是什么样子。我们的第一个集群是在亚马逊弹性计算云(EC2)的 us-east 区域运行的 OpenShift 集群。我们的第二个集群是在 Google 计算平台的 europe-west3 区域运行的 OpenShift 集群。我们可以使用以下命令在 RHACM 中检查任何管理的集群:

$ oc get managedclusters --show-labels
NAME HUB ACCEPTED MANAGED CLUSTER URLS JOINED AVAILABLE AGE LABELS
local-cluster true True True 55m 
  cloud=Amazon,clusterID=65333a32-ba14-4711-98db-28c2aa0153d6,
  installer.name=multiclusterhub,installer.
  namespace=open-
cluster-management,local-cluster=true,vendor=OpenShift

cluster1-aws-east true True True 52m 
  apps/pacman=deployed,cloud=Amazon,
  clusterID=7de6ab45-58ac-47f7-897d-
b742b7197653,environment=dev,
  name=cluster1-aws-east,region=us-east,vendor=OpenShift

cluster3-gcp-europe-west3 true True True 52m 
  apps/pacman=deployed,cloud=Google,
  clusterID=9e170dd8-a463-44c7-a59f-
39b7459964ec,environment=dev,name=cluster3-gcp-europe-west3,
  region=europe-west3,vendor=OpenShift

我们的PlacementRule基于我们之前定义的matchLabelsmatchExpressions,已经识别出这两个符合条件的集群:

$ oc get placementrule -n pacman-app pacman-dev-clusters -oyamlapiVersion: 
apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: pacman-dev-clusters
 namespace: pacman-app
 resourceVersion: "3954663"
 selfLink: /apis/apps.open-cluster-management.io/v1/namespaces/pacman-
app/placementrules/pacman-dev-clusters
 uid: 4baae9ee-520c-407e-9cbd-645465e122ea
spec:
 clusterConditions:
 - status: "True"
 type: ManagedClusterConditionAvailable
 clusterSelector:
 clusterReplicas: 2
 matchExpressions:
 - key: region
 operator: In
 values:
 - us-east
 - us-west
 - europe-west3
 matchLabels:
 apps/pacman: deployed
 environment: dev
status:
 decisions:
 - clusterName: cluster1-aws-east
 clusterNamespace: cluster1-aws-east
 - clusterName: cluster3-gcp-europe-west3
 clusterNamespace: cluster3-gcp-europe-west3

我们可以在高级集群管理拓扑视图中查看我们的应用程序及其相关部分(由订阅描述),这些部分部署到我们的两个由PlacementRule确定的管理集群(由通道标识),源自我们的 Git 仓库。在图 5-8 中,我们可以看到该应用程序正好有一个订阅(可能有多个),并且部署在两个集群上。

我们可以选择拓扑图中的元素以查看更多信息,如图 5-9 所示。

图 5-8. 在 RHACM 拓扑视图中显示应用程序的可视化

图 5-9. 在 RHACM 拓扑视图中显示多个集群的详细信息

图 5-10 中描绘的集群图标显示了我们选择的集群。

图 5-10. 显示选定集群的详细信息在 RHACM 拓扑视图中

部署图标展示了我们的部署情况以及它是否成功部署并且当前在我们管理的集群上保持健康状态。

单击“在搜索中启动资源”将显示跨所有管理的集群中pacman部署的详细信息(见图 5-11)。

在这里,我们可以看到我们的pacman部署在两个集群上运行:cluster3-gcp-europe-west3 和 cluster1-aws-east。从这里,我们可以进一步检查相关的对象,包括部署使用的相关 Pod、服务和秘密。

强大的搜索功能允许您以整体视角了解您的应用程序。至少,您能够验证应用程序的部分按预期部署。如果出现问题,这些视图帮助您隔离可能导致观察到的故障的根本原因。以更易消化的形式提供信息有助于 SRE 和开发人员更有效地处理多集群或多云架构的复杂性。

图 5-11. 在 RHACM web 控制台中显示pacman部署

摘要

本章概述了几种支持在传统 Kubernetes 和 OpenShift 集群中进行生产环境持续交付的流行工具和方法。首先介绍了 Helm,这是 Kubernetes 应用程序的流行打包工具。接下来,我们描述了 Kustomize,它提供了使用现有的 Kubernetes YAML 文件进行多种用途和配置的能力,同时避免使用模板。然后,我们描述了几种支持持续交付流水线的流行方法,包括 GitOps、Razee、Tekton 和 Argo CD。最后,我们详细讨论了 OpenShift Pipelines 和 Open Cluster Management 工具,用于在多个集群中部署和管理 OpenShift 应用程序。通过本章学习的技术,您现在对于可用的最流行和经过验证的持续交付选项有了扎实的理解,并且具备了管理跨多个集群的应用程序的实际经验。在下一章中,我们将深入探讨在多集群环境中进行配置和升级的关键操作。

^(1) Jeff Regan 和 Phil Wittrock,“介绍 Kustomize;用于 Kubernetes 的无模板配置定制”,Kubernetes 博客(2018 年 5 月 29 日),https://oreil.ly/fli5E

^(2) Weaveworks,“GitOps 指南”,https://oreil.ly/QIQ24.

^(3) “引入持续交付基金会,Tekton、Jenkins、Jenkins X 和 Spinnaker 的新家园”,Google 开源博客(2019 年 3 月 12 日),https://oreil.ly/FvwF1.

第六章:多集群舰队:供应和升级生命周期

多集群多云 这些术语在当今的景观中变得普遍。对于本讨论的目的,我们将定义这些术语如下:

多集群

指的是管理超过一个集群或应用程序由部分托管在多个集群上的场景

多云

涉及使用多个集群的场景,这些集群也跨越基础设施基板,可能包括私有数据中心和单个或多个公共云提供商

这里的差异更多是学术性的;实际情况是,你很可能需要管理多个集群,就像你的组织不得不管理多个运行 VM 的 VMware ESXi 主机一样。

当你的容器编排平台在基础设施基板上有变化时,这些差异就会有所影响。我们将讨论当前一些可能出现这种情况的地方,以及可能影响一些你的管理技术或应用架构的地方。

为什么要多集群?

让我们讨论导致管理多个集群的使用案例。

使用案例:使用多个集群为你的应用程序提供区域性可用性

如同 第四章 中所讨论的,一个单一集群可以跨越多个可用区。每个可用区具有独立的故障特性。例如,电源供应、网络提供商,甚至物理空间(例如数据中心建筑)的故障应该被隔离到一个可用区内。通常情况下,跨可用区的网络连接仍提供显著的吞吐量和低延迟,允许 Kubernetes API 服务器的 etcd 集群跨越运行在不同可用区主机上的情况。然而,你的应用程序可能需要容忍影响同一区域内超过两个可用区或整个区域的停机情况。

所以,可能最容易理解的一个使用案例是在两个或更多区域创建超过一个多可用区集群。你通常会发现跨两个“游泳道”联合的应用程序,有时被称为 蓝绿架构。在同一区域内经常可以找到“蓝绿”配对模式,在其他区域中也有交替的蓝绿配对。你可以选择将同样的架构带到 OpenShift 中,运行两个分开的集群,为应用程序托管相同的组件集,有效地运行两个完整的端到端环境,其中任何一个都可以支持大多数用户的负载。围绕支持跨区域部署所需的架构模式,涉及负载均衡和数据管理的其他问题将在 第八章 中进行讨论。

使用案例:使用多个集群为多个租户提供服务

Kubernetes 社区对租户的界限是一个单独的集群。一般来说,Kubernetes 内的 API 结构侧重于将集群的计算资源划分为命名空间(在 OpenShift 中也称为项目)。然后,用户被分配角色或ClusterRole来访问他们的命名空间。然而,像ClusterRole、CRD、命名空间/项目、Webhook 配置等集群范围的资源确实不能由独立方管理。每个 API 资源在同类 API 资源集合中必须有唯一的名称。如果集群内真正有多租户,那么某种 API 概念(比如租户)将会组织ClusterRole、CRD 和 Webhook 配置等事物,并防止跨每个租户中的命名或行为发生冲突,就像应用程序的项目(例如,部署、服务和PersistentVolumeClaim在不同命名空间/项目中可以重复命名或行为一样)。

因此,当您可以为租户分配一个集群时,Kubernetes 最容易被消费。租户可以是组织内的业务线或功能团队(例如,质量工程或性能和规模测试)。然后,一组集群管理员或类似的ClusterRole可以分配给集群的所有者。

因此,一个新兴的模式是 OpenShift 集群管理平台团队将定义一个流程,消费者可以请求一个集群来满足其目的。因此,多个集群现在需要一致的治理和策略管理。

使用案例:支持远边使用案例,在这些案例中,集群不运行在传统数据中心或云中。

技术正在被应用到多种使用案例中,这些案例中,计算能力与来自摄像头、音频传感器或环境传感器的传感器数据以及机器学习或人工智能结合,以提高效率、提供更大的安全性,或创建新型消费者互动[¹]。这些使用案例通常被泛称为边缘计算,因为计算能力位于数据中心之外,更接近消费者体验的“边缘”。

高带宽能力与 5G 的引入也创造了场景,边缘计算解决方案可以在类似制造厂的空间内使用本地化的 5G 网络,边缘计算应用程序帮助追踪产品组装,自动化员工安全控制,或保护敏感机器设备。

就像容器为企业基于 Web 的应用程序提供了一个独立的包装一样,在基于边缘的应用程序中使用容器也有显著的好处。同样,你的容器编排的自动服务恢复在计算资源不易访问的数据中心内更为有益。

架构特征

现在我们已经看到一些使用多个集群或云来支持您需求的原因,让我们来看看这种方法的一些架构优势和挑战。

区域可用性与可用区可用性

通过多个托管应用程序的集群,您可以在多个云区域中分布应用程序的实例。每个区域内的每个集群仍将计算容量分布在多个可用区中。请参见图 6-1 以查看此拓扑结构的可视化表示。

图 6-1. 集群区域可用性允许在独立云区域中运行多个集群

在这种架构风格下,每个集群可以容忍任何一个可用区的完全丧失(例如 AZ1、AZ2 或 AZ3 可能变得不可用,但不会超过一个),并且工作负载将继续运行和提供请求。由于两个可用区的故障,etcd 集群将丧失法定人数,并且控制平面将变得不可用。

Kubernetes 控制平面在超过单个可用区故障时无法运行的原因是由于 etcd 的法定人数要求。通常情况下,etcd 将在控制平面中维护三个副本,每个副本由正好一个可用区支持。如果单个可用区丢失,仍然存在三个副本中的两个,并且分布式写入仍然可以确保写入事务被接受。如果两个可用区失败,则写入尝试将被拒绝。在集群中运行在工作节点上的 Pod 可能仍然能够提供流量服务,但不会接受或执行与 Kubernetes API 相关的任何更新。然而,在其他区域的独立集群仍然可以继续响应用户请求。详细分析此过程的内容请参见第四章。

根据地理位置减少用户的延迟

如果您的用户分布在不同地区,使用多个云区域还可以改善用户的响应时间。当用户尝试访问您应用的 Web 用户体验或工作负载提供的 API 时,他们的请求可以被路由到最近可用的应用实例。通常情况下,使用全球服务器负载均衡器(GSLB)来有效地处理这些流量路由。当用户尝试访问服务时,DNS 查询将委托给由您的 GSLB 托管的名称服务器。然后,根据请求的来源启发式算法,名称服务器将返回最近托管实例的 IP 地址。您可以在图 6-2 中看到此过程的可视化表示。

图 6-2. 使用 GSLB 解析全局服务地址的请求将返回最接近请求发起者的实例,基于其与请求发起者的距离。

您的平台一致性(托管 Kubernetes 与 OpenShift 加云身份提供商)

OpenShift 容器平台的一个主要优势是,它在所有云提供商和基础设施基板(如 VMware 和裸金属)上都能部署和运行一致。当您考虑是选择消费托管 Kubernetes 提供商还是 OpenShift 时,请注意每个 Kubernetes 分发版都会做出各种架构决策,这可能需要更多关注您的应用程序,以确保跨提供商的可移植性。

跨云资源配置

选择一个 Kubernetes 策略提供了简化应用程序如何消费弹性云基础设施的一种有效方式。在某种程度上,解决如何消费云资源的问题从为每个应用程序解决细节转变为一个平台,即您的组织将如何在基础设施基板上采用和管理 Kubernetes。

有几种方式可以从社区支持的项目中为 Kubernetes 提供资源。在本节中,我们将专注于如何使用 Red Hat OpenShift 容器平台来进行 Kubernetes 的资源配置。然后,我们将讨论如何在资源配置生命周期中作为一部分使用托管的 OpenShift 或托管的 Kubernetes 服务。

用户管理的 OpenShift

当您配置 OpenShift 容器平台 4.x 集群时,您有两种选项来创建基础设施资源。用户提供的基础设施(UPI)允许您更多控制权,可以启动虚拟机、网络资源和存储,然后将这些详细信息提供给安装过程,并允许它们引导到一个运行中的集群。或者,您可以依赖更自动化的安装程序提供的基础设施(IPI)方法。使用 IPI,安装程序接受具有适当特权的云凭证来创建所需的基础设施资源。IPI 过程通常会定义一个虚拟私有云(VPC)。请注意,如果您的组织有自己的约定方式来创建和管理这些资源,您可以将 VPC 指定为输入参数。在 VPC 内,资源包括网络负载均衡器、对象存储桶、虚拟计算资源、弹性 IP 地址等,所有这些资源都由安装过程创建和管理。

让我们看一下如何在三个云提供商(AWS、Microsoft Azure 和 Google Cloud Platform)上配置 OpenShift 集群。在本讨论中,我们将回顾安装过程如何使用声明性配置(与 Kubernetes 一般的做法相同),以及这与管理 OpenShift 集群生命周期的 ClusterVersionOperator(CVO)的关系。

首先,您需要根据适当的版本下载 openshift-installer 二进制文件。访问Red Hat,创建帐户,并按照创建集群和下载本地使用二进制文件的步骤进行操作。有关安装可用选项的具体详细信息,请参阅产品文档

让我们通过查看一些 openshift-installer 二进制文件的示例配置文件来演示此方法的工作原理。OpenShift 4.x 安装方法的所有支持选项超出了本书的范围。请参阅OpenShift Container Platform 文档了解所有支持选项的详细参考。以下示例将突出显示 OpenShift 4.x 安装方法的声明性特性如何简化在多个基础设施上配置集群的过程。此外,通过MachineSet API 的演练示例将演示在配置集群后如何继续管理集群的生命周期和健康状态。

示例 6-1 定义了一组选项,用于在 AWS 上配置 OpenShift 集群。示例 6-2 定义了如何在 Microsoft Azure 上配置 OpenShift 集群,而示例 6-3 则为 Google Cloud Platform 提供了等效的配置。示例 6-4——你猜对了!—为 VMware vSphere 提供了一个配置示例。除了 VMware vSphere 示例(对您自己的环境更敏感外),您可以使用这些示例来最小化更新以配置您自己的集群。请参阅 OpenShift Container Platform 产品文档,详细了解安装方法。

Example 6-1. 一个用于在 AWS 上配置 OpenShift 集群的示例install-config.yaml
apiVersion: v1
metadata:
 name: clusterName
baseDomain: yourcompany.domain.com
controlPlane:
 hyperthreading: Enabled
 name: master
 platform: {}
 replicas: 3
compute:
- hyperthreading: Enabled
 name: worker
 platform:
 aws:
 zones:
 - us-east-1b
 - us-east-1c
 - us-east-1d
 type: m5.xlarge
 rootVolume:
 iops: 4000
 size: 250
 type: io1
 replicas: 3
networking:
 clusterNetwork:
 - cidr: 10.128.0.0/14
 hostPrefix: 23
 machineCIDR: 10.0.0.0/16
 networkType: OpenShiftSDN
 serviceNetwork:
 - 172.30.0.0/16
platform:
 aws:
 region: us-east-1
 userTags:
 owner: user@email.domain
publish: External
pullSecret: 'REDACTED'
sshKey: |
 REDACTED
Example 6-2. 一个用于在 Microsoft Azure 上配置 OpenShift 集群的示例install-config.yaml
apiVersion: v1
 metadata:
 name: clusterName
baseDomain: yourcompany.domain.com
controlPlane:
 hyperthreading: Enabled
 name: master
 replicas: 3
 platform:
 azure:
 osDisk:
 diskSizeGB: 128
 type: Standard_D4s_v3
compute:
- hyperthreading: Enabled
 name: worker
 replicas: 3
 platform:
 azure:
 type: Standard_D2s_v3
 osDisk:
 diskSizeGB: 128
 zones:
 - "1"
 - "2"
 - "3"
networking:
 clusterNetwork:
 - cidr: 10.128.0.0/14
 hostPrefix: 23
 machineCIDR: 10.0.0.0/16
 networkType: OpenShiftSDN
 serviceNetwork:
 - 172.30.0.0/16
platform:
 azure:
 baseDomainResourceGroupName: resourceGroupName
 region: centralus
pullSecret: 'REDACTED'
Example 6-3. 一个用于在 Google Cloud Platform 上配置 OpenShift 集群的示例install-config.yaml
apiVersion: v1
metadata:
 name: clusterName
baseDomain: yourcompany.domain.com
controlPlane:
 hyperthreading: Enabled
 name: master
 replicas: 3
 platform:
 gcp:
 type: n1-standard-4
compute:
- hyperthreading: Enabled
 name: worker
 replicas: 3
 platform:
 gcp:
 type: n1-standard-4
networking:
 clusterNetwork:
 - cidr: 10.128.0.0/14
 hostPrefix: 23
 machineCIDR: 10.0.0.0/16
 networkType: OpenShiftSDN
 serviceNetwork:
 - 172.30.0.0/16
platform:
 gcp:
 projectID: gcpProjectId
 region: us-east1
 userTags:
 owner: user@email.com
pullSecret: 'REDACTED'
sshKey: |-
 REDACTED
Example 6-4. 一个用于在 VMware vSphere 上配置 OpenShift 集群的示例install-config.yaml
apiVersion: v1
metadata:
 name: example
baseDomain: demo.red-chesterfield.com
compute:
- hyperthreading: Enabled
 name: worker
 replicas: 3
platform:
 vsphere:
 vCenter: example.vCenterServer.io
 username: admin
 password: admin
 datacenter: exampleDatacenter
 defaultDatastore: exampleDatastore
 cluster: exampleClusterName
 apiVIP: 10.0.0.200
 ingressVIP: 10.0.0.201
 network: Public Network
pullSecret: 'REDACTED'
sshKey: |-
 REDACTED

这些install-config.yaml文件中的任何一个都可以用来使用以下命令来配置您的集群:

$ mkdir hubcluster
$ # copy install-config.yaml template from above 
$ # or customize your own into the “hubcluster” dir
$ openshift-installer create cluster --dir=hubcluster

请注意,每个示例都共享一些相同的选项,特别是clusterNamebaseDomain,这些将用于推导集群的默认网络地址(应用程序默认托管在https://*.apps.clusterName.baseDomain,而 OpenShift 的 API 端点将可用于https://api.clusterName.baseDomain:6443)。当 openshift-installer 运行时,云提供商上的 DNS 条目(例如 AWS 的 Route 53)将被创建并链接到适当的网络负载均衡器(也由安装过程创建),这些负载均衡器进而解析为运行在 VPC 内的 IP 地址。

每个示例定义了与将创建和管理的 MachineSet 相对应的 controlPlanecompute 部分。我们将很快讨论这些与集群中操作员的关系。compute 部分可以指定多个 MachinePoolcontrolPlanecompute 部分均提供了对计算主机的配置能力,并且可以自定义使用的可用性区域。如果省略,将选择合理的默认设置,包括每个主机的类型(或大小)以及附加到主机的存储类型选项。

现在,如果我们比较每个基板的 install-config.yaml 属性的差异,我们将在 platform 部分中找到特定于云的选项。全局 platform 指定集群应在哪个地区创建,每个 controlPlanecompute 部分下的 platform 部分可以覆盖为每个创建的主机设置。

如第五章介绍的,Open Cluster Management 项目是管理大多数集群维护者遇到的多集群挑战的新方法。第五章讨论了如何轻松地在集群之间分发应用程序。现在让我们看看如何使用 Open Cluster Management 驱动集群的配置、升级和退役过程。

在以下示例中,我们将演示如何在云服务提供商上创建新的集群。其底层行为利用了我们刚讨论过的 openshift-install 进程。一旦创建,Open Cluster Management 框架将安装一个作为一组 pod 运行的代理。我们称这个代理为 klusterlet,模仿运行在 Kubernetes 集群节点上的 kubelet 进程的命名。

注意

假设用户已经按照第五章中描述的方式设置了 Open Cluster Management 项目或 RHACM 用于 Kubernetes。

从 RHACM for Kubernetes Web 控制台中,打开“自动化基础设施 > 集群”页面,并点击“创建集群”操作,如图 6-3 所示。

Figure 6-3. 集群概览页面允许您从控制台为新的集群提供资源。

在图 6-4 中显示的“创建集群”操作打开一个表单,您可以在其中提供名称并选择一个可用的云服务提供商。

Figure 6-4. 通过 RHACM for Kubernetes 的集群创建表单

接下来,选择要部署的 OpenShift 版本。可用列表直接映射到 hub 集群上的 ClusterImageSet。您可以使用以下命令检查这些镜像:

$ oc get clusterimagesets
NAME             RELEASE
img4.3.40-x86-64 quay.io/openshift-release-dev/ocp-release:4.3.40-x86_64
img4.4.27-x86-64 quay.io/openshift-release-dev/ocp-release:4.4.27-x86_64
img4.5.15-x86-64 quay.io/openshift-release-dev/ocp-release:4.5.15-x86_64
img4.6.1-x86-64  quay.io/openshift-release-dev/ocp-release:4.6.1-x86_64

页面底部,如 图 6-5 所示,您还需要指定一个提供者连接。在 AWS 的情况下,您需要提供访问 ID 和 Secret Key,以允许安装过程通过您的 AWS 帐户进行 API 访问。

图 6-5. 选择您的发布镜像(要供应的版本)和您的提供者连接

此时,您只需单击“创建”即可开始供应集群。然而,让我们浏览一下 MachinePool 运算符如何允许您在集群内管理 MachineSet

自定义“Worker pool1” NodePool 以满足您期望的区域和可用区。参见图 6-6 和 6-7 中表单中的示例。集群供应后,您还可以修改这些选项。

图 6-6. 自定义集群工作节点的区域和区域

图 6-7. 自定义在区域内有效托管工作节点的可用区

最终确认您的选择的摘要在 图 6-8 中供查看。

图 6-8. 在表单中确认的选项

完成最终定制后,单击“创建”以开始供应过程,如 图 6-9 所示。Web 控制台提供了一个视图,其中包含指向集群供应日志的链接。如果集群无法供应(例如,由于云账户的配额限制),供应日志提供了需要排除故障的线索。

图 6-9. RHACM Web 控制台视图,包含指向集群供应日志的链接

在表单编辑器后面,创建了许多 Kubernetes API 对象。其中一小部分是集群范围的(特别是ManagedCluster)。ManagedCluster 控制器将确保存在一个映射到集群名称的项目(命名空间)。其他控制器,包括开始供应过程的控制器,将使用 cluster 项目(命名空间)来存储提供供应的 API 控制面资源。让我们来看看你应该熟悉的其中一部分。

ManagedCluster

ManagedCluster(API 组:cluster.open-cluster-management.io/v1;集群范围)承认远程集群受控于中心集群。在远程集群上运行的代理将尝试在中心集群上创建 ManagedCluster 如果它在中心集群上不存在,并且必须由具有适当权限的用户身份在中心集群上接受。您可以查看在 示例 6-5 中创建的示例。请注意,此对象的标签将在本章后面驱动放置决策。

示例 6-5. ManagedCluster API 对象的示例
apiVersion: cluster.open-cluster-management.io/v1
kind: ManagedCluster
metadata:
 ...
 labels:
 cloud: Amazon
 clusterID: f2c2853e-e003-4a99-a4f7-2e231f9b36d9
 name: mycluster
 region: us-east-1
 vendor: OpenShift
 name: mycluster
spec:
 hubAcceptsClient: true
 leaseDurationSeconds: 60
status:
 allocatable:
 cpu: "21"
 memory: 87518Mi
 capacity:
 cpu: "24"
 memory: 94262Mi
 conditions:
 ...
 version:
 kubernetes: v1.18.3+2fbd7c7

ClusterDeployment

ClusterDeployment(API 组:hive.openshift.io/v1;命名空间范围)控制集群的供应和解除阶段。中央控制器负责代表您运行 openshift-installer。如果由于任何原因(例如,在您的云帐户中遇到配额限制)集群创建过程失败,云资源将被销毁,并在等待一段时间后再次尝试成功创建集群。与传统的“尝试一次”并在失败时需要用户干预的自动化方法不同,此 API 种类的 Kubernetes 协调循环将继续尝试创建集群(在之间适当的等待期间)如 示例 6-6 所示。您还可以像任何 Kubernetes 本机资源一样直接通过 ockubectl 创建这些资源。

示例 6-6. 通过表单创建的示例 ClusterDeployment
apiVersion: hive.openshift.io/v1
kind: ClusterDeployment
metadata:
 ...
 name: mycluster
 namespace: mycluster
spec:
 baseDomain: demo.red-chesterfield.com
 clusterMetadata:
 adminKubeconfigSecretRef:
 name: mycluster-0-cqpz4-admin-kubeconfig
 adminPasswordSecretRef:
 name: mycluster-0-cqpz4-admin-password
 clusterID: f2c2853e-e003-4a99-a4f7-2e231f9b36d9
 infraID: mycluster-9bn6s
 clusterName: mycluster
 controlPlaneConfig:
 servingCertificates: {}
 installed: true
 platform:
 aws:
 credentialsSecretRef:
 name: mycluster-aws-creds
 region: us-east-1
 provisioning:
 imageSetRef:
 name: img4.5.15-x86-64
 installConfigSecretRef:
 name: mycluster-install-config
 sshPrivateKeySecretRef:
 name: mycluster-ssh-private-key
 pullSecretRef:
 name: mycluster-pull-secret
status:
 apiURL: https://api.mycluster.REDACTED:6443
 cliImage: quay.io/openshift-release-dev/ocp-v4.0-art-
dev@sha256:8b8e08e498c61ccec5c446d6ab50c96792799c992c78cfce7bbb8481f04a64cb
 conditions:
 ...
 installerImage: quay.io/openshift-release-dev/ocp-v4.0-art-
dev@sha256:a3ed2bf438dfa5a114aa94cb923103432cd457cac51d1c4814ae0ef7e6e9853b
 provisionRef:
 name: mycluster-0-cqpz4
 webConsoleURL: https://console-openshift-console.apps.mycluster.REDACTED

KlusterletAddonConfig

KlusterletAddonConfig(API 组:agent.open-cluster-management.io/v1;命名空间范围)代表应在管理集群的远程代理上提供的功能。在 示例 6-7 中,Open Cluster Management 项目将远程代理称为 klusterlet,与 kubelet 的语言相呼应。

示例 6-7. KlusterletAddonConfig API 对象的示例
apiVersion: agent.open-cluster-management.io/v1
kind: KlusterletAddonConfig
metadata:
 ..
 name: mycluster
 namespace: mycluster
spec:
 applicationManager:
 enabled: true
 certPolicyController:
 enabled: true
 clusterLabels:
 cloud: Amazon
 vendor: OpenShift
 clusterName: mycluster
 clusterNamespace: mycluster
 iamPolicyController:
 enabled: true
 policyController:
 enabled: true
 searchCollector:
 enabled: true
 version: 2.1.0

MachinePool

MachinePool(API 组:hive.openshift.io/v1;命名空间范围)允许您创建一组共同工作并共享特征的主机集合。您可以使用 MachinePool 来将支持特定团队或业务线的一组计算能力分组。正如我们将在下一节中看到的,MachinePool 还允许您动态调整集群的大小。最后,状态提供了一个查看在远程集群上管理的 MachineSet 的视图。查看 示例 6-8 以获取先前创建的示例 MachinePool,它提供了一个控制界面,用于在池中增加或减少副本数量,并提供有关在远程集群上管理的 MachineSet 的状态。

示例 6-8. 示例 MachinePool API 对象
apiVersion: hive.openshift.io/v1
kind: MachinePool
metadata:
 ...
 name: mycluster-worker
 namespace: mycluster
spec:
 clusterDeploymentRef:
 name: mycluster
 name: worker
 platform:
 aws:
 rootVolume:
 iops: 100
 size: 100
 type: gp2
 type: m5.xlarge
 replicas: 3
status:
 machineSets:
 - maxReplicas: 1
 minReplicas: 1
 name: mycluster-9bn6s-worker-us-east-1a
 replicas: 1
 - maxReplicas: 1
 minReplicas: 1
 name: mycluster-9bn6s-worker-us-east-1b
 replicas: 1
 - maxReplicas: 1
 minReplicas: 1
 name: mycluster-9bn6s-worker-us-east-1c
 replicas: 1
 - maxReplicas: 0
 minReplicas: 0
 name: mycluster-9bn6s-worker-us-east-1d
 replicas: 0
 - maxReplicas: 0
 minReplicas: 0
 name: mycluster-9bn6s-worker-us-east-1e
 replicas: 0
 - maxReplicas: 0
 minReplicas: 0
 name: mycluster-9bn6s-worker-us-east-1f
 replicas: 0
 replicas: 3

一旦提供,Kubernetes API 服务器和 OpenShift Web 控制台的地址将在集群详细信息页面中可用。您可以使用这些坐标打开您的 Web 浏览器,并作为 kubeadmin 用户对新集群进行身份验证。您还可以访问 KUBECONFIG 证书,以便您可以通过命令行访问集群。

您可以按照图 6-10 所示从 RHACM Web 控制台下载新集群的KUBECONFIG授权,位于集群概述页面下方或从命令行访问。从 Web 控制台,点击集群列表中的集群名称以查看该集群的概述。一旦完成配置过程,您将能够下载KUBECONFIG文件,从而允许您通过命令行访问集群。

示例 6-10

图 6-10. 从 RHACM Web 控制台下载 kubeconfig 授权

从命令行可以检索存储在集群项目(命名空间)下的秘密信息,例如示例 6-9。保存文件内容并配置您的KUBECONFIG环境变量指向文件的位置。然后oc将能够对远程集群运行命令。

示例 6-9. 集群KUBECONFIG文件的输出
$ CLUSTER_NAME=mycluster ; oc get secret -n $CLUSTER_NAME \
 -l hive.openshift.io/cluster-deployment-name=$CLUSTER_NAME \
 -l hive.openshift.io/secret-type=kubeconfig \
 -ogo-template="{{range .items}}{{.data.kubeconfig|base64decode}}{{end}}"

现在我们的集群已经启动并运行,让我们来看看如何扩展集群。我们将从 oc CLI 的上下文中审视这个概念。

首先,打开两个终端并为每个 hub 集群和我们新创建的mycluster配置KUBECONFIG或上下文。查看示例 6-10 和 6-11,了解在运行这些命令后每个独立终端的外观示例。请注意,临时覆盖您的 PS1 shell 提示,以避免在每个集群上运行命令时混淆。

示例 6-10. 终端 1 的示例外观
$ export PS1="hubcluster $ "
hubcluster $ export KUBECONFIG=hubcluster/auth/kubeconfig 
hubcluster $ oc cluster-info
Kubernetes master is running at https://api.hubcluster.<baseDomain>:6443

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
示例 6-11. 终端 2 的示例外观
$ export PS1="mycluster $ "
mycluster $ CLUSTER_NAME=mycluster ; \
 oc get secret -n $CLUSTER_NAME \
 -l hive.openshift.io/cluster-deployment-name=$CLUSTER_NAME \
 -l hive.openshift.io/secret-type=kubeconfig \
 -ogo-template="{{range .items}}{{.data.kubeconfig|base64decode}}{{end}}" \
 > mycluster-kubeconfig mycluster $ export KUBECONFIG=mycluster-kubeconfig
mycluster $ oc cluster-info
Kubernetes master is running at https://api.mycluster.<baseDomain>:6443

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

现在,您应该在终端 1 中有一个包含hubcluster的提示,而在终端 2 中有一个包含mycluster的提示。在后续示例中,我们将根据相应的名称引用这些终端。

在以下步骤中,我们将回顾MachineSet API,这是 OpenShift 集群理解计算容量的基础。然后,我们将使用之前见过的MachinePool API 来调整我们管理的集群大小。

mycluster终端中,查看您集群的MachineSet

mycluster $ oc get machinesets -n openshift-machine-api
NAME                              DESIRED CURRENT READY AVAILABLE AGE
mycluster-9bn6s-worker-us-east-1a 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1b 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1c 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1d 0                     0         1d
mycluster-9bn6s-worker-us-east-1e 0                     0         1d
mycluster-9bn6s-worker-us-east-1f 0                     0         1d

每个MachineSet的名称遵循以下模式:<clusterName>-<five-character identifier>-<machinePoolName>-<availabilityZone>。在您的集群中,您应该看到每个MachineSet所需机器数量、当前可用的机器数量以及当前被视为Ready以作为节点集成到 OpenShift 集群中的机器数量。请注意,这三个计数通常应该是相等的,并且只有在集群处于过渡状态(添加或删除机器)或者当集群中的基础可用性问题导致一个或多个机器被视为不健康时,这些计数才会有所变化。例如,当您编辑一个MachineSet以增加所需副本数时,您会看到该MachineSetDesired计数增加一个。随着机器的供应和进行kubelet的引导和配置,Current计数将增加一个。最后,当kubelet向 Kubernetes API 控制平面注册并将节点标记为Ready时,Ready计数将增加一个。如果在任何时候机器变得不健康,Ready计数可能会减少。类似地,如果您减少了Desired计数一个,您会看到相同的计数逐步减少,因为机器通过各种生命周期状态直到被移除。

在 hub 终端中,查看为托管集群定义的worker MachinePool

hubcluster $ oc get machinepool -n mycluster
NAME             POOLNAME CLUSTERDEPLOYMENT REPLICAS
mycluster-worker worker   mycluster         3

我们将增加托管集群mycluster的节点数目:

hubcluster $ oc patch machinepool -n mycluster mycluster-worker \
 -p '{"spec":{"replicas":4}}' --type=merge
machinepool.hive.openshift.io/mycluster-worker patched

工作节点的大小将由在MachinePool mycluster-worker中设置的值决定。新节点的可用区将由MachinePool控制器确定,节点将尽可能均匀地分布在可用区中。

在您打补丁增加所需副本数的MachinePool之后立即重新运行命令查看您托管集群上的MachineSet

mycluster $ oc get machinesets -n openshift-machine-api
NAME                              DESIRED CURRENT READY AVAILABLE AGE
mycluster-9bn6s-worker-us-east-1a 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1b 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1c 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1d 1                     1         1d
mycluster-9bn6s-worker-us-east-1e 0                     0         1d
mycluster-9bn6s-worker-us-east-1f 0                     0         1d

在几分钟内,您应该看到托管集群上的新节点从DesiredCurrentReady的转换,最终输出看起来像以下内容:

mycluster $ oc get machinesets -n openshift-machine-api
NAME                              DESIRED CURRENT READY AVAILABLE AGE
mycluster-9bn6s-worker-us-east-1a 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1b 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1c 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1d 1       1       1     1         1d
mycluster-9bn6s-worker-us-east-1e 0                     0         1d
mycluster-9bn6s-worker-us-east-1f 0                     0         1d

让我们回顾一下刚才看到的内容。首先,我们使用声明性方法(install-config.yaml)来配置我们的第一个称为 hub 的集群。接下来,我们使用 hub 来为我们的舰队中的第一个托管集群进行配置。该托管集群是在幕后使用相同的 IPI 方法创建的,但借助 Kubernetes API 和持续协调器,以确保运行中的集群与Desired状态匹配。管理Desired状态的 API 之一是 hub 集群上的MachinePoolAPI。因为我们的第一个舰队成员mycluster是从 hub 集群创建的,我们可以使用MachinePool API来控制mycluster如何添加或删除节点。或者,我们可以创建额外的MachinePool来增加集群的容量。

在整个过程中,底层的基础设施底层完全通过运算符进行管理。在管理的集群上,MachineSet运算符通过中心的MachinePool运算符获得更新指令,以增加支持mycluster中一个MachineSet中的可用机器数量。

注意

我们将使用术语基础设施底层来作为一个总称术语,用于指代您数据中心内提供的裸金属虚拟化或公共云提供的虚拟化中所提供的计算、网络和存储资源。

将您的集群升级到最新版本的 Kubernetes

如同我们在MachinePoolMachineSet中所见,运算符提供了一种强大的方式来抽象基础设施底层的差异,允许管理员以声明方式指定期望的结果。OpenShift 集群由 CVO 管理,其作为“运算符的运算符”模式来管理集群配置的各个方面(身份验证、网络、机器创建、引导和移除等)。每个集群都将有一个名为versionClusterVersion API 对象。您可以使用以下命令检索此对象的详细信息:

$ oc get clusterversion version -o yaml

ClusterVersion指定了一个“通道”来查找集群的可用版本和从该通道中选择的期望版本。将通道视为可用版本的持续列表(例如 4.5.1、4.5.2、4.5.7 等)。有用于快速采纳新版本的通道,也有用于“稳定”版本的通道。快速通道快速生成新版本。与来自运行在不同基础设施底层和行业的广泛 OpenShift 集群的连接的遥测数据结合使用,快速通道允许快速交付和验证新发布的版本(一般在几周或几天内)。由于快速通道中的发布版本有足够的支持证据表明它们在全球 OpenShift 集群范围内被广泛接受,因此这些版本会晋升为稳定通道。因此,通道中版本的列表并非总是连续的。一个示例ClusterVersion API 对象在示例 6-12 中表示。

示例 6-12. 记录集群版本历史和期望版本的ClusterVersion API 对象示例—更改期望版本将导致运算符开始应用更新以实现目标
apiVersion: config.openshift.io/v1
kind: ClusterVersion
metadata:
 name: version
spec:
 channel: stable-4.5
 clusterID: f2c2853e-e003-4a99-a4f7-2e231f9b36d9
 desiredUpdate:
 force: false
 image: quay.io/openshift-release-dev/ocp-
release@sha256:38d0bcb5443666b93a0c117f41ce5d5d8b3602b411c574f4e164054c43408a01
 version: 4.5.22
 upstream: https://api.openshift.com/api/upgrades_info/v1/graph
status:
 availableUpdates: null
 conditions:
 - lastTransitionTime: "2020-12-02T23:08:32Z"
 message: Done applying 4.5.22
 status: "True"
 type: Available
 - lastTransitionTime: "2020-12-11T17:05:00Z"
 status: "False"
 type: Failing
 - lastTransitionTime: "2020-12-11T17:09:32Z"
 message: Cluster version is 4.5.22
 status: "False"
 type: Progressing
 - lastTransitionTime: "2020-12-02T22:46:39Z"
 status: "True"
 type: RetrievedUpdates
 desired:
 force: false
 image: quay.io/openshift-release-dev/ocp-
release@sha256:38d0bcb5443666b93a0c117f41ce5d5d8b3602b411c574f4e164054c43408a01
 version: 4.5.22
 history:
 - completionTime: "2020-12-11T17:09:32Z"
 image: quay.io/openshift-release-dev/ocp-
release@sha256:38d0bcb5443666b93a0c117f41ce5d5d8b3602b411c574f4e164054c43408a01
 startedTime: "2020-12-11T16:39:05Z"
 state: Completed
 verified: false
 version: 4.5.22
 - completionTime: "2020-12-02T23:08:32Z"
 image: quay.io/openshift-release-dev/ocp-
release@sha256:1df294ebe5b84f0eeceaa85b2162862c390143f5e84cda5acc22cc4529273c4c
 startedTime: "2020-12-02T22:46:39Z"
 state: Completed
 verified: false
 version: 4.5.15
 observedGeneration: 2
 versionHash: m0fIO00kMu8=

将 Kubernetes 版本以及其周围所有其他支持 API 和基础设施一起升级可能是一项令人生畏的任务。控制所有容器镜像生命周期的操作器非正式称为“辛辛那提”,正式称为OpenShift 更新服务(OSUS)。OSUS(或辛辛那提)维护版本的连接图,并跟踪图中称为良好升级路径的“行走”或“路由”。例如,在早期发布通道可能检测到的问题表明,从 4.4.23 升级到 4.5.0 再到 4.5.18 可能会与特定问题相关联。可以发布修复版本 4.4.24,然后可以成功和可预测地从 4.4.23 升级到 4.4.24,再到 4.5.0,最后到 4.5.18。该图记录了必须行走的连续节点,以确保成功。

然而,OSUS 操作器消除了猜测工作,允许集群管理员从通道中指定所需版本。从那里开始,CVO 将执行以下任务:^(2)

  1. 升级 Kubernetes 和 OpenShift 控制平面的 pod,包括 etcd。

  2. 升级运行控制平面 pod 的节点操作系统。

  3. 升级控制认证、网络、存储等方面的集群运算符。

  4. 对由 MachineConfigOperator 管理的节点,升级运行数据平面 pod(用户工作负载)的操作系统。

升级以滚动方式进行,避免使集群的规模突然增加或同时占用太多容量。因为控制平面分布在三台机器上,每台机器进行操作系统更新和重启时,其他两个节点会保持 Kubernetes 控制平面的可用性,包括数据存储(etcd)、调度器、控制器、Kubernetes API 服务器和网络入口控制器。

当数据平面升级时,升级过程将尊重 PodDisruptionBudget 并通过生存探针和就绪探针寻找有关 OpenShift 和每个节点上运行的用户工作负载健康状况的反馈。

注意

有时,管理下的集群组被称为舰队。个别管理的集群可能被称为舰队成员,主要是为了区分它们与负责管理的中心集群。

通过 RHACM Web 控制台,您可以为单个联合成员或整个联合提供的版本管理群集。从控制台选择“升级集群”操作,以升级显示“可升级”的任何群集。回顾通道周围的讨论,不是每个通道都可以当前提供升级。此外,版本列表可能不是连续的。图 6-11、6-12 和 6-13 提供了特定群集或多个群集的实际示例。

图 6-11. 允许对群集执行的操作允许联合管理者升级群集的期望版本

图 6-12. 用户可选择的可用版本列表。

图 6-13. 可选择多个集群进行升级,可用版本会根据ClusterVersion对象附加的通道配置而变化。

本书的核心主题之一是如何将您的群集作为一个联合管理,并为此,我们将依赖策略。前面的讨论应为您提供了理解各个组成部分并看到您可以通过策略显式触发整个联合中的升级行为的基础。在 第七章 中,我们将讨论如何通过策略控制整个联合的升级行为。

多云集群供应概述

在我们的示例中,具体的基础设施基质出现在几个声明性 API 中,特别是 install-config.yaml 用于中心群集和作为 ClusterDeployment API 对象的受管群集的一部分的秘密引用。然而,通过 Kubernetes API 对象完全驱动了为该联合成员的新群集进行供应以及添加或移除节点的操作。

此外,通过 CVO 管理的升级生命周期在受支持的基础设施基质中保持一致。因此,无论您在公共云服务上还是在您的数据中心中提供 OpenShift 群集,您仍然可以声明式地管理升级过程。您现在应该明白的强大实现是,在多云场景中管理 OpenShift 群集的基础设施基质可以完全从许多基本的群集供应生命周期操作中抽象出来。

除了从中心控制您的联合成员的能力,您还可以使用 Open Cluster Management 分配策略并驱动像联合升级这样的行为。我们将在 第七章 中看到通过策略进行联合升级的示例。

OpenShift 作为一种服务

在上一节中,我们描述了如何在多个基础设施基板上抽象出 OpenShift 的供应和生命周期。根据我们所提出的模型,您负责集群的可用性。出于预算或组织原因,您可能选择考虑使用 OpenShift 或 Kubernetes 的托管服务。使用供应商提供的“OpenShift 即服务”或“Kubernetes 即服务”可以改变您与某些维度的交互方式,包括集群的创建或退役。但是,无论供应商是否管理基础设施,您的应用程序都会始终稳定运行。

Azure Red Hat OpenShift

Azure Red Hat OpenShift 整合到 Microsoft Azure 生态系统中,包括 Azure 计费。其他方面,包括单点登录,都通过 Azure Active Directory 自动配置,简化了向您的组织展示功能的方式,特别是如果您已经在 Azure 上消费其他服务的情况下。底层服务由 Microsoft 和 Red Hat 合作维护。

AWS 上的 Red Hat OpenShift

AWS 上的 Red Hat OpenShift 在 2020 年末宣布,并计划在 2021 年提供。它将 OpenShift 整合到亚马逊生态系统中,允许通过亚马逊云控制台访问和创建,并与您的亚马逊帐户的其余部分进行一致的计费。底层服务由亚马逊和 Red Hat 合作维护。

IBM 云上的 Red Hat OpenShift

IBM 云上的 Red Hat OpenShift 将 OpenShift 消费整合到 IBM 云生态系统中,包括与 IBM 云单点登录和计费的集成。此外,提供了 IBM 云 API 来管理集群的供应、工作节点和升级过程。这些 API 允许通过 IBM 云身份和访问管理来进行集群管理与管理集群中资源的访问控制分离。底层服务由 IBM 维护。

OpenShift Dedicated

OpenShift Dedicated 是由 Red Hat 提供的托管 OpenShift 即服务。可以通过此服务在各种云中创建 OpenShift 集群,在某些情况下可以在您自己的现有云帐户下进行。集群的可用性和维护由 Red Hat SRE 团队处理。底层服务由 Red Hat 维护,并且在某些支持的基础设施提供商如 AWS 上有带来自己的云帐户的选项。

Kubernetes 即服务

除了供应商管理的 OpenShift 即服务外,许多供应商还提供托管的 Kubernetes 即服务发行版。这些通常是供应商采用 Kubernetes 并将其集成到其生态系统中的情况。以下是一些这些服务的例子:

  • 亚马逊弹性 Kubernetes 服务

  • Azure Kubernetes 服务

  • Google Kubernetes Engine

  • IBM 云 Kubernetes 服务

因为 Kubernetes 社区将一些决策留给供应商或用户自行组装其分发版,每个托管服务都可能引入一些变化,这些变化在你采用它们作为更大的多云策略的一部分时需要注意。特别是,在 Kubernetes 的几个特定领域中,发展迅速:

  • 集群创建

  • 用户身份和访问管理

  • 网络路由

  • Pod 安全管理

  • 基于角色的访问控制

  • 增值准入控制器

  • 工作节点的操作系统管理

  • 不同的安全工具来管理合规性

在每个维度上,提供托管 Kubernetes 服务的供应商必须决定如何最好地将 Kubernetes 的这一方面整合到云提供商的生态系统中。核心 API 应该通过 CNCF Kubernetes 认证流程 保持一致响应。实际上,差异通常出现在 Kubernetes 集成到特定云生态系统中的地方。

例如,在某些情况下,托管的 Kubernetes 服务将会预先配置和部署 Kubernetes 的 RBAC。其他供应商可能会让集群创建者为 Kubernetes 配置 RBAC。对于自动配置了 Kubernetes RBAC 的供应商,预设的ClusterRole和角色集可能会有所不同。

在其他情况下,Kubernetes 集群的网络入口可能会因云特定的扩展或使用社区网络入口控制器而有所不同。因此,你的应用可能需要根据你选择提供 Kubernetes 的云服务商提供的情况,提供替代的网络入口行为。在特定云供应商管理的 Kubernetes 上使用 Ingress(API 组:networking.k8s.io/v1)时,跨供应商的注释集可能会有所不同,需要额外的验证以容忍不同的托管 Kubernetes 服务。在 OpenShift 集群(由供应商管理或你自己管理)中,所有应用程序都使用标准的 Ingress API,并具有固定的注释集或 Route API(API 组:route.openshift.io/v1),这将正确地暴露到特定的基础设施底层。

在你规划采用策略时,你必须解决应用架构和多云管理策略中的这些变化。无论你选择将 OpenShift 作为服务提供商采用,还是在自己的云账户中运行 OpenShift,所有面向 API 的应用程序,包括 RBAC 和网络,都会表现相同。

节点的操作系统版本

随着 OpenShift 群集的使用量增加,必须解决围绕安全性和操作系统当前性的实际问题。在 OpenShift 4.x 群集中,控制平面主机配置了 Red Hat CoreOS 作为操作系统。当对群集进行升级时,控制平面节点的操作系统也会升级。CoreOS 软件包管理器采用新颖的方法来应用更新:更新被打包为容器并以事务方式应用。整个更新要么成功要么失败。在管理 OpenShift 控制平面的更新时,此方法的结果限制了由操作系统中未知或未经测试配置的交互部分完成或失败的潜力。默认情况下,为工作节点提供的操作系统也将使用 Red Hat CoreOS,使得群集的数据平面享有同样的事务性更新优势。

可以向配置了 RHEL 的 OpenShift 群集添加工作节点。添加 RHEL 工作节点的过程在产品文档中有所涵盖,但超出了本书的范围。

如果将托管的 Kubernetes 服务集成到您的多集群策略中,请注意供应商和团队之间责任划分:谁负责群集中工作节点的当前/合规状态?几乎所有托管的 Kubernetes 服务提供商管理控制平面节点的操作系统。但是,主要云供应商在工作节点操作系统的责任划分上存在差异。

总结

本章我们覆盖了很多内容。现在,您应该了解 IPI 如何提供多个基础设施底层的一致抽象。一旦部署,OpenShift 内的操作员将管理群集的关键功能的生命周期操作,包括机器管理、认证、网络和存储。我们还可以使用这些操作员提供的 API 请求和驱动控制平面和支持节点操作系统的升级操作。

我们还介绍了通过Open Cluster Management使用支持提供的集群生命周期管理,Red Hat Advanced Cluster Management。使用 RHACM,我们看到了如何触发任何基础设施底层的用户管理群集的升级行为。

在下一章中,我们将继续利用群集操作员通过定义可应用于一个或多个管理中的群集的 Open Cluster Management 策略来配置和驱动群集行为。

^(1) Ted Dunning,《在边缘计算中使用数据布局和 Kubernetes》,The Newstack(2020 年 5 月 21 日),https://oreil.ly/W3J7f;《Chick-fil-A 的边缘计算》,Chick-fil-A 技术博客(2018 年 7 月 30 日),https://oreil.ly/HcJqZ

^(2) Rob Szumski,“集群管理员的 OpenShift 发布和升级流程终极指南”,红帽 OpenShift 博客(2020 年 11 月 9 日),https://oreil.ly/hKCex

第六章:多集群群集:配置和升级生命周期

多集群多云 这两个术语已经在当今的环境中变得很普遍。对于本次讨论,我们将以下列定义来定义这些术语:

多集群

指的是管理多个集群或应用程序由多个部分组成并托管在多个集群上的情况。

多云

指的是使用的多个集群还跨越基础设施基板的情况,这可能包括一个私有数据中心和一个单一的公共云提供商,或多个公共云提供商。

这里的区别更多是学术性的;事实上,您很可能不得不像您的组织不得不管理运行虚拟机的多个 VMware ESXi 主机一样,管理许多集群。

当您的容器编排平台在基础设施基板上有变化时,这些差异就会很重要。我们将讨论当前出现这些变化的一些地方,以及可能影响到一些管理技术或应用架构的地方。

为什么要使用多集群?

让我们讨论导致多个集群管理的使用案例。

使用案例:使用多个集群为您的应用程序提供区域性可用性

如 第四章 所讨论的,单个集群可以跨越多个可用区。每个可用区都有独立的故障特性。例如,电源供应、网络提供商,甚至物理空间(例如,数据中心建筑)的故障应该被隔离到一个可用区。通常情况下,跨可用区的网络链接仍然提供了显著的吞吐量和低延迟,允许 Kubernetes API 服务器的 etcd 集群跨越运行在不同可用区的主机。然而,您的应用程序可能需要容忍影响超过两个可用区的故障,或容忍整个区域的故障。

因此,创建两个或多个区域内的多个多可用区集群可能是最容易理解的使用案例之一。您经常会发现应用程序在两个“泳道”上联合,有时称为 蓝绿架构。这种“蓝绿”配对模式通常可以在同一区域内找到,在其他区域中有备用的蓝绿配对。您可以选择将同样的架构带到 OpenShift,您可以在其中运行两个独立的集群来托管应用程序的相同组件集,从而有效地运行两个完整的端到端环境,其中任何一个都可以支持大多数用户的负载。围绕支持跨区域部署所需的架构模式,涉及到额外的负载平衡和数据管理问题将在 第八章 中进行讨论。

使用案例:为多租户使用多个集群

Kubernetes 社区的租户边界是单个集群。一般来说,Kubernetes 内的 API 构建专注于将集群的计算资源划分为命名空间(在 OpenShift 中称为项目)。然后,用户被分配角色或ClusterRole以访问他们的命名空间。然而,像ClusterRole、CRD、命名空间/项目、Webhook 配置等集群范围的资源确实不能由独立方管理。每个 API 资源在同类 API 资源的集合中必须有唯一的名称。如果集群内有真正的多租户,那么某些 API 概念(如租户)将会将ClusterRole、CRD 和 Webhook 配置等分组,并防止跨每个租户的名称或行为冲突,就像应用程序(例如部署、服务和PersistentVolumeClaim)的项目所做的那样。

因此,当您能为一个租户分配一个集群时,Kubernetes 使用起来最为简单。一个租户可以是您组织内的业务线或功能团队(例如质量工程或性能和规模测试)。然后,一组集群管理员或类似的高级ClusterRole可以分配给集群的所有者。

因此,一个新兴的模式是,管理 OpenShift 集群的平台团队将定义一个流程,消费者可以为其目的请求一个集群。因此,现在多个集群需要一致的治理和策略管理。

使用案例:支持远端用例,在这些用例中,集群不在传统的数据中心或云中运行。

有一些很好的例子展示了技术如何应用于各种使用案例,其中计算能力与来自摄像头、音频传感器或环境传感器的传感器数据以及机器学习或人工智能结合,以提高效率、提供更大安全性或创建新型消费者互动。[¹]

引入 5G 的高带宽能力也创造了一些场景,其中边缘计算解决方案可以利用类似制造厂的空间内的本地化 5G 网络,边缘计算应用有助于跟踪产品装配、自动化员工安全控制或保护敏感设备。

正如容器为企业 Web 应用程序提供了一个独立的包装,使用容器在基于边缘的应用程序中也有显著的好处。类似地,您的容器编排的自动恢复服务也是有益的,尤其是当计算资源不易在您的数据中心内访问时。

架构特征

现在我们已经看到了您可能使用多个集群或云来支持您的需求的一些原因,让我们来看看这种方法的一些架构优势和挑战。

区域可用性与可用性区域

当应用程序托管在多个集群中时,您可以将应用程序的实例分布在多个云区域中。每个区域内的集群仍会将计算能力分布在多个可用性区域中。请参见图 6-1 以查看此拓扑结构的可视表示。

图 6-1. 集群区域可用性允许多个集群在独立的云区域中运行

在这种架构风格下,每个集群可以容忍任何一个可用性区域的完全丢失(AZ1、AZ2 或 AZ3 可能不可用,但不会超过一个),并且工作负载将继续运行和提供请求。由于两个可用性区域的失败,etcd 集群将失去法定人数,控制平面将变得不可用。

Kubernetes 控制平面在超过一个可用性区域失败时变得无法操作的原因是 etcd 的法定人数要求。通常,etcd 将在控制平面中维护三个副本,每个副本由一个可用性区域支持。如果一个可用性区域丢失,仍然有三个副本中的两个存在,并且分布式写入仍然可以确保写入事务被接受。如果两个可用性区域失败,则写入尝试将被拒绝。在集群中运行在工作节点上的 Pod 可能仍然能够提供流量服务,但不会接受或发生与 Kubernetes API 相关的任何更新。然而,在另一个区域中运行的独立集群仍可以继续响应用户请求。请参见第四章以深入分析此过程的工作原理。

根据地理位置减轻用户的延迟

如果您的用户位于不同的位置,使用多个云区域也可以提高用户的响应时间。当用户尝试访问您的应用程序的 Web 用户体验或工作负载提供的 API 时,他们的请求可以被路由到最近可用的应用程序实例。通常,在这些情况下会使用全局服务器负载均衡器(GSLB)来有效地路由流量。当用户尝试访问服务时,DNS 查找将被委托给您的 GSLB 托管的名称服务器。然后,根据请求的来源地的启发式,名称服务器将返回最近托管的应用程序实例的 IP 地址。您可以在图 6-2 中看到这一过程的可视表示。

图 6-2. 使用 GSLB 解析全局服务地址的请求将返回距离请求发起者最近的实例。

平台的一致性(托管 Kubernetes 与 OpenShift 加云身份提供商)

OpenShift 容器平台的主要优势之一是它在所有云提供商和基础设施(如 VMware 和裸金属)上部署和运行一致。在考虑是否使用托管 Kubernetes 提供商或 OpenShift 时,请注意 Kubernetes 的每个分发版本都会做出各种架构决策,可能需要更多关注应用程序,以确保跨提供商的可移植性。

跨云部署

选择 Kubernetes 策略提供了简化应用程序如何消费弹性云基础设施的有效方式。在某种程度上,解决如何为每个应用程序解决云资源的细节问题,转变为解决一个平台问题,即你的组织如何在基础设施底层采用和管理 Kubernetes。

有几种从社区支持的项目中部署 Kubernetes 的方法。在本节中,我们将重点介绍如何使用 Red Hat OpenShift 容器平台来部署 Kubernetes。然后,我们将讨论如何在你的部署生命周期中,作为替代选择,使用托管 OpenShift 或托管 Kubernetes 服务。

用户管理的 OpenShift

当你部署 OpenShift 容器平台 4.x 集群时,有两种选择如何创建基础设施资源。用户提供的基础设施(UPI)允许你更多地控制,以便启动虚拟机、网络资源和存储,然后将这些详细信息提供给安装过程,并允许它们引导到运行中的集群。或者,你可以依赖更自动化的安装程序提供的基础设施(IPI)方法。使用 IPI,安装程序接受具有创建所需基础设施资源所需权限的云凭据。IPI 过程通常会定义一个虚拟私有云(VPC)。请注意,如果你的组织有自己的资源创建和管理约定,你可以将 VPC 指定为输入参数。在 VPC 内部,资源包括网络负载均衡器、对象存储桶、虚拟计算资源、弹性 IP 地址等,都由安装过程创建和管理。

让我们看看如何在 AWS、Microsoft Azure 和 Google Cloud Platform 三个云提供商上部署 OpenShift 集群。在本讨论中,我们将审查安装过程如何利用声明性配置(与 Kubernetes 一般情况下一样),以及这与管理 OpenShift 集群生命周期的 ClusterVersionOperator(CVO)的关系。

首先,您需要为适当版本下载 openshift-installer 二进制文件。访问Red Hat,创建帐户,并按照创建集群和下载本地使用二进制文件的步骤进行操作。有关可用于安装的选项的具体详细信息,请参阅产品文档

让我们通过查看 openshift-installer 二进制文件的一些示例配置文件来演示这种方法的工作原理。 OpenShift 4.x 安装方法的所有支持选项超出了本书的范围。请查看OpenShift 容器平台文档详细参考所有支持的选项。以下示例将突出显示 OpenShift 4.x 安装方法的声明性本质如何简化在多个基础设施上配置集群。此外,将演示MachineSet API 的步骤示例,展示操作员在配置集群后继续管理其生命周期和健康状况。

示例 6-1 定义了在 AWS 上配置 OpenShift 集群的一组选项。示例 6-2 定义了如何在 Microsoft Azure 上配置 OpenShift 集群,而示例 6-3 定义了 Google Cloud Platform 的等效配置。示例 6-4—你猜对了!—提供了一个 VMware vSphere 的配置示例。除了 VMware vSphere 示例(更依赖于您自己的环境),您可以使用这些示例以最少的更新来配置自己的集群。请参考 OpenShift Container Platform 产品文档,详细查看安装方法。

示例 6-1. 一个示例install-config.yaml,用于在 AWS 上配置 OpenShift 集群。
$ oc get clusterserviceversion
NAME                                  DISPLAY                                      
VERSION   REPLACES   PHASE
advanced-cluster-management.v1.0.0    Advanced Cluster Management for Kubernetes   
1.0.0                Succeeded
container-security-operator.v1.0.5    Container Security                           
1.0.5                Succeeded
etcdoperator.v0.9.4                   etcd                                         
0.9.4                Succeeded
openshift-pipelines-operator.v1.0.1   OpenShift Pipelines Operator                 
1.0.1                Succeeded
示例 6-2. 一个示例install-config.yaml,用于在 Microsoft Azure 上配置 OpenShift 集群。
$ oc get subscriptions.operators.coreos.com -n openshift-operators
NAME                              PACKAGE                           SOURCE
                CHANNEL
container-security-operator       container-security-operator       community-
operators       alpha
示例 6-3. 一个示例install-config.yaml,用于在 Google Cloud Platform 上配置 OpenShift 集群。
$ oc get clusteroperators
NAME                                       VERSION   AVAILABLE   PROGRESSING   
DEGRADED   SINCE
authentication                             4.4.3     True        False         
False      48d
cloud-credential                           4.4.3     True        False         
False      48d
cluster-autoscaler                         4.4.3     True        False         
False      48d
console                                    4.4.3     True        False         
False      48d
csi-snapshot-controller                    4.4.3     True        False         
False      48d
dns                                        4.4.3     True        False         
False      48d
etcd                                       4.4.3     True        False         
False      28h
image-registry                             4.4.3     True        False         
False      48d
ingress                                    4.4.3     True        False         
False      48d
insights                                   4.4.3     True        False         
False      48d
kube-apiserver                             4.4.3     True        False         
False      48d
kube-controller-manager                    4.4.3     True        False         
False      48d
kube-scheduler                             4.4.3     True        False         
False      48d
kube-storage-version-migrator              4.4.3     True        False         
False      48d
machine-api                                4.4.3     True        False         
False      48d
machine-config                             4.4.3     True        False         
False      48d
marketplace                                4.4.3     True        False         
False      48d
monitoring                                 4.4.3     True        False         
False      41h
network                                    4.4.3     True        False         
False      48d
node-tuning                                4.4.3     True        False         
False      48d
openshift-apiserver                        4.4.3     True        False         
False      38d
openshift-controller-manager               4.4.3     True        False         
False      48d
openshift-samples                          4.4.3     True        False         
False      48d
operator-lifecycle-manager                 4.4.3     True        False         
False      48d
operator-lifecycle-manager-catalog         4.4.3     True        False         
False      48d
operator-lifecycle-manager-packageserver   4.4.3     True        False         
False      28h
service-ca                                 4.4.3     True        False         
False      48d
service-catalog-apiserver                  4.4.3     True        False         
False      48d
service-catalog-controller-manager         4.4.3     True        False         
False      48d
storage                                    4.4.3     True        False         
False      48d
示例 6-4. 一个示例install-config.yaml,用于在 VMware vSphere 上配置 OpenShift 集群。
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
 name: cluster
spec:
 identityProviders: []
 tokenConfig:
   accessTokenMaxAgeSeconds: 86400

这些install-config.yaml文件中的任何一个都可以使用以下命令来配置您的集群:

$ htpasswd -c -b -B .htpasswd username password

注意,每个示例都共享一些相同的选项,特别是clusterNamebaseDomain,这些选项将用于派生集群的默认网络地址(应用程序将默认托管在https://*.apps.clusterName.baseDomain,OpenShift 的 API 端点将在https://api.clusterName.baseDomain:6443可用)。当 openshift-installer 运行时,云提供商的 DNS 条目(例如 AWS 的 Route 53)将被创建并链接到由安装过程创建的适当网络负载均衡器,后者又解析为 VPC 内运行的 IP 地址。

每个示例定义了与将要创建和管理的 MachineSet 相对应的 controlPlanecompute 部分。我们将讨论这些与集群内运算符的关系。在 compute 部分可以指定一个以上的 MachinePoolcontrolPlanecompute 部分提供了计算主机的可配置性,并可以自定义使用哪些可用区。如果省略,将选择合理的默认设置,包括每个主机的类型(或大小)以及与主机连接的存储选项。

现在,如果我们比较每个基础设施在 install-config.yaml 属性上的不同之处,我们会发现在 platform 部分中有特定于云的选项。全局的 platform 指定了集群应创建在哪个区域,同时在每个 controlPlanecompute 部分下也有 platform 部分,用于覆盖每个预配主机的设置。

如 第五章 中介绍的,Open Cluster Management 项目是管理大多数集群维护者面临的多集群挑战的新方法。第五章 讨论了如何轻松地在多个集群之间分发应用程序。现在让我们看看如何使用 Open Cluster Management 驱动集群预配、升级和退役过程。

在下面的示例中,我们将演示如何在云提供商上创建一个新的集群。其底层行为利用了我们刚刚讨论过的 openshift-install 进程。一旦预配完成,Open Cluster Management 框架将安装一个代理作为新集群上一组 Pod 运行。我们将这个代理称为 klusterlet,模仿运行在 Kubernetes 集群节点上的 kubelet 进程的命名。

注意

以下假设用户已经按 第五章 中描述的设置好了 Open Cluster Management 项目或 RHACM for Kubernetes。

从 RHACM for Kubernetes 的 Web 控制台,打开“自动化基础设施 > 集群”页面,并点击“创建集群”操作,如 图 6-3 所示。

图 6-3. 集群概览页面允许您从控制台预配新集群

打开 图 6-4 中显示的“创建集群”操作,进入一个表单,您需要提供一个名称并选择可用的云提供商。

图 6-4. 通过 RHACM for Kubernetes 的集群创建表单

接下来,选择要预配的 OpenShift 版本。可用列表直接映射到 Hub 集群上的 ClusterImageSet,您可以使用以下命令检查这些镜像:

$ oc create secret generic htpass-secret --from-file=htpasswd=.htpasswd -n 
openshift-config

页面下方如图 6-5 所示,您还需要指定提供者连接。在 AWS 的情况下,您需要提供访问 ID 和密钥,以允许安装过程通过您的 AWS 账户访问 API。

图 6-5. 选择您的发布镜像(要预配的版本)和提供者连接

此时,您可以简单地点击“创建”,即可开始集群的预配过程。然而,让我们来看一下 MachinePool 操作员如何允许您在集群内管理 MachineSet

自定义“Worker pool1” NodePool,以符合您期望的区域和可用性区域。参见 图 6-6 和 图 6-7,了解此表单中的示例。在集群预配后,您也可以修改这些选项。

图 6-6. 自定义集群工作节点的区域和区域可用性区域

图 6-7. 自定义区域内的可用性区域,以便托管工作节点

为了审查确认的选择,您可以查看在 图 6-8 中呈现的最终摘要。

图 6-8. 确认在表单中选择的选项

完成最终的自定义后,点击“创建”即可开始预配过程,如 图 6-9 所示。Web 控制台提供的视图包括指向集群预配日志的链接。如果集群无法预配(例如,由于云账户中的配额限制),预配日志将提供故障排除的线索。

图 6-9. RHACM Web 控制台视图,包括集群预配日志的链接

在表单编辑器后面,会创建多个 Kubernetes API 对象。其中少量的 API 对象是集群范围的(特别是 ManagedCluster)。ManagedCluster 控制器将确保存在映射到集群名称的项目(命名空间)。其他控制器,包括启动预配过程的控制器,将使用 cluster 项目(命名空间)存储提供用于预配的 API 控制表面的资源。让我们看一下您应该熟悉的这些对象的子集。

ManagedCluster

ManagedCluster(API 组:cluster.open-cluster-management.io/v1;集群范围)识别远程集群在中心集群控制下。在远程集群上运行的代理将尝试创建 ManagedCluster,如果在中心集群上不存在,则必须由具有适当权限的用户身份接受。您可以在 示例 6-5 中看到创建的示例。请注意,此对象的标签将在本章后期的位置决策中起作用。

示例 6-5. ManagedCluster API 对象示例
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
 name: cluster
spec:
 identityProviders:
 - htpasswd:
     fileData:
       name: htpass-secret
   mappingMethod: claim
   name: htpasswdidp
   type: htpasswd
 tokenConfig:
   accessTokenMaxAgeSeconds: 86400

ClusterDeployment

ClusterDeployment(API 组:hive.openshift.io/v1;命名空间范围)控制集群的配置和销毁阶段。中心上的控制器负责代表您运行 openshift-installer。如果由于任何原因(例如,在您的云帐户中遇到配额限制),集群创建过程失败,那么将销毁云资源,并在等待一段时间后尝试重新创建集群。与“仅尝试一次”且失败后需要用户干预的传统自动化方法不同,此 API 种类的 Kubernetes 协调循环将继续尝试创建集群(在之间适当的等待期之后),如示例 6-6 所示。您还可以像任何 Kubernetes 本机资源一样直接通过 ockubectl 创建这些资源。

示例 6-6. 通过表单创建的 ClusterDeployment 示例
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 name: policy-project-frontend-app-prod
 namespace: open-cluster-management-policies
 annotations:
   policy.open-cluster-management.io/standards: NIST-CSF
   policy.open-cluster-management.io/categories: PR.IP Information Protection 
Processes and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline configuration
spec:
 disabled: false
 remediationAction: enforce
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: policy-project-frontend-app-prod
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
         - frontend-app-prod
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: project.openshift.io/v1
           kind: Project
           metadata:
             name: frontend-app-prod
         status:
           Validity: {}
       - complianceType: musthave
         objectDefinition:
           apiVersion: v1
           kind: LimitRange
           metadata:
             name: mem-limit-range
             namespace: frontend-app-prod
           spec:
             limits:
               - default:
                   memory: 512Mi
                 defaultRequest:
                   memory: 256Mi
                 type: Container
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
 name: binding-policy-project-frontend-app-prod
 namespace: open-cluster-management-policies
placementRef:
 name: production-clusters
 kind: PlacementRule
 apiGroup: apps.open-cluster-management.io
subjects:
- name: policy-project-frontend-app-prod
 kind: Policy
 apiGroup: policy.open-cluster-management.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: placement-policy-project-frontend-app-prod
 namespace: open-cluster-management-policies
spec:
 clusterConditions:
 - status: "True"
   type: ManagedClusterConditionAvailable
 clusterSelector:
   matchExpressions: []

KlusterletAddonConfig

KlusterletAddonConfig(API 组:agent.open-cluster-management.io/v1;命名空间范围)表示应在管理集群的远程代理上提供的功能。在示例 6-7 中,Open Cluster Management 项目将远程代理称为 klusterlet,反映了 kubelet 的语言。

示例 6-7. KlusterletAddonConfig API 对象示例
policy.open-cluster-management.io/standards: NIST-CSF
   policy.open-cluster-management.io/categories: PR.IP Information Protection 
Processes and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline configuration

MachinePool

MachinePool(API 组:hive.openshift.io/v1;命名空间范围)允许您创建一组共同工作并共享特征的主机。您可以使用 MachinePool 将支持特定团队或业务线的计算能力集中在一起。正如我们将在下一节中看到的那样,MachinePool 还允许您动态调整集群的大小。最后,状态提供了对 ManagedCluster 上可用的 MachineSet 的视图。有关之前创建的示例 MachinePool 的控制界面以及有关远程集群管理下的 MachineSet 的状态,请参见示例 6-8。

示例 6-8. MachinePool API 对象示例
object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: project.openshift.io/v1
           kind: Project
           metadata:
             name: frontend-app-prod
         status:
           Validity: {}

一旦配置完成,可以从集群详细信息页面获取 Kubernetes API 服务器和 OpenShift Web 控制台的地址。您可以使用这些坐标打开 Web 浏览器,并作为 kubeadmin 用户进行认证与新集群通信。您还可以访问 KUBECONFIG 证书,以便通过命令行访问集群。

您可以从 RHACM Web 控制台的群集概览页面下载新群集的KUBECONFIG授权,如图 6-10 所示,或通过命令行访问。从 Web 控制台,单击群集名称以查看该群集的概述。一旦完成配置过程,您将能够下载KUBECONFIG文件,从而可以通过命令行访问群集。

图 6-10. 从 RHACM Web 控制台下载 kubeconfig 授权

从命令行,您可以像在示例 6-9 中那样检索存储在群集项目(命名空间)下的秘密信息。保存文件内容并配置您的KUBECONFIG环境变量以指向文件的位置。然后oc将能够对远程群集运行命令。

示例 6-9. 群集KUBECONFIG文件的输出
matchLabels:
    apps/pacman: deployed
  matchExpressions:
    - {key: region, operator: In, values: [us-east, us-west]}
    - {key: env, operator: NotIn, values: [development]}
    - {key: authenticationProfile, operator: Exists}

现在我们的群集已经启动运行,让我们从 oc CLI 的上下文中讨论如何扩展群集。我们将从以下示例中审视这个概念。

首先,打开两个终端,并为每个中心群集和我们新建的mycluster配置KUBECONFIG或上下文。查看示例 6-10 和 6-11 来了解运行这些命令后两个独立终端的外观示例。请注意,临时覆盖您的 PS1 shell 提示以避免在每个群集上运行命令时混淆。

示例 6-10. 终端 1 的外观示例
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 name: policy-etcdencryption
 annotations:
   policy.open-cluster-management.io/standards: NIST SP 800-53
   policy.open-cluster-management.io/categories: CM Configuration Management
   policy.open-cluster-management.io/controls: CM-2 Baseline Configuration
spec:
 remediationAction: enforce
 disabled: false
 policy-templates:
   - objectDefinition:
       apiVersion: policy.open-cluster-management.io/v1
       kind: ConfigurationPolicy
       metadata:
         name: enable-etcd-encryption
       spec:
         severity: high
         namespaceSelector:
           exclude:
             - kube-*
           include:
             - default
         object-templates:
           - complianceType: musthave
             objectDefinition:
               apiVersion: config.openshift.io/v1
               kind: APIServer
               metadata:
                 name: cluster
               spec:
                 encryption:
                   type: aescbc
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
 name: binding-policy-etcdencryption
placementRef:
 name: placement-policy-etcdencryption
 kind: PlacementRule
 apiGroup: apps.open-cluster-management.io
subjects:
- name: policy-etcdencryption
 kind: Policy
 apiGroup: policy.open-cluster-management.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: placement-policy-etcdencryption
spec:
 clusterConditions:
 - status: "True"
   type: ManagedClusterConditionAvailable
 clusterSelector:
   matchExpressions:
     - {key: environment, operator: In, values: ["dev", “prod”]}
示例 6-11. 终端 2 的外观示例
$ oc get openshiftapiserver \
  -o=jsonpath='{range.items[0].status.conditions[?(@.type=="Encrypted")]}
  {.reason}{"\n"}{.message}{"\n"}'

EncryptionInProgress
Resource routes.route.openshift.io is not encrypted
…

$ oc get openshiftapiserver \
  -o=jsonpath='{range.items[0].status.conditions[?(@.type=="Encrypted")]}
  {.reason}{"\n"}{.message}{"\n"}'

EncryptionInProgress
Resource routes.route.openshift.io is being encrypted
…
$ oc get openshiftapiserver \
  -o=jsonpath='{range.items[0].status.conditions[?(@.type=="Encrypted")]}
  {.reason}{"\n"}{.message}{"\n"}'

EncryptionCompleted
All resources encrypted: routes.route.openshift.io, 
oauthaccesstokens.oauth.openshift.io, oauthauthorizetokens.oauth.openshift.io

现在您应该有一个包含hubcluster的终端 1 和一个包含mycluster的终端 2。我们将在后续示例中使用适当的名称引用这些终端。

在接下来的步骤中,我们将审视MachineSet API,这是 OpenShift 群集了解计算能力的基础。然后,我们将使用我们之前看到的MachinePool API 来扩展我们管理的群集的大小。

mycluster终端中,查看您的群集的MachineSet

apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 name: policy-role-developer
 annotations:
   policy.open-cluster-management.io/standards: NIST SP 800-53
   policy.open-cluster-management.io/categories: AC Access Control
   policy.open-cluster-management.io/controls: AC-3 Access Enforcement
spec:
 remediationAction: enforce
 disabled: false
 policy-templates:
   - objectDefinition:
       apiVersion: policy.open-cluster-management.io/v1
       kind: ConfigurationPolicy
       metadata:
         name: policy-role-developer
       spec:
         remediationAction: enforce 
          severity: high
         namespaceSelector:
           exclude: ["kube-*"]
           include: ["default"]
         object-templates:
           - complianceType: mustonlyhave # role definition should exact match
             objectDefinition:
               apiVersion: rbac.authorization.k8s.io/v1
               kind: ClusterRole
               metadata:
                 name: developer-read
               rules:
                 - apiGroups: ["*"]
                   resources: ["deployments", "configmaps", "services", "secrets"]
                   verbs: ["get", "list", "watch"]
           - complianceType: mustonlyhave # role definition should exact match
             objectDefinition:
               apiVersion: rbac.authorization.k8s.io/v1
               kind: ClusterRole
               metadata:
                 name: developer-write
               rules:
                 - apiGroups: ["*"]
                   resources: ["deployments", "configmaps", "services", "secrets"]
                   verbs: ["create", "delete", "patch", "update"]
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
 name: binding-policy-role-developer
placementRef:
 name: placement-policy-role-developer
 kind: PlacementRule
 apiGroup: apps.open-cluster-management.io
subjects:
- name: policy-role-developer
 kind: Policy
 apiGroup: policy.open-cluster-management.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: placement-policy-role-developer
spec:
 clusterConditions:
 - status: "True"
   type: ManagedClusterConditionAvailable
 clusterSelector:
   matchExpressions:
     - {key: environment, operator: In, values: ["dev"]}

每个MachineSet将按照以下模式命名:<clusterName>-<five-character identifier>-<machinePoolName>-<availabilityZone>。在您的集群中,您应该看到每个MachineSet的期望机器数量、当前可用的机器数量以及准备作为节点集成到 OpenShift 集群中的当前数量。请注意,这三个计数通常应该相等,只有在集群处于过渡状态(添加或删除机器)或者当集群中的底层可用性问题导致一个或多个机器被视为不健康时才会有所不同。例如,当您编辑MachineSet以增加所需副本时,您将看到该MachineSetDesired计数增加一个。当机器被配置和启动kubelet并注册到 Kubernetes API 控制平面并将节点标记为Ready时,Current计数将增加一个。最后,如果机器变得不健康,Ready计数可能会减少。类似地,如果将Desired计数减少一个,则会看到与机器通过各种生命周期状态直到移除的减少计数相同的错位减少。

接下来,在中心终端中查看为托管集群定义的worker MachinePool

apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 name: policy-role-developer-binding
 annotations:
   policy.open-cluster-management.io/standards: NIST SP 800-53
   policy.open-cluster-management.io/categories: AC Access Control
   policy.open-cluster-management.io/controls: AC-3 Access Enforcement
spec:
 remediationAction: enforce
 disabled: false
 policy-templates:
   - objectDefinition:
       apiVersion: policy.open-cluster-management.io/v1
       kind: ConfigurationPolicy
       metadata:
         name: policy-role-developer-binding
       spec:
         remediationAction: enforce 
         severity: high
         namespaceSelector:
           exclude: ["kube-*"]
           include: ["default"]
         object-templates:
           - complianceType: mustonlyhave # role definition should exact match
             objectDefinition:
               apiVersion: rbac.authorization.k8s.io/v1
               kind: RoleBinding
               metadata:
                 name: role-developer-read-binding
                 namespace: game-app
               roleRef:
                 apiGroup: rbac.authorization.k8s.io
                 kind: ClusterRole
                 name: developer-read
               subjects:
               - apiGroup: rbac.authorization.k8s.io
                 kind: Group
                 name: "game-developers"
           - complianceType: mustonlyhave # role definition should exact match
             objectDefinition:
               apiVersion: rbac.authorization.k8s.io/v1
               kind: RoleBinding
               metadata:
                 name: role-developer-read-binding
                 namespace: game-app
               roleRef:
                 apiGroup: rbac.authorization.k8s.io
                 kind: ClusterRole
                 name: developer-write
               subjects:
               - apiGroup: rbac.authorization.k8s.io
                 kind: Group
                 name: "game-developers"
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
 name: binding-policy-role-developer-binding
placementRef:
 name: placement-policy-role-developer-binding
 kind: PlacementRule
 apiGroup: apps.open-cluster-management.io
subjects:
- name: policy-role-developer-binding
 kind: Policy
 apiGroup: policy.open-cluster-management.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: placement-policy-role-developer-binding
spec:
 clusterConditions:
 - status: "True"
   type: ManagedClusterConditionAvailable
 clusterSelector:
   matchExpressions:
     - {key: environment, operator: In, values: ["dev"]}

我们将增加托管集群mycluster的大小一个节点:

apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 name: policy-htpasswd-auth-provider
 namespace: open-cluster-management-policies
 annotations:
   policy.open-cluster-management.io/standards: NIST-CSF
   policy.open-cluster-management.io/categories: PR.IP Information Protection 
Processes and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline configuration
spec:
 complianceType: mustonlyhave
 remediationAction: enforce
 disabled: false
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: policy-htpasswd-auth-provider
     spec:
       object-templates:
       - complianceType: mustonlyhave
         objectDefinition:
           apiVersion: config.openshift.io/v1
           kind: OAuth
           metadata:
             name: cluster
           spec:
             identityProviders:
             - htpasswd:
                 fileData:
                   name: htpass-secret
               mappingMethod: claim
               name: htpasswdidp
               type: htpasswd
             tokenConfig:
               accessTokenMaxAgeSeconds: 7776000
       - complianceType: mustonlyhave
         objectDefinition:
           apiVersion: v1
           data:
             htpasswd: ""
           kind: Secret
           metadata:
             name: htpass-secret
             namespace: openshift-config
           type: Opaque
       # - complianceType: musthave
       #   objectDefinition:
       #     kind: Identity
       #     apiVersion: user.openshift.io/v1
       #     metadata:
       #       name: 'htpassidp:johndoe'
       #     providerName: htpassidp
       #     providerUserName: johndoe
       #     user:
       #       name: johndoe
       #       uid: e4d768dd-a6b5-489c-8900-2c18a160d76f
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
 name: binding-policy-htpasswd-auth-provider
 namespace: open-cluster-management-policies
placementRef:
 name: placement-policy-oauth-provider
 kind: PlacementRule
 apiGroup: apps.open-cluster-management.io
subjects:
- name: policy-htpasswd-auth-provider
 kind: Policy
 apiGroup: policy.open-cluster-management.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: placement-policy-oauth-provider
 namespace: open-cluster-management-policies
spec:
 clusterConditions:
 - status: "True"
   type: ManagedClusterConditionAvailable
 clusterSelector:
   matchExpressions:
   - key: authenticationProfile
     operator: In
     values:
     - htpasswd
   matchLabels: {}

工作节点的大小将由MachinePool mycluster-worker中设置的值确定。新节点的可用区将由MachinePool控制器确定,其中节点尽可能均匀地分布在可用区。

在您修补了MachinePool以增加所需副本数量后,请重新运行命令以查看托管集群上的MachineSet

$ touch htpasswd.txt
htpasswd -b -B htpasswd.txt username password

几分钟后,您应该会看到托管集群上的新节点从DesiredCurrent再到Ready的过渡,最终结果看起来如下输出:

# Upgrade Policy to select known desired version from public connected registry
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 annotations:
   policy.open-cluster-management.io/categories: CM Configuration Management
   policy.open-cluster-management.io/controls: CM-2 Baseline Configuration
   policy.open-cluster-management.io/standards: NIST SP 800-53
 name: upgrade-cluster
 namespace: upgrade-policies
spec:
 disabled: false
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: upgrade-cluster
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - '*'
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: config.openshift.io/v1
           kind: ClusterVersion
           metadata:
             name: version
           spec:
             desiredUpdate:
               force: false
               image: ""
               version: 4.5.9
       remediationAction: enforce
       severity: high
 remediationAction: enforce
status: 
 # Note that the associated PlacementRules are omitted for this example
 placement:
 - placementBinding: binding-upgrade-cluster
   placementRule: placement-upgrade-cluster
 status:
 - clustername: east1
   clusternamespace: east1
   compliant: Compliant

让我们回顾一下刚才看到的内容。首先,我们使用声明性方法(install-config.yaml)来配置我们的第一个集群,称为中心集群。接下来,我们使用中心集群来为我们的舰队中的第一个托管集群进行配置。该托管集群是在幕后使用相同的 IPI 方法创建的,但借助于 Kubernetes API 和持续协调器来确保运行中的集群与Desired状态匹配。管理Desired状态的 API 之一是中心集群上的MachinePool API。因为我们的第一个舰队成员mycluster是从中心集群创建的,所以我们可以使用MachinePool API来管理mycluster如何添加或移除节点。或者,我们可以创建额外的MachinePool来增加集群的容量。

在整个过程中,底层基础设施基板完全通过操作符进行管理。在管理的集群上,MachineSet操作符通过来自中心的MachinePool操作符的更新指令,以增加支持mycluster中一个MachineSet中的机器数量。

注意

我们将使用术语基础设施基板作为一个统称术语,用来指代由您的数据中心提供的裸金属虚拟化或公共云提供商提供的虚拟化中的计算、网络和存储资源。

将您的集群升级到最新的 Kubernetes 版本

就像我们在MachinePoolMachineSet中看到的那样,运算符提供了一种强大的抽象方式,可以在基础设施基板之间隐藏差异,允许管理员声明性地指定期望的结果。OpenShift 集群由 CVO 管理,其作为“操作符的操作符”模式来管理集群配置的每个维度的操作符(包括认证、网络、机器创建、引导和移除等)。每个集群都将有一个名为versionClusterVersion API 对象。您可以使用以下命令检索此对象的详细信息:

apiVersion: config.openshift.io/v1
kind: ClusterVersion
metadata:
  name: version
spec:
  desiredUpdate:
    force: false
    image: ""
    version: 4.5.9

ClusterVersion指定了一个“通道”,以寻找集群可用版本以及从该通道获取的期望版本。将通道视为可用版本的持续列表(例如,4.5.1、4.5.2、4.5.7 等)。有用于“快速”采用新版本的通道,以及“稳定”版本的通道。快速通道快速产生新版本。结合来自运行在不同基础设施基板和行业的 OpenShift 集群的广泛数据的连接遥测数据,快速通道允许非常快速地交付和验证新版本(以周或天为单位)。由于快速通道中的发布具有足够的支持证据,可以广泛接受全球 OpenShift 集群,因此版本被提升到稳定通道。因此,通道内版本列表并不总是连续的。示例ClusterVersion API 对象在示例 6-12 中表示。

示例 6-12. 记录集群版本历史和期望版本的示例ClusterVersion API 对象-更改期望版本将导致操作符开始应用更新以实现目标
# Configure the OpenShift Update Service (OSUS) also known informally as 
"Cincinnati".
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 annotations:
   policy.open-cluster-management.io/categories: PR.IP Information Protection 
Processes
     and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline Configuration
   policy.open-cluster-management.io/standards: NIST-CSF
 name: policy-cincinatti-operator
 namespace: upgrade-policies
spec:
 disabled: false
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: cincinatti-policy-prod
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
       object-templates:
         apiVersion: cincinnati.openshift.io/v1beta1
         kind: Cincinnati
         metadata:
           name: example-cincinnati
         spec:
           registry: quay.io
           replicas: 1
           repository: openshift-release-dev/ocp-release
       remediationAction: inform
       severity: low
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: cincinatti-policy-subscription
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: operators.coreos.com/v1alpha1
           kind: Subscription
           metadata:
             name: cincinnati-subscription
             namespace: cincinnati-operator
           spec:
             channel: alpha
             installPlanApproval: Automatic
             name: cincinnati-operator
             source: redhat-operators
             sourceNamespace: openshift-marketplace
       remediationAction: inform
       severity: low
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: cincinatti-policy-operatorgroup
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: operators.coreos.com/v1
           kind: OperatorGroup
           metadata:
             name: cincinnati-operatorgroup
             namespace: cincinnati-operator
           spec:
             targetNamespaces:
             - cincinnati-operator
       remediationAction: inform
       severity: low
 remediationAction: enforce
status:
 placement:
 - placementBinding: binding-policy-cincinatti-operator
   placementRule: placement-policy-cincinatti-operator
---
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 annotations:
   policy.open-cluster-management.io/categories: 
     PR.IP Information Protection 
Processes
     and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline Configuration
   policy.open-cluster-management.io/standards: NIST-CSF
 name: policy-config-imageconfig
 namespace: upgrade-policies
spec:
 disabled: false
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: policy-config-imageconfig-prod
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: config.openshift.io/v1
           kind: Image
           metadata:
             name: cluster
           spec:
             additionalTrustedCA:
               name: trusted-ca
       remediationAction: inform
       severity: low
 remediationAction: enforce
status:
 placement:
 - placementBinding: binding-policy-config-imageconfig
   placementRule: placement-policy-config-imageconfig
---
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 annotations:
   policy.open-cluster-management.io/categories: PR.IP Information Protection 
Processes
     and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline Configuration
   policy.open-cluster-management.io/standards: NIST-CSF
 name: policy-configmap-ca
 namespace: upgrade-policies
spec:
 disabled: false
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: configmapca
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: v1
           data:
             cincinnati-registry: |-
               -----BEGIN CERTIFICATE-----
               YOUR_DISCONNECTED_REGISTRY_CERTIFICATE
               -----END CERTIFICATE-----
           kind: ConfigMap
           metadata:
             name: trusted-ca
             namespace: openshift-config
       remediationAction: inform
       severity: low
 remediationAction: enforce
status:
 placement:
 - placementBinding: binding-policy-configmap-ca
   placementRule: placement-policy-configmap-ca
---
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
 annotations:
   policy.open-cluster-management.io/categories: PR.IP Information Protection 
Processes
     and Procedures
   policy.open-cluster-management.io/controls: PR.IP-1 Baseline Configuration
   policy.open-cluster-management.io/standards: NIST-CSF
 name: policy-namespace-operatorgroup
 namespace: upgrade-policies
spec:
 disabled: false
 policy-templates:
 - objectDefinition:
     apiVersion: policy.open-cluster-management.io/v1
     kind: ConfigurationPolicy
     metadata:
       name: policy-namespace-operatorgroup-prod
     spec:
       namespaceSelector:
         exclude:
         - kube-*
         include:
         - default
       object-templates:
       - complianceType: musthave
         objectDefinition:
           apiVersion: v1
           kind: Namespace
           metadata:
             name: cincinnati-operator
       remediationAction: inform
       severity: low
 remediationAction: enforce
status:
 placement:
 - placementBinding: binding-policy-namespace-operatorgroup
   placementRule: placement-policy-namespace-operatorgroup
# END Policies for cincinnati-operator

升级 Kubernetes 版本以及其周围的所有其他支持 API 和基础设施可能是一项艰巨的任务。负责所有容器映像生命周期的操作符通常称为“Cincinnati”,正式称为OpenShift 更新服务(OSUS)。 OSUS(或 Cincinnati)维护版本的连接图,并跟踪在图中已知的良好升级路径。例如,可能在早期发布通道中检测到问题,表明从 4.4.23 升级到 4.5.0 到 4.5.18 可能与特定问题相关。可以发布修复程序以创建新版本 4.4.24,然后允许成功和可预测地从 4.4.23 升级到 4.4.24 到 4.5.0 到 4.5.18。图记录必须走过的连续节点,以确保成功。

然而,OSUS 运算符消除了猜测工作,允许集群管理员从通道中指定所需的版本。从那里,CVO 将执行以下任务:^(2)

  1. 升级 Kubernetes 和 OpenShift 控制平面 Pod,包括 etcd。

  2. 升级运行控制平面 Pod 的节点操作系统。

  3. 升级控制认证、网络、存储等方面的集群运算符。

  4. 对于由MachineConfigOperator管理的节点,需要升级运行数据平面 Pod(用户工作负载)的操作系统。

升级以滚动方式进行,避免集群规模暴增或同时削减太多容量。由于控制平面分布在三台机器上,每台机器进行操作系统更新和重启时,其他两个节点维护 Kubernetes 控制平面的可用性,包括数据存储(etcd)、调度器、控制器、Kubernetes API 服务器和网络入口控制器。

当数据平面进行升级时,升级过程将尊重PodDisruptionBudget并通过活跃性和就绪性探针查找有关 OpenShift 和运行在每个节点上的用户工作负载健康状态的反馈。

注意

有时管理下的集群组被称为fleet。主要将管理下的单个集群称为fleet members,主要是为了区别它们与负责管理的中枢集群。

通过 RHACM Web 控制台,您可以为单个成员或整个集群控制期望的托管集群版本。从控制台中选择任何显示“可升级”的集群的“升级集群”操作。回想一下关于通道的讨论,并非每个通道当前都有可用的升级。此外,版本列表可能不是连续的。图示 6-11、6-12 和 6-13 提供了针对特定集群或多个集群的实际展示示例。

图 6-11. 可以在集群上执行的操作允许集群管理员升级所需的版本

图 6-12. 提供给用户选择的可用版本列表

图 6-13. 可以选择多个集群进行升级,可用的版本会根据ClusterVersion对象中附加的通道配置而变化

本书的核心主题之一是如何将您的集群作为一个整体进行管理,为此,我们将依赖策略。前面的讨论应该为您提供了一个理解各个部分并明确如何在整个集群中触发升级行为的基础。在第七章中,我们将讨论如何通过策略控制整个集群的升级行为。

多云集群配置总结

在我们的示例中,特定的基础设施底层技术通过几个声明性 API 出现,具体来说是作为install-config.yaml用于中心集群,并作为ClusterDeployment API 对象的一部分引用的秘密。然而,通过 Kubernetes API 对象来进行新集群的配置和添加或移除成员节点完全是由此驱动的。

此外,通过 CVO 管理的升级生命周期在支持的基础设施底层技术上保持一致。因此,无论您是在公共云服务上还是在您的数据中心中提供 OpenShift 集群,您仍然可以完全声明式地管理升级过程。您现在应该理解到的强大功能是,在多云场景下管理 OpenShift 集群的基础设施底层技术可以完全抽象出许多基本的集群配置生命周期操作。

除了从中心控制您的集群的容量外,您还可以使用 Open Cluster Management 分配策略并驱动像集群升级这样的行为。我们将在第七章中看到一个通过策略进行集群升级的示例。

OpenShift 作为服务

上一节描述了如何在多个基础设施基板上抽象化 OpenShift 的供应和生命周期。根据我们所概述的模型,您需要负责集群的可用性。基于预算或组织原因,您可能选择考虑使用 OpenShift 或 Kubernetes 的托管服务。使用供应商提供的“OpenShift 即服务”或“Kubernetes 即服务”可以改变您与某些维度(包括集群创建或退役)的交互方式。然而,无论供应商是否管理基础架构,您的应用程序将始终保持一致运行。

Azure Red Hat OpenShift

Azure Red Hat OpenShift已整合到 Microsoft Azure 生态系统中,包括 Azure 计费。其他方面,包括单点登录,与 Azure Active Directory 自动配置,简化了您向组织公开功能的方式,特别是如果您已经在 Azure 上消费其他服务。底层服务由微软和 Red Hat 的合作关系维护。

Red Hat OpenShift on AWS

Red Hat OpenShift on AWS于 2020 年底宣布,计划于 2021 年可用。它将 OpenShift 整合到亚马逊生态系统中,允许通过亚马逊云控制台访问和创建,并与您的亚马逊账户的其余部分一致计费。底层服务由亚马逊和 Red Hat 的合作关系维护。

Red Hat OpenShift on IBM Cloud

Red Hat OpenShift on IBM Cloud将 OpenShift 消费整合到 IBM Cloud 生态系统中,包括与 IBM Cloud 单点登录和计费的集成。此外,提供了 IBM Cloud API 来管理集群的供应、工作节点和升级过程。这些 API 允许通过 IBM Cloud 身份和访问管理分别访问控制集群管理与管理集群资源所用的访问控制。底层服务由 IBM 维护。

OpenShift Dedicated

OpenShift Dedicated是 Red Hat 提供的托管 OpenShift 即服务。可以从该服务在各种云中创建 OpenShift 集群,在某些情况下可以在您自己现有的云账户下操作。集群的可用性和维护由 Red Hat 的 SRE 团队处理。底层服务由 Red Hat 维护,并提供在一些支持的基础设施提供商如 AWS 上带上自己的云账户的选项。

Kubernetes 即服务

除了供应商管理的 OpenShift 即服务外,许多供应商还提供管理的 Kubernetes 即服务分发。这些通常是供应商采用 Kubernetes 并将其整合到其生态系统中的地方。以下是这些服务的一些示例:

  • Amazon 弹性 Kubernetes 服务

  • Azure Kubernetes Service

  • Google Kubernetes Engine

  • IBM Cloud Kubernetes 服务

由于 Kubernetes 社区将一些决策留给供应商或用户来组装自己的发行版,因此在采用它们作为更大的多云战略的一部分时,您应意识到这些管理服务中可能引入的一些变化。特别是,在 Kubernetes 的几个特定领域已经迅速发展:

  • 集群创建

  • 用户身份和访问管理

  • 网络路由

  • Pod 安全管理

  • 基于角色的访问控制

  • 增值准入控制器

  • 工作节点的操作系统管理

  • 不同的安全设备来管理合规性

对于每个维度,提供托管 Kubernetes 服务的供应商必须决定如何最佳地将 Kubernetes 的这一方面集成到云提供商的生态系统中。核心 API 应通过 CNCF Kubernetes 认证流程 保持一致响应。实际上,当 Kubernetes 集成到特定的云生态系统中时,差异往往会产生。

例如,在某些情况下,托管 Kubernetes 服务将自带 Kubernetes RBAC 并配置好。其他供应商可能会将 RBAC 的配置留给集群创建者来完成。对于自动配置 Kubernetes RBAC 的供应商,出厂的 ClusterRole 和角色集合可能会有所不同。

在其他情况下,Kubernetes 集群的网络入口可能因云特定扩展或使用社区网络入口控制器而异。因此,根据您选择提供 Kubernetes 的云提供商,您的应用可能需要提供基于不同云提供商的替代网络入口行为。在特定云供应商管理的 Kubernetes 上使用 Ingress(API 组:networking.k8s.io/v1)时,受尊重的注释集合可能因提供商而异,需要对必须容忍不同托管 Kubernetes 服务的应用程序进行额外验证。对于由供应商或您管理的 OpenShift 集群,所有应用程序都定义了带有一组固定注释的标准 Ingress API 或 Route(API 组:route.openshift.io/v1)API,这些将正确暴露到特定的基础设施基板中。

您在应用架构和多云管理策略中必须解决的这种变化并不是不可逾越的。但是,在制定采用策略时,请注意这些方面。无论您是采用作为服务提供商的 OpenShift 还是在自己的云帐户中运行 OpenShift,包括 RBAC 和网络在内的所有 API 面向的应用程序将表现相同。

节点的操作系统货币

随着您对 OpenShift 集群的消耗增长,必须解决围绕安全性和操作系统当前性的实际问题。对于 OpenShift 4.x 集群,控制平面主机配置了 Red Hat CoreOS 作为操作系统。当集群进行升级时,控制平面节点的操作系统也会升级。CoreOS 软件包管理器使用一种新颖的方法来应用更新:将更新打包到容器中,并以事务方式应用。整个更新要么成功完成,要么失败。在管理 OpenShift 控制平面的更新时,这种方法的结果限制了由操作系统内未知或未经测试配置的交互而导致部分完成或失败安装的潜力。默认情况下,为工作节点配置的操作系统也将使用 Red Hat CoreOS,从而使您集群的数据平面获得相同的事务更新优势。

在配置了 RHEL 的 OpenShift 集群中可以添加工作节点。添加 RHEL 工作节点的过程在产品文档中有所覆盖,超出了本书的范围。

如果将托管的 Kubernetes 服务集成到您的多集群策略中,请注意供应商和您的团队之间的责任划分:谁负责集群中工作节点的操作系统的当前/合规状态?几乎所有的托管 Kubernetes 服务提供商管理控制平面节点的操作系统。然而,在主要云供应商之间在工作节点操作系统的责任上存在差异。

总结

在本章中我们已经涵盖了很多内容。到目前为止,您应该了解 IPI 如何提供多个基础设施基板的一致抽象。一旦被配置,OpenShift 内的操作员管理集群的关键功能的生命周期操作,包括机器管理、认证、网络和存储。我们还可以使用这些操作员暴露的 API 来请求和驱动对集群控制平面和支持节点操作系统的升级操作。

我们还介绍了使用支持服务提供的Red Hat 高级集群管理实现的开放集群管理中的集群生命周期管理。使用 RHACM,我们看到如何在任何基础设施基板上触发用户管理的集群的升级行为。

在下一章中,我们将继续利用集群操作员通过定义可以应用于一个或多个受管理集群的开放集群管理策略来配置和驱动集群行为。

^(1) Ted Dunning,“在边缘计算中使用数据织物和 Kubernetes”,The Newstack(2020 年 5 月 21 日),https://oreil.ly/W3J7f;“Chick-fil-A 的边缘计算”,Chick-fil-A 技术博客(2018 年 7 月 30 日),https://oreil.ly/HcJqZ

^(2) Rob Szumski,“集群管理员的 OpenShift 版本和升级流程终极指南”,红帽 OpenShift 博客(2020 年 11 月 9 日),https://oreil.ly/hKCex

第八章:多集群应用程序交付的工作示例

让我们来看一个简单的工作示例,一个带有后备数据存储的 Web 应用程序。为了我们的目的,我们将部署一个模仿雅达利游戏 PAC-MAN 的应用程序。用户将与一个动态前端进行交互,该前端将信息存储在后备的 MongoDB 中。

我们将使用在第五章讨论的技术在多个不同的集群上部署此应用程序。每个集群将从运行 Open Cluster Management 的中心进行配置,如第六章所述。此外,我们将配置由 F5 托管的 GSLB 服务提供的外部负载均衡器。来自全局域名的传入用户请求将被路由到特定集群中的一个。如果任何一个集群或应用程序遇到问题,则将不再将用户请求路由到该集群。我们将进一步探讨如何集成集群外资源,例如 F5 DNS 负载均衡器云服务,并将在示例中集成 ServiceNow 变更工单。

在图 8-1 中,我们看到我们的 PAC-MAN 应用程序在两个 OpenShift 集群上运行,负载均衡器将流量路由到任一集群中的应用实例。我们可以看到中心集群有各种资源,这些资源有助于管理整个系统。在本章的其余部分,我们将解释这些各种部分正在做什么,并提供一个可以自行尝试的演示,涵盖所有移动部件。

图 8-1。由多个集群组成的工作示例,以及一个由 Open Cluster Management 中心管理的两层 Web 应用程序,集成了集群外自动化。

失败是不可避免的。

我们从多个角度看到了 OpenShift 如何从基础设施或云提供商的故障中恢复,以及如何实现响应迅速且适应性强的开放式混合云,以支持您的应用程序。让我们回顾一些我们谈论过的事情。

在第四章中,我们讨论了单个集群可用性,其中 Kubernetes 集群的控制平面能够容忍任何一个可用区的故障。当单个集群中的可用区失败时,控制平面和集群内运行的应用程序都能够容忍支持计算、网络或存储基础设施的丢失。您的应用程序必须利用像服务这样的 Kubernetes 概念,在集群内充当负载均衡器,将用户请求路由到多个支持应用程序 Pod。当应用程序 Pod 不再健康(由健康检查确定)或支持该 Pod 的节点不再健康时,Kubernetes 调度器将寻找一个新的 Pod 家。我们通过拥有继续运行并服务传入用户请求的冗余 Pod 来设计具有弹性的应用程序。

现在,如果集群由于耗尽容量而无法重新调度失败的 Pod 会发生什么?如果超过一个可用区失败会发生什么?在这些情况下,将应用程序的额外实例部署到其他集群中的其他区域甚至其他云提供商可能是更可取的选择。

正如我们在第六章中所看到的,我们可以使用 OpenShift 在许多不同的云提供商上提供正在运行的集群。开放集群管理项目允许您从称为“中心”集群的中央控制平面管理这些集群。

此外,在第七章中,我们看到如何通过PlacementRule将给定的集群与所需配置匹配。每当 Open Cluster Management 策略与集群匹配时,因为该集群被PlacementRule选中,所需的配置可以被审核或强制执行到该集群。由于 OpenShift 对操作员中心化非常重视,无论是对控制平面还是工作负载,使用策略来驱动声明性配置是确保您的集群准备好支持应用工作负载的一种自然且简单的方式。另一个好处是,如果您正在采用完全或半 GitOps 方法,声明性策略可以在您的源代码控制系统中轻松管理。

在 第五章 中,我们探讨了如何通过适当的 PlacementRule 确保应用程序跨多个集群运行。通过在不同地区的多个集群中运行应用程序,我们现在可以容忍整个云区域的故障。或者,当您的组织仅因组织惯性、并购,或因需要抵御云服务提供商的完全故障时,也可以使用此功能。尽管多集群的最简单示例可能是利用现有的数据中心虚拟化提供商,并至少采纳一个公共云提供商。PlacementRule 很容易将“需要运行的内容”与“需要运行的位置”分开。PlacementRule 还可以使您的应用程序在集群的可用性或标记发生变化时进行调整。也就是说,如果 PlacementRule 更改或可用集群的集合因增加或删除而更改,则应用程序组件将动态重新部署到新的集群或从失败的集群中撤出。

因此,我们可以轻松创建集群(第六章),我们可以确保这些集群配置正确,以支持企业配置和安全标准(第七章),并且我们可以将应用程序交付到这些集群中(第五章)。

多集群负载均衡

如何确保用户请求路由到健康的应用实例?在我们的示例中,我们将使用由 F5 管理的基于云的 GSLB 服务。

处理多集群负载均衡有多种解决方案。出于这些原因,我们选择使用来自 F5 的云服务:

  • 在撰写本文时,该操作员不支持(但仍然对我们的应用程序进行自动化非常重要)。

  • 您可以通过 AWS Marketplace 轻松注册一个轻量级帐户。

  • 我们预计大多数数据中心将需要自动化其应用程序如何与 F5 BIG-IP 负载均衡器集成。尽管基于云的服务和 F5 BIG-IP 设备使用不同的 API,但我们相信示例将为您提供足够的理解,以便您可以根据自己的应用程序调整所展示的原则。另外,如果我们使用 BIG-IP 虚拟设备作为示例,这将增加用户重新创建完整示例的复杂性。

  • 即使您的集群跨越完全不同的云环境或您自己的公共路由暴露的数据中心,F5 服务也将为您提供服务。

以下是为提供跨集群负载均衡的另外两个选项,仅供您参考和了解:

  • external-dns 项目将典型的内部集群 DNS 注册表服务扩展到公共 DNS 记录。截至撰写本文时,该项目仍较为年轻,但支持多个提供者。

  • k8gb.io 项目将在每个集群内设置 DNS 注册表。某些顶级 DNS 提供者必须配置外部 DNS 条目,以委派到特定集群的 DNS 注册表。这种架构的好处是没有中心化控制器可能成为单点故障。然而,像基于延迟的路由等功能目前不受支持(例如在 F5 或 external-dns 选项中)。

在我们的示例应用程序中,您将建立一个顶级 DNS 记录(例如 *.www-apps.<*clusterName*>.<*baseDomain*>),将其委派给 F5 DNS 负载均衡器以解析地址(例如 ns1.f5cloudservices.com 和 ns2.f5cloudservices.com)。然后,DNS 请求解析地址的一般流程将经过以下步骤:

  1. 用户通过顶级 DNS 提供者解析 app-name.www-apps.<*clusterName*>.<*baseDomain*>,该提供者将请求重定向到与 *.<*baseDomain*> 相关的云 DNS 提供者。

  2. <*baseDomain*> 将由您的云提供商的 DNS 解析(例如,Amazon Route 53)。

  3. 云 DNS 提供商返回一个名字服务器记录,用于 *.www-apps.<*clusterName*>.<*baseDomain*> 的路由请求,以便将请求路由至 ns1.f5cloudservices.com 或 ns2.f5cloudservices.com 中的一个。

  4. 对 ns1.f5cloudservices.com 或 ns2.f5cloudservices.com 的最终解析请求评估托管区域列表,并根据 DNS 请求的来源和支持服务的当前健康状态返回与您的集群之一托管的应用程序路由最佳匹配。

如图 8-2 所示,在 F5 DNS 负载均衡器云服务中,每个集合集群的 DNS 负载均衡器区域将对应于您的云提供商 DNS 中的一个顶级 DNS 条目,该条目委派给 F5 以解析请求的正确集群中的应用程序路由。

图 8-2. F5 DNS 负载均衡云服务

登录到 F5 DNS 负载均衡器时,您将看到各种 DNS 负载均衡器服务(其中每个服务跨多个集群提供一个或多个应用程序),以及当前注册的 IP 终端。每个 IP 终端解析为托管一个应用程序实例的集群的应用程序路由器。

现在每当您的应用程序有一个实例可用或从集群中移除时,我们需要更新 DNS 负载均衡器区域的相关应用程序(由 *.www-apps.<*clusterName*>.<*baseDomain*> 标识)。我们将如何自动完成这项任务?在这里,我们将介绍如何在您的环境中不是“本地” Kubernetes 时,围绕您的集群自动化。为了实现这一目标,我们将介绍如何使用 Ansible 在我们的应用程序引入新集群或从现有集群中删除时,自动更新 F5 DNS 负载均衡器。

您需要完成以下先决条件,以运行我们的示例应用程序:

  1. 在 F5 DNS 负载均衡器云服务上创建一个账户。你可以在 F5 云服务 或者通过 AWS Marketplace 完成这一步骤。

  2. 将全局域名委托给 F5 DNS 域名服务器。创建一个委托命名服务器的 DNS 记录,使用你将在 F5 上使用的全局域名。你可以通过 Route 53 或者你的 DNS 提供商完成此操作。关于此先决条件的问题,可以查看 F5 DNS 负载均衡器云服务 FAQ 获取答案。

无操作员自动化

正如我们在本书中详细讨论的那样,Kubernetes 广泛使用声明性配置。虽然我们相信 Kubernetes 将在未来十年中支撑大多数现代应用程序,但我们也意识到并非所有事物今天都是 Kubernetes 原生的,未来也可能永远不会是 Kubernetes 原生的。

对于应用程序跨多个集群甚至多个云的各个方面,我们介绍 Ansible 作为一种方法,可以自动化系统引入动态变化时想要发生的任何行为。正如前一节讨论的那样,我们希望我们的 OpenShift 环境可以自动将我们的应用实例放置在多个集群上。每当我们的应用程序在新集群上部署或因集群不健康而被移除时,我们希望在系统前端的全局负载均衡器配置也能得到更新。由于我们严重依赖于自动恢复失败,我们希望这种行为也是自动的。

使用 Ansible,有一个充满活力的社区使用自动化简化系统管理员管理 Linux、容器、云、网络设备、安全等的生活。我们不会深入教您有关 Ansible 的知识。但是,我们将介绍一些基本概念,以便您了解其工作原理并评估是否适合您的需求。

请注意,即使您不必在集群外自动化任何事物,所有涉及可用性、多集群配置、配置、应用程序交付等细节仍然适用。

对于我们的目的,您只需要掌握 Ansible 中以下概念:

Playbook

Ansible playbook 是“自动化任务的蓝图——这些任务是在有限或无人参与的情况下执行的复杂 IT 操作。Ansible playbook 在组合、群组或主机分类上执行。”^(1)

项目

Ansible 项目将 playbooks 与支持资源一起组合,以运行这些 playbooks。方便的是,项目可以由用于管理 Ansible playbooks 和支持资源的源代码控制存储库支持。

Ansible Tower

Ansible Tower 是开源项目 Ansible AWX 的支持版本。Ansible Tower 提供了组织一组 Ansible 项目和自动化引擎的能力,可以跟踪凭据、定期作业、需要调用的作业模板以及可用的系统清单等。从本质上讲,可以将 Tower 视为组织和跟踪自动化系统管理所需的一切工具。

作业模板

Ansible 作业模板定义了 Ansible Tower 中来自 Ansible 项目的可用剧本。作业模板可以指定要外部化的参数和要使用的存储凭据,还可以关联作业模板的所有调用列表,用于审核或诊断目的。

工作

Ansible 作业是作业模板的运行实例。

如前所述,我们将使用 Ansible 更新 F5 DNS 负载均衡器云服务。我们将使用来自 F5 的稍作修改的开源工具的版本,该工具集成了更新服务所需的所有 API 调用,并在 Ansible 中实现。我们将将 f5-bd-gslb-tool playbooks 加载到一个 Ansible 项目中,并定义一个作业模板,调用所需的 playbook,并接受当前托管应用程序的集群参数。要查看 Ansible Tower 中的 Ansible 项目示例,请参见 图 8-3。

图 8-3. Ansible Tower 中的 Ansible 项目;项目内容在源代码控制下管理(通过 GitHub

我们还将使用 Ansible 集成由 ServiceNow 支持的简单变更管理流程。我们的示例源自关于该主题的已发布博客文章。^(2) 许多 IT 组织和运营商仍然广泛使用基于工单的变更管理流程。在我们的示例应用程序中,将在调整应用程序位置之前使用 Ansible 作业创建一个 ServiceNow 中的变更请求。我们的示例仅 superficial 使用 ServiceNow,但您将看到一种在系统经历动态自动变更时如何仍然使用现有流程进行记录和审核的方法。

我们已经介绍了支持应用程序部署的几个概念,包括 Open Cluster Management 订阅(API 组:apps.open-cluster-management.io/v1)和 PlacementRule(API 组:apps.open-cluster-management.io/v1)。现在我们介绍了一个新的 API 种类,它将成为我们示例应用程序的一部分:AnsibleJob(API 组:tower.ansible.com/v1alpha1)。与本书中介绍的其他自定义资源定义(CRD)一样,AnsibleJob 由一个称为 Ansible 资源操作器的操作员来协调。让我们将 Ansible 资源操作器与 Ansible Tower 一起部署,这样我们就可以在系统更新我们应用程序实例的放置时,链接 Ansible 作业的执行。用于创建变更请求票证的 AnsibleJob 如 示例 8-1 所示。

示例 8-1. AnsibleJob API 种类允许我们调用在 Ansible Tower 中配置的 Ansible 作业模板;输入参数通过 extra_vars 参数发送。
apiVersion: tower.ansible.com/v1alpha1
kind: AnsibleJob
metadata:
 name: snow-create-change-record
 namespace: pacman-app
 labels:
   app.kubernetes.io/name: pacman
spec:
 tower_auth_secret: toweraccess
 job_template_name: snow-create-change-record
 extra_vars:
   app_name: pacman
   change_request:
     severity: 2
     priority: 2
     implementation_plan: "Updated by Red Hat Advanced Cluster Management for 
Kubernetes"
     justification: "A new revision was available from the application channel in 
GitHub."
     description: "The following resources have been updated: [...]"
     risk_impact_analysis: "Changes are made automatically based on approved 
changes in GitHub."
     test_plan: "Run synthetic validation tests post-deployment."

在顺序上,用于创建 ServiceNow 票证的 AnsibleJob 将在应用新的放置决策之前运行(一个预处理钩子),用于更新 F5 负载均衡器的 AnsibleJob 将在最后运行(一个后处理钩子)。

部署示例应用程序

我们正在铺设许多功能来实现一个简单的 Web 前端应用程序——目标是为您提供一个实际的示例,展示您在采用 Kubernetes 和 OpenShift 作为企业标准的过程中可能会遇到的一些关注点。

我们已经介绍了应用程序将如何在多个集群中进行复制,并在这些多个应用程序实例前面有一个全局负载均衡器,以及每当放置决策变化时,我们将在 ServiceNow 中有一个简单的基于票证的记录。

我们在 第五章 中广泛审查了 PAC-MAN 应用程序,但这里是一个快速的回顾:

  • PAC-MAN 由两个部署组成(Web 前端应用程序和后端 MongoDB 数据存储)。

  • PAC-MAN 公开了两个公共路由:一个用于本地集群,另一个由我们的 F5 DNS 负载均衡器提供的全局路由。

  • PAC-MAN 订阅引用一个 Git 存储库,我们在其中管理我们的 Kubernetes 清单。如果对这些 Kubernetes 清单进行更改,则更新将自动推送到所有活动集群。

  • PlacementRule 定义了集群必须匹配的条件列表,以选择托管该应用程序的集群。

出于我们的目的(因为我们已经在处理多个移动部件),我们不会对支持跨多个集群的 MongoDB 副本集进行任何集群化操作。我们可以通过 Ansible 或操作器执行额外操作,以集群化 MongoDB 副本集,并在集群丢失时使分布式写入持久化。或者,我们可以使用基于云的 MongoDB 服务来备份 PAC-MAN 应用的状态。关于应用程序后备数据存储的持久性以及应用程序本身的相关 MTBF 和 MTTR 可用性指标,您可能会有自己的强烈意见。

现在我们已经讨论了所有部件如何拼合在一起的细节,让我们深入了解并操作一个运行示例!

为简化此过程,请分叉示例存储库。您需要进行一些修改,以便能够从您的存储库分支提供应用程序。

repository 分叉到您自己的组织中,然后将存储库克隆到您的笔记本电脑上:

$ git clone --branch ansible 
git@github.com:SPECIFY_YOUR_GITHUB_ORG/k8s-pacman-app.git

在以下部分引用的所有路径均指此存储库中的文件。

根据您的特定 DNS 设置更新以下文件中的值,以适应您的 OpenShift 集群和 Route53:

  • hack/install-config.yaml

  • deploy/posthook/f5-update-dns-load-balancer.yaml

  • deploy/pacman-f5-route.yaml

  • hack/tower-setup/config/inventory

表 8-1 显示了所需的更新内容。

表 8-1. 所需文件更新

Key Value
SPECIFY_YOUR_CLUSTER_NAME install-config.yaml 中定义的 Hub 集群名称。
SPECIFY_YOUR_BASE_DOMAIN install-config.yaml 中定义的 Hub 集群的基本 DNS 名称的值。
SPECIFY_YOUR_CLUSTER_ADDRESS clusterNameclusterBaseDomain 以点号分隔的串联(例如 clusterName.clusterBaseDomain)。
SPECIFY_YOUR_OWN_PASSWORD 定义您自己的安全密码。当有疑问时,**uuid** 的输出可以作为有用的密码。
SPECIFY_YOUR_EMAIL_ADDRESS 用于为跟踪目的分配云资源的标记。如果不想指定电子邮件地址,您也可以只删除标记。
SPECIFY_YOUR_PULL_SECRET 登录服务后可以从 Red Hat 下载的镜像拉取密钥。
SPECIFY_YOUR_SSH_RSA_PUBLIC_KEY 可用于连接到作为 OpenShift 集群的一部分进行配置的主机的 SSH 公钥。

配置您的 Hub 集群

请记住,Hub 集群托管 Open Cluster Management 的组件,允许从集中控制平面管理托管集群。您可以从 Hub 提供集群,也可以从像 Red Hat OpenShift on IBM Cloud 或 Azure Red Hat OpenShift 这样的托管提供程序导入现有的 OpenShift 集群。

在接下来的部分中,您将创建一个用作中心的集群,安装 Open Cluster Management 中心,并配置两个集群以托管示例应用程序。

配置一个 OpenShift 集群以托管 Open Cluster Management 中心

遵循默认说明来配置一个 OpenShift 4.5 或更新的集群。集群应至少有三个工作节点,总计 18 个 CPU 和 80G 内存,或在 AWS EC2 上使用 m5.xlarge(三个工作节点)。

下面的示例install-config.yaml用于准备此示例的中心集群,但只要您的起始集群具有所需的容量,您就不需要像install-config.yaml中的示例 8-2 一样配置一个集群。

示例 8-2。一个install-config.yaml示例,用于配置用作中心的集群的集群
apiVersion: v1
baseDomain: SPECIFY_YOUR_BASE_DOMAIN
controlPlane:
 hyperthreading: Enabled
 name: master
 replicas: 3
 platform:
   aws:
     type: m5.xlarge
     rootVolume:
       iops: 2000
       size: 100
       type: io1
compute:
- hyperthreading: Enabled
 name: worker
 replicas: 4
 platform:
   aws:
     type: m5.xlarge
     rootVolume:
       size: 100
       type: gp2
metadata:
 name: SPECIFY_YOUR_CLUSTER_NAME
networking:
 clusterNetwork:
 - cidr: 10.128.0.0/14
   hostPrefix: 23
 machineCIDR: 10.0.0.0/16
 networkType: OpenShiftSDN
 serviceNetwork:
 - 172.30.0.0/16
platform:
 aws:
   region: us-east-1
   userTags:
     contact: SPECIFY_YOUR_EMAIL_ADDRESS
     purpose: demo
publish: External
pullSecret: 'SPECIFY_YOUR_PULL_SECRET'
sshKey: |
 ssh-rsa SPECIFY_YOUR_SSH_RSA_PUBLIC_KEY

配置 Open Cluster Management 中心

对于我们的示例,我们将使用 Red Hat 支持的产品 RHACM 部署 Open Cluster Management。有关配置 RHACM 的说明,请参见第五章。您还可以直接从 Red Hat Operator 目录部署 RHACM,或使用文档中记录的说明

配置两个或更多集群来托管应用程序

配置中心集群后,您将能够注册您的云凭据并自动管理中心集群中的一个或多个集群。这些集群可以在同一云提供商的不同区域或多个云提供商中进行配置。例如 PAC-MAN 应用程序非常轻量级,因此您不需要大量容量来运行示例应用程序。

部署 Ansible Tower 和 Ansible 资源操作符

除了容器化应用程序外,我们还将配置由 F5 提供的 DNS 负载平衡器,并使用 ServiceNow 开发者实例来演示如何将变更管理流程集成到容器化应用程序的部署生命周期中。设置基于容器的 Ansible Tower 安装方法的详细说明,请参阅Ansible 文档。以下步骤记录了准备在此存储库中捕获的演示所采取的具体操作。

为简化此示例的配置,我们将利用一些预定义策略来帮助准备中心集群以部署 Ansible。

克隆示例存储库,并在hack/manifests/policies下应用策略:

$ cd hack/manifests/policies
$ oc apply -f ansible-tower-policies-subscription.yaml

这些策略的结果将设置 Ansible 资源操作符,准备名为tower的 Ansible Tower 项目,并使用默认存储类创建一个PersistentVolumeClaim来支持 Ansible Tower 的数据库(PostgreSQL)。

几分钟后,请验证您的 Hub 集群上是否正确应用了资源。您可以从 RHACM Web 控制台(在“治理风险”下)验证 policy-ansible-tower-preppolicy-auth-provider 是否合规。您还可以验证已创建的资源如下:

$ oc get subs.operators --all-namespaces
NAMESPACE                 NAME                        PACKAGE          
             SOURCE                CHANNEL
open-cluster-management   acm-operator-subscription   advanced-cluster-
management   acm-custom-registry   release-2.2
tower-resource-operator   awx-resource-operator       awx-resource-
operator     redhat-operators      release-0.1

$ oc get pvc -n tower
NAME        STATUS  VOLUME                                CAPACITY  ACCESS 
MODES   STORAGECLASS AGE
postgresql   Bound    pvc-1554a179-0947-4a65-9af0-81c5f2d8b476   5Gi        RWO      
         gp2           3d20h

可用的发布版本 下载 Ansible Tower 安装程序发布版。将归档文件解压缩到工作目录中。为您的 OpenShift 集群配置清单。清单文件应直接放置在归档文件夹下(例如,ansible-tower-openshift-setup-3.7.2/inventory)。

示例 8-3 中的清单可以放置在解压释放归档的根目录下。一定要覆盖以下内容:

SPECIFY_YOUR_OWN_PASSWORD

选择一个至少 16 个字符的强密码。

SPECIFY_YOUR_CLUSTER_ADDRESS

为您的 OpenShift 集群提供 API 服务器的正确主机名。

SPECIFY_YOUR_OPENSHIFT_CREDENTIALS

您的 OpenShift 集群管理员用户的密码。如果您已定义替代管理用户 kubeadmin,则一定要覆盖它。

示例 8-3. 为 Ansible Tower 安装程序提供输入值的示例清单文件
localhost ansible_connection=local ansible_python_interpreter="/usr/bin/env python"
[all:vars]
# This will create or update a default admin (superuser) account in Tower
admin_user=admin
admin_password='SPECIFY_YOUR_OWN_PASSWORD'
# Tower Secret key
# It's *very* important that this stay the same between upgrades or you will 
# lose the ability to decrypt your credentials
secret_key='SPECIFY_YOUR_OWN_PASSWORD'
# Database Settings
# =================
# Set pg_hostname if you have an external postgres server, otherwise
# a new postgres service will be created
# pg_hostname=postgresql
# If using an external database, provide your existing credentials.
# If you choose to use the provided containerized Postgres depolyment, these
# values will be used when provisioning the database.
pg_username='admin'
pg_password='SPECIFY_YOUR_OWN_PASSWORD'
pg_database='tower'
pg_port=5432
pg_sslmode='prefer'  # set to 'verify-full' for client-side enforced SSL
# Note: The user running this installer will need cluster-admin privileges.
# Tower's job execution container requires running in privileged mode,
# and a service account must be created for auto peer-discovery to work.
# Deploy into Openshift
# =====================
openshift_host=https://api.SPECIFY_YOUR_CLUSTER_ADDRESS:6443
openshift_skip_tls_verify=true
openshift_project=tower
openshift_user=kubeadmin
openshift_password=SPECIFY_YOUR_OPENSHIFT_CREDENTIALS
# If you don't want to hardcode a password here, just do:
# ./setup_openshift.sh -e openshift_token=$TOKEN
# Skip this section if you BYO database. This is only used when you want the
# installer to deploy a containerized Postgres deployment inside of your
# OpenShift cluster. This is only recommended if you have experience storing and
# managing persistent data in containerized environments.
#
#
# Name of a PVC you've already provisioned for database:
openshift_pg_pvc_name=postgresql
#
# Or... use an emptyDir volume for the OpenShift Postgres pod.
# Useful for demos or testing purposes.
# openshift_pg_emptydir=true
# Deploy into Vanilla Kubernetes
# ==============================
# kubernetes_context=test-cluster
# kubernetes_namespace=ansible-tower

更新用于运行已定义作业的默认任务映像。因为作业使用额外模块,我们需要确保各种 Python 模块依赖性可用。

group_vars/all 中,更新以下键:

kubernetes_task_image: quay.io/mdelder/ansible-tower-task

您可以构建此镜像,并从您自己的注册表中消耗它,方法是在 hack/tower-setup/container_task_image 下构建 Dockerfile.taskimage。或者,您可以构建任务镜像并发布到您自己的注册表。如果您使用之前定义的现有镜像,则无需构建自己的镜像。根据您下载的发布版本使用正确的 Ansible 版本:

$ cd hack/tower-setup/container_task_image
$ docker build -t quay.io/YOUR_USERID/ansible-tower-task:3.7.2 \
-f Dockerfile.taskimage
$ docker push quay.io/YOUR_USERID/ansible-tower-task:3.7.2

一旦您对清单文件进行了相关更新,您就可以运行 Ansible Tower 安装程序。从 OpenShift Web 控制台中检索认证令牌,方法是在您的用户名下的“复制登录命令”操作中:

$ ./setup_openshift.sh -e openshift_token=$TOKEN

启动 Tower Web 控制台:

$ open https://$(oc get route -n tower ansible-tower-web-svc \
-ojsonpath='{.status.ingress[0].host}')

使用您在清单文件中指定的用户和密码登录。然后必须选择 Tower 的许可证。如果您有 Red Hat 用户身份,则可以登录并选择 60 天的评估许可证。

可选择地,您可以自定义 hack/manifests/ansible-tower-console-link.yaml 以适合您自己的集群。然后应用文件(注意:在此之前,您必须更新文件中的 URL 才能在您的集群中正常工作):

$ cd hack/manifests
$ oc apply ansible-tower-console-link.yaml

在应用ConsoleLink后,刷新您的 OpenShift Web 控制台,并在标题栏的应用程序下拉菜单中查看您的 Ansible Tower 快捷方式。

为 ServiceNow 和 F5 DNS 负载均衡器配置项目

该示例应用程序使用 F5 DNS 负载均衡器云服务和 ServiceNow 来演示 Ansible 自动化。这假设您已完成以下步骤:

  1. 创建 ServiceNow 的开发者实例。如果您需要 ServiceNow 的开发者实例,请按照ServiceNow Developers的说明进行操作。

  2. 使用 F5 DNS Load Balancer Cloud Service 创建帐户。如果您需要使用F5 DNS Load Balancer Cloud Service创建帐户,您可以直接或通过AWS Marketplace完成此操作。

  3. 将全球域委托给 F5 DNS 域名服务器。创建一个使用与 F5 一起使用的全球域的名称服务器委托 DNS 记录。您可以通过 Route 53 或您的 DNS 提供商完成此操作。F5 DNS Load Balancer Cloud Service FAQ 解答了与此先决条件相关的问题。

一旦您获得了这些服务的凭据,您可以配置 Ansible Tower 实例,该实例使用两个相关的 Ansible 项目来提供将作为预钩子和后钩子执行的作业模板,当应用程序被放置或从集群中移除时。为了方便起见,所有与 Ansible Tower 的配置都是完全自动化的(使用与 Ansible Tower 对话以创建所需的项目/作业模板/作业/凭据的 Ansible playbook)。

hack/tower-setup目录下创建名为tower_cli.cfg的文件,内容如下:

# hack/tower-setup/tower_cli.cfg
[general]
host = https://ansible-tower-web-svc-tower.apps.cluster.baseDomain
verify_ssl = false
#oauth_token = ALTERNATIVELY_USE_A_TOKEN
username = admin
# password = SPECIFY_YOUR_OWN_PASSWORD

如果您不确定 Tower 的主机地址,可以使用oc查找正确的值:

$ oc get route -n tower ansible-tower-web-svc \
-ojsonpath='{.status.ingress[0].host}'

hack/tower-setup/group_vars目录下创建名为credentials.yml的文件,内容如下:

# User and password for the F5 CloudServices account.
f5aas_username: SPECIFY_YOUR_F5_USERNAME
f5aas_password: SPECIFY_YOUR_F5_PASSWORD

# Credentials for ServiceNow
snow_username: admin
snow_password: SPECIFY_YOUR_SERVICENOW_USERNAME
# Specify your ServiceNow developer instance ID.
snow_instance: devXXXXX

您可能需要安装所需的 Python 库:

# Optionally specify the correct version of python required by Ansible
# Of course, you must update the PYTHON var specific to your environment
$ export PYTHON="/usr/local/Cellar/ansible/2.9.13/libexec/bin/python3.8"
$ $PYTHON -m pip install --upgrade ansible-tower-cli

运行将与 Ansible Tower 对话并配置我们的两个项目(一个用于 F5,一个用于 ServiceNow)和相关作业模板的 playbooks:

$ export PYTHON="/usr/local/Cellar/ansible/2.9.13/libexec/bin/python3.8"
$ ansible-playbook -e ansible_python_interpreter="$PYTHON" tower-setup.yml

配置toweraccess秘密并创建 Ansible Tower 令牌

从 Ansible Tower 创建授权令牌。授权令牌将被用于应用程序引用以调用 Ansible Tower 作业。请按照以下步骤操作:

  1. 登录到 Ansible Tower 实例:

    $ open https://$(oc get route -n tower ansible-tower-web-svc \
    -ojsonpath='{.status.ingress[0].host}')
    
  2. 点击页眉中的“admin”用户。

  3. 点击令牌。

  4. 点击+号。

  5. 将范围设置为写入。

  6. 点击保存,确保复制并保存令牌的值。

  7. hack/manifests目录下创建名为hack/manifests/toweraccess-secret.yaml的文件,内容如下:

    apiVersion: v1
    stringData:
      host: ansible-tower-web-svc-
    tower.apps.SPECIFY_YOUR_CLUSTER_NAME.SPECIFY_YOUR_BASE_DOMAIN
      token: SPECIFY_YOUR_ANSIBLE_TOWER_ADMIN_TOKEN
    kind: Secret
    metadata:
      name: toweraccess
      namespace: pacman-app
    type: Opaque
    

部署pacman-app示例到您的集群

现在我们将部署管理应用程序放置的清单到中心。通过创建应用程序、订阅和PlacementRule,您将使中心能够动态地将应用程序部署到您之前创建的一个或多个集群。

首先,为应用程序创建项目并应用秘密:

$ oc new-project pacman-app
$ oc apply -f hack/manifests/toweraccess-secret.yaml

您可以通过 RHACM web 控制台(托管应用程序 > 创建应用程序)创建应用程序清单,也可以应用来自示例 Git 存储库的预构建资源。最终结果的示例在 Example 8-4 中提供。

示例 8-4. PAC-MAN 应用程序清单和支持资源,以允许中心集群将应用程序部署到 fleet 中的任何受管集群
---
apiVersion: apps.open-cluster-management.io/v1
kind: Channel
metadata:
  name: pacman-app-latest
  namespace: pacman-app
  labels:
    app.kubernetes.io/name: pacman
  annotations:

    apps.open-cluster-management.io/github-path: deploy
spec:
  type: GitHub
  pathname: https://github.com/SPECIFY_YOUR_GITHUB_ORG/k8s-pacman-app.git
  # secretRef:
  #   name: github-credentials
---
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
  name: pacman-app
  namespace: pacman-app
spec:
  componentKinds:
  - group: apps.open-cluster-management.io
    kind: Subscription
  descriptor: {}
  selector:
    matchExpressions:
    - key: app.kubernetes.io/name
      operator: In
      values:
      - pacman
---
apiVersion: apps.open-cluster-management.io/v1
kind: Subscription
metadata:
  annotations:
    apps.open-cluster-management.io/git-branch: ansible
    apps.open-cluster-management.io/github-path: deploy
  name: pacman-app
  namespace: pacman-app
  labels:
    app.kubernetes.io/name: pacman
spec:
  channel: pacman-app/pacman-app-latest
  hooksecretref:
    name: toweraccess
  placement:
    placementRef:
      kind: PlacementRule
      name: pacman-dev-clusters
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
 name: pacman-dev-clusters
 namespace: pacman-app
spec:
 clusterConditions:
 - status: "True"
   type: ManagedClusterConditionAvailable
 clusterReplicas: 2
 clusterSelector:
   # matchExpressions:
   # - key: region
   #   operator: In
   #   values:
   #   - us-east-1
   #   - us-west-1
   #   - europe-west3
   matchLabels:
     apps/pacman: deployed

channel 引用 GitHub 上 Kubernetes 清单的来源。对支持的 GitHub 仓库进行的任何更改都将触发整个群集的更新。application 提供了一种将一组订阅关联到部署和管理的逻辑单元的方法。subscription 选择 GitHub 仓库内的特定分支和目录。您可以拥有一个单一的 GitHub 仓库,为一个应用或多个应用提供多个订阅。每个订阅可以独立放置,因此您可以将应用的不同部分部署到不同的集群中。PlacementRule 定义了一组标签和匹配表达式,必须满足这些条件才能将订阅部署到 fleet 中的受管集群上。

要注意如何标记您的集群和支持的 PlacementRule。您希望确保 PlacementRule 在其状态条件中指示所选的集群:

$ oc get placementrule -n pacman-app -oyaml
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
name: pacman-app-placement-0
namespace: pacman-app
spec:
clusterSelector:
    matchLabels:
    apps/pacman: deployed
status:
decisions:
- clusterName: foxtrot-ap-northeast-1
    clusterNamespace: foxtrot-ap-northeast-1
- clusterName: foxtrot-gcp-europe
    clusterNamespace: foxtrot-gcp-europe

现在您已经部署了所有支持的部件,您可以通过添加或删除匹配的集群或更改 PlacementRule 中指定的所需标签来进行实验。应用实例将会自动添加或删除到支持的集群,同时保持 F5 DNS 负载均衡器云服务的最新状态。

从拓扑视图看,您的应用程序部署应类似于 图 8-4

图 8-4. 配置了 Ansible Tower 作业的 PAC-MAN 应用程序

概述

在本章中,我们专注于提供一个实际示例,演示如何跨多个集群交付应用程序,如何管理系统中任何仍不支持 Kubernetes 操作员的部分,并如何在全局负载均衡器服务后面集成一组集群。本书早期的许多具体示例都被用来整合所有这些内容,包括使用 Tekton 流水线构建其支持镜像的 PAC-MAN 应用程序,以及使用 Ansible Resource Operator 在中心集群上配置操作员,并使用 Open Cluster Management hub 提供支持正在运行的应用程序的集群。我们还介绍了一种通过 Ansible 调整现有变更管理流程的方法,以在系统动态响应变更时创建变更请求票据。

希望到目前为止,您已经对 Kubernetes 和 OpenShift 的可能性有了坚实的把握,并了解了如何利用这些能力来简化您的组织对这些技术的采用。

^(1) From “What Is an Ansible Playbook?”, Red Hat, https://oreil.ly/At7XY.

^(2) Colin McCarthy, “Ansible + ServiceNow Part 1: Opening and Closing Tickets,” Red Hat Ansible Blog (June 6, 2019), https://oreil.ly/NI9ZX.

第九章:Kubernetes 和 OpenShift 的未来

近年来,在 KubeCon + CloudNativeCon 大会上花一些时间,您很快会得出结论,Kubernetes 的未来非常光明。KubeCon + CloudNativeCon 大会的参与人数正在爆炸式增长。此外,Kubernetes 的开源社区贡献者数量不断扩大和加强。采用 Kubernetes 的行业数量令人震惊。^(1) 同样,OpenShift 在过去一年中其客户基础从 1,000 增长到 1,700,并且在财富 500 强公司中也得到了强劲的采用。^(2) 在本章中,我们对传统 Kubernetes 和 OpenShift 的未来做出了一些预测。我们讨论了这些技术在云原生计算的多个方面,包括遗留应用迁移、高性能计算、机器学习和深度学习应用、开源市场以及多云环境中的增长和影响的预期。然后,我们结论本章并讨论了进一步学习的推荐资源。

遗留企业应用向云原生应用的迁移加速

多年来,传统的 Kubernetes 和 OpenShift 都取得了显著的改进,将加速遗留企业应用向云原生应用的迁移。引入操作员框架后,这些平台现在能够更好地支持有状态应用,并管理这些应用的完整软件管理生命周期。此外,正如本书前面提到的,OpenShift 平台在工具化领域持续创新,减少了从源代码到拥有完全功能的容器化云原生应用的复杂性。同时,通过利用像 Knative 这样的新项目,Kubernetes 和 OpenShift 大大增加了它们可以支持的应用类型,以支持无服务器和基于函数的编程模型。所有这些创新的融合使得传统的 Kubernetes 和特别是 OpenShift 非常适合支持更广泛的企业应用领域。因此,我们预计在不久的将来,将会出现遗留企业应用向在传统 Kubernetes 和 OpenShift 上运行的云原生应用的急剧加速迁移。此外,我们预计传统大型机应用与云原生应用之间的互操作性和集成将会进一步增强。事实上,已经有一些早期的实验工作表明,COBOL 应用程序可以被容器化,并在 Kubernetes 和 OpenShift 上运行。^(3)

Kubernetes 在高性能计算中的增强采用

支持高性能计算倡议的服务器集群有着悠久而丰富的历史。这一领域的开创性工作始于上世纪 80 年代末,如 Parallel Virtual Machine(并行虚拟机)平台。^(4) 多年来,高性能计算社区接纳了能够提升效率和可伸缩性的新技术。Kubernetes 及其基于容器的方法提供了多项优势,使其非常适合高性能计算应用环境。由于 Kubernetes 基于容器,该平台启动新任务时的开销较小,并且这些任务可以比基于 VM 的云计算环境支持的任务更为细粒度操作。使用容器而非 VM 创建和销毁计算任务时相关的延迟减少提升了高性能计算环境的可伸缩性。此外,通过在物理服务器上打包更多容器而非有限数量的 VM,可以实现更高效的效率,这对高性能应用至关重要。

除了减少延迟外,Kubernetes 环境还支持并行工作队列模型。可以在Kubernetes Up and Running(O’Reilly)中找到对 Kubernetes 工作队列模型的出色概述。该书中描述的工作队列模型本质上是“任务袋”并行计算模型。研究表明,这种并行计算模型是在集群环境中执行高性能并行应用程序的优越方法。^(5)

尽管 Kubernetes 项目自其创立以来已经成熟,但它继续以改进其有效运行工作负载的方式进行创新。最近,Kubernetes 在其工作负载调度算法方面取得了显著进展,使其能够实现更好的集群利用率和高可用性。^(6) Kubernetes SIG Scheduler 团队多年来的努力使 Kubernetes 成为一个在如何为各种定制应用程序需求调度工作负载方面非常灵活和高效的平台。^(7)

基于所有这些因素,以及提供基于 Kubernetes 的环境的大量云计算环境,我们预计高性能计算社区将大量采用 Kubernetes。

Kubernetes 和 OpenShift 将成为机器学习和深度学习的事实标准平台

机器学习和深度学习应用通常需要高度可扩展的环境。在这些领域有专业知识的数据科学家希望使用易于使用的平台,使他们的应用能够在生产环境中规模化运行。类似于我们为高性能计算采用 Kubernetes 的理由,我们预计机器学习和深度学习环境将大大受益于将基于 Kubernetes 的环境作为其主要平台。事实上,像Kubeflow这样的倡议已经吸引了大量贡献者参与其开源项目。

开放云市场将加速云原生应用的采用

越来越多的企业开始同时使用多个公共云和托管他们本地云的私有数据中心。这些企业迅速意识到,他们需要一种简便的方式来购买、部署和管理能够在所有这些环境中运行的云原生应用软件。为了解决这种情况,开放云市场开始出现。在这些市场上,云原生应用程序使用 Kubernetes 运算符打包模型打包。这种方法使得开放云市场能够自动安装、部署、更新、扩展和备份云原生应用程序。

最近一个开放云市场的例子是红帽市场,它允许客户购买能够在所有主要云环境以及本地云上运行的云原生应用软件。红帽市场提供认证、漏洞扫描以及对云原生应用软件的支持,无论您选择在哪个云上部署它。图 9-1 展示了红帽市场首页的快照。

图 9-1. 红帽市场

由于红帽市场上的软件经过审查和认证,它为开发人员提供了构建企业解决方案的最佳工具和软件堆栈的策划视图。这使开发人员能够专注于软件提供的功能,而不是它是否适合他们将其运行的环境。

开发人员可能每年只有几次开始新的开发项目的机会,因此他们很少有机会选择应用程序堆栈和相关工具。像 Red Hat Marketplace 这样的开放云市场使开发人员可以免费尝试他们想要的软件,并在准备好时无缝过渡到商业支持的版本。此外,Red Hat Marketplace 还可以向开发经理提供关于将应用程序部署到每个云的消耗量的可见性。这让他们可以看到在每个平台上的利用情况,从而更好地管理开发成本。

OpenShift 将成为企业多云的平台

阅读完本书后,你应该对这个最后的预测毫不意外。OpenShift 已经证明它提供了优越的互操作性和工作负载可移植性,并且在所有主要云提供商(包括 IBM Cloud、Google Cloud、Amazon Cloud、Azure 等)中都可用。此外,OpenShift 还利用了 Istio Service Mesh,这一关键的网络技术降低了连接驻留在不同云中的应用程序的复杂性。它还具有专为管理多云集群量身定制的内置工具。基于所有这些原因,我们完全预期 OpenShift 将成为企业多云活动的首选平台。事实上,许多使用 OpenShift 的企业客户已经采纳了使用多个集群的做法,这些集群部署在多个云或多个数据中心中。这种行为可能由多种因素导致。首先,企业可能会因收购而继承在不同云上运行的应用程序。其次,不同的云可能提供不同的服务,企业可能需要一个涵盖多云解决方案的最佳解决方案。第三,数据局部性约束可能作为将关键应用程序锚定在某些云或本地云上的依据。基于所有这些原因,OpenShift 很好地定位为企业多云应用的理想平台。

推荐资源

本书涵盖了关于在生产环境中运行传统 Kubernetes 和 OpenShift 的许多概念。在本节中,我们提供了一些推荐资源的列表,这些资源是扩展您的技能和理解 Kubernetes、OpenShift 和云原生应用程序的优秀来源。

IBM 开发者网站

IBM 开发者网站为学习 Kubernetes、OpenShift 和其他一百多种开源技术提供大量开发者培训资源。该网站包含大量可重复使用的代码模式,这些模式是开发者每天面临问题的完整解决方案。此外,该网站还提供教程、培训视频、文章以及关于学习开源技术的在线和线下工作坊的列表。IBM 开发者网站的大部分内容都专注于 Kubernetes、OpenShift 以及 Istio、Knative 和容器等相关技术。IBM 开发者网站的 OpenShift 部分如图 9-2 所示。

图 9-2. IBM 开发者网站的OpenShift 内容

学习 OpenShift

另一个学习 OpenShift 的在线选项是Learn OpenShift 交互式学习门户。这个交互式学习网站提供预配置的免费 OpenShift 环境,让您可以进行关于多个与 OpenShift 相关主题的实际操作学习,如持续交付、构建运算符、为 OpenShift 添加持久性和开发 OpenShift 应用程序等。图 9-3 展示了 Learn OpenShift 提供的实践课程快照。

图 9-3. 学习 OpenShift 交互式学习门户的课程提供

Kubernetes 网站

Kubernetes 网站是获取 Kubernetes 相关信息的绝佳起点。该网站链接了更多关于文档、社区、博客和案例研究等主题的信息。在 Kubernetes GitHub 仓库的社区部分,您可以找到更多关于如何加入众多 Kubernetes 特别兴趣小组的信息。Kubernetes 特别兴趣小组信息页面的快照如图 9-4 所示。正如图中所示,每个特别兴趣小组都专注于 Kubernetes 的特定方面。希望您能找到一个激发您兴趣并符合您兴趣的小组。

图 9-4. Kubernetes 特别兴趣小组列表

Kubernetes IBM 云提供者特别兴趣小组

如果您有兴趣关注 IBM 云 Kubernetes 服务及其相关技术的演进,这个小组适合您。IBM 云的许多开发者和领导者都在这个小组中公开合作,以确定 IBM 在 Kubernetes 社区中的贡献和参与的未来。您还可以与构建和运营 IBM 云的团队进行互动。有关该小组及其会议的更多信息,请访问云提供者特别兴趣小组网站

Kubernetes 贡献者体验特别兴趣小组

Kubernetes 社区非常重视其贡献者的幸福感。事实上,他们有一个完整的特别兴趣小组,即贡献者体验 SIG,致力于改善贡献者的体验。贡献者体验 SIG 是一个了不起的团队,他们希望更多了解您,并理解您在成为 Kubernetes 贡献者时可能遇到的问题。访问贡献者体验 SIG 网站获取有关如何联系该小组以及了解他们专注的贡献者主题的更多信息。

摘要

在本书中,我们涵盖了广泛的主题,以帮助您成功地在生产环境中运行 Kubernetes 和 OpenShift。我们提供了传统 Kubernetes 和 OpenShift 的历史概述,并讨论了推动它们广受欢迎的两个平台的关键优势。我们描述了两种环境的架构以及重要的 Kubernetes 和 OpenShift 概念。然后,我们开始了一个关于基础生产相关主题的旅程,如高级资源管理、单集群高可用性和持续交付。接下来,我们探讨了更高级的多集群利用主题,重点关注多集群的配置、升级和策略支持。然后,我们查看了一个多集群应用交付的工作示例。最后,我们简要讨论了 Kubernetes 和 OpenShift 的未来,并提供了几个推荐的进一步学习资源。我们希望您找到本书有用,并且它为您提供了深入的技能和信心,使您能够在可能遇到的最复杂和先进的 Kubernetes 和 OpenShift 生产级环境上开发和部署云原生应用程序。

^(1) Cloud Native Computing Foundation 网站 提供更多信息。

^(2) Jeffrey Burt,“OpenShift、Kubernetes 和混合云”,The Next Platform(2020 年 4 月 28 日),https://oreil.ly/aNyAD

^(3) JJ Asghar,“在 Kubernetes 上运行 COBOL 程序”,IBM Developer(2019 年 5 月 14 日),https://oreil.ly/qhqwg;IBM Developer Staff,“在 Red Hat OpenShift 和 Kubernetes 上使 COBOL 正常运行”,IBM Developer(2020 年 4 月 20 日),https://oreil.ly/FcFJ8

^(4) VS Sunderam,“PVM:并行分布式计算的框架”,《并发:实践与经验》2:4(1990 年):315–339。

^(5) BK Schmidt 和 VS Sunderam,“集群环境中开销的实证分析”,《并发实践与经验》6(1994 年):1–32。

^(6) Wei Huang 和 Aldo CulquiCondor,“引入 PodTopologySpread”,Kubernetes 博客(2020 年 5 月 5 日),https://oreil.ly/iKzaV

^(7) 查看文档中的 Kubernetes Scheduler 获取更多信息。

posted @ 2025-11-17 09:51  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报