Helm-学习手册(全)

Helm 学习手册(全)

原文:zh.annas-archive.org/md5/AB61831A08B0763334412D2ABCB093BB

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

目前,容器化被认为是实施 DevOps 的最佳方式。虽然 Docker 引入了容器并改变了 DevOps 时代,但 Google 开发了一个广泛的容器编排系统 Kubernetes,现在被认为是容器编排的领先者。本书的主要目标是了解使用 Helm 管理在 Kubernetes 上运行的应用的效率。本书将从简要介绍 Helm 及其如何有益于整个容器环境开始。然后,您将深入了解架构方面,以及学习 Helm 图表及其用例。您将学习如何编写 Helm 图表以实现在 Kubernetes 上自动化应用部署。本书专注于提供围绕 Helm 和自动化的企业就绪模式,涵盖了围绕 Helm 的应用开发、交付和生命周期管理的最佳实践。通过本书,您将了解如何利用 Helm 开发企业模式,以实现应用交付。

本书适合对象

本书面向对学习 Helm 以实现在 Kubernetes 上应用开发自动化感兴趣的 Kubernetes 开发人员或管理员。具备基本的 Kubernetes 应用开发知识会很有帮助,但不需要事先了解 Helm。建议具备自动化提供的业务用例的基本知识。

本书涵盖内容

第一章理解 Kubernetes 和 Helm,介绍了 Kubernetes 和 Helm。您将了解在将应用部署到 Kubernetes 时用户面临的挑战,以及 Helm 如何帮助简化部署并提高生产力。

第二章准备 Kubernetes 和 Helm 环境,涵盖了在本地 Kubernetes 集群上使用 Helm 部署应用所需的工具。此外,您还将了解安装后发生的基本 Helm 配置。

第三章安装您的第一个 Helm 图表,解释了如何通过安装 Helm 图表将应用部署到 Kubernetes,并涵盖了使用 Helm 部署的应用的不同生命周期阶段。

第四章理解 Helm 图表,深入探讨了 Helm 图表的构建模块,并为您提供构建自己的 Helm 图表所需的知识。

第五章“构建您的第一个 Helm 图表”,提供了一个构建 Helm 图表的端到端演练。本章从构建利用基本 Helm 构造的 Helm 图表的基本概念开始,并逐渐修改基线配置以包含更高级的 Helm 构造。最后,您将学习如何将图表部署到基本图表存储库

第六章“测试 Helm 图表”,讨论了围绕对 Helm 图表进行 linting 和测试的不同方法论。

第七章“使用 CI/CD 和 GitOps 自动化 Helm 流程”,探讨了在利用 CI/CD 和 GitOps 模型自动化 Helm 任务方面的高级用例。即,围绕测试、打包和发布 Helm 图表开发一个流程。此外,还介绍了在多个不同环境中管理 Helm 图表安装的方法。

第八章“使用 Operator 框架与 Helm”,讨论了在 Kubernetes 上使用 operator 的基本概念,以便利用 operator 框架提供的 operator-sdk 工具从现有的 Helm 图表构建一个 Helm operator。

第九章“Helm 安全注意事项”,深入探讨了在使用 Helm 时的一些安全注意事项和预防措施,从安装工具到在 Kubernetes 集群上安装 Helm 图表的整个过程。

为了充分利用本书

虽然不是强制性的,因为基本概念在整本书中都有解释,但建议对 Kubernetes 和容器技术有一定了解。

对于本书中使用的工具,第 2-9 章将重点关注以下关键技术:

这些工具的安装在第二章“准备 Kubernetes 和 Helm 环境”中有详细讨论。本书中使用的其他工具是特定于章节的,它们的安装方法在使用它们的章节中进行描述。

如果您使用的是本书的数字版本,我们建议您自己输入代码或通过 GitHub 存储库(链接在下一节中提供)访问代码。这样做将有助于避免与复制/粘贴代码相关的任何潜在错误。

下载示例代码文件

您可以从www.packt.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便文件直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. 登录或注册www.packt.com

  2. 选择“支持”选项卡。

  3. 点击“代码下载”。

  4. 在搜索框中输入书名,然后按照屏幕上的说明进行操作。

文件下载后,请确保使用最新版本的解压缩或提取文件夹:

  • WinRAR/7-Zip 用于 Windows

  • Zipeg/iZip/UnRarX 用于 Mac

  • 7-Zip/PeaZip 用于 Linux

该书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/-Learn-Helm。如果代码有更新,将在现有的 GitHub 存储库上进行更新。

我们还有来自我们丰富的图书和视频目录的其他代码包,可在 https://github.com/PacktPublishing/上找到。去看看吧!

代码实例

本书的实际代码演示视频可在 https://bit.ly/2AEAGvm 上观看。

下载彩色图像

我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在这里下载:www.packtpub.com/sites/default/files/downloads/9781839214295_ColorImages.pdf

使用的约定

本书中使用了许多文本约定。

文本中的代码:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。这是一个例子:“将下载的WebStorm-10*.dmg磁盘映像文件挂载为系统中的另一个磁盘。”

代码块设置如下:

html, body, #map {
 height: 100%; 
 margin: 0;
 padding: 0
}

当我们希望引起您对代码块的特定部分的注意时,相关的行或项目会以粗体显示:

[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)

任何命令行输入或输出都以以下方式编写:

$ mkdir css
$ cd css

粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词会以这样的方式出现在文本中。这是一个例子:“从管理面板中选择系统信息”。

提示或重要说明

以这种方式出现。

第一部分:介绍和设置

本节将介绍 Helm 解决的问题陈述,以及通过实际示例演示其提供的解决方案。

本节包括以下章节:

第一章,理解 Kubernetes 和 Helm

第二章,准备 Kubernetes 和 Helm 环境

第三章,安装您的第一个 Helm 图表

第一章:理解 Kubernetes 和 Helm

感谢您选择了本书《学习 Helm》。如果您对本书感兴趣,您可能已经意识到现代应用程序带来的挑战。团队面临巨大的压力,确保应用程序轻量且可扩展。应用程序还必须具有高可用性,并能承受不同的负载。在历史上,应用程序通常被部署为单体应用,或者在单个系统上提供的大型单层应用。随着时间的推移,行业已经转向了微服务方法,或者转向了在多个系统上提供的小型多层应用。行业通常使用容器技术进行部署,开始利用诸如 Kubernetes 之类的工具来编排和扩展其容器化的微服务。

然而,Kubernetes 也带来了自己的一系列挑战。虽然它是一个有效的容器编排工具,但它提供了一个陡峭的学习曲线,对团队来说可能很难克服。一个帮助简化在 Kubernetes 上运行工作负载挑战的工具是 Helm。Helm 允许用户更简单地部署和管理 Kubernetes 应用程序的生命周期。它抽象了许多配置 Kubernetes 应用程序的复杂性,并允许团队在平台上更加高效地工作。

在本书中,您将探索 Helm 提供的每个好处,并了解 Helm 如何使在 Kubernetes 上部署应用程序变得更简单。您将首先扮演终端用户的角色,使用社区编写的 Helm 图表,并学习利用 Helm 作为软件包管理器的最佳实践。随着本书的进展,您将扮演 Helm 图表开发人员的角色,并学习如何以易于消费和高效的方式打包 Kubernetes 应用程序。在本书的最后,您将了解关于应用程序管理和安全性的高级模式。

让我们首先了解微服务、容器、Kubernetes 以及这些方面对应用程序部署带来的挑战。然后,我们将讨论 Helm 的主要特点和好处。在本章中,我们将涵盖以下主要主题:

  • 单体应用、微服务和容器

  • Kubernetes 概述

  • Kubernetes 应用的部署方式

  • 配置 Kubernetes 资源的挑战

  • Helm 提供的简化在 Kubernetes 上部署应用程序的好处

从单体应用到现代微服务

软件应用程序是大多数现代技术的基础组成部分。无论它们是以文字处理器、网络浏览器还是媒体播放器的形式出现,它们都能够使用户进行交互以完成一个或多个任务。应用程序有着悠久而传奇的历史,从第一台通用计算机 ENIAC 的时代,到阿波罗太空任务将人类送上月球,再到互联网、社交媒体和在线零售的兴起。

这些应用程序可以在各种平台和系统上运行。我们说在大多数情况下它们在虚拟或物理资源上运行,但这难道是唯一的选择吗?根据它们的目的和资源需求,整个机器可能会被专门用来满足应用程序的计算和/或存储需求。幸运的是,部分归功于摩尔定律的实现,微处理器的功率和性能最初每年都在增加,同时与物理资源相关的整体成本也在增加。这一趋势在最近几年有所减弱,但这一趋势的出现以及在处理器存在的前 30 年中的持续对技术的进步起着关键作用。

软件开发人员充分利用了这一机会,在他们的应用程序中捆绑了更多的功能和组件。因此,一个单一的应用程序可能由几个较小的组件组成,每个组件本身都可以被编写为它们自己的独立服务。最初,捆绑组件在一起带来了几个好处,包括简化的部署过程。然而,随着行业趋势的改变,企业更加关注能够更快地交付功能,一个可部署的单一应用程序的设计也带来了许多挑战。每当需要进行更改时,整个应用程序及其所有基础组件都需要再次验证,以确保更改没有不利的特性。这个过程可能需要多个团队的协调,从而减慢了功能的整体交付速度。

更快地交付功能,特别是跨组织内的传统部门,也是组织所期望的。这种快速交付的概念是 DevOps 实践的基础,其在 2010 年左右开始流行起来。DevOps 鼓励对应用程序进行更多的迭代更改,而不是在开发之前进行广泛的规划。为了在这种新模式下可持续发展,架构从单一的大型应用程序发展为更青睐能够更快交付的几个较小的应用程序。由于这种思维方式的改变,更传统的应用程序设计被标记为“单片”。将组件分解为单独的应用程序的这种新方法被称为“微服务”。微服务应用程序固有的特征带来了一些理想的特性,包括能够同时开发和部署服务,以及独立扩展(增加实例数量)。

软件架构从单片到微服务的变化也导致重新评估应用程序在运行时的打包和部署方式。传统上,整个机器都专门用于一个或两个应用程序。现在,由于微服务导致单个应用程序所需资源的总体减少,将整个机器专门用于一个或两个微服务已不再可行。

幸运的是,一种名为“容器”的技术被引入并因填补许多缺失的功能而受到欢迎,以创建微服务运行时环境。Red Hat 将容器定义为“一组与系统其余部分隔离的一个或多个进程,并包括运行所需的所有文件”(https://www.redhat.com/en/topics/containers/whats-a-linux-container)。容器化技术在计算机领域有着悠久的历史,可以追溯到 20 世纪 70 年代。许多基础容器技术,包括 chroot(能够更改进程和其任何子进程的根目录到文件系统上的新位置)和 jails,今天仍在使用中。

简单且便携的打包模型,以及在每台物理或虚拟机上创建许多隔离的沙盒的能力的结合,导致了微服务领域容器的快速采用。2010 年代中期容器流行的上升也可以归因于 Docker,它通过简化的打包和可以在 Linux、macOS 和 Windows 上使用的运行时将容器带给了大众。轻松分发容器镜像的能力导致了容器技术的流行增加。这是因为首次用户不需要知道如何创建镜像,而是可以利用其他人创建的现有镜像。

容器和微服务成为了天作之合。应用程序具有打包和分发机制,以及共享相同计算占用的能力,同时又能够从彼此隔离。然而,随着越来越多的容器化微服务被部署,整体管理成为了一个问题。你如何确保每个运行的容器的健康?如果一个容器失败了怎么办?如果你的底层机器没有所需的计算能力会发生什么?于是 Kubernetes 应运而生,它帮助解决了容器编排的需求。

在下一节中,我们将讨论 Kubernetes 的工作原理以及它为企业提供的价值。

什么是 Kubernetes?

Kubernetes,通常缩写为k8s(发音为kaytes),是一个开源的容器编排平台。起源于谷歌的专有编排工具 Borg,该项目于 2015 年开源并更名为 Kubernetes。在 2015 年 7 月 21 日发布 v1.0 版本后,谷歌和 Linux 基金会合作成立了云原生计算基金会CNCF),该基金会目前是 Kubernetes 项目的维护者。

Kubernetes 这个词是希腊词,意思是“舵手”或“飞行员”。舵手是负责操纵船只并与船员紧密合作以确保航行安全和稳定的人。Kubernetes 对于容器和微服务有类似的责任。Kubernetes 负责容器的编排和调度。它负责“操纵”这些容器到能够处理它们工作负载的工作节点。Kubernetes 还将通过提供高可用性和健康检查来确保这些微服务的安全。

让我们回顾一些 Kubernetes 如何帮助简化容器化工作负载管理的方式。

容器编排

Kubernetes 最突出的特性是容器编排。这是一个相当复杂的术语,因此我们将其分解为不同的部分。

容器编排是指根据容器的需求,将其放置在计算资源池中的特定机器上。容器编排的最简单用例是在可以处理其资源需求的机器上部署容器。在下图中,有一个应用程序请求 2 Gi 内存(Kubernetes 资源请求通常使用它们的“二的幂”值,在本例中大致相当于 2 GB)和一个 CPU 核心。这意味着容器将从底层机器上分配 2 Gi 内存和 1 个 CPU 核心。Kubernetes 负责跟踪具有所需资源的机器(在本例中称为节点),并将传入的容器放置在该机器上。如果节点没有足够的资源来满足请求,容器将不会被调度到该节点上。如果集群中的所有节点都没有足够的资源来运行工作负载,容器将不会被部署。一旦节点有足够的空闲资源,容器将被部署在具有足够资源的节点上:

图 1.1:Kubernetes 编排和调度

图 1.1 - Kubernetes 编排和调度

容器编排使您不必一直努力跟踪机器上的可用资源。Kubernetes 和其他监控工具提供了对这些指标的洞察。因此,日常开发人员不需要担心可用资源。开发人员只需声明他们期望容器使用的资源量,Kubernetes 将在后台处理其余部分。

高可用性

Kubernetes 的另一个好处是它提供了帮助处理冗余和高可用性的功能。高可用性是防止应用程序停机的特性。它是由负载均衡器执行的,它将传入的流量分配到应用程序的多个实例中。高可用性的前提是,如果一个应用程序实例出现故障,其他实例仍然可以接受传入的流量。在这方面,避免了停机时间,最终用户,无论是人类还是另一个微服务,都完全不知道应用程序出现了故障实例。Kubernetes 提供了一种名为 Service 的网络机制,允许应用程序进行负载均衡。我们将在本章的部署 Kubernetes 应用程序部分更详细地讨论服务。

可扩展性

鉴于容器和微服务的轻量化特性,开发人员可以使用 Kubernetes 快速扩展他们的工作负载,无论是水平还是垂直方向。

水平扩展是部署更多容器实例的行为。如果一个团队在 Kubernetes 上运行他们的工作负载,并且预期负载会增加,他们可以简单地告诉 Kubernetes 部署更多他们的应用实例。由于 Kubernetes 是一个容器编排器,开发人员不需要担心这些应用将部署在哪些物理基础设施上。它会简单地在集群中找到一个具有可用资源的节点,并在那里部署额外的实例。每个额外的实例都将被添加到一个负载均衡池中,这将允许应用程序继续保持高可用性。

垂直扩展是为应用程序分配额外的内存和 CPU 的行为。开发人员可以在应用程序运行时修改其资源需求。这将促使 Kubernetes 重新部署运行实例,并将它们重新调度到可以支持新资源需求的节点上。根据配置方式的不同,Kubernetes 可以以一种防止新实例部署期间停机的方式重新部署每个实例。

活跃的社区

Kubernetes 社区是一个非常活跃的开源社区。因此,Kubernetes 经常收到补丁和新功能。社区还为官方 Kubernetes 文档以及专业或业余博客网站做出了许多贡献。除了文档,社区还积极参与全球各地的聚会和会议的策划和参与,这有助于增加平台的教育和创新。

Kubernetes 庞大的社区带来的另一个好处是构建了许多不同的工具来增强所提供的能力。Helm 就是其中之一。正如我们将在本章后面和整本书中看到的,Helm 是 Kubernetes 社区成员开发的一个工具,通过简化应用程序部署和生命周期管理,大大改善了开发人员的体验。

了解了 Kubernetes 为管理容器化工作负载带来的好处,现在让我们讨论一下如何在 Kubernetes 中部署应用程序。

部署 Kubernetes 应用程序

在 Kubernetes 上部署应用程序基本上与在 Kubernetes 之外部署应用程序类似。所有应用程序,无论是容器化还是非容器化,都必须具有围绕以下主题的配置细节:

  • 网络连接

  • 持久存储和文件挂载

  • 可用性和冗余

  • 应用程序配置

  • 安全

在 Kubernetes 上配置这些细节是通过与 Kubernetes 的应用程序编程接口API)进行交互来完成的。

Kubernetes API 充当一组端点,可以与之交互以查看、修改或删除不同的 Kubernetes 资源,其中许多用于配置应用程序的不同细节。

让我们讨论一些基本的 API 端点,用户可以与之交互,以在 Kubernetes 上部署和配置应用程序。

部署

我们将要探索的第一个 Kubernetes 资源称为部署。部署确定了在 Kubernetes 上部署应用程序所需的基本细节。其中一个基本细节包括 Kubernetes 应该部署的容器映像。容器映像可以在本地工作站上使用诸如dockerjib之类的工具构建,但也可以直接在 Kubernetes 上使用kaniko构建。因为 Kubernetes 不公开用于构建容器映像的本机 API 端点,所以我们不会详细介绍在配置部署资源之前如何构建容器映像。

除了指定容器映像外,部署还指定要部署的应用程序的副本数或实例数。创建部署时,它会生成一个中间资源,称为副本集。副本集部署应用程序的实例数量由部署上的replicas字段确定。应用程序部署在一个容器内,容器本身部署在一个称为 Pod 的构造内。Pod 是 Kubernetes 中的最小单位,至少封装一个容器。

部署还可以定义应用程序的资源限制、健康检查和卷挂载。创建部署时,Kubernetes 创建以下架构:

图 1.2:部署创建一组 Pod

图 1.2 - 部署创建一组 Pod

Kubernetes 中的另一个基本 API 端点用于创建服务资源,我们将在下面讨论。

服务

虽然部署用于将应用程序部署到 Kubernetes,但它们不配置允许应用程序与 Kubernetes 通信的网络组件,Kubernetes 公开了一个用于定义网络层的单独 API 端点,称为服务。服务允许用户和其他应用程序通过为服务端点分配静态 IP 地址来相互通信。然后可以配置服务端点以将流量路由到一个或多个应用程序实例。这种配置提供了负载平衡和高可用性。

一个使用服务的示例架构在下图中描述。请注意,服务位于客户端和 Pod 之间,以提供负载平衡和高可用性:

图 1.3:服务负载平衡传入请求

图 1.3 - 服务负载平衡传入请求

最后一个例子,我们将讨论PersistentVolumeClaim API 端点。

PersistentVolumeClaim

微服务风格的应用程序通过以临时方式维护其状态来实现自给自足。然而,存在许多情况,数据必须存在于单个容器的寿命之外。Kubernetes 通过提供一个用于抽象存储提供和消耗方式的子系统来解决这个问题。为了为他们的应用程序分配持久存储,用户可以创建一个PersistentVolumeClaim端点,该端点指定所需存储的类型和数量。Kubernetes 管理员负责静态分配存储,表示为PersistentVolume,或使用StorageClass动态配置存储,该存储类根据PersistentVolumeClaim端点分配PersistentVolumePersistentVolume包含所有必要的存储细节,包括类型(如网络文件系统[NFS]、互联网小型计算机系统接口[iSCSI]或来自云提供商)以及存储的大小。从用户的角度来看,无论在集群中使用PersistentVolume分配方法或存储后端的哪种方法,他们都不需要管理存储的底层细节。在 Kubernetes 中利用持久存储的能力增加了可以在平台上部署的潜在应用程序的数量。

下图描述了持久存储的一个例子。该图假定管理员已通过StorageClass配置了动态配置:

图 1.4:由 PersistentVolumeClaim 创建的 Pod 挂载 PersistentVolume

图 1.4 - 由 PersistentVolumeClaim 创建的 Pod 挂载的 PersistentVolume。

Kubernetes 中有更多的资源,但到目前为止,你可能已经有了一个大致的了解。现在的问题是这些资源实际上是如何创建的?

我们将在下一节进一步探讨这个问题。

资源管理的方法

为了在 Kubernetes 上部署应用程序,我们需要与 Kubernetes API 交互以创建资源。 kubectl是我们用来与 Kubernetes API 交互的工具。 kubectl是一个用于将 Kubernetes API 的复杂性抽象化的命令行接口(CLI)工具,允许最终用户更有效地在平台上工作。

让我们讨论一下如何使用 kubectl 来管理 Kubernetes 资源。

命令式和声明式配置

kubectl 工具提供了一系列子命令,以命令式方式创建和修改资源。以下是这些命令的一个小列表:

  • create

  • describe

  • edit

  • delete

kubectl 命令遵循一个常见的格式:

kubectl <verb> <noun> <arguments>

动词指的是 kubectl 的子命令之一,名词指的是特定的 Kubernetes 资源。例如,可以运行以下命令来创建一个部署:

kubectl create deployment my-deployment --image=busybox

这将指示 kubectl 与部署 API 对话,并使用来自 Docker Hub 的 busybox 镜像创建一个名为 my-deployment 的新部署。

您可以使用 kubectl 获取有关使用 describe 子命令创建的部署的更多信息:

kubectl describe deployment my-deployment

此命令将检索有关部署的信息,并以可读格式格式化结果,使开发人员可以检查 Kubernetes 上的实时 my-deployment 部署。

如果需要对部署进行更改,开发人员可以使用 edit 子命令在原地修改它:

kubectl edit deployment my-deployment

此命令将打开一个文本编辑器,允许您修改部署。

在删除资源时,用户可以运行 delete 子命令:

kubectl delete deployment my-deployment

这将指示 API 删除名为 my-deployment 的部署。

一旦创建,Kubernetes 资源将作为 JSON 资源文件存在于集群中,可以将其导出为 YAML 文件以获得更大的人类可读性。可以在此处看到 YAML 格式的示例资源:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
        - name: main
          image: busybox
          args:
            - sleep
            - infinity

前面的 YAML 格式呈现了一个非常基本的用例。它部署了来自 Docker Hub 的 busybox 镜像,并无限期地运行 sleep 命令以保持 Pod 运行。

虽然使用我们刚刚描述的 kubectl 子命令以命令式方式创建资源可能更容易,但 Kubernetes 允许您以声明式方式直接管理 YAML 资源,以获得对资源创建更多的控制。kubectl 子命令并不总是让您配置所有可能的资源选项,但直接创建 YAML 文件允许您更灵活地创建资源并填补 kubectl 子命令可能包含的空白。

在声明式创建资源时,用户首先以 YAML 格式编写他们想要创建的资源。接下来,他们使用kubectl工具将资源应用于 Kubernetes API。而在命令式配置中,开发人员使用kubectl子命令来管理资源,声明式配置主要依赖于一个子命令——apply

声明式配置通常采用以下形式:

kubectl apply -f my-deployment.yaml

该命令为 Kubernetes 提供了一个包含资源规范的 YAML 资源,尽管也可以使用 JSON 格式。Kubernetes 根据资源的存在与否来推断要执行的操作(创建或修改)。

应用程序可以通过以下步骤进行声明式配置:

  1. 首先,用户可以创建一个名为deployment.yaml的文件,并提供部署的 YAML 格式规范。我们将使用与之前相同的示例:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
        - name: main
          image: busybox
          args:
            - sleep
            - infinity
  1. 然后可以使用以下命令创建部署:
kubectl apply -f deployment.yaml

运行此命令后,Kubernetes 将尝试按照您指定的方式创建部署。

  1. 如果要对部署进行更改,比如将replicas的数量更改为2,您首先需要修改deployment.yaml文件:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox
spec:
  replicas: 2
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
        - name: main
          image: busybox
          args:
            - sleep
            - infinity
  1. 然后,您可以使用kubectl apply应用更改:
kubectl apply -f deployment.yaml

运行该命令后,Kubernetes 将在先前应用的deployment上应用提供的部署声明。此时,应用程序将从replica值为1扩展到2

  1. 在删除应用程序时,Kubernetes 文档实际上建议以命令式方式进行操作;也就是说,使用delete子命令而不是apply
kubectl delete -f deployment.yaml
  1. 通过传递-f标志和文件名,可以使delete子命令更具声明性。这样可以向kubectl提供在特定文件中声明的要删除的资源的名称,并允许开发人员继续使用声明性 YAML 文件管理资源。

了解了 Kubernetes 资源的创建方式,现在让我们讨论一下资源配置中涉及的一些挑战。

资源配置挑战

在前一节中,我们介绍了 Kubernetes 有两种不同的配置方法——命令式和声明式。一个需要考虑的问题是,在使用命令式和声明式方法创建 Kubernetes 资源时,用户需要注意哪些挑战?

让我们讨论一些最常见的挑战。

Kubernetes 资源的多种类型

首先,Kubernetes 中有许多许多不同的资源。以下是开发人员应该了解的资源的简短列表:

  • 部署

  • StatefulSet

  • 服务

  • 入口

  • ConfigMap

  • Secret

  • StorageClass

  • PersistentVolumeClaim

  • ServiceAccount

  • 角色

  • RoleBinding

  • 命名空间

在 Kubernetes 上部署应用程序并不像按下标有“部署”的大红按钮那么简单。开发人员需要能够确定部署其应用程序所需的资源,并且需要深入了解这些资源,以便能够适当地配置它们。这需要对平台有很多的了解和培训。虽然理解和创建资源可能已经听起来像是一个很大的障碍,但实际上这只是许多不同操作挑战的开始。

保持活动和本地状态同步

我们鼓励的一种配置 Kubernetes 资源的方法是将它们的配置保留在源代码控制中,供团队编辑和共享,这也使得源代码控制存储库成为真相的来源。在源代码控制中定义的配置(称为“本地状态”)然后通过将它们应用到 Kubernetes 环境中来创建,并且资源变为“活动”或进入可以称为“活动状态”的状态。这听起来很简单,但当开发人员需要对其资源进行更改时会发生什么?正确的答案应该是修改本地文件并应用更改,以将本地状态与活动状态同步,以更新真相的来源。然而,这通常不是最终发生的事情。在短期内,更改活动资源的位置通常更简单,而不是修改本地文件。这会导致本地和活动状态之间的状态不一致,并且使得在 Kubernetes 上扩展变得困难。

应用程序生命周期很难管理

生命周期管理是一个复杂的术语,但在这个上下文中,我们将把它称为安装、升级和回滚应用程序的概念。在 Kubernetes 世界中,安装会创建资源来部署和配置应用程序。初始安装将创建我们在这里称为应用程序的“版本 1”。

然后,升级可以被视为对一个或多个 Kubernetes 资源的编辑或修改。每一批编辑可以被视为一个单独的升级。开发人员可以修改单个服务资源,将版本号提升到“版本 2”。然后开发人员可以修改部署、配置映射和服务,将版本计数提升到“版本 3”。

随着应用程序的新版本继续部署到 Kubernetes 上,跟踪已发生的更改变得更加困难。在大多数情况下,Kubernetes 没有固有的方式来记录更改的历史。虽然这使得升级更难以跟踪,但也使得恢复先前版本的应用程序变得更加困难。假设开发人员之前对特定资源进行了错误的编辑。团队如何知道要回滚到哪个版本?n-1情况特别容易解决,因为那是最近的版本。然而,如果最新的稳定版本是五个版本之前呢?团队经常因为无法快速识别先前有效的最新稳定配置而不得不匆忙解决问题。

资源文件是静态的。

这是一个主要影响应用 YAML 资源的声明性配置风格的挑战。遵循声明性方法的困难部分在于,Kubernetes 资源文件并非原生设计为可参数化。资源文件大多被设计为在应用之前完整地编写出来,并且内容保持不变,直到文件被修改。在处理 Kubernetes 时,这可能是一个令人沮丧的现实。一些 API 资源可能会很长,包含许多不同的可定制字段,因此完整地编写和配置 YAML 资源可能会非常繁琐。

静态文件很容易变成样板文件。样板文件代表在不同但相似的上下文中基本保持一致的文本或代码。如果开发人员管理多个不同的应用程序,可能需要管理多个不同的部署资源、多个不同的服务资源等。比较不同应用程序的资源文件时,可能会发现它们之间存在大量相似的 YAML 配置。

下图描述了两个资源之间具有重要样板配置的示例。蓝色文本表示样板行,而红色文本表示唯一行:

图 1.5:两个具有样板的资源示例

图 1.5 - 两个具有样板的资源示例

在这个例子中,请注意,每个文件几乎完全相同。当管理类似这样相似的文件时,样板变成了团队以声明方式管理其应用程序的主要头痛。

Helm 来拯救!

随着时间的推移,Kubernetes 社区发现创建和维护用于部署应用程序的 Kubernetes 资源是困难的。这促使开发了一个简单而强大的工具,可以让团队克服在 Kubernetes 上部署应用程序时所面临的挑战。创建的工具称为 Helm。Helm 是一个用于在 Kubernetes 上打包和部署应用程序的开源工具。它通常被称为Kubernetes 软件包管理器,因为它与您在喜爱的操作系统上找到的任何其他软件包管理器非常相似。Helm 在整个 Kubernetes 社区广泛使用,并且是一个 CNCF 毕业项目。

鉴于 Helm 与传统软件包管理器的相似之处,让我们首先通过回顾软件包管理器的工作原理来开始探索 Helm。

理解软件包管理器

软件包管理器用于简化安装、升级、回滚和删除系统应用程序的过程。这些应用程序以称为软件包的单位进行定义,其中包含了关于目标软件及其依赖关系的元数据。

软件包管理器背后的过程很简单。首先,用户将软件包的名称作为参数传递。然后,软件包管理器执行针对软件包存储库的查找,以查看该软件包是否存在。如果找到了,软件包管理器将安装由软件包及其依赖项定义的应用程序到系统上指定的位置。

软件包管理器使管理软件变得非常容易。举个例子,假设你想要在 Fedora 机器上安装htop,一个 Linux 系统监视器。安装这个软件只需要输入一个命令:

dnf install htop --assumeyes	

这会指示自 2015 年以来成为 Fedora 软件包管理器的 dnf 在 Fedora 软件包存储库中查找 htop 并安装它。dnf还负责安装htop软件包的依赖项,因此您无需担心事先安装其要求。在dnf从上游存储库中找到htop软件包后,它会询问您是否确定要继续。--assumeyes标志会自动回答yes这个问题和dnf可能潜在询问的任何其他提示。

随着时间的推移,新版本的htop可能会出现在上游存储库中。dnf和其他软件包管理器允许用户高效地升级软件的新版本。允许用户使用dnf进行升级的子命令是升级:

dnf upgrade htop --assumeyes

这会指示dnfhtop升级到最新版本。它还会将其依赖项升级到软件包元数据中指定的版本。

虽然向前迈进通常更好,但软件包管理器也允许用户向后移动,并在必要时将应用程序恢复到先前的版本。dnf使用downgrade子命令来实现这一点:

dnf downgrade htop --assumeyes

这是一个强大的过程,因为软件包管理器允许用户在报告关键错误或漏洞时快速回滚。

如果您想彻底删除一个应用程序,软件包管理器也可以处理。dnf提供了remove子命令来实现这一目的:

dnf remove htop --assumeyes	

在本节中,我们回顾了在 Fedora 上使用dnf软件包管理器来管理软件包的方法。作为 Kubernetes 软件包管理器的 Helm 与dnf类似,无论是在目的还是功能上。dnf用于在 Fedora 上管理应用程序,Helm 用于在 Kubernetes 上管理应用程序。我们将在接下来更详细地探讨这一点。

Kubernetes 软件包管理器

考虑到 Helm 的设计目的是提供类似于软件包管理器的体验,dnf或类似工具的有经验的用户将立即理解 Helm 的基本概念。然而,当涉及到具体的实现细节时,情况变得更加复杂。dnf操作RPM软件包,提供可执行文件、依赖信息和元数据。另一方面,Helm 使用charts。Helm chart 可以被视为 Kubernetes 软件包。Charts 包含部署应用程序所需的声明性 Kubernetes 资源文件。与RPM类似,它还可以声明应用程序运行所需的一个或多个依赖项。

Helm 依赖于存储库来提供对图表的广泛访问。图表开发人员创建声明性的 YAML 文件,将它们打包成图表,并将它们发布到图表存储库。然后,最终用户使用 Helm 搜索现有的图表,以部署到 Kubernetes,类似于dnf的最终用户搜索要部署到 Fedora 的RPM软件包。

让我们通过一个基本的例子来看看。Helm 可以使用发布到上游存储库的图表来部署Redis,一个内存缓存,到 Kubernetes 中。这可以使用 Helm 的install命令来执行:

helm install redis bitnami/redis --namespace=redis

这将在 bitnami 图表存储库中安装redis图表到名为redis的 Kubernetes 命名空间。这个安装将被称为初始修订,或者 Helm 图表的初始部署。

如果redis图表的新版本可用,用户可以使用upgrade命令升级到新版本:

helm upgrade redis bitnami/redis --namespace=redis

这将升级Redis,以满足新的redis-ha 图表定义的规范。

在操作系统中,用户应该关注如果发现了错误或漏洞,如何回滚。在 Kubernetes 上的应用程序也存在同样的问题,Helm 提供了回滚命令来处理这种情况:

helm rollback redis 1 --namespace=redis

这个命令将Redis回滚到它的第一个修订版本。

最后,Helm 提供了使用uninstall命令彻底删除Redis的能力:

helm uninstall redis --namespace=redis

比较dnf,Helm 的子命令,以及它们在下表中提供的功能。注意dnf和 Helm 提供了类似的命令,提供了类似的用户体验:

理解了 Helm 作为一个包管理器的功能,让我们更详细地讨论 Helm 为 Kubernetes 带来的好处。Helm 的好处

在本章的前面,我们回顾了如何通过管理 Kubernetes 资源来创建 Kubernetes 应用程序,并讨论了一些涉及的挑战。以下是 Helm 可以克服这些挑战的几种方式。

抽象的 Kubernetes 资源的复杂性

假设开发人员被要求在 Kubernetes 上部署 MySQL 数据库。开发人员需要创建所需的资源来配置其容器、网络和存储。从头开始配置这样的应用程序所需的 Kubernetes 知识量很高,对于新手甚至中级的 Kubernetes 用户来说是一个很大的障碍。

使用 Helm,负责部署 MySQL 数据库的开发人员可以简单地在上游图表存储库中搜索 MySQL 图表。这些图表已经由社区中的图表开发人员编写,并且已经包含了部署 MySQL 数据库所需的声明性配置。在这方面,具有这种任务的开发人员将像任何其他软件包管理器一样使用 Helm 作为简单的最终用户。

持续的修订历史

Helm 有一个称为发布历史的概念。当首次安装 Helm 图表时,Helm 将该初始修订添加到历史记录中。随着修订通过升级的增加,历史记录会进一步修改,保留应用程序在不同修订中配置的各种快照。

以下图表描述了持续的修订历史。蓝色的方块说明了资源已经从其先前版本进行了修改:

图 1.6:修订历史的示例

图 1.6 - 修订历史的示例

跟踪每个修订的过程为回滚提供了机会。Helm 中的回滚非常简单。用户只需将 Helm 指向先前的修订,Helm 将 live 状态恢复到所选修订的状态。有了 Helm,过去的n-1备份已经过时。Helm 允许用户将其应用程序回滚到他们想要的任何时间,甚至可以回滚到最初的安装。

动态配置声明性资源

以声明方式创建资源的最大麻烦之一是 Kubernetes 资源是静态的,无法参数化。正如您可能还记得的那样,这导致资源在应用程序和类似配置之间变得样板化,使团队更难以将其应用程序配置为代码。Helm 通过引入模板来缓解这些问题。

值就是 Helm 称为图表参数的简单东西。模板是基于给定值集的动态生成文件。这两个构造为图表开发人员提供了根据最终用户提供的值自动生成基于值的 Kubernetes 资源的能力。通过这样做,由 Helm 管理的应用程序变得更加灵活,减少样板代码,并更易于维护。

值和模板允许用户执行以下操作:

  • 参数化常见字段,比如在部署中的图像名称和服务中的端口

  • 根据用户输入生成长篇的 YAML 配置,比如在部署中的卷挂载或 ConfigMap 中的数据

  • 根据用户输入包含或排除资源

能够动态生成声明性资源文件使得创建基于 YAML 的资源变得更简单,同时确保应用以一种易于复制的方式创建。

本地和实时状态之间的一致性

软件包管理器可以防止用户手动管理应用程序及其依赖关系。所有管理都可以通过软件包管理器本身完成。Helm 也是如此。因为 Helm 图表包含了灵活的 Kubernetes 资源配置,用户不应该直接对实时的 Kubernetes 资源进行修改。想要修改他们的应用程序的用户可以通过向 Helm 图表提供新值或将其应用程序升级到相关图表的更新版本来实现。这使得本地状态(由 Helm 图表配置表示)和实时状态在修改过程中保持一致,使用户能够为他们的 Kubernetes 资源配置提供真实的来源。

智能部署

Helm 通过确定 Kubernetes 资源需要创建的顺序来简化应用部署。Helm 分析每个图表的资源,并根据它们的类型对它们进行排序。这种预先确定的顺序存在是为了确保常常有资源依赖于它们的资源首先被创建。例如,Secrets 和 ConfigMaps 应该在部署之前创建,因为部署很可能会使用这些资源作为卷。Helm 在没有用户交互的情况下执行此排序,因此这种复杂性被抽象化,用户无需担心这些资源被应用的顺序。

自动生命周期钩子

与其他软件包管理器类似,Helm 提供了定义生命周期钩子的能力。生命周期钩子是在应用程序生命周期的不同阶段自动执行的操作。它们可以用来执行诸如以下操作:

  • 在升级时执行数据备份。

  • 在回滚时恢复数据。

  • 在安装之前验证 Kubernetes 环境。

生命周期钩子非常有价值,因为它们抽象了可能不是特定于 Kubernetes 的任务的复杂性。例如,Kubernetes 用户可能不熟悉数据库备份背后的最佳实践,或者可能不知道何时应执行此类任务。生命周期钩子允许专家编写自动化,以在建议时执行这些最佳实践,以便用户可以继续高效工作,而无需担心这些细节。

摘要

在本章中,我们首先探讨了采用基于微服务的架构的变化趋势,将应用程序分解为几个较小的应用程序,而不是部署一个庞大的单体应用程序。创建更轻量级且更易管理的应用程序导致利用容器作为打包和运行时格式,以更频繁地发布版本。通过采用容器,引入了额外的运营挑战,并通过使用 Kubernetes 作为容器编排平台来管理容器生命周期来解决这些挑战。

我们讨论了配置 Kubernetes 应用程序的各种方式,包括部署、服务和持久卷索赔。这些资源可以使用两种不同的应用程序配置样式来表示:命令式和声明式。这些配置样式中的每一种都对部署 Kubernetes 应用程序涉及的一系列挑战做出了贡献,包括理解 Kubernetes 资源工作的知识量以及管理应用程序生命周期的挑战。

为了更好地管理构成应用程序的每个资产,Helm 被引入为 Kubernetes 的软件包管理器。通过其丰富的功能集,可以轻松管理应用程序的完整生命周期,包括安装、升级、回滚和删除。

在下一章中,我们将详细介绍配置 Helm 环境的过程。我们还将安装所需的工具,以便使用 Helm 生态系统,并按照本书提供的示例进行操作。

进一步阅读

有关构成应用程序的 Kubernetes 资源的更多信息,请参阅 Kubernetes 文档中的了解 Kubernetes 对象页面,网址为 https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/。

为了加强本章讨论的 Helm 的一些好处,请参考 Helm 文档中的使用 Helm页面,网址为 https://helm.sh/docs/intro/using_helm/。 (本页还深入讨论了 Helm 周围的一些基本用法,这将在本书中更详细地讨论。)

问题

  1. 单体应用和微服务应用有什么区别?

  2. Kubernetes 是什么?它旨在解决什么问题?

  3. 在部署应用程序到 Kubernetes 时,常用的一些kubectl命令是什么?

  4. 在部署应用程序到 Kubernetes 时通常涉及哪些挑战?

  5. Helm 如何作为 Kubernetes 的包管理器?它是如何解决 Kubernetes 提出的挑战的?

  6. 假设您想要回滚在 Kubernetes 上部署的应用程序。哪个 Helm 命令允许您执行此操作?Helm 如何跟踪您的更改以使此回滚成为可能?

  7. 允许 Helm 作为包管理器运行的四个主要 Helm 命令是什么?

第二章:准备 Kubernetes 和 Helm 环境

Helm 是一个提供各种好处的工具,帮助用户更轻松地部署和管理 Kubernetes 应用程序。然而,在用户可以开始体验这些好处之前,他们必须满足一些先决条件。首先,用户必须能够访问 Kubernetes 集群。其次,用户应该具有 Kubernetes 和 Helm 的命令行工具。最后,用户应该了解 Helm 的基本配置选项,以便尽可能少地产生摩擦地提高生产力。

在本章中,我们将概述开始使用 Helm 所需的工具和概念。本章将涵盖以下主题:

  • 使用 Minikube 准备本地 Kubernetes 环境

  • 设置kubectl

  • 设置 Helm

  • 配置 Helm

技术要求

在本章中,您将在本地工作站上安装以下技术:

  • Minikube

  • VirtualBox

  • Helm

这些工具可以通过软件包管理器安装,也可以通过下载链接直接下载。我们将提供在 Windows 上使用Chocolatey软件包管理器,在 macOS 上使用Homebrew软件包管理器,在基于 Debian 的 Linux 发行版上使用apt-get软件包管理器,在基于 RPM 的 Linux 发行版上使用dnf软件包管理器的使用说明。

使用 Minikube 准备本地 Kubernetes 环境

没有访问 Kubernetes 集群,Helm 将无法部署应用程序。因此,让我们讨论一个用户可以遵循的选项,在他们的机器上运行自己的集群的选项—Minikube。

Minikube 是一个由社区驱动的工具,允许用户轻松在本地机器上部署一个小型的单节点 Kubernetes 集群。使用 Minikube 创建的集群是在一个虚拟机(VM)内创建的,因此可以在与运行 VM 的主机操作系统隔离的方式下创建和丢弃。Minikube 提供了一个很好的方式来尝试 Kubernetes,并且还可以用来学习如何在本书中提供的示例中使用 Helm。

在接下来的几节中,我们将介绍如何安装和配置 Minikube,以便在学习如何使用 Helm 时拥有一个可用的 Kubernetes 集群。有关更全面的说明,请参考官方 Minikube 网站的入门页面minikube.sigs.k8s.io/docs/start/

安装 Minikube

与本章中将安装的其他工具一样,Minikube 的二进制文件是为 Windows、macOS 和 Linux 操作系统编译的。在 Windows 和 macOS 上安装最新版本的 Minikube 的最简单方法是通过软件包管理器,例如 Windows 的Chocolatey和 macOS 的Homebrew

Linux 用户将发现,通过从 Minikube 的 GitHub 发布页面下载最新的minikube二进制文件更容易安装,尽管这种方法也可以在 Windows 和 macOS 上使用。

以下步骤描述了如何根据您的计算机和安装偏好安装 Minikube。请注意,在撰写本书中使用的示例的编写和开发过程中使用了 Minikube 版本 v1.5.2。

要通过软件包管理器安装它(在 Windows 和 macOS 上),请执行以下操作:

  • 对于 Windows,请使用以下命令:
> choco install minikube
  • 对于 macOS,请使用以下命令:
$ brew install minikube

以下步骤向您展示了如何通过下载链接(在 Windows、macOS 和 Linux 上)安装它。

Minikube二进制文件可以直接从其在 Git 上的发布页面下载Hub at https://github.com/kubernetes/minikube/releases/:

  1. 在发布页面的底部,有一个名为Assets的部分,其中包含了各种支持的平台可用的 Minikube 二进制文件:图 2.1:来自 GitHub 发布页面的 Minikube 二进制文件

图 2.1:来自 GitHub 发布页面的 minikube 二进制文件

  1. Assets部分下,应下载与目标平台对应的二进制文件。下载后,您应将二进制文件重命名为minikube。例如,如果您正在下载 Linux 二进制文件,您将运行以下命令:
$ mv minikube-linux-amd64 minikube
  1. 为了执行minikube,Linux 和 macOS 用户可能需要通过运行chmod命令添加可执行位:
$ chmod u+x
  1. 然后,minikube应移动到由PATH变量管理的位置,以便可以从命令行的任何位置执行它。PATH变量包含的位置因操作系统而异。对于 macOS 和 Linux 用户,可以通过在终端中运行以下命令来确定这些位置:
$ echo $PATH
  1. Windows 用户可以通过在命令提示符或 PowerShell 中运行以下命令来确定PATH变量的位置:
> $env:PATH
  1. 然后,您可以使用 mv 命令将 minikube 二进制文件移动到新位置。以下示例将 minikube 移动到 Linux 上的常见 PATH 位置:
$ mv minikube /usr/local/bin/
  1. 您可以通过运行 minikube version 并确保显示的版本与下载的版本相对应来验证 Minikube 的安装:
$ minikube version
minikube version: v1.5.2
commit: 792dbf92a1de583fcee76f8791cff12e0c9440ad-dirty

尽管您已经下载了 Minikube,但您还需要一个 hypervisor 来运行本地 Kubernetes 集群。这可以通过安装 VirtualBox 来实现,我们将在下一节中描述。

安装 VirtualBox

Minikube 依赖于存在的 hypervisors,以便在虚拟机上安装单节点 Kubernetes 集群。对于本书,我们选择讨论 VirtualBox 作为 hypervisor 选项,因为它是最灵活的,并且可用于 Windows、macOS 和 Linux 操作系统。每个操作系统的其他 hypervisor 选项可以在官方 Minikube 文档中找到 minikube.sigs.k8s.io/docs/start/

与 Minikube 一样,VirtualBox 可以通过 Chocolatey 或 Homebrew 轻松安装,但也可以使用 apt-get(Debian-based Linux)和 dnf(RPM/RHEL-based Linux)轻松安装:

  • 在 Windows 上安装 VirtualBox 的代码如下:
> choco install virtualbox
  • 在 macOS 上安装 VirtualBox 的代码如下:
$ brew cask install virtualbox
  • 在基于 Debian 的 Linux 上安装 VirtualBox 的代码如下:
$ apt-get install virtualbox
  • 在 RHEL-based Linux 上安装 VirtualBox 的代码如下:
$ dnf install VirtualBox

可以在其官方下载页面 www.virtualbox.org/wiki/Downloads 找到安装 VirtualBox 的其他方法。

安装了 VirtualBox 后,必须配置 Minikube 以利用 VirtualBox 作为其默认 hypervisor。此配置将在下一节中进行。

将 VirtualBox 配置为指定的 hypervisor

可以通过将 minikubevm-driver 选项设置为 virtualbox 来将 VirtualBox 设置为默认 hypervisor:

$ minikube config set vm-driver virtualbox

请注意,此命令可能会产生以下警告:

These changes will take effect upon a minikube delete and then a minikube start

如果工作站上没有活动的 Minikube 集群,则可以安全地忽略此消息。此命令表示任何现有的 Kubernetes 集群在删除并重新创建集群之前都不会使用 VirtualBox 作为 hypervisor。

可以通过评估 vm-driver 配置选项的值来确认切换到 VirtualBox:

$ minikube config get vm-driver

如果一切顺利,输出将如下所示:

Virtualbox

除了配置默认的 hypervisor 之外,您还可以配置分配给 Minikube 集群的资源,这将在下一节中讨论。

配置 Minikube 资源分配

默认情况下,Minikube 将为其虚拟机分配两个 CPU 和 2 GB 的 RAM。这些资源对本书中的每个示例都足够,除了第七章中更需要资源的示例。如果您的机器有可用资源,应该将默认内存分配增加到 4 GB(CPU 分配可以保持不变)。

运行以下命令将增加新 Minikube 虚拟机的默认内存分配为 4 GB(4000 MB)。

$ minikube config set memory 4000

可以通过运行minikube config get memory命令来验证此更改,类似于之前验证vm-driver更改的方式。

让我们继续探索 Minikube,讨论其基本用法。

探索基本用法

在本书中,了解典型 Minikube 操作中使用的关键命令将非常方便。在本书的示例执行过程中,了解这些命令也是至关重要的。幸运的是,Minikube 是一个很容易上手的工具。

Minikube 有三个关键子命令:

  • start

  • stop

  • delete

start子命令用于创建单节点 Kubernetes 集群。它将创建一个虚拟机并在其中引导集群。一旦集群准备就绪,命令将终止:

$ minikube start
 minikube v1.5.2 on Fedora 30
  Creating virtualbox VM (CPUs=2, Memory=4000MB, Disk=20000MB) ...
  Preparing Kubernetes v1.16.2 on Docker '18.09.9' ...
  Pulling images ...
  Launching Kubernetes ...
  Waiting for: apiserver
  Done! kubectl is now configured to use 'minikube'

stop子命令用于关闭集群和虚拟机。集群和虚拟机的状态将保存到磁盘上,允许用户再次运行start子命令快速开始工作,而不必从头开始构建新的虚拟机。当您完成对集群的工作并希望以后返回时,应该尝试养成运行minikube stop的习惯:

$ minikube stop
  Stopping 'minikube' in virtualbox ...
  'minikube' stopped.

delete子命令用于删除集群和虚拟机。此命令将擦除集群和虚拟机的状态,释放先前分配的磁盘空间。下次执行minikube start时,将创建一个全新的集群和虚拟机。当您希望删除所有分配的资源并在下次调用minikube start时在一个全新的 Kubernetes 集群上工作时,应该运行delete子命令:

$ minikube delete
  Deleting 'minikube' in virtualbox ...
  The 'minikube' cluster has been deleted.
  Successfully deleted profile 'minikube'

还有更多 Minikube 子命令可用,但这些是您应该知道的主要命令。

安装并配置了 Minikube 后,您现在可以安装kubectl,即 Kubernetes 命令行工具,并满足使用 Helm 的其余先决条件。

设置 Kubectl

第一章中所述,了解 Kubernetes 和 Helm,Kubernetes 是一个公开不同 API 端点的系统。这些 API 端点用于在集群上执行各种操作,例如创建、查看或删除资源。为了提供更简单的用户体验,开发人员需要一种与 Kubernetes 交互的方式,而无需管理底层 API 层。

虽然在本书的过程中,您主要会使用 Helm 命令行工具来安装和管理应用程序,但kubectl是常见任务的必备工具。

继续阅读以了解如何在本地工作站上安装kubectl。请注意,写作时使用的kubectl版本为v1.16.2

安装 Kubectl

kubectl可以使用 Minikube 安装,也可以通过软件包管理器或直接下载获取。我们首先描述如何使用 Minikube 获取kubectl

通过 Minikube 安装 Kubectl

使用 Minikube 安装kubectl非常简单。Minikube 提供了一个名为kubectl的子命令,它将下载 Kubectl 二进制文件。首先运行minikube kubectl

$ minikube kubectl version
  Downloading kubectl v1.16.2

此命令将kubectl安装到$HOME/.kube/cache/v1.16.2目录中。请注意,路径中包含的kubectl版本将取决于您使用的 Minikube 版本。要访问kubectl,可以使用以下语法:

          minikube kubectl -- <subcommand> <flags>

以下是一个示例命令:

$ minikube kubectl -- version –client
Client Version: version.Info{Major:'1', Minor:'16', GitVersion:'v1.16.2', GitCommit:'c97fe5036ef3df2967d086711e6c0c405941e14b', GitTreeState:'clean', BuildDate:'2019-10-15T19:18:23Z', GoVersion:'go1.12.10', Compiler:'gc', Platform:'linux/amd64'}

使用minikube kubectl调用kubectl就足够了,但是语法比直接调用kubectl更加笨拙。可以通过将kubectl可执行文件从本地 Minikube 缓存复制到由PATH变量管理的位置来克服这个问题。在每个操作系统上执行此操作类似,但以下是如何在 Linux 机器上实现的示例:

$ sudo cp ~/.kube/cache/v1.16.2/kubectl /usr/local/bin/

完成后,kubectl可以作为独立的二进制文件调用,如下所示:

$ kubectl version –client
Client Version: version.Info{Major:'1', Minor:'16', GitVersion:'v1.16.2', GitCommit:'c97fe5036ef3df2967d086711e6c0c405941e14b', GitTreeState:'clean', BuildDate:'2019-10-15T19:18:23Z', GoVersion:'go1.12.10', Compiler:'gc', Platform:'linux/amd64'}

在没有 Minikube 的情况下安装 Kubectl

Kubectl 也可以在没有 Minikube 的情况下安装。Kubernetes 官方文档提供了多种不同的机制来为各种目标操作系统进行安装,网址为 https://kubernetes.io/docs/tasks/tools/install-kubectl/。

使用软件包管理器

kubectl可以在没有 Minikube 的情况下通过本机软件包管理进行安装。以下列表演示了如何在不同的操作系统上完成此操作:

  • 使用以下命令在 Windows 上安装kubectl
> choco install kubernetes-cli
  • 使用以下命令在 macOS 上安装kubectl
$ brew install kubernetes-cli
  • 使用以下命令在基于 Debian 的 Linux 上安装kubectl
$ sudo apt-get update && sudo apt-get install -y apt-transport-https gnupg2
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ echo 'deb https://apt.kubernetes.io/ kubernetes-xenial main' | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
$ sudo apt-get install -y kubectl
  • 使用以下命令在基于 RPM 的 Linux 上安装kubectl
$ cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
$ yum install -y kubectl

我们将在下一节讨论最终的 Kubectl 安装方法。

直接从链接下载

Kubectl 也可以直接从下载链接下载。下载链接将包含要下载的 Kubectl 版本。您可以通过在浏览器中访问storage.googleapis.com/kubernetes-release/release/stable.txt来确定 Kubectl 的最新版本。

以下示例说明了如何下载版本 v1.16.2,这是本书中使用的 Kubectl 版本:

Kubectl 二进制文件可以移动到由PATH变量管理的位置。在 macOS 和 Linux 操作系统上,确保授予可执行权限:

$ chmod u+x kubectl

可以通过运行以下命令来验证 Kubectl 的安装。

$ kubectl version –client
Client Version: version.Info{Major:'1', Minor:'16', GitVersion:'v1.16.2', GitCommit:'c97fe5036ef3df2967d086711e6c0c405941e14b', GitTreeState:'clean', BuildDate:'2019-10-15T19:18:23Z', GoVersion:'go1.12.10', Compiler:'gc', Platform:'linux/amd64'}

现在我们已经介绍了如何设置kubectl,我们准备进入本书的关键技术——Helm。

设置 Helm

安装 Minikube 和kubectl后,下一个逻辑工具是配置 Helm。请注意,写作本书时使用的 Helm 版本是v3.0.0,但建议您使用 Helm v3 发布的最新版本,以获得最新的漏洞修复和 bug 修复。

安装 Helm

Chocolatey 和 Homebrew 都有 Helm 软件包,可以方便地在 Windows 或 macOS 上安装。在这些系统上,可以运行以下命令来使用软件包管理器安装 Helm:

  • 使用以下命令在 Windows 上安装 Helm:
> choco install kubernetes-helm     
  • 使用以下命令在 macOS 上安装 Helm:
$ brew install helm

Linux 用户或者宁愿从直接可下载链接安装 Helm 的用户可以按照以下步骤从 Helm 的 GitHub 发布页面下载存档文件:

  1. 在 Helm 的 GitHub 发布页面上找到名为Installation的部分:图 2.2:Helm GitHub 发布页面上的安装部分

图 2.2:Helm GitHub 发布页面上的安装部分

  1. 下载与所使用操作系统对应版本的存档文件。

  2. 下载后,需要解压文件。可以通过在 PowerShell 上使用Expand-Archive命令函数或在 Bash 上使用tar实用程序来实现这一点:

  • 对于 Windows/PowerShell,请使用以下示例:
> Expand-Archive -Path helm-v3.0.0-windows-amd64.zip -DestinationPath $DEST
  • 对于 Linux 和 Mac,请使用以下示例:
$ tar -zxvf helm-v3.0.0-linux.amd64.tgz

确保指定与下载版本对应的版本。helm二进制文件可以在未解压的文件夹中找到。它应该被移动到由PATH变量管理的位置。

以下示例向您展示了如何将helm二进制文件移动到 Linux 系统上的/usr/local/bin文件夹中:

$ mv ~/Downloads/linux-amd64/helm /usr/local/bin

无论 Helm 是以何种方式安装的,都可以通过运行helm version命令来进行验证。如果结果输出类似于以下输出,则 Helm 已成功安装:

$ helm version
version.BuildInfo{Version:'v3.0.0', GitCommit:'e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6', GitTreeState:'clean', GoVersion:'go1.13.4'}

安装了 Helm 后,继续下一部分,了解基本的 Helm 配置主题。

配置 Helm

Helm 是一个具有合理默认值的工具,允许用户在安装后无需执行大量任务即可提高生产力。话虽如此,用户可以更改或启用几种不同的选项来修改 Helm 的行为。我们将在接下来的部分中介绍这些选项,首先是配置上游仓库。

添加上游仓库

用户可以开始修改他们的 Helm 安装的一种方式是添加上游图表存储库。在[第一章]中,理解 Kubernetes 和 Helm,我们描述了图表存储库包含 Helm 图表,用于打包 Kubernetes 资源文件。作为 Kubernetes 包管理器的 Helm,可以连接到各种图表存储库来安装 Kubernetes 应用程序。

Helm 提供了 repo 子命令,允许用户管理配置的图表存储库。这个子命令包含其他子命令,可以用来执行针对指定存储库的操作。

以下是五个 repo 子命令:

  • add:添加图表存储库

  • list:列出图表存储库

  • remove:删除图表存储库

  • update:从图表存储库更新本地可用图表的信息

  • index:根据包含打包图表的目录生成索引文件

使用上述列表作为指南,可以使用 repo add 子命令来添加图表存储库,如下所示:

$ helm repo add $REPO_NAME $REPO_URL

为了安装其中管理的图表,需要添加图表存储库。本书将详细讨论图表安装。

您可以通过利用 repo list 子命令来确认存储库是否已成功添加:

$ helm repo list
NAME 	      URL                 	 
bitnami         https://charts.bitnami.com

已添加到 Helm 客户端的存储库将显示在此输出中。前面的示例显示,bitnami 存储库已添加,因此它出现在 Helm 客户端已知的存储库列表中。如果添加了其他存储库,它们也将出现在此输出中。

随着时间的推移,更新的图表将被发布并发布到这些存储库中。存储库元数据被本地缓存。因此,Helm 不会自动意识到图表已更新。您可以通过运行 repo update 子命令来指示 Helm 从每个添加的存储库检查更新。一旦执行了这个命令,您就可以从每个存储库安装最新的图表:

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the 'bitnami' chart repository
Update Complete. Happy Helming!

您可能还需要删除先前添加的存储库。这可以通过使用 repo remove 子命令来完成:

$ helm repo remove bitnami
'bitnami' has been removed from your repositories

最后剩下的 repo 子命令形式是 index。这个子命令被存储库和图表维护者用来发布新的或更新的图表。这个任务将在[第五章]中更详细地介绍,构建您的第一个 Helm 图表

接下来,我们将讨论 Helm 插件配置。

添加插件

插件是可以用来为 Helm 提供额外功能的附加功能。大多数用户不需要担心 Helm 的插件和插件管理。Helm 本身就是一个强大的工具,并且在开箱即用时就具备了它承诺的功能。话虽如此,Helm 社区维护了各种不同的插件,可以用来增强 Helm 的功能。这些插件的列表可以在helm.sh/docs/community/related/找到。

Helm 提供了一个plugin子命令来管理插件,其中包含进一步的子命令,如下表所述:

插件可以提供各种不同的生产力增强。

以下是一些上游插件的示例:

  • helm diff: 在部署的发布和建议的 Helm 升级之间执行差异

  • helm secrets: 用于帮助隐藏 Helm 图表中的秘密

  • helm monitor: 用于监视发布并在发生特定事件时执行回滚

  • helm unittest: 用于对 Helm 图表执行单元测试

我们将继续讨论 Helm 配置选项,通过审查可以设置的不同环境变量来改变 Helm 行为的各个方面。

环境变量

Helm 依赖于外部化变量的存在来配置低级选项。Helm 文档列出了用于配置 Helm 的六个主要环境变量:

  • XDG_CACHE_HOME: 设置存储缓存文件的替代位置

  • XDG_CONFIG_HOME: 设置存储 Helm 配置的替代位置

  • XDG_DATA_HOME: 设置存储 Helm 数据的替代位置

  • HELM_DRIVER: 设置后端存储驱动程序

  • HELM_NO_PLUGINS: 禁用插件

  • KUBECONFIG: 设置替代的 Kubernetes 配置文件

Helm 遵循XDG 基本目录规范,该规范旨在提供一种标准化的方式来定义操作系统文件系统上不同文件的位置。根据 XDG 规范,Helm 会根据需要在每个操作系统上自动创建三个不同的默认目录:

Helm 使用缓存路径存储从上游图表存储库下载的图表。安装的图表被缓存到本地机器,以便在下次引用时更快地安装图表。要更新缓存,用户可以运行helm repo update命令,这将使用最新可用的信息刷新存储库元数据,并将图表保存到本地缓存中。

配置路径用于保存通过运行helm repo add命令添加的存储库信息。当安装尚未缓存的图表时,Helm 使用配置路径查找图表存储库的 URL。Helm 使用该 URL 来了解图表所在的位置以便下载。

数据路径用于存储插件。当使用helm plugin install命令安装插件时,插件数据存储在此位置。

关于我们之前详细介绍的其余环境变量,HELM_DRIVER用于确定发布状态在 Kubernetes 中的存储方式。默认值为secret,这也是推荐的值。Secret将在 Kubernetes Secret中对状态进行 Base64 编码。其他选项包括configmap,它将在明文 Kubernetes ConfigMap 中存储状态,以及memory,它将在本地进程的内存中存储状态。本地内存的使用是为了测试目的,不适用于通用或生产环境。

HELM_NO_PLUGINS环境变量用于禁用插件。如果未设置,默认值将保持插件启用为0。要禁用插件,应将变量设置为1

KUBECONFIG环境变量用于设置用于对 Kubernetes 集群进行身份验证的文件。如果未设置,默认值将为~/.kube/config。在大多数情况下,用户不需要修改此值。

Helm 的另一个可配置的组件是选项卡完成,接下来讨论。

选项卡完成

Bash 和 Z shell 用户可以启用选项卡完成以简化 Helm 的使用。选项卡完成允许在按下Tab键时自动完成 Helm 命令,使用户能够更快地执行任务并帮助防止输入错误。

这类似于大多数现代终端仿真器的默认行为。当按下Tab键时,终端会通过观察命令和环境的状态来猜测下一个参数应该是什么。例如,在 Bash shell 中,cd /usr/local/b输入可以通过 Tab 补全为cd /usr/local/bin。类似地,输入helm upgrade hello-可以通过 Tab 补全为helm upgrade hello-world

可以通过运行以下命令启用 Tab 补全:

$ source <(helm completion $SHELL)

$SHELL变量必须是bashzsh。请注意,自动补全只存在于运行前述命令的终端窗口中,因此其他窗口也需要运行此命令才能体验到自动补全功能。

身份验证

Helm 需要能够通过kubeconfig文件对 Kubernetes 集群进行身份验证,以便部署和管理应用程序。它通过引用kubeconfig文件进行身份验证,该文件指定了不同的 Kubernetes 集群以及如何对其进行身份验证。

在阅读本书时使用 Minikube 的人不需要配置身份验证,因为 Minikube 在每次创建新集群时会自动配置kubeconfig文件。然而,没有运行 Minikube 的人可能需要创建kubeconfig文件或者根据使用的 Kubernetes 发行版提供一个。

kubeconfig文件可以通过利用三个不同的kubectl命令来创建:

  • 第一个命令是set-cluster
kubectl config set-cluster

set-cluster命令将在kubeconfig文件中定义一个cluster条目。它确定 Kubernetes 集群的主机名或 IP 地址,以及其证书颁发机构。

  • 下一个命令是set-credentials
kubectl config set-credentials

set-credentials命令将定义用户的名称以及其身份验证方法和详细信息。此命令可以配置用户名和密码对、客户端证书、持有者令牌或身份验证提供程序,以允许用户和管理员指定不同的身份验证方法。

  • 然后,我们有set-context命令:
kubectl config set-context

set-context命令用于将凭据与集群关联起来。一旦建立了凭据和集群之间的关联,用户将能够使用凭据的身份验证方法对指定的集群进行身份验证。

kubectl config view命令可用于查看kubeconfig文件。注意kubeconfigclusterscontextsuser部分与先前描述的命令相对应,如下所示:

$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /home/helm-user/.minikube/ca.crt
    server: https://192.168.99.102:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /home/helm-user/.minikube/client.crt
    client-key: /home/helm-user/.minikube/client.key

一旦存在有效的 kubeconfig 文件,Kubectl 和 Helm 将能够与 Kubernetes 集群进行交互。

在下一节中,我们将讨论授权如何针对 Kubernetes 集群进行处理。

授权/RBAC

身份验证是确认身份的一种方式,授权定义了经过身份验证的用户被允许执行的操作。Kubernetes 使用基于角色的访问控制(RBAC)来执行对 Kubernetes 的授权。RBAC 是一种设计角色和特权的系统,可以分配给特定用户或用户组。用户被允许在 Kubernetes 上执行的操作取决于用户被分配的角色。

Kubernetes 在平台上提供了许多不同的角色。这里列出了三种常见的角色:

  • cluster-admin:允许用户对整个集群中的任何资源执行任何操作

  • edit:允许用户在命名空间或逻辑分组的大多数资源中进行读写

  • view:防止用户修改现有资源,只允许用户在命名空间内读取资源

由于 Helm 使用 kubeconfig 文件中定义的凭据对 Kubernetes 进行身份验证,因此 Helm 被赋予与文件中定义的用户相同级别的访问权限。如果启用了edit访问权限,Helm 可以假定具有足够的权限来安装应用程序,在大多数情况下。对于仅具有查看权限的情况,Helm 将无法安装应用程序,因为这种访问级别是只读的。

运行 Minikube 的用户在集群创建后默认被赋予cluster-admin权限。虽然这在生产环境中不是最佳做法,但对于学习和实验是可以接受的。运行 Minikube 的用户不必担心配置授权以便跟随本书提供的概念和示例。那些使用其他不是 Minikube 的 Kubernetes 集群的用户需要确保他们至少被赋予编辑角色才能够使用 Helm 部署大多数应用程序。可以通过要求管理员运行以下命令来实现这一点:

$ kubectl create clusterrolebinding $USER-edit --clusterrole=edit --user=$USER

第九章中将讨论 RBAC 的最佳实践,Helm 安全考虑,我们将更详细地讨论与安全相关的概念,包括如何适当地应用角色以防止集群中的错误或恶意意图。

总结

使用 Helm 需要准备各种不同的组件。在本章中,您学习了如何安装 Minikube,以提供可用于本书的本地 Kubernetes 集群。您还学习了如何安装 Kubectl,这是与 Kubernetes API 交互的官方工具。最后,您学习了如何安装 Helm 客户端,并探索了 Helm 的各种配置方式,包括添加存储库和插件,修改环境变量,启用选项卡完成,并配置针对 Kubernetes 集群的身份验证和授权。

现在您已经安装了必备的工具,可以开始学习如何使用 Helm 部署您的第一个应用程序。在下一章中,您将从上游图表存储库安装 Helm 图表,并了解生命周期管理和应用程序配置。完成本章后,您将了解 Helm 如何作为 Kubernetes 的软件包管理器。

进一步阅读

查看以下链接,了解 Minikube、Kubectl 和 Helm 的安装选项:

我们介绍了各种不同的 Helm 后安装配置方式。查看以下链接,了解更多有关以下主题的信息:

问题

  1. 您可以列出安装 Helm 客户端的各种方法吗?

  2. Helm 如何对 Kubernetes 集群进行身份验证?

  3. 有什么机制可以为 Helm 客户端提供授权?管理员如何管理这些权限?

  4. helm repo add命令的目的是什么?

  5. Helm 使用的三个 XDG 环境变量是什么?它们有什么作用?

  6. 为什么 Minikube 是学习如何使用 Kubernetes 和 Helm 的好选择?Minikube 自动配置了哪些内容,以使用户能够更快地开始使用?

第三章:安装您的第一个 Helm 图表

在本书的早期,我们将 Helm 称为“Kubernetes 软件包管理器”,并将其与操作系统的软件包管理器进行了比较。软件包管理器允许用户快速轻松地安装各种复杂性的应用程序,并管理应用程序可能具有的任何依赖关系。Helm 以类似的方式工作。

用户只需确定他们想要在 Kubernetes 上部署的应用程序,Helm 会为他们完成其余的工作。Helm 图表——Kubernetes 资源的打包——包含安装应用程序所需的逻辑和组件,允许用户执行安装而无需知道具体所需的资源。用户还可以传递参数,称为值,到 Helm 图表中,以配置应用程序的不同方面,而无需知道正在配置的 Kubernetes 资源的具体细节。您将通过本章来利用 Helm 作为软件包管理器,在 Kubernetes 上部署 WordPress 实例,来探索这些功能。

本章将涵盖以下主要主题:

  • 在 Helm Hub 上找到 WordPress 图表

  • 创建 Kubernetes 环境

  • 附加安装说明

  • 安装 WordPress 图表

  • 访问 WordPress 应用程序

  • 升级 WordPress 发布

  • 回滚 WordPress 发布

  • 卸载 WordPress 发布

技术要求

本章将使用以下软件技术:

  • minikube

  • kubectl

  • helm

我们将假设这些组件已经安装在您的系统上。有关这些工具的更多信息,包括安装和配置,请参阅第二章准备 Kubernetes 和 Helm 环境

了解 WordPress 应用程序

在本章中,您将使用 Helm 在 Kubernetes 上部署WordPress。WordPress 是一个用于创建网站和博客的开源内容管理系统CMS)。有两种不同的变体可用——WordPress.comWordPress.orgWordPress.com是 CMS 的软件即服务SaaS)版本,这意味着 WordPress 应用程序及其组件已经由 WordPress 托管和管理。在这种情况下,用户不需要担心安装自己的 WordPress 实例,因为他们可以简单地访问已经可用的实例。另一方面,WordPress.org是自托管选项。它要求用户部署自己的 WordPress 实例,并需要专业知识来维护。

由于WordPress.com更容易上手,可能听起来更加可取。然而,这个 WordPress 的 SaaS 版本与自托管的WordPress.org相比有很多缺点:

  • 它不提供与WordPress.org一样多的功能。

  • 它不给用户对网站的完全控制。

  • 它要求用户支付高级功能。

  • 它不提供修改网站后端代码的能力。

另一方面,自托管的WordPress.org版本让用户完全控制他们的网站和 WordPress 实例。它提供完整的 WordPress 功能集,从安装插件到修改后端代码。

自托管的 WordPress 实例需要用户部署一些不同的组件。首先,WordPress 需要一个数据库来保存网站和管理数据。 WordPress.org 指出数据库必须是 MySQLMariaDB,它既是网站的位置,也是管理门户。在 Kubernetes 中,部署这些组件意味着创建各种不同的资源:

  • 用于数据库和管理控制台身份验证的secrets

  • 用于外部化数据库配置的 ConfigMap

  • 网络服务

  • 用于数据库存储的PersistentVolumeClaim

  • 用于以有状态的方式部署数据库的 StatefulSet

  • 用于部署前端的Deployment

创建这些 Kubernetes 资源需要 WordPress 和 Kubernetes 方面的专业知识。需要 WordPress 方面的专业知识,因为用户需要了解所需的物理组件以及如何配置它们。需要 Kubernetes 方面的专业知识,因为用户需要知道如何将 WordPress 的要求表达为 Kubernetes 资源。考虑到所需的资源的复杂性和数量,将 WordPress 部署到 Kubernetes 上可能是一项艰巨的任务。

这项任务带来的挑战是 Helm 的一个完美用例。用户可以利用 Helm 作为软件包管理器,在不需要专业知识的情况下,在 Kubernetes 上部署和配置 WordPress,而不是专注于创建和配置我们已描述的每个 Kubernetes 资源。首先,我们将探索一个名为 Helm Hub 的平台,以找到 WordPress Helm 图表。之后,我们将使用 Helm 在 Kubernetes 集群上部署 WordPress,并在此过程中探索基本的 Helm 功能。

查找 WordPress 图表

Helm 图表可以通过发布到图表存储库来供使用。图表存储库是存储和共享打包图表的位置。存储库只是作为 HTTP 服务器托管,并且可以采用各种实现形式,包括 GitHub 页面、Amazon S3 存储桶或简单的 Web 服务器,如 Apache HTTPD。

为了能够使用存储在存储库中的现有图表,Helm 首先需要配置到一个可以使用的存储库。这可以通过使用 helm repo add 来添加存储库来实现。添加存储库涉及的一个挑战是,有许多不同的图表存储库可供使用;可能很难找到适合您用例的特定存储库。为了更容易找到图表存储库,Helm 社区创建了一个名为 Helm Hub 的平台。

Helm Hub 是上游图表存储库的集中位置。由一个名为 Monocular 的社区项目提供支持,Helm Hub 旨在汇总所有已知的公共图表存储库并提供搜索功能。在本章中,我们将使用 Helm Hub 平台来搜索 WordPress Helm 图表。一旦找到合适的图表,我们将添加该图表所属的存储库,以便安装后续使用。

首先,可以通过命令行或 Web 浏览器与 Helm Hub 进行交互。当使用命令行搜索 Helm 图表时,返回的结果提供了 Helm Hub 的 URL,可以用来查找有关图表的其他信息以及如何添加其图表存储库的说明。

让我们按照这个工作流程来添加一个包含 WordPress 图表的图表存储库。

从命令行搜索 WordPress 图表

一般来说,Helm 包含两个不同的搜索命令,以帮助我们找到 Helm 图表:

  • 要在 Helm Hub 或 Monocular 实例中搜索图表,请使用以下命令:
helm search hub
  • 要在图表中搜索关键字,请使用以下命令:
helm search repo

如果之前没有添加存储库,用户应该运行helm search hub命令来查找所有公共图表存储库中可用的 Helm 图表。添加存储库后,用户可以运行helm search repo来搜索这些存储库中的图表。

让我们在 Helm Hub 中搜索任何现有的 WordPress 图表。Helm Hub 中的每个图表都有一组关键字,可以针对其进行搜索。执行以下命令来查找包含wordpress关键字的图表:

$ helm search hub wordpress

运行此命令后,应显示类似以下的输出:

图 3.1–运行 helm search hub wordpress 的输出

图 3.1–运行helm search hub wordpress的输出

该命令返回的每行输出都是来自 Helm Hub 的图表。输出将显示每个图表的 Helm Hub 页面的 URL。它还将显示图表版本,这是 Helm 图表的最新版本,以及应用程序版本,这是图表默认部署的应用程序版本。该命令还将打印每个图表的描述,通常会说明图表部署的应用程序。

正如您可能已经注意到的,返回的一些值被截断了。这是因为helm search hub的默认输出是一个表,导致结果以表格格式返回。默认情况下,宽度超过 50 个字符的列会被截断。可以通过指定--max-col-width=0标志来避免这种截断。

尝试运行以下命令,包括--max-col-width标志,以查看表格格式中未截断的结果:

$ helm search hub wordpress  --max-col-width=0

结果以表格格式显示每个字段的完整内容,包括 URL 和描述。

URL 如下:

描述如下:

  • 用于构建博客和网站的网络发布平台。

  • 用于在 Presslabs Stack 上部署 WordPress 站点的 Helm 图表

  • Presslabs WordPress Operator Helm Chart

或者,用户可以传递--output标志,并指定yamljson输出,这将以完整形式打印搜索结果。

尝试再次运行上一个命令,带上--output yaml标志:

$ helm search hub wordpress --output yaml

结果将以 YAML 格式显示,类似于此处显示的输出:

图 3.2 - helm search hub wordpress--output yaml的输出

在此示例中,我们将选择安装在前面示例输出中返回的第一个图表。要了解有关此图表及其安装方式的更多信息,我们可以转到hub.helm.sh/charts/bitnami/wordpress,这将帮助我们从 Helm Hub 查看图表。

生成的内容将在下一节中探讨。

在浏览器中查看 WordPress 图表

使用helm search hub是在 Helm Hub 上搜索图表的最快方法。但是,它并不提供安装所需的所有细节。换句话说,用户需要知道图表的存储库 URL,以便添加其存储库并安装图表。图表的 Helm Hub 页面可以提供此 URL,以及其他安装细节。

将 WordPress 图表的 URL 粘贴到浏览器窗口后,应显示类似以下内容的页面:

图 3.3 - 来自 Helm Hub 的 WordPress Helm 图表

图 3.3 - 来自 Helm Hub 的 WordPress Helm 图表

Helm Hub 上的 WordPress 图表页面提供了许多详细信息,包括图表的维护者(Bitnami,这是一家提供可部署到不同环境的软件包的公司)以及有关图表的简要介绍(说明此图表将在 Kubernetes 上部署一个 WordPress 实例,并将 Bitnami MariaDB 图表作为依赖项)。该网页还提供了安装详细信息,包括用于配置安装的图表支持的值,以及 Bitnami 的图表存储库 URL。这些安装详细信息使用户能够添加此存储库并安装 WordPress 图表。

在页面的右侧,您应该会看到一个名为添加 bitnami 存储库的部分。该部分包含可用于添加 Bitnami 图表存储库的命令。让我们看看如何使用它:

  1. 在命令行中运行以下命令:
$ helm repo add bitnami https://charts.bitnami.com
  1. 通过运行helm repo list来验证图表是否已添加:
$ helm repo list
NAME  	 URL 
bitnami     https://charts.bitnami.com

现在我们已经添加了存储库,我们可以做更多事情。

  1. 运行以下命令来查看包含bitnami关键字的本地配置存储库中的图表:
$ helm search repo bitnami --output yaml

以下输出显示了返回的结果的缩短列表:

图 3.4 - helm search repo --output yaml 的输出

图 3.4 - helm search repo bitnami --output yaml的输出

helm search hub命令类似,helm search repo命令接受关键字作为参数。使用bitnami作为关键字将返回bitnami存储库下的所有图表,以及可能还包含bitnami关键字的存储库外的图表。

为了确保您现在可以访问 WordPress 图表,请使用wordpress参数运行以下helm search repo命令:

$ helm search repo wordpress

输出将显示您在 Helm Hub 上找到并在浏览器中观察到的 WordPress 图表:

图 3.5 - helm search repo wordpress 的输出

图 3.5 - helm search repo wordpress的输出

斜杠(/)前的NAME字段中的值表示返回的 Helm 图表所在的存储库的名称。截至撰写本文时,bitnami存储库中 WordPress 图表的最新版本是8.1.0。这是将用于安装的版本。通过向search命令传递--versions标志可以观察以前的版本:

$ helm search repo wordpress --versions

然后,您应该看到每个可用 WordPress 图表的每个版本的新行:

图 3.6 - bitnami 存储库上 WordPress 图表的版本列表

图 3.6 - bitnami 存储库上 WordPress 图表的版本列表

现在已经确定了 WordPress 图表,并且已经添加了图表的存储库,我们将探讨如何使用命令行来了解有关图表的更多信息,以准备在下一节中进行安装。

从命令行显示 WordPress 图表信息

您可以在其 Helm Hub 页面上找到有关 Helm 图表的许多重要细节。一旦图表存储库被本地添加,这些信息(以及更多)也可以通过以下列表中描述的四个helm show子命令从命令行中查看:

  • 这个命令显示了图表的元数据(或图表定义):
helm show chart
  • 这个命令显示了图表的README文件:
helm show readme
  • 这个命令显示了图表的值:
helm show values
  • 这个命令显示了图表的定义、README 文件和值:
helm show all

让我们使用这些命令与 Bitnami WordPress 图表。在这些命令中,图表应该被引用为bitnami/wordpress。请注意,我们将传递--version标志来检索关于此图表版本8.1.0的信息。如果省略此标志,将返回图表最新版本的信息。

运行helm show chart命令来检索图表的元数据:

$ helm show chart bitnami/wordpress --version 8.1.0

这个命令的结果将是 WordPress 图表的图表定义。图表定义描述了图表的版本、依赖关系、关键字和维护者等信息:

图 3.7 - wordpress 图表定义

图 3.7 - WordPress 图表定义

运行helm show readme命令来从命令行查看图表的 README 文件:

$ helm show readme bitnami/wordpress --version 8.1.0

这个命令的结果可能看起来很熟悉,因为图表的 README 文件也显示在其 Helm Hub 页面上。利用这个选项从命令行提供了一种快速查看 README 文件的方式,而不必打开浏览器:

图 3.8 - 在命令行中显示的 wordpress 图表的 README 文件

图 3.8 - 在命令行中显示的 WordPress 图表的 README 文件

我们使用helm show values来检查图表的值。值作为用户可以提供的参数,以便定制图表安装。在本章的为配置创建一个 values 文件部分中,当我们安装图表时,我们将稍后运行此命令。

最后,helm show all将前三个命令的所有信息汇总在一起。如果您想一次检查图表的所有细节,请使用此命令。

现在我们已经找到并检查了一个 WordPress 图表,让我们设置一个 Kubernetes 环境,以便稍后安装这个图表。

创建一个 Kubernetes 环境

为了在本章中创建一个 Kubernetes 环境,我们将使用 Minikube。我们在第二章中学习了如何安装 Minikube,准备 Kubernetes 和 Helm 环境

让我们按照以下步骤设置 Kubernetes:

  1. 通过运行以下命令启动您的 Kubernetes 集群:
$ minikube start
  1. 经过短暂的时间,您应该在输出中看到一行类似于以下内容的内容:
 Done! kubectl is now configured to use 'minikube'
  1. 一旦 Minikube 集群启动并运行,为本章的练习创建一个专用命名空间。运行以下命令创建一个名为chapter3的命名空间:
$ kubectl create namespace chapter3

现在集群设置已经完成,让我们开始安装 WordPress 图表到您的 Kubernetes 集群。

安装 WordPress 图表

安装 Helm 图表是一个简单的过程,可以从检查图表的值开始。在下一节中,我们将检查 WordPress 图表上可用的值,并描述如何创建一个允许自定义安装的文件。最后,我们将安装图表并访问 WordPress 应用程序。

为配置创建一个 values 文件

您可以通过提供一个 YAML 格式的values文件来覆盖图表中定义的值。为了正确创建一个values文件,您需要检查图表提供的支持的值。这可以通过运行helm show values命令来完成,如前所述。

运行以下命令检查 WordPress 图表的值:

$ helm show values bitnami/wordpress --version 8.1.0

该命令的结果应该是一个可能值的长列表,其中许多已经设置了默认值:

图 3.9 - 运行helm show values生成的值列表

先前的输出显示了 WordPress 图表数值的开始。这些属性中的许多已经有默认设置,这意味着如果它们没有被覆盖,这些数值将代表图表的配置方式。例如,如果在values文件中没有覆盖image数值,WordPress 图表使用的图像将使用来自 docker.io 注册表的bitnami/wordpress容器图像,标签为5.3.2-debian-9-r0

图表数值中以井号(#)开头的行是注释。注释可以用来解释一个数值或一组数值,也可以用来注释数值以取消设置它们。在先前输出的顶部的global YAML 段中显示了通过注释取消设置数值的示例。除非用户显式设置,否则这些数值默认情况下将被取消设置。

如果我们进一步探索helm show values的输出,我们可以找到与配置 WordPress 博客元数据相关的数值:

图 3.10 - 运行helm show values命令返回的数值

这些数值似乎对配置 WordPress 博客很重要。让我们通过创建一个values文件来覆盖它们。在你的机器上创建一个名为wordpress-values.yaml的新文件。在文件中输入以下内容:

wordpressUsername: helm-user
wordpressPassword: my-pass
wordpressEmail: helm-user@example.com
wordpressFirstName: Helm_is
wordpressLastName: Fun
wordpressBlogName: Learn Helm!

如果你愿意,可以更有创意地使用这些数值。继续从helm show values中列出的数值列表中,还有一个重要的数值应该在开始安装之前添加到values文件中,如下所示:

图 3.11 - 运行 helm show values 后返回的 LoadBalancer 数值

图 3.11 - 运行helm show values后返回的 LoadBalancer 数值

如注释所述,这个数值说明如果我们使用 Minikube,我们需要将默认的LoadBalancer类型更改为NodePort。在 Kubernetes 中,LoadBalancer服务类型用于从公共云提供商中提供负载均衡器。虽然可以通过利用minikube tunnel命令来支持这个数值,但将这个数值设置为NodePort将允许您直接访问本地端口的 WordPress 应用,而不必使用minikube tunnel命令。

将这个数值添加到你的wordpress-values.yaml文件中:

service:
  type: NodePort

一旦这个数值被添加到你的values文件中,你的完整的values文件应该如下所示:

wordpressUsername: helm-user
wordpressPassword: my-pass
wordpressEmail: helm-user@example.com
wordpressFirstName: Helm_is
wordpressLastName: Fun
wordpressBlogName: Learn Helm!
service:
  type: NodePort

现在values文件已经完成,让我们开始安装。

运行安装

我们使用helm install来安装 Helm 图表。标准语法如下:

helm install [NAME] [CHART] [flags]

NAME参数是您想要给 Helm 发布的名称。发布捕获了使用图表安装的 Kubernetes 资源,并跟踪应用程序的生命周期。我们将在本章中探讨发布如何工作。

CHART参数是安装的 Helm 图表的名称。可以通过遵循<repo name>/<chart name>的形式安装存储库中的图表。

helm install中的flags选项允许您进一步自定义安装。flags允许用户定义和覆盖值,指定要处理的命名空间等。可以通过运行helm install --help来查看标志列表。我们也可以将--help传递给其他命令,以查看它们的用法和支持的选项。

现在,对于helm install的使用有了适当的理解,运行以下命令:

$ helm install wordpress bitnami/wordpress --values=wordpress-values.yaml --namespace chapter3 --version 8.1.0

此命令将使用bitnami/wordpress Helm 图表安装一个名为wordpress的新发布。它将使用wordpress-values.yaml文件中定义的值来自定义安装,并且图表将安装在chapter3命名空间中。它还将部署8.1.0版本,如--version标志所定义。没有此标志,Helm 将安装 Helm 图表的最新版本。

如果图表安装成功,您应该看到以下输出:

图 3.12–成功安装 WordPress 图表的输出

图 3.12–成功安装 WordPress 图表的输出

此输出显示有关安装的信息,包括发布的名称、部署时间、安装的命名空间、部署状态(为deployed)和修订号(由于这是发布的初始安装,因此设置为1)。

输出还显示了与安装相关的注释列表。注释用于为用户提供有关其安装的其他信息。在 WordPress 图表的情况下,这些注释提供了有关如何访问和验证 WordPress 应用程序的信息。尽管这些注释直接在安装后出现,但可以随时使用helm get notes命令检索,如下一节所述。

完成第一次 Helm 安装后,让我们检查发布以观察应用的资源和配置。

检查您的发布

检查发布并验证其安装的最简单方法之一是列出给定命名空间中的所有 Helm 发布。为此,Helm 提供了list子命令。

运行以下命令以查看chapter3命名空间中的发布列表:

$ helm list --namespace chapter3

您应该只在此命名空间中看到一个发布,如下所示:

图 3.13 - 列出 Helm 发布的 helm list 命令的输出

图 3.13 - 列出 Helm 发布的helm list命令的输出

list子命令提供以下信息:

  • 发布名称

  • 发布命名空间

  • 发布的最新修订号

  • 最新修订的时间戳

  • 发布状态

  • 图表名称

  • 应用程序版本

请注意,状态、图表名称和应用程序版本从前面的输出中被截断。

虽然list子命令对于提供高级发布信息很有用,但用户可能想要了解特定发布的其他信息。Helm 提供了get子命令来提供有关发布的更多信息。以下列表描述了可用于提供一组详细发布信息的命令:

  • 要获取命名发布的所有钩子,请运行以下命令:
helm get hooks
  • 要获取命名发布的清单,请运行以下命令:
helm get manifest
  • 要获取命名发布的注释,请运行以下命令:
helm get notes
  • 要获取命名发布的值,请运行以下命令:
helm get values
  • 要获取有关命名发布的所有信息,请运行以下命令:
helm get all

前面列表中的第一个命令helm get hooks用于显示给定发布的钩子。在第五章 构建您的第一个 Helm 图表第六章 测试 Helm 图表中,您将了解有关构建和测试 Helm 图表时更详细地探讨钩子。目前,钩子可以被视为 Helm 在应用程序生命周期的某些阶段执行的操作。

运行以下命令以查看包含在此发布中的钩子:

$ helm get hooks wordpress --namespace chapter3

在输出中,您将找到两个带有以下注释的 Kubernetes Pod 清单:

'helm.sh/hook': test-success

此注释表示在执行test子命令期间运行的钩子,我们将在第六章中更详细地探讨,测试 Helm 图表。这些测试钩子为图表开发人员提供了一种确认图表是否按设计运行的机制,并且可以被最终用户安全地忽略。

由于此图表中包含的两个钩子都是用于测试目的,让我们继续进行前面列表中的下一个命令,以继续进行发布检查。

helm get manifest命令可用于获取作为安装的一部分创建的 Kubernetes 资源列表。请按照以下示例运行此命令:

$ helm get manifest wordpress --namespace chapter3

运行此命令后,您将看到以下 Kubernetes 清单:

  • 两个secrets`清单。

  • 两个ConfigMaps清单(第一个用于配置 WordPress 应用程序,而第二个用于测试,由图表开发人员执行,因此可以忽略)。

  • 一个PersistentVolumeClaim清单。

  • 两个services清单。

  • 一个Deployment清单。

  • 一个StatefulSet清单。

从此输出中,您可以观察到在配置 Kubernetes 资源时您的值产生了影响。一个要注意的例子是 WordPress 服务中的type已设置为NodePort

图 3.14 - 将类型设置为 NodePort

图 3.14 - 将type设置为NodePort

您还可以观察我们为 WordPress 用户设置的其他值。这些值在 WordPress 部署中被定义为环境变量,如下所示:

图 3.15 - 值设置为环境变量

图 3.15 - 值设置为环境变量

图表提供的大多数默认值都保持不变。这些默认值已应用于 Kubernetes 资源,并可以通过helm get manifest命令观察到。如果这些值已更改,则 Kubernetes 资源将以不同的方式配置。

让我们继续下一个get命令。helm get notes命令用于显示 Helm 发布的注释。您可能还记得,安装 WordPress 图表时显示了发布说明。这些说明提供了有关访问应用程序的重要信息,可以通过运行以下命令再次显示:

$ helm get notes wordpress --namespace chapter3

helm get values命令对于回忆为给定发布使用的值非常有用。运行以下命令以查看在wordpress发布中提供的值:

$ helm get values wordpress --namespace chapter3

此命令的结果应该看起来很熟悉,因为它们应该与wordpress-values.yaml文件中指定的值匹配:

图 3.16 - wordpress 发布中的用户提供的值

图 3.16 - wordpress 发布中的用户提供的值

虽然回忆用户提供的值很有用,但在某些情况下,可能需要返回发布使用的所有值,包括默认值。这可以通过传递额外的--all标志来实现,如下所示:

$ helm get values wordpress --all --namespace chapter3

对于此图表,输出将会很长。以下输出显示了前几个值:

图 3.17 - wordpress 发布的所有值的子集

图 3.17 - wordpress 发布的所有值的子集

最后,Helm 提供了一个helm get all命令,可以用来聚合各种helm get命令的所有信息:

$ helm get all wordpress --namespace chapter3

除了 Helm 提供的命令之外,kubectl CLI 也可以用于更仔细地检查安装。例如,可以使用kubectl来缩小范围,仅查看一种类型的资源,如部署,而不是获取安装创建的所有 Kubernetes 资源。为了确保返回的资源属于 Helm 发布,可以在部署上定义一个标签,并将其提供给kubectl命令,以表示发布的名称。Helm 图表通常会在它们的 Kubernetes 资源上添加一个app标签。使用kubectl CLI 通过运行以下命令来检索包含此标签的部署:

$ kubectl get all -l app=wordpress --namespace chapter3

您会发现以下部署存在于chapter3命名空间中:

图 3.18 - 章节 3 命名空间中的 wordpress 部署

图 3.18 - chapter3命名空间中的 wordpress 部署

其他安装说明

很快,我们将探索刚刚安装的 WordPress 应用程序。首先,在离开安装主题之前,应该提到几个需要考虑的领域。

-n 标志

可以使用-n标志代替--namespace标志,以减少输入命令时的输入工作量。这对于稍后将在本章中描述的upgraderollback命令也适用。从现在开始,我们将在表示 Helm 应该与之交互的命名空间时使用-n标志。

环境变量 HELM_NAMESPACE

您还可以设置一个环境变量来表示 Helm 应该与之交互的命名空间。

让我们看看如何在各种操作系统上设置这个环境变量:

  • 您可以在 macOS 和 Linux 上设置变量如下:
$ export HELM_NAMESPACE=chapter3
  • Windows 用户可以通过在 PowerShell 中运行以下命令来设置此环境变量:
> $env:HELM_NAMESPACE = 'chapter3'

可以通过运行helm env命令来验证此变量的值:

$ helm env

您应该在结果输出中看到HELM_NAMESPACE变量。默认情况下,该变量设置为default

在本书中,我们不会依赖HELM_NAMESPACE变量,而是会在每个命令旁边传递-n标志,以便更清楚地指出我们打算使用哪个命名空间。提供-n标志也是指定 Helm 命名空间的最佳方式,因为它确保我们正在针对预期的命名空间。

在--set 和--values 之间进行选择

对于installupgraderollback命令,您可以选择两种方式之一来传递值给您的图表:

  • 要从命令行中传递值,请使用以下命令:
--set
  • 要在 YAML 文件或 URL 中指定值,请使用以下命令:
--values

在本书中,我们将把--values标志视为配置图表值的首选方法。原因是这种方式更容易配置多个值。维护一个values文件还将允许我们将这些资产保存在源代码管理SCM)系统中,例如git,这样可以更容易地重现安装过程。请注意,诸如密码之类的敏感值不应存储在源代码控制存储库中。我们将在第九章中涵盖安全性问题,Helm 安全性考虑。目前,重要的是要记住不要将secrets推送到源代码控制存储库中。当需要在图表中提供 secrets 时,建议的方法是明确使用--set标志。

--set标志用于直接从命令行传递值。这是一个可接受的方法,适用于简单的值,以及需要配置的少量值。再次强调,使用--set标志并不是首选方法,因为它限制了使安装更具可重复性的能力。以这种方式配置复杂值也更加困难,例如列表或复杂映射形式的值。还有其他相关的标志,如--set-file--set-string--set-file标志用于传递一个具有key1=val1key2=val2格式的配置值的文件,而--set-string标志用于将提供的所有值设置为字符串的key1=val1key2=val2格式。

解释到此为止,让我们来探索刚刚安装的 WordPress 应用程序。

访问 WordPress 应用程序

WordPress 图表的发布说明提供了四个命令,您可以运行这些命令来访问您的 WordPress 应用程序。运行此处列出的四个命令:

  • 对于 macOS 或 Linux,请运行以下命令:
$ export NODE_PORT=$(kubectl get --namespace chapter3 -o jsonpath="{.spec.ports[0].nodePort}" services wordpress)
$ export NODE_IP=$(kubectl get nodes --namespace chapter3 -o jsonpath="{.items[0].status.addresses[0].address}")
$ echo "WordPress URL: http://$NODE_IP:$NODE_PORT/"
$ echo "WordPress Admin URL: http://$NODE_IP:$NODE_PORT/admin"
  • 对于 Windows PowerShell,请运行以下命令:
> $NODE_PORT = kubectl get --namespace chapter3 -o jsonpath="{.spec.ports[0].nodePort}" services wordpress | Out-String
> $NODE_IP = kubectl get nodes --namespace chapter3 -o jsonpath="{.items[0].status.addresses[0].address}" | Out-String
> echo "WordPress URL: http://$NODE_IP:$NODE_PORT/"
> echo "WordPress Admin URL: http://$NODE_IP:$NODE_PORT/admin"

根据一系列kubectl查询定义了两个环境变量后,结果的echo命令将显示访问 WordPress 的 URL。第一个 URL 是查看主页的 URL,访问者将通过该 URL 访问您的网站。第二个 URL 是到达管理控制台的 URL,网站管理员用于配置和管理站点内容。

将第一个 URL 粘贴到浏览器中,您应该会看到一个与此处显示的内容类似的页面:

图 3.19 – WordPress 博客页面

图 3.19 – WordPress 博客页面

本页的几个部分可能会让你感到熟悉。首先,请注意屏幕左上角的博客标题为学习 Helm!这不仅与本书的标题相似,而且也是您在安装过程中先前提供的wordpressBlogName值。您还可以在页面底部的版权声明中看到这个值,© 2020 学习 Helm!

影响主页定制的另一个值是wordpressUsername。请注意,包括的Hello world!帖子的作者是helm-user。这是提供给wordpressUsername值的用户的名称,如果提供了替代用户名,它将显示不同。

在上一组命令中提供的另一个链接是管理控制台。将第二个echo命令中的链接粘贴到浏览器中,您将看到以下屏幕:

图 3.20:WordPress 管理控制台登录页面

图 3.20:WordPress 管理控制台登录页面

要登录到管理控制台,请输入安装过程中提供的wordpressUsernamewordpressPassword值。这些值可以通过查看本地的wordpress-values.yaml文件来查看。它们也可以通过运行 WordPress 图表注释中指定的以下命令来检索:

$ echo Username: helm-user
$ echo Password: $(kubectl get secret --namespace chapter3 wordpress -o jsonpath='{.data.wordpress-password}' | base64 --decode)

验证后,管理控制台仪表板将显示如下:

图 3.21 – WordPress 管理控制台页面

图 3.21 – WordPress 管理控制台页面

如果您负责管理这个 WordPress 网站,这就是您可以配置您的网站、撰写文章和管理插件的地方。如果您点击右上角的链接,上面写着你好,helm-user,您将被引导到helm-user个人资料页面。从那里,您可以看到安装过程中提供的其他值,如下所示:

图 3.22 – WordPress 个人资料页面

图 3.22 – WordPress 个人资料页面

名字姓氏电子邮件字段分别指代它们对应的wordpressFirstnamewordpressLastnamewordpressEmail Helm 值。

随时继续探索您的 WordPress 实例。完成后,继续下一节,了解如何对 Helm 版本执行升级。

升级 WordPress 版本

升级版本是指修改安装版本的值或升级到图表的新版本的过程。在本节中,我们将通过配置围绕 WordPress 副本和资源需求的附加值来升级 WordPress 版本。

修改 Helm 值

Helm 图表通常会公开值来配置应用程序的实例数量及其相关的资源集。以下截图展示了与此目的相关的helm show values命令的几个部分。

第一个值replicaCount设置起来很简单。由于replica是一个描述部署应用程序所需的 Pod 数量的 Kubernetes 术语,因此可以推断出replicaCount用于指定作为发布的一部分部署的应用程序实例的数量:

图 3.23 - 命令中的 replicaCount

图 3.23 - helm show values命令中的replicaCount

将以下行添加到您的wordpress-values.yaml文件中,将副本数从1增加到2

replicaCount: 2

我们需要定义的第二个值是resources YAML 部分下的一组值:

图 3.24 - 资源部分的值

图 3.24 - 资源部分的值

值可以缩进,就像resources部分一样,以提供逻辑分组。在resources部分下是一个requests部分,用于配置 Kubernetes 将分配给 WordPress 应用程序的memorycpu值。让我们在升级过程中修改这些值,将内存请求减少到256Mi(256 mebibytes),将cpu请求减少到100m(100 millicores)。将这些修改添加到wordpress-values.yaml文件中,如下所示:

resources:
  requests:
    memory: 256Mi
    cpu: 100m

定义了这两个新值后,您的整个wordpress-values.yaml文件将如下所示:

wordpressUsername: helm-user
wordpressPassword: my-pass
wordpressEmail: helm-user@example.com
wordpressFirstName: Helm
wordpressLastName: User
wordpressBlogName: Learn Helm!
service:
  type: NodePort
replicaCount: 2
resources:
  requests:
    memory: 256Mi
    cpu: 100m

一旦values文件使用这些新值进行了更新,您可以运行helm upgrade命令来升级发布,我们将在下一节讨论。

运行升级

helm upgrade命令在基本语法上几乎与helm install相同,如下例所示:

helm upgrade [RELEASE] [CHART] [flags]

虽然helm install希望您为新发布提供一个名称,但helm upgrade希望您提供应该升级的已存在发布的名称。

values文件中定义的值可以使用--values标志提供,与helm install命令相同。运行以下命令,使用一组新值升级 WordPress 发布:

$ helm upgrade wordpress bitnami/wordpress --values wordpress-values.yaml -n chapter3 --version 8.1.0

一旦执行命令,您应该看到类似于helm install的输出,如前面的部分所示:

图 3.25 - 的输出

图 3.25 - helm upgrade的输出

您还应该通过运行以下命令看到wordpress Pods 正在重新启动:

$ kubectl get pods -n chapter3

在 Kubernetes 中,当部署被修改时,会创建新的 Pod。在 Helm 中也可以观察到相同的行为。在升级过程中添加的值引入了 WordPress 部署的配置更改,并且创建了新的 WordPress Pods,因此使用更新后的配置。这些更改可以使用之前安装后使用的相同的helm get manifestkubectl get deployment命令来观察。

在下一节中,我们将进行更多的升级操作,以演示值在升级过程中有时可能会有不同的行为。

在升级过程中重用和重置值

helm upgrade命令包括两个额外的标志,用于操作在helm install命令中不存在的值。

现在让我们来看看这些标志:

  • --reuse-values:在升级时重用上一个发布的值。

  • --reset-values:在升级时,将值重置为图表默认值。

如果在升级时没有使用--set--values标志提供值,则默认添加--reuse-values标志。换句话说,如果没有提供值,升级期间将再次使用先前发布使用的相同值:

  1. 再次运行upgrade命令,而不指定任何值:
$ helm upgrade wordpress bitnami/wordpress -n chapter3 --version 8.1.0
  1. 运行helm get values命令来检查升级中使用的值:
$ helm get values wordpress -n chapter3

请注意,显示的值与先前的升级是相同的:

图 3.26 - 的输出

图 3.26 - helm get values的输出

当在升级过程中通过命令行提供值时,可以观察到不同的行为。如果通过--set--values标志传递值,则所有未提供的图表值都将重置为默认值。

  1. 通过使用--set提供单个值再次进行升级:
$ helm upgrade wordpress bitnami/wordpress --set replicaCount=1 -n chapter3 --version 8.1.0
  1. 升级后,运行helm get values命令:
$ helm get values wordpress -n chapter3

输出将声明,唯一由用户提供的值是replicaCount的值:

图 3.27 - 的输出

图 3.27 - replicaCount的输出

在升级过程中,如果至少提供了一个值,Helm 会自动应用--reset-values标志。这会导致所有值都被设置回它们的默认值,除了使用--set--values标志提供的单个属性。

用户可以手动提供--reset-values--reuse-values标志,明确确定升级过程中值的行为。如果您希望下一次升级在从命令行覆盖值之前将每个值重置为默认值,请使用--reset-values标志。如果您希望在从命令行设置不同值的同时重用先前修订的每个值,请提供--reuse-values标志。为了简化升级过程中值的管理,请尝试将值保存在一个文件中,该文件可用于声明性地为每次升级设置值。

如果您一直在本章中使用提供的每个命令,现在应该有 WordPress 发布的四个修订版本。这第四个修订版本并不完全符合我们希望配置应用程序的方式,因为大多数值都被设置回默认值,只指定了replicaCount值。在下一节中,我们将探讨如何将 WordPress 发布回滚到包含所需值集的稳定版本。

回滚 WordPress 发布

尽管向前推进是首选,但有些情况下,回到应用程序的先前版本更有意义。helm rollback命令存在是为了满足这种情况。让我们将 WordPress 发布回滚到先前的状态。

检查 WordPress 历史

每个 Helm 发布都有一个修订历史。修订用于跟踪特定发布版本中使用的值、Kubernetes 资源和图表版本。当安装、升级或回滚图表时,将创建新的修订。修订数据默认保存在 Kubernetes 秘密中(其他选项是 ConfigMap 或本地内存,由HELM_DRIVER环境变量确定)。这允许不同用户在 Kubernetes 集群上管理和交互您的 Helm 发布,前提是他们具有允许他们查看或修改命名空间中的资源的基于角色的访问控制RBAC)。

可以使用kubectlchapter3命名空间获取秘密来观察修订秘密:

$ kubectl get secrets -n chapter3

这将返回所有的秘密,但您应该在输出中看到这四个:

sh.helm.release.v1.wordpress.v1
Sh.helm.release.v1.wordpress.v2
sh.helm.release.v1.wordpress.v3
sh.helm.release.v1.wordpress.v4

这些秘密中的每一个都对应于发布的修订历史的条目,可以通过运行helm history命令来查看:

$ helm history wordpress -n chapter3

此命令将显示每个修订的表格,类似于以下内容(为了可读性,某些列已被省略):

REVISION  ...  STATUS     ...  DESCRIPTION
1              superseded      Install complete
2              superseded      Upgrade complete
3              superseded      Upgrade complete
4              deployed        Upgrade complete     

在此输出中,每个修订都有一个编号,以及更新时间、状态、图表、升级的应用程序版本和升级的描述。状态为superseded的修订已经升级。状态为deployed的修订是当前部署的修订。其他状态包括pendingpending_upgrade,表示安装或升级当前正在进行中。failed指的是特定修订未能安装或升级,unknown对应于具有未知状态的修订。你不太可能遇到状态为unknown的发布。

先前描述的helm get命令可以通过指定--revision标志针对修订号使用。对于此回滚,让我们确定具有完整所需值集的发布。您可能还记得,当前修订修订 4只包含replicaCount值,但修订 3应该包含所需的值。可以通过使用--revision标志运行helm get values命令来验证这一点:

$ helm get values wordpress --revision 3 -n chapter3

通过检查此修订,可以呈现完整的值列表:

图 3.28 - 检查特定修订的输出

图 3.28 - 检查特定修订的输出

可以针对修订号运行其他helm get命令进行进一步检查。如果需要,还可以针对修订 3执行helm get manifest命令来检查将要恢复的 Kubernetes 资源的状态。

在下一节中,我们将执行回滚。

运行回滚

helm rollback命令具有以下语法:

helm rollback <RELEASE> [REVISION] [flags]

用户提供发布的名称和要回滚到的期望修订号,以将 Helm 发布回滚到以前的时间点。运行以下命令来执行将 WordPress 回滚到修订 3

$ helm rollback wordpress 3 -n chapter3

rollback子命令提供了一个简单的输出,打印以下消息:

Rollback was a success! Happy Helming!

可以通过运行helm history命令在发布历史中观察到此回滚:

$ helm history wordpress -n chapter3

在发布历史中,您会注意到添加了第五个状态为deployed的修订版本,并且描述为回滚到 3。当应用程序回滚时,它会向发布历史中添加一个新的修订版本。这不应与升级混淆。最高的修订版本号仅表示当前部署的发布。请务必检查修订版本的描述,以确定它是由升级还是回滚创建的。

您可以通过再次运行helm get values来获取此发布的值,以确保回滚现在使用所需的值:

$ helm get values wordpress -n chapter3

输出将显示最新稳定发布的值:

图 3.29 - 来自最新稳定发布的值

图 3.29 - 来自最新稳定发布的值

您可能会注意到,在rollback子命令中,我们没有明确设置图表版本或发布的值。这是因为rollback子命令不是设计为接受这些输入;它是设计为将图表回滚到先前的修订版本并利用该修订版本的图表版本和值。请注意,rollback子命令不应成为日常 Helm 实践的一部分,它应该仅用于应急情况,其中应用程序的当前状态不稳定并且必须恢复到先前的稳定点。

如果成功回滚了 WordPress 发布,那么您即将完成本章的练习。最后一步是通过利用uninstall子命令从 Kubernetes 集群中删除 WordPress 应用程序,我们将在下一节中描述。

卸载 WordPress 发布

卸载 Helm 发布意味着删除它管理的 Kubernetes 资源。此外,uninstall命令还会删除发布的历史记录。虽然这通常是我们想要的,但指定--keep-history标志将指示 Helm 保留发布历史记录。

uninstall命令的语法非常简单:

helm uninstall RELEASE_NAME [...] [flags]

通过运行helm uninstall命令卸载 WordPress 发布:

$ helm uninstall wordpress -n chapter3

卸载后,您将看到以下消息:

release 'wordpress' uninstalled

您还会注意到wordpress发布现在不再存在于chapter3命名空间中:

$ helm list -n chapter3

输出将是一个空表。您还可以通过尝试使用kubectl来获取 WordPress 部署来确认该发布不再存在:

$ kubectl get deployments -l app=wordpress -n chapter3
No resources found in chapter3 namespace.

如预期的那样,不再有 WordPress 部署可用。

$ kubectl get pvc -n chapter3

但是,您会注意到在命名空间中仍然有一个PersistentVolumeClaim命令可用:

图 3.30 - 显示 PersistentVolumeClaim 的输出

图 3.30 - 显示PersistentVolumeClaim的输出

这个PersistentVolumeClaim资源没有被删除,因为它是由StatefulSet在后台创建的。在 Kubernetes 中,如果删除了StatefulSet,则由StatefulSet创建的PersistentVolumeClaim资源不会自动删除。在helm uninstall过程中,StatefulSet被删除,但相关的PersistentVolumeClaim没有被删除。这是我们所期望的。可以使用以下命令手动删除PersistentVolumeClaim资源:

$ kubectl delete pvc -l release=wordpress -n chapter3

现在我们已经安装并卸载了 WordPress,让我们清理一下您的 Kubernetes 环境,以便在本书后面的章节中进行练习时有一个干净的设置。

清理您的环境

要清理您的 Kubernetes 环境,可以通过运行以下命令删除本章的命名空间:

$ kubectl delete namespace chapter3

删除chapter3命名空间后,您还可以停止 Minikube 虚拟机:

$ minikube stop

这将关闭虚拟机,但将保留其状态,以便您可以在下一个练习中快速开始工作。

总结

在本章中,您学习了如何安装 Helm 图表并管理其生命周期。我们首先在 Helm Hub 上搜索要安装的 WordPress 图表。在找到图表后,按照其 Helm Hub 页面上的说明添加了包含该图表的存储库。然后,我们开始检查 WordPress 图表,以创建一组覆盖其默认值的数值。这些数值被保存到一个values文件中,然后在安装过程中提供。

图表安装后,我们使用helm upgrade通过提供额外的数值来升级发布。然后我们使用helm rollback进行回滚,将图表恢复到先前的状态。最后,在练习结束时使用helm uninstall删除了 WordPress 发布。

本章教会了您如何作为最终用户和图表消费者利用 Helm。您使用 Helm 作为包管理器将 Kubernetes 应用程序安装到集群中。您还通过执行升级和回滚来管理应用程序的生命周期。了解这个工作流程对于使用 Helm 管理安装是至关重要的。

在下一章中,我们将更详细地探讨 Helm 图表的概念和结构,以开始学习如何创建图表。

进一步阅读

要了解有关本地添加存储库、检查图表以及使用本章中使用的四个生命周期命令(installupgraderollbackuninstall)的更多信息,请访问helm.sh/docs/intro/using_helm/

问题

  1. Helm Hub 是什么?用户如何与其交互以查找图表和图表存储库?

  2. helm gethelm show命令集之间有什么区别?在何时使用其中一个命令集而不是另一个?

  3. helm installhelm upgrade命令中的--set--values标志有什么区别?使用其中一个的好处是什么?

  4. 哪个命令可用于提供发布的修订列表?

  5. 升级发布时默认情况下会发生什么,如果不提供任何值?这种行为与提供升级值时有何不同?

  6. 假设您有五个发布的修订版本。在将发布回滚到“修订版本 3”后,helm history命令会显示什么?

  7. 假设您想查看部署到 Kubernetes 命名空间的所有发布。您应该运行什么命令?

  8. 假设您运行helm repo add来添加一个图表存储库。您可以运行什么命令来列出该存储库下的所有图表?

第二部分:Helm 图表开发

在本节中,您将学习 Helm 图表的结构。您将学习如何从头开始构建 Helm 图表,并学习调试和测试图表的技巧。

本节包括以下章节:

第四章,理解 Helm 图表

第五章,构建您的第一个 Helm 图表

第六章,测试 Helm 图表

第四章:理解 Helm 图表

在上一章中,您学习了如何从最终用户的角度使用 Helm,将其作为一个包管理器来安装应用程序到 Kubernetes。以这种方式使用 Helm 不需要任何 Kubernetes 专业知识或对应用程序的深入理解,因为所有资源和逻辑都包含在 Helm 图表的一部分中。您需要熟悉的唯一概念是图表提供的值,以便自定义安装。

现在我们将从使用 Helm 图表转向理解它们是如何工作和创建的。

为此,我们将涵盖以下主题:

  • 理解 YAML 格式

  • 理解图表模板

  • 理解图表定义

  • 生命周期管理

  • 记录 Helm 图表

技术要求

本节要求在本地机器上安装helm二进制文件。有关此工具的安装和配置在第二章中有介绍,准备 Kubernetes 和 Helm 环境。

理解 YAML 格式

YAML 不是一种标记语言(YAML)是一种用于创建可读性强的配置文件的文件格式。它是配置 Kubernetes 资源最常用的文件格式,也是 Helm 图表中许多文件的格式。

YAML 文件遵循键值格式来声明配置。让我们探索 YAML 键值构造。

定义键值对

这里展示了一个最基本的 YAML 键值对示例:

name: LearnHelm

在前面的示例中,name键被赋予了LearnHelm值。在 YAML 中,键和值由冒号(:)分隔。冒号左边的字符代表键,而冒号右边的字符代表值。

在 YAML 格式中,间距很重要。以下行不构成键值对:

name:LearnHelm

请注意,冒号和LearnHelm字符串之间缺少空格。这将导致解析错误。冒号和值之间必须存在空格。

虽然前面的示例代表了一个简单的键值对,但 YAML 允许用户配置具有嵌套元素或块的更复杂的配对。下面是一个示例:

resources:
  limits:
    cpu: 100m
    memory: 512Mi

前面的示例演示了一个包含两个键值对的资源对象的映射:

键是通过遵循 YAML 块下的缩进来确定的。每个缩进都会在键的名称中添加一个点(.)分隔符。当 YAML 块中不再有缩进时,就已经到达了键的值。按照通常的做法,YAML 中的缩进应该使用两个空格,但用户可以提供任意多的空格,只要在整个文档中保持一致。

重要提示:

YAML 不支持制表符,使用制表符会导致解析错误。

通过理解 YAML 键值对,现在让我们来探索一些常见的值类型。

值类型

YAML 文件中的值可以是不同的类型。最常见的类型是字符串,它是一个文本值。字符串可以通过用引号括起来来声明,但这并不总是必需的。如果一个值包含至少一个字母或特殊字符,那么这个值被认为是一个字符串,无论是否有引号。多行字符串可以通过使用管道(|)符号来设置,如下所示:

configuration: |
  server.port=8443
  logging.file.path=/var/log

值也可以是整数。当一个数值字符没有用引号括起来时,它就是一个整数值。以下的 YAML 声明了一个整数值:

replicas: 1

将其与以下 YAML 进行比较,该 YAML 将副本分配给一个字符串值:

replicas: '1'

布尔值也经常被使用,可以用 true 或 false 来声明:

ingress:
  enable: true

这个 YAML 将ingress.enable设置为true布尔值。其他可接受的布尔值包括yesnoonoffynYN

值也可以设置为更复杂的类型,比如列表。在 YAML 中,列表中的项目由破折号(-)符号标识。

以下演示了一个 YAML 列表:

servicePorts:
  - 8080
  - 8443

这个 YAML 将servicePorts设置为整数列表(比如80808443)。这种语法也可以用来描述对象列表:

deployment:
  env:
    - name: MY_VAR
      value: MY_VALUE
    - name: SERVICE_NAME
      value: MY_SERVICE

在这种情况下,env被设置为一个包含namevalue字段的对象列表。列表在 Kubernetes 和 Helm 配置中经常被使用,理解它们对于充分利用 Helm 是很有价值的。

虽然 YAML 在 Kubernetes 和 Helm 的世界中更常用,因为它易于阅读,但JavaScript 对象表示JSON)格式也可以使用。让我们简要描述一下这种格式。

JSON 格式

YAML 是另一种广泛使用的格式 JSON 的超集。JSON 是一串键值对,类似于 YAML。主要区别在于,YAML 依赖于空格和缩进来正确配置键值对,而 JSON 依赖于大括号和方括号。

以下示例将前面的 YAML 示例转换为 JSON 格式:

{
  'deployment': {
    'env': [
      {
        'name': 'MY_VAR',
        'value': 'MY_VALUE'
      },
      {
        'name': 'SERVICE_NAME',
        'value': 'MY_SERVICE'
      }
    ]
  }

JSON 中的所有键都用引号括起来,并放在冒号之前:

  • 花括号({)以类似于 YAML 中缩进表示块的方式表示块。

  • 方括号(``)以类似于 YAML 中破折号表示列表的方式表示列表。

YAML 和 JSON 格式有许多其他构造,但这个介绍提供了足够的信息来理解它们如何在 Helm 图表中使用。

在下一节中,我们将讨论 Helm 图表文件结构,您可能会注意到其中包含几个 YAML 和 JSON 文件。

Helm 图表结构

正如您可能还记得之前的章节,Helm 图表是 Kubernetes 资源的打包,允许用户将各种复杂性的应用程序部署到 Kubernetes。然而,为了被视为 Helm 图表,必须遵循一定的文件结构:

my-chart/
  # chart files and directories

最佳实践是将顶层目录命名为 Helm 图表的名称。这不是技术要求,但它可以更简单地识别 Helm 图表的名称。对于前面的示例文件结构,Helm 图表的名称很可能是my-chart

在顶层目录下是组成 Helm 图表的文件和目录。以下表格显示了每个可能的文件和目录:

![

在本章中,我们将探讨这些文件,以了解如何创建 Helm 图表。我们将首先通过了解图表模板的工作原理来允许动态生成 Kubernetes 资源。

理解图表模板

Helm 图表的主要目的是创建和管理组成应用程序的 Kubernetes 资源。这是通过图表模板实现的,值作为参数来自定义这些模板。在本节中,我们将讨论 Helm 模板和值的功能。

Helm 图表必须包含一个templates/目录,该目录定义要部署的 Kubernetes 资源(尽管如果图表声明了依赖项,则不严格需要此目录)。templates/目录下的内容是由 Kubernetes 资源组成的 YAML 文件。templates/目录的内容可能类似于以下内容:

templates/
  configmap.yaml
  deployment.yaml
  service.yaml

然后configmap.yaml资源可能如下所示:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}
data:
  configuration.txt: |-
    {{ .Values.configurationData }}

您可能会质疑先前的示例是否是有效的 YAML 语法。这是因为configmap.yaml文件实际上是一个 Helm 模板,它将根据一定的一组值修改此资源的配置,以生成有效的 YAML 资源。开放和关闭的大括号代表了GolangGo)模板的输入文本,这些将在安装或升级过程中被移除。

让我们更多地了解 Go 模板以及它们如何用于生成 Kubernetes 资源文件。

Go 模板

Go是由 Google 于 2009 年开发的一种编程语言。它是 Kubernetes、Helm 和 Kubernetes 和容器社区中许多其他工具使用的编程语言。Go 编程语言的核心组件是模板,可以用来生成不同格式的文件。在 Helm 的情况下,Go 模板用于在 Helm 图表的templates/目录下生成 Kubernetes YAML 资源。

Go 模板控制结构和处理从两个开放的大括号({{)开始,并以两个结束的大括号(}})结束。虽然这些标点符号可能出现在templates/目录下的本地文件中,但它们在安装或升级过程中进行的处理过程中被移除。

我们将在《第五章》[构建您的第一个 Helm 图表]中深入探讨 Go 模板,您将在其中构建自己的 Helm 图表。在本章中,我们将讨论 Go 模板的常见功能,作为这一功能的介绍,然后进行一些实际操作。我们将从 Go 模板提供的一系列功能开始讨论,从参数化开始。

使用值和内置对象对字段进行参数化

Helm 图表在其图表目录中包含一个values.yaml文件。该文件声明了图表的所有默认值,这些值由 Go 模板引用,并由 Helm 处理以动态生成 Kubernetes 资源。

图表的values.yaml文件可以定义如下的值:

## chapterNumber lists the current chapter number
chapterNumber: 4
## chapterName gives a description of the current chapter
chapterName: Understanding Helm Charts

以井号(#)开头的行是注释(在执行过程中被忽略),应提供有关它们描述的值的详细信息,以便用户了解应如何应用它们。注释还可以包括值的名称,以便在搜索值时出现注释。文件中的其他行表示键值对。本章开头描述了 YAML 格式的介绍。

.Values开头的 Go 模板将引用在values.yaml文件中定义的值,或者在安装或升级期间使用--set--values标志传递的值。

以下示例表示模板在处理之前的样子:

env:
  - name: CHAPTER_NUMBER
    value: {{ .Values.chapterNumber }}
  - name: CHAPTER_NAME
    values: {{ .Values.chapterName }}

模板处理后,YAML 资源的片段呈现如下:

env:
  - name: CHAPTER_NUMBER
    value: 4
  - name: CHAPTER_NAME
    values: Understanding Helm Charts

用于引用图表值的.Values构造是一个内置对象,可用于参数化。Helm 文档中可以找到内置对象的完整列表(https://helm.sh/docs/chart_template_guide/builtin_objects/),但最常见的对象在下表中描述:

每个对象前面的点(.)表示对象范围。点后面跟着对象名称将范围限制为该对象。例如,.Values范围只能使图表的值可见;.Release范围只能使Release对象下的字段可见;而.范围表示全局范围,使所有这些对象可见,以及在前面的表中定义的常见对象。

values.schema.json 文件

在谈论值和参数化时,让我们花一点时间讨论values.schema.json文件,这是图表目录中可能包含的文件之一。values.schema.json文件用于在values文件中强制执行特定的模式。此模式可用于在安装或升级期间验证提供的值。

以下片段显示了values.schema.json文件的外观:

{
  '$schema': 'https://json-schema.org/draft-07/schema#',
  'properties': {
    'replicas': {
      'description': 'number of application instances to deploy',
      'minimum': 0
      'type' 'integer'
    },
    . . .
  'title': 'values',
  'type': 'object'
}

有了这个模式文件,replicas值应该设置为0作为最小值。添加到此文件的其他值会对可以提供的值施加额外的限制。这个文件是确保用户只提供图表模板中支持的值的好方法。

虽然 Go 模板允许图表开发人员对 Helm 图表进行参数化,但它们也允许开发人员在 YAML 文件中提供条件逻辑。我们将在下面探讨这个特性。

使用流程控制进行精细化模板处理

虽然参数化允许图表开发人员用特定值替换字段,但 Go 模板还提供了控制模板流程和结构的能力。这可以通过以下关键字(在 Go 中称为actions)来实现:

在图表模板化过程中,有时需要包含或排除某些 Kubernetes 资源或某些资源的某些部分。if…else操作可以用于此目的。以下来自部署模板的片段包括一个条件块:

readinessProbe:
{{- if .Values.probeType.httpGet }}
  httpGet:
    path: /healthz
    port: 8080
    scheme: HTTP
{{- else }}
  tcpSocket:
    port: 8080
{{- end }}
  initialDelaySeconds: 30
  periodSeconds: 10

if块用于有条件地设置readinessProbe段。如果probeType.httpGet值计算为true或非空,则将模板化httpGet readinessProbe。否则,创建的readinessProbe将是tcpSocket readinessProbe类型。大括号中使用的破折号用于指示在处理后应删除空格。在开括号后使用破折号删除括号前的空格,在闭括号前使用破折号删除括号后的空格。

图表开发人员还可以使用with操作来修改值的范围。当引用的值块深度嵌套时,这个操作非常有用。它可以通过减少引用深度嵌套值所需的字符数量来简化模板文件的可读性和可维护性。

以下代码描述了一个values文件,其中包含了深度嵌套的值:

application:
  resources:
    limits:
      cpu: 100m
      memory: 512Mi

没有with操作,这些值将在template文件中被引用,如下所示:

cpu: {{ .Values.application.resources.limits.cpu }}
memory: {{ .Values.application.resources.limits.memory }}

with操作允许开发人员修改这些值的范围,并使用缩短的语法引用它们:

{{- with .Values.application.resources.limits }}
cpu: {{ .cpu }}
memory: {{ .memory }}
{{- end }}

最后,开发人员可以使用range操作执行重复的操作。这个操作允许开发人员循环遍历一个值列表。假设一个图表有以下值:

servicePorts:
  - name: http
    port: 8080
  - name: https
    port: 8443
  - name: jolokia
    port: 8778

上述代码提供了一个servicePorts列表,可以循环遍历,如下例所示:

spec:
  ports:
{{- range .Values.servicePorts }}
  - name: {{ - name }}
  port: {{ .port }}
{{- end }}

withrange操作限制了提供的对象的范围。在range示例中,range作用于.Values.servicePorts对象,将点(.)符号的范围限制在此对象下定义的值。要在range下实施全局范围,其中所有值和内置对象都被引用,开发人员应该使用美元符号($)符号作为前缀引用,如下所示:

{{- range .Values.servicePorts }}
  - name: {{ $.Release.Name }}-{{ .name }}
  port: {{ .port }}
{{- end }}

除了图表的值,开发人员还可以创建变量来帮助渲染资源。我们将在下一节中探讨这一点。

模板变量

尽管它们不像其他模板特性那样常用,但图表开发人员可以在他们的图表模板中创建变量,以提供额外的处理选项。这种方法的常见用途是流程控制,但模板变量也可以用于其他用例。

图表模板中的变量定义如下:

{{ $myvar := 'Hello World!' }}

myvar变量设置为Hello World!字符串。变量也可以分配给对象,比如图表的值:

{{ $myvar := .Values.greeting }}

然后在模板中以以下方式引用设置的变量:

data:
  greeting.txt: |
    {{ $myvar }}

使用变量的最佳情况之一是在范围块中,其中变量被设置为捕获列表迭代的索引和值:

data:
  greetings.txt: |
{{- range $index, $value := .Values.greetings }}
    Greeting {{ $index }}: {{ $value }}
{{- end }}

结果可以如下呈现:

data:
  greetings.txt: |
    Greeting 0: Hello
    Greeting 1: Hola
    Greeting 2: Hallo

变量还可以简化地图迭代的处理,如下所示:

data:
  greetings.txt: |
{{- range $key, $val := .Values.greetings }}
    Greeting in {{ $key }}: {{ $val }}
{{- end }}

可能的结果可能如下所示:

data:
  greetings.txt: |
    Greeting in English: Hello
    Greeting in Spanish: Hola
    Greeting in German: Hallo

最后,变量可以用来引用当前范围之外的值。

考虑以下with块:

{{- with .Values.application.configuration }}
My application is called {{ .Release.Name }}
{{- end }}

这样的模板将无法处理,因为.Release.Name不在.Values.application.configuration的范围内。可以通过在with块上方设置一个变量为.Release.Name来解决这个问题:

{{ $appName := .Release.Name }}
{{- with .Values.application.configuration }}
My application is called {{ $appName }}
{{- end }}

虽然这是解决这个问题的一种可能的方法,但使用美元符号来引用全局范围的方法更受欢迎,因为它需要更少的配置行,并且在图表复杂性增加时更容易阅读。

流程控制和变量是强大的概念,可以动态生成资源。除了流程控制,图表开发人员还可以利用函数和管道来帮助资源的渲染和格式化。

使用函数和管道进行复杂处理

Go 提供了函数和管道的概念,以在模板内对数据进行复杂处理。

Go 模板函数类似于您在其他语言和结构中遇到的其他函数。函数包含旨在消耗某些输入并根据提供的输入提供输出的逻辑。

对于 Go 模板,可以使用以下语法调用函数:

functionName arg1 arg2 . . .

常用的一个 Go 函数是indent函数。此函数用于缩进指定数量的字符的字符串,以确保字符串格式正确,因为 YAML 是一种对空格敏感的标记语言。indent函数接受缩进的空格数和应该缩进的字符串作为输入。

以下模板说明了这一点:

data:
  application-config: |-
{{ indent 4 .Values.config }}

这个例子通过4个空格缩进config值中包含的字符串,以确保该字符串在application-config YAML 键下正确缩进。

Helm 提供的另一个结构是管道。管道是从 UNIX 借鉴的概念,其中一个命令的输出被作为输入传递给另一个命令:

cat file.txt | grep helm

前面的示例显示了 UNIX 管道。管道的左侧(|)是第一个命令,右侧是第二个命令。第一个命令cat file.txt打印名为file.txt的文件的内容,并将其作为输入传递给grep helm命令,该命令过滤第一个命令的输出以获取单词helm

Go 管道的工作方式类似。这可以再次通过indent函数来演示:

data:
  application-config: |-
{{ .Values.config | indent 4 }}

这也将config值缩进 4 个空格。管道最适合用于将多个命令链接在一起。第三个命令可以添加到管道中,称为quote,它在最终模板化产品周围添加引号引号:

data:
  application-config: |-
{{ .Values.config | indent 4 | quote }}

因为这是以管道形式编写的,所以阅读起来很容易和自然。

Helm 图表中可以使用许多不同的 Go 模板函数。这些函数可以在 Go 文档 https://golang.org/pkg/text/template/#hdr-Functions 和 Sprig 模板库 http://masterminds.github.io/sprig/中找到。您在图表开发过程中可能使用的一些常见 Go 模板函数如下:

  • date:格式化日期

  • default:设置默认值

  • fail:失败的模板渲染

  • include:执行 Go 模板并返回结果

  • nindent:类似于 indent,但在缩进之前添加一个新行

  • indent:通过一定数量的空格缩进文本

  • now:显示当前日期/时间

  • quote:将字符串用引号括起来

  • required:要求用户输入

  • splitList:将字符串拆分为字符串列表

  • toYaml:将字符串转换为 YAML 格式

Go 模板语言还包括以下布尔运算符,可以在if操作中使用,以进一步控制生成 YAML 资源:

  • and

  • or

  • not

  • eq(等于的缩写)

  • ne(不等于的缩写)

  • lt(小于的缩写)

  • le(小于或等于的缩写)

  • gt(大于的缩写)

  • ge(大于或等于的缩写)

除了生成 Kubernetes 资源外,Go 模板还可以用于创建可以在具有重复模板的 YAML 资源中重用的函数。这可以通过创建命名模板来实现,下一节将对其进行描述。

使用命名模板实现代码重用

在创建模板文件时,可能会有 Kubernetes 资源中的样板或重复的 YAML 块。

一个例子是资源的标签,可以指定如下:

labels:
  'app.kubernetes.io/instance': {{ .Release.Name }}
  'app.kubernetes.io/managed-by': {{ .Release.Service }}

为了保持一致,这些标签中的每一个都可以添加到 Helm 图表中的每个资源中。如果图表包含许多不同的 Kubernetes 资源,那么在每个文件中包含所需的标签可能会很麻烦,特别是如果需要修改标签或者将来需要向每个资源中添加新标签。

Helm 提供了一种称为命名模板的构造,允许图表开发人员创建可重用的模板,以减少样板文件。命名模板定义在templates/目录下,是以下划线开头并以.tpl文件扩展名结尾的文件。许多图表都使用名为_helpers.tpl的文件来包含命名模板,尽管文件不一定要被称为helpers

要在tpl文件中创建一个命名模板,开发人员可以利用define操作。以下示例创建了一个命名模板,可用于封装资源标签:

{{- define 'mychart.labels' }}
labels:
  'app.kubernetes.io/instance': {{ .Release.Name }}
  'app.kubernetes.io/managed-by': {{ .Release.Service }}
{{- end }}

define操作以模板名称作为参数。在前面的示例中,模板名称称为mychart.labels。命名模板的常见约定是$CHART_NAME.$TEMPLATE_NAME,其中$CHART_NAME是 Helm 图表的名称,$TEMPLATE_NAME是一个简短的描述性名称,描述模板的目的。

mychart.labels名称意味着该模板是本地的mychart Helm 图表,并将为应用到的资源生成标签。

要在 Kubernetes YAML 模板中使用命名模板,可以使用include函数,其用法如下:

include [TEMPLATE_NAME] [SCOPE]

TEMPLATE_NAME参数是应该处理的命名模板的名称。SCOPE参数是应该处理的值和内置对象的范围。大多数情况下,这个参数是一个点(.)来表示当前顶层范围,但如果命名模板引用当前范围之外的值,则应该使用美元符号($)。

以下示例演示了如何使用include函数来处理命名模板:

metadata:
  name: {{ .Release.Name }}
{{- include 'mychart.labels' . | indent 2 }}

这个例子首先将资源的名称设置为发布的名称。然后使用include函数来处理标签,并且通过管道声明每行缩进两个空格。处理完成后,发布中的资源template-demonstration可能如下所示:

metadata:
  name: template-demonstration
  labels:
    'app.kubernetes.io/instance': template-demonstration
    'app.kubernetes.io/managed-by': Helm

Helm 还提供了一个template操作,可以扩展命名模板。这个操作与include具有相同的用法,但有一个主要限制——它不能在管道中用于提供额外的格式化和处理。template操作用于简单地内联显示数据。由于这个限制,图表开发者应该使用include函数而不是template操作,因为include具有与template相同的功能,但还提供了管道处理的额外好处。

在下一节中,我们将学习如何使用命名模板来减少跨多个不同图表的样板文件。

图书馆图表

Helm 图表在Chart.yaml文件中定义了一个type字段,可以设置为applicationlibrary。应用程序图表用于将完整的应用程序部署到 Kubernetes。这是最常见的图表类型,也是默认设置。但是,图表也可以定义为库图表。这种类型的图表不用于部署应用程序,而是用于提供可以在多个不同图表中使用的命名模板。在前一节中定义的labels示例中就是这种用例的一个例子。开发人员可以维护多个不同的图表,这些图表的资源具有相同的标签。开发人员可以声明一个库图表,该图表提供用于生成资源标签的命名模板作为依赖项,而不是在每个图表的_helpers.tpl文件中定义相同的命名模板。

虽然 Helm 最常用于创建传统的 Kubernetes 资源,但它也可以创建Custom ResourcesCRs),我们将在下一节中解释。

模板 CRs

CRs用于创建不属于 Kubernetes API 的资源。您可能希望使用此功能来增强 Kubernetes 提供的功能。CRs 可以使用 Helm 模板创建,例如本机 Kubernetes 资源,但必须首先有一个定义 CR 的Custom Resource Definition (CRD)。如果在创建 CR 之前不存在 CRD,则安装将失败。

Helm 图表可以包括一个crds/文件夹,其中包含必须在安装模板之前呈现的 CRDs。这里显示了一个示例crds/文件夹:

crds/
  my-custom-resource-crd.yaml

文件my-custom-resource-crd.yaml可能包含以下内容:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: my-custom-resources.learnhelm.io
spec:
  group: learnhelm.io
  names:
    kind: MyCustomResource
    listKind: MyCustomResourceList
    plural: MyCustomResources
    singular: MyCustomResource
    scope: Namespaced
    version: v1

模板/目录然后可以包含 MyCustomResource 资源的一个实例。

templates/
  my-custom-resource.yaml

这样的文件结构将确保在templates/目录下定义的 CR 之前安装MyCustomResource CRD。

重要说明:

此功能要求用户是集群管理员,因为创建 CRDs 需要提升的权限。如果您不是集群管理员,最好请管理员事先创建您的 CRDs。如果这样做,crds/文件夹就不需要包含在您的图表中,因为 CRDs 已经存在于集群中。

到目前为止,我们已经详细介绍了 Helm 模板。总之,Helm 模板是您的 Helm 图表的“大脑”,用于生成 Kubernetes 资源。我们将亲自体验编写 Helm 模板,以及本章讨论的其他主题,在第五章中,构建您的第一个 Helm 图表

现在,让我们继续讨论 Helm 图表基础知识,与图表模板同等重要的一个主题是Chart.yaml文件。

了解图表定义

Chart.yaml文件,也称为图表定义,是声明有关 Helm 图表的不同元数据的资源。此文件是必需的,如果它没有包含在图表的文件结构中,您将收到以下错误:

Error: validation: chart.metadata is required

第三章安装您的第一个 Helm 图表中,我们通过运行helm show chart命令来探索Bitnami 的 WordPress 图表的图表定义。再次运行此命令来回忆这个图表定义。我们将假设 Bitnami 图表存储库已经被添加,因为这个任务是在第三章安装您的第一个 Helm 图表中执行的:

$ helm show chart bitnami/wordpress --version 8.1.0

以下列出了 wordpress 图表的图表定义。

图 4.1 - Bitnami 图表存储库的输出

图 4.1 - wordpress 图表的图表定义。

图表定义,或Chart.yaml文件,可以包含许多不同的字段。一些字段是必需的,而大多数其他字段是可选的,只有在必要时才能提供。

现在我们对Chart.yaml文件有了基本的了解,接下来我们将在下一节中探讨文件的必填字段。

必填字段

图表定义必须包含包含关键图表元数据的以下字段:

让我们更详细地探讨这些必填字段:

  • apiVersion字段可以设置为两个不同的值:

v1

v2

  • 如果apiVersion字段设置为v1,这意味着该图表遵循传统的图表结构。这是在 Helm 3 发布之前使用的apiVersion值,在图表结构中支持了额外的requirement.yaml文件,并且在图表定义中不支持type字段。Helm 3 向后兼容apiVersionv1,但新图表应该设置为apiVersionv2,以避免使用废弃的功能。

  • name字段用于定义 Helm 图表的名称。该值应等于包含 Helm 图表文件的顶级目录的名称。Helm 图表的名称将出现在helm search命令的搜索结果中,以及helm list命令,以返回用于发布的图表的名称。该字段的值应该简洁而具有描述性,用一个简短的名称描述图表安装的应用程序,如wordpressredis-cluster。在名称中区分不同单词时,使用短横线分隔单词是常见的约定。有时,名称将被写成一个单词,比如rediscluster

  • version 字段用于确定 Helm chart 的版本。版本必须遵循语义化版本SemVer2.0.0 格式才能成为有效的图表版本。SemVer 根据 Major.Minor.Patch 格式描述版本,其中当引入破坏性更改时,Major 版本应增加,当发布向后兼容的功能时,Minor 版本应增加,当修复错误时,Patch 版本应增加。当增加 Minor 版本时,Patch 版本设置为 0。当增加 Major 版本时,MinorPatch 版本都重置为 0。图表开发人员在增加图表版本时应特别小心,因为它们用于指示何时发布破坏性更改、新功能和错误修复。

虽然这三个字段是 Chart.yaml 文件中唯一需要的字段,但还有许多可选字段可以包含在其中,以向图表添加附加元数据。

让我们来看看其他可能的 Chart.yaml 字段。

可选元数据

除了必填字段外,还有许多可选字段可用于提供有关图表的其他详细信息,如下表所述:

其中一些字段提供简单的元数据,以向用户显示有关 Helm chart 的信息。然而,其他字段用于修改 Helm chart 的行为。其中第一个字段是 type 字段,可以设置为 applicationlibrary。如果设置为 application,则图表部署 Kubernetes 资源。如果设置为 library,则图表通过助手模板的形式为其他图表提供函数。

可以修改 Helm chart 行为的第二个字段是 dependencies 字段,将在下一节中讨论。

管理图表依赖项

图表依赖项用于安装 Helm chart 可能依赖的其他图表资源。一个例子是 wordpress 图表,它将 mariaDB 图表声明为依赖项以保存后端数据。通过使用 mariadb 依赖项,WordPress 图表无需从头开始定义其资源。

通过填充 dependencies 字段在 Chart.yaml 文件中声明依赖项。以下是 wordpress 图表定义中的相关片段:

图 4.2 – wordpress 图表定义的片段

图 4.2 - 在 wordpress Helm 图表中声明的 mariadb 依赖项。

虽然此示例显示了单个依赖项mariadb,但dependencies块可以定义多个依赖项的列表。

dependencies块包含许多不同的字段,可以应用于修改图表依赖项管理的行为。这些字段在下表中定义:

dependencies块下的最小必需字段是namerepositoryversion字段。如前面的wordpress依赖片段所示,依赖的名称是 mariadb,存储库可以在kubernetes-charts.storage.googleapis.com找到。这将在提供的存储库中搜索一个 Helm 图表,其Chart.yaml文件中的name字段为mariadbdependencies块的version字段指定应包含的图表版本。这可以固定到特定版本,如7.0.0,也可以指定通配符版本。前面示例中列出的依赖项提供了一个通配符版本7.x.x,它指示 Helm 下载与通配符匹配的图表的最新版本。

现在,了解了所需的依赖项字段,让我们学习如何下载声明的依赖项。

下载依赖项

可以使用下表中列出的helm dependency子命令下载依赖项:

要首次下载依赖项,可以运行helm dependency update命令,将每个依赖项下载到给定 Helm 图表的charts/目录中:

$ helm dependency update $CHART_PATH

helm dependency update命令从存储库中下载以.tgz文件扩展名的GZip存档形式的依赖项。此命令还生成一个名为Chart.lock的文件。Chart.lock文件类似于Chart.yaml文件。但是,Chart.yaml文件包含图表依赖项的期望状态,而Chart.lock文件定义了应用的依赖项的实际状态。

可以在这里看到一个Chart.lock文件的示例:

图 4.3 - 一个 Chart.lock 文件

图 4.3 - 一个Chart.lock文件

将其与一个简单的相应的Chart.yaml文件进行比较:

图 4.4 - 相应的 Chart.yaml 文件

图 4.4 - 相应的Chart.yaml文件

Chart.yaml文件中,您可以看到指定的mariadb依赖项的版本是7.x.x,但是Chart.lock文件中的版本是7.3.1。这是因为Chart.yaml文件指示 Helm 下载7.x.x版本的最新版本,实际下载的版本是7.3.1

有了Chart.lock文件,Helm 能够重新下载最初下载的确切依赖项,以防charts/目录被删除或需要重建。这可以通过针对图表运行helm dependency build命令来实现:

$ helm dependency build $CHART_PATH

因为你可以使用helm dependency build命令下载依赖项,所以可以省略charts/目录,以减少存储库的大小。

随着时间的推移,7.x.x版本的新版本将可用。可以再次运行helm dependency update命令来协调此依赖项,这意味着将下载最新可用版本,并且Chart.lock文件将重新生成。如果将来想要从8.x.x版本下载或者想要将依赖项固定到特定版本,比如7.0.0,可以在Chart.yaml文件中设置并运行helm dependency update

helm dependency list命令可用于查看保存在本地计算机上的 Helm 图表的已下载依赖项:

$ helm dependency list $CHART_NAME

您将看到类似以下的输出:

图 4.5 - CHART_NAME 命令的输出

图 4.5 - "helm dependency list"输出

STATUS列确定了依赖项是否已成功下载到charts/目录。如果状态显示为ok,则已下载依赖项。如果状态显示为missing,则尚未下载依赖项。

默认情况下,Chart.yaml文件中声明的每个依赖项都将被下载,但可以通过提供dependencies块的conditiontags字段来修改,我们将在下一节讨论。

条件依赖

conditionflags字段可以在安装或升级过程中有条件地包含依赖项。考虑Chart.yaml文件中的一个示例dependencies块:

dependencies:
  - name: dependency1
    repository: https://example.com
    version: 1.x.x
    condition: dependency1.enabled
    tags:
      - monitoring
  - name: dependency2
    repository: https://example.com
    version: 2.x.x
    condition: dependency2.enabled
    tags:
      - monitoring

请注意conditiontags字段的包含。condition字段列出了用户应提供的值,或者在图表的values.yaml文件中设置的值。如果评估为truecondition字段将导致图表作为依赖项被包括进来。如果为false,则不会包括依赖项。可以通过用逗号分隔每个条件来定义多个条件,如下所示:

condition: dependency1.enabled, global.dependency1.enabled

设置条件的最佳实践是遵循chartname.enabled值格式,其中每个依赖项根据依赖项的图表名称设置唯一的条件。这允许用户通过遵循直观的值模式来启用或禁用单个图表。如果条件值未包含在图表的values.yaml文件中,或者用户未提供该字段,则将忽略此字段。

condition字段用于启用或禁用单个依赖项,tags字段用于启用或禁用依赖项组。在前面的dependencies块中,两个依赖项都列出了一个名为monitoring的标签。这意味着如果启用了monitoring标签,两个依赖项都会被包括进来。如果monitoring标签设置为false,则依赖项将被省略。通过在父图表的values.yaml文件中的tags YAML 对象下设置它们来启用或禁用标签,如下所示:

tags:
  monitoring: true

依赖项可以通过遵循列表的 YAML 语法在Chart.yaml文件中定义多个标签。只需要一个标签被评估为true,依赖项就会被包括进来。

重要提示:

如果忽略了依赖项的所有标签,依赖项将默认包括进来。

在本节中,我们讨论了如何有条件地声明依赖关系。接下来,我们将讨论如何覆盖和引用依赖项的值。

覆盖和引用子图表的值

默认情况下,属于依赖图表(也称为子图表)的值可以通过将它们包装在名称设置为与子图表相同的映射中来被覆盖或引用。想象一个名为my-dep的子图表,支持以下值:

replicas: 1
servicePorts:
  - 8080
  - 8443

当此图表作为依赖项安装时,可以通过在父图表的values.yaml文件中设置my-dep YAML 对象来覆盖这些值,如下所示:

my-dep:
  replicas: 3
  servicePorts:
    - 8080
    - 8443
    - 8778

前面的例子覆盖了my-dep中定义的replicasservicePorts的值,将replicas设置为3,并将8778添加到servicePorts中。这些值可以通过点表示法在父图的模板中引用,例如my-dep.replicas。除了覆盖和引用值之外,您还可以通过定义import-values字段直接导入依赖值,下一节将对此进行解释。

使用import-values导入值

Chart.yaml文件的dependencies块支持一个import-values字段,可用于导入子图的默认值。该字段有两种工作方式。第一种方式是提供要从子图导入的键列表。为了使其工作,子图必须在exports块下声明值,如下所示:

exports:
  image:
    registry: 'my-registry.io'
    name: learnhelm/my-image
    tag: latest

然后父图可以在Chart.yaml文件中定义import-values字段:

dependencies:
  - name: mariadb
    repository: https://charts.bitnami.com
    version: 7.x.x
    import-values:
      - image

这允许在父图中如下引用子图中exports.image下的默认值:

registry: 'my-registry.io'
name: learnhelm/my-image
tag: latest

请注意,这已经删除了image映射,并且只留下了其下面的键值对。如果您不希望发生这种情况,import-values字段可以通过遵循所谓的child-parent格式保留image映射。这允许图表开发人员指定应从子图导入的值,并提供它们在父图中应被称为的名称。child-parent格式允许在不需要子图中的exports块中的值的情况下完成此操作。以下dependencies块演示了这种情况的示例:

dependencies:
  - name: mariadb
    repository: https://charts.bitnami.com
    version: 7.x.x
    import-values:
      - child: image
        parent: image

此示例将子图中image块下的每个值导入到父图中的image块下。

重要提示:

使用import-values字段导入的值不能在父图中被覆盖。如果您需要覆盖子图中的值,您不应该使用import-values字段,而应该通过在每个值的前缀中加上子图的名称来覆盖所需的值。

在本节中,我们介绍了如何在Chart.yaml文件中管理依赖关系。现在,让我们了解一下如何在 Helm 图中定义生命周期管理钩子。

生命周期管理

Helm 图表及其相关发布的主要优势之一是能够在 Kubernetes 上管理复杂的应用程序。发布在其生命周期中经历多个阶段。为了提供关于发布生命周期的额外管理能力,Helm 提供了一个hooks机制,以便可以在发布周期的不同时间点执行操作。在本节中,我们将探讨发布生命周期的不同阶段,并介绍如何使用hooks来提供与发布以及整个 Kubernetes 环境的交互能力。

第三章中,安装您的第一个 Helm 图表,我们遇到了涵盖 Helm 发布整个生命周期的几个阶段,包括安装、升级、删除和回滚。鉴于 Helm 图表可能很复杂,因为它们管理将部署到 Kubernetes 的一个或多个应用程序,通常需要执行除了部署资源之外的其他操作。这些操作可能包括以下内容:

  • 完成应用程序所需的先决条件,例如管理证书和密钥

  • 作为图表升级的一部分进行数据库管理,以执行备份或恢复

  • 在删除图表之前清理资产

潜在选项列表可能很长,首先了解 Helm 挂钩的基础知识以及它们何时可以执行是很重要的,我们将在下一节中描述。

Helm 挂钩的基础知识

挂钩在发布的生命周期中的指定时间点执行一次操作。与 Helm 中的大多数功能一样,挂钩被实现为另一个 Kubernetes 资源,更具体地说是在一个容器中。虽然 Kubernetes 中的大多数工作负载都设计为长时间运行的进程,比如提供 API 请求的应用程序,但工作负载也可以由一个单独的任务或一组任务组成,使用脚本执行,一旦完成就指示成功或失败。

在 Kubernetes 环境中通常用于创建短暂任务的两个选项是使用裸podjob。裸 pod 是一个运行直到完成然后终止的 pod,但如果底层节点失败,它将不会被重新调度。因此,可能更倾向于将生命周期钩子作为作业运行,如果节点失败或不可用,则重新调度钩子。

由于钩子只是被定义为 Kubernetes 资源,它们也被放置在templates/文件夹中,并用 helm.sh/hook 注释进行标注。这个注释的指定确保它们不会与标准处理过程中应用于 Kubernetes 环境的其他资源一起渲染。相反,它们根据 helm.sh/hook 注释中指定的值进行渲染和应用,该值确定了它应该在 Helm 发布生命周期的 Kubernetes 中何时执行。

以下是如何将钩子定义为作业的示例:

apiVersion: batch/v1
kind: Job
metadata:
  name: helm-auditing
  annotations:
    'helm.sh/hook': pre-install,post-install
spec:
  template:
    metadata:
      name: helm-auditing
    spec:
      restartPolicy: Never
      containers:
      - name: helm-auditing
        command: ["/bin/sh", "-c", "echo Hook Executed at $(date)"]
        image: alpine

这个微不足道的例子在休眠 10 秒之前打印出容器中的当前日期和时间。Helm 在安装图表之前和之后执行这个钩子,如'helm.sh/hook'注释的值所示。这种类型的钩子的一个用例是连接到一个审计系统,跟踪应用程序在 Kubernetes 环境中的安装。类似的钩子可以在安装完成后添加,以跟踪完成图表安装过程所花费的总时间。

现在我们已经解释了 Helm 钩子的基础知识,让我们讨论如何在 Helm 图表中定义钩子。

钩子执行

正如你在前一节的job钩子中看到的,helm.sh/hook注释的值是pre-installpre-install是 Helm 图表生命周期中可以执行钩子的时间点之一。

以下表格表示了helm.sh/hook注释的可用选项,指示钩子的执行时间。每个钩子的描述都引用了官方 Helm 文档,可以在 https://helm.sh/docs/topics/charts_hooks/#the-available-hooks 找到。

helm.sh/hook注释可以包含多个值,表示在图表发布周期内的不同时间点执行相同的资源。例如,为了在图表安装之前和之后执行钩子,可以在 pod 或作业上定义以下注释:

annotations:
  'helm.sh/hook': pre-install,post-install

了解钩子何时以及如何执行是有用的,以确定需要选择的图表生命周期中的期望阶段。如前面的示例所述,当钩子被指定在执行helm install命令的pre-installpost-install部分时,将发生以下操作:

  1. 用户安装 Helm 图表(例如通过运行helm install bitnami/wordpress --version 8.1.0)。

  2. 调用 Helm API。

  3. 在 Kubernetes 环境中加载crds/文件夹中的 CRD。

  4. 执行图表模板的验证并呈现资源。

  5. 预安装钩子按权重排序,然后呈现并加载到 Kubernetes 中。

  6. Helm 等待直到钩子准备就绪。

  7. 模板资源被呈现并应用于 Kubernetes 环境。

  8. 执行post-install钩子。

  9. Helm 等待直到post-install钩子完成。

  10. 返回helm install命令的结果。

了解 Helm 钩子执行的基础知识后,让我们来讨论一些关于 Helm 钩子的更高级主题。

高级钩子概念

虽然将标准 Helm 模板资源转换为钩子所需的工作量很小,但还有其他选项可帮助执行图表和删除资源。

在 Helm 图表的生命周期中执行的钩子数量没有限制,可能存在多个钩子为同一生命周期阶段配置的情况。当出现这种情况时,默认情况下,钩子按名称按字母顺序排序。但是,您可以通过使用helm.sh/weight注释指定每个钩子的权重来定义顺序。权重按升序排序,但如果多个钩子包含相同的权重值,则使用默认逻辑按名称按字母顺序排序。

虽然钩子为生命周期管理提供了有用的机制,但应该记住,与常规模板资源不同,钩子在调用helm uninstall命令时不会随图表的其余部分一起删除,因为它们不受 Helm 跟踪或管理。相反,可以采用一些策略来在发布的生命周期中删除钩子,例如配置删除策略和设置作业的 TTL。

首先,可以在与钩子相关的 pod 或 job 上指定helm.sh/hook-delete-policy注释。此注释确定 Helm 应何时对 Kubernetes 中的资源进行删除。有以下选项(描述参考 Helm 文档,可在 https://helm.sh/docs/topics/charts_hooks/#hook-deletion-policies 找到):

此外,Kubernetes 提供了定义生存时间TTL)机制的选项,以限制资源在完成后保留的时间量,使用作业的ttlSecondsAfterFinished属性,如下所示:

apiVersion: batch/v1
kind: Job
metadata:
  name: ttl-job
  annotations:
    'helm.sh/hook': post-install
spec:
  ttlSecondsAfterFinished: 60

在此示例中,资源在完成或失败后的 60 秒内被删除。

发布生命周期的最后阶段是删除,尽管在调用helm uninstall命令时会删除标准图表模板,但您可能希望保留某些资源,以便 Helm 不对其采取行动。一个常见的用例是,在发布生命周期的开始时通过PersistentVolumeClaim命令创建新的持久卷,但在结束时不应与其他资源一起删除,以便保留卷的数据。通过使用helm.sh/resource-policy注释启用此选项,如下所示:

'helm.sh/resource-policy': keep

Helm 将不再在执行helm uninstall命令期间考虑删除此资源。需要注意的是,当资源不再受管理时,一旦其余资源被删除,它就变成了孤立的。如果使用helm install命令,可能会导致资源命名冲突,因为之前未删除的现有资源。可以使用kubectl delete命令手动删除孤立的资源。

本节讨论了如何编写钩子和自动化来管理图表的生命周期。在下一节中,我们将讨论如何适当地记录 Helm 图表,以确保其用户拥有流畅的体验。

记录 Helm 图表

与用户交互的任何其他软件一样,Helm 图表应该有适当的文档,以便用户知道如何与其交互。Helm 图表结构支持用于记录用法的README.md文件,用于覆盖用法和分发权利的LICENSE文件,以及用于在图表安装期间生成用法说明的templates/NOTES.txt文件。

README.md 文件

README是软件开发中常用的文件,用于描述产品的安装、使用和其他细节。 Helm 图表的 README 文件通常包含以下细节:

  • 先决条件:先决条件的一个常见示例是在安装图表之前在 Kubernetes 集群中创建一个secret或一组 secrets,以便挂载到 Kubernetes 部署中。 用户可以通过参考 README 文件来了解这一要求。

  • :图表通常包含许多不同的值,每个值都应在README文件中以表格形式描述。 表格应指定值的名称,其描述或功能以及其默认值。 您还可能发现有帮助的是指示该值是否需要在安装或升级期间提供。

  • 特定于应用程序的信息:一旦使用 Helm 图表安装了应用程序,您可能需要有关应用程序本身的其他信息,例如如何访问它或应用程序的功能。 这些细节可以在README文件中提供。

Helm READMEs 使用Markdown格式语言编写。 Markdown 通常用于 GitHub 项目和开源软件,并且是一种可以以优雅格式显示的文本的简便编码方式。 可以在Markdown Guide网站上进一步探索 Markdown,位于 https://www.markdownguide.org/。

许可证文件

除了README文件中包含的技术说明之外,图表维护者可能发现有必要包含一个许可证,以指示用户在图表使用和分发方面的权限。 这些细节可以在图表目录下的名为LICENSE的文件中组成。

LICENSE文件是一个包含软件许可证的纯文本文件。 许可证可以是自定义编写的,也可以是常用于开源软件的许可证的副本,例如 Apache 许可证 2.0 或 MIT 许可证。 理解许可证之间的区别以及使用和分发软件的合法性超出了本书的范围,但您可以开始在选择许可证网站(https://choosealicense.com/)上探索这些细节,该网站将帮助您选择适合您的 Helm 图表的合适许可证。

templates/NOTES.txt 文件

README.md文件类似,templates/NOTES.txt文件用于提供使用说明,一旦使用 Helm 安装应用程序。不同之处在于,README.md文件是静态的,而NOTES.txt文件可以使用 Go 模板动态生成。

假设 Helm 图表在其values.yaml文件中配置了以下值:

## serviceType can be set to NodePort or LoadBalancer
serviceType: NodePort

根据设置的服务类型,访问应用程序的说明将有所不同。如果服务是NodePort服务,则可以使用在每个 Kubernetes 节点上设置的特定端口号来访问。如果服务设置为LoadBalancer,则将使用在创建服务时自动配置的负载均衡器的 URL 来访问应用程序。根据所使用的服务类型访问应用程序的方式可能对经验不足的 Kubernetes 用户来说有些困难,因此该图表的维护者应该在templates/目录下提供一个NOTES.txt文件,其中提供了关于如何访问应用程序的说明。

以下示例说明了如何使用templates/NOTES.txt文件来实现此目的:

Follow these instructions to access your application.
{{- if eq .Values.serviceType 'NodePort' }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath='{.spec.ports[0].nodePort}' services {{.Release.Name }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath='{.items[0].status.addresses[0].address}')
echo "URL: http://$NODE_IP:$NODE_PORT"
{{- else }}
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Name }} wordpress --template '{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}')
echo "URL: http://$SERVICE_IP"
{{- end }}

此文件将在应用程序的安装、升级和回滚阶段生成和显示,并且可以通过运行helm get notes命令来调用。通过提供此文件,用户将更好地了解如何使用应用程序。

到目前为止,在本章中,我们已经描述了 Helm 图表的大部分组成部分,除了实际的打包,这样可以使图表易于分发。这个概念将在下一节中描述。

打包 Helm 图表

虽然 Helm 图表遵循通用的文件结构,但它们应该打包以便于分发。图表以tgz存档的形式打包。虽然可以使用tar bash 实用程序或存档管理器手动创建这些存档,但 Helm 提供了helm package命令来简化此任务。helm package命令的语法如下所示:

$ helm package [CHART_NAME] [...] [flags]

helm package命令针对本地图表目录运行。如果此命令成功,它将生成一个具有以下文件格式的tgz存档:

$CHART_NAME-$CHART_VERSION.tgz

存档然后可以通过推送到图表存储库来分发,这是在第五章中进一步探讨的任务,构建您的第一个 Helm 图表

helm package命令包括图表目录下的每个文件。虽然这通常是首选行为,但如果目录中包含对 Helm 不必要的文件,则可能并非总是如此。一个常见的例子是.git/目录,它存在于由Git SCM管理的项目中。如果将此文件打包到图表的tgz存档中,它将毫无意义,只会增加存档的大小。Helm 支持一个名为.helmignore的文件,可用于从 Helm 存档中省略某些文件和文件夹。以下是一个示例.helmignore文件:

# Ignore git directories and files
.git/
.gitignore

上述文件指示,如果.git/目录或.gitignore文件出现在图表目录中,它们将被helm package命令忽略,这意味着它们不会出现在生成的tgz存档中。在此文件中以井号符号(#)开头的行用作注释。如果您的图表目录包含对图表的整体功能不必要的文件和文件夹,请确保在 Helm 图表中包含一个.helmignore文件。

摘要

Helm 图表是一组文件,主要以 YAML 格式编写,遵循特定的文件结构。Chart.yaml文件用于设置图表元数据并声明依赖关系。templates/目录用于包含 Kubernetes YAML 资源,这些资源是 Go 模板化的,允许它们动态生成。在templates/目录下定义的 Kubernetes 资源还可以包含某些钩子,用于配置应用程序生命周期中的各个阶段。为了向用户提供文档,图表可以包含README.mdtemplates/NOTES.txt文件,还可以包含LICENSE文件以声明图表的使用和分发权利。最后,图表可以包含一个.helmignore文件,用于从最终打包的产品中省略声明的文件。

在本章中,您了解了 Helm 图表的结构以及如何配置关键的图表组件。有了本章的知识,您现在了解了如何从头开始编写您的第一个 Helm 图表的基本概念,我们将在第五章中进行,构建您的第一个 Helm 图表

进一步阅读

要了解有关创建 Helm 图表的基础知识,请参阅 Helm 文档中的 Chart 模板指南页面,网址为 https://helm.sh/docs/chart_template_guide/。https://helm.sh/docs/topics/charts/中的“图表”部分还描述了本章中讨论的许多主题,包括图表文件结构、依赖关系和Chart.yaml文件。

问题

  1. 在 Kubernetes 和 Helm 中最常用的文件格式是什么?

  2. Chart.yaml文件中的三个必填字段是什么?

  3. 如何引用或覆盖图表依赖项的值?

  4. 想象一下,您想要对使用 Helm 部署的数据库进行数据快照。在将数据库升级到更新版本之前,您可以采取什么措施来确保在升级数据库之前进行数据“快照”?

  5. 作为图表开发人员,您可以创建哪些文件来为最终用户提供文档并简化图表安装过程?

  6. 您可以利用哪种 Helm 模板构造来生成重复的 YAML 部分?

  7. Chart.yaml文件与Chart.lock文件有什么不同?

  8. 什么是将资源定义为钩子的注释的名称?

  9. 图表模板中的函数和管道的目的是什么?可以使用哪些常见函数?

第五章:构建您的第一个 Helm 图表

在上一章中,您了解了组成 Helm 图表的各个方面。现在,是时候将这些知识付诸实践,构建一个 Helm 图表了。学会构建 Helm 图表将使您能够以简单的方式打包复杂的 Kubernetes 应用程序。

在本章中,您将学习如何构建一个 Helm 图表,用于部署guestbook应用程序,这是 Kubernetes 社区中广泛使用的快速入门应用程序。通过遵循 Kubernetes 和 Helm 图表开发的最佳实践,构建此图表将提供一个编写良好且易于维护的自动化部分。在开发此图表的过程中,您将学习许多不同的技能,可以应用于构建自己的 Helm 图表。在本章结束时,您将学习如何打包您的 Helm 图表并将其部署到图表存储库,以便最终用户可以轻松访问。

本章涵盖的主要主题如下:

  • 了解 Guestbook 应用程序

  • 创建 Guestbook Helm 图表

  • 改进 Guestbook Helm 图表

  • 将 Guestbook 图表发布到图表存储库

技术要求

本章需要以下技术:

  • minikube

  • kubectl

  • helm

除了前面提到的工具之外,您还会发现本书的 GitHub 存储库位于github.com/PacktPublishing/-Learn-Helm。我们将引用本章中包含的helm-charts/charts/guestbook文件夹。

建议您拥有自己的 GitHub 帐户,以便完成本章的最后一节创建图表存储库。有关如何创建您自己的帐户的说明将在该部分提供。

了解 Guestbook 应用程序

在本章中,您将创建一个 Helm 图表,用于部署 Kubernetes 社区提供的 Guestbook 教程应用程序。该应用程序在 Kubernetes 文档的以下页面中介绍:kubernetes.io/docs/tutorials/stateless-application/guestbook/

Guestbook 应用程序是一个简单的PHP:超文本预处理器PHP)前端,旨在将消息持久保存到 Redis 后端。前端包括对话框和提交按钮,如下截图所示:

图 5.1:Guestbook PHP 前端

图 5.1:Guestbook PHP 前端

用户可以按照以下步骤与该应用程序进行交互:

  1. 消息对话框中输入一条消息。

  2. 单击提交按钮。

  3. 当单击提交按钮时,消息将被保存到 Redis 数据库中。

Redis 是一个内存中的键值数据存储,本章中将被用于数据复制的集群。该集群将包括一个主节点,Guestbook 前端将向其写入数据。一旦写入,主节点将在多个从节点之间复制数据,Guestbook 前端将从中读取。

以下图描述了 Guestbook 前端与 Redis 后端的交互方式:

图 5.2:Guestbook 前端和 Redis 交互

图 5.2:Guestbook 前端和 Redis 交互

在对 Guestbook 前端和 Redis 后端的交互有了基本了解之后,让我们设置一个 Kubernetes 环境来开始开发 Helm 图表。在开始之前,让我们首先启动 minikube 并为本章创建一个专用的命名空间。

设置环境

为了看到您的图表运行情况,您需要按照以下步骤创建您的 minikube 环境:

  1. 通过运行minikube start命令来启动 minikube,如下所示:
$ minikube start
  1. 创建一个名为chapter5的新命名空间,如下所示:
$ kubectl create namespace chapter5

在部署 Guestbook 图表时,我们将使用这个命名空间。现在环境已经准备好,让我们开始编写图表。

创建 Guestbook Helm 图表

在本节中,我们将创建一个 Helm 图表来部署 Guestbook 应用程序。最终的图表已经发布在 Packt 存储库的helm-charts/charts/guestbook文件夹下。随时参考这个位置,以便您可以跟随示例。

我们将首先搭建 Guestbook Helm 图表,以创建图表的初始文件结构。

搭建初始文件结构

正如您可能还记得的第四章理解 Helm 图表,Helm 图表必须遵循特定的文件结构才能被视为有效。换句话说,一个图表必须包含以下必需文件:

  • Chart.yaml:用于定义图表元数据

  • values.yaml:用于定义默认图表值

  • templates/:用于定义图表模板和要创建的 Kubernetes 资源

我们在第四章理解 Helm 图表中提供了图表可能包含的每个文件的列表,但前面提到的三个文件是开始开发新图表所必需的文件。虽然这三个文件可以从头开始创建,但 Helm 提供了helm create命令,可以更快地搭建一个新的图表。除了创建之前列出的文件外,helm create命令还会生成许多不同的样板模板,可以更快地编写您的 Helm 图表。让我们使用这个命令来搭建一个名为guestbook的新 Helm 图表。

helm create命令将 Helm 图表的名称(guestbook)作为参数。在本地命令行上运行以下命令来搭建这个图表:

$ helm create guestbook

运行此命令后,您将在您的机器上看到一个名为guestbook/的新目录。这是包含您 Helm 图表的目录。在目录中,您将看到以下四个文件:

  • charts/

  • Chart.yaml

  • templates/

  • values.yaml

正如你所看到的,helm create命令创建了一个charts/目录,除了必需的Chart.yamlvalues.yamltemplates/文件。charts/目录目前是空的,但以后当我们声明一个图表依赖时,它将自动填充。您可能还注意到其他提到的文件已经自动填充了默认设置。在本章的开发guestbook图表过程中,我们将利用许多这些默认设置。

如果您探索templates/目录下的内容,您会发现许多不同的模板资源已经默认包含在内。这些资源将节省创建这些资源所需的时间。虽然生成了许多有用的模板,我们将删除templates/tests/文件夹。这个文件夹用于包含您 Helm 图表的测试,但我们将专注于在第六章测试 Helm 图表中编写您自己的测试。运行以下命令来删除templates/tests/文件夹:

$ rm -rf guestbook/templates/tests

现在guestbook图表已经被搭建好了,让我们继续评估生成的Chart.yaml文件。

评估图表定义

图定义,或Chart.yaml文件,用于包含 Helm 图的元数据。我们在第四章中讨论了Chart.yaml文件的每个可能选项,了解 Helm 图,但让我们回顾一下典型图定义中包含的一些主要设置,如下所示:

  • apiVersion:设置为v1v2v2是 Helm 3 的首选选项)

  • version:Helm 图的版本。这应该是符合语义化版本规范SemVer)的版本。

  • appVersion:Helm 图部署的应用程序的版本

  • name:Helm 图的名称

  • description:Helm 图的简要描述及其设计部署的内容

  • type:设置为applicationlibraryApplication图用于部署特定应用程序。Library图包含一组辅助函数(也称为“命名模板”),可在其他图中使用,以减少样板文件。

  • dependencies:Helm 图依赖的图列表

如果你观察你的脚手架Chart.yaml文件,你会注意到每个字段(除了 dependencies)已经被设置。这个文件可以在以下截图中看到:

图 5.3:脚手架 Chart.yaml 文件

图 5.3:脚手架 Chart.yaml 文件

我们暂时将文件中包含的每个设置保持为默认值(尽管如果你愿意,可以随意编写更有创意的描述)。在本章后面,当这些默认值变得相关时,我们将更新其中的一些默认值。

默认图定义中未包含的另一个设置是dependencies。我们将在下一节中更详细地讨论这一点,其中将添加一个 Redis 依赖项,以简化开发工作。

添加 Redis 图依赖

正如在了解留言板应用程序部分提到的,这个 Helm 图必须能够部署一个 Redis 数据库,用来保存应用程序的状态。如果你完全从头开始创建这个图,你需要对 Redis 的工作原理和如何正确部署到 Kubernetes 有适当的了解。你还需要创建相应的图模板来部署 Redis。

或者,通过包含已包含逻辑和所需图表模板的 Redis 依赖项,您可以大大减少创建guestbook Helm 图表所涉及的工作量。让我们通过添加 Redis 依赖项来修改生成的Chart.yaml文件,以简化图表开发。

添加 Redis 图表依赖的过程可以通过以下步骤完成:

  1. 通过运行以下命令在 Helm Hub 存储库中搜索 Redis 图表:
$ helm search hub redis
  1. 将显示的图表之一是 Bitnami 的 Redis 图表。这是我们将用作依赖项的图表。如果您尚未在第三章中添加bitnami图表存储库,请使用helm add repo命令立即添加此图表存储库。请注意,存储库统一资源定位符URL)是从 Helm Hub 存储库中 Redis 图表的页面中检索的。代码可以在以下片段中看到:
$ helm add repo bitnami https://charts.bitnami.com
  1. 确定您想要使用的 Redis 图表的版本。可以通过运行以下命令找到版本号列表:
$ helm search repo redis --versions
NAME                        	CHART VERSION	APP VERSION
bitnami/redis               	10.5.14       	5.0.8
bitnami/redis               	10.5.13       	5.0.8
bitnami/redis               	10.5.12       	5.0.8
bitnami/redis               	10.5.11       	5.0.8

您必须选择的版本是图表版本,而不是应用程序版本。应用程序版本仅描述 Redis 版本,而图表版本描述实际 Helm 图表的版本。

依赖项允许您选择特定的图表版本,或者使用诸如10.5.x之类的通配符。使用通配符可以轻松地使您的图表与匹配该通配符的最新 Redis 版本保持更新(在本例中,该版本为10.5.14)。在本例中,我们将使用版本10.5.x

  1. dependencies字段添加到Chart.yaml文件中。对于guestbook图表,我们将使用以下最低要求字段配置此字段(其他字段在第四章了解 Helm 图表中讨论):

name:依赖图的名称

version:依赖图的版本

repository:依赖图的存储库 URL

将以下YAML 不是标记语言YAML)代码添加到您的Chart.yaml文件的末尾,提供您已收集的有关 Redis 图表的信息以配置依赖项的设置:

dependencies:
  - name: redis
    version: 10.5.x
    repository: https://charts.bitnami.com

添加依赖项后,您的完整Chart.yaml文件应如下所示(为简洁起见,已删除注释和空行):

apiVersion: v2
name: guestbook
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: 1.16.0
dependencies:
  - name: redis
    version: 10.5.x
    repository: https://charts.bitnami.com

该文件也可以在 Packt repository at https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/Chart.yaml 中进行查看(请注意,版本和appVersion字段可能不同,因为我们将在本章后面修改这些字段)。

现在您的依赖已经添加到图表定义中,让我们下载这个依赖,以确保它已经正确配置。

下载 Redis 图表依赖

首次下载依赖时,应使用helm dependency update命令。此命令将下载您的依赖到charts/目录,并将生成Chart.lock文件,该文件指定了已下载的图表的元数据。

运行helm dependency update命令来下载您的 Redis 依赖。该命令以 Helm 图表的位置作为参数,并可以在以下代码片段中看到:

$ helm dependency update guestbook
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the 'bitnami' chart repository
Update Complete.  Happy Helming!
Saving 1 charts
Downloading redis from repo https://charts.bitnami.com
Deleting outdated charts

您可以通过确保 Redis 图表出现在charts/文件夹下来验证下载是否成功,如下所示:

$ ls guestbook/charts
redis-10.5.14.tgz

现在 Redis 依赖已经包含,让我们继续修改values.yaml文件。在这里,我们将覆盖特定于配置 Redis 以及 Guestbook 前端应用程序的值。

修改 values.yaml 文件

Helm chart 的values.yaml文件用于提供一组默认参数,这些参数在整个图表模板中被引用。当用户与 Helm 图表交互时,他们可以使用--set--values标志覆盖这些默认值。除了提供一组默认参数外,一个写得好的 Helm 图表应该是自说明的,包含每个值的直观名称和解释难以实现的值的注释。编写一个自说明的value.yaml文件允许用户和维护者简单地参考这个文件,以便了解图表的值。

helm create命令生成一个值文件,其中包含许多在 Helm 图表开发中常用的样板值。让我们通过在文件末尾添加一些额外的值来完成配置 Redis 依赖。之后,我们将专注于修改一些样板值,以配置 Guestbook 前端资源。

添加值以配置 Redis 图表

虽然添加依赖项可以防止您需要创建其图表模板,但您可能仍然需要覆盖一些值以对其进行配置。在这种情况下,需要覆盖一些 Redis 图表的值,以使其能够与guestbook图表的其余部分无缝配合。

让我们首先了解一下 Redis 图表的值。这可以通过对下载的 Redis 图表运行helm show values命令来完成,如下所示:

$ helm show values charts/redis-10.5.14.tgz

请确保修改命令以匹配您下载的 Redis 图表版本。显示值列表后,让我们识别需要被覆盖的值,如下所示:

  1. Redis 图表中需要被覆盖的第一个值是fullnameOverride。此值显示在helm show values输出中,如下所示:
## String to fully override redis.fullname template
##
# fullnameOverride:

图表通常在一个名为$CHART_NAME.fullname的命名模板中使用这个值,以便轻松生成它们的 Kubernetes 资源名称。当设置了fullnameOverride时,命名模板将评估为这个值。否则,此模板的结果将基于.Release.Name对象,或者安装时提供的 Helm 发布的名称。

Redis 依赖项使用redis.fullname模板来帮助设置 Redis 主和 Redis 从服务的名称。

以下片段显示了在 Redis 图表中生成 Redis 主服务名称的示例:

name: {{ template 'redis.fullname' . }}-master

Guestbook 应用程序需要将 Redis 服务命名为redis-masterredis-slave。因此,fullnameOverride值应设置为redis

如果您有兴趣了解redis.fullname模板的工作原理以及它在整个 Redis 图表中的应用方式,您可以在charts/文件夹下解压 Redis 依赖项。在该文件夹中,您将在templates/_helpers.tpl文件中找到redis.fullname模板,并注意其在每个 YAML 模板中的调用。 (事实证明,您生成的guestbook图表中也包含一个类似的模板在_helpers.tpl文件中,但一般来说,最好参考依赖项的资源,以防其维护者定制了模板。)

如果您有兴趣了解 Guestbook 应用程序的工作原理,可以在 GitHub 上找到源代码。以下文件定义了所需的 Redis 服务名称:

https://github.com/kubernetes/examples/blob/master/guestbook/php-redis/guestbook.php

  1. 需要从 Redis 图表中覆盖的下一个值是usePassword。以下代码片段显示了helm show values输出中这个值的样子:
## Use password authentication
usePassword: true

Guestbook 应用程序已经编写为无需身份验证即可访问 Redis 数据库,因此我们将希望将此值设置为false

  1. 我们需要覆盖的最后一个值是configmap。以下是helm show values输出中此值的样子:
## Redis config file
## ref: https://redis.io/topics/config
##
configmap: |-
  # Enable AOF https://redis.io/topics/persistence#append-only-file
  appendonly yes
  # Disable RDB persistence, AOF persistence already enabled.
  save ''

默认的configmap值将启用 Redis 可以使用的两种持久性类型,追加日志文件AOF)和Redis 数据库文件RDF)持久性。Redis 中的 AOF 持久性通过将新数据条目添加到类似于更改日志的文件中来提供更改历史。RDF 持久性通过在一定间隔内将数据复制到文件中,以创建数据快照。

在本章后面,我们将创建简单的生命周期钩子,允许用户将 Redis 数据库备份和恢复到先前的快照。因为只有 RDB 持久性与快照文件一起工作,我们将覆盖configmap值以读取appendonly no,这将禁用 AOF 持久性。

识别每个 Redis 值后,将这些值添加到图表的values.yaml文件的末尾,如下面的代码块所示:

redis:
  # Override the redis.fullname template
  fullnameOverride: redis
  # Enable unauthenticated access to Redis
  usePassword: false
  # Disable AOF persistence
  configmap: |-
    appendonly no

请记住第四章**, 理解 Helm 图表,从图表依赖中覆盖的值必须在该图表名称下进行范围限定。这就是为什么每个这些值将被添加到redis:段下面。

您可以通过参考位于 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/values.yaml 的 Packt 存储库中的values.yaml文件,检查是否正确配置了 Redis 值。

重要提示

与 Redis 无关的一些值可能与您的values.yaml文件不同,因为我们将在下一节中修改这些值。

配置了 Redis 依赖项的值后,让我们继续修改helm create生成的默认值,以部署 Guestbook 前端。

修改值以部署 Guestbook 前端

当您在本章开头运行helm create命令时,它创建的一些项目是templates/目录下的默认模板和values.yaml文件中的默认值。

以下是创建的默认模板列表:

  • deployment.yaml:用于将 Guestbook 应用程序部署到 Kubernetes。

  • ingress.yaml:提供了一种从 Kubernetes 集群外部访问 Guestbook 应用程序的选项。

  • serviceaccount.yaml:用于为 Guestbook 应用程序创建一个专用的serviceaccount

  • service.yaml:用于在 Guestbook 应用程序的多个实例之间进行负载平衡。还可以提供一种从 Kubernetes 集群外部访问 Guestbook 应用程序的选项。

  • _helpers.tp:提供了一组在 Helm 图表中广泛使用的常见模板。

  • NOTES.txt:提供了安装后访问应用程序所使用的一组说明。

每个模板都由图表的值配置。虽然helm create命令为部署 Guestbook 应用程序提供了一个很好的起点,但它没有提供所需的每个默认值。为了用所需的值替换默认值,我们可以观察生成的图表模板并相应地修改它们的参数。

让我们逐步了解指示需要进行修改的模板位置。

第一个位置在deployment.yaml图表模板中。在该文件中,有一行指示要部署的容器映像,如下所示:

image: '{{ .Values.image.repository }}:{{ .Chart.AppVersion }}'

如您所见,image 由image.repository值和AppVersion图表设置确定。如果您查看您的values.yaml文件,您会看到image.repository值当前配置为默认部署nginx映像,如下所示:

image:
  repository: nginx

同样,如果您查看Chart.yaml文件,您会看到AppVersion目前设置为1.16.0,如下所示:

appVersion: 1.16.0

由于 Guestbook 应用程序起源于 Kubernetes 教程,您可以在 Kubernetes 文档中找到需要部署的特定映像,网址为 https://kubernetes.io/docs/tutorials/stateless-application/guestbook/#creating-the-guestbook-frontend-deployment。在文档中,您可以看到必须指定映像如下:

image: gcr.io/google-samples/gb-frontend:v4

因此,为了正确生成 image 字段,image.repository值必须设置为gcr.io/google-samples/gb-frontend,并且AppVersion图表设置必须设置为v4

必须进行修改的第二个位置是service.yaml图表模板。在这个文件中,有一行确定服务类型的代码,如下所示:

type: {{ .Values.service.type }}

根据service.type的值,该服务将默认为ClusterIP服务类型,如values.yaml文件中所示:

service:
  type: ClusterIP

对于guestbook图表,我们将修改此值,以创建一个NodePort服务。这将允许在 minikube 环境中更容易地访问应用程序,通过在 minikube 虚拟机(VM)上暴露一个端口。连接到端口后,我们可以访问 Guestbook 前端。

请注意,虽然helm create生成了一个ingress.yaml模板,也允许访问,但在 minikube 环境中工作时,更常见的建议是使用NodePort服务,因为不需要附加组件或增强功能。幸运的是,生成的图表默认禁用了入口资源的创建,因此无需禁用此功能。

现在我们已经确定了需要更改的默认设置,让我们首先按照以下方式更新values.yaml文件:

  1. image.repository值替换为gcr.io/google-samples/gb-frontend。整个image:部分现在应该如下所示:
image:
  repository: gcr.io/google-samples/gb-frontend
  pullPolicy: IfNotPresent
  1. service.type值替换为NodePort。整个service:部分现在应该如下所示:
service:
  type: NodePort
  port: 80
  1. 您可以通过参考 Packt 存储库中的文件来验证您的values.yaml文件是否已正确修改。

接下来,让我们更新Chart.yaml文件,以便部署正确的 Guestbook 应用程序版本,如下所示:

  1. appVersion字段替换为v4appVersion字段现在应该如下所示:
appVersion: v4
  1. 您可以通过参考 Packt 存储库中的文件来验证您的Chart.yaml文件是否已正确修改。

现在图表已经使用正确的值和设置进行了更新,让我们通过将其部署到 minikube 环境中来看看这个图表的运行情况。

安装 Guestbook 图表

要安装您的guestbook图表,请在guestbook/目录之外运行以下命令:

$ helm install my-guestbook guestbook -n chapter5

如果安装成功,将显示以下消息:

NAME: my-guestbook
LAST DEPLOYED: Sun Apr 26 09:57:52 2020
NAMESPACE: chapter5
STATUS: deployed
REVISION: 1
NOTES:
1\. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace chapter5 -o jsonpath='{.spec.ports[0].nodePort}' services my-guestbook)
  export NODE_IP=$(kubectl get nodes --namespace chapter5 -o jsonpath='{.items[0].status.addresses[0].address}')
  echo http://$NODE_IP:$NODE_PORT

安装成功后,您可能会发现留言板和 Redis pods 并不立即处于“准备就绪”状态。当一个 Pod 没有准备就绪时,它还不能被访问。

您还可以通过传入--wait标志来强制 Helm 等待这些 Pod 准备就绪。--wait标志可以与--timeout标志一起使用,以增加 Helm 等待 Pod 准备就绪的时间(以秒为单位)。默认设置为 5 分钟,这对于这个应用程序来说已经足够了。

您可以通过检查每个 Pod 的状态来确保所有的 Pod 都已准备就绪,而不使用--wait标志,如下所示:

$ kubectl get pods -n chapter5

当每个 Pod 准备就绪时,您将能够观察到每个 Pod 在READY列下报告为1/1,如下所示:

图 5.4:当每个 Pod 准备就绪时,kubectl get pods –n chapter5 的输出

图 5.4:当每个 Pod 准备就绪时,kubectl get pods –n chapter5 的输出

一旦 Pod 准备就绪,您可以运行发布说明中显示的命令。如果需要,可以通过运行以下代码再次显示它们:

$ helm get notes my-guestbook -n chapter5
NOTES:
1\. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace chapter5 -o jsonpath='{.spec.ports[0].nodePort}' services my-guestbook)
  export NODE_IP=$(kubectl get nodes --namespace chapter5 -o jsonpath='{.items[0].status.addresses[0].address}')
  echo http://$NODE_IP:$NODE_PORT

将留言板 URL(从echo命令的输出中复制并粘贴)到您的浏览器中,留言板用户界面UI)应该显示出来,如下截图所示:

图 5.5:留言板前端

图 5.5:留言板前端

尝试在对话框中输入一条消息,然后单击提交。留言板前端将在提交按钮下显示消息,这表明消息已保存到 Redis 数据库,如下截图所示:

图 5.6:留言板前端显示先前发送的消息

图 5.6:留言板前端显示先前发送的消息

如果您能够编写一条消息并在屏幕上看到它显示出来,那么您已成功构建和部署了您的第一个 Helm 图表!如果您无法看到您的消息,那么您的 Redis 依赖项可能没有正确设置。在这种情况下,请确保您的 Redis 值已经正确配置,并且您的 Redis 依赖已经在Chart.yaml文件中正确声明。

当您准备好时,使用helm uninstall命令卸载此图表,就像这样:

$ helm uninstall my-guestbook -n chapter5

您还需要手动删除 Redis PersistentVolumeClaimsPVCs),因为 Redis 依赖于使用StatefulSet使数据库持久化(在删除时不会自动删除 PVCs)。

运行以下命令以删除 Redis PVCs:

$ kubectl delete pvc -l app=redis -n chapter5

在下一节中,我们将探讨改进guestbook图表的方法。

改进 Guestbook Helm 图表

在上一节中创建的图表成功部署了 Guestbook 应用程序。然而,与任何类型的软件一样,Helm 图表总是可以改进的。在本节中,我们将专注于以下两个功能,以改进guestbook图表:

  • 生命周期钩子备份和恢复 Redis 数据库

  • 输入验证以确保只提供有效的值

让我们首先专注于添加生命周期钩子。

创建 pre-upgrade 和 pre-rollback 生命周期钩子

在本节中,我们将创建两个生命周期钩子,如下:

  1. 第一个钩子将出现在pre-upgrade生命周期阶段。这个阶段发生在运行helm upgrade命令之后,但在任何 Kubernetes 资源被修改之前。这个钩子将用于在执行升级之前对 Redis 数据库进行数据快照,确保在升级出现错误时可以备份数据库。

  2. 第二个钩子将出现在pre-rollback生命周期阶段。这个阶段发生在运行helm rollback命令之后,但在任何 Kubernetes 资源被回滚之前。这个钩子将把 Redis 数据库恢复到先前的数据快照,并确保 Kubernetes 资源配置被恢复到快照被拍摄时的状态。

在本节结束时,您将更加熟悉生命周期钩子以及它们可以执行的一些强大功能。请记住,本节中创建的钩子非常简单,仅用于探索 Helm 钩子的基本功能。不建议尝试在生产环境中直接使用这些钩子。

让我们来看看如何创建pre-upgrade生命周期钩子。

创建 pre-upgrade 钩子以进行数据快照

在 Redis 中,数据快照包含在dump.rdb文件中。我们可以通过创建一个钩子来备份这个文件,该钩子首先在 Kubernetes 命名空间中创建一个新的 PVC。然后,该钩子可以创建一个job资源,将dump.rdb文件复制到新的PersistentVolumeClaim中。

虽然helm create命令生成了一些强大的资源模板,可以快速创建初始的guestbook图表,但它没有生成任何可用于此任务的钩子。因此,您可以通过以下步骤从头开始创建预升级钩子:

  1. 首先,您应该创建一个新的文件夹来包含钩子模板。虽然这不是技术要求,但它有助于将钩子模板与常规图表模板分开。它还允许您按功能对钩子模板进行分组。

在您的guestbook文件结构中创建一个名为templates/backup的新文件夹,如下所示:

$ mkdir guestbook/templates/backup
  1. 接下来,您应该创建两个模板,以执行备份所需的两个模板。所需的第一个模板是PersistentVolumeClaim模板,将用于包含复制的dump.rdb文件。第二个模板将是一个作业模板,用于执行复制操作。

创建两个空模板文件作为占位符,如下所示:

$ touch guestbook/templates/backup/persistentvolumeclaim.yaml
$ touch guestbook/templates/backup/job.yaml
  1. 您可以通过参考 Packt 存储库来仔细检查您的工作。您的文件结构应该与 https://github.com/PacktPublishing/-Learn-Helm/tree/master/helm-charts/charts/guestbook/templates/backup 中找到的结构完全相同。

  2. 接下来,让我们创建persistentvolumeclaim.yaml模板。将下面文件的内容复制到您的backup/persistentvolumeclaim.yaml文件中(此文件也可以从 Packt 存储库 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/backup/persistentvolumeclaim.yaml 中复制。请注意,空格由空格组成,而不是制表符,符合有效的 YAML 语法。文件的内容可以在这里看到:图 5.7:备份/persistentvolumeclaim.yaml 模板

图 5.7:备份/persistentvolumeclaim.yaml 模板

在继续之前,让我们浏览persistentvolumeclaim.yaml文件的一部分,以帮助理解它是如何创建的。

此文件的第 1行和第 17行由一个if操作组成。由于该操作封装了整个文件,这表明只有在redis.master.persistence.enabled值设置为true时,才会包括此资源。在 Redis 依赖图中,此值默认为true,可以使用helm show values命令观察到。

第 5 行确定新 PVC 备份的名称。其名称基于 Redis 依赖图创建的 Redis 主 PVC 的名称,即redis-data-redis-master-0,以便明确指出这是设计为备份的 PVC。其名称还基于修订号。因为此钩子作为预升级钩子运行,它将尝试使用正在升级的修订号。sub函数用于从此修订号中减去1,以便明确指出此 PVC 包含先前修订的数据快照。

第 9 行创建一个注释,将此资源声明为pre-upgrade钩子。第 10 行创建一个helm.sh/hook-weight注释,以确定此资源应与其他预升级钩子相比的创建顺序。权重按升序运行,因此此资源将在其他预升级资源之前创建。

  1. 创建persistentvolumeclaim.yaml文件后,我们将创建最终的预升级模板job.yaml。将以下内容复制到您的backup/job.yaml文件中(此文件也可以从 Packt 存储库 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/backup/job.yaml 中复制):

图 5.8:备份/job.yaml 模板

让我们逐步了解job.yaml模板的部分内容,以了解它是如何创建的。

第 9 行再次定义此模板为预升级钩子。第 11 行将钩子权重设置为1,表示此资源将在其他预升级PersistentVolumeClaim之后创建。

第 10 行设置了一个新的注释,以确定何时应删除此作业。默认情况下,Helm 不管理钩子的创建之外的内容,这意味着当运行helm uninstall命令时,它们不会被删除。helm.sh/hook-delete-policy注释用于确定资源应该在何种条件下被删除。该作业包含before-hook-creation删除策略,这表明如果它已经存在于命名空间中,它将在helm upgrade命令期间被删除,从而允许创建一个新的作业。该作业还将具有hook-succeeded删除策略,如果成功运行,则将导致其被删除。

第 19 行执行dump.rdb文件的备份。它连接到 Redis 主服务器,保存数据库的状态,并将文件复制到备份 PVC。

第 29 行和第 32 行分别定义了 Redis 主 PVC 和备份 PVC。这些 PVC 被作业挂载,以便复制dump.rdb文件。

如果您已经按照前面的每个步骤进行操作,那么您已经为 Helm 图表创建了预升级钩子。让我们继续下一节,创建预回滚钩子。之后,我们将重新部署guestbook图表,以查看这些钩子的作用。

创建预回滚钩子以恢复数据库

而预升级钩子是用来从 Redis 主 PVC 复制dump.rdb文件到备份 PVC,pre-rollback钩子可以编写以执行相反的操作,将数据库恢复到先前的快照。

按照以下步骤创建预回滚钩子:

  1. 创建templates/restore文件夹,用于包含预回滚钩子,如下所示:
$ mkdir guestbook/templates/restore
  1. 接下来,创建一个空的job.yaml模板,用于恢复数据库,如下所示:
$ touch guestbook/templates/restore/job.yaml
  1. 您可以通过引用 Packt 存储库来检查是否已创建了正确的结构github.com/PacktPublishing/-Learn-Helm/tree/master/helm-charts/charts/guestbook/templates/restore。

  2. 接下来,让我们向job.yaml文件添加内容。将以下内容复制到您的restore/job.yaml文件中(此文件也可以从 Packt 存储库 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/c](https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/restore/job.yaml)harts/guestbook/templates/restore/job.yaml)中复制):

图 5.9:回滚/job.yaml 模板

图 5.9:回滚/job.yaml 模板

此模板的第 7 行将此资源声明为pre-rollback钩子。

实际的数据恢复在第 18 行第 19 行执行。第 18 行dump.rdb文件从备份 PVC 复制到 Redis 主 PVC。复制后,第 19 行重新启动数据库,以便重新加载快照。用于重新启动 Redis 数据库的命令将返回失败的退出代码,因为与数据库的连接将意外终止,但可以通过在命令后添加|| true来解决这个问题,这将否定退出代码。

第 29 行定义了 Redis 主卷,第 32 行定义了所需的备份卷,这取决于要回滚到的修订版本。

创建了升级前和回滚前的生命周期钩子后,让我们在 minikube 环境中运行它们,看看它们的作用。

执行生命周期钩子

为了运行您创建的生命周期钩子,您必须首先通过运行helm install命令再次安装您的图表,如下所示:

$ helm install my-guestbook guestbook -n chapter5

当每个 Pod 报告1/1 Ready状态时,通过遵循显示的发布说明访问您的 Guestbook 应用程序。请注意,访问应用程序的端口将与以前不同。

访问 Guestbook 前端后写一条消息。示例消息可以在以下截图中看到:

图 5.10:安装 Guestbook 图表并输入消息后的 Guestbook 前端

图 5.10:安装 Guestbook 图表并输入消息后的 Guestbook 前端

一旦写入消息并且其文本显示在提交按钮下方,运行helm upgrade命令触发 pre-upgrade 钩子。helm upgrade命令将暂时挂起,直到备份完成,并且可以在这里看到:

$ helm upgrade my-guestbook guestbook -n chapter5

当命令返回时,您应该会发现 Redis 主 PVC 以及一个新创建的 PVC,名为redis-data-redis-master-0-backup-1,可以在这里看到:

$ kubectl get pvc -n chapter5
NAME                                 STATUS
redis-data-redis-master-0            Bound
redis-data-redis-master-0-backup-1   Bound

此 PVC 包含一个数据快照,可用于在预回滚生命周期阶段恢复数据库。

现在,让我们继续向 Guestbook 前端添加额外的消息。您应该在提交按钮下看到两条消息,如下面的截图所示:

图 5.11:运行回滚前的 Guestbook 消息

图 5.11:运行回滚前的 Guestbook 消息

现在,运行helm rollback命令以恢复到第一个修订版。此命令将暂时挂起,直到恢复过程完成,并且可以在这里看到:

$ helm rollback my-guestbook 1 -n chapter5

当此命令返回时,请在浏览器中刷新您的 Guestbook 前端。您会看到您在升级后添加的消息消失,因为在进行数据备份之前它不存在,如下面的截图所示:

图 5.12:在预回滚生命周期阶段完成后的 Guestbook 前端

图 5.12:在预回滚生命周期阶段完成后的 Guestbook 前端

虽然这个备份和恢复场景只是一个简单的用例,但它演示了向图表添加 Helm 生命周期钩子可以提供的许多可能性之一。

重要提示

通过在相应的生命周期命令(helm installhelm upgradehelm rollbackhelm uninstall)中添加--no-hooks标志,可以跳过钩子。应用此命令的命令将跳过该生命周期的钩子。

现在,我们将专注于用户输入验证以及如何进一步改进 Guestbook 图表以帮助防止提供不当值。

添加输入验证

在使用 Kubernetes 和 Helm 时,当创建新资源时,Kubernetes 应用程序编程接口API)服务器会自动执行输入验证。这意味着如果 Helm 创建了无效的资源,API 服务器将返回错误消息,导致安装失败。尽管 Kubernetes 执行原生输入验证,但图表开发人员仍可能希望在资源到达 API 服务器之前执行验证。

让我们开始探索如何使用guestbook Helm 图表中的fail函数执行输入验证。

使用 fail 函数

fail函数用于立即失败模板渲染。这个函数可以用在用户提供了无效值的情况下。在本节中,我们将实现一个限制用户输入的示例用例。

你的guestbook图表的values.yaml文件包含一个名为service.type的值,用于确定应该为前端创建什么类型的服务。这个值可以在这里看到:

service:
  type: NodePort

我们将这个值默认设置为NodePort,但从技术上讲,也可以使用其他服务类型。假设你想将服务类型限制为只有NodePortClusterIP服务。这个操作可以通过使用fail函数来执行。

按照以下步骤来限制guestbook图表中的服务类型:

  1. 找到templates/service.yaml服务模板。这个文件包含一行,根据service.type值设置服务类型,如下所示:
type: {{ .Values.service.type }}

我们应该首先检查service.type值是否等于ClusterIPNodePort,然后再设置服务类型。这可以通过将一个变量设置为正确设置的列表来实现。然后,可以进行检查以确定service.type值是否包含在有效设置的列表中。如果是,那么就继续设置服务类型。否则,图表渲染应该被停止,并向用户返回错误消息,通知他们有效的service.type输入。

  1. 复制下面的service.yaml文件来实现步骤 1中描述的逻辑。这个文件也可以从 Packt 仓库 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/service.yaml 中复制:

图 5.13:在 service.yaml 模板中实现的 service.type 验证

第 8 行第 13 行代表了输入验证。第 8 行创建了一个名为serviceTypes的变量,它等于正确的服务类型列表。第 9 行第 13 行代表了一个if操作。第 9 行中的has函数将检查service.type值是否包含在serviceTypes中。如果是,那么渲染将继续到第 10 行来设置服务的类型。否则,渲染将继续到第 12 行第 12 行使用fail函数来停止模板渲染,并向用户显示关于有效服务类型的消息。

尝试通过提供无效的服务类型来升级你的my-guestbook发布(如果你已经卸载了你的发布,重新安装也可以)。为此,请运行以下命令:

$ helm upgrade my-guestbook . -n chapter5 --set service.type=LoadBalancer

如果你在前面的步骤 2中的更改成功了,你应该会看到类似以下的消息:

Error: UPGRADE FAILED: template: guestbook/templates/service.yaml:12:6: executing 'guestbook/templates/service.yaml' at <fail 'value 'service.type' must be either 'ClusterIP' or 'NodePort''>: error calling fail: value 'service.type' must be either 'ClusterIP' or 'NodePort'

使用fail验证用户输入是确保提供的值符合一定约束的好方法,但也有时候需要确保用户首先提供了某些值。这可以通过使用下一节中解释的required函数来实现。

使用required函数

required函数和fail一样,也用于停止模板渲染。不同之处在于,required函数用于确保在图表模板渲染时值不为空。

回想一下,你的图表中包含一个名为image.repository的值,如下所示:

image:
  repository: gcr.io/google-samples/gb-frontend

这个值用于确定将部署的镜像。考虑到这个值对 Helm 图表的重要性,我们可以用required函数来确保在安装图表时它始终有一个值。虽然我们目前在这个图表中提供了一个默认值,但添加required函数可以让你在需要确保用户始终提供自己的容器镜像时删除这个默认值。

按照以下步骤对image.repository值实施required函数:

  1. 找到templates/deployment.yaml图表模板。该文件包含一行,根据image.repository的值设置容器镜像(appName图表设置也有助于设置容器镜像,但在这个例子中,我们只关注image.repository),如下所示:
image: '{{ .Values.image.repository }}:{{ .Chart.AppVersion }}'
  1. required函数接受以下两个参数:
  • 显示错误消息,指出是否提供了该值 必须提供的值

给定这两个参数,修改deployment.yaml文件,使image.repository的值是必需的。

要添加这个验证,你可以从以下代码片段中复制,或者参考 Packt 仓库 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/deployment.yaml 中的内容:

图 5.14:使用第 28 行上所需功能的 deployment.yaml 片段

  1. 尝试通过提供空的image.repository值来升级您的my-guestbook发布,如下所示:
$ helm upgrade my-guestbook . -n chapter5 --set image.repository=''

如果您的更改成功,您应该会看到类似以下的错误消息:

Error: UPGRADE FAILED: execution error at (guestbook/templates/deployment.yaml:28:21): value 'image.repository' is required

到目前为止,您已成功编写了您的第一个 Helm 图表,包括生命周期挂钩和输入验证!

在下一节中,您将学习如何使用 GitHub Pages 创建一个简单的图表存储库,该存储库可用于使您的guestbook图表对世界可用。

将 Guestbook 图表发布到图表存储库

现在您已经完成了 Guestbook 图表的开发,该图表可以发布到存储库,以便其他用户可以轻松访问。让我们首先创建图表存储库。

创建图表存储库

图表存储库是包含两个不同组件的服务器,如下所示:

  • Helm 图表,打包为tgz存档

  • 一个包含存储库中包含的图表的元数据的index.yaml文件

基本的图表存储库要求维护者生成自己的index.yaml文件,而更复杂的解决方案,如 Helm 社区的ChartMuseum工具,在推送新图表到存储库时动态生成index.yaml文件。在这个例子中,我们将使用 GitHub Pages 创建一个简单的图表存储库。GitHub Pages 允许维护者从 GitHub 存储库创建一个简单的静态托管站点,该站点可用于创建一个基本的图表存储库来提供 Helm 图表。

您需要一个 GitHub 帐户来创建 GitHub Pages 图表存储库。如果您已经有一个 GitHub 帐户,您可以在 https://github.com/login 登录。否则,您可以在 https://github.com/join 创建一个新帐户。

一旦您登录 GitHub,按照这些步骤创建您的图表存储库:

  1. 跟随 https://github.com/new 链接访问创建新存储库页面。

  2. 为您的图表存储库提供一个名称。我们建议使用名称Learn-Helm-Chart-Repository

  3. 选择使用 README 初始化此存储库旁边的复选框。这是必需的,因为 GitHub 不允许您创建静态站点,如果它不包含任何内容。

  4. 您可以将其余设置保留为默认值。请注意,为了利用 GitHub Pages,除非您拥有付费的 GitHub Pro 帐户,否则必须将隐私设置保留为公共

  5. 单击创建存储库按钮完成存储库创建过程。

  6. 尽管您的存储库已创建,但在启用 GitHub Pages 之前,它无法提供 Helm 图表的服务。单击存储库内的设置选项卡以访问存储库设置。

  7. 设置页面(和选项选项卡)的GitHub Pages部分中找到它,它出现在页面底部。

  8. 来源下,从下拉列表中选择主分支选项。这将允许 GitHub 创建一个提供主分支内容的静态站点。

  9. 如果您成功配置了 GitHub Pages,您将收到屏幕顶部显示的消息,上面写着GitHub Pages 源已保存。您还将能够看到您静态站点的 URL,如下面的示例截图所示:

图 5.15:GitHub Pages 设置和示例 URL

配置好 GitHub 存储库后,您应该将其克隆到本地计算机。按照以下步骤克隆存储库:

  1. 通过选择页面顶部的Code选项卡导航到存储库的根目录。

  2. 选择绿色的克隆或下载按钮。这将显示您的 GitHub 存储库的 URL。请注意,此 URL 与您的 GitHub Pages 静态站点不同。

如果需要,您可以使用以下示例截图来查找您的 GitHub 存储库 URL:

图 5.16:单击克隆或下载按钮即可找到您的 GitHub 存储库 URL

图 5.16:单击克隆或下载按钮即可找到您的 GitHub 存储库 URL

  1. 一旦您获得了存储库的git引用,就将存储库克隆到本地计算机。确保在运行以下命令时不在guestbook目录内,因为我们希望该存储库与guestbook图表分开。
$ git clone $REPOSITORY_URL

一旦您克隆了存储库,继续下一节将guestbook图表发布到您的图表存储库。

发布 Guestbook Helm 图表

Helm 提供了几个不同的命令来使发布 Helm 图表成为一个简单的任务。然而,在运行这些命令之前,您可能会发现需要增加您的图表的version字段在Chart.yaml文件中。对您的图表进行版本控制是发布过程的重要部分,就像其他类型的软件一样。

修改您的图表的Chart.yaml文件中的版本字段为 1.0.0,如下所示:

version: 1.0.0

一旦您的guestbook图表的版本已经增加,您可以继续将您的图表打包成一个tgz存档。这可以通过使用helm package命令来完成。从您本地guestbook目录的上一级运行此命令,如下所示:

$ helm package guestbook

如果成功,这将创建一个名为guestbook-1.0.0.tgz的文件。

重要提示

在处理包含依赖关系的图表时,helm package命令需要将这些依赖关系下载到charts/目录中,以便成功打包图表。如果您的helm package命令失败了,请检查您的 Redis 依赖是否已经下载到charts/目录中。如果没有,您可以在helm package中添加--dependency-update标志,这将在同一命令中下载依赖并打包您的 Helm 图表。

一旦您的图表被打包,通过运行以下命令将生成的tgz文件复制到您的 GitHub 图表仓库的克隆中:

$ cp guestbook-1.0.0.tgz $GITHUB_CHART_REPO_CLONE

当这个文件被复制后,您可以使用helm repo index命令为您的 Helm 仓库生成index.yaml文件。这个命令以您的图表仓库克隆的位置作为参数。运行以下命令来生成您的index.yaml文件:

$ helm repo index $GITHUB_CHART_REPO_CLONE

这个命令会悄悄地成功,但是你会在Learn-Helm-Chart-Repository文件夹内看到新的index.yaml文件。这个文件的内容提供了guestbook图表的元数据。如果这个仓库中还包含其他图表,它们的元数据也会出现在这个文件中。

您的 Helm 图表仓库现在应该包含tgz存档和index.yaml文件。通过使用以下git命令将这些文件推送到 GitHub:

$ git add --all
$ git commit -m 'feat: adding the guestbook helm chart'
$ git push origin master

您可能会被提示输入您的 GitHub 凭据。一旦提供,您的本地内容将被推送到远程仓库,您的guestbook Helm 图表将从 GitHub Pages 静态站点提供服务。

接下来,让我们将您的图表仓库添加到本地的 Helm 客户端中。

添加您的图表仓库

与其他图表存储库的过程类似,您必须首先知道您的 GitHub Pages 图表存储库的 URL,以便将其添加到本地。 此 URL 显示在“设置”选项卡中,如“创建图表存储库”部分所述。

一旦您知道您的图表存储库的 URL,您可以使用helm repo add命令将此存储库添加到本地,如下所示:

$ helm repo add learnhelm $GITHUB_PAGES_URL

此命令将允许您的本地 Helm 客户端与名为learnhelm的存储库进行交互。 您可以通过搜索您的本地配置的存储库来验证您的图表是否已发布。 可以通过运行以下命令来完成此操作:

$ helm search repo guestbook

您应该在搜索输出中找到learnhelm/guestbook图表。

成功发布guestbook图表后,让我们通过清理 minikube 环境来结束。

清理

您可以通过删除chapter5命名空间来清理环境,方法如下:

$ kubectl delete namespace chapter5

如果您已经完成工作,还可以使用minikube stop命令停止您的 minikube 集群。

摘要

在本章中,您学会了如何通过编写一个部署 Guestbook 应用程序的图表来从头开始构建 Helm 图表。 您首先创建了一个部署 Guestbook 前端和 Redis 依赖图表的图表,然后通过编写生命周期挂钩和添加输入验证来改进了此图表。 最后,通过使用 GitHub Pages 构建自己的图表存储库并将guestbook图表发布到此位置来结束了本章。

在下一章中,您将学习有关测试和调试 Helm 图表的策略,以帮助您进一步加强图表开发技能。

进一步阅读

有关 Guestbook 应用程序的其他信息,请参阅 Kubernetes 文档中的“使用 Redis 部署 PHP Guestbook 应用程序”教程,网址为 https://kubernetes.io/docs/tutorials/stateless-application/guestbook/。

要了解有关开发 Helm 图表模板的更多信息,请参考以下链接:

问题

  1. 可以使用哪个命令来创建一个新的 Helm 图表脚手架?

  2. 在开发guestbook图表时,声明 Redis 图表依赖提供了哪些关键优势?

  3. 可以使用哪个注释来设置给定生命周期阶段的钩子的执行顺序?

  4. 使用fail函数的常见用例是什么?required函数呢?

  5. 为了将 Helm 图表发布到 GitHub Pages 图表存储库,涉及哪些 Helm 命令?

  6. 图表存储库中的index.yaml文件的目的是什么?

第六章:测试 Helm 图表

测试是工程师在软件开发过程中必须执行的常见任务。测试是为了验证产品的功能性,以及在产品随着时间的推移而发展时防止回归。经过充分测试的软件更容易随着时间的推移进行维护,并允许开发人员更有信心地向最终用户提供新版本。

为了确保 Helm 图表能够按预期的质量水平提供其功能,应该对其进行适当的测试。在本章中,我们将讨论如何实现强大的 Helm 图表测试,包括以下主题:

  • 设置您的环境

  • 验证 Helm 模板

  • 在一个实时集群中进行测试

  • 通过图表测试项目改进图表测试

  • 清理

技术要求

本章将使用以下技术:

  • minikube

  • kubectl

  • helm

  • git

  • yamllint

  • yamale

  • chart-testing (ct)

除了这些工具,您还可以在 Packt GitHub 存储库中跟随示例,该存储库位于github.com/PacktPublishing/-Learn-Helm,本章将引用该存储库。在本章中使用的许多示例命令中,我们将引用 Packt 存储库,因此您可能会发现通过运行git clone命令克隆此存储库会很有帮助:

$ git clone https://github.com/PacktPublishing/-Learn-Helm Learn-Helm

现在,让我们继续设置您的本地minikube环境。

设置您的环境

在本章中,我们将为上一章创建的Guestbook图表创建并运行一系列测试。运行以下步骤来设置您的minikube环境,在这里我们将测试 Guestbook 图表:

  1. 通过运行minikube start命令启动minikube
minikube start
  1. 然后,创建一个名为chapter6的新命名空间:
kubectl create namespace chapter6

准备好您的minikube环境后,让我们开始讨论如何测试 Helm 图表。我们将首先讨论您可以使用的方法来验证您的 Helm 模板。

验证 Helm 模板

在上一章中,我们从头开始构建了一个 Helm 图表。最终产品非常复杂,包含参数化、条件模板和生命周期钩子。由于 Helm 的主要目的之一是创建 Kubernetes 资源,因此在将资源模板应用到 Kubernetes 集群之前,您应该确保这些资源模板被正确生成。这可以通过多种方式来完成,我们将在下一节中讨论。

使用 helm template 在本地验证模板生成

验证图表模板的第一种方法是使用helm template命令,该命令可用于在本地呈现图表模板并在标准输出中显示其完全呈现的内容。

helm template命令具有以下语法:

$ helm template [NAME] [CHART] [flags]

此命令在本地呈现模板,使用NAME参数满足.Release内置对象,使用CHART参数表示包含 Kubernetes 模板的图表。Packt 存储库中的helm-charts/charts/guestbook文件夹可用于演示helm template命令的功能。该文件夹包含在上一节中开发的图表,以及稍后在本章中将使用的其他资源。

通过运行以下命令在本地呈现guestbook图表:

$ helm template my-guestbook Learn-Helm/helm-charts/charts/guestbook

此命令的结果将显示每个 Kubernetes 资源,如果将其应用于集群,将会创建这些资源,如下所示:

图 6.1 - 用于 guestbook 图表的 ConfigMap

图 6.1 - "helm template"输出

前面的屏幕截图显示了针对上一章中创建的 Guestbook 图表执行的helm template命令的输出的开始部分。正如您所看到的,显示了一个完全呈现的ConfigMap,以及另一个ConfigMap的开始,该ConfigMap是使用该版本创建的。在本地呈现这些资源可以让您了解如果将该版本安装到 Kubernetes 集群中,将会创建哪些确切的资源和规范。

在图表开发过程中,您可能希望定期使用helm template命令来验证您的 Kubernetes 资源是否被正确生成。

您可能想要验证图表开发的一些常见方面,包括以下内容:

  • 参数化字段成功地被默认值或覆盖值替换

  • 控制操作,如ifrangewith,根据提供的值成功生成 YAML 文件

  • 资源包含适当的间距和缩进

  • 函数和管道被正确使用以正确格式化和操作 YAML 文件

  • 诸如requiredfail之类的函数根据用户输入正确验证值

了解图表模板如何在本地呈现后,现在让我们深入一些特定方面,您可以通过利用helm template命令进行测试和验证。

测试模板参数化

重要的是要检查模板的参数是否成功填充了值。这很重要,因为您的图表可能由多个不同的值组成。您可以通过确保每个值具有合理的默认值或具有验证来确保您的图表被正确参数化,如果未提供值,则验证失败图表呈现。

想象以下部署:

apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: {{ .Values.replicas }}
<skipping>
          ports:
            - containerPort: {{ .Values.port }}

在图表的values.yaml文件中应定义replicasport值的合理默认值,如下所示:

replicas: 1
port: 8080

运行helm template命令针对此模板资源呈现以下部署,将replicasport值替换为它们的默认值:

apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: 1
<skipping>
          ports:
            - containerPort: 8080

helm template的输出允许您验证参数是否被其默认值正确替换。您还可以通过向helm template命令传递--values--set参数来验证提供的值是否成功覆盖:

$ helm template my-chart $CHART_DIRECTORY --set replicas=2

生成的模板反映了您提供的值:

apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: 2
<skipping>
          ports:
            - containerPort: 8080

虽然具有默认设置的值通常很容易通过helm template进行测试,但更重要的是测试需要验证的值,因为无效的值可能会阻止图表正确安装。

您应该使用helm template来确保具有限制的值,例如仅允许特定输入的值,通过requiredfail函数成功验证。

想象以下部署模板:

apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: {{ .Values.replicas }}
<skipping>
      containers:
        - name: main
          image: {{ .Values.imageRegistry }}/{{ .Values.imageName }}
          ports:
            - containerPort: {{ .Values.port }}

如果此部署属于具有相同values文件的图表,并且您期望用户提供imageRegistryimageName值来安装图表,如果您然后使用helm template命令而不提供这些值,则结果不尽如人意,如下输出所示:

apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: 1
<skipping>
      containers:
        - name: main
          image: /
          ports:
            - containerPort: 8080

由于没有设置门控,呈现的结果是一个具有无效图像的部署,/。因为我们使用了helm template进行测试,所以我们知道需要处理这些值未定义的情况。可以通过使用required函数来提供验证,以确保这些值被指定:

apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: {{ .Values.replicas }}
<skipping>
      containers:
        - name: main
          image: {{ required 'value 'imageRegistry' is required' .Values.imageRegistry }}/{{ required 'value 'imageName' is required' .Values.imageName }}
          ports:
            - containerPort: {{ .Values.port }}

当对具有更新的部署模板的图表应用helm template命令时,结果会显示一条消息,指示用户提供模板引擎遇到的第一个缺失的值:

$ helm template my-chart $CHART_DIRECTORY
Error: execution error at (test-chart/templates/deployment.yaml:17:20): value 'imageRegistry' is required

您还可以通过在helm template命令旁边提供有效的值文件来进一步测试此验证。例如,我们假设以下值是在用户管理的values文件中提供的:

imageRegistry: my-registry.example.com
imageName: learnhelm/my-image

然后在执行以下命令时提供此文件:

$ helm template my-chart $CHART_DIRECTORY --values my-values.yaml
---
# Source: test-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
<skipping>
  replicas: 1
<skipping>
      containers:
        - name: main
          image: my-registry.example.com/learnhelm/my-image
          ports:
            - containerPort: 8080

作为参数化的一般准则,请确保跟踪您的值,并确保每个值在您的图表中都有用到。在values.yaml文件中设置合理的默认值,并在无法设置默认值的情况下使用required函数。使用helm template函数确保值被正确渲染并产生期望的 Kubernetes 资源配置。

另外,您可能还希望考虑在您的values.yaml文件中包含必需的值,将其作为空字段,并在注释中指出它们是必需的。这样用户就可以查看您的values.yaml文件,并查看您的图表支持的所有值,包括他们必须自己提供的值。在添加了imageRegistryimageName值后,考虑以下values文件:

replicas: 1
port: 8080
## REQUIRED
imageRegistry:
## REQUIRED
imageName:

尽管这些值写在您的图表的values.yaml文件中,但当helm template命令运行时,这些值仍然会评估为 null,提供与之前执行时相同的行为。不同之处在于现在您可以明确地看到这些值是必需的,因此当您首次尝试安装图表时,您不会感到惊讶。

接下来,我们将讨论如何在本地生成您的图表模板可以帮助您测试图表的控制操作。

测试控制操作

除了基本的参数化,您还应该考虑使用helm template命令来验证控制操作(特别是ifrange)是否被正确处理以产生期望的结果。

考虑以下部署模板:

apiVersion: apps/v1
kind: Deployment
<skipping>
{{- range .Values.env }}
          env:
            - name: {{ .name }}
              value: {{ .value }}
{{- end }}
{{- if .Values.enableLiveness }}
          livenessProbe:
            httpGet:
              path: /
              port: {{ .Values.port }}
            initialDelaySeconds: 5
            periodSeconds: 10
{{- end }}
          ports:
            containerPort: 8080

如果envenableLiveness的值都是null,您可以通过运行helm template命令来测试此渲染是否仍然成功:

$ helm template my-chart $CHART_DIRECTORY --values my-values.yaml
---
# Source: test-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
<skipping>
          ports:
            - containerPort: 8080

您会注意到rangeif操作均未生成。对于range子句,空值或空值不会有任何条目对其进行操作,并且当提供给if操作时,这些值也被评估为false。通过向helm template提供envenableLiveness值,您可以验证您已经正确编写了模板以使用这些操作生成 YAML。

您可以将这些值添加到一个values文件中,如下所示:

env:
  - name: BOOK
    value: Learn Helm
enableLiveness: true

进行这些更改后,验证helm template命令的期望结果,以证明模板已正确编写以使用这些值:

---
# Source: test-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
<skipping>
          env:
            - name: BOOK
              value: Learn Helm
          livenessProbe:
            httpGet:
              path: /
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          ports:
            - containerPort: 8080

您应该确保在向图表添加额外的控制结构时,定期使用helm template渲染您的模板,因为这些控制结构可能会使图表开发过程变得更加困难,特别是如果控制结构数量众多或复杂。

除了检查控制结构是否正确生成外,您还应检查您的函数和流水线是否按预期工作,接下来我们将讨论这一点。

测试函数和流水线

helm template命令还可以用于验证函数和流水线生成的渲染结果,这些函数和流水线通常用于生成格式化的 YAML。

以以下模板为例:

apiVersion: apps/v1
kind: Deployment
<skipping>
          resources:
{{ .Values.resources | toYaml | indent 12 }}

此模板包含一个流水线,该流水线对resources值进行参数化和格式化,以指定容器的资源需求。在您的图表的values.yaml文件中包含一个明智的默认值,以确保应用程序具有适当的限制,以防止集群资源的过度利用。

此模板的resources值示例如下:

resources:
  limits:
    cpu: 200m
    memory: 256Mi

您需要运行helm template命令,以确保该值被正确转换为有效的YAML格式,并且输出被正确缩进以生成有效的部署资源。

对此模板运行helm template命令的结果如下:

apiVersion: apps/v1
kind: Deployment
<skipping>
          resources:
            limits:
              cpu: 200m
              memory: 256Mi

接下来,我们将讨论如何在使用helm template渲染资源时启用服务器端验证。

向图表渲染添加服务器端验证

虽然helm template命令对图表开发过程很重要,并且应该经常用于验证图表渲染,但它确实有一个关键的限制。helm template命令的主要目的是提供客户端渲染,这意味着它不会与 Kubernetes API 服务器通信以提供资源验证。如果您希望在生成资源后确保资源有效,可以使用--validate标志指示helm template在生成资源后与 Kubernetes API 服务器通信:

$ helm template my-chart $CHART_DIRECTORY --validate

任何生成的模板如果未生成有效的 Kubernetes 资源,则会提供错误消息。例如,假设使用了一个部署模板,其中apiVersion值设置为apiVersion: v1。为了生成有效的部署,必须将apiVersion值设置为apps/v1,因为这是提供部署资源的 API 的正确名称。仅将其设置为v1将通过helm template的客户端渲染生成看似有效的资源,但是使用--validation标志,您期望看到以下错误:

Error: unable to build kubernetes objects from release manifest: unable to recognize '': no matches for kind 'Deployment' in version 'v1'

--validate标志旨在捕获生成的资源中的错误。如果您可以访问 Kubernetes 集群,并且想要确定您的图表是否生成有效的 Kubernetes 资源,则应使用此标志。或者,您可以针对installupgraderollbackuninstall命令使用--dry-run标志来执行验证。

以下是使用此标志与install命令的示例:

$ helm install my-chart $CHART --dry-run

此标志将生成图表的模板并执行验证,类似于使用--validate标志运行helm template命令。使用--dry-run将在命令行打印每个生成的资源,并且不会在 Kubernetes 环境中创建资源。它主要由最终用户使用,在运行安装之前执行健全性检查,以确保他们提供了正确的值,并且安装将产生期望的结果。图表开发人员可以选择以这种方式使用--dry-run标志来测试图表渲染和验证,或者他们可以选择使用helm template在本地生成图表的资源,并提供--validate以添加额外的服务器端验证。

虽然有必要验证您的模板是否按照您的意图生成,但也有必要确保您的模板是按照最佳实践生成的,以简化开发和维护。Helm 提供了一个名为helm lint的命令,可以用于此目的,我们将在下面更多地了解它。

Linting Helm charts and templates

对您的图表进行 lint 是很重要的,可以防止图表格式或图表定义文件中的错误,并在使用 Helm 图表时提供最佳实践的指导。helm lint命令具有以下语法:

$ helm lint PATH [flags]

helm lint命令旨在针对图表目录运行,以确保图表是有效的和正确格式化的。

重要提示:

helm lint命令不验证渲染的 API 模式,也不对您的 YAML 样式进行 linting,而只是检查图表是否包含应有的文件和设置,这是一个有效的 Helm 图表应该具有的。

您可以对您在第五章中创建的 Guestbook 图表,或者对 Packt GitHub 存储库中helm-charts/charts/guestbook文件夹下的图表运行helm lint命令,网址为github.com/PacktPublishing/-Learn-Helm/tree/master/helm-charts/charts/guestbook

$ helm lint $GUESTBOOK_CHART_PATH
==> Linting guestbook/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed

这个输出声明了图表是有效的,这是由1 chart(s) linted, 0 chart(s) failed消息所指出的。[INFO]消息建议图表在Chart.yaml文件中包含一个icon字段,但这并非必需。其他类型的消息包括[WARNING],它表示图表违反了图表约定,以及[ERROR],它表示图表将在安装时失败。

让我们通过一些例子来运行。考虑一个具有以下文件结构的图表:

guestbook/
  templates/
  values.yaml

请注意,这个图表结构存在问题。这个图表缺少定义图表元数据的Chart.yaml文件。对具有这种结构的图表运行 linter 会产生以下错误:

==> Linting .
Error unable to check Chart.yaml file in chart: stat Chart.yaml: no such file or directory
Error: 1 chart(s) linted, 1 chart(s) failed

这个错误表明 Helm 找不到Chart.yaml文件。如果向图表中添加一个空的Chart.yaml文件以提供正确的文件结构,错误仍会发生,因为Chart.yaml文件包含无效的内容:

guestbook/
  Chart.yaml  # Empty
  templates/
  values.yaml

对这个图表运行 linter 会产生以下错误:

==> Linting .
[ERROR] Chart.yaml: name is required
[ERROR] Chart.yaml: apiVersion is required. The value must be either 'v1' or 'v2'
[ERROR] Chart.yaml: version is required
[INFO] Chart.yaml: icon is recommended
[ERROR] templates/: validation: chart.metadata.name is required
Error: 1 chart(s) linted, 1 chart(s) failed

此输出列出了在Chart.yaml文件中缺少的必需字段。它指示该文件必须包含nameapiVersionversion字段,因此应将这些字段添加到Chart.yaml文件中以生成有效的 Helm 图表。检查器还对apiVersionversion设置提供了额外的反馈,检查apiVersion值是否设置为v1v2,以及version设置是否为正确的SemVer版本。

该检查器还将检查其他必需或建议的文件的存在,例如values.yaml文件和templates目录。它还将确保templates目录下的文件具有.yaml.yml.tpl.txt文件扩展名。helm lint命令非常适合检查图表是否包含适当的内容,但它不会对图表的 YAML 样式进行广泛的 linting。

要执行此 linting,您可以使用另一个名为yamllint的工具,该工具可以在github.com/adrienverge/yamllint找到。可以使用以下命令在一系列操作系统上使用pip软件包管理器安装此工具:

pip install yamllint --user

也可以按照yamllint快速入门说明中描述的方式,使用操作系统的软件包管理器进行安装,该说明位于yamllint.readthedocs.io/en/stable/quickstart.html

为了在图表的 YAML 资源上使用yamllint,您必须将其与helm template命令结合使用,以去除 Go 模板化并生成您的 YAML 资源。

以下是针对 Packt GitHub 存储库中的 guestbook 图表运行此命令的示例:

$ helm template my-guestbook Learn-Helm/helm-charts/charts/guestbook | yamllint -

此命令将在templates/文件夹下生成资源,并将输出传输到yamllint

结果如下所示:

图 6.2 - 一个示例 yamllint 输出

图 6.2 - 一个示例yamllint输出

提供的行号反映了整个helm template输出,这可能会使确定yamllint输出中的哪一行对应于您的 YAML 资源中的哪一行变得困难。

您可以通过将helm template输出重定向到以下命令来确定其行号,针对guestbook图表:

$ cat -n <(helm template my-guestbook Learn-Helm/helm-charts/charts/guestbook)

yamllint将针对许多不同的规则进行 lint,包括以下内容:

  • 缩进

  • 行长度

  • 训练空间

  • 空行

  • 注释格式

您可以通过创建以下文件之一来覆盖默认规则:

  • .yamllint.yamllint.yaml.yamllint.yml在当前工作目录中

  • $XDB_CONFIG_HOME/yamllint/config

  • ~/.config/yamllint/config

要覆盖针对 guestbook 图表报告的缩进规则,您可以在当前工作目录中创建一个.yamllint.yaml文件,其中包含以下内容:

rules:
  indentation:
    # Allow      myList
    #            - item1
    #            - item2
    # Or
    #            myList
    #              - item1
    #              - item2
    indent-sequences: whatever

此配置覆盖了yamllint,使其在添加列表条目时不强制执行一种特定的缩进方法。它由indent-sequences: whatever行配置。创建此文件并再次针对 guestbook 运行 linter 将消除先前看到的缩进错误:

$ helm template my-guestbook guestbook | yamllint -

在本节中,我们讨论了如何使用helm templatehelm lint命令验证 Helm 图表的本地渲染。然而,这实际上并没有测试您的图表功能或应用程序使用您的图表创建的资源的能力。

在下一节中,我们将学习如何在实时 Kubernetes 环境中创建测试来测试您的 Helm 图表。

在实时集群中进行测试

创建图表测试是开发和维护 Helm 图表的重要部分。图表测试有助于验证您的图表是否按预期运行,并且它们可以帮助防止在添加功能和修复图表时出现回归。

测试包括两个不同的步骤。首先,您需要在图表的templates/目录下创建包含helm.sh/hook: test注释的pod模板。这些pod将运行测试您的图表和应用程序功能的命令。接下来,您需要运行helm test命令,该命令会启动test`钩子并创建具有上述注释的资源。

在本节中,我们将学习如何通过向 Guestbook 图表添加测试来在实时集群中进行测试,继续开发您在上一章中创建的图表。作为参考,您将创建的测试可以在 Packt 存储库中的 Guestbook 图表中查看,位于github.com/PacktPublishing/-Learn-Helm/tree/master/helm-charts/charts/guestbook

从您的 Guestbook 图表的templates/目录下添加test/frontend-connection.yamltest/redis-connection.yaml文件开始。请注意,图表测试不一定要位于test子目录下,但将它们放在那里是一种很好的方式,可以使您的测试组织和主要图表模板分开:

$ mkdir $GUESTBOOK_CHART_DIR/templates/test
$ touch $GUESTBOOK_CHART_DIR/templates/test/frontend-connection.yaml
$ touch $GUESTBOOK_CHART_DIR/templates/test/backend-connection.yaml

在本节中,我们将填充这些文件以验证它们关联的应用程序组件的逻辑。

现在我们已经添加了占位符,让我们开始编写测试。

创建图表测试

您可能还记得,Guestbook 图表由 Redis 后端和 PHP 前端组成。用户在前端的对话框中输入消息,并且这些消息将持久保存到后端。让我们编写一些测试,以确保安装后前端和后端资源都可用。我们将从检查 Redis 后端的可用性开始。将以下内容添加到图表的templates/test/backend-connection.yaml文件中(此文件也可以在 Packt 存储库中查看:https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/test/backend-connection.yaml):

图 6.3 - 对 Guestbook 服务的 HTTP 请求

图 6.3 - Guestbook Helm 图表的后端连接测试

此模板定义了在测试生命周期钩子期间将创建的 Pod。此模板中还定义了一个钩子删除策略,指示何时应删除先前的测试 Pod。如果我们创建的测试需要按顺序运行,还可以添加钩子权重。

容器对象下的 args 字段显示了测试将基于的命令。它将使用 redis-cli 工具连接到 Redis 主服务器并运行命令 MGET messages。Guestbook 前端设计为将用户输入的消息添加到名为 messages 的数据库键中。这个简单的测试旨在检查是否可以连接到 Redis 数据库,并且它将通过查询 messages 键返回用户输入的消息。

PHP 前端也应该进行可用性测试,因为它是应用程序的用户界面组件。将以下内容添加到 templates/test/frontend-connection.yaml 文件中(这些内容也可以在 Packt 存储库 https://github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/charts/guestbook/templates/test/frontend-connection.yaml 中找到)。

图 6.4 - Guestbook Helm 图表的前端连接测试

图 6.4 - Guestbook Helm 图表的前端连接测试

这是一个非常简单的测试,它会向 Guestbook 服务发送 HTTP 请求。发送到服务的流量将在 Guestbook 前端实例之间进行负载平衡。此测试将检查负载平衡是否成功执行以及前端是否可用。

现在,我们已经完成了图表测试所需的模板。请注意,这些模板也可以通过 helm 模板命令在本地呈现,并使用 helm lint 和 yamllint 进行检查,如本章前面部分所述。在开发自己的 Helm 图表时,您可能会发现这对于更高级的测试用例很有用。

现在测试已经编写完成,我们将继续在 Minikube 环境中运行它们。

运行图表测试

为了运行图表的测试,必须首先使用helm install命令在 Kubernetes 环境中安装图表。因为编写的测试是设计在安装完成后运行的,所以可以在安装图表时使用--wait标志,以便更容易确定何时 pod 准备就绪。运行以下命令安装 Guestbook 图表:

$ helm install my-guestbook $GUESTBOOK_CHART_DIR -n chapter6 --wait

安装图表后,可以使用helm test命令执行test生命周期钩子并创建测试资源。helm test命令的语法如下所示:

helm test [RELEASE] [flags]

针对my-guestbook发布运行helm test命令:

$ helm test my-guestbook -n chapter6

如果您的测试成功,您将在输出中看到以下结果:

TEST SUITE:     my-guestbook-test-frontend-connection
Last Started:   Tue Jan 28 18:50:23 2020
Last Completed: Tue Jan 28 18:50:25 2020
Phase:          Succeeded
TEST SUITE:     my-guestbook-test-backend-connection
Last Started:   Tue Jan 28 18:50:25 2020
Last Completed: Tue Jan 28 18:50:26 2020
Phase:          Succeeded

在运行测试时,还可以使用--logs标志将日志打印到命令行,从而执行测试。

使用此标志再次运行测试:

$ helm test my-guestbook -n chapter6 --logs

您将看到与之前相同的测试摘要,以及每个测试相关的容器日志。以下是前端连接测试日志输出的第一部分:

POD LOGS: my-guestbook-test-frontend-connection
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
<html ng-app='redis'>
  <head>
    <title>Guestbook</title>

以下是后端连接test日志输出:

POD LOGS: my-guestbook-test-backend-connection

这次测试的日志将为空,因为您尚未在 Guestbook 前端输入任何消息。您可以在从前端添加消息后再次运行测试,以确保消息持久。在运行安装和test套件时,会打印确定 Guestbook 前端 URL 的说明。

这些说明再次显示在这里:

export IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[0].address}')
export PORT=$(kubectl get svc my-guestbook -n chapter6 -o jsonpath='{.spec.ports[0].nodePort}')
echo http://$IP:$PORT

从浏览器访问前端后,向 Guestbook 应用程序添加一条消息。

以下是一个示例截图:

图 6.4 - Guestbook 应用程序的前端

图 6.4-1 - Guestbook 应用程序的前端

一旦添加了消息,再次运行test套件,使用--logs标志显示测试日志。您应该能够通过观察后端连接test日志输出来验证是否已添加此消息:

$ helm test my-guestbook -n chapter6 --logs

以下是显示后端连接test日志输出的片段。您可以验证消息是否已持久到 Redis 数据库中:

POD LOGS: my-guestbook-test-backend-connection
,Writing Helm charts is fun!

在本节中,我们编写了简单的测试,作为一个整体,对图表的安装进行了烟雾测试。有了这些测试,我们将更有信心对图表进行更改和添加功能,前提是在每次修改后运行图表测试以确保功能保持不变。

在下一节中,我们将讨论如何通过利用一个名为ct的工具来改进测试过程。

使用图表测试项目改进图表测试

前一节中编写的测试已足够测试 Guestbook 应用程序是否可以成功安装。然而,标准 Helm 测试过程中存在一些关键限制,需要指出。

要考虑的第一个限制是测试图表值中可能发生的不同排列的困难。因为helm test命令无法修改您发布的值,除了在安装或升级时设置的值,所以在针对不同的值设置运行helm test时,必须遵循以下工作流程:

  1. 使用初始值安装您的图表。

  2. 针对您的发布运行helm test

  3. 删除您的发布。

  4. 使用不同的值集安装您的图表。

  5. 重复步骤 24,直到测试了大量的值可能性。

除了测试不同值的排列组合外,您还应确保在修改图表时不会出现回归。防止回归并测试图表的新版本的最佳方法是使用以下工作流程:

  1. 安装先前的图表版本。

  2. 将您的发布升级到更新的图表版本。

  3. 删除发布。

  4. 安装更新的图表版本。

对每组值的排列组合重复此工作流程,以确保没有回归或意外的破坏性更改发生。

这些流程听起来很繁琐,但想象一下当维护多个不同的 Helm 图表时,图表开发人员需要进行仔细的测试,会增加额外的压力和维护工作。在维护多个 Helm 图表时,图表开发人员倾向于采用git monorepo 设计。当同一个存储库中包含多个不同的构件或模块时,该存储库被认为是 monorepo。

在 Helm 图表的情况下,monorepo 可能具有以下文件结构:

helm-charts/
  guestbook/
    Chart.yaml
    templates/
    README.md
    values.yaml
  redis/           # Contains the same file structure as 'guestbook'
  wordpress/       # Contains the same file structure as 'guestbook'
  README.md

在修改 Helm 图表时,应对其进行测试,以确保没有意外的破坏性更改发生。当修改图表时,其Chart.yaml文件中的version字段也应根据正确的SemVer版本进行增加,以表示所做更改的类型。SemVer版本遵循MAJOR.MINOR.PATCH版本编号格式。

使用以下列表作为如何增加SemVer版本的指南:

  • 如果您对图表进行了破坏性更改,请增加MAJOR版本。破坏性更改是指与先前的图表版本不兼容的更改。

  • 如果您正在添加一个功能但没有进行破坏性更改,请增加MINOR版本。如果您所做的更改与先前的图表版本兼容,应该增加此版本。

  • 如果您正在修复错误或安全漏洞,而不会导致破坏性更改,请增加PATCH版本。如果更改与先前的图表版本兼容,应该增加此版本。

没有良好编写的自动化,当修改图表并增加它们的版本时,确保测试图表会变得越来越困难,特别是在维护多个 Helm 图表的 monorepo 时。这一挑战促使 Helm 社区创建了一个名为ct的工具,以提供图表测试和维护的结构和自动化。我们接下来将讨论这个工具。

介绍图表测试项目

图表测试项目可以在github.com/helm/chart-testing找到,并设计用于针对 git monorepo 中的图表执行自动化的 linting、验证和测试。通过使用 git 检测已更改的图表来实现自动化测试。已更改的图表应该经历测试过程,而未更改的图表则无需进行测试。

该项目的 CLIct提供了四个主要命令:

  • lint:对已修改的图表进行 lint 和验证

  • install:安装和测试已修改的图表

  • lint-and-install:对已修改的图表进行 lint、安装和测试

  • list-changed:列出已修改的图表

list-changed命令不执行任何验证或测试,而lint-and-install命令将lintinstall命令结合起来,对已修改的图表进行lintinstalltest。它还会检查您是否已增加了每个图表的Chart.yaml文件中修改的图表的version字段,并对未增加版本但内容已修改的图表进行测试失败。这种验证有助于维护者根据所做更改的类型保持严格,以增加其图表版本。

除了检查图表版本外,图表测试还提供了为测试目的指定多个值文件的能力。在调用lintinstalllint-and-install命令时,图表测试会循环遍历每个测试values文件,以覆盖图表的默认值,并根据提供的不同值排列进行验证和测试。测试values文件写在一个名为ci/的文件夹下,以将这些值与图表的默认values.yaml文件分开,如下例文件结构所示:

guestbook/
  Chart.yaml
  ci/
    nodeport-service-values.yaml
    ingress-values.yaml
  templates/
  values.yaml

图表测试适用于ci/文件夹下的每个values文件,无论文件使用的名称如何。您可能会发现,根据被覆盖的值为每个values文件命名,以便维护者和贡献者可以理解文件内容,这是有帮助的。

您可能会经常使用的最常见的ct命令是lint-and-install命令。以下列出了该命令用于 lint、安装和测试在git monorepo 中修改的图表的步骤:

  1. 检测已修改的图表。

  2. 使用helm repo update命令更新本地 Helm 缓存。

  3. 使用helm dependency build命令下载每个修改后的图表的依赖项。

  4. 检查每个修改后的图表版本是否已递增。

  5. 对于在步骤 4中评估为true的每个图表,对图表和ci/文件夹下的每个values文件进行 lint。

  6. 对于在步骤 4中评估为true的每个图表,执行以下附加步骤:

在自动创建的命名空间中安装图表。

通过执行helm test来运行测试。

删除命名空间。

ci/文件夹下的每个values文件上重复。

正如您所看到的,该命令执行各种不同的步骤,以确保您的图表通过在单独的命名空间中安装和测试每个修改后的图表来正确进行 lint 和测试,重复该过程对ci/文件夹下定义的每个values文件。然而,默认情况下,lint-and-install命令不会通过从图表的旧版本升级来检查向后兼容性。可以通过添加--upgrade标志来启用此功能:

如果没有指示有破坏性变化,则--upgrade标志会修改上一组步骤中的步骤 6,通过运行以下步骤:

  1. 在自动创建的命名空间中安装图表的旧版本。

  2. 通过执行helm test来运行测试。

  3. 升级发布到修改后的图表版本并再次运行测试。

  4. 删除命名空间。

  5. 在新的自动创建的命名空间中安装修改后的图表版本。

  6. 通过执行helm test来运行测试。

  7. 再次使用相同的图表版本升级发布并重新运行测试。

  8. 删除命名空间。

  9. ci/文件夹下的每个values文件上重复。

建议您添加--upgrade标志,以便对 Helm 升级进行额外测试,并防止可能的回归。

重要提示:

--upgrade标志将不会生效,如果您已经增加了 Helm 图表的MAJOR版本,因为这表示您进行了破坏性更改,并且在此版本上进行就地升级将不会成功。

让我们在本地安装图表测试 CLI 及其依赖项,以便稍后可以看到此过程的实际操作。

安装图表测试工具

为了使用图表测试 CLI,您必须在本地机器上安装以下工具:

  • helm

  • git(版本2.17.0或更高)

  • yamllint

  • yamale

  • kubectl

图表测试在测试过程中使用这些工具。helmkubectl第二章中安装,准备 Kubernetes 和 Helm 环境,Git 在第五章中安装,构建您的第一个 Helm 图表,yamllint 在本章开头安装。如果您迄今为止一直在跟随本书,现在您应该需要安装的唯一先决条件工具是 Yamale,这是图表测试用来验证您的图表的Chart.yaml文件与Chart.yaml模式文件相匹配的工具。

Yamale 可以使用pip软件包管理器安装,如下所示:

$ pip install yamale --user

您也可以通过从github.com/23andMe/Yamale/archive/master.zip手动下载存档来安装 Yamale。

下载后,解压缩存档并运行安装脚本:

$ python setup.py install

请注意,如果您使用下载的存档安装工具,您可能需要以提升的权限运行setup.py脚本,例如在 macOS 和 Linux 上作为管理员或 root 用户。

安装所需的工具后,您应该从项目的 GitHub 发布页面github.com/helm/chart-testing/releases下载图表测试工具。每个发布版本都包含一个Assets部分,其中列出了存档文件。

下载与本地机器平台类型对应的存档。本书使用的版本是v3.0.0-beta.1

图 6.5 - GitHub 上的图表测试发布页面

图 6.5 - GitHub 上的图表测试发布页面

从 GitHub 发布页面下载适当的文件后,解压缩图表测试版本。解压缩后,您将看到以下内容:

LICENSE
README.md
etc/chart_schema.yaml
etc/lintconf.yaml
ct

您可以删除LICENSEREADME.md文件,因为它们是不需要的。

etc/chart_schema.yamletc/lintconf.yaml文件应移动到本地计算机上的$HOME/.ct//etc/ct/位置。ct文件应移动到由系统的PATH变量管理的位置:

$ mkdir $HOME/.ct
$ mv $HOME/Downloads/etc/* $HOME/.ct/
$ mv $HOME/Downloads/ct /usr/local/bin/

现在,所有必需的工具都已安装。在本示例中,我们将在本地对 Packt 存储库进行更改,并使用图表测试来对修改后的图表进行 lint 和安装。

如果您尚未将存储库克隆到本地计算机,请立即执行此操作:

$ git clone https://github.com/PacktPublishing/-Learn-Helm Learn-Helm

克隆后,您可能会注意到该存储库在顶层有一个名为ct.yaml的文件,其中包含以下内容:

chart-dirs:
  - helm-charts/charts
chart-repos:
  - bitnami=https://charts.bitnami.com

该文件的chart-dirs字段指示ct,相对于ct.yaml文件,helm-charts/charts目录是图表 monorepo 的根目录。chart-repos字段提供了应该运行helm repo add的存储库列表,以确保它能够下载依赖项。

还有许多其他配置可以添加到此文件中,这些将在此时不予讨论,但可以在图表测试文档中查看。每次调用ct命令都会引用ct.yaml文件。

现在,工具已安装,并且 Packt 存储库已克隆,让我们通过执行lint-and-install命令来测试ct工具。

运行图表测试 lint-and-install 命令

lint-and-install命令针对Learn-Helm/helm-charts/charts下包含的三个 Helm 图表使用:

  • guestbook:这是您在上一章中编写的 Guestbook 图表。

  • nginx:这是我们为演示目的包含的另一个 Helm 图表。通过运行helm create命令创建的此图表用于部署nginx反向代理。

要运行测试,首先导航到Learn-Helm存储库的顶层:

$ cd $LEARN_HELM_LOCATION
$ ls
ct.yaml  guestbook-operator  helm-charts  jenkins  LICENSE  nginx-cd  README.md

ct.yaml文件通过chart-dirs字段显示了图表 monorepo 的位置,因此您可以直接从顶层运行ct lint-and-install命令:

$ ct lint-and-install

运行此命令后,您将在输出的末尾看到以下消息显示:

图 6.6 - 当图表没有被修改时的图表测试 lint-and-install 输出

图 6.6 - 当图表没有被修改时的图表测试lint-and-install输出

由于这个存储库中的图表都没有被修改,ct没有对您的图表执行任何操作。我们应该至少修改其中一个图表,以便看到lint-and-install过程发生。修改应该发生在master之外的分支上,因此应该通过执行以下命令创建一个名为chart-testing-example的新分支:

$ git checkout -b chart-testing-example

修改可以是大的或小的;对于这个例子,我们将简单地修改每个图表的Chart.yaml文件。修改Learn-Helm/helm-charts/charts/guestbook/Chart.yaml文件的description字段如下所示:

description: Used to deploy the Guestbook application

先前,这个值是A Helm chart for Kubernetes

修改Learn-Helm/helm-charts/charts/nginx/Chart.yaml文件的description字段如下所示:

description: Deploys an NGINX instance to Kubernetes

先前,这个值是A Helm chart for Kubernetes。通过运行git status命令验证上次git提交后两个图表是否已被修改:

图 6.7 - 在修改了两个图表后的 git status 输出

图 6.7 - 在修改了两个图表后的git status输出

您应该看到guestbooknginx图表的变化。修改了这些图表后,尝试再次运行lint-and-install命令:

$ ct lint-and-install

这次,ct确定了这个 monorepo 中两个图表是否发生了更改,如下所示的输出:

图 6.8 - 指示对 guestbook 和 nginx 图表的更改的消息

图 6.8 - 指示对guestbooknginx图表的更改的消息

然而,这个过程后来会失败,因为这两个图表的版本都没有被修改:

图 6.9 - 当没有图表更改时的输出

图 6.9 - 当没有图表更改时的输出

这可以通过增加guestbooknginx图表的版本来解决。由于这个更改没有引入新功能,我们将增加PATCH版本。在各自的Chart.yaml文件中将两个图表的版本都修改为version 1.0.1

version: 1.1.0

通过运行git diff命令确保每个图表都已进行了此更改。如果在输出中看到每个版本的修改,请继续再次运行lint-and-install命令:

$ ct lint-and-install

现在图表版本已经增加,lint-and-install命令将遵循完整的图表测试工作流程。您将看到每个修改的图表都会被 linted 并部署到自动创建的命名空间中。一旦部署的应用程序的 pod 被报告为就绪状态,ct将自动运行每个图表的测试用例,这些测试用例由带有helm.sh/hook: test注释的资源表示。图表测试还将打印每个测试 pod 的日志,以及命名空间事件。

您可能会注意到,在lint-and-install输出中,nginx图表部署了两次,而guestbook图表只部署和测试了一次。这是因为nginx图表有一个位于Learn-Helm/helm-charts/charts/nginx/ci/ci/文件夹,其中包含两个不同的values文件。ci/文件夹中的values文件将被图表测试迭代,该测试将安装与values文件数量相同的图表,以确保每个值组合都能成功安装。guestbook图表不包括ci/文件夹,因此此图表只安装了一次。

这可以在lint-and-install输出的以下行中观察到:

Linting chart with values file 'nginx/ci/clusterip-values.yaml'...
Linting chart with values file 'nginx/ci/nodeport-values.yaml'...
Installing chart with values file 'nginx/ci/clusterip-values.yaml'...
Installing chart with values file 'nginx/ci/nodeport-values.yaml'...

虽然该命令对于测试两个图表的功能很有用,但它并未验证对新版本的升级是否成功。

为此,我们需要向lint-and-install命令提供--upgrade标志。再次尝试运行此命令,但这次使用--upgrade标志:

$ ct lint-and-install --upgrade

这次,每个ci/下的values文件将进行原地升级。这可以在输出中看到如下:

Testing upgrades of chart 'guestbook => (version: '1.0.1', path: 'guestbook')' relative to previous revision 'guestbook => (version: '1.0.0', path: 'ct_previous_revision216728160/guestbook')'...

请记住,只有在版本之间的MAJOR版本相同时,原地升级才会被测试。如果您使用--upgrade标志,但未更改MAJOR版本,您将看到类似以下的消息:

Skipping upgrade test of 'guestbook => (version: '2.0.0', path: 'helm-charts/charts/guestbook')' because: 1 error occurred:
	* 2.0.0 does not have same major version as 1.0.0

现在,通过了解如何使用图表测试对 Helm 图表进行强大的测试,我们将通过清理您的minikube环境来结束。

清理

如果您已经完成了本章中描述的示例,可以从您的minikube集群中删除chapter6命名空间:

$ kubectl delete ns chapter6

最后,通过运行minikube stop关闭您的minikube集群。

摘要

在本章中,您了解了可以应用于测试 Helm 图表的不同方法。测试图表的最基本方法是针对本地图表目录运行helm template命令,以确定其资源是否正确生成。您还可以使用helm lint命令来确保您的图表遵循正确的格式,并且可以使用yamllint命令来检查图表中使用的 YAML 样式。

除了本地模板化和检查外,您还可以使用helm test命令和ct工具在 Kubernetes 环境中执行实时测试。除了执行图表测试外,图表测试还提供了使图表开发人员更容易在 monorepo 中维护 Helm 图表的功能。

在下一章中,您将了解 Helm 如何在持续集成/持续交付CI/CD)和 GitOps 设置中使用,从图表开发人员构建和测试 Helm 图表的角度,以及从使用 Helm 将应用程序部署到 Kubernetes 的最终用户的角度。

进一步阅读

有关helm templatehelm lint命令的更多信息,请参阅以下资源:helm.sh/docs/helm/helm_template/

Helm 文档中的以下页面讨论了图表测试和helm test命令:helm.sh/docs/topics/chart_tests/

问题

  1. helm template命令的目的是什么?它与helm lint命令有何不同?

  2. 在将图表模板安装到 Kubernetes 之前,您可以做什么来验证它们?

  3. 可以利用哪个工具来检查您的 YAML 资源的样式?

  4. 如何创建图表测试?如何执行图表测试?

  5. ct工具为 Helm 内置的测试功能带来了什么附加价值?

  6. 在使用ct工具时,ci/文件夹的目的是什么?

  7. --upgrade 标志如何改变 ct lint-and-install 命令的行为?

第三部分:高级部署模式

本节将在基本概念的基础上进行构建,并将教你更多关于使用 Helm 进行应用管理的高级概念和可能性。

本节包括以下章节:

第七章,使用 CI/CD 和 GitOps 自动化 Helm 流程

第八章,使用 Operator Framework 与 Helm

第九章,Helm 安全考虑

第七章:使用 CI/CD 和 GitOps 自动化 Helm 流程

在本书中,我们迄今为止讨论了两个高级流程。首先,我们探讨了使用 Helm 作为最终用户,利用 Helm 作为软件包管理器将各种复杂性的应用程序部署到 Kubernetes。其次,我们探讨了作为图表开发人员开发和测试 Helm 图表,这涉及将 Kubernetes 的复杂性封装在 Helm 图表中,并对图表进行测试,以确保所需的功能成功交付给最终用户。

这两个流程都涉及调用各种不同的 Helm CLI 命令。这些 Helm CLI 命令在执行各自的任务时非常有效,但需要从命令行手动调用。手动调用在管理多个不同的图表或应用程序时可能会成为一个痛点,并且可能会使大型企业难以扩展。因此,我们应该探索提供额外自动化的替代选项,以在 Helm 已经提供的基础上提供额外的自动化。在本章中,我们将调查与持续集成持续交付CI/CD)以及GitOps相关的概念,这些方法可以自动调用 Helm CLI 以及其他命令,以执行针对 Git 存储库的自动化工作流。这些工作流可以用于使用 Helm 自动部署应用程序,并在图表开发生命周期中构建、测试和打包 Helm 图表。

在本章中,我们将涵盖以下主题:

  • 理解 CI/CD 和 GitOps

  • 设置我们的环境

  • 创建用于构建 Helm 图表的 CI 流水线

  • 使用 Helm 创建 CD 流水线以部署应用程序

  • 清理

技术要求

本章需要您在本地机器上安装以下技术:

  • Minikube

  • Helm

  • kubectl

  • Git

除了这些工具,您还应该在 GitHub 的 Packt 存储库中找到与本章中使用的示例相关的资源,网址为github.com/PacktPublishing/-Learn-Helm。本存储库将在本章中被引用。

理解 CI/CD 和 GitOps

到目前为止,我们已经讨论了许多与 Helm 开发相关的关键概念——构建、测试和部署。然而,我们的探索仅限于手动配置和调用 Helm CLI。当您希望将图表移入类似生产环境时,有几个问题需要考虑,包括以下内容:

  • 我如何确保图表开发和部署的最佳实践得到执行?

  • 合作者参与开发和部署过程的影响是什么?

这些观点适用于任何软件项目,不仅适用于 Helm 图表开发。虽然我们已经涵盖了许多最佳实践,但在接纳新的合作者时,他们可能对这些主题没有相同的理解,或者没有执行这些关键步骤的纪律。通过使用自动化和可重复的流程,诸如 CI/CD 之类的概念已经被建立起来,以解决其中的一些挑战。

CI/CD

需要一个自动化的软件开发流程,每次软件发生变化时都能遵循,这导致了 CI 的产生。CI 不仅确保了最佳实践的遵守,而且还有助于消除许多开发人员面临的常见挑战,正如“它在我的机器上可以运行”所体现的。我们之前讨论过的一个因素是使用版本控制系统,比如git,来存储源代码。通常,每个用户都会有自己独立的源代码副本,这使得在增加贡献者时难以管理代码库。

CI 是通过使用自动化工具来正确启用的,其中源代码在发生更改时经历一组预定的步骤。对于正确的自动化工具的需求导致了专门为此目的设计的软件的兴起。一些 CI 工具的例子包括 Jenkins、TeamCity 和 Bamboo,以及各种基于软件即服务(SaaS)的解决方案。通过将任务的责任转移到第三方组件,开发人员更有可能频繁提交代码,项目经理可以对团队的技能和产品的健壮性感到自信。

大多数这些工具中都具有的一个关键特性是能够及时通知项目当前状态的能力。通过使用持续集成,不是在软件开发周期的后期才发现破坏性变化,而是在变化被整合后立即执行流程并向相关方发送通知。通过利用快速通知,它为引入变化的用户提供了解决问题的机会,而兴趣所在的领域正处于头脑前沿,而不是在交付过程的后期,当时他们可能已经在其他地方忙碌。

将 CI 的许多概念应用于整个软件交付生命周期,随着应用程序向生产环境推进,导致了 CD 的产生。CD 是一组定义的步骤,编写用于推进软件通过发布过程(更常被称为流水线)。CI 和 CD 通常一起配对,因为执行 CI 的许多相同引擎也可以实现 CD。CD 在许多组织中得到了接受和流行,这些组织强制执行适当的变更控制,并要求批准,以便软件发布过程能够进展到下一个阶段。由于 CI/CD 周围的许多概念都是以可重复的方式自动化的,团队可以寻求完全消除手动批准步骤的需要,一旦他们确信已经建立了可靠的框架。

在没有任何人为干预的情况下实施完全自动化的构建、测试、部署和发布过程的过程被称为持续部署。虽然许多软件项目从未完全实现持续部署,但通过实施 CI/CD 强调的概念,团队能够更快地产生真正的业务价值。在下一节中,我们将介绍 GitOps 作为改进应用程序及其配置管理的机制。

将 CI/CD 提升到下一个级别,使用 GitOps

Kubernetes 是一个支持声明式配置的平台。与任何编程语言编写的应用程序(如 Python、Golang 或 Java)通过 CI/CD 流水线的方式一样,Kubernetes 清单也可以实现许多相同的模式。清单也应该存储在源代码仓库(如 Git)中,并且可以经历相同类型的构建、测试和部署实践。在 Git 存储库中管理 Kubernetes 集群配置的生命周期的流行度上升,然后以自动化的方式应用这些资源,导致了 GitOps 的概念。GitOps 最早由软件公司 WeaveWorks 在 2017 年首次引入,自那时以来,作为管理 Kubernetes 配置的一种方式,GitOps 的流行度一直在增加。虽然 GitOps 在 Kubernetes 的背景下最为人所知,但其原则可以应用于任何云原生环境。

与 CI/CD 类似,已经开发了工具来管理 GitOps 流程。这些包括 Intuit 的 ArgoCD 和 WeaveWorks 的 Flux,这个组织负责创造 GitOps 这个术语。您不需要使用专门设计用于 GitOps 的工具,因为任何自动化工具,特别是设计用于管理 CI/CD 流程的工具,都可以被利用。传统 CI/CD 工具和专为 GitOps 设计的工具之间的关键区别在于 GitOps 工具能够不断观察 Kubernetes 集群的状态,并在当前状态与 Git 存储中定义的期望状态不匹配时应用所需的配置。这些工具利用了 Kubernetes 本身的控制器模式。

由于 Helm 图表最终被渲染为 Kubernetes 资源,它们也可以用于参与 GitOps 流程,并且许多前述的 GitOps 工具本身原生支持 Helm。我们将在本章的其余部分中看到如何利用 CI/CD 和 GitOps 来使用 Helm 图表,利用 Jenkins 作为 CI 和 CD 的首选工具。

设置我们的环境

在本章中,我们将开发两种不同的流水线,以演示如何自动化 Helm 周围的不同流程。

开始设置本地环境的步骤如下:

  1. 首先,鉴于本章的内存要求增加,如果在[第二章](B15458_02_Final_JM_ePub.xhtml#_idTextAnchor098)中未使用 4g 内存初始化minikube集群,则应删除该集群并使用 4g 内存重新创建。可以通过运行以下命令来完成:
$ minikube delete
$ minikube start --memory=4g
  1. Minikube 启动后,创建一个名为chapter7的新命名空间:
$ kubectl create namespace chapter7

此外,您还应该 fork Packt 存储库,这将允许您根据这些练习中描述的步骤对存储库进行修改:

  1. 通过单击 Git 存储库上的Fork按钮来创建 Packt 存储库的分支:图 7.1 - 选择 Fork 按钮来创建 Packt 存储库的分支

图 7.1 - 选择 Fork 按钮来创建 Packt 存储库的分支

您必须拥有 GitHub 帐户才能 fork 存储库。创建新帐户的过程在[第五章](B15458_05_Final_JM_ePub.xhtml#_idTextAnchor265)中有描述,构建您的第一个 Helm 图表

  1. 创建 Packt 存储库的分支后,通过运行以下命令将此分支克隆到本地计算机:
$ git clone https://github.com/$GITHUB_USERNAME/-Learn-Helm.git Learn-Helm

除了创建 Packt 存储库的分支外,您可能还希望从您的 Helm 存储库中删除guestbook图表,该图表是从您的 GitHub Pages 存储库中提供的,我们在[第五章](B15458_05_Final_JM_ePub.xhtml#_idTextAnchor265)中创建了构建您的第一个 Helm 图表。虽然这并不是绝对必要的,但本章中的示例将假定一个干净的状态。

使用以下步骤从图表存储库中删除此图表:

  1. 导航到 Helm 图表存储库的本地克隆。您会记得,我们建议的图表存储库的名称是Learn-Helm-Chart-Repository,因此在本章中我们将使用这个名称来引用您的基于 GitHub Pages 的图表存储库:
$ cd $LEARN_HELM_CHART_REPOSITORY_DIR
$ ls
guestbook-1.0.0.tgz   index.yaml   README.md
  1. 从图表存储库中删除guestbook-1.0.0.tgzindex.yaml文件:
$ rm guestbook-1.0.0.tgz index.yaml
$ ls
README.md
  1. 将这些更改推送到您的远程存储库:
$ git add --all
$ git commit -m 'Preparing for chapter 7'
$ git push origin master
  1. 您应该能够在 GitHub 中确认您的图表和索引文件已被删除,只留下README.md文件:

图 7.2 - 您在图表存储库中应该看到的唯一文件是 README.md 文件

](image/Figure_7.2.jpg)

图 7.2 - 您在图表存储库中应该看到的唯一文件是 README.md 文件

现在您已经启动了 Minikube,创建了 Packt 存储库的一个分支,并从Learn-Helm-Chart-Repository中删除了 Guestbook 图表,让我们开始学习如何创建一个 CI 流水线来发布 Helm 图表。

创建一个 CI 流水线来构建 Helm 图表

CI 的概念可以应用于构建、测试、打包和发布 Helm 图表到图表存储库的图表开发人员的视角。在本节中,我们将描述使用端到端 CI 流水线来简化这个过程可能是什么样子,以及如何通过构建一个示例流水线来引导您。第一步是设计示例流水线所需的组件。

设计流水线

在之前的章节中,开发 Helm 图表主要是一个手动过程。虽然 Helm 提供了在 Kubernetes 集群中创建test钩子的自动化,但在代码更改后手动执行helm linthelm testct lint-and-install命令以确保测试仍然通过。一旦代码更改后继续通过 linting 和测试,图表就可以通过运行helm package命令进行打包。如果使用 GitHub Pages 存储库(比如在第五章中创建的那个,构建您的第一个 Helm 图表),则通过运行helm repo index创建index.yaml文件,并将index.yaml文件以及打包的图表推送到 GitHub 存储库。

虽然手动调用每个命令当然是可行的,但随着您开发更多的 Helm 图表或添加更多的贡献者,这种工作流程可能变得越来越难以维持。使用手动工作流程,很容易允许未经测试的更改被应用到您的图表中,并且很难确保贡献者遵守测试和贡献指南。幸运的是,通过创建一个自动化发布流程的 CI 流水线,可以避免这些问题。

以下步骤概述了使用本书中讨论的命令和工具来进行示例 CI 工作流。它将假定生成的图表保存在 GitHub Pages 存储库中:

  1. 图表开发人员对git monorepo 中的一个图表或一组图表进行代码更改。

  2. 开发人员将更改推送到远程存储库。

  3. 已修改的图表会通过运行ct lintct install命令在 Kubernetes 命名空间中自动进行 linting 和测试。

  4. 如果 linting 和测试成功,图表将自动使用helm package命令打包。

  5. index.yaml文件将使用helm repo index命令自动生成。

  6. 打包的图表和更新的index.yaml文件将自动推送到存储库。它们将被推送到stablestaging,具体取决于作业运行的分支。

在下一节中,我们将使用Jenkins执行这个过程。让我们首先了解一下 Jenkins 是什么以及它是如何工作的。

了解 Jenkins

Jenkins 是一个用于执行自动化任务和工作流程的开源服务器。它通常用于通过 Jenkins 的管道即代码功能创建 CI/CD 流水线,该功能在一个名为Jenkinsfile的文件中编写,该文件定义了 Jenkins 流水线。

Jenkins 流水线是使用 Groovy领域特定语言DSL)编写的。Groovy 是一种类似于 Java 的语言,但与 Java 不同的是,它可以用作面向对象的脚本语言,适合编写易于阅读的自动化。在本章中,我们将带您了解两个已经为您准备好的Jenkinsfile文件。您不需要有任何关于从头开始编写Jenkinsfile文件的经验,因为深入研究 Jenkins 超出了本书的范围。话虽如此,到本章结束时,您应该能够将学到的概念应用到您选择的自动化工具中。虽然本章中介绍了 Jenkins,但其概念也可以应用于任何其他自动化工具。

当创建一个Jenkinsfile文件时,工作流程的一组定义的步骤将在 Jenkins 服务器本身上执行,或者委托给运行该作业的单独代理。还可以通过自动调度 Jenkins 代理作为单独的 Pod 集成额外的功能,每当启动构建时,简化代理的创建和管理。代理完成后,可以配置为自动终止,以便下一个构建可以在一个新的、干净的 Pod 中运行。在本章中,我们将使用 Jenkins 代理运行示例流水线。

Jenkins 还非常适合 GitOps 的概念,因为它提供了扫描源代码存储库以查找Jenkinsfile文件的能力。对于每个包含Jenkinsfile文件的分支,将自动配置一个新作业,该作业将从所需分支克隆存储库开始。这样可以很容易地测试新功能和修复,因为新作业可以自动创建并与其相应的分支一起使用。

在对 Jenkins 有基本了解之后,让我们在 Minikube 环境中安装 Jenkins。

安装 Jenkins

与许多通常部署在 Kubernetes 上的应用程序一样,Jenkins 可以使用来自 Helm Hub 的许多不同社区 Helm 图之一进行部署。在本章中,我们将使用来自Codecentric软件开发公司的 Jenkins Helm 图。添加codecentric图存储库以开始安装 Codecentric Jenkins Helm 图:

$ helm repo add codecentric https://codecentric.github.io/helm-charts

在预期的与 Kubernetes 相关的值中,例如配置资源限制和服务类型,codecentric Jenkins Helm 图包含其他用于自动配置不同 Jenkins 组件的 Jenkins 相关值。

由于配置这些值需要对超出本书范围的 Jenkins 有更深入的了解,因此为您提供了一个values文件,该文件将自动准备以下 Jenkins 配置:

  • 添加未包含在基本镜像中的相关 Jenkins 插件。

  • 配置所需的凭据以与 GitHub 进行身份验证。

  • 配置专门设计用于测试和安装 Helm 图的 Jenkins 代理。

  • 配置 Jenkins 以根据Jenkinsfile文件的存在自动创建新作业。

  • 跳过通常在新安装启动时发生的手动提示。

  • 禁用身份验证,以简化本章中对 Jenkins 的访问。

values文件还将配置以下与 Kubernetes 相关的细节:

  • 针对 Jenkins 服务器设置资源限制。

  • 将 Jenkins 服务类型设置为NodePort

  • 创建 Jenkins 和 Jenkins 代理在 Kubernetes 环境中运行作业和部署 Helm 图所需的 ServiceAccounts 和 RBAC 规则。

  • 将 Jenkins 的PersistentVolumeClaim大小设置为2Gi

该 values 文件可在github.com/PacktPublishing/-Learn-Helm/blob/master/jenkins/values.yaml找到。浏览这些值的内容时,您可能会注意到fileContent下定义的配置包含 Go 模板。该值的开头如下所示:

图 7.3 - Jenkins Helm 图表的 values.yaml 文件包含 Go 模板

图 7.3 - Jenkins Helm 图表的values.yaml文件包含 Go 模板

虽然 Go 模板通常在values.yaml文件中无效,但 Codecentric Jenkins Helm 图表向模板函数tpl提供了fileContent配置。在模板方面,这看起来如下所示:

{{- tpl .Values.fileContent }}

tpl命令将解析fileContent值作为 Go 模板,使其可以包含 Go 模板,即使它是在values.yaml文件中定义的。

在本章中,fileContent配置中定义的 Go 模板有助于确保 Jenkins 安装方式符合本章的要求。换句话说,模板将需要在安装过程中提供以下附加值:

  • githubUsername:GitHub 用户名

  • githubPassword:GitHub 密码

  • githubForkUrl:您的 Packt 存储库分支的 URL,该分支在本章的技术要求部分中提取

  • githubPagesRepoUrl:您的 GitHub Pages Helm 存储库的 URL,该存储库是在第五章结束时创建的,构建您的第一个 Helm 图表

请注意,这不是您静态站点的 URL,而是 GitHub 存储库本身的 URL,例如,https://github.com/$GITHUB_USERNAME/Learn-Helm-Chart-Repository.git。

前述列表中描述的四个值可以使用--set标志提供,也可以使用--values标志从额外的values文件中提供。如果选择创建单独的values文件,请确保不要将该文件提交和推送到源代码控制,因为它包含敏感信息。本章的示例偏向于使用--set标志来提供这四个值。除了上述描述的值之外,还应该使用--values标志提供 Packt 存储库中包含的values.yaml文件。

使用以下示例作为参考,使用helm install命令安装您的Jenkins实例:

$ helm install jenkins codecentric/jenkins \
  -n chapter7 --version 1.5.1 \
  --values Learn-Helm/jenkins/values.yaml \
  --set githubUsername=$GITHUB_USERNAME \
  --set githubPassword=$GITHUB_PASSWORD \
  --set githubForkUrl=https://github.com/$GITHUB_USERNAME/-Learn-Helm.git \
  --set githubPagesRepoUrl=https://github.com/$GITHUB_USERNAME/Learn-Helm-Chart-Repository.git

您可以通过对chapter7命名空间中的 Pod 运行监视来监视安装。

$ kubectl get Pods -n chapter7 -w

请注意,在极少数情况下,您的 Pod 可能会在Init:0/1阶段卡住。如果外部依赖出现可用性问题,比如 Jenkins 插件站点及其镜像正在经历停机时间,就会发生这种情况。如果发生这种情况,请尝试在几分钟后删除您的发布并重新安装它。

一旦您的 Jenkins Pod 在READY列下报告1/1,您的Jenkins实例就可以被访问了。复制并粘贴显示的安装后说明的以下内容以显示 Jenkins URL:

$ export NODE_PORT=$(kubectl get service --namespace chapter7 -o jsonpath='{.spec.ports[0].nodePort}' jenkins-master)
$ export NODE_IP=$(kubectl get nodes --namespace chapter7 -o jsonpath='{.items[0].status.addresses[0].address}')
echo "http://$NODE_IP:$NODE_PORT"

当您访问 Jenkins 时,您的首页应该看起来类似于以下屏幕截图:

图 7.4-运行 Helm 安装后的 Jenkins 主页

图 7.4-运行 Helm 安装后的 Jenkins 主页

如果图表安装正确,您会注意到一个名为测试和发布 Helm 图表的新作业被创建。在页面的左下角,您会注意到构建执行器状态面板,用于提供当前正在运行的活动作业的概览。当作业被创建时,将自动触发该作业,这就是为什么当您登录到 Jenkins 实例时会看到它正在运行。

现在 Jenkins 已安装并且其前端已经验证,让我们浏览一下 Packt 存储库中的示例Jenkinsfile文件,以了解 CI 管道的工作原理。请注意,本章节中我们不会显示Jenkinsfile文件的全部内容,因为我们只想简单地突出感兴趣的关键领域。文件的全部内容可以在 Packt 存储库中查看github.com/PacktPublishing/-Learn-Helm/blob/master/helm-charts/Jenkinsfile

理解管道

触发“测试和部署 Helm 图表”作业时发生的第一件事是创建一个新的 Jenkins 代理。通过利用Learn-Helm/jenkins/values.yaml中提供的值,Jenkins 图表安装会自动配置一个名为chart-testing-agent的 Jenkins 代理。以下一行指定该代理为此Jenkinsfile文件的代理:

agent { label 'chart-testing-agent' }

此代理由 Jenkins 图表值配置,使用 Helm 社区提供的图表测试图像运行。位于quay.io/helmpack/chart-testing的图表测试图像包含了第六章中讨论的许多工具,测试 Helm 图表。具体来说,该图像包含以下工具:

  • helm

  • ct

  • yamllint

  • yamale

  • git

  • Kubectl

由于此图像包含测试 Helm 图表所需的所有工具,因此可以将其用作执行 Helm 图表的 CI 的主要图像。

当 Jenkins 代理运行时,它会使用githubUsernamegithubPassword进行身份验证,隐式地克隆您的 GitHub 分支,由githubForkUrl值指定。Jenkins 会自动执行此操作,因此不需要在Jenkinsfile文件中指定任何代码来执行此操作。

Jenkins 代理克隆您的存储库后,将开始执行Jenkinsfile文件中定义的阶段。阶段是管道中的逻辑分组,可以帮助可视化高级步骤。将执行的第一个阶段是 lint 阶段,其中包含以下命令:

sh 'ct lint'

前述命令中的sh部分是用于运行 bash shell 或脚本并调用ct工具的lint子命令。您会记得,此命令会针对已修改的所有图表的Chart.yamlvalues.yaml文件对主分支进行检查,我们在第六章中已经讨论过这一点,测试 Helm 图表

如果 linting 成功,流水线将继续进行到测试阶段,并执行以下命令:

sh 'ct install --upgrade'

这个命令也应该很熟悉。它会从主分支上的版本安装每个修改的图表,并执行定义的测试套件。它还确保从上一个版本的任何升级都成功,有助于防止回归。

请注意,前两个阶段可以通过运行单个ct lint-and-install --upgrade命令来合并。这仍然会导致有效的流水线,但这个示例将它们分成单独的阶段,可以更好地可视化执行的操作。

如果测试阶段成功,流水线将继续进行到打包图表阶段,执行以下命令:

sh 'helm package --dependency-update helm-charts/charts/*'

在这个阶段,命令将简单地打包helm-charts/charts文件夹下包含的每个图表。它还将更新和下载每个声明的依赖项。

如果打包成功,管道将继续进行到最后一个阶段,称为推送图表到存储库。这是最复杂的阶段,所以我们将把它分解成较小的步骤。第一步可以在这里看到:

// Clone GitHub Pages repository to a folder called 'chart-repo'
sh "git clone ${env.GITHUB_PAGES_REPO_URL} chart-repo"
// Determine if these charts should be pushed to 'stable' or 'staging' based on the branch
def repoType
if (env.BRANCH_NAME == 'master') {
  repoType = 'stable'
} else {
  repoType = 'staging'
}
// Create the corresponding 'stable' or 'staging' folder if it does not exist
def files = sh(script: 'ls chart-repo', returnStdout: true)
if (!files.contains(repoType)) {
  sh "mkdir chart-repo/${repoType}"
}

由于 Helm 图表存储库是一个单独的 GitHub Pages 存储库,我们必须克隆该存储库,以便我们可以添加新的图表并推送更改。一旦克隆了 GitHub Pages 存储库,就会设置一个名为repoType的变量,具体取决于 CI/CD 管道针对的分支。该变量用于确定前一阶段打包的图表应该推送到stablestaging图表存储库。

对于这个管道,stable意味着图表已经经过测试、验证并合并到主分支中。staging意味着图表正在开发中,尚未合并到主分支,也尚未正式发布。或者,您可以在切换到发布分支时在稳定存储库中发布图表,但是在这个例子中,我们将采用假设每次合并到主分支都是一个新发布的前一种方法。

stablestaging作为两个单独的图表存储库提供;这可以通过在 GitHub Pages 存储库的顶层创建两个单独的目录来完成:

Learn-Helm-Repository/
  stable/
  staging/

然后,稳定和暂存文件夹包含它们自己的index.yaml文件,以区分它们作为单独的图表存储库。

为了方便起见,前述管道摘录的最后一部分会在管道执行依赖于其存在的分支时自动创建stablestaging文件夹。

现在确定了图表应该推送到的存储库类型,我们继续进行管道的下一个阶段,如下所示:

// Move charts from the packaged-charts folder to the corresponding 'stable' or 'staging' folder
sh "mv packaged-charts/*.tgz chart-repo/${repoType}"
// Generate the updated index.yaml
sh "helm repo index chart-repo/${repoType}"
// Update git config details
sh "git config --global user.email 'chartrepo-robot@example.com'"
sh "git config --global user.name 'chartrepo-robot'"

第一条命令将从前一阶段复制每个打包的图表到stablestaging文件夹。接下来,使用helm repo index命令更新stablestagingindex.yaml文件,以反映已更改或添加的图表。

需要记住的一点是,如果我们使用不同的图表存储库解决方案,比如ChartMuseum(由 Helm 社区维护的图表存储库解决方案),则不需要使用helm repo index命令,因为当 ChartMuseum 接收到新的打包 Helm 图表时,index.yaml文件会自动更新。对于不会自动计算index.yaml文件的实现,比如 GitHub Pages,helm repo index命令是必要的,正如我们在这个管道中所看到的。

前面片段的最后两个命令设置了gitusernameemail,这些是推送内容到git存储库所必需的。在本例中,我们将用户名设置为chartrepo-robot,以表示 CI/CD 过程促进了git交互,我们将设置邮箱为(mailto:chartrepo-robot@example.com)作为示例值。您可能希望邮箱代表负责维护图表存储库的组织。

最后一步是推送更改。这个操作在最终的管道片段中被捕获,如下所示:

// Add and commit the changes
sh 'git add --all'
sh "git commit -m 'pushing charts from branch ${env.BRANCH_NAME}'"
withCredentials([usernameColonPassword(credentialsId: 'github-auth', variable: 'USERPASS')]) {
    script {
    // Inject GitHub auth and push to the master branch, where the charts are being served
    def authRepo = env.GITHUB_PAGES_REPO_URL.replace('://', "://${USERPASS}@")
    sh "git push ${authRepo} master"
    }
}

打包的图表首先使用git addgit commit命令添加和提交。接下来,使用git push命令对存储库进行推送,使用名为github-auth的凭据。这个凭据是在安装过程中从githubUsernamegithubPassword值创建的。github-auth凭据允许您安全地引用这些机密,而不会在管道代码中以明文形式打印出来。

请注意,Helm 社区发布了一个名为Chart Releaser的工具(github.com/helm/chart-releaser),可以作为使用helm repo index命令生成index.yaml文件并使用git push上传到 GitHub 的替代方案。Chart Releaser工具旨在通过管理包含在 GitHub Pages 中的 Helm 图表来抽象一些额外的复杂性。

我们决定在本章中不使用这个工具来实现管道,因为在撰写本文时,Chart Releaser不支持 Helm 3。

既然我们已经概述了 CI 管道,让我们通过一个示例执行来运行一遍。

运行管道

正如我们之前讨论的,当我们安装 Jenkins 时,这个流水线的第一次运行实际上是自动触发的。该作业针对主分支运行,并且可以通过单击 Jenkins 登陆页面上的测试和发布 Helm Charts链接来查看。您会注意到有一个成功的作业针对主分支运行了:

图 7.5 - 流水线的第一次运行

图 7.5 - 流水线的第一次运行

Jenkins 中的每个流水线构建都有一个关联的日志,其中包含执行的输出。您可以通过在左侧选择蓝色圆圈旁边的#1链接,然后在下一个屏幕上选择控制台输出来访问此构建的日志。此构建的日志显示第一个阶段Lint成功,显示了这条消息:

All charts linted successfully
----------------------------------
No chart changes detected.

这是我们所期望的,因为从主分支的角度来看,没有任何图表发生变化。在安装阶段也可以看到类似的输出:

All charts installed successfully
-----------------------------------
No chart changes detected.

因为 Lint 和 Install 阶段都没有错误,所以流水线继续到了 Package Charts 阶段。在这里,您可以查看输出:

+ helm package --dependency-update helm-charts/charts/guestbook helm-charts/charts/nginx
Successfully packaged chart and saved it to: /home/jenkins/agent/workspace/t_and_Release_Helm_Charts_master/guestbook-1.0.0.tgz
Successfully packaged chart and saved it to: /home/jenkins/agent/workspace/t_and_Release_Helm_Charts_master/nginx-1.0.0.tgz

最后,流水线通过克隆您的 GitHub Pages 存储库,在其中创建一个stable文件夹,将打包的图表复制到stable文件夹中,将更改提交到 GitHub Pages 存储库本地,并将更改推送到 GitHub。我们可以观察到每个添加到我们存储库的文件都在以下行中输出:

+ git commit -m 'pushing charts from branch master'
[master 9769f5a] pushing charts from branch master
 3 files changed, 32 insertions(+)
 create mode 100644 stable/guestbook-1.0.0.tgz
 create mode 100644 stable/index.yaml
 create mode 100644 stable/nginx-1.0.0.tgz

您可能会好奇在自动推送后您的 GitHub Pages 存储库是什么样子。您的存储库应该如下所示,其中包含一个新的stable文件夹,其中包含 Helm 图表:

图 7.6 - CI 流水线完成后存储库的状态

图 7.6 - CI 流水线完成后存储库的状态

stable文件夹中,您应该能够看到三个不同的文件,两个单独的图表和一个index.yaml文件:

图 7.7 - 文件夹的内容

图 7.7 - stable文件夹的内容

这个第一个流水线构建成功地创建了一组初始的stable图表,但没有演示在被认为是稳定并且可以供最终用户使用之前,新图表如何进行 linting 和测试。为了演示这一点,我们需要从主分支切出一个功能分支来修改一个或多个图表,将更改推送到功能分支,然后在 Jenkins 中启动一个新的构建。

首先,从主分支创建一个名为 chapter7 的新分支:

$ cd $PACKT_FORK_DIR
$ git checkout master
$ git checkout -b chapter7

在这个分支上,我们将简单地修改ngnix图表的版本以触发图表的 linting 和测试。NGINX 是一个 Web 服务器和反向代理。它比我们在本书中一直使用的 Guestbook 应用程序要轻量得多,因此,为了避免 Jenkins 在您的 Minikube 环境中运行时可能出现的任何资源限制,我们将在本示例中使用 Packt 存储库中的ngnix图表。

helm-charts/charts/nginx/Chart.yaml文件中,将图表的版本从1.0.0更改为1.0.1

version: 1.0.1

运行 git status 确认已检测到变化:

$ git status
On branch chapter7
Changes not staged for commit:
  (use 'git add <file>...' to update what will be committed)
  (use 'git checkout -- <file>...' to discard changes in working directory)
        modified:   helm-charts/charts/nginx/Chart.yaml
no changes added to commit (use 'git add' and/or 'git commit -a')

注意ngnixChart.yaml文件已经被修改。添加文件,然后提交更改。最后,您可以继续将更改推送到您的分支:

$ git add helm-charts
$ git commit -m 'bumping NGINX chart version to demonstrate chart testing pipeline'
$ git push origin chapter7

在 Jenkins 中,我们需要触发仓库扫描,以便 Jenkins 可以检测并针对此分支启动新的构建。转到测试和发布 Helm Charts页面。您可以通过点击顶部标签栏上的测试和发布 Helm Charts标签轻松实现:

图 7.8 – 测试和发布 Helm Charts 页面

图 7.8 – 测试和发布 Helm Charts 页面

选择后,点击左侧菜单中的立即扫描多分支流水线按钮。这允许 Jenkins 检测到您的新分支并自动启动新的构建。扫描应在大约 10 秒内完成。刷新页面,新的chapter7分支应如下出现在页面上:

图 7.9 – 扫描新的 chapter7 分支后的测试和部署 Helm Charts 页面

图 7.9 – 扫描新的chapter7分支后的测试和部署 Helm Charts 页面

由于chapter7作业包含经过修改的 Helm 图表,并使用图表测试工具进行测试,因此chapter7作业的运行时间将比主作业长。您可以通过导航到chapter7的控制台输出来观察此流水线的运行情况。从测试和发布 Helm 图表概述页面,选择第七章分支,然后在左下角选择#1链接。最后,选择控制台输出链接。如果您在流水线仍在运行时导航到此页面,您将实时收到日志更新。等到流水线结束,在那里应该显示以下消息:

Finished: SUCCESS

在控制台输出日志的开始处,注意ct lintct install命令是针对ngnix图表运行的,因为这是唯一发生更改的图表:

Charts to be processed:
---------------------------------------------------------------
 nginx => (version: '1.0.1', path: 'helm-charts/charts/nginx')

每个命令的附加输出应该已经很熟悉,因为它与第六章中描述的输出相同,测试 Helm 图表

在您的 GitHub Pages 存储库中,您应该看到staging文件夹中的ngnix图表的新版本,因为它没有构建在主分支上:

图 7.10 - “staging”文件夹的内容

图 7.10 - staging文件夹的内容

要发布nginx-1.0.1.tgz图表,您需要将chapter7分支合并到主分支,这将导致该图表被推送到稳定存储库。在命令行上,将您的chapter7分支合并到主分支并将其推送到remote存储库:

$ git checkout master
$ git merge chapter7
$ git push origin master

在 Jenkins 中,通过返回到测试和发布 Helm 图表页面并点击master作业来导航到主流水线作业。您的屏幕应该如下所示:

图 7.11 - 测试和发布 Helm 图表项目的主作业

图 7.11 - 测试和发布 Helm 图表项目的主作业

一旦进入此页面,点击左侧的立即构建链接。再次注意日志中的内容,图表测试被跳过,因为图表测试工具将克隆与主分支进行了比较。由于内容相同,工具确定没有需要测试的内容。构建完成后,导航到您的 GitHub Pages 存储库,确认新的nginx-1.0.1.tgz图表位于stable存储库下:

图 7.12 - 添加新的 nginx 图表后存储库的状态

图 7.12 - 添加新的nginx图表后存储库的状态

您可以通过在本地添加helm repo add来验证这些图表是否已正确部署到 GitHub Pages 的stable存储库。在第五章中,构建您的第一个 Helm 图表,您添加了 GitHub Pages 存储库的根位置。但是,我们修改了文件结构以包含stablestaging文件夹。如果仍然配置,您可以通过运行以下命令来删除此存储库:

$ helm repo remove learnhelm

可以使用stable存储库的更新位置再次添加存储库:

$ helm repo add learnhelm $GITHUB_PAGES_SITE_URL/stable

请注意,$GITHUB_PAGES_SITE_URL的值引用 GitHub 提供的静态站点,而不是您实际的git存储库。您的 GitHub Pages 站点 URL 应该类似于$GITHUB_USERNAME.github.io/Learn-Helm-Repository/stable。确切的链接可以在 GitHub Pages 存储库的设置选项卡中找到。

在添加stable存储库后,运行以下命令查看在两个主构建过程中构建和推送的每个图表:

$ helm search repo learnhelm --versions

您应该看到三个结果,其中两个包含构建和推送的nginx图表的两个版本:

图 7.13 - 命令的结果

图 7.13 - helm search repo命令的结果

在本节中,我们讨论了如何通过 CI 管道管理 Helm 图表的生命周期。通过使用提供的示例遵循自动化工作流程,您可以在发布图表给最终用户之前轻松执行常规的 linting 和测试。

虽然本节主要关注 Helm 图表的 CI,但 CD 和 GitOps 也可以用于将 Helm 图表部署到不同的环境。我们将在下一节中探讨如何构建 CD 管道。

创建一个使用 Helm 部署应用程序的 CD 管道

CD 管道是一组可重复部署到一个或多个不同环境的步骤。在本节中,我们将创建一个 CD 管道,以部署我们在上一节中测试并推送到 GitHub Pages 存储库的nginx图表。还将通过引用保存到git存储库的values文件来利用 GitOps。

让我们设计需要包括在此管道中的高级步骤。

设计管道

在以前的章节中,使用 Helm 部署到 Kubernetes 环境是一个手动过程。然而,这个 CD 管道旨在在抽象使用 Helm 的同时部署到多个不同的环境。

以下步骤描述了我们将在本节中涵盖的 CD 工作流程。

  1. 添加包含nginx图表发布的稳定 GitHub Pages 存储库。

  2. nginx图表部署到开发环境。

  3. nginx图表部署到质量保证QA)环境。

  4. 等待用户批准管道以继续进行生产部署。

  5. nginx图表部署到生产环境。

CD 工作流包含在单独的Jenkinsfile文件中,与先前为 CI 管道创建的文件不同。在创建Jenkinsfile文件之前,让我们更新 Minikube 和 Jenkins 环境,以便执行 CD 流程。

更新环境

开发、QA 和生产环境将由本地 Minikube 集群中的不同命名空间建模。虽然我们通常不建议允许非生产(开发和 QA)和生产环境共存于同一集群中,但为了演示我们的示例 CD 流程,我们将这三个环境放在一起。

创建devqaprod命名空间来表示每个环境:

$ kubectl create ns dev
$ kubectl create ns qa
$ kubectl create ns prod

您还应该删除在上一节中创建的chapter7分支。应删除此分支,因为当创建新的 CD 管道时,Jenkins 将尝试针对存储库的每个分支运行它。为简单起见,并避免资源限制,我们建议仅使用主分支进行推进。

使用以下命令从存储库中删除chapter7分支:

$ git push -d origin chapter7
$ git branch -D chapter7

最后,您需要升级您的 Jenkins 实例以设置一个名为GITHUB_PAGES_SITE_URL的环境变量。这是您在 GitHub Pages 中图表存储库的位置,格式为$GITHUB_USERNAME.github.io/Learn-Helm-Chart-Repository/stable。CD 流水线中引用了该环境变量,以通过helm repo add添加stable GitHub Pages 图表存储库。要添加此变量,您可以通过使用--reuse-values标志重新使用先前应用的值,同时使用--set指定一个名为githubPagesSiteUrl的附加值。

执行以下命令来升级您的 Jenkins 实例:

$ helm upgrade jenkins codecentric/jenkins \
  -n chapter7 --version 1.5.1 \
  --reuse-values --set githubPagesSiteUrl=$GITHUB_PAGES_SITE_URL

此次升级将导致 Jenkins 实例重新启动。您可以通过针对chapter7命名空间的 Pod 运行 watch 来等待 Jenkins Pod 准备就绪:

$ kubectl get Pods -n chapter7 -w

当 Jenkins Pod 指示1/1个容器已准备就绪时,该 Jenkins Pod 可用。

一旦 Jenkins 准备就绪,通过使用上一节中相同的 URL 访问 Jenkins 实例。您应该会找到另一个作业,名为Deploy NGINX Chart,它代表了 CD 流水线:

图 7.14-升级 Jenkins 版本后的 Jenkins 首页

图 7.14-升级 Jenkins 版本后的 Jenkins 首页

当设置了 GITHUB_PAGES_SITE_URL 时,此作业将在values.yaml文件中配置为创建(以帮助改进本章流程)。

请注意,与 CI 流水线一样,CD 流水线也会自动启动,因为它是首次被检测到。在我们审查此流水线的日志之前,让我们先来看看构成 CD 流水线的过程。

理解流水线

在本节中,我们将仅审查流水线的关键领域,但完整的 CD 流水线已经编写好,并位于github.com/PacktPublishing/-Learn-Helm/blob/master/nginx-cd/Jenkinsfile

与之前的 CI 流水线一样,为了测试和发布 Helm 图表,CD 流水线首先通过动态创建一个新的 Jenkins 代理作为运行图表测试镜像的 Kubernetes Pod 来开始:

agent { label 'chart-testing-agent' }

虽然我们在这个流水线中没有使用ct工具,但是图表测试镜像包含了执行nginx部署所需的 Helm CLI,因此该镜像足以用于这个示例 CD 流水线。然而,也可以创建一个更小的镜像,删除未使用的工具也是可以接受的。

一旦代理被创建,Jenkins 会隐式克隆您的分支,就像在 CI 流水线中一样。

流水线的第一个明确定义的阶段称为“设置”,它将托管在 GitHub Pages 上的您的stable图表存储库添加到 Jenkins 代理上的本地 Helm 客户端中。

sh "helm repo add learnhelm ${env.GITHUB_PAGES_SITE_URL}"

一旦存储库被添加,流水线就可以开始将 NGINX 部署到不同的环境中。下一个阶段称为“部署到开发环境”,将 NGINX 图表部署到您的dev命名空间:

dir('nginx-cd') {
  sh "helm upgrade --install nginx-${env.BRANCH_NAME} learnhelm/nginx --values common-values.yaml --values dev/values.yaml -n dev --wait"
}

您可能注意到这个阶段的第一个细节是dir('nginx-cd')闭包。这是Jenkinsfile语法,用于设置其中包含的命令的工作目录。我们将很快更详细地解释nginx-cd文件夹。

您还可以看到,这个阶段使用提供的--install标志运行helm upgrade命令。helm upgrade通常针对已经存在的发布执行,并且如果尝试针对不存在的发布执行则会失败。然而,--install标志会在发布不存在时安装图表。如果发布已经存在,helm upgrade命令会升级发布。--install标志对于自动化流程非常方便,比如本节中描述的 CD 流水线,因为它可以避免您需要执行检查来确定发布的存在。

关于这个helm upgrade命令的另一个有趣细节是它两次使用了--values标志——一次针对名为common-values.yaml的文件,一次针对名为dev/values.yaml的文件。这两个文件都位于nginx-cd文件夹中。以下内容位于nginx-cd文件夹中:

nginx-cd/
  dev/
    values.yaml
  qa/
    values.yaml
  prod/
    values.yaml
  common-values.yaml
  Jenkinsfile

在将应用程序部署到不同的环境时,您可能需要稍微修改应用程序的配置,以使其能够与环境中的其他服务集成。devqaprod文件夹下的每个values文件都包含一个环境变量,该变量根据部署的环境设置在 NGINX 部署上。例如,这里显示了dev/values.yaml文件的内容:

env:
 - name: ENVIRONMENT
   value: dev

类似地,这里显示了qa/values.yaml文件的内容:

env:
 - name: ENVIRONMENT
   value: qa

prod/values.yaml文件的内容如下:

env:
 - name: ENVIRONMENT
   value: prod

虽然在这个示例中部署的 NGINX 图表是直接的,并且不严格要求指定这些值,但您会发现将环境特定的配置分开放在单独的values文件中,使用这里展示的方法对于复杂的真实用例非常有帮助。然后可以通过将相应的 values 文件传递给helm upgrade --install命令来应用安装,其中${env}表示devqaprod

正如其名称所示,common-values.yaml文件用于所有部署环境中通用的值。这个示例的common-values.yaml文件写成如下形式:

service:
 type: NodePort

这个文件表示在安装图表期间创建的每个 NGINX 服务都应该具有NodePort类型。由于它们没有在common-values.yaml文件或单独的values.yaml环境文件中被覆盖,NGINX 图表的values.yaml文件中设置的所有其他默认值也被应用到每个环境中。

重要的一点是,您的应用程序应该在每个部署环境中尽可能相同地部署。任何改变运行中的 Pod 或容器的物理属性的值都应该在common-values.yaml文件中指定。这些配置包括但不限于以下内容:

  • 副本计数

  • 资源请求和限制

  • 服务类型

  • 镜像名称

  • 镜像标签

  • ImagePullPolicy

  • 卷挂载

修改与特定环境服务集成的配置可以在单独的环境values文件中进行修改。这些配置可能包括以下内容:

  • 指标或监控服务的位置

  • 数据库或后端服务的位置

  • 应用/入口 URL

  • 通知服务

回到 CD 流水线的Deploy to Dev阶段中使用的 Helm 命令,--values common-values.yaml--values dev/values.yaml标志的组合将这两个values文件合并到dev中安装nginx图表。该命令还使用-n dev标志表示部署应该在dev命名空间中执行。此外,--wait标志用于暂停nginx Pod,直到它被报告为ready

继续进行流水线,部署到dev后的下一个阶段是烟雾测试。该阶段运行以下命令:

sh 'helm test nginx -n dev'

NGINX 图表包含一个测试钩子,用于检查 NGINX Pod 的连接。如果test钩子能够验证可以与 Pod 建立连接,则测试将返回为成功。虽然helm test命令通常用于图表测试,但它也可以作为在 CD 过程中执行基本烟雾测试的良好方法。烟雾测试是部署后进行的测试,以确保应用的关键功能按设计工作。由于 NGINX 图表测试不会以任何方式干扰正在运行的应用程序或部署环境的其余部分,因此helm test命令是确保 NGINX 图表成功部署的适当方法。

烟雾测试后,示例 CD 流水线运行下一个阶段,称为部署到 QA。该阶段包含一个条件,评估流水线正在执行的当前分支是否是主分支,如下所示:

when {
  expression {
    return env.BRANCH_NAME == 'master'
  }
}

该条件允许您使用功能分支来测试values.yaml文件中包含的部署代码,而无需将其提升到更高的环境。这意味着只有主分支中包含的 Helm 值应该是生产就绪的,尽管这不是您在 CD 流水线中发布应用时可以采取的唯一策略。另一种常见的策略是允许在以release/前缀开头的发布分支上进行更高级别的推广。

部署到 QA阶段中使用的 Helm 命令显示如下:

dir('nginx-cd') {
    sh "helm upgrade --install nginx-${env.BRANCH_NAME} learnhelm/nginx --values common-values.yaml --values qa/values.yaml -n qa --wait"
}

鉴于您对部署到 Dev阶段和常见值与特定环境值的分离的了解,部署到 QA的代码是可以预测的。它引用了qa/values.yaml文件中的 QA 特定值,并传递了-n qa标志以部署到qa命名空间。

在部署到qa或类似的测试环境之后,您可以再次运行前面描述的烟雾测试,以确保qa部署的基本功能正常工作。您还可以在这个阶段包括任何其他自动化测试,以验证在部署到prod之前应用的功能是否正常。这些细节已从此示例流水线中省略。

流水线的下一个阶段称为等待输入

stage('Wait for Input') {
    when {
        expression {
            return env.BRANCH_NAME == 'master'
        }
    }
    steps {
        container('chart-testing') {
            input 'Deploy to Prod?'
        }
    }
}

这个输入步骤暂停了 Jenkins 流水线,并用“部署到生产环境?”的问题提示用户。在运行作业的控制台日志中,用户有两个选择 - “继续”和“中止”。虽然可以自动执行生产部署而无需此手动步骤,但许多开发人员和公司更喜欢在“非生产”和“生产”部署之间设置一个人为的门。这个“输入”命令为用户提供了一个机会,让用户决定是否继续部署或在qa阶段之后中止流水线。

如果用户决定继续,将执行最终阶段,称为“部署到生产环境”:

dir('nginx-cd') {
  sh "helm upgrade --install nginx-${env.BRANCH_NAME} learnhelm/nginx --values common-values.yaml --values prod/values.yaml -n prod --wait"
}

这个阶段几乎与“部署到 Dev”和“部署到 QA”阶段相同,唯一的区别是生产特定的values文件和作为helm upgrade --install命令的一部分定义的prod命名空间。

现在示例 CD 流水线已经概述,让我们观察流水线运行,该流水线是在您升级 Jenkins 实例时启动的。

运行流水线

要查看此 CD 流水线的运行情况,请导航到“部署 NGINX 图”作业的主分支。在 Jenkins 首页,点击部署 NGINX 图主分支。您的屏幕应该如下所示:

图 7.15 - 部署 NGINX 图 CD 流水线的主分支

图 7.15 - 部署 NGINX 图 CD 流水线的主分支

一旦您导航到此页面,请点击#1链接并导航到控制台日志:

图 7.16 - 部署 NGINX 图 CD 流水线的控制台输出页面

图 7.16 - 部署 NGINX 图 CD 流水线的控制台输出页面

当您导航到日志时,您应该会看到一个提示,上面写着“部署到生产环境?”。我们很快会解决这个问题。首先,让我们回顾一下日志的开头,以便查看到目前为止流水线的执行情况。

您可以看到的第一个部署是dev部署:

+ helm upgrade --install nginx-master learnhelm/nginx --values common-values.yaml --values dev/values.yaml -n dev --wait
Release 'nginx-master' does not exist. Installing it now.
NAME: nginx-master
LAST DEPLOYED: Thu Apr 30 02:07:55 2020
NAMESPACE: dev
STATUS: deployed
REVISION: 1
NOTES:
1\. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace dev -o jsonpath='{.spec.ports[0].nodePort}' services nginx-master)
  export NODE_IP=$(kubectl get nodes --namespace dev -o jsonpath='{.items[0].status.addresses[0].address}')
  echo http://$NODE_IP:$NODE_PORT

然后,您应该会看到由helm test命令运行的冒烟测试:

+ helm test nginx-master -n dev
Pod nginx-master-test-connection pending
Pod nginx-master-test-connection pending
Pod nginx-master-test-connection succeeded
NAME: nginx-master
LAST DEPLOYED: Thu Apr 30 02:07:55 2020
NAMESPACE: dev
STATUS: deployed
REVISION: 1
TEST SUITE:     nginx-master-test-connection
Last Started:   Thu Apr 30 02:08:03 2020
Last Completed: Thu Apr 30 02:08:05 2020
Phase:          Succeeded

冒烟测试之后是qa部署:

+ helm upgrade --install nginx-master learnhelm/nginx --values common-values.yaml --values qa/values.yaml -n qa --wait
Release 'nginx-master' does not exist. Installing it now.
NAME: nginx-master
LAST DEPLOYED: Thu Apr 30 02:08:09 2020
NAMESPACE: qa
STATUS: deployed
REVISION: 1

这将带我们到输入阶段,我们在首次打开日志时看到的:

图 7.17 - 部署到生产环境之前的输入步骤

图 7.17 - 部署到生产环境之前的输入步骤

点击继续链接以继续流水线执行,点击中止将导致流水线失败,并阻止生产部署的发生。然后您将看到prod部署发生:

+ helm upgrade --install nginx-master learnhelm/nginx --values common-values.yaml --values prod/values.yaml -n prod --wait
Release 'nginx-master' does not exist. Installing it now.
NAME: nginx-master
LAST DEPLOYED: Thu Apr 30 03:46:22 2020
NAMESPACE: prod
STATUS: deployed
REVISION: 1

最后,如果生产部署成功,您将在流水线结束时看到以下消息:

[Pipeline] End of Pipeline
Finished: SUCCESS

您可以手动验证部署是否成功。运行 helm list 命令查找 nginx-master 发布版本:

$ helm list -n dev
$ helm list -n qa
$ helm list -n prod

每个命令都应该列出每个命名空间中的 nginx 发布版本:

NAME 	            NAMESPACE	    REVISION  	
nginx-master	      dev      	    1

您还可以使用 kubectl 列出每个命名空间中的 Pod,并验证 NGINX 是否已部署:

$ kubectl get Pods -n dev
$ kubectl get Pods -n qa
$ kubectl get Pods -n prod

每个命名空间的结果将类似于以下内容(dev 还将有一个在冒烟测试阶段执行的已完成测试 Pod):

NAME                    READY   STATUS    RESTARTS   AGE
nginx-fcb5d6b64-rmc2j   1/1     Running   0          46m

在本节中,我们讨论了如何在 Kubernetes 中的 CD 流水线中使用 Helm 来部署应用程序到多个环境中。该流水线依赖于 GitOps 实践,将配置(values.yaml文件)存储在源代码控制中,并引用这些文件来正确配置 NGINX。了解了 Helm 如何在 CD 环境中使用后,您现在可以清理您的 Minikube 集群。

清理

要清理本章练习中的 Minikube 集群,请删除 chapter7devqaprod 命名空间:

$ kubectl delete ns chapter7
$ kubectl delete ns dev
$ kubectl delete ns qa
$ kubectl delete ns prod

您还可以关闭您的 Minikube 虚拟机:

$ minikube stop

摘要

在 CI 和 CD 流水线中调用 Helm CLI 是进一步抽象 Helm 提供的功能的有效方式。图表开发人员可以通过编写 CI 流水线来自动化端到端的图表开发过程,包括代码检查、测试、打包和发布到图表存储库。最终用户可以编写 CD 流水线,使用 Helm 在多个不同的环境中部署图表,利用 GitOps 来确保应用程序可以作为代码部署和配置。编写流水线有助于开发人员和公司通过抽象和自动化过程更快、更轻松地扩展应用程序,避免了可能变得繁琐并引入人为错误的过程。

在下一章中,我们将介绍另一种抽象 Helm CLI 的选项——编写 Helm operator。

进一步阅读

要了解有关图表测试容器映像的更多信息,请访问helm.sh/blog/chart-testing-intro/

要了解更多关于 Jenkins 和 Jenkins 流水线的信息,请查阅 Jenkins 项目文档(jenkins.io/doc/)、Jenkins 流水线文档(jenkins.io/doc/book/pipeline/)和多分支流水线插件文档(plugins.jenkins.io/workflow-multibranch/)。

问题

  1. CI 和 CD 之间有什么区别?

  2. CI/CD 和 GitOps 之间有什么区别?

  3. CI/CD 流水线创建和发布 Helm 图表包括哪些高级步骤?

  4. CI 给图表开发者带来了哪些优势?

  5. CD 流水线部署 Helm 图表包括哪些高级步骤?

  6. CD 流水线给图表的最终用户带来了哪些优势?

  7. 如何将应用程序的配置作为代码在多个环境中进行维护?如何减少values文件中的样板代码?

第八章:使用 Helm 与操作员框架

使用 Helm 的一个优势是能够同步本地和实时状态。使用 Helm,本地状态是通过值文件进行管理的,当使用installupgrade命令提供这些值时,将这些值应用于 Kubernetes 集群中的实时状态以进行同步。在之前的章节中,当希望对应用程序进行更改时,通过调用这些命令来执行此操作。

另一种同步这些更改的方法是在集群内创建一个应用程序,定期检查期望状态是否与环境中的当前配置匹配。如果状态不匹配,应用程序可以自动修改环境以匹配期望的状态。这种应用程序被称为 Kubernetes 操作员。在本章中,我们将创建一个基于 Helm 的操作员,以确保本地定义的状态始终与集群的实时状态匹配。如果不匹配,操作员将执行适当的 Helm 命令来更新环境。

本章将涵盖以下主题:

  • 理解 Kubernetes 操作员

  • 创建一个 Helm 操作员

  • 使用 Helm 来管理操作员和自定义资源(CRs)

  • 清理您的 Kubernetes 环境

技术要求

对于本章,您需要在本地机器上安装以下技术:

  • minikube

  • helm

  • kubectl

除了这些工具之外,您还应该在 GitHub 上找到 Packt 存储库,其中包含与示例相关的资源,网址为github.com/PacktPublishing/-Learn-Helm。本存储库将在本章中被引用。

理解 Kubernetes 操作员

自动化是 Kubernetes 平台的核心。正如在第一章中所介绍的,了解 Kubernetes 和 Helm,Kubernetes 资源可以通过运行kubectl命令隐式管理,也可以通过应用 YAML 格式的表示来声明性地管理。一旦使用 Kubernetes 命令行界面(CLI)应用了资源,Kubernetes 的基本原则之一是将集群中资源的当前状态与期望状态匹配,这个过程称为控制循环。这种持续的、非终止的监视集群状态的模式是通过控制器实现的。Kubernetes 包括许多本地于平台的控制器,例如拦截对 Kubernetes 应用程序编程接口(API)的请求的准入控制器,以及管理运行的 Pod 副本数量的复制控制器。

随着对 Kubernetes 的兴趣开始增长,提供用户扩展基础平台功能的能力,以及提供更多关于管理应用程序生命周期的智能的组合,导致了几个重要概念的产生,这些概念定义了 Kubernetes 开发的第二波。首先,引入了自定义资源定义(CRD),使用户能够扩展默认的 Kubernetes API,这是与 Kubernetes 平台交互的机制,以创建和注册新类型的资源。注册新的 CRD 会在 Kubernetes API 服务器上创建一个新的 RESTful 资源路径。因此,类似于您可以使用 Kubernetes CLI 执行kubectl get pods来检索所有 Pod 对象,例如,为名为Guestbook的对象类型注册一个新的 CRD,允许调用kubectl get guestbook来查看先前创建的所有 Guestbook 对象。有了这种新的能力,开发人员现在可以创建自己的控制器来监视这些类型的 CR,以管理可以通过 CRD 描述的应用程序的生命周期。

第二个主要趋势是 Kubernetes 部署的应用程序类型的进展。与小型简单的应用程序不同,更复杂和有状态的应用程序被部署得更频繁。这些高级应用程序通常需要更高级的管理和维护水平,例如处理多个组件的部署,以及围绕“第二天”活动的考虑,如备份和恢复。这些任务超出了 Kubernetes 中典型控制器的范围,因为必须嵌入与其管理的应用程序相关的深层知识。使用 CR 来管理应用程序及其组件的这种模式被称为Operator模式。由软件公司 CoreOS 在 2016 年首次提出,Operators 旨在捕获人类操作员在管理应用程序生命周期方面的知识。Operators 被打包为普通的容器化应用程序——部署在 pod 中——对 CR 的 API 更改做出反应。

Operators 通常使用称为 Operator Framework 的工具包编写,并基于以下三种不同的技术之一:

  • Go

  • Ansible

  • Helm

基于 Go 的 Operators 利用 Go 编程语言实现控制循环逻辑。基于 Ansible 的 Operators 利用 Ansible CLI 工具和 Ansible playbooks。Ansible 是一种自动化工具,其逻辑是在称为 playbooks 的 YAML 文件中编写的。

在本章中,我们将专注于基于 Helm 的 Operators。Helm Operators 将其控制循环逻辑基于 Helm 图表和 Helm CLI 提供的一部分功能。因此,它们代表了 Helm 用户实现其 Operators 的一种简单方式。

了解了 Operators,让我们使用 Helm 创建自己的 operator。

创建一个 Helm operator

在本节中,我们将编写一个基于 Helm 的 operator,用于安装第五章中创建的 Guestbook Helm 图表,构建您的第一个 Helm 图表。该图表可以在 Packt 存储库的guestbook/文件夹下找到(https://github.com/PacktPublishing/-Learn-Helm/tree/master/helm-charts/charts/guestbook)。

操作员是作为一个包含控制循环逻辑以维护应用程序的容器镜像构建的。下图演示了访客留言簿操作员部署后的功能:

图 8.1 - 访客留言簿操作员工作流

图 8.1 - 访客留言簿操作员工作流

访客留言簿操作员将不断监视访客留言簿 CR 的更改。当创建访客留言簿 CR 时,访客留言簿操作员将安装您在第五章中创建的访客留言簿图表,构建您的第一个 Helm 图表。相反,如果删除了访客留言簿 CR,访客留言簿操作员将删除访客留言簿 Helm 图表。

了解访客留言簿操作员的功能后,让我们设置一个可以构建和部署操作员的环境。

设置环境

首先,由于操作员将部署到 Kubernetes,您应该通过运行以下命令来启动 Minikube 环境:

$ minikube start

启动 Minikube 后,创建一个名为chapter8的命名空间,如下所示:

$ kubectl create ns chapter8

由于访客留言簿操作员是作为一个容器镜像构建的,您需要创建一个可以存储它以便以后引用的镜像存储库。为了存储这个镜像,我们将在 Quay(quay.io)中创建一个新的存储库,这是一个公共容器注册表(如果您在其他地方有帐户,那也可以)。我们还将准备一个本地开发环境,其中包含构建操作员镜像所需的必要工具。

让我们从在 Quay 中创建一个新的镜像存储库开始。

创建 Quay 存储库

在 Quay 中创建一个新的存储库需要您拥有一个 Quay 帐户。按照以下步骤创建一个 Quay 帐户:nt:

  1. 在浏览器中导航到 https://quay.io/signin/。屏幕会提示您输入 Quay 凭据,如下截图所示:图 8.2 - 红帽 Quay 登录页面

图 8.2 - 红帽 Quay 登录页面

  1. 在页面底部,单击创建帐户链接。屏幕会提示您使用一组对话框来创建一个新的 Quay 帐户,如下截图所示:图 8.3 - 红帽 Quay 创建新帐户页面

图 8.3 - 红帽 Quay创建新帐户页面

  1. 输入您想要的凭据,然后选择创建免费帐户

  2. 您很快将收到一封电子邮件确认。单击确认电子邮件中的链接以验证您的帐户并继续使用新帐户的 Quay。

创建了新的 Quay 帐户后,您可以继续为 operator 图像创建新的图像存储库。

要创建新的图像存储库,请在 Quay 页面右上角选择+加号图标,然后选择新存储库,如下截图所示:

图 8.4 - 选择“新存储库”以创建新的图像存储库

图 8.4 - 选择“新存储库”以创建新的图像存储库

  1. 然后,您将被带到创建新存储库页面,在那里您应该输入以下细节:

对于存储库名称,输入guestbook-operator

选择Public单选按钮,表示对存储库的无身份验证访问。此更改将简化 Kubernetes 访问图像的方式。

其余选项可以保持默认值。完成后,创建新存储库页面应该会出现,如下截图所示:

图 8.5 - Quay 中的“创建新存储库”页面

图 8.5 - Quay 中的“创建新存储库”页面

  1. 选择创建公共存储库按钮以创建 Quay 存储库。

现在已经创建了一个存储库来存储 Guestbook Operator 图像,让我们准备一个环境,其中包含构建 Helm operator 所需的工具。

准备本地开发环境

要创建 Helm operator,您至少需要以下 CLI 工具:

  • operator-sdk

  • dockerpodmanbuildah

operator-sdk CLI 是用于帮助开发 Kubernetes Operators 的工具包。它包含简化 operator 开发过程的内在逻辑。在幕后,operator-sdk需要一个容器管理工具,它可以用来构建 operator 图像。operator-sdk CLI 支持dockerpodmanbuildah作为底层容器管理工具。

要安装operator-sdk CLI,您可以从它们的GitHub 存储库 https://github.com/operator-framework/operator-sdk/releases 下载一个版本。但是,安装dockerpodmanbuildah的过程可能会因操作系统而异;更不用说,Windows 用户将无法原生地使用operator-sdk工具包。

幸运的是,Minikube 虚拟机(VM)可以作为开发人员的工作环境,因为它是一个 Linux VM,并且还包含 Docker CLI,适用于许多不同操作系统。在本节中,我们将在 Minikube VM 上安装operator-sdk,并将使用此环境来创建 operator。请注意,虽然提供的步骤旨在在 VM 中运行,但大多数步骤也适用于所有 Linux 和 Mac 机器。

按照以下步骤在 Minikube VM 上安装operator-sdk

  1. 通过运行minikube ssh命令来访问 VM,如下所示:
$ minikube ssh
  1. 一旦进入 VM,您需要下载operator-sdk CLI。这可以通过使用curl命令来完成。请注意,写作时使用的operator-sdk版本是0.15.2版本。

要下载此版本的operator-sdk CLI,请运行以下命令:

$ cu**rl -o operator-sdk -L https://github.com/operator-framework/operator-sdk/releases/download/v0.15.2/operator-sdk-v0**.15.2-x86_64-linux-gnu 
  1. 下载后,您需要更改operator-sdk二进制文件的权限为用户可执行。运行chmod命令进行此修改,如下所示:
$ chmod u+x operator-sdk
  1. 接下来,将operator-sdk二进制文件移动到 VM 的PATH变量管理的位置,例如/usr/bin。因为此操作需要 root 权限,您需要使用sudo运行mv命令,如下所示:
$ sudo mv operator-sdk /usr/bin
  1. 最后,通过运行operator-sdk version命令来验证您的operator-sdk安装,如下所示:
$ operator-sdk version
operator-sdk version: 'v0.15.2', commit: 'ffaf278993c8fcb00c6f527c9f20091eb8dd3352', go version: 'go1.13.3 linux/amd64'

如果此命令执行没有错误,那么您已成功安装了operator-sdk CLI。

  1. 作为一个额外的步骤,您还应该在 Minikube VM 中克隆 Packt 存储库,因为我们将稍后利用guestbook Helm 图表来构建 Helm operator。在 VM 中运行以下命令来克隆存储库:](https://github.com/PacktPublishing/-Learn-Helm.git)
$ git clone https://github.com/PacktPub**lishing/-Learn-Helm.git Learn-Helm

现在您已经有了 Quay 镜像存储库和从 Minikube VM 创建的本地开发环境,让我们开始编写 Guestbook Operator。请注意,operator 代码的示例位于 Packt 存储库的 https://github.com/PacktPublishing/-Learn-Helm/tree/master/guestbook-operator 位置。

搭建 operator 文件结构

与 Helm 图表本身类似,由operator-sdk CLI 构建的 Helm Operators 具有必须遵守的特定文件结构。文件结构在下表中进行了解释:

图 8.6 - 文件结构解释

图 8.6 - 文件结构解释

使用operator-sdk new命令可以轻松创建操作员文件结构。在您的 Minikube VM 中,执行以下命令来创建 Guestbook Operator 的脚手架:

$ operator-sdk new guestbook-operator --type helm --kind Guestbook --helm-chart Learn-Helm/helm-charts/charts/guestbook
INFO[0000] Creating new Helm operator 'guestbook-operator'. 
INFO[0003] Created helm-charts/guestbook       
WARN[0003] Using default RBAC rules: failed to get Kubernetes config: could not locate a kubeconfig 
INFO[0003] Created build/Dockerfile                     
INFO[0003] Created watches.yaml                         
INFO[0003] Created deploy/service_account.yaml          
INFO[0003] Created deploy/role.yaml                     
INFO[0003] Created deploy/role_binding.yaml             
INFO[0003] Created deploy/operator.yaml                 
INFO[0003] Created deploy/crds/charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml 
INFO[0003] Generated CustomResourceDefinition manifests. 
INFO[0003] Project creation complete.

operator-sdk new命令创建了一个名为guestbook-operator的本地目录,其中包含操作员内容。指定应使用--type标志创建 Helm 操作员,以及Guestbook作为 CR 的名称。

最后,--helm-chart标志指示operator-sdk CLI 将源 Guestbook 图表复制到操作员目录。

成功创建了 Guestbook 操作员的脚手架,让我们构建操作员并将其推送到您的 Quay 注册表。

构建操作员并将其推送到 Quay

operator-sdk CLI 提供了一个operator-sdk build命令,可以轻松构建操作员图像。此命令旨在针对操作员的顶级目录运行,并将通过引用位于操作员build/文件夹下的 Dockerfile 来构建图像。

在您的 Minikube VM 中,运行operator-sdk build命令,将您的 Quay 用户名替换为指定位置,如下所示:

$ cd guestbook-operator
$ operator-sdk build quay.io/$QUAY_USERNAME/guestbook-operator

如果构建成功,您将收到以下消息:

INFO[0092] Operator build complete.

由于 Minikube VM 安装了 Docker,operator-sdk CLI 在后台使用 Docker 构建图像。您可以运行docker images命令来验证图像是否已构建,如下所示:

$ docker images

操作员图像在本地构建后,必须将其推送到图像注册表,以便可以从 Kubernetes 中拉取。为了使用 Docker 将图像推送到注册表,您必须首先对目标注册表进行身份验证。使用docker login命令登录到 Quay,如下面的代码片段所示:

$ docker login quay.io --username $QUAY_USERNAME --password $QUAY_PASSWORD

登录到 Quay 后,使用docker push命令将操作员图像推送到 Quay 注册表,就像这样:

$ docker push quay.io/$QUAY_USERNAME/guestbook-operator

推送完成后,返回到您在创建 Quay 存储库部分创建的guestbook-operator存储库。您应该能够在存储库标签部分看到一个新的标签发布,如下面的屏幕截图所示:

图 8.7 – 应将新标签推送到您的 Quay 注册表

图 8.7 – 应将新标签推送到您的 Quay 注册表

现在您的操作员已经推送到容器注册表,让我们继续通过将操作员部署到您的 Kubernetes 环境。

部署 Guestbook 操作员

在搭建 Guestbook Operator 时,operator-sdk CLI 还创建了一个名为deploy的文件夹,并生成了部署操作员所需的文件。

以下是deploy文件夹中的内容所示的文件结构:

deploy/
  crds/
    charts.helm.k8s.io_guestbooks_crd.yaml
    charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml
  operator.yaml
  role_binding.yaml
  role.yaml
  service_account.yaml

crds/文件夹包含创建 Guestbook CRD 所需的 YAML 资源(charts.helm.k8s.io_guestbooks_crd.yaml)。此文件用于在 Kubernetes 中注册新的 Guestbook API 端点。此外,crds/文件夹包含一个示例 Guestbook CR 应用程序(charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml)。创建此文件将触发操作员安装 Guestbook Helm 图表。

请查看 CR 的内容,以熟悉所定义属性的类型,如下所示:

$ cat guestbook-operator/deploy/crds/charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml

以下代码块中提供了输出的片段:

图 8.8 - Guestbook CR 的片段

图 8.8 - Guestbook CR 的片段

spec部分中的每个条目都指向 Guestbook 图表的values.yaml文件。operator-sdk工具自动使用此文件中包含的每个默认值创建了此示例 CR。在应用此 CR 之前,可以添加或修改其他条目,以覆盖 Guestbook 图表的其他值。这些值在运行时由操作员使用,以相应地部署 Guestbook 应用程序。

deploy/operator.yaml文件定义了实际的操作员本身,并包含一个简单的部署资源。我们将很快返回到这个文件的内容。

role_binding.yamlrole.yamlservice_account.yaml文件是为了为操作员提供必要的权限,以便监视 Guestbook CR 并将 Guestbook Helm 图表安装到 Kubernetes 中。它通过在service_account.yaml文件中定义的服务帐户进行身份验证,然后执行这些操作。一旦经过身份验证,操作员将根据role.yamlrole_binding.yaml资源获得授权。role.yaml文件列出了描述操作员被允许执行的确切资源和操作的精细权限。role_binding.yaml文件将角色绑定到操作员的服务帐户。

了解操作员deploy/文件夹下创建的每个资源后,请按照以下步骤部署您的 Guestbook 操作员:

  1. 不幸的是,Minikube VM 不包含Kubectl,所以如果您仍然通过命令行连接到 VM,您必须首先退出到您的本地系统,通过运行以下命令:
$ exit
  1. 早些时候使用operator-sdk创建的资源也位于 Packt 存储库的guestbook-operator/文件夹下。如果您之前没有克隆过这个存储库,请使用以下命令现在克隆它:
$ git clone https://github.com/PacktPublishing/-Learn-Helm.git Learn-Helm

作为一个快速的旁注,需要注意的是,Packt 存储库中唯一修改自 Minikube VM 中创建的资源的资源是role.yaml文件。operator-sdk CLI 基于包含在 guestbook Helm 图表中的模板文件生成了一个简单的role.yaml文件。但是,如果您能回忆起来,guestbook 图表包含了一些资源,只有在条件值基础上才会包含这些资源。这些资源是JobPersistentVolumeClaim挂钩资源,只有在启用持久存储时才会包含。其中一个示例显示在PersistentVolumeClaim模板中,如下面的代码片段所示:

{{- if .Values.redis.master.persistence.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim

operator-sdk CLI 没有自动为JobsPersistentVolumeClaims创建基于角色的访问控制RBAC)规则,因为它不知道是否应该包含此模板。

因此,作者已将这些规则添加到位于 https://github.com/PacktPublishing/-Learn-Helm/blob/master/guestbook-operator/deploy/role.yaml#L81-L104role.yaml文件中。

  1. Guestbook 操作员将依赖于一个新的 API 端点。通过在guestbook-operator/deploy/crds文件夹下应用 CRD 来创建此端点,如下所示:
$ kubectl apply -f guestbook-operator/deploy/crds/charts.helm.k8s.io_guestbooks_crd.yaml

我们将在稍后使用该文件夹下的第二个文件(CR)来部署 Guestbook 应用程序。

  1. 接下来,您需要修改guestbook-operator/deploy/operator.yaml文件,以指定您之前构建的操作员图像。您会注意到在这个文件中有以下代码行:
# Replace this with the built image name
image: REPLACE_IMAGE

REPLACE_IMAGE文本替换为您的操作员图像的位置。此值应类似于quay.io/$QUAY_USERNAME/guestbook-operator

  1. 一旦您应用了 CRD 并更新了您的operator.yaml文件,您可以通过运行以下命令来继续应用guestbook-operator/deploy/文件夹中的每个资源:
$ kubectl apply -f guestbook-operator/deploy -n chapter8
  1. 通过对chapter8命名空间中的 Pods 运行观察,等待操作员报告1/1就绪状态,就像这样:
$ kubectl get pods -n chapter8 -w

现在 Guestbook operator 已部署,让我们使用它来安装 Guestbook Helm chart。

部署 Guestbook 应用程序

当使用 Helm 作为独立的 CLI 工具时,您可以通过运行helm install命令来安装 Helm chart。使用 Helm operator,您可以通过创建 CR 来安装 Helm chart。通过创建位于guestbook-operator/deploy/crds/文件夹下的提供的 CR 来安装 Guestbook Helm chart,如下面的代码片段所示:

$ kubectl apply -f guestbook-operator/deploy/crds/charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml -n chapter8

chapter8命名空间中的 Pod 运行另一个watch命令,如下面的代码片段所示,您应该能够看到 Guestbook 和 Redis Pods 因 Helm chart 安装而启动:

$ kubectl get pods -n chapter8 -w

以下代码块描述了每个 Pod 处于READY状态:

NAME                                  READY   STATUS    RESTARTS
example-guestbook-65bc5fdc55-jvkdz    1/1     Running   0
guestbook-operator-6fddc8d7cb-94mzp   1/1     Running   0
redis-master-0                        1/1     Running   0
redis-slave-0                         1/1     Running   0
redis-slave-1                         1/1     Running   0

当您创建 Guestbook CR 时,操作员会执行helm install命令来安装 Guestbook chart。您可以通过运行helm list来确认已创建的发布,就像这样:

$ helm list -n chapter8
NAME             	NAMESPACE	REVISION	UPDATED       
example-guestbook	chapter8 	1       	2020-02-24

通过修改example-guestbook CR 来执行发布的升级。修改您的guestbook-operator/deploy/crds/charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml文件,将副本数从1更改为2,就像这样:

replicaCount: 2

在更新了replicaCount值之后应用更改,如下所示:

$ kubectl apply -f guestbook-operator/deploy/crds/charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml -n chapter8

修改 Guestbook CR 将触发针对example-guestbook发布的helm upgrade命令。正如您可能还记得第五章中所述,构建您的第一个 Helm Chart,Guestbook Helm chart 的升级钩子将启动对 Redis 数据库的备份。如果您在修改 CR 后对chapter8命名空间中的 Pod 运行watch,您将注意到一个备份Job开始,并且一旦备份完成,您将看到两个 Guestbook Pods 中的一个终止。您还将从以下代码片段中的helm list命令中注意到example-guestbook发布的修订号已增加到2

$ helm list -n chapter8
NAME             	NAMESPACE	REVISION	UPDATED       
example-guestbook	chapter8 	2       	2020-02-24

尽管修订号已增加到2,但截至撰写本文时,基于 Helm 的 Operators 的一个限制是您无法像使用 CLI 那样发起回滚到先前的修订。如果您尝试对example-guestbook发布运行helm history,您还将注意到只有第二个修订在发布历史中,如下面的代码片段所示:

$ helm history example-guestbook -n chapter8
REVISION	UPDATED                 	STATUS        
2       	Tue Feb 25 04:36:10 2020	deployed

这是使用 Helm CLI 和使用基于 Helm 的 operator 之间的重要区别。由于不保留发布历史记录,基于 Helm 的 operator 不允许执行显式回滚。但是,如果升级失败,将运行helm rollback命令。在这种情况下,将执行回滚钩子,试图回滚到尝试的升级。

尽管基于 Helm 的 operator 不保留发布历史记录,但它在同步应用程序的期望状态和实际状态方面表现出色。这是因为 operator 不断监视 Kubernetes 环境的状态,并确保应用程序始终配置为与 CR 上指定的配置匹配。换句话说,如果修改了 Guestbook 应用程序的资源之一,operator 将立即恢复更改,使其与 CR 上定义的规范匹配。您可以通过修改 Guestbook 资源之一上的字段来看到这一点。

例如,我们将直接将 Guestbook 部署的副本计数从2更改为3,并观察 operator 自动将其恢复为2个副本,以重新同步 CR 中定义的期望状态。

执行以下kubectl patch命令,将 Guestbook 部署的副本计数从2更改为3

$ kubectl patch deployment example-guestbook -p '{'spec':{'replicas':3}}' -n chapter8

通常,这只会添加一个额外的 Guestbook 应用程序副本。但是,因为 Guestbook CR 当前仅定义了2个副本,所以 operator 会快速将副本计数更改回2,并终止创建的额外 Pod。如果您实际上想将副本计数增加到3,则必须更新 Guestbook CR 上的replicaCount值。该过程的优势在于确保期望状态与集群的实际状态匹配。

使用基于 Helm 的 operator 卸载 Guestbook 应用程序就像删除 CR 一样简单。删除example-guestbook CR 以卸载发布,就像这样:

$ kubectl delete -f guestbook-operator/deploy/crds/charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml -n chapter8

这将删除example-guestbook发布以及所有相关资源。

您还可以删除 Guestbook Operator 及其资源,因为我们在下一节中将不再需要它们。您可以通过运行以下命令来执行此操作:

$ kubectl delete -f guestbook-operator/deploy/ -n chapter8

一般来说,您应该始终确保在删除运算符之前先删除 CR。当您删除 CR 时,运算符会执行helm uninstall命令来删除您的发布。如果您意外地先删除了运算符,您将不得不在命令行上手动运行helm uninstall

在本节中,您创建了一个 Helm 运算符,并学习了如何使用基于运算符的方法部署应用程序。在下一节中,我们将继续讨论运算符,探讨如何使用 Helm 来管理它们。

使用 Helm 管理运算符和 CRs

在前一节中,您首先通过创建位于guestbook-operator/deploy/crds/文件夹下的 CRD 来安装了 Guestbook 运算符。接下来,您创建了位于guestbook-operator/deploy/文件夹下的运算符资源。最后,您创建了 CR 来部署 Guestbook 应用程序。这些任务都是使用 Kubectl CLI 执行的,但也可以使用 Helm 图表来提供更灵活和可重复的解决方案来安装和管理运算符。

Helm 允许您在 Helm 图表中提供一个名为crds/的特殊目录,用于在安装图表时创建 CRDs。Helm 会在templates/文件夹下定义的任何其他资源之前创建 CRDs,使得安装依赖于 CRDs 存在的应用程序(如运算符)更加简单。

以下文件结构描述了一个 Helm 图表,可用于安装 Guestbook 运算符:

guestbook-operator/
  Chart.yaml
  crds/
    charts.helm.k8s.io_guestbooks_crd.yaml
  templates/
    operator.yaml
    role_binding.yaml
    role.yaml
    Service_account.yaml
  values.yaml

安装此 Helm 图表时,首先会安装 Guestbook CRD。如果 CRD 已经存在于集群中,它将跳过 CRD 的创建,而只会创建模板资源。请注意,虽然 CRDs 可以方便地包含在 Helm 图表中,但存在一些限制。首先,Helm 图表中的 CRDs 不能包含任何 Go 模板,因此 CRDs 无法像典型资源那样受益于参数化。CRDs 也永远无法升级、回滚或删除。因此,如果需要执行这些操作,用户必须小心地手动修改或删除 CRDs。最后,如前所述安装此类图表将需要集群管理员权限,这是 Kubernetes 中允许的最高权限,因为图表至少包含一个 CRD 资源。

前面描述的 Helm chart 可以被集群管理员使用,以便轻松安装 Guestbook operator。然而,这只是方程的一半,因为最终用户仍然必须创建 CRs 来部署 Guestbook 应用程序。幸运的是,operator 的最终用户也可以利用 Helm,创建一个包装 Guestbook CR 的 Helm chart。

这样的 Helm chart 的示例布局显示在以下文件结构中:

guestbook-cr
  Chart.yaml
  templates/
    guestbook.yaml
  values.yaml

前面的示例包括一个名为guestbook.yaml的模板。这个模板可以包含最初由operator-sdk CLI 生成的 Guestbook CR,名称为charts.helm.k8s.io_v1alpha1_guestbook_cr.yaml。与 CRDs 不同,templates/文件夹下的 CRs 受益于 Go 模板和生命周期管理,就像所有其他资源一样。当 CR 包含基于用户提供的值有条件地包含的复杂字段,或者当同一个发布中必须包含多个不同的 CRs 时,这种方法提供了最大的价值。通过这种方法,您还可以管理 CRs 的生命周期并保持修订历史。

现在您已经了解了如何创建 Helm operator 以及如何使用 Helm 来帮助管理 Operators,可以在下一节中自由地清理您的 Kubernetes 环境。

清理您的 Kubernetes 环境

首先,运行以下命令来删除您的 Guestbook CRD:

$ kubectl delete crd guestbooks.charts.helm.k8s.io

在继续下一个清理步骤之前,请注意,在问题部分后面提出的一个问题将挑战您编写自己的 Helm charts 来实现使用 Helm 管理 Operators 和 CRs部分讨论的图表设计。您可能希望推迟这些步骤来测试您的实现。

要继续清理工作,请运行以下命令来删除您的chapter8命名空间:

$ kubectl delete ns chapter8

最后,运行minikube stop命令来停止您的 Minikube 虚拟机。

摘要

operator 对于确保期望状态始终与实际状态匹配非常重要。这样的功能允许用户更轻松地维护资源配置的真实来源。用户可以利用基于 Helm 的 operator 来提供这种类型的资源协调,并且很容易上手,因为它使用 Helm 图表作为部署机制。当创建 CR 时,Helm operator 将安装相关的 Helm 图表以创建新的发布。当修改 CR 时,将执行后续升级,并且在删除 CR 时将卸载发布。

为了管理 operator,集群管理员可以创建一个单独的 Helm 图表,用于创建 operator 的资源和 CRDs。最终用户也可以创建一个单独的 Helm 图表,用于创建 operator 的 CRs,以及其他可能相关的任何资源。

在下一章中,我们将讨论 Helm 生态系统中安全性的最佳实践和主题。

进一步阅读

有关 Kubernetes 资源的更多信息,您可以查看以下链接:

问题

  1. Kubernetes operator 是如何工作的?

  2. 使用 Helm CLI 和使用基于 Helm 的 operator 之间有什么区别?

  3. 假设你被要求将现有的 Helm 图表创建为 Helm operator。你会采取哪些步骤来完成这个任务?

  4. 在 Helm operator 中,安装、升级、回滚和卸载的生命周期钩子函数是如何工作的?

  5. 在 Helm 图表中,crds/文件夹的目的是什么?

  6. 在“使用 Helm 管理 Operators 和 CRs”部分中,我们介绍了两种不同的 Helm 图表,可以用来帮助管理 Operators 和 CRs。使用该部分提供的图表布局来实现 Helm 图表。这些图表应该用于安装 Guestbook operator 和安装 Guestbook CR。有关创建 Helm 图表的帮助,请参考“第五章”,构建您的第一个 Helm 图表

第九章:Helm 安全性考虑

正如你可能在本书中意识到的那样,Helm 是一个强大的工具,为用户提供了许多部署可能性。然而,如果不认识和遵循某些安全范例,这种力量可能会失控。幸运的是,Helm 提供了许多方法来将安全性纳入日常使用中,这些方法简单易行,从下载 Helm CLI 到在 Kubernetes 集群上安装 Helm 图表的整个过程中都可以实现。

在本章中,我们将涵盖以下主题:

  • 数据溯源和完整性

  • Helm 图表安全性

  • 关于 RBAC、值和图表仓库的额外考虑

技术要求

本章将使用以下技术:

  • minikube

  • kubectl

  • Helm

  • GNU 隐私保护GPG

Minikube、Kubectl 和 Helm 的安装和配置在第二章准备 Kubernetes 和 Helm 环境中有介绍。

我们还将利用 Packt 仓库中的guestbook图表,位于github.com/PacktPublishing/-Learn-Helm,在本章的后续示例中。如果你还没有克隆这个仓库,请使用以下命令进行克隆。

$ git clone https://github.com/PacktPublishing/-Learn-Helm.git Learn-Helm

数据溯源和完整性

在处理任何类型的数据时,有两个经常被忽视的问题需要考虑:

  • 数据是否来自可靠的来源或者你期望的来源?

  • 数据是否包含你期望的所有内容?

第一个问题涉及数据溯源的主题。数据溯源是关于确定数据的来源。

第二个问题涉及数据完整性的主题。数据完整性是关于确定你从远程位置接收到的内容是否代表你期望接收到的内容,并且可以帮助确定数据在传输过程中是否被篡改。数据溯源和数据完整性都可以使用称为数字签名的概念进行验证。作者可以基于密码学创建一个唯一的签名来签署数据,而数据的消费者可以使用密码工具来验证该签名的真实性。

如果真实性得到验证,那么消费者就知道数据来自期望的来源,并且在传输过程中没有被篡改。

作者可以通过首先创建一个Pretty Good PrivacyPGP)密钥对来创建数字签名。在这种情况下,PGP 指的是 OpenPGP,这是一组基于加密的标准。PGP 侧重于建立非对称加密,这是基于使用两个不同密钥——私钥和公钥的。

私钥应保密,而公钥则设计为共享。对于数字签名,私钥用于加密数据,而公钥由消费者用于解密数据。PGP 密钥对通常使用一个名为 GPG 的工具创建,这是一个实现 OpenPGP 标准的开源工具。

创建 PGP 密钥对后,作者可以使用 GPG 对数据进行签名。当数据被签名时,GPG 在后台执行以下步骤:

  1. 哈希是基于数据内容计算的。输出是一个称为消息摘要的固定长度字符串。

  2. 消息摘要使用作者的私钥加密。输出是数字签名。

要验证签名,消费者必须使用作者的公钥来解密它。这种验证也可以使用 GPG 来执行。

数字签名在 Helm 中发挥两种作用:

  • 首先,每个 Helm 下载都有一个来自维护者之一的数字签名,可用于验证二进制文件的真实性。签名可用于验证下载的来源以及其完整性。

  • 其次,Helm 图表也可以进行数字签名以从相同的验证中受益。图表的作者在打包期间对图表进行签名,图表用户使用作者的公钥验证图表的有效性。

了解数据来源和完整性如何与数字签名相关之后,让我们在本地工作站上创建一个 GPG 密钥对,如果您还没有一个,这将用于详细说明先前描述的许多概念。

创建 GPG 密钥对

要创建密钥对,您必须首先在本地计算机上安装 GPG。请使用以下说明作为在本地计算机上安装 GPG 的指南。请注意,在 Linux 系统上,您可能已经安装了 GPG:

  • 对于 Windows,您可以使用 Chocolatey 软件包管理器,如下命令所示:
> choco install gnupg

您还可以从 https://gpg4win.org/download.html 下载 Win 的安装程序。

  • 对于 macOS,您可以使用 Homebrew 软件包管理器,使用以下命令:
$ brew install gpg

您还可以从 https://sourceforge.net/p/gpgosx/docu](https://sourceforge.net/p/gpgosx/docu/Download/)下载基于 macOS 的安装程序。

  • 对于基于 Debian 的 Linux 发行版,您可以使用apt软件包管理器,如下所示:
$ sudo apt install gnupg
  • 对于基于 RPM 的 Linux 发行版,您可以使用dnf软件包管理器,如下所示:
$ sudo dnf install gnupg

安装了 GPG 之后,您可以创建自己的 GPG 密钥对,我们将在数据来源和完整性讨论中使用它。

配置此密钥对的步骤如下:

  1. 运行以下命令创建新的密钥对。此命令可以从任何目录运行:
$ gpg --generate-key
  1. 按照提示输入您的姓名和电子邮件地址。这些将用于标识您作为密钥对的所有者,并且将是接收您的公钥的人看到的名称和电子邮件地址。

  2. 按下O键继续。

  3. 然后将提示您输入私钥密码。输入并确认所需的用于加密和解密操作的密码短语。

一旦您的 GPG 密钥对创建成功,您将看到类似以下的输出:

图 9.1:成功创建 GPG 密钥对后的输出

图 9.1:成功创建 GPG 密钥对后的输出

输出显示有关公共(pub)和私有(sub)密钥的信息,以及公钥的指纹(输出的第二行)。指纹是用于识别您作为该密钥所有者的唯一标识符。以uid开头的第三行显示了您在生成 GPG 密钥对时输入的姓名和电子邮件地址。

现在您的gpg密钥对已创建,请继续下一节,了解如何验证 Helm 下载。

验证 Helm 下载

第二章中所讨论的,准备 Kubernetes 和 Helm 环境,Helm 可以通过从 GitHub 下载存档的方式进行安装。可以从 Helm 的 GitHub 发布页面(github.com/helm/helm/releases)安装这些存档,方法是选择以下截图中显示的链接之一:

图 9.2:Helm 的 GitHub 发布页面中的安装部分

图 9.2:Helm 的 GitHub 发布页面中的安装部分

安装部分的底部,您会注意到一个段落解释了发布已经签名。每个 Helm 发布都由 Helm 维护人员签名,并可以根据对应于下载的 Helm 发布的数字签名进行验证。每个数字签名都位于资产部分下面。

以下截图显示了这些文件的表示方式:

图 9.3:Helm 的 GitHub 发布页面上的资产部分

图 9.3:Helm 的 GitHub 发布页面上的资产部分

为了验证 Helm 下载的来源和完整性,您还应该下载相应的.asc文件。请注意,.sha256.asc文件仅用于验证完整性。在本例中,我们将下载相应的.asc文件,它将同时验证来源和完整性。

通过以下步骤开始验证 Helm 发布:

  1. 在与您的操作系统对应的安装下下载 Helm 存档。虽然 Helm 二进制文件可能已经安装,但您仍然可以下载存档以便按照示例进行操作。完成示例后,您可以从工作站中删除存档。

  2. 下载与您的操作系统相对应的.asc文件。例如,如果您正在运行基于 AMD64 的 Linux 系统,您将下载helm-v3.0.0-linux-amd64.tar.gz.asc文件。

重要提示

文件名中包含的版本对应于您正在下载的实际 Helm 版本。

下载完这两个文件后,您应该在命令行的同一目录中看到两个类似的文件:

helm-v3.0.0-linux-amd64.tar.gz
helm-v3.0.0-linux-amd64.tar.gz.asc

下一步涉及将 Helm 维护人员的公钥导入到您的本地gpg密钥环中。这样可以解密.asc文件中包含的数字签名,以验证您下载的内容的来源和完整性。可以通过转到其 keybase 帐户来检索维护人员的公钥。将鼠标悬停在keybase 帐户一词上,即可找到该链接。在图 9.2的示例中,此位置解析为keybase.io/bacongobbler。然后,可以通过在末尾添加/pgp_keys.asc来下载公钥,生成的链接为keybase.io/bacongobbler/pgp_keys.asc。

请注意,Helm 有多个维护者,因此如果您对不同版本执行验证,则您的链接可能会有所不同。请确保您下载的是与签署发布的密钥对应的正确公钥。

让我们继续验证过程:

  1. 使用命令行,下载与 Helm 发布签名对应的公钥:
$ curl -o **release_key.asc** https://keybase.io/bacongobbler/pgp_keys.asc
  1. 下载完成后,您需要将公钥导入到您的 gpg 密钥环中。通过运行以下命令来完成:
$ gpg --import release_key.asc

如果导入成功,您将看到以下消息:

gpg: key 92AA783CBAAE8E3B: public key 'Matthew Fisher <matt.fisher@microsoft.com>' imported
gpg: Total number processed: 1
gpg:               imported: 1
  1. 现在已经导入了数字签名的公钥,您可以通过利用 GPG 的--verify子命令来验证 Helm 安装的发布。这应该针对helm*.asc文件运行:
$ gpg --verify helm-v3.0.0-linux-amd64.tar.gz.asc

该命令将尝试解密.asc文件中包含的数字签名。如果成功,这意味着 Helm 下载(以.tar.gz结尾的文件)是由您期望的人(本次发布的Matthew Fisher)签名的,并且下载没有被修改或以任何方式更改。成功的输出如下:

gpg: assuming signed data in 'helm-v3.0.0-linux-amd64.tar.gz'
gpg: Signature made Wed 13 Nov 2019 08:05:01 AM CST
gpg:                using RSA key 967F8AC5E2216F9F4FD270AD92AA783CBAAE8E3B
gpg: Good signature from 'Matthew Fisher <matt.fisher@microsoft.com>' [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 967F 8AC5 E221 6F9F 4FD2  70AD 92AA 783C BAAE 8E3B

在进一步检查此输出时,您可能会注意到“警告”消息,指示该密钥未经认证,这可能会让您对此是否真正成功产生疑问。验证是成功的,但您尚未指示 gpg 维护者的公钥已获得认证,属于他们声称的人。

您可以按照以下步骤执行此认证:

  1. 检查输出末尾显示的主密钥指纹的最后 64 位(8 个字符),与 Helm 发布页面显示的 64 位指纹匹配。正如您从图 9.2中记得的那样,指纹是这样显示的:
This release was signed with 92AA 783C BAAE 8E3B and **can be found** at @bacongobbler's keybase account.
  1. 从前面的代码中可以看出,Helm 发布页面显示了主密钥指纹的最后 64 位,因此我们知道这个公钥确实属于我们期望的人。因此,我们可以安全地认证维护者的公钥。可以通过使用自己的gpg密钥对对公钥进行签名来完成此步骤。使用以下命令执行此步骤:
$ gpg --sign-key 92AA783CBAAE8E3B # Last 64 bits of fingerprint
  1. 在“真的要签名吗?”提示中,输入y

现在您已经签署了维护者的公钥,该密钥现在已经获得认证。现在可以在不显示“警告”消息的情况下运行验证:

$ gpg --verify helm-v3.0.0-linux-amd64.tar.gz.asc
gpg: assuming signed data in 'helm-v3.0.0-linux-amd64.tar.gz'
gpg: Signature made Wed 13 Nov 2019 08:05:01 AM CST
gpg:                using RSA key 967F8AC5E2216F9F4FD270AD92AA783CBAAE8E3B
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   1  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: depth: 1  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
gpg: next trustdb check due at 2022-03-11
gpg: Good signature from 'Matthew Fisher <matt.fisher@microsoft.com>' [full]

数字签名还在验证 Helm 图表的来源和完整性中发挥作用。我们将在下一节中继续讨论这个问题。

签署和验证 Helm 图表

类似于 Helm 维护者如何签署发布版,您可以签署自己的 Helm 图表,以便用户可以验证他们安装的图表实际上来自您,并包含了预期的内容。要签署一个图表,您必须首先在本地工作站上拥有一个gpg密钥对。

接下来,您可以利用helm package命令的某些标志来使用指定的密钥对图表进行签名。

让我们演示如何通过利用 Packt 存储库中的“留言簿”图表来实现这一点。该图表位于Learn-Helm/helm-charts/charts/guestbook文件夹中。我们假设您已经在本地工作站上拥有 gpg 密钥对,但如果没有,您可以按照本章的设置部分中数据来源和完整性部分的说明来配置您的密钥对。

在签署“留言簿”图表之前需要注意的一点是,如果您使用 GPG 版本2或更高版本,则必须将您的公钥和秘钥导出为传统格式。之前的 GPG 版本将密钥环存储在.gpg文件格式中,这是 Helm 期望您的密钥环所在的格式(在撰写本文时)。较新版本的 GPG 将密钥环存储在.kbx文件格式中,目前不受支持。

通过将您的 GPG 公钥和秘钥环转换为.gpg文件格式来开始签名过程:

  1. 通过运行以下命令来查找您的gpg版本:
$ gpg --version
gpg (GnuPG) 2.2.9
libgcrypt 1.8.3
Copyright (C) 2018 Free Software Foundation, Inc.
  1. 如果您的gpg版本是2或更高版本,请使用以下命令导出您的公钥和秘钥环:
$ gpg --export > ~/.gnupg/pubring.gpg
$ gpg --export-secret-keys > ~/.gnupg/secring.gpg

一旦您的密钥环被导出,您就可以对 Helm 图表进行签名和打包。helm package命令提供了三个关键(双关语)标志,允许您对图表进行签名和打包:

--sign:允许您使用 PGP 私钥对图表进行签名

--key:签名时要使用的密钥的名称

--keyring:包含 PGP 私钥的密钥环的位置

在下一步中,这些标志将与helm package命令一起使用,以签署和打包“留言簿”Helm 图表。

  1. 运行以下helm package命令:
$ helm package --sign --key '$KEY_NAME' --keyring ~/.gnupg/secring.gpg guestbook

$KEY_NAME变量可以指代与所需密钥相关的电子邮件、姓名或指纹。这些细节可以通过利用gpg --list-keys命令来发现。

在不签名的情况下使用helm package命令,您预计会看到一个文件作为输出——包含 Helm 图表的tgz存档。在这种情况下,当签名和打包guestbookHelm 图表时,您将看到以下两个文件被创建:

guestbook-1.0.0.tgz
guestbook-1.0.0.tgz.prov

guestbook-1.0.0.tgz.prov文件称为来源文件。来源文件包含一个来源记录,显示以下内容:

  • 来自Chart.yaml文件的图表元数据

  • Helm guestbook-1.0.0.tgz文件的 sha256 哈希值

  • guestbook-1.0.0.tgz文件的 PGP 数字签名

Helm 图表的用户将利用来源文件来验证图表的数据来源和完整性。将图表推送到图表存储库时,开发人员应确保上传 Helm 图表的.tgz存档和.tgz.prov来源文件。

一旦您打包并签署了 Helm 图表,您将需要导出与用于加密数字签名的私钥对应的公钥。这将允许用户下载您的公钥并在验证过程中使用。

  1. 将您的公钥导出为ascii-armor格式,使用以下命令:
$ gpg --armor --export $KEY_NAME > pubkey.asc

如果您公开发布guestbook图表,那么该密钥可以被您的图表用户保存到可下载的位置,例如 Keybase。然后用户可以利用本章节验证 Helm 发布部分描述的gpg --import命令导入此公钥。

图表用户可以利用helm verify命令在安装之前验证图表的数据来源和完整性。该命令旨在针对本地下载的.tgz图表存档和.tgz.prov来源文件运行。

  1. 以下命令提供了针对guestbookHelm 图表运行此过程的示例,并假定您的公钥已导入到名为~/.gnupg/pubring.gpg的密钥环中:
$ helm verify --keyring ~/.gnupg/pubring.gpg guestbook-1.0.0.tgz

如果验证成功,将不会显示任何输出。否则,将返回错误消息。验证可能因多种原因失败,包括以下情况:

.tgz 和.tgz.prov 文件不在同一个目录中。

.tgz.prov 文件损坏。

文件哈希值不匹配,表明完整性丢失。

用于解密签名的公钥与最初用于加密的私钥不匹配。

helm verify命令旨在在本地下载的图表上运行,因此用户可能会发现最好是利用helm install --verify命令,该命令执行验证和安装的单个命令,假设.tgz.tgz.prov文件都可以从图表存储库下载。

以下命令描述了如何使用helm install --verify命令:

$ helm install my-guestbook $CHART_REPO/guestbook --verify --keyring ~/.gnupg/pubring.gpg

通过使用本节描述的签名和验证 Helm 图表的方法,您和您的用户都可以确保您安装的图表既属于您自己,又未经修改。

了解数据可靠性和完整性在 Helm 中起到的作用后,让我们继续讨论 Helm 安全性考虑,转而讨论我们下一个主题——与 Helm 图表和 Helm 图表开发相关的安全性。

开发安全的 Helm 图表

虽然可靠性和完整性在 Helm 的安全性中起着重要作用,但它们并不是您需要考虑的唯一问题。图表开发人员应确保在开发过程中,他们遵守有关安全性的最佳实践,以防止用户在 Kubernetes 集群中安装图表时引入漏洞。在本节中,我们将讨论与 Helm 图表开发相关的安全性的许多主要问题,以及作为开发人员,您可以做些什么来编写以安全性为优先考虑的 Helm 图表。

我们将首先讨论您的 Helm 图表可能使用的任何容器镜像的安全性。

使用安全镜像

由于 Helm(和 Kubernetes)的目标是部署容器镜像,因此镜像本身是一个主要的安全问题。首先,图表开发人员应该意识到镜像标签和镜像摘要之间的区别。

标签是对给定图像的可读引用,并为开发人员和消费者提供了一种确定图像内容的简单方法。然而,标签可能会带来安全问题,因为无法保证给定标签的内容始终保持不变。图像所有者可能会选择使用相同的标签提供更新的图像,例如,以解决安全漏洞,这将导致在运行时执行不同的基础图像,即使标签相同。对相同标签进行这些修改引入了回归的可能性,这可能会对用户造成意外的不利影响。除了使用标签引用图像外,图像也可以通过摘要引用。图像摘要是图像的计算 SHA-256 值,不仅为确切图像提供了不可变标识符,还允许容器运行时验证从远程图像注册表检索到的图像包含了预期的内容。这消除了部署包含对给定标签的意外回归的图像的风险,并且还可以消除中间人攻击的风险,其中标签的内容被恶意修改。

举例来说,可以在图表模板中将图像的引用从quay.io/bitnami/redis:5.0.9改为使用摘要引用,如quay.io/bitnami/redissha256:70b816f2127afb5d4af7ec9d6e8636b2f0f 973a3cd8dda7032f9dcffa38ba11f。请注意,图像名称后面没有标签,而是明确指定了 SHA-256 摘要。这可以确保图像内容随时间不会改变,即使标签发生变化,从而加强了您的安全性。

随着时间的推移,可以预期与图像相关联的标签或摘要将变得不安全,因为最终可能会针对该图像可能包含的软件包或操作系统版本发布漏洞。有许多不同的方法可以确定与给定图像相关联的漏洞。一种方法是利用图像所属的注册表的本机功能。许多不同的图像注册表包含围绕图像漏洞扫描的功能,可以帮助了解图像何时存在漏洞。

例如,Quay 容器注册表可以自动扫描镜像,以确定镜像包含的漏洞数量。Nexus 和 Artifactory 容器注册表也是具有此功能的容器注册表的例子。除了容器注册表提供的原生扫描功能外,还可以利用其他工具,如 Clair(也是Quay的后备扫描技术)、Anchore、Vuls 和 OpenSCAP。当您的镜像注册表或独立扫描工具报告镜像存在漏洞时,如果有新版本可用,您应立即更新图表的镜像,以防止漏洞被引入到用户的 Kubernetes 集群中。

为了简化更新容器镜像的流程,您可以制定一个定期的节奏来检查镜像更新。这有助于防止您的目标镜像包含漏洞,使其不适合部署。许多团队和组织还规定镜像只能来自受信任的注册表,以减少运行包含漏洞的镜像的可能性。此设置在容器运行时级别进行配置,具体位置和配置因运行时而异。

除了镜像漏洞扫描和内容获取之外,您还应避免部署需要提升权限或功能的镜像。功能用于给进程提供一组根权限的子集。一些功能的例子是NET_ADMIN,允许进程执行与网络相关的操作,以及SYS_TIME,允许进程修改系统的时钟。以 root 身份运行容器会赋予容器所有功能,应尽可能限制。功能列表可以在 Linux 手册页的CAPABILITIES(7)页面中找到(http://man7.org/linux/man-pages/man7/capabilities.7.html)。

授予容器功能或允许其以 root 身份运行会使恶意进程更有可能损害底层主机。这不仅影响引入漏洞的容器,还影响在该主机上运行的任何其他容器,可能还影响整个 Kubernetes 集群。如果容器存在漏洞但没有被授予任何功能,攻击向量将小得多,甚至可能完全被阻止。在开发 Helm 图表时,必须考虑图像的漏洞和权限要求,以确保用户和 Kubernetes 集群的其他租户的安全。

除了部署的容器镜像外,图表开发人员还应关注授予应用程序的资源。我们将在下一节中深入探讨这个话题。

设置资源限制

一个 pod 使用属于其底层节点的资源。如果没有适当的默认设置,pod 可能会耗尽节点资源,导致诸如 CPU 限制和 pod 驱逐等问题。耗尽底层节点还将阻止其他工作负载在那里被调度。由于资源限制不受控制时可能出现的问题,图表开发人员应该关注在其 Helm 图表或 Kubernetes 集群中设置合理默认值。

许多图表允许将部署的resources字段声明为 Helm 值。图表开发人员可以在values.yaml文件中默认设置resources字段,设置开发人员认为应用程序应该需要的资源量。以下代码显示了一个示例:

resources:
  limits:
    cpu: 500m
    memory: 2Gi

如果保持默认设置,此示例值将用于将 pod 的 CPU 限制设置为500m,内存限制设置为2Gi。在values.yaml文件中设置此默认值可以防止 pod 耗尽节点资源,同时为所需的应用程序资源量提供建议值。用户可以选择在必要时覆盖资源限制。请注意,图表开发人员还可以为资源请求设置默认值,但这不会阻止 pod 耗尽节点资源。

虽然您应该考虑在values.yaml文件中设置默认资源限制,但您也可以在将要安装图表的 Kubernetes 命名空间中设置限制范围和资源配额。这些资源通常不包括在 Helm 图表中,而是在应用部署之前由集群管理员创建。限制范围用于确定容器在命名空间内允许使用的资源数量。限制范围还用于为每个部署到尚未定义资源限制的命名空间的容器设置默认资源限制。以下是由LimitRange对象定义的示例限制范围:

apiVersion: v1
kind: LimitRange
metadata:
  name: limits-per-container
spec:
  limits:
    - max:
        cpu: 1
        memory: 4Gi
      default:
        cpu: 500m
        memory: 2Gi
      type: Container

LimitRange在创建LimitRange对象的命名空间中强制执行指定的限制。它将允许容器资源的最大数量设置为1cpu核心和4Gi内存。如果未定义资源限制,它会自动将资源限制设置为500mcpu2Gi内存。通过将type字段设置为Pod,还可以在 Pod 级别应用限制范围。这将确保 Pod 中所有容器的资源利用总和在指定限制之下。除了设置对 CPU 和内存利用的限制,您还可以通过将type字段设置为PersistentVolumeClaim来设置LimitRange对象以默认声明PersistentVolumeClaim对象的存储。

这将允许您创建以下资源,以设置单个 PVC 的存储限制:

apiVersion: v1
kind: LimitRange
metadata:
  name: limits-per-pvc
spec:
  - max:
      storage: 4Gi
    type: PersistentVolumeClaim

当然,您也可以在 Helm 图表的values.yaml文件中设置默认存储量。在values.yaml文件中设置的默认值反映了您认为默认安装所需的存储量,LimitRange对象强制执行用户可以覆盖的绝对最大值。

除了限制范围,您还可以设置资源配额以对命名空间的资源使用添加额外限制。虽然限制范围强制执行每个容器、Pod 或 PVC 级别的资源,资源配额则强制执行每个命名空间级别的资源使用。它们用于定义命名空间可以利用的资源的最大数量。以下是一个资源配额的示例:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-and-pvc-quota
spec:
  hard:
    limits.cpu: '4'
    limits.memory: 8Gi
    requests.storage: 20Gi

前面的ResourceQuota对象在应用于 Kubernetes 命名空间时,将最大 CPU 利用率设置为4核,最大内存利用率设置为8Gi,并将命名空间中所有工作负载的最大存储请求设置为20Gi。资源配额还可以用于设置每个命名空间中secretsConfigMaps和其他 Kubernetes 资源的最大数量。通过使用资源配额,您可以防止单个命名空间过度利用集群资源。

通过在 Helm 图表中设置合理的默认资源限制,以及存在LimitRangeResourceQuota,您可以确保 Helm 图表的用户不会耗尽集群资源并导致中断或停机。了解如何强制执行资源限制后,让我们继续讨论 Helm 图表安全性周围的下一个主题——处理 Helm 图表中的机密信息。

在 Helm 图表中处理机密信息

处理机密信息是在使用 Helm 图表时的常见问题。考虑一下来自第三章的 WordPress 应用程序,安装您的第一个 Helm 图表,在那里您需要提供一个密码来配置管理员用户。这个密码在values.yaml文件中默认没有提供,因为如果您忘记覆盖password值,这将使应用程序容易受到攻击。图表开发人员应该养成不为诸如密码之类的机密值提供默认值的习惯,而应该要求用户提供明确的值。这可以通过利用required函数轻松实现。Helm 还具有使用randAlphaNum函数生成随机字符串的能力。

请注意,此函数每次升级图表时都会生成一个新的随机字符串。因此,开发人员应设计图表,期望用户提供自己的密码或其他机密密钥,并且required函数作为确保提供值的门槛。

当用户在图表安装期间提供秘密时,该值应保存在secret中,而不是ConfigMap中。ConfigMaps 以明文显示值,并且不设计包含凭据或其他秘密值。另一方面,Secrets 通过 Base64 编码其内容提供了混淆。Secrets 还允许您将其内容挂载到 pod 作为tmpfs挂载,这意味着内容被挂载到 pod 的易失性内存中,而不是在磁盘上。作为图表开发人员,您应确保由您的 Helm 图表管理的所有凭据和秘密配置都是使用 Kubernetes Secrets 创建的。

图表开发人员应确保使用 Kubernetes Secrets 和required函数适当处理密钥,而图表用户应确保诸如凭据之类的密钥安全地提供给 Helm 图表。通常,值是通过--values标志提供给 Helm 图表的,额外或覆盖的值在单独的values文件中声明,并在安装期间传递给 Helm CLI。这是在处理常规值时的适当方法,但在处理秘密值时应谨慎。用户应确保包含秘密的values文件未被检入git存储库或其他公共位置,以免泄露这些秘密。用户可以通过利用--set标志从本地命令行内联传递秘密来避免泄露秘密。这降低了凭据泄露的风险,但用户应意识到这将在 bash 历史记录中显示凭据。

用户可以通过利用加密工具加密包含秘密的values文件来避免泄露秘密。这将继续允许用户应用--values标志并将values文件推送到远程位置,例如 git 存储库。然后,只有具有适当密钥的用户才能解密values文件,并且对于所有其他用户,它将保持加密状态,只允许受信任的成员访问数据。用户可以简单地利用 GPG 加密values文件,或者他们可以利用特殊工具如SopsSops (https://github.com/mozilla/sops) 是一个设计用于加密 YAML 或 JSON 文件的值但保留密钥未加密的工具。以下代码显示了来自 Sops 加密文件的秘密键/值对:

password:ENC[AES256GCM,data:xhdUx7DVUG8bitGnqjGvPMygpw==,iv:3LR9KcttchCvZNpRKqE5LcXRyWD1I00v2kEAIl1ttco=,tag:9HEwxhT9s1pxo9lg19wyNg==,type:str]

请注意password键是未加密的,但值是加密的。这样可以让您轻松地查看文件中包含的值的类型,而不会暴露它们的机密信息。

还有其他工具可以加密包含机密的values文件。一些例子包括git-https://github.com/AGWA/git-crypt)crypthttps://github.com/AGWA/git-crypt)和blackboxhttps://github.com/StackExchange/blackbox)。此外,诸如 HashiCorp 的Vault或 CyberArk Conjur 之类的工具可以用于以键/值存储的形式加密机密。然后,可以通过使用秘密管理系统进行身份验证,然后通过使用--set将它们传递给 Helm 来检索机密。

了解安全如何在 Helm 图表开发中发挥作用后,现在让我们讨论如何在 Kubernetes 中应用基于角色的访问控制RBAC)以为用户提供更大的安全性。

配置 RBAC 规则

在 Kubernetes 中,经过身份验证的用户执行操作的能力是通过一组 RBAC 策略来管理的。正如第二章中介绍的准备 Kubernetes 和 Helm 环境,策略,也就是角色,可以与用户或服务账户关联,Kubernetes 包含几个可以关联的默认角色。自 Kubernetes 版本1.6以来,RBAC 已默认启用。在考虑 Helm 使用中的 Kubernetes RBAC 时,您需要考虑两个因素:

  • 安装 Helm 图表的用户

  • 与运行工作负载的 pod 关联的服务账户

在大多数情况下,安装 Helm 图表的个人与 Kubernetes 用户关联。但是,Helm 图表也可以通过其他方式安装,例如由与服务账户关联的 Kubernetes 操作员。

在 Kubernetes 集群中,默认情况下,用户和服务账户具有最低权限。通过将权限授予个别命名空间的角色或授予集群级别访问权限的集群角色来获得额外的权限。然后,根据所针对的策略类型,将其与用户或服务账户关联起来,使用角色绑定或集群角色绑定。虽然 Kubernetes 包含了一些可以应用的角色,但应尽可能使用最低权限访问的概念。最低权限访问是指只授予用户或应用程序所需的最小权限集以正常运行。例如,我们之前开发的 guestbook 图表。假设我们想要添加新功能,可以查询 guestbook 应用程序命名空间中的 pods 的元数据。

虽然 Kubernetes 包含一个名为view的内置角色,提供了在给定命名空间中读取 pod 清单所需的权限,但它也可以访问其他资源,如 ConfigMaps 和部署。为了最小化授予应用程序的访问级别,可以创建一个自定义策略,以角色或集群角色的形式,仅提供应用程序所需的必要权限。由于 Kubernetes 集群的大多数典型用户无权在集群级别创建资源,让我们创建一个应用于 Helm 图表部署的命名空间的角色。

要创建一个新角色,可以使用 kubectl create role 命令。一个基本的角色包含两个关键元素:

  • 针对 Kubernetes API 进行的操作类型(动词)

  • 要定位的 Kubernetes 资源列表

例如,为了演示如何在 Kubernetes 中配置 RBAC,让我们配置一组 RBAC 规则,允许经过身份验证的用户在命名空间内查看 pods。

重要提示

如果您想在本地工作站上运行此示例,请确保首先运行 minikube start 来启动 Minikube。

然后可以通过运行 kubectl create ns chapter9 来创建一个名为 chapter9 的新命名空间:

  1. 使用 kubectl CLI 创建一个名为 guestbook-pod-viewer 的新角色:
$ kubectl create role guestbook-pod-viewer --resource=pods --verb=get,list -n chapter9

有了这个新角色,它需要与用户或服务账户关联。由于我们想要将其与在 Kubernetes 中运行的应用程序关联起来,我们将把角色应用到一个服务账户上。当创建一个 pod 时,它会使用一个名为default的服务账户。在尝试遵守最小特权访问原则时,建议使用一个单独的服务账户。这是为了确保在与guestbook应用程序相同的命名空间中没有部署其他工作负载,因为它也会继承相同的权限。

  1. 通过执行以下命令创建一个名为guestbook的新服务账户:
$ kubectl create sa guestbook -n chapter9
  1. 接下来,创建一个名为guestbook-pod-viewers的角色绑定,将guestbook-pod-viewerguestbook ServiceAccount关联起来:
$ kubectl create rolebinding guestbook-pod-viewers --role=guestbook-pod-viewer --serviceaccount=chapter9:guestbook -n chapter9

最后,要使用新创建的guestbook ServiceAccount来运行guestbook应用程序本身,需要将服务账户的名称应用到部署中。

以下显示了在部署 YAML 中serviceAccount配置的外观:

serviceAccountName: guestbook

您可以通过使用您在第五章中创建的图表,或者通过使用位于 Packt 存储库中的图表来轻松安装guestbook应用程序。该图表公开了一组用于配置部署服务账户的值。

  1. 通过运行以下命令安装guestbook Helm 图表:
$ helm install my-guestbook Learn-Helm/helm-charts/charts/guestbook \
--set serviceAccount.name=guestbook \
--set serviceAccount.create=false \
-n chapter9

请注意,在步骤 4中,serviceAccount.create的值设置为false。当您在第五章中使用helm create命令创建 Helm 图表时,提供了在图表安装时创建服务账户的能力。由于您之前已经使用kubectl创建了一个服务账户,这是不需要的。然而,在图表安装期间创建与 RBAC 相关的其他资源的能力并不需要止步于创建服务账户。实际上,如果您的 Helm 图表包含创建角色和角色绑定所需的 YAML 资源,您可以在单个图表安装中执行步骤 1、2 和 3。

  1. 此时,guestbook应用程序具有列出和获取 pod 所需的权限。为了验证这一假设,kubectl有一个命令可以查询用户或服务账户是否有权执行某个操作。执行以下命令来验证ServiceAccount guestbook 是否有权限查询guestbook命名空间中的所有 pod:
$ kubectl auth can-i list pods --as=system:serviceaccount:chapter9:guestbook -n chapter9

--as标志利用了 Kubernetes 中的用户模拟功能,允许调试授权策略。

  1. 该命令的结果应该打印yes作为输出。为了确认服务账户不能访问不应该能够访问的资源,比如列出部署,执行以下命令:
$ kubectl can-i list deployments --as=system:serviceaccount:guestbook:guestbook -n chapter9
  1. 可以使用helm uninstall命令随意删除您的发布:
$ helm uninstall my-guestbook -n chapter9

您还可以停止 Minikube 实例,这在本章的其余部分中是不需要的:

$ minikube stop

no的输出中可以看到,预期的策略已经就位。

当有效使用时,Kubernetes RBAC 有助于为 Helm 图表开发人员提供必要的工具,以强制执行最小特权访问,保护用户和应用程序免受潜在的错误或恶意行为的影响。

接下来,我们将讨论如何保护和访问图表仓库,以增强 Helm 的整体安全性。

访问安全的图表仓库

图表仓库提供了在 Kubernetes 集群上发现 Helm 图表并安装它们的能力。仓库在"第一章: Understanding Kubernetes and Helm" on page 305Understanding Kubernetes and Helm中被介绍为一个包含与仓库中图表相关的元数据的index.yaml文件的 HTTP 服务器。在之前的章节中,我们使用了来自各种上游仓库的图表,并且还使用 GitHub Pages 实现了我们自己的仓库。这些仓库都可以自由使用,供任何感兴趣的人使用。然而,Helm 确实支持整合额外的安全措施来保护仓库中存储的内容,包括以下内容:

  • 认证

  • 安全套接字层/传输层安全SSL/TLS)加密

虽然大多数公共 Helm 存储库不需要任何形式的身份验证,但 Helm 确实允许用户对受保护的图表存储库执行基本和基于证书的身份验证。对于基本身份验证,可以在使用helm repo add命令添加存储库时提供用户名和密码,通过使用--username--password标志。例如,如果您想访问受基本身份验证保护的存储库,则添加存储库将采取以下形式:

$ helm repo add $REPO_URL --username=<username> --password=<password>

然后,存储库可以进行交互,而无需重复提供凭据。

对于基于证书的身份验证,helm repo add命令提供--ca-file--cert-file--key-file标志。--ca-file标志用于验证图表存储库的证书颁发机构,而--cert-file--key-file标志用于分别指定您的客户端证书和密钥。

在图表存储库本身上启用基本身份验证和证书身份验证取决于所使用的存储库实现。例如,流行的图表存储库 ChartMuseum 提供了--basic-auth-user--basic-auth-pass标志,可在启动时用于配置基本身份验证的用户名和密码。它还提供了--tls-ca-cert标志来配置证书身份验证的证书颁发机构(CA)证书。其他图表存储库实现可能提供其他标志或要求您提供配置文件。

即使有了认证,确保 HTTP 服务器和 Helm 客户端之间的传输是安全的也很重要。这可以通过使用安全套接字层(SSL)/传输层安全性(TLS)加密来实现,以保护 Helm 客户端和 Helm 图表存储库之间的通信。虽然需要证书认证,但需要基本认证(和未经认证的存储库)的存储库仍然可以从加密网络流量中受益,因为这将保护认证尝试以及存储库的内容。与认证一样,配置图表存储库上的 TLS 取决于所使用的存储库实现。ChartMuseum 提供了--tls-cert--tls-key标志来提供证书链和密钥文件。更一般的 Web 服务器,如 NGINX,通常需要一个配置文件,提供服务器上证书和密钥文件的位置。像 GitHub Pages 这样的服务已经配置了 TLS。

到目前为止我们使用的每个 Helm 存储库都使用了由公开可用的 CA 签名的证书,这些证书存储在您的 Web 浏览器和底层操作系统中。许多大型组织都有自己的 CA,可以用来生成图表存储库中配置的证书。由于这个证书可能不是来自公开可用的 CA,Helm CLI 可能不信任该证书,添加存储库会导致以下错误:

Error: looks like '$REPO_URL' is not a valid chart repository or cannot be reached: Get $REPO_URL/index.yaml: x509: certificate signed by unknown authority

为了让 Helm CLI 信任图表存储库的证书,CA 证书或包含多个证书的 CA 捆绑包可以添加到操作系统的信任存储中,或者可以在helm repo add命令的--ca-file标志中明确指定。这样可以使命令在没有错误的情况下执行。

最后,根据图表存储库的配置,还可以获取额外的指标来执行请求级别的审计和日志记录,以确定谁尝试访问存储库。

通过使用认证和管理传输层的证书,可以实现增强 Helm 存储库的安全性。

总结

在本章中,你了解了在使用 Helm 时需要考虑的一些安全主题。首先,你了解了如何证明 Helm 发布和 Helm 图表的数据来源和完整性。接下来,你了解了 Helm 图表安全性以及图表开发人员如何在安全方面采用最佳实践来编写稳定和安全的 Helm 图表。最后,你了解了如何使用 RBAC 来创建基于最小特权访问概念的环境,以及如何保护图表存储库以提供 HTTPS 加密并要求身份验证。现在,有了这些概念,你更有能力创建一个安全的 Helm 架构和工作环境。

进一步阅读

问题

  1. 什么是数据来源和完整性?数据来源和数据完整性有什么不同?

  2. 想象一下,你想要证明 Helm 下载的数据来源和完整性。除了发布存档之外,用户需要从 Helm 的 GitHub 发布页面下载哪个文件来完成这个任务?

  3. 用户可以运行哪些命令来验证 Helm 图表的数据来源和完整性?

  4. 作为 Helm 图表开发人员,你可以做些什么来确保部署稳定的容器镜像?

  5. 在 Helm 图表上设置资源限制为什么很重要?还有哪些 Kubernetes 资源可以用来配置 Pod 和命名空间的资源限制?

  6. 什么是最小特权访问的概念?哪些 Kubernetes 资源允许你配置授权并帮助实现最小特权访问?

  7. 可以使用什么命令和一组标志来对图表存储库进行身份验证?

第十章:评估

第一章:理解 Kubernetes 和 Helm

以下是本章中提出的一些问题的答案:

  1. 如果一个应用程序在一个单一应用程序中包含了所有必要的逻辑和功能,那么它就是单体的。单体应用程序可以分解成多个不同的应用程序,称为微服务

  2. Kubernetes 是一个容器编排工具。举几个例子,它解决了关于工作负载调度、可用性和可伸缩性的问题。

  3. 创建描述编辑删除应用

  4. 用户必须了解许多不同类型的资源才能部署应用程序。同时,保持本地和实时状态同步、管理应用程序生命周期以及维护样板 YAML 资源文件也是具有挑战性的。

  5. Helm 包括四个生命周期命令,可以让用户轻松管理 Kubernetes 应用程序。用户可以应用这些命令与 Helm 图表进行交互,Helm 图表是部署应用程序所需的 Kubernetes 资源的打包。Helm 抽象了 Kubernetes 资源的复杂性,并为给定应用程序提供了修订历史,允许将应用程序回滚到先前的快照。它还允许动态生成 YAML 资源,并简化了本地和实时状态之间的同步。最后,Helm 按照预定的顺序应用 Kubernetes 资源,并允许自动化的生命周期钩子,可用于执行各种自动化任务。

  6. 您可以使用helm rollback命令。Helm 为每个应用程序快照分配一个修订版本。当应用程序的一个或多个区域从其先前应用的状态进行修改时,将分配一个新的修订版本。

  7. 安装升级回滚卸载

第二章:准备 Kubernetes 和 Helm 环境

以下是本章中提出的一些问题的答案:

  1. Windows 和 Mac 用户可以使用 Chocolatey 或 Homebrew 软件包管理器安装 Helm。所有用户(Windows、Mac 和 Linux)也可以从 Helm 的 GitHub 发布页面github.com/helm/helm/releases安装 Helm。

  2. Helm 使用本地的kubeconfig文件进行身份验证。

  3. Kubernetes 角色提供授权。管理员可以通过创建RoleBinding来管理这些特权,将角色绑定到用户或组。

  4. helm repo add 命令用于本地配置 Helm 图表存储库。这是安装存储库中包含的图表的要求。

  5. Helm 使用的三个 XDG 环境变量是 XDG_CACHE_HOME、XDG_CONFIG_HOME 和 XDG_DATA_HOME。XDG_CACHE_HOME 用于指定缓存文件的位置(包括从上游图表存储库下载的图表)。XDG_CONFIG_HOME 用于设置 Helm 配置的位置(包括 helm repo add 保存的存储库信息)。XDG_DATA_HOME 用于保存使用 helm plugin install 命令添加的插件信息。

  6. Minikube 允许用户在他们的本地机器上轻松创建单节点 Kubernetes 集群。Minikube 会自动为认证配置 Kubeconfig,并分配给用户 cluster-admin 权限来执行任何所需的操作。

第三章:安装您的第一个 Helm 图表

以下是本章提出的问题的一些答案:

  1. Helm Hub 是上游图表存储库的集中位置。用户可以使用 helm search hub 命令与其交互,或者访问 Helm Hub 网站hub.helm.sh/

  2. helm get 命令用于获取已安装的 Helm 发布的详细信息,例如应用的值和生成的 Kubernetes 资源。helm show 命令用于显示 Helm 图表的一般信息,例如支持的值列表和图表 README。

  3. --set 标志用于提供内联值,对于提供简单值或包含不应保存到文件的机密的值很有用。--values 标志用于通过使用值文件提供值,对于一次提供大量值并将应用的值保存到源代码控制存储库很有用。

  4. helm history 命令可用于列出发布的修订版本。

  5. 如果升级发布而不提供任何值,则默认应用--reuse-values 标志,该标志将重用先前发布中应用的每个值。如果提供了至少一个值,则将应用--reset-values 标志,该标志将将每个值重置为其默认值,然后合并提供的值。

  6. helm history 命令将显示六个发布版本,第六个发布版本表示应用程序已回滚到第 3 个修订版本。

  7. helm list 命令可用于查看部署到命名空间的所有发布。

  8. helm search repo命令可用于列出存储库的每个图表。

第四章:理解 Helm 图表

以下是本章中提出的一些问题的答案:

  1. YAML 是最常用的格式,尽管也可以使用 JSON。

  2. 三个必填字段是apiVersionnameversion

  3. 可以通过在名称等于依赖图表的名称的映射中放置所需的依赖值来引用或覆盖图表依赖的值。还可以使用import-values设置导入值,该设置可用于允许使用不同名称引用依赖值。

  4. 您可以创建升级钩子以确保在运行helm upgrade命令之前进行数据快照。

  5. 您可以提供README.md文件来为您的图表提供文档。您还可以创建templates/NOTES.txt文件,该文件可以在安装时动态生成发布说明。最后,LICENSE文件可用于提供法律信息。

  6. range操作允许图表开发人员生成重复的 YAML 部分。

  7. Chart.yaml文件用于定义有关 Helm 图表的元数据。此文件也称为图表定义。Chart.lock文件用于保存图表依赖状态,提供有关所使用的确切依赖版本的元数据,以便可以重新创建charts/文件夹。

  8. helm.sh/hook注释用于定义钩子资源。

  9. 函数和管道允许图表开发人员在模板中执行复杂的处理和数据格式化。常见函数包括dateincludeindentquotetoYaml

第五章:创建您的第一个 Helm 图表

以下是本章中提出的一些问题的答案:

  1. helm create命令可用于创建新的 Helm 图表。

  2. 声明 Redis 依赖性使您无需在 Helm 图表中创建 Redis 模板。它允许您部署 Redis 而无需知道所需的正确 Kubernetes 资源配置。

  3. helm.sh/hook-weight注释可用于设置执行顺序。按权重升序执行钩子。

  4. fail函数用于立即失败渲染,并可用于限制用户输入以符合一组有效设置。required函数用于声明必需值,如果未提供该值,则图表模板将失败。

  5. 要将 Helm 图表发布到 GitHub Pages 图表存储库,必须首先使用helm package命令将 Helm 图表打包为 TGZ 格式。接下来,应使用helm repo index命令生成存储库的index.yaml文件。最后,存储库内容应推送到 GitHub。

  6. index.yaml文件包含有关图表存储库中包含的每个图表的元数据。

第六章:测试 Helm 图表

以下是本章提出的一些问题的答案:

  1. helm template命令用于在本地生成 Helm 模板。helm lint命令用于检查图表结构和图表定义文件中的错误。它还尝试查找会导致安装失败的错误。

  2. 在安装之前验证图表模板,可以运行helm template命令在本地生成您的 YAML 资源,以确保它们被正确生成。您还可以使用--verify标志在不安装资源的情况下与 API 服务器检查您的 YAML 模式是否正确。helm install --dry-run命令也可以在安装之前与 API 服务器执行此检查。

  3. 可用于检查 YAML 资源样式的工具之一是yamllint工具。它可以与helm template一起使用来检查生成的资源(例如,helm template my-test test-chart | yamllint -)。

  4. 创建图表测试是通过创建一个带有helm.sh/hook: test注释的图表模板来实现的。图表测试通常是执行脚本或简短命令的 Pod。可以通过运行helm test命令来执行它们。

  5. Chart Testing(ct)工具允许 Helm 图表维护者更轻松地在 git monorepo 中测试 Helm 图表。它进行彻底的测试,并确保已修改的图表已增加其版本。

  6. ci/文件夹用于测试多种不同的 Helm 值组合。

  7. 添加--upgrade标志将有助于确保对未增加主要版本的图表未发生回归。它将首先安装图表的旧版本,然后升级到新版本。然后,它将删除发布,安装新版本,并尝试对自身进行升级。测试将在每次安装/升级之间进行。

第七章:使用 CI/CD 和 GitOps 自动化 Helm 流程

以下是本章提出的一些问题的答案:

  1. CI 是一种自动化的软件开发过程,可以在软件发生变化时重复进行。CD 是一组定义的步骤,用于将软件推进到发布过程中(通常称为管道)。

  2. 虽然 CI/CD 描述了软件开发和发布过程,但 GitOps 描述了在 Git 中存储配置的行为。一个例子是将值文件存储在 Git 中,然后应用于将应用程序部署到 Kubernetes。

  3. 用于创建和发布 Helm 图表的 CI 管道可以对 Helm 图表进行 lint、安装和测试。Chart 测试工具可以帮助更轻松地执行这些步骤,特别是在维护图表 monorepo 时。管道还应该打包每个 Helm 图表并将图表部署到图表存储库。对于 GitHub Pages 图表存储库,必须生成index.yaml文件,并将内容推送到存储库。

  4. CI 允许轻松快速地测试和发布图表。它还可以帮助防止在添加新功能时出现回归。

  5. CD 管道将 Helm 图表部署到每个所需的环境,每个环境都是不同的管道阶段。每次部署后都可以使用helm test命令进行烟雾测试。

  6. CD 管道允许用户轻松部署其应用程序,而无需手动调用 Helm CLI 命令。这可以帮助防止在使用 Helm 部署应用程序时出现人为错误的可能性。

  7. 为了维护多个环境的配置,可以使用单独的文件夹来按环境分隔值文件。为了减少样板文件,可以保存一个包含每个环境中使用的通用值的文件,并将其应用于每个 Helm 部署。

第八章:使用 Operator Framework 的 Helm

以下是本章提出的问题的一些答案:

  1. 操作员通过利用自定义控制器和自定义资源来工作。当创建新的自定义资源时,操作员将执行自定义控制器实现的逻辑。对自定义资源的更改也会触发控制器逻辑。操作员通常用于安装和管理应用程序的生命周期。

  2. 当使用 Helm CLI 时,您必须从命令行执行installupgraderollbackuninstall命令。但是,当使用基于 Helm 的 operator 时,当您createmodifydelete自定义资源时,这些命令将自动执行。当使用基于 Helm 的 operator 时,您不必在本地运行任何 Helm CLI 命令。

关于应用程序生命周期,Helm CLI 允许用户回滚到先前的修订版本,而 Helm operator 不允许这样做,因为它不保留修订版本的历史记录。

  1. 您可以首先使用operator-sdk new命令来创建一个新的 Helm operator,将该命令指向现有的 Helm 图表,并使用--helm-chart标志。接下来,您可以使用operator-sdk build命令构建 operator。最后,您可以将 operator 镜像推送到容器注册表。

  2. 安装是通过创建新的自定义资源来执行的。升级是通过修改自定义资源来执行的。如果升级失败,回滚将自动执行,但不能显式执行。卸载是通过删除自定义资源来执行的。

  3. crds/文件夹允许在创建templates/中的内容之前创建自定义资源定义(CRD)。它提供了一种轻松的方式来部署依赖于 CRD 的 operator。

  4. 答案会有所不同,但已在github.com/PacktPublishing/-Learn-Helm/tree/master/ch8-q6-answer提供了这些图表的示例。该示例创建了一个名为guestbook-operator的图表,用于部署 operator 资源(包括 CRD),而另一个图表名为guestbook-cr,用于部署自定义资源。

第九章:Helm 安全考虑

以下是本章中提出的一些问题的示例答案:

  1. 数据溯源是关于确定数据的来源。数据完整性确定您收到的数据是否是您期望的数据。

  2. 用户需要下载附带的.asc文件,其中包含数字签名。

  3. helm verify命令可用于验证本地下载的图表,而helm install --verify命令可用于针对存储在上游图表存储库中的图表。

  4. 您可以整合常规漏洞扫描。您还可以尝试避免部署需要以 root 或 root 权限子集运行的映像。最后,您可以使用 sha256 值引用映像,而不是标签,以确保始终部署预期的映像。

  5. 资源限制有助于防止应用程序耗尽底层节点资源。您还可以利用 LimitRanges 来设置每个 Pod 或 PVC 的最大资源量,并且可以利用 ResourceQuotas 来设置每个命名空间的最大资源量。

  6. 最小权限是指仅授予用户或应用程序所需的最小权限集以正常运行。要实现最小权限访问,您可以使用 Kubernetes 的 RolesRoleBindings 来创建最小权限角色,并将这些角色绑定到用户或组。

  7. helm repo add 命令提供了 --username--password 标志,用于基本身份验证,以及 --ca-file--cert-file--key-file 标志,用于基于证书的身份验证。--ca-file 标志还用于验证图表存储库的证书颁发机构。

posted @ 2024-05-06 18:33  绝不原创的飞龙  阅读(37)  评论(0编辑  收藏  举报