每个-DevOps-工程师都应该知道的-50-个-Kubernates-概念-全-

每个 DevOps 工程师都应该知道的 50 个 Kubernates 概念(全)

原文:annas-archive.org/md5/3611152ac52b8dc999d255ec3dd7fe27

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Kubernetes 背后的理念是让工程师的工作变得更轻松,对吧?虽然这个说法是对的,但每项技术和平台都有其优缺点。归根结底,Kubernetes 的确使得容器化的管理更加高效,但这并不意味着它很容易。许多组织和工程师需要付出大量的努力,才能让 Kubernetes 按照应有的方式运行。

本书的目标以及整体的 50 个概念,旨在帮助缓解这些难题。虽然一本书无法解决所有可能出现的问题,也无法让每个组件都按照预期工作,但总体目标是帮助你以更简便的方式在生产环境中使用 Kubernetes,包括从云到本地到监控和安全等方面的 50 个关键点,涵盖了方方面面。如今,网络上充满了大量的内容和教学方式,旨在帮助你学习 Kubernetes。本书的目的是帮助你提升到下一个水平。

在本书中,你将看到从创建环境到部署服务网格和 Kubernetes 资源的所有内容。老实说,本书中的很多话题实际上足够构成一本书。因此,书中的解释和内容整体有所精简。正因如此,你可能不会在这本书中找到所有答案,但它会为你开始 Kubernetes 生产环境之旅提供一个极好的起点。

有了这 50 个概念,你应该能够将你在本书中学到的知识运用到生产环境中,并最终扩展它们。将你所学应用到实际中,最终能帮助你明白如何深入学习这些概念的方向。

本书适合的人群

本书是为那些希望在生产环境中使用 Kubernetes 的工程师准备的。也许你刚刚学会了 Kubernetes 的基础和初学者知识,现在准备提升到下一个层次。或者,你可能正在准备将 Kubernetes 实施到生产环境中,或者测试适合你环境的容器化工作负载。在这两种情况下,你都可以使用本书作为参考,展示你在生产环境中应考虑的事项。

把这本书当作一本“指南”。它既有理论部分,也有实践部分,还有从头到尾实际可用的代码,帮助你创建和部署 Kubernetes 资源。如前言中所述,这本书无法涵盖每个话题的深度,因为很多话题本身就足够构成一本书,但你可以将它当作“指南”来部署到生产环境。

本书内容概述

第一章当今世界的 Kubernetes,从理论角度讲解了你在当前生态系统中应该如何看待 Kubernetes —— 比如它为何重要,“云原生”究竟意味着什么,以及容器化整体上为工程师做了什么。

第二章启动 Kubernetes 和三大云平台之旅,快速启动集群部署。你将学习如何在 Azure、AWS 和 GCP 中部署 Kubernetes 集群。你将从 UI/GUI 的角度看到如何通过代码部署集群。本章使用 Terraform 进行基础设施即代码IaC),因为这是当前生产环境中最流行的方法。

第三章与其他云伙伴一起运行 Kubernetes,教你如何部署三大最流行的托管 Kubernetes 服务。然而,这并不意味着这些是唯一的方法。在本章中,你将看到一些在生产环境中常用的其他流行选项,它们大多用于测试生产工作负载,因为从成本角度来看它们稍微便宜一些。

在当今云为中心的世界里,你在社交媒体上看到的许多技术营销和内容并未涉及本地部署。现实是,本地部署,尤其是本地 Kubernetes 集群,依然是非常常见的。在第四章本地 Kubernetes 现实检查中,你将了解如何从理论角度和一些实践操作来思考本地部署。

第五章像真正的云原生一样部署 Kubernetes 应用,开始了你将应用部署到云的旅程。在前几章中,你学习了集群管理,这非常重要,但只是整个拼图的一半。拼图的第二部分是实际的 Kubernetes 资源部署。

从上一章的结束处开始,第六章Kubernetes 部署——同样的游戏,下一个层次,将 Kubernetes 资源部署提升到一个新的层次。你将接触到 CI/CD、GitOps 和服务网格部署等概念。这被认为是 Kubernetes 资源部署的“高级”部分,你将在生产中看到很多。

到目前为止,你已经学会了如何部署和管理集群与应用。一旦集群和应用部署完成,你还需要确认它们是否按预期运行。此时,观察性和监控就显得尤为重要,我们将在第七章Kubernetes 监控 与观察性中讨论这个话题。

为了完成任何 Kubernetes 生产部署,你需要在任何资源到达生产环境之前考虑一个重要因素——安全性。安全性是决定一个环境成功与否的关键因素,它决定了你是拥有一个成功的环境,还是度过一个不断处理问题的漫长周末。在第八章安全性现实检查中,你将学习到如何确保 Kubernetes 环境的安全,以及你可以使用的一些关键工具和平台来实现这一目标。

为了最大程度地从本书中受益

本书是理论与实践的良好结合。这是因为理论很重要,但如果你不知道如何实现它,它在实际生产中将没有太大用处。为了跟上本书的内容,你应该能够访问主要的云平台,拥有一些虚拟机,并且可能需要花费一些资金来搭建环境。

如果你使用的是本书的数字版,我们建议你自己输入代码或访问本书的 GitHub 仓库中的代码(链接在下一节提供)。这样可以帮助你避免因复制和粘贴代码而导致的潜在错误。

下载示例代码文件

你可以从 GitHub 下载本书的示例代码文件,网址是 github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know。如果代码有更新,GitHub 仓库中的代码将会更新。

我们还提供了来自我们丰富书籍和视频目录的其他代码包,可以在 github.com/PacktPublishing/ 上查看!快来看看吧!

下载彩色图像

我们还提供了一份包含本书中使用的截图和图表的彩色图像的 PDF 文件。你可以在此下载:packt.link/FQMAS

使用的约定

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

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

代码块如下所示:


terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
    }
  }
}

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


variable "name" { 
       type = string 
       default = "aksenvironment01" 
}

任何命令行输入或输出都按如下格式书写:


sudo systemctl daemon-reload 
sudo systemctl enable crio --now

粗体:表示新术语、重要词汇或屏幕上显示的词语。例如,菜单或对话框中的词语通常以粗体显示。以下是一个示例:“从管理面板中选择系统信息。”

提示或重要说明

以这种方式显示。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果你对本书的任何方面有疑问,请发送邮件至 customercare@packtpub.com,并在邮件主题中提及书名。

勘误:尽管我们已尽力确保内容的准确性,但错误仍然可能发生。如果你发现了本书中的错误,我们将非常感激你向我们报告。请访问 www.packtpub.com/support/errata 并填写表格。

盗版:如果您在互联网上遇到任何我们作品的非法复制品,请提供该位置地址或网站名称,我们将不胜感激。请通过版权@packt.com 与我们联系,并附上材料的链接。

如果您有兴趣成为作者:如果您有专长的主题并且有兴趣撰写或贡献书籍,请访问 authors.packtpub.com

分享您的想法

一旦您阅读完50 个每个 DevOps 工程师应该知道的 Kubernetes 概念,我们很想听听您的想法!请点击这里直接前往 Amazon 评论页面并分享您的反馈。

您的评价对我们和技术社区非常重要,它将帮助我们确保提供优质的内容。

下载此书的免费 PDF 副本

感谢您购买本书!

您喜欢随时随地阅读,但无法将纸质书籍随身携带吗?

您的电子书购买是否无法与您选择的设备兼容?

不用担心,现在购买每本 Packt 图书,您都可以免费获得该书的无 DRM PDF 版本。

随时随地、在任何设备上阅读。直接将您最喜欢的技术书籍中的代码搜索、复制并粘贴到您的应用程序中。

福利不仅仅如此,您还可以独享折扣、新闻通讯以及每天直接送到您邮箱的优质免费内容。

按照以下简单步骤获得福利:

  1. 扫描二维码或访问以下链接

https://packt.link/free-ebook/9781804611470

  1. 提交您的购买凭证

  2. 就是这样!我们将直接通过电子邮件发送您的免费 PDF 和其他福利。

第一部分:前 20 个 Kubernetes 概念——云内外

当工程师首次深入 Kubernetes 时,可能会感觉它像是某种工具。你用它来运行和部署容器。然而,事实并非如此。Kubernetes 本身是一个平台。它几乎是运行容器的一种方法论的子集。Kubernetes,和许多其他平台一样,是为什么整个平台工程师这一职称变得如此流行的原因之一。DevOps 领域正逐渐摆脱仅仅关注工具的思维,转而专注于整个平台和环境。

随着不同平台的出现,问题就来了——你在哪里运行它?50 个概念中的第一组概念将在这里通过 Kubernetes 的整体架构进行解释。

Kubernetes 在云中被广泛使用,但它在本地也被广泛使用。一个例子是某些监管要求。我最近和一位在国防领域工作的同事聊天。由于他们有明显的安全要求,并且需要尽量靠近某些区域,在边缘使用 Kubernetes 是成功的关键。一些边缘 Kubernetes 节点运行在 k3s 上,这是运行 Kubernetes 在 ARM 设备上的一种流行方法。这些 ARM 设备是本地的,所以不在云中。

从另一方面来看,很多组织没有这种监管要求,因此在云中以托管服务方式运行 Kubernetes 是完全合理的。这也是开始使用 Kubernetes 时更简单的一种方法。例如,利用Azure Kubernetes ServiceAKS)比用 Kubeadm 启动一个五节点集群要容易得多。

到这一部分结束时,你应该完全理解如何开始使用 Kubernetes,如何在云中运行它,以及有哪些不同的流行云服务可供你使用。虽然这四章并没有涵盖每一个托管的 Kubernetes 服务,但它们将帮助你了解最流行的服务,并让你对其他服务的外观和使用方法有一个清晰的了解。

本书的这一部分包括以下章节:

  • 第一章今天的 Kubernetes 世界

  • 第二章与 Kubernetes 和三大云平台一起启动

  • 第三章与其他云平台一起运行 Kubernetes

  • 第四章本地 Kubernetes 现实检查

第一章:Kubernetes 在今天的世界

如果你正在阅读这本书,那么很可能你曾经或者仍然在某个层面上身处于技术/IT 领域,无论是从运维方面,开发方面,还是两者兼有——甚至可能是技术领导或产品管理。无论如何,你很可能已经听说过一种叫做Kubernetes的平台/技术。从每个公司,无论大小,都在讨论 Kubernetes 来看,许多工程师和领导人员认为它能够解决很多问题。虽然这是事实,但事情并不简单,像所有让我们生活更轻松的事物一样,Kubernetes 也有一些陷阱。

本章主要是理论性的内容,将回答你关于迁移到云端、混合环境、云原生/特定应用程序以及 Kubernetes 如何接管微服务生态系统等问题的许多疑问。

到本章结束时,你将能够回答一些关于实施 Kubernetes 的优缺点的问题。你将对为什么工程师和领导团队都在转向 Kubernetes 有一个扎实的理解。你也会开始思考你当前的应用程序是什么样子,以及 Kubernetes 是否适合它们。

本章将涵盖以下主题:

  • 向云端转型

  • Kubernetes,新的云操作系统和数据中心

  • 云原生应用程序及其重要性

  • 抽象更容易了,但有个转折

  • 慢慢开始,快速前进

技术要求

本章内容更多是理论而非实践,所以你不必担心任何编码或实验。要跟上本章的内容,以及本书的内容,你应该具备 Kubernetes 初学者水平的知识,具有中级云计算知识,并且有一些应用程序和架构的经验。

在接下来的章节中,本书将包括实验和动手操作。你可以在本书的 GitHub 仓库中找到每个练习的代码:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know

向云端转型

在深入了解 Kubernetes 之前,有一个重要的问题需要问:为什么使用 Kubernetes?现实情况是,许多组织已经多年没有 Kubernetes 也在部署应用程序。1980 年代,工程师们写软件到软盘上时并没有 Kubernetes。那么,为什么现在需要它?

这个问题的答案是复杂的,最好的起点是思考云计算为我们做了什么——不仅仅是云计算本身是什么,更重要的是,从架构师、工程师和管理的角度来看,云计算在部署软件和系统时帮助我们思考了什么。在本节中,你将了解云计算的以下几个方面:

  • 为什么组织关心云计算

  • 云计算为工程师做了什么

  • 抽象如何帮助我们从错误中学习

  • 云计算并不完全是人们想象的那样

让我们仔细看看。

为什么组织关心云计算

组织中的领导团队,无论是 CIO、CTO 还是其他技术领导者,通常会把 Kubernetes 与云计算联系在一起。然而,这其实与事实相去甚远。其原因可能是极其优秀的技术营销,也可能是由于没有足够的亲身经验,无法真正理解云环境下的底层运作。然而,让我们暂时离开这个话题,思考一下为什么每个人都关心云计算。最好的方法是通过一个视觉图示,所以让我们来看看以下图示:

图 1.1 – 数据中心 Web 应用架构

图 1.1 – 数据中心 Web 应用架构

上述图示展示的是一个数据中心架构。它包含了很多组件,有些已经标出,有些则没有,包含以下内容:

  • 服务器

  • 网络设备

  • 服务器之间的连接

  • 以太网电缆和电源电缆

所有这些硬件不仅带来了购买成本,还有雇佣工程师来管理、维护它并保持数据中心正常运转的费用。更不用说,建立一个完整的数据中心大约需要 4 到 6 个月的时间。考虑到数据中心建设所需的时间,再加上硬件的成本和管理,基于云的系统对于任何组织的高级领导团队来说,尤其是从初创企业到财富 500 强企业,开始变得非常有意义。

现在,让我们来看看图 1.2 中的架构。该图展示了一些内容,包括以下几点:

  • RDS(亚马逊的数据库服务)

  • 负载均衡器

  • CDN(内容分发网络)

  • S3 存储桶(AWS 中的云存储)

  • Route 53(AWS 的 DNS 服务)

图 1.2 中的架构图与图 1.1 类似,它们都是数据中心,但只有图 1.2 是虚拟化的,完全体现了数据中心的样子。你可以看到网络组件、存储、数据库、服务器以及一切相关内容。最大不同在于,这里看到的是虚拟化的。这是一个虚拟数据中心,包含虚拟服务。因为有工程师在 AWS 工作,管理硬件、网络和其他外围设备,你不再需要亲自去做这些。你只需关注服务本身,确保它们按预期工作。

不再需要购买硬件。不再需要更换硬盘。不再需要等待 4 到 8 个月,等硬件运送到你的数据中心,然后才能开始构建。相反,一个完整的数据中心只需要几次点击按钮或几行自动化代码就能实现:

图 1.2 – 云 Web 应用架构

图 1.2 – 云 Web 应用架构

根据前面的图示,Kubernetes 就是发挥作用的地方。无论你选择哪种方式,无论是本地部署还是云端,都有大量内容需要管理。即使云计算使基础设施管理变得更简单,创建所有云基础设施仍然需要大量的人员投入和巨大的成本。

以下是需要管理的几个组成部分:

  • 负载均衡器

  • 虚拟机(或裸机服务器)

  • 网络设备(虚拟或物理)

  • 子网、私有 IP、公有 IP 和网关

  • 多个虚拟化硬件和服务的安全性

这只是概括了主要类别。每个类别内都有多个组件(物理和/或虚拟)需要管理。有了 Kubernetes,这些都被抽象化了。没有防火墙或网关,因为它们是通过 kube-proxy 管理的。除了 Kubernetes 节点,你无需部署其他虚拟机,因为应用程序是在 Kubernetes Pods 中运行的。

如果你在 Azure Kubernetes ServiceAKS)或 GKE 等 Kubernetes 服务中运行 Kubernetes,控制平面(有时被称为 API 服务器或主节点,这种描述控制平面已被弃用)的管理完全由你抽象化处理。

AKS、GKE 或其他云 Kubernetes 服务在后台做的事情和你在一堆虚拟机中运行原生 Kubernetes 集群时所做的事情是一样的。底层技术、运作方式以及使用方式都没有变化。唯一改变的是抽象层。

这就是为什么云计算对 Kubernetes 很重要,也就是为什么 CIO、CTO 和工程师应该关注它。

云计算为工程师做了什么

云就是别人的计算机”,正如有些人可能随口说或贴在有趣的贴纸上。正如我们所知,每个笑话中都有一些真理。真相就是,这是对的。当你与云互动时,并不是与某种神奇的服务互动,这些服务就是存在的。相反,你是在与由其他工程师管理的服务互动。

例如,假设你正在使用 Azure 虚拟机或 AWS 中的 EC2 实例。从你的角度来看,你登录到其中一个门户或编写一些 基础设施即代码IaC),几分钟后,你的新虚拟服务器/云服务器就部署好了。在后台,涉及的事情远不止这些。这里面有很多不同的部分,其中包括以下内容:

  • 自动扩展服务器

  • 进行多次检查,确保所使用的物理/裸机服务器有足够的硬盘、CPU 和 RAM 空间。

  • 网络设置

  • 大量自动化

记住,因为你部署的云服务器运行在裸机服务器上,所以必须有人来管理这些服务器并进行维护。云计算是一个你看不见的抽象层。话虽如此,云计算确实为工程师做了很多事情。

让我们以一个初创公司为例。几年前,如果一家初创公司想要在科技领域有所作为,他们需要服务器来托管应用程序和网站。对于一个在狭小办公室甚至某人家中办公的小公司来说,拥有高可用性、冗余性和可扩展性的一层是不可能的。他们根本无法负担硬件、空间和员工来实现这些目标。

借助云计算,他们不再需要担心所有这些事情。相反,初创公司可以专注于构建应用程序并将其部署到云端。将应用程序部署到云端并不容易,确实有其自身的复杂性,但背后的理念是将那些公司可能不想/不必担心的物理需求(服务器、基础设施等)抽象化。

Kubernetes,新的云操作系统和数据中心

Kubernetes 是一个大家都在关注的话题,但与此同时,许多人并不理解为什么。是平台本身吗?还是平台在当今世界为工程师们做了些什么?这些问题的答案是——有点两者兼而有之。Kubernetes 做了很多事情,但其中的主要部分包括以下几点:

  • 部署你的容器化应用程序

  • 扩展你的应用程序

  • 确保你的应用程序高度可用

  • 让你能够保护你的应用程序及其访问用户的安全

这四个要点听起来像是工程师自从第一台大型计算机问世以来就一直在做的事情。那么问题来了,为什么 Kubernetes 这么受欢迎?

云中的 Kubernetes

无论你往哪里看,都感觉总有一种新的方式可以利用 Kubernetes 平台,或者某个新的工具可以让你的生活更轻松。其中一些平台包括以下内容(你将在接下来的章节中了解更多):

  • 云 Kubernetes 服务,如 AKS、谷歌 Kubernetes 引擎GKE)和亚马逊弹性 Kubernetes 服务EKS

  • 平台即服务PaaS)如 OpenShift

  • 无服务器 Kubernetes 平台,如 Azure 容器应用和 AWS Fargate 在 EKS 上的配置

虽然这并不是一个详尽的清单,但你可以看到,仅仅是平台的数量之多,就可能让选择最终该使用哪个变得非常困难。对此问题的半公认答案是,这完全取决于你当前的生态系统。如果你在 AWS,使用 EKS。如果你在 Azure,使用 AKS。如果你是 Red Hat Enterprise 客户,看看 OpenShift。原因是,归根结底,所有这些 Kubernetes 服务都在做同样的事情。它们都在幕后使用 Kubernetes,并利用云服务让你的生活更轻松。

举个例子,如果你使用的是 AKS,可能你会想用 Azure Active Directory (AAD) 来管理谁能访问 AKS 集群中的哪些内容。Azure 使得实现这一点非常简单,因为云中 Kubernetes 服务的目标正是实现这一点。无论你使用哪个云服务,所有公共云的目标都是让你的生活更轻松。一个很好的例子是,你可以通过与 Cloud Identity 的联合,将 AAD 租户、用户和组映射到 GKE 中使用 AAD。

为什么选择 Kubernetes?

本章开始时提出的关于为什么人们想要使用 Kubernetes 的问题已经有了些许答案,但仍有更多需要思考的地方。首先,我们必须思考为什么每个人都趋向于使用 Kubernetes,尤其是在云中使用 Kubernetes 服务的原因。人们使用云中 Kubernetes 服务的答案通常类似于以下几种:

  • 你无需担心底层基础设施

  • 工作节点和控制平面会自动进行扩展

尽管这些回答很棒,但如果 Kubernetes 所做的仅仅是大家已经在技术行业做了多年的事情,你依然无法得到为什么要使用 Kubernetes 的答案。它并没有实现任何新颖或与众不同的东西。

简单来说,人们喜欢 Kubernetes 的原因是它允许你通过 API 与基础设施交互。当你运行类似 kubectl apply -f deployment.yaml 的 Kubernetes 命令时,你实际上是在与 Kubernetes API 进行交互。当你运行类似 kubectl get deployments 的命令时,你也是在与 API 交互。你与 Kubernetes 交互时所做的 99% 的事情都是基于 API 的。这是大量的 GETPOST 请求。Kubernetes 让工程师生活更轻松的原因是,过去为了让一个应用程序在多台服务器上运行,你需要做的工作现在都被抽象化了,所有这些都变成了程序化层面的操作,都是 API。

Kubernetes 作为数据中心

记得数据中心吗?那些有着大而嘈杂的计算机运行、配有一堆风扇和空调的地方?也许你曾经是那个在数据中心里花费几个小时、安装和堆叠服务器,并用背包当枕头在数据中心地板上小睡的人。如果你从来没有做过这些事情,恭喜你算是一个幸运的人!

当我们考虑数据中心时,有几个组件,但我们先来思考工程师关心的主要部分:

  • 服务器

  • 网络设备(防火墙、负载均衡器、路由器、交换机、网关等等)

  • 外部和内部连接性

  • 安全性

  • 在服务器上运行软件和虚拟化的能力

像 LXC 和 Docker 这样的容器化平台能够为我们提供这里提到的第五点——操作系统虚拟化和运行软件的能力——但其他方面怎么办呢?工程师们需要一种方式来编排和管理软件与虚拟化的操作系统。这就是 Kubernetes 的作用所在。

Kubernetes 填补了数据中心每一块拼图:

  • 网络,包括 Pod 到 Pod 的通信、服务、服务网格、Ingress、负载均衡和路由。

  • Pods 和服务之间的安全性与加密

  • 集群的高可用性

  • 部署、管理、扩展和维护各种应用的能力(必须是容器化的)

  • 来自第三方工具(如 AAD 和 IAM 用户/角色)的认证与授权能力

Kubernetes 是一个一站式的解决方案,涵盖了你在数据中心中会遇到的所有内容。最大的区别在于,基础设施(如果你是在云上运行而非本地部署)是完全抽象化的。你不需要担心第一天的操作;你只需要关心如何部署、编排并使应用按照你和团队的需求正常工作。

这里需要考虑的一个重要信息是,新的技术带来新的问题。Kubernetes 并不简单。仅仅因为你不需要在数据中心地板上过夜,并不意味着你不会面临一套全新的问题,去理解并解决。Kubernetes 会让你的工程师生活更轻松吗?是的。Kubernetes 会让你的生活更艰难吗?是的。尽管如此,目标是让你在使用 Kubernetes 时稍微轻松一点,请记住,它不是一个你设定好就能忘记的魔法盒子。

云原生应用及其重要性

在思考创建任何类型的应用、自动化代码或软件时,总是需要某种标准。问题是,存在许多标准,并没有一个适用于所有的解决方案。当然,对于编写代码有一些(应该是)强制性的标准,比如将代码存储在源代码管理中并运行某些类型的测试,但每个组织的工作流将会截然不同。

对于云原生应用和在 Kubernetes 上运行的应用,工作流的思维方式与任何其他应用相同,但有一些真实的、标准化的流程会自动为你实现。这包括以下内容:

  • 简单的自动扩展

  • 自愈能力

  • 开箱即用的网络

  • 以及更多

在接下来的章节中,我们将基于你之前学到的内容,深入探讨云原生应用如何为组织带来价值。

云原生应用为组织带来的价值

从定义上来说,云原生应用使你能够做到以下几点:

  • 轻松扩展

  • 开箱即用地提供高可用性

  • 更高效的部署

  • 相比于裸机/数据中心环境,Kubernetes 使得持续改动变得更加轻松

在考虑云原生应用程序和前述内容时,通常会联想到微服务。微服务的背后理念,正是云原生背后的重要理念之一,即实现更快速、更高效的变更。当你在处理一个单体应用时,应用程序有很多依赖,基本上是紧密结合的。你不能更新应用程序的某一部分而不影响到其余部分。由于单体应用的紧密耦合,蓝绿部署和金丝雀部署会变得更加复杂。自我修复和可扩展性意味着需要扩展整个应用程序,而不仅仅是需要扩展的部分,这通常意味着消耗比实际需要更多的资源(如 RAM、CPU 等)。

云原生和微服务思维方式旨在解决这个问题。在 Kubernetes 中运行微服务带来了一些极大的好处。你可以管理应用程序的副本(副本数)数量。这样,当需要时,你可以将它们扩展或缩减。Pod 的自我修复效率更高,因为如果运行在 Pod 中的某个应用程序部分出现故障,这不是什么大问题,因为它会自动重新启动。运行在 Pod 中的应用程序,Pod 内包含一个或多个容器,采用松散耦合的方式,因此在蓝绿部署或金丝雀部署场景下,使用滚动更新来更新/升级应用程序版本时,失败的可能性要小得多。

在团队方面,尤其是针对单个工程师,微服务帮助非常大。对于单体应用程序,在更改代码库中的任何内容时,团队之间必须进行相当多的协调。尽管团队合作和沟通至关重要,但不应该让每个人都知道你在开发环境中进行的代码更改,尤其是你在测试某个功能时,不想破坏其他人的代码。在当今这个组织希望快速推进的时代,这个过程会使工程团队的效率大幅下降,甚至停滞不前。更不用说,如果工程师想测试某个功能如何与其他应用程序集成时,他们不应该担心整个应用程序的崩溃。这正是微服务的优势所在。

在构建 Kubernetes 架构时,其思想与云原生应用程序相同——一种松散耦合的架构,易于扩展,并且没有大量依赖(因此出现了微服务运动)。你可以在 Kubernetes 上运行单片应用吗?完全可以。它们仍然会自我修复和自动扩展。云原生应用环境和云原生 Kubernetes 的理念是使用微服务风格的架构,但这不应阻止你开始使用 Kubernetes。主要目标是拥有可以通过应用程序编程 接口API)访问的独立服务。

最后的难题是容器化应用程序。甚至在将应用程序放入 Kubernetes 运行之前,它必须先进行容器化。在 Docker 出现之前,关于容器的想法早已存在,其思想是将整个应用程序分解为微小的微服务。构建容器时,它们与以下方面具有相同的思维模式:

  • 自包含执行环境

  • 虚拟化操作系统

  • 具有将整个应用程序分割并合并到单个容器中的能力的微服务架构,以便轻松扩展、更新和替换

世界是基于云的

在今天的工程视角下,组织可以做的最糟糕的事情之一是被抛在后面。组织最不希望看到的情况是,10 年后意识到其系统和依赖关系如此老旧,以至于没有任何组织或软件公司支持它们。在 2015/2016 年之前的黄金法则是确保每 5 到 10 年进行一次架构更新和人员/工程师更新。现在,随着技术发展的速度加快,这个周期更像是每 2 到 5 年。

当看到微软、谷歌和 AWS 等组织时,它们一直在不断发布重大的变化和更新。参加像微软 Build 或 AWS Summit 这样的会议时,主题演讲充斥着变革性技术,云平台上的大量新服务也随时都在推出。事实是,如果组织不想落后,就不能等待超过 5 年来考虑最新的技术。

话虽如此,许多组织不能简单地每六个月或每年升级系统,因为它们规模太大,没有足够的人手进行这些迁移和更新。然而,技术领导者需要开始考虑这将会带来什么样的变化,因为公司的未来将会受到影响。例如,让我们看看过去几年中 Windows Server 的变化。微软过去常常在每次会议上谈论新的 Windows Server 版本和功能。现在,一切都集中在 Azure 上。技术世界正在发生巨大的变化。

Kubernetes 在这里的作用是,它几乎自动地帮助你做出云原生和快速发展的决策。例如,假设(在一个疯狂的世界里)Kubernetes 在三年内消失了。你依然拥有容器化的应用程序和保存在源代码控制中的代码库,并且它们是松耦合的,这意味着你可以将它们运行在任何地方,比如在无服务器服务中,甚至是虚拟机中,万一需要这样做。根据当前的技术趋势,问题不在于是否始终使用 Kubernetes 来防止组织倒下,而是 Kubernetes 为工程师所做的事情,它让你能够在 API 层面管理基础设施和应用程序。

工程人才正向云计算发展

我们最后要讨论的一个小话题是工程师自身的未来。新一代技术专业人士热衷于学习最新最前沿的技术。为什么?因为他们希望能够保持竞争力并获得工作机会。他们希望保持与时俱进,从而拥有长久而健康的职业生涯。这意味着,他们不太愿意学习如何运行数据中心,因为科技界正在告诉每个人要学习云计算。

随着时间的推移,组织越来越难以找到能够管理和维护遗留系统的人才。尽管如此,遗留系统的消失似乎没有尽头。因此,像银行这样的组织仍然在寻找 COBOL 开发人员。问题是,没有哪个工程师愿意在 20 多岁时把自己的职业生涯押注在学习这些遗留技术上。

抽象更简单了,但有个小变化

今天,技术领域最热门的词之一就是抽象。抽象,在最高层次上,意味着将你需要做的某些工作移除掉。例如,如果一个开发者需要运行代码,那么他只需要运行代码。他不需要构建虚拟机或部署网络,他只需要运行应用程序。去除虚拟机或网络的需求就是在抽象出开发者不需要花时间和精力去关注的部分。

抽象的作用

让我们从两个角度来看抽象的作用——开发和运维。

从开发者的角度来看,开发者的目标是规划应用程序的各个部分,编写代码使这些部分能够正常工作,并将其部署以查看各部分如何协同工作。然而,为了部署代码,过去你需要一个服务器、一个操作系统以及其他组件。像 Kubernetes 这样的平台如今不再需要这些。开发者不再需要担心部署虚拟机,只需编写一个包含应用程序的容器镜像的 Kubernetes 清单。再也不需要为第一天的操作而烦恼。

从运营的角度来看,基础设施工程师或云工程师不再需要担心为了订购服务器、部署虚拟机、并努力让操作系统按预期运行而中断当前的工作。相反,他们可以编写 Kubernetes 清单以及其他基于 API 的技术(如 IaC)来确保 Kubernetes 集群能够启动并运行,保持运作,并准备好托管开发者的代码/容器镜像。

抽象化不能做的事情

抽象化不能做的主要事情之一是去除工程相关工作中对逻辑思维和架构思考的需求。抽象化移除了现在被认为是环境中低悬果实的部分。例如,虚拟机需要部署操作系统并管理所有组件,现在可以视为低悬果实,因为另一种选择是部署 Kubernetes 集群,并在 API 层面管理基础设施。

需要记住的重要一点是,工程师和开发者仍然需要思考。抽象化并不是让你按下一个或两个按钮,哗啦一下,应用程序就能运行,并具备扩展性和高度可用性。在这个层次上的抽象化仍然需要扎实的架构、规划和可重复的流程。

从慢慢开始,快速前进

本章的最后部分将涉及如何在实现 Kubernetes 时,既能从慢开始,又能迅速前进的策略。这个想法是你要理解你组织内部发生了什么,才能真正了解 Kubernetes 的需求。一旦你了解了这一点,就可以尽可能快地开始实施,而不必承担技术债务和管理上的困扰。当考虑如何从慢开始、快速前进时,前提是理解围绕 Kubernetes 的对话背后的为什么,然后一旦你知道了这一点,就可以开始迭代。

理解 Kubernetes 的工程需求

每个优秀的工程师都有很多目标,但以下是一些主要目标:

  • 让我的生活更轻松

  • 移除那些不重要的工作

  • 为组织开展以价值为驱动的工作

在解决问题时,凌晨 2:00 醒来并急匆匆地去为开发者让服务器运行起来并不是工程师一天中最有趣的部分。相反,他们更想专注于为组织提供价值。抽象化帮助大量消除不必要的工作,正如去除琐事一样。

开发者也是如此。他们不想担心等待数天或数周(甚至更久)才能让服务器运行并托管应用程序。他们希望有一种快速、高效且可扩展的方式来托管应用程序,而无需坐等。

为了让工程师理解 Kubernetes 的必要性,目标是理解最新和最伟大的技术,以便能够实施它们。这对许多工程师来说通常是有趣的部分,无论是在运维还是开发方面。然而,最重要的部分是理解 Kubernetes 可以消除设置环境的低效果果,而是允许您专注于价值驱动的工作。

理解 Kubernetes 的业务需求

在任何组织的技术计划中,技术/工程方面和业务方面总有两面性。在业务方面,主要的重要部分如下:

  • Kubernetes 是否帮助我们更快地前进?

  • Kubernetes 是否使我们更有效率?

  • Kubernetes 是否帮助我们更快地进入市场?

  • Kubernetes 是否帮助我们减少停机时间和工程开销?

这些问题的答案是肯定的和否定的,作为一名工程师,您必须准备好回答这些问题。黄金法则是,Kubernetes 消除了机房架构和堆栈的复杂性,就像云一样。在与业务讨论 Kubernetes 时,这不是关于“实施这个 Kubernetes 东西,然后所有问题都解决了”的对话。对话更多是围绕“这个 Kubernetes 东西将使我们的生活更轻松”。

规划是困难的部分

作为工程师,无论是在开发还是运维方面,玩耍新技术都是有趣的。学习新技巧、新平台,并加强您的简历以在市场上保持竞争力,这是许多人考虑的事情。虽然这很棒,但您也必须考虑实施 Kubernetes 背后的原因。

在进入下一章之前,请考虑以下三点:

  • 我为什么觉得 Kubernetes 很重要?

  • Kubernetes 如何帮助我的环境进步?

  • Kubernetes 如何使软件部署更容易?

现在,让我们总结一下本章学到的内容。

摘要

在您甚至考虑实施 Kubernetes 之前,您需要了解云对工程师的作用,云原生应用对工程师的作用,以及为什么组织需要开始思考 Kubernetes。这始终是任何工程相关决策的第一步,因为它不仅影响您,还影响整个组织。由于技术世界的变化方式,理解实施基于云的解决方案的必要性以及如何快速移动但慢慢开始,是组织成功实施 Kubernetes 部署和顺畅过渡传统单片应用程序到实施微服务的关键。

现在您知道了实施云原生技术(例如 Kubernetes)背后的原因以及云原生应用程序对组织的影响,现在是时候开始学习如何开始使用 Kubernetes。我们将通过了解如何在前三个云中实现 Kubernetes 服务来开始下一章。

进一步阅读

要了解本章涵盖的更多主题,请查看以下资源:

第二章:使用 Kubernetes 和三大云平台快速入门

在开始你的 Kubernetes 之旅时,典型的第一步是创建一个 Kubernetes 集群。原因是,如果你从创建 Kubernetes Manifest(后续章节会详细介绍)开始,你将没有地方可以部署该 Manifest,因为你没有 Kubernetes 集群。另一点是,Kubernetes 需要大量的云原生运维管理 – 比如监控集群、自动化部署集群和扩展集群等。因此,理解集群创建是 Kubernetes 之旅中至关重要的一步。

在上一章中,你不仅了解了 Kubernetes 为什么如此重要,还了解了为什么工程师在当今世界中想要使用容器编排技术。在本章中,你将通过在三大云平台上创建和管理你自己的 Kubernetes 集群,迅速上手 – 包括 Azure、Amazon Web ServicesAWS)和 Google Cloud PlatformGCP)。

到本章结束时,你将能够在三大云平台中完全创建、部署、管理和自动化 Kubernetes 集群。你在本章中学到的技能还可以应用到其他 Kubernetes 集群部署中。例如,你将使用 Terraform 自动化 Kubernetes 集群的创建,并且可以使用 Terraform 在其他云平台和本地环境中部署 Kubernetes 集群。

本章我们将涵盖以下主题:

  • Azure Kubernetes Service

  • AWS EKS

  • GKE

在每个主题中,你将学习如何在生产环境中正确运行它们。在本章的其余部分,你将深入参与多个以实践为主的实验室,自动化和手动创建资源。

技术要求

在本章开始之前,你应该已经知道如何浏览每个云平台的控制台,并对如何自动化创建云基础设施有一般的理解。虽然深入这些主题会非常有帮助,但这些内容非常庞大,市面上有专门的书籍来讲解它们。因此,在开始之前,你应该了解自动化工作流、Terraform 以及云的基本知识。

要在云中工作,你将需要以下内容,所有这些你都可以注册并获得免费额度:

  • 一个 Azure 账户

  • 一个 AWS 账户

  • 一个 GCP 账户

  • 一个基础设施自动化工具,如 Terraform

本章的代码可以在 GitHub 仓库或目录中找到:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch2

Azure Kubernetes Service

在使用 Microsoft Azure 时,你有几种选择可以在容器和 Kubernetes 中使用:

  • Azure Kubernetes 服务 (AKS)

  • Azure 容器实例

  • Azure 容器 应用 (ACA)

AKS 是在 Azure 中运行 Kubernetes 工作负载的主要方式。你无需担心管理控制平面或 API 服务器,而是只需处理应用程序的部署、扩展和管理或维护云基础设施。然而,仍然有一些工作是需要你管理的,特别是对于工作节点。例如,如果你想扩展 Kubernetes 集群、采用多云模型或实施某种混合云模型,你将完全负责实施该设置。AKS 抽象了管理和扩展控制平面或 API 服务器的需求,但其他所有事情(如扩展工作负载、监控和可观测性)都由你负责。

重要提示

有一项新的服务最近在 2022 年微软 Build 大会上发布,称为 ACA,并且已全面可用(GA)。虽然我们在本书中不会详细讲解 ACA,但你应该知道它本质上是 无服务器 Kubernetes。与 AKS 相比,它有很大的不同,因此,如果你计划使用 ACA,请确保提前了解这些技术领域。

在接下来的章节中,你将首先学习如何手动创建 AKS 集群。之后,你将从手动角度学到的内容应用到 Terraform 中,学习如何自动化这一过程。接着,你将学习从垂直自动扩展的角度来扩展 AKS 集群。最后,你将学习无服务器 Kubernetes。我们开始吧!

手动创建 AKS 集群

在管理 AKS 集群之前,你需要学习如何创建一个。在当今的世界里,你很可能永远不会手动执行这个过程,因为每个组织都需要追求自动化和可重复的思维方式。然而,因为你无法在没有先手动做一遍的情况下进行自动化,所以你将在本节中学习如何手动完成这一过程:

  1. 登录到 Azure 门户。

图 2.1 – Azure 门户

图 2.1 – Azure 门户

  1. 搜索 azure kubernetes services

图 2.2 – 搜索 AKS

图 2.2 – 搜索 AKS

  1. 点击 创建 下拉菜单,选择 创建 Kubernetes 集群 选项:

图 2.3 – 创建 AKS 集群

图 2.3 – 创建 AKS 集群

  1. 选择 Kubernetes 集群的选项,包括集群的名称和你的 Azure 资源组:

图 2.4 – 在创建集群之前添加集群详细信息

图 2.4 – 在创建集群之前添加集群详细信息

  1. 主节点池部分,你可以选择希望为 Kubernetes 工作节点提供的虚拟机VM)大小、节点数量以及是否希望启用自动扩展。云 Kubernetes 服务(如 AKS)的一大优势就是自动扩展。在生产环境中,建议根据需要启用自动扩展。然而,你也需要明白这会带来额外的成本,因为会预配置额外的虚拟机。现在暂时保持默认设置,向下滚动到主节点池部分:

图 2.5 – 指定工作节点大小、节点数量和扩展方法

图 2.5 – 指定工作节点大小、节点数量和扩展方法

  1. 一旦选择了你的选项,在开发环境中可能只有一个节点,但在生产环境中会有所不同,点击蓝色的审查 + 创建按钮。你的 AKS 集群现在已经创建。

现在你知道如何手动创建 AKS 集群了,接下来学习如何使用 Terraform 创建它,以确保你在环境中的过程是可重复的。

使用自动化创建 AKS 集群

在许多生产级别的情况下,你将在 CI/CD 管道中运行以下 Terraform 代码以确保可重复性。为了本节的目的,你可以在本地运行它。你首先会看到 main.tf 配置文件,然后会查看 variables.tf

让我们逐步解析代码。

首先是 Terraform 提供程序本身。azurerm Terraform 提供程序用于通过 API 调用来编程访问 Azure:


terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
    }
  }
}
provider "azurerm" {
  features {}
}

接下来是 azurerm_kubernetes_cluster Terraform 资源块,用于创建 AKS 集群。这里有几个关键参数,包括名称和 default_node_pool 参数块。你可以指定虚拟机大小、节点数量以及节点池名称:


resource "azurerm_kubernetes_cluster" "k8squickstart" {
  name                = var.name
  location            = var.location
  resource_group_name = var.resource_group_name
  dns_prefix          = "${var.name}-dns01"
  default_node_pool {
    name       = "default"
    node_count = var.node_count
    vm_size    = "Standard_A2_v2"
  }
  identity {
    type = "SystemAssigned"
  }
  tags = {
    Environment = "Production"
  }
}

把所有内容整合在一起,你将拥有一个 Terraform 配置,用于在 Azure 中创建 AKS 集群。

现在你有了 Terraform 配置,接下来需要传入变量。这些变量使你的代码保持可重复性——遵循 不要重复自己DRY)原则——这样你就不必不断更改硬编码的值或为每个环境创建新的配置。

有四个变量:

  • name:AKS 集群的名称

  • resource_group_name:AKS 将所在的资源组

  • location:AKS 集群所在的区域

  • node_count:AKS 集群中将有多少个 Kubernetes 工作节点

以下代码块中可以看到这些变量:


variable "name" {
  type = string
  default = "aksenvironment01"
}
variable "resource_group_name" {
  type = string
  default = "devrelasaservice"
}
variable "location" {
  type = string
  default = "eastus"
}
variable "node_count" {
  type = string
  default = 3
}

main.tfvariables.tf 配置文件放在同一目录下,将创建一个用于创建 AKS 集群的 Terraform 模块。你可以在几乎任何环境中使用它,根据需要更改配置(例如节点数量),并使你的过程保持可重复。

扩展 AKS 集群

扩展 AKS 集群是通过实现 Kubernetes 集群自动扩展器来实现的。就像 Azure 虚拟机的自动扩展组一样,AKS 根据工作节点的负载决定如何以及为什么扩展集群,而工作节点就是后台的 Azure 虚拟机。集群自动扩展器通常会以cluster-autoscaler容器镜像的形式部署到 Kubernetes 集群。

登录到 Azure 门户,进入 AKS 服务。在那里,进入设置 | 节点池

图 2.6 – 节点池设置

图 2.6 – 节点池设置

按照以下截图点击三个点,然后选择扩展节点池选项:

图 2.7 – 扩展节点池

图 2.7 – 扩展节点池

扩展节点池窗格会弹出,你将看到自动扩展节点池或手动扩展的选项,并选择你希望提供的节点数量:

图 2.8 – 指定节点数量和扩展方法

图 2.8 – 指定节点数量和扩展方法

从自动化和可重复性的角度来看,你可以做同样的事情。以下是使用enable_auto_scaling参数设置为true时,创建azurerm_kubernetes_cluster_node_pool Terraform 资源的示例:


resource "azurerm_kubernetes_cluster_node_pool" "example" {
  name                  = "internal"
  kubernetes_cluster_id = azurerm_kubernetes_cluster.example.id
  vm_size               = "Standard_DS2_v2"
  node_count            = 1
  enable_auto_scaling = true
  tags = {
    Environment = "Production"
  }
}

节点池实际上是作为 Kubernetes 工作节点运行的 Azure 虚拟机。在考虑自动扩展时,记住水平自动扩展是有成本的。虽然它非常必要,但你应该限制可用的 Kubernetes 工作节点数量。这样,你可以跟踪成本以及容器化应用所需的资源。

AKS 和虚拟 Kubelet

在总结 AKS 时,还有虚拟 Kubelet。虚拟 Kubelet 不是 AKS 特有的。虚拟 Kubelet 允许你将 Kubernetes 连接到其他 API。Kubelet 是运行在每个节点上的节点代理,负责将节点注册到 Kubernetes。AKS 虚拟 Kubelet 注册无服务器容器平台。

在 Azure 中,它是Azure 容器实例ACI)。ACI 是一种无需使用 Kubernetes 即可运行容器的方式。如果某个使用 Kubernetes 的人由于成本或管理原因不想扩展工作节点,他们可以使用 ACI 扩展,它使用虚拟 Kubelet。它本质上告诉 Kubernetes 将部署、Pod 和其他工作负载发送到 ACI,而不是将它们保留在本地 Kubernetes 集群中。

现在,ACA 已经 GA,你可能会看到这种类型的实现减少。然而,对于那些想要扩展,但不想管理大型 AKS 集群的团队来说,它仍然是一个很好的用例。

管理和维护 AKS 集群

一旦 Kubernetes 集群创建并运行,思维模式将从第一天的运维转向第二天的运维。第二天的运维将专注于以下内容:

  • 管理集群

  • 监控和维护集群

  • 部署应用程序并启动服务

在管理 AKS 集群时,最需要考虑的是配置存放的位置以及你正在使用哪些工具来管理它。例如,Terraform 配置可能存放在 GitHub 中,而你可能通过 Azure Monitor 和 AKS 集群中可用的其他 Azure 配置来管理集群。第二天操作(Day-two Ops)是确保集群和配置按照预期运行。重点实际上是在“我的环境是否按预期工作并且表现良好”这个问题上。

在监控、告警和整体可观察性方面,有几个选择。Azure Monitor 和 Azure Insights 已集成在 Azure 中,但如果你有一个多云或混合云环境,你可能需要查看其他选项。此时,Prometheus 和 Grafana 的组合就派上用场了。你选择哪个工具(因为有好几个工具)并不重要,重要的是你监控的内容。你需要同时监控集群本身和集群内的 Kubernetes 资源(例如 Pods、Services 或 Ingresses)。

因为管理 Kubernetes 集群的方式差别不大(除了原生云工具),所以可以安全地假设,无论你使用的是 AKS、EKS 还是 GKE,前进的路径都将是相同的。

AWS EKS

在使用 AWS 时,你有一些选择可以用来使用容器和 Kubernetes:

  • EKS

  • 使用 Fargate 配置文件的 EKS

  • 弹性容器 服务ECS

EKS 是在 AWS 内运行 Kubernetes 工作负载的主要方式。如果你不想走 Kubernetes 路线,但仍然希望具有可扩展性,你可以使用 ECS,它能够让你扩展并创建可靠的微服务,但无需使用 Kubernetes。

与 AKS 一样,在 EKS 中,你无需担心管理控制平面或 API 服务器。你只需要关注管理和扩展工作节点。如果你愿意,你甚至可以更进一步,使用 Fargate 配置文件实现 EKS,它将控制平面或 API 服务器以及工作节点抽象化,确保实现一个完全的 无服务器 Kubernetes 体验。

与 AKS 一样,在接下来的几个部分中,你将首先学习如何手动创建 EKS 集群。之后,你将从手动角度学到的内容自动化使用 Terraform。然后,你将学习如何从垂直自动扩展的角度扩展 EKS 集群。最后,你将结束于无服务器 Kubernetes。

手动创建 EKS 集群

与 AKS 集群类似,在从自动化角度创建 EKS 集群之前,你必须学习如何手动部署它们。在本节中,你将学习如何在 AWS 控制台中使用节点组部署 EKS 集群:

  1. 登录 AWS 门户并搜索 EKS 服务:

图 2.9 – AWS 门户

图 2.9 – AWS 门户

  1. 点击橙色的 添加集群 按钮并选择 创建 选项:

图 2.10 – 添加集群

图 2.10 – 添加集群

  1. 配置 EKS 集群时,您需要提供一些选项来唯一标识它,包括以下内容:

    • EKS 集群名称

    • Kubernetes API 版本

    • IAM 角色

IAM 角色非常重要,因为必须将特定的策略附加到分配给 EKS 集群的角色。这些策略包括以下内容:

  • AmazonEC2ContainerRegistryReadOnly

  • AmazonEKSClusterPolicy

没有前述策略,EKS 集群将无法按预期工作:

图 2.11 – 配置集群

图 2.11 – 配置集群

  1. 接下来,您需要设置网络。您希望使用的最少子网数量是两个公共子网,这些子网在不同的可用区中具有不同的 CIDR。有关完整的推荐列表,请查看 AWS 文档(docs.aws.amazon.com/eks/latest/userguide/network_reqs.html):

图 2.12 – 指定网络配置

图 2.12 – 指定网络配置

  1. 在配置集群端点访问时,您有三个选项:

    • 公共意味着 EKS 集群基本上对外开放

    • 公共和私有意味着 API 服务器或控制平面对外部开放,但工作节点流量将保持内部。

    • 私有意味着 EKS 集群仅在 AWS 虚拟私有 VPC)内部可用:

图 2.13 – 配置集群 API 访问

图 2.13 – 配置集群 API 访问

  1. 从网络角度来看,最后一步是选择容器网络接口CNI)和 CoreDNS 的版本。选择最新版本通常是最合适的:

图 2.14 – 网络附加组件

图 2.14 – 网络附加组件

  1. 点击橙色的下一步按钮。

  2. 创建 EKS 集群的最后一步是 API 日志记录。无论您计划将日志、追踪和指标保存在何处,从可观察性角度来看,如果您希望集群记录任何类型的日志,都必须将此选项设置为开启

图 2.15 – 配置可观察性

图 2.15 – 配置可观察性

  1. 选择日志选项后,点击橙色的下一步按钮,您将进入最后一页,审核并创建您的 EKS 集群。

现在您已经了解了如何手动创建 EKS 集群,是时候学习如何使用 Terraform 创建它,以确保在您的环境中实现可重复的流程。

使用 Terraform 创建 EKS 集群

在许多生产级案例中,您将在 CI/CD 流水线中运行以下 Terraform 代码,以确保可重复性。对于本节的目的,您可以在本地运行它。

首先,您将看到main.tf配置,然后查看variables.tf

因为 AWS EKS 的 main.tf 配置比 EKS 本身要长得多,所以我们将其分成几个部分,以便更容易理解:

  1. 首先是 Terraform 提供者块。为了确保整个团队的可重复性,你可以使用 S3 存储桶作为 TFSTATE 的后端。Terraform 块还包括 AWS Terraform 提供者:

    
    terraform {
    
    
      backend "s3" {
    
    
        bucket = "terraform-state-k8senv"
    
    
        key    = "eks-terraform-workernodes.tfstate"
    
    
        region = "us-east-1"
    
    
      }
    
    
      required_providers {
    
    
        aws = {
    
    
          source = "hashicorp/aws"
    
    
        }
    
    
      }
    
    
    }
    
  2. 接下来,创建第一个资源。该资源允许将 IAM 角色附加到 EKS 集群。为了让 EKS 访问 AWS 的各种组件和服务以及工作节点,需要附加一些策略:

    
    resource "aws_iam_role" "eks-iam-role" {
    
    
      name = "k8squickstart-eks-iam-role"
    
    
      path = "/"
    
    
      assume_role_policy = <<EOF
    
    
    {
    
    
      "Version": "2012-10-17",
    
    
      "Statement": [
    
    
        {
    
    
          "Effect": "Allow",
    
    
          "Principal": {
    
    
            "Service": "eks.amazonaws.com"
    
    
          },
    
    
          "Action": "sts:AssumeRole"
    
    
        }
    
    
      ]
    
    
    }
    
    
    EOF
    
    
    }
    
  3. 在 IAM 角色后面是需要附加到角色的 IAM 策略。成功部署 EKS 所需的两项策略如下:

    • AmazonEKSClusterPolicy:为 Kubernetes 提供所需的权限,以便代表你管理资源:

      
      resource "aws_iam_role_policy_attachment" "AmazonEKSClusterPolicy" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
      
      
        role       = aws_iam_role.eks-iam-role.name
      
      
      }
      
    • AmazonEC2ContainerRegistryReadOnly:如果你决定将容器镜像放置在 Elastic Container Registry 中,这个策略将提供只读访问权限:

      
      resource "aws_iam_role_policy_attachment" "AmazonEC2ContainerRegistryReadOnly-EKS" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
      
      
        role       = aws_iam_role.eks-iam-role.name
      
      
      }
      
  4. 一旦定义了 IAM 角色和策略,就该创建 EKS 集群本身了。EKS 集群资源将创建 EKS 本身,启用日志记录,并附加你之前创建的 IAM 角色:

    
    resource "aws_eks_cluster" "k8squickstart-eks" {
    
    
      name = "k8squickstart-cluster"
    
    
      role_arn = aws_iam_role.eks-iam-role.arn
    
    
      enabled_cluster_log_types = ["api", "audit", "scheduler", "controllerManager"]
    
    
      vpc_config {
    
    
        subnet_ids = [var.subnet_id_1, var.subnet_id_2]
    
    
      }
    
    
      depends_on = [
    
    
        aws_iam_role.eks-iam-role,
    
    
      ]
    
    
    }
    
  5. 下一个资源是另一个 IAM 角色,用于工作节点。创建 EKS 集群时,你将创建多个资源,因为你正在创建两套服务:

    • EKS 集群本身及其所需的所有权限和策略:

    • Kubernetes 工作节点具备所需的所有权限和策略:

      
      resource "aws_iam_role" "workernodes" {
      
      
        name = "eks-node-group-example"
      
      
        assume_role_policy = jsonencode({
      
      
          Statement = [{
      
      
            Action = "sts:AssumeRole"
      
      
            Effect = "Allow"
      
      
            Principal = {
      
      
              Service = "ec2.amazonaws.com"
      
      
            }
      
      
          }]
      
      
          Version = "2012-10-17"
      
      
        })
      
      
      }
      
  6. 一旦创建了工作节点的 IAM 角色,接下来需要附加一些策略:

    • AmazonEKSWorkerNodePolicy:为 Kubernetes 提供所需的权限,以便代表你管理资源:

      
      resource "aws_iam_role_policy_attachment" "AmazonEKSWorkerNodePolicy" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
      
      
        role       = aws_iam_role.workernodes.name
      
      
      }
      
    • AmazonEKS_CNI_Policy:为 Kubernetes 内部网络(kubeproxy)附加 CNI 策略:

      
      resource "aws_iam_role_policy_attachment" "AmazonEKS_CNI_Policy" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
      
      
        role       = aws_iam_role.workernodes.name
      
      
      }
      
    • EC2InstanceProfileForImageBuilderECRContainerBuilds:EC2 Image Builder 使用服务关联角色授权其他 AWS 服务代表你执行操作:

      
      resource "aws_iam_role_policy_attachment" "EC2InstanceProfileForImageBuilderECRContainerBuilds" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/EC2InstanceProfileForImageBuilderECRContainerBuilds"
      
      
        role       = aws_iam_role.workernodes.name
      
      
      }
      
    • AmazonEC2ContainerRegistryReadOnly:如果你决定将容器镜像放在 Elastic Container Registry 中,这个策略将提供只读访问权限:

      
      resource "aws_iam_role_policy_attachment" "AmazonEC2ContainerRegistryReadOnly" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
      
      
        role       = aws_iam_role.workernodes.name
      
      
      }
      
    • CloudWatchAgentServerPolicy:允许工作节点运行 CloudWatch 代理进行监控、日志记录、追踪和指标收集:

      
      resource "aws_iam_role_policy_attachment" "CloudWatchAgentServerPolicy-eks" {
      
      
        policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
      
      
        role       = aws_iam_role.workernodes.name
      
      
      }
      
  7. 创建 IAM 角色和策略后,最后一步是创建 EKS 节点组资源,也就是 Kubernetes 工作节点。你将定义以下内容:

    • IAM 角色和子网 ID:

      
      resource "aws_eks_node_group" "worker-node-group" {
      
      
        cluster_name    = aws_eks_cluster.k8squickstart-eks.name
      
      
        node_group_name = "k8squickstart-workernodes"
      
      
        node_role_arn   = aws_iam_role.workernodes.arn
      
      
        subnet_ids      = [var.subnet_id_1, var.subnet_id_2]
      
      
        instance_types = ["t3.xlarge"]
      
    • 自动扩展所需的目标规模大小:

      
        scaling_config {
      
      
          desired_size = 3
      
      
          max_size     = 4
      
      
          min_size     = 2
      
      
        }
      
    • 资源所依赖的策略:

      
        depends_on = [
      
      
          aws_iam_role_policy_attachment.AmazonEKSWorkerNodePolicy,
      
      
          aws_iam_role_policy_attachment.AmazonEKS_CNI_Policy,
      
      
        ]
      
      
      }
      
  8. 现在你已经有了 Terraform 配置,你需要传入变量。变量让你的代码保持可重复性,这样你就不需要不断更改硬编码值或为每个环境创建新的配置:

你需要的两个变量是你选择的 VPC 中的子网 ID,这些子网将与 EKS 一起使用。你可以传入两个位于不同可用区的公共子网 ID:


variable "subnet_id_1" {
  type = string
  default = ""
}
variable "subnet_id_2" {
  type = string
  default = ""
}

综合来看,你将拥有一个 Terraform 配置,它创建了一个 AKS 集群。

扩展 EKS 集群

扩展 EKS 集群的可能性是通过实现 Kubernetes 集群自动扩展器(Cluster Autoscaler)来实现的。与自动扩展 EC2 实例类似,EKS 根据负载的视角决定如何以及为何扩展集群。集群自动扩展器通常使用 cluster-autoscaler 容器镜像部署到 Kubernetes 集群中。

在 Kubernetes 的 GitHub 仓库中,cluster-autoscaler 目录下有一个云提供商列表。其中一个云提供商是 AWS。在 AWS 目录下,有一个名为 cluster-autoscaler-autodiscover.yaml 的 Kubernetes Manifest 示例,显示它正在使用 cluster-autoscaler 容器镜像。它作为 Kubernetes Deployment 运行在你的集群上,并监听某些资源限制。为了自动扩展集群,你需要一个附加了 AmazonEKSClusterAutoscalerPolicy 策略的 IAM 角色。

现在你了解了如何通过 cluster-autoscaler 扩展 EKS 集群,并知道了如何实现,接下来让我们来谈谈 AWS Fargate 配置文件的无服务器 Kubernetes,以及它们如何帮助自动化日常操作。

EKS Fargate 配置文件

关于 Fargate 配置文件的内容与 AKS 虚拟 Kubelet 和 ACI 爆发类似。然而,你不需要像在 AKS 中那样手动部署虚拟 Kubelet。相反,你可以设置 Fargate 配置文件,作为你的 Kubernetes 工作节点。虚拟 Kubelet 仍然在 Fargate 上运行,与 EKS API 服务器或控制平面交互,但这一切几乎是自动完成的。

这里最大的区别在于,你不需要管理工作节点。相反,Fargate 配置文件就像是无服务器 Kubernetes。你部署的是 EKS 集群,它是 API 服务器或控制平面。然后,你部署一个 Fargate 配置文件,这是你的 Kubernetes 资源(例如,Deployments、Pods 和 Services)运行的地方。你不需要担心集群管理或维护 EC2 实例,后者通常作为 Kubernetes 工作节点运行。

要在 EKS 集群上添加 Fargate 配置文件,你进入 EKS 集群的 计算 选项卡,然后你会看到一个添加或创建 Fargate 配置文件的选项,如下图所示:

图 2.16 – Fargate 配置文件和计算

图 2.16 – Fargate 配置文件和计算

现在你知道如何手动和自动创建 EKS 集群,并且熟悉了自动扩展和无服务器 Kubernetes 的日二操作考虑事项,接下来是时候学习最后一个 大 3 Kubernetes 服务 —— GKE。

GKE

当你使用 GCP 时,在使用容器和 Kubernetes 时,你有几个选项可以选择:

  • GKE

  • GKE 自动驾驶

  • Google Cloud Run

GKE 是在 GCP 内部运行 Kubernetes 工作负载的主要方式。如果您不想走 Kubernetes 路线,但仍然希望具备可扩展性,您可以使用 Google Cloud Run。Cloud Run 使您能够扩展并创建可靠的微服务,但无需 Kubernetes。它支持 Node.js、Go、Java、Kotlin、Scala、Python、.NET 和 Docker。

与 AKS 和 EKS 一样,使用 GKE 时,您无需担心管理控制平面或 API 服务器。您只需要关注管理和扩展工作节点。如果您愿意,您甚至可以进一步实施 GKE Autopilot,它将控制平面和 API 服务器以及工作节点抽象化,从而确保一个完全的 无服务器 Kubernetes 体验。

在容器和 DevOps 社区中,关于哪种云端 Kubernetes 服务是更优选择的讨论一直不断。虽然我们不打算站队,但很多工程师钟爱 GKE,并认为它是实现 Kubernetes 的一种卓越方式。由于 Kubernetes 起源于 Google,因此 GKE 服务在具有深思熟虑的特性和实现上,肯定是非常可靠的。

在以下部分,您将学习如何使用 Terraform 自动创建 GKE 集群,并了解如何通过 GKE Autopilot 思考无服务器 Kubernetes。

重要提示

我们跳过了关于 GKE 集群扩展的部分,因为它与其他云的概念相同。它在后台使用 Kubernetes Autoscaler。所有的自动扩展器都被视为水平自动扩展器,因为它们创建新的工作节点或虚拟机来运行 Kubernetes 工作负载。

使用 Terraform 创建 GKE 集群

在本章中,您已经学习了几种在云中创建 Kubernetes 集群的手动方式。与其继续沿着手动操作的路走,不如直接开始使用 Terraform 自动化创建 GKE 集群的可重复过程。

使用 GKE,您会发现与 EKS 等相比,它所需的代码要少得多。首先,您会看到 main.tf 配置文件,然后查看 variables.tf 文件。让我们解析以下代码:

  1. 首先,您需要使用 Google Terraform 提供程序,在其中指定您要部署 GKE 集群的 GCP 项目 ID 和区域:

    
    provider "google" {
    
    
      project     = var.project_id
    
    
      region      = var.region
    
    
    }
    
  2. 接下来,您将创建 google_container_cluster 资源,即 GKE 集群。它将指定集群名称、区域和工作节点数:

    
    resource "google_container_cluster" "primary" {
    
    
      name     = var.cluster_name
    
    
      location = var.region
    
    
      remove_default_node_pool = true
    
    
      initial_node_count       = 1
    
    
      network    = var.vpc_name
    
    
      subnetwork = var.subnet_name
    
    
    }
    
  3. 最终要创建的资源是 google_container_node_pool 资源,它用于创建 Kubernetes 工作节点。在这里,您可以指定:

    • 工作节点数量:

      
      resource "google_container_node_pool" "nodes" {
      
      
        name       = "${google_container_cluster.primary.name}-node-pool"
      
      
        location   = var.region
      
      
        cluster    = google_container_cluster.primary.name
      
      
        node_count = var.node_count
      
    • 您希望 GKE 拥有访问权限的 GCP 范围(或服务):

      
        node_config {
      
      
          oauth_scopes = [
      
      
            "https://www.googleapis.com/auth/logging.write",
      
      
            "https://www.googleapis.com/auth/monitoring",
      
      
          ]
      
      
          labels = {
      
      
            env = var.project_id
      
      
          }
      
    • 虚拟机类型或大小:

      
          machine_type = "n1-standard-1"
      
      
          tags         = ["gke-node", "${var.project_id}-gke"]
      
      
          metadata = {
      
      
            disable-legacy-endpoints = "true"
      
      
          }
      
      
        }
      
      
      }
      

将所有内容结合在一起,您将拥有一个 main.tf 配置文件,可以用来设置 GKE 集群。

  1. 接下来,让我们看看 variables.tf,它将包含以下内容:

    • GCP 项目 ID:

      
      variable "project_id" {
      
      
        type = string
      
      
        default = "gold-mode-297211"
      
      
      }
      
    • GCP 区域:

      
      variable "region" {
      
      
        type = string
      
      
        default = "us-east1"
      
      
      }
      
    • GKE 所在的 GCP VPC 名称:

      
      variable "vpc_name" {
      
      
        type = string
      
      
        default = "default"
      
      
      }
      
    • 你希望 GKE 附加到的 VPC 内子网名称:

      
      variable "subnet_name" {
      
      
        type = string
      
      
        default = "default"
      
      
      }
      
    • 代码计数(Kubernetes 工作节点):

      
      variable "node_count" {
      
      
        type = string
      
      
        default = 2
      
      
      }
      
    • GKE 集群名称:

      
      variable "cluster_name" {
      
      
        type = string
      
      
        default = "gkek8senv"
      
      
      }
      

你现在可以将接下来的代码放入适当的 main.tfvariables.tf 配置文件中,以创建你的 GKE 环境。

GKE 自动驾驶模式

为了总结 GKE 部分,我们快速讨论一下 GKE 自动驾驶模式。自动驾驶模式和 EKS Fargate 的概念相同。它是无服务器 Kubernetes,这意味着你不必担心管理 GKE 集群的工作节点。相反,你只需关注部署应用程序,并设置任何你希望从 GKE 集群捕获的监控、日志、追踪、警报和指标。

关于多云的一点说明

很多刚开始接触 Kubernetes 的工程师可能不会遇到太多相关问题,但多云环境确实是现实存在的。就像组织不希望仅依赖一个数据中心进行冗余一样,有些组织也不希望仅依赖一个云进行冗余。相反,他们希望考虑多云方法——例如,将 Kubernetes 工作负载从 AKS 扩展到 GKE。

这个实现可能相当复杂,需要大量的与安全相关的权限、跨云的认证和授权能力,以及大量的网络知识,以确保不同云之间的 Kubernetes 集群能够相互通信。因此,强烈建议在实施之前进行充分的研究,并确保所有适当的测试都按预期进行。

总结

虽然多云方法可能不是每个人都关注的焦点,但理解三大云如何与 Kubernetes 一起工作依然非常关键。原因在于,几乎可以肯定,在你使用 Kubernetes 的过程中,你可能会先在一个云中工作,但当需要使用其他云时,你应该做好准备。

在本章中,你学习了如何在 Azure、AWS 和 GCP 中设置、管理和维护 Kubernetes 集群。最大的收获之一是,最终在多个云中设置 Kubernetes 并没有太大区别。它们基本上是在做相同的事情,只是服务名称不同。

进一步阅读

  • 构建 Google 云平台解决方案 由 Ted Hunter、Steven Porter 和 Legorie Rajan PS 编写:

www.packtpub.com/product/building-google-cloud-platform-solutions/9781838647438

  • 在 Azure 上实践 Kubernetes – 第二版 由 Nills Franssens、Shivakumar Gopalakrishnan 和 Gunther Lenz 编写:

www.packtpub.com/product/hands-on-kubernetes-on-azure-second-edition/9781800209671

  • 学习 AWS – 第二版 由 Aurobindo Sarkar 和 Amit Shah 编写:

www.packtpub.com/product/learning-aws-second-edition/9781787281066

第三章:与其他云服务伙伴一起运行 Kubernetes

很可能在本书的整个过程中,甚至到目前为止,你会因为发现有那么多地方和不同的方式可以部署 Kubernetes 而感到目不暇接。现实是,在实际工作中,你将会遇到更多这种“鞭打”的情况。无论你是全职 Kubernetes 工程师还是顾问,每个你去的公司,在部署 Kubernetes 的方式和地点上都会有所不同。

在上一章中,你了解了三大 Kubernetes 云服务——AKS、EKS 和 GKE。然而,在私有云和平台即服务PaaS)解决方案中,仍有许多其他出色的选项。虽然你会看到许多组织,从初创公司到财富 200 强及以上,都在使用流行的 Kubernetes 云服务,如 AKS、EKS 和 GKE,但越来越多的组织开始为次级 Kubernetes 集群使用私有云,甚至是为了节省成本,因为大型云服务提供商通常价格更高。更进一步,一些组织完全放弃了在云中部署 Kubernetes 集群的想法,转而使用 PaaS,接下来的章节会向你解释原因。

在本章结束时,结合上一章的内容,你将能够确定你的组织应该选择哪种解决方案,并理解其使用价值。从个人角度来看,你将掌握多种托管 Kubernetes 服务的知识。这样,你在求职市场中将更具竞争力,并能够使用所有不同的平台。

在本章中,我们将涵盖以下主题:

  • 理解 Linode Kubernetes 引擎

  • 探索 DigitalOcean 托管 Kubernetes

  • 什么是 Kubernetes PaaS,它与其他服务有何不同?

技术要求

对于本章,你应该已经了解一些云技术。关键是所有的云服务或多或少都是相同的。服务名称上有所不同,但它们做的事情基本相同。

如果你对云服务已经比较熟悉,并且曾在一些云服务平台上工作过,那么你在阅读本章时会非常顺利。

要在云服务中工作,你将需要以下内容:

  • 一个 Linode 账户

  • 一个 DigitalOcean 账户

  • 一个 Red Hat 账户

  • 一个 AWS 账户(用于本章的最后部分)

你可以注册所有这些服务并获得免费额度。只需确保在完成操作后关闭 Kubernetes 环境以节省费用。

本章的代码可以在本书的 GitHub 仓库找到,地址是github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch3

理解 Linode Kubernetes 引擎

Linode,最近被 Akami Technologies 收购,是一个开发者友好的私有云平台,因其简洁的仪表盘和功能丰富的平台而广为人知,同时又不会过于复杂。Linode 专注于易用性,秉持“为所有人提供云服务”的理念。Linode 的一些关键特点包括透明定价,几乎无需猜测,工作负载易于扩展,提供完整/公开的 API,以及基于图形界面的云管理器。最重要的是,Linode 以其始终有人的客户支持而闻名。

在比较 Linode 与其他私有云提供商时,Linode 通过提供云 GPU 和高速的出站传输速率,以及出色的客户支持脱颖而出。

在本节中,你将了解为什么你要使用Linode Kubernetes EngineLKE),以及如何设置 LKE 门户、手动创建 LKE 中的 Kubernetes 集群、将相同的手动过程自动化,并部署你的 Kubernetes 工作负载。

为什么选择 LKE?

当你选择云服务时,最后你不希望的事情就是不得不猜测每月账单的金额。这也是人们对云服务以及无服务器技术感到担忧的原因。月度费用可能是未知的,这对于 CFO 来说并不是一个最佳答案。使用 Linode 时,费用是捆绑在一起的,所以你确切知道自己要为哪些服务付费,这对于账单管理员和工程师来说都非常重要。在扩展方面,无论是水平扩展还是垂直扩展,任何工程师都不希望需要坐下来手动计算环境每月会给公司带来多少费用。

另一个显著的成本节省来源是控制平面。与任何 Kubernetes 云服务类似,思想是将 Kubernetes 控制平面/API 服务器抽象化,这样你就不需要担心管理除工作节点和应用程序外的其他任何事情。Linode 不收取控制平面费用,而其他云服务提供商则收取。例如,EKS 和 GKE 每个集群的管理费用为每小时 $10 或每月 $73.00。虽然这看起来不多,但对于那些依靠启动资金并且账单已够多的初创公司来说,他们可能不想再增加一项费用。

手动设置 LKE

现在,你已经了解了选择 Linode 的背后理论,以及一些定价指标和其他使 Linode 优秀的方面,是时候亲自动手,了解如何设置 LKE 了。

在本节中,确保你已通过选择的网页浏览器登录到 Linode。按照以下步骤操作:

  1. 在 Linode 仪表盘中,选择Kubernetes

图 3.1 – LKE 门户

图 3.1 – LKE 门户

  1. 点击蓝色的创建 集群按钮:

图 3.2 – 创建集群按钮

图 3.2 – 创建集群按钮

  1. 选择你的集群名称、LKE 集群所在的区域/位置,以及 Kubernetes API 版本:

图 3.3 – 添加集群标签、区域和 Kubernetes 版本

图 3.3 – 添加集群标签、区域和 Kubernetes 版本

  1. 在选择节点池时,你有几个选项:

    • 专用 CPU:适用于对一致性性能要求较高的工作负载,尤其是日常工作流程

    • 共享 CPU:适用于中等工作负载,如秘密引擎(流量较小的应用)

    • 高内存:适用于对内存要求高的应用程序,如较旧的 Java 应用程序、内存数据库和缓存数据

对于这一部分,你可以选择共享 CPU,因为它是最具成本效益的:

图 3.4 – 工作节点大小

图 3.4 – 工作节点大小

  1. 为了保持成本效益,选择Linode 2 GB选项,并确保将其缩减为1个节点:

图 3.5 – 添加节点池页面

图 3.5 – 添加节点池页面

  1. 在任何生产级环境中,你总是要考虑高可用性HA)。对于 Kubernetes 来说也不例外。LKE 提供了启用 Kubernetes 控制平面的 HA 功能。对于生产环境,你 100%会想要实现这一点。而对于实验/开发环境(即你现在为了学习而构建的环境),你不必启用 HA。完成后,点击蓝色的创建 集群按钮:

图 3.6 – HA 控制平面

图 3.6 – HA 控制平面

现在你已经熟悉了手动创建 LKE 集群的过程,是时候学习如何将其自动化,并使该过程在生产级环境中可重复。

自动化 LKE 部署

现在你知道如何手动创建 LKE 集群,是时候学习如何使用 Terraform 创建它,以确保在你的环境中实现可重复的过程。在许多生产级案例中,你将在 CI/CD 管道中运行以下 Terraform 代码,以确保可重复性。对于这一部分,你可以在本地运行它。

首先,你会看到main.tf配置,然后查看variables.tf

首先是 Terraform 提供程序。该提供程序将使用最新版本的 Linode Terraform 提供程序。为了让 Terraform 与 Linode API 进行交互,你需要传入一个 API 密钥,该密钥可以在你的 Linode 账户中创建:


terraform {
  required_providers {
    linode = {
      source = "linode/linode"
    }
  }
}
provider "linode" {
  token = var.token
}

接下来是linode_lke_cluster资源,它将创建 LKE 集群。在动态块内,你会看到一个for_each循环,指定了基于池数量将创建多少个工作节点。池数量是你希望部署的工作节点的数量(建议生产环境中设置为 3 到 4 个):


resource "linode_lke_cluster" "packtlke" {
    k8s_version = var.apiversion
    region = var.region
    dynamic "pool" {
        for_each = var.pools
        content {
            type  = pool.value["type"]
            count = pool.value["count"]
        }
    }
}

最后一段代码是kubeconfig的输出,其中包含所有用于连接 Kubernetes 集群的身份验证和授权配置:


output "kubeconfig" {
   value = linode_lke_cluster.packtlke.kubeconfig
   sensitive = true
}

现在你已经有了主要的 Terraform 配置,你还需要传递一些变量。这些变量使得你的代码保持可重复性,这样你就不必不断地更改硬编码的值或为每个环境创建新的配置。之所以这么做,是因为由于格式问题,在你阅读本章时,它可能会显得有些不寻常。

在这一部分,你可以在 GitHub 上查看变量,地址是 github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/blob/main/Ch3/LKE/variables.tf

尽管这些都是标准的 Terraform 变量,不需要太多解释,但有一个变量需要特别注意,那就是pools变量。请注意,变量指定了列表类型,其中包括 Linode 上工人节点的数量和大小。之所以将变量设为列表类型,是因为在main.tf配置中,dynamic “pool”块在使用for循环时需要调用一个列表。

在使用 LKE 时,需要牢记的一点是理解 Linode。尽管 Linode 是一个很棒的云服务提供商,但事实上,它在 Kubernetes 的服务和功能方面不如 EKS。例如,EKS 提供了可以配置的 IAM 角色和 RBAC 相关的权限、与 Route53 的 DNS 管理、Secrets 管理、容器注册表以及用于无服务器 Kubernetes 的 Fargate 配置文件。即使是 Azure 和 GCP 也有类似的服务。然而,像 Linode 这样的提供商并没有这些服务。这并不是说 Linode 不好,或者它不是一个优秀的 Kubernetes 提供商,事实上,它非常优秀。然而,Linode 不具备内建的 IAM/RBAC 功能,可能会成为许多生产工程和安全团队的决定性障碍。

现在你已经了解了如何手动和自动创建 LKE 集群,接下来是时候进入下一部分了。

探索 DigitalOcean 托管的 Kubernetes

DigitalOcean 和 Linode 类似,面向的市场是易于使用的云,相较于其他大型云服务(它们似乎有成千上万的服务可供选择)。DigitalOcean 的口号是 更简单的云。更开心的开发者。更好的结果。多年来,DigitalOcean 不仅以其云平台闻名,还以其博客和操作指南而著称。对于许多工程师来说,DigitalOcean 成为学习如何动手实践的标准在线位置。许多作者使用 DigitalOcean 为作者/博主创建的技术写作指南,以遵循最佳实践。

在这一部分,你将学习为什么要使用DigitalOcean 托管的 Kubernetes,Kubernetes 服务的优点,如何手动设置 DigitalOcean 托管的 Kubernetes,并将相同的手动过程以自动化的方式通过 Terraform 实现。

为什么选择 DigitalOcean Kubernetes 引擎?

自 2011 年 DigitalOcean 成立以来,全球开发者一直在使用它,因其易于使用和简单的部署方式。许多工程师甚至使用 DigitalOcean 来托管他们的项目(个人网站、博客、服务器等等)。在许多情况下,它比在大型公共云中创建一堆服务要简单得多。

从易用性的角度来看,DigitalOcean Kubernetes 引擎表现不负众望。与其他任何 Kubernetes 服务类似,其目的是抽象掉管理底层控制平面/API 服务器的需求。这里的核心思想是降低使用 Kubernetes 服务时的进入门槛。

与其他产品如 EKS/GKE/AKS 相比,DigitalOcean Kubernetes 引擎更侧重于 Kubernetes 的 Day Two 操作部分。许多 Kubernetes 服务的复杂性有时会让工程师望而却步,因为他们希望有一个开箱即用的解决方案。

重要提示

尽管 DigitalOcean 托管 Kubernetes 易于使用,但与其他同类产品相比,它从 Kubernetes API 的角度来看有些过时。许多 Kubernetes 服务提供 v1.23 及以上版本的 Kubernetes API,而 DigitalOcean 在撰写本文时只提供到 v1.22.8。请记住这一点,并时常检查,因为你可能需要不同的 API 版本。

手动设置 DigitalOcean 托管 Kubernetes

现在你已经了解了为什么要选择 DigitalOcean 的理论基础,以及一些定价指标和其他使 DigitalOcean 出色的因素,接下来是实际操作,学习如何设置 DigitalOcean Kubernetes 引擎。

对于本节内容,确保你已通过选择的 Web 浏览器登录到 DigitalOcean。按照以下步骤操作:

  1. 在 DigitalOcean 仪表板上,选择Kubernetes

图 3.7 – DigitalOcean 托管 Kubernetes

图 3.7 – DigitalOcean 托管 Kubernetes

  1. 点击蓝色的创建 Kubernetes 集群按钮:

图 3.8 – 创建 Kubernetes 集群按钮

图 3.8 – 创建 Kubernetes 集群按钮

  1. 选择你的地区、VPC 名称和 Kubernetes API 版本。一般建议的 API 版本是始终使用最新版本,除非你有特定的理由不使用最新版本(这个规则适用于任何 Kubernetes 环境):

图 3.9 – 添加集群详细信息

图 3.9 – 添加集群详细信息

  1. 选择集群容量。这里有两个非常重要的部分:

    • 机器类型:在这一步,你需要选择最适合你和你的生产环境的选项。虽然 DigitalOcean 没有 Linode 那么多的选项,但你可以从基本节点、基于 Intel 的节点或基于 AMD 的节点中选择(从 CPU 角度来看)。

    • 高可用性控制平面:对于这一点,你总是希望确保控制平面具有高可用性。控制平面包含调度器、etcd 以及许多其他重要的 Kubernetes 组件。没有它们,Kubernetes 就无法工作。

图 3.10 – 工作节点大小

图 3.10 – 工作节点大小

  1. 通过查看每月费用并点击绿色的 创建 集群 按钮,确认你的集群:

图 3.11 – 完成集群

图 3.11 – 完成集群

现在你已经熟悉了手动创建 DigitalOcean Kubernetes 引擎集群的过程,是时候学习如何自动化它,并使这一过程在生产级环境中可重复执行。

自动化 DigitalOcean 管理的 Kubernetes

从自动化的角度来看,你有几种选择。最流行的两种是 DigitalOcean CLI 和 基础设施即代码IaC)。在本节中,你将学习如何使用 Terraform 创建一个 DigitalOcean 管理的 Kubernetes 集群。

在许多生产级案例中,你将在 CI/CD 流水线中运行以下 Terraform 代码,以确保可重复性。对于本节内容,你可以在本地运行它。

就像我们为 LKE 所做的那样,首先,我们将查看 main.tf 配置,然后你将查看 variables.tf

Terraform 配置像所有其他配置一样开始:从提供商开始。DigitalOcean Terraform 提供商要求你传入一个 DigitalOcean API 令牌,你可以从 DigitalOcean UI 生成它:


terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
    }
  }
}
provider "digitalocean" {
  token = var.do_token
}

接下来,需要一个资源块,用于创建整个集群和节点池。这些是 DigitalOcean Droplets,最终将成为 Kubernetes 工作节点。它还创建了水平自动扩展。对于某些 DigitalOcean 账户,最大 Droplet 数量为三,因此你很可能需要为生产环境增加这个数量:


resource "digitalocean_kubernetes_cluster" "packtdo" {
  name    = var.cluster_name
  region  = var.region
  version = var.k8s_version
  node_pool {
    name       = "autoscale-worker-pool"
    size       = "s-2vcpu-2gb"
    auto_scale = true
    min_nodes  = 2
    max_nodes  = 3
  }
}

现在你已经有了 Terraform 配置,你需要传递变量。这些变量允许你的代码保持可重复性,这样你就不需要不断更改硬编码值或为每个环境创建新配置。

有四个变量,如下所示:

  • region:DigitalOcean Kubernetes 引擎集群运行的区域。

  • cluster_name:Kubernetes 集群的名称。

  • K8s_version:Kubernetes API 版本。

  • do_token:DigitalOcean API 令牌。对于生产级环境,你会希望将其存储在某种类型的密钥存储中,并让 Terraform 通过数据块提取它。将 API 令牌写入变量并推送到源代码控制中是绝对不允许的:


variable "region" {
    type = string
    default = "nyc1"
}
variable "cluster_name" {
    type = string
    default = "packtdo01"
}
variable "k8s_version" {
    type = string
    default = "1.22.11-do.0"
}
variable "do_token" {
    type = string
    default = ""
    sensitive = true
}

在这一部分关于 DigitalOcean 的内容总结时,需要记住的一个点是,在 理解 Linode Kubernetes 引擎 部分中提到的相同内容——更大的云服务商将拥有更多可以与托管的 Kubernetes 服务相结合的服务。在你决定什么最适合你的环境时,这一点需要特别注意。

在本章的下一节也是最后一节中,你将从理论和实践的角度了解 OpenShift 上的 PaaS。

什么是 Kubernetes PaaS,它有什么不同?

工程师们感觉到,以不同方式部署 Kubernetes 集群好像是逐步发展的。最初是原生 Kubernetes 集群。你必须手动部署所有东西,从控制平面到 证书授权机构 (CA) 及其间的一切。之后,出现了云中的 Kubernetes 服务,如 AKS、GKE 和 EKS。现在,有了无服务器 Kubernetes,如 GKE AutoPilot 和 EKS Fargate,正如你在上一章中所学到的那样。

另一个特别突出的选项,尤其是在企业中,是基于 PaaS 的 Kubernetes 解决方案,例如 Red Hat 的 OpenShift

在本节中,你将了解为什么要使用 OpenShift,企业如何利用基于 PaaS 的 Kubernetes(如 OpenShift),以及如何在本地计算机上使用 OpenShift 设置 Dev 环境,开发和部署生产就绪的 OpenShift 集群到主要的云服务商,并在 OpenShift 内部部署生产就绪的应用程序。

OpenShift

OpenShift 是一个充满矛盾的存在,介于完整的 Kubernetes 和它自己独立的调度系统之间。OpenShift 在底层使用 Kubernetes。如果你为 Deployment、Pod 等编写 Kubernetes 清单,你可以在 OpenShift 上使用它。从本质上讲,Kubernetes 和 OpenShift 在功能上没有区别。然而,在管理 OpenShift 和管理 Kubernetes 的方式上有所不同。OpenShift 是一个 PaaS,而 Kubernetes 可以由云服务商进行管理,所以它在某种程度上感觉像是 软件即服务 (SaaS),并且可以从裸机角度进行管理。由于 Kubernetes 是一个非常灵活的平台,它不能被归类为单一类别。

使用 OpenShift 时必须记住的一点是,它是企业专用的。除非是为了学习(这正是你在本章所做的),否则工程师不太可能在实验环境中使用 OpenShift。使用 Kubernetes,你有更多的部署选择以及关于部署位置的选择。而 OpenShift 限制了你使用某种类型的虚拟机,以及部署的位置/方式。这并不意味着它是坏事。OpenShift 不是为了让工程师像使用 minikube 和 Docker Desktop 那样做实验而设计的,它是为了企业客户而构建的。如果你有兴趣深入了解这个话题,我强烈推荐阅读 Tomasz Cholewa 关于 Kubernetes 和 OpenShift 比较的这篇博客:blog.cloudowski.com/articles/10-differences-between-openshift-and-kubernetes/

根据 Red Hat 的定义,OpenShift “Red Hat OpenShift 为传统和云原生应用提供了完整的应用平台,使它们能够在任何地方运行。基于 Red Hat Enterprise Linux,并兼容 Red Hat Ansible Automation Platform,Red Hat OpenShift 实现了在 Kubernetes 集群内外的自动化。”

简而言之,它允许您在 PaaS 环境中编排和管理容器化应用程序。

OpenShift 在企业中的应用

此时,你可能会想知道为什么有人会选择在标准 Kubernetes 部署之上使用 OpenShift。Kubernetes 获得了大量支持,被所有主要的云提供商支持,并且是最新最好的技术。在企业环境中,Kubernetes 的认知有所不同。

对于领导团队来说,Kubernetes 往往被认为是一个神秘的黑盒子,会花费大量金钱来维护和支持。事实上,在企业环境中,领导团队希望在出现问题时能够拨打支持电话或联系客户经理。他们希望有企业软件,这样如果(或者说当)出现问题,工程团队就知道有人可以联系。尽管工程师们很可能会花更多的时间等待支持回复,而不是自己动手解决,企业许可给领导团队带来了安心感。然而,安心也伴随着成本。OpenShift 许可费用非常昂贵,而且还要记住,你需要在某个地方运行它,这也会产生费用。例如,如果你在 AWS 上运行 OpenShift,你需要支付在 AWS 上运行的云基础设施费用、OpenShift 许可费用和 Red Hat 支持费用。如果你决定选择 OpenShift,确保你的领导团队了解这些成本。

从技术和工程的角度来看,OpenShift 和 Kubernetes 并没有做什么不同的事情。当然,要让 Kubernetes 像 OpenShift 一样工作,需要一些工作和工程努力来构建,但这些都是完全可行的。虽然 OpenShift 是一个很棒的平台,但与 Kubernetes 相比,并没有做什么特别出众的事情。

开始使用 OpenShift 沙盒

在花费资金购买 OpenShift 之前,您可以在沙盒环境中使用 OpenShift ReadyContainers 进行测试。虽然沙盒环境不适合生产使用,但这是一个测试和熟悉 OpenShift 工作方式的好方法。它也非常适合实验室环境!请按照以下步骤操作:

重要提示

如果你使用的是 M1 Mac,OpenShift ReadyContainers 目前不支持 ARM 设备,因此这个实验不适合你。

  1. 登录红帽控制台:console.redhat.com/

  2. 点击 OpenShift

图 3.12 – 红帽控制台

图 3.12 – 红帽控制台

  1. 选择 Clusters 选项:

图 3.13 – 集群

图 3.13 – 集群

  1. Clusters 选项下,你会看到三个选项 – CloudDatacenterLocal。选择 Local

图 3.14 – 本地集群

图 3.14 – 本地集群

  1. 点击蓝色 Download OpenShift Local 按钮下载 OpenShift 本地:

图 3.15 – 下载 OpenShift 本地

图 3.15 – 下载 OpenShift 本地

  1. 一旦安装了 OpenShift Local,你将需要运行两个命令(安装 CRC 的说明可以在这里找到:crc.dev/crc/#minimum-system-requirements-operating-system_gsg):

    1. crc setup: 设置配置以进行 Red Hat 认证

    2. crc start: 启动本地 OpenShift 集群:

图 3.16 – OpenShift 本地设置

图 3.16 – OpenShift 本地设置

  1. 当你运行 crc start 命令后,你会在终端上看到类似以下的输出:

图 3.17 – 启动 OpenShift 本地

图 3.17 – 启动 OpenShift 本地

根据你阅读的时间和基于版本变化,你可能需要一些配置,包括在本地主机传递 OpenShift 令牌进行身份验证。为了简化这些步骤,因为这些信息已经在 Red Hat 提供的安装指南中可用,你可以在这里按照安装说明操作:access.redhat.com/documentation/en-us/red_hat_openshift_local/2.5/html/getting_started_guide/installation_gsg#installing_gsg

虽然在本节中我们没有涉及到,但还有第二个实验环境选项称为 OpenShift Sandbox,它不同于 ReadyContainers。你可以在这里设置 OpenShift Sandbox:developers.redhat.com/developer-sandbox

现在你知道如何启动和运行 OpenShift 沙盒了,让我们学习如何在 AWS 上设置一个生产就绪的 OpenShift 集群。

在 AWS 上的 OpenShift

CodeReady containers 非常棒,因为它们允许你利用本地计算机学习 OpenShift,就像 Minikube 让你免费学习 Kubernetes 和 Docker Desktop 让你免费学习 Docker 一样。

现在你已经了解了免费版本,让我们快速看看如何将 OpenShift 部署到云端。在本例中,你将学习 AWS,但其他支持的云提供商有相同的工作流程。

对于本节,确保你通过 AWS CLI 登录到 AWS 控制台,并且你也已经登录到你的 Red Hat 账户。按照以下步骤操作:

  1. 登录到 Red Hat 控制台:console.redhat.com/

  2. 点击 OpenShift

图 3.18 – OpenShift 管理器

图 3.18 – OpenShift 管理器

  1. 点击 Clusters 按钮,然后点击蓝色 Create cluster 按钮:

图 3.19 – 创建集群

图 3.19 – 创建集群

  1. 你将看到多个选项,包括 Azure 和 IBM Cloud。点击 AWS 选项下的蓝色 创建集群 按钮:

图 3.20 – AWS ROSA

图 3.20 – AWS ROSA

  1. 第一个页面将关联你的 AWS 账户与 Red Hat,如果你还没有完成这一步。点击 选择账户 按钮,并按照引导完成配置 ROSA 的流程,ROSA 是 AWS 上的 Red Hat OpenShift 服务:

图 3.21 – 创建 ROSA 集群页面

图 3.21 – 创建 ROSA 集群页面

  1. 一旦你为 ROSA 设置了 AWS 账户权限和角色,下一页将是配置集群的页面,其中包括集群名称、OpenShift 版本、区域和可用性选项。OpenShift 提供的一个很酷的选项是能够加密 etcd 并使用客户密钥创建持久卷。这个附加的安全性通常在企业中受到密切关注:

图 3.22 – 添加集群详细信息

图 3.22 – 添加集群详细信息

  1. 选择你的机器池,其中包括 AWS EC2 实例的大小和自动扩展功能:

图 3.23 – 工作节点大小

图 3.23 – 工作节点大小

  1. 接下来是网络选项,包括你是否希望 Kubernetes 控制平面/API 服务器是公开的还是私有的,以及是否希望为 OpenShift 创建一个新的 AWS VPC,还是将 ROSA 集群安装到现有的 VPC 中。一旦选择 虚拟私有云 (VPC) 选项,你需要选择 Kubernetes Pod 内部网络和集群 IP 范围的 CIDR:

图 3.24 – 网络配置

图 3.24 – 网络配置

  1. 对于集群角色和策略,你可以选择手动设置角色和策略,或者让 OpenShift 自动为你设置:

图 3.25 – 集群角色和策略

图 3.25 – 集群角色和策略

  1. 对于最后一步,你可以选择如何为 ROSA 集群实施更新。更新的发生是基于来自 国家漏洞数据库 (NVD) 的 CVE 分数:图 3.26 – 漏洞扫描

图 3.26 – 漏洞扫描

  1. 一旦填写完所有选项,你就可以正式创建你的 ROSA 集群。

现在你已经知道如何在本地和云端启动 OpenShift,让我们总结一下本章内容。

总结

无论你选择在哪个环境中部署 Kubernetes,无论是在大型云平台、小型云平台还是 PaaS 解决方案中,目标始终是相同的,永远不会改变——构建一个能够管理容器化应用的 orchestration 平台。

市面上有很多炫酷的工具,各种不同的平台,以及许多承诺每一个新平台都能从 Kubernetes 角度让你的生活变得更轻松。事实是,无论如何,它们都有一个共同的目标。

目标是使用 Kubernetes 来编排和管理容器化应用。确保在使用每个平台和工具时,记住这一点——编排我的容器化应用。如果你牢牢记住这一点,选择和排除市场营销噪音将变得更加容易。

在下一章中,我们将学习本地 Kubernetes,并了解理解 Kubernetes 集群的底层组件为什么重要,以及为什么它如此重要。

进一步阅读

要了解更多关于本章涉及的主题,请查看以下资源:

第四章:本地 Kubernetes 现实检查

我知道你在想什么——本地部署?为什么这个家伙在教我们本地 Kubernetes?不是都在 云端吗!

尽管从技术营销和大型云服务提供商大肆宣传 Kubernetes 似乎是这样,但在生产环境中,实际上有很多本地 Kubernetes 集群,也有很多工程师在管理它们。德国著名汽车制造商梅赛德斯·奔驰在 OpenStack 上托管着超过 900 个 Kubernetes 集群,而 OpenStack 是一个私有云解决方案。所有这些集群都位于数据中心,而不是公共云中。如果你揭开云服务提供商运行 Kubernetes 集群的层面,你会发现他们做的事情很相似。他们有几个数据中心,像你在本地部署或在 eBay 上购买的服务器一样运行 Kubernetes。唯一的区别是,云服务商是在全球范围内大规模地运行 Kubernetes。然而,Kubernetes 运行的方式并没有什么不同。

事实上,本章本身可以是一本完整的书——它可能是几本书。Kubernetes,尤其是在没有云抽象的情况下,是一个极为庞大的话题。人们说 Kubernetes 就像是一个自带数据中心,这背后有其原因。仅仅是如何以及在哪里运行本地 Kubernetes 就是一个深奥的话题。例如,使用什么规模的基础设施,如何扩展工作负载,纵向和横向扩展,网络带宽,高可用性,以及更多的内容都涉及到使用何种系统以及在哪些地方运行它们的问题。

本章结束时,你将理解运行本地 Kubernetes 有多么复杂,但与此同时,对于那些在 Kubernetes 上投入大量努力的组织来说,它又是多么有回报。你将具备实践技能和理论知识,了解如何思考组织 Kubernetes 环境的扩展问题。从本章你将学到的一件事是,使用酷技术远不如从架构角度思考平台团队应该是什么样的。

在本章中,我们将涵盖以下主题:

  • 了解操作系统和基础设施

  • 排查本地 Kubernetes 集群的问题

  • 介绍混合服务

  • 探索网络和系统组件

  • 了解虚拟化裸金属

本章将结合实践操作和理论知识。由于我们只有一章来讨论这个话题,可以说我们无法展示你需要了解的所有内容。然而,这应该是你生产环境之旅的一个良好起点。

技术要求

要完成本章,你应该首先阅读 第二章第三章。虽然这听起来很显而易见,但我们还是要强调这一点,因为在深入了解 Kubernetes 的本地需求之前,理解不同的部署方式是至关重要的。因为基于云的 Kubernetes 部署抽象了很多本地部署时需要做的事情,但它依然展示了组件需要部署的整体工作流。

要学习本章内容,你应该具备一定的基础设施和故障排除背景。对于本地 Kubernetes 集群而言,它们对基础设施的依赖非常大,因此如果没有这些知识,完成本章可能会比较困难。至少,你应该具备以下内容:

  • Linux 知识

  • 服务器知识

本章的代码可以在本书的 GitHub 仓库中找到,地址是:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch4

本章的 Kubeadm 部分,如果你有两台虚拟机可用,你可以跟着一起操作。如果没有也完全没问题:你可以从理论的角度来阅读本章内容。如果你有两台额外的虚拟机可用,无论是本地的还是云端的,都将帮助你更好地理解本章的整体讲解。

操作系统和基础设施的理解

一切都从服务器开始。不管你是在云端、无服务器平台还是容器上运行工作负载——一切都从服务器开始。工程师们不总是考虑服务器,或者说不总是考虑工作负载从哪里开始,这一点在今天的世界里是常见的,因为底层的基础设施已被抽象化了。在云计算的世界里,你很少需要问,你用什么硬件来运行这些虚拟机?戴尔?惠普? 相反,你更关心的是服务器部署后的事情,这有时被称为“第二天操作”(这里插入更多流行词)。我们所指的意思是,现在工程师们更多关注的是自动化、应用部署、平台和可扩展性,而不是在线订购服务器、将它们上架并配置虚拟化管理程序(如 ESXi、KVM、Hyper-V 等)。

在许多初创公司和中小型组织中,典型的现实是使用云计算。对于大型组织,另一个现实是本地工作负载,这些工作负载要么是虚拟化的,要么是纯粹的裸金属。如果在讨论中提到云与本地结合的情况,这时像混合解决方案这样的内容就会出现,你将在本章稍后学习到这些内容。

假设你现在正在阅读这篇文章,并且 100%在云端工作。你仍然需要了解虚拟机大小、扩展、虚拟机的位置(数据中心 – 区域、可用性区域、地理位置等)、网络延迟以及其他大量属于系统和基础设施类别的内容。例如,在上一章中,你学习了如何为 DigitalOcean 和 Linode 中的高 CPU、高内存和中等 CPU/内存工作负载选择工作节点大小。

在本节中,你将了解在架构本地 Kubernetes 平台时必须考虑的核心系统和基础设施需求。

Kubeadm 部署

在进入理论之前,我想展示一下如何通过 Kubeadm 启动 Kubernetes 集群。主要目的是展示实际部署 Kubernetes 的过程,同时让你看到部署过程中不被抽象化的部分。抽象化是一个很棒的东西,但只有在你了解手动部署的方法后,它才显得有意义。否则,抽象化只会引起混淆。

对于虚拟机,安装基于 Ubuntu。然而,如果你使用的是其他 Linux 发行版,它也能工作,但你需要稍微修改命令以适应特定的发行版。例如,Ubuntu 使用 Aptitude 包管理器,而 CentOS 使用 Yum 包管理器。

安装控制平面和工作节点

让我们开始吧。

  1. 首先,确保更新 Ubuntu:

    
    sudo apt update -y
    
  2. 安装传输层:

    
    sudo apt-get install -y apt-transport-https curl
    
  3. 在 Ubuntu 上安装 Kubernetes 包:

    
    curl -s **https://packages.cloud.google.com/apt/doc/apt-key.gp**g | sudo apt-key add -
    
    
    echo "deb **https://apt.kubernetes.io**/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  4. 更新 Ubuntu,确保 Kubernetes 包已存在:

    
    sudo apt update -y
    
  5. 接下来,切换到 root 用户:

    
    sudo su -
    
  6. 安装并配置 CRI-O 容器运行时:

    
    OS=xUbuntu_20.04
    
    
    VERSION=1.22
    
    
    echo "deb **https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS**/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
    
    
    echo "deb **http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/$VERSION/$OS**/ /" > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.list
    
    
    curl -L **https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/Release.ke**y | apt-key add -
    
    
    curl -L **https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/Release.ke**y | apt-key add -
    
  7. 退出 root 用户:

    
    exit
    
  8. 现在,更新 Ubuntu,确保CRI-O可用:

    
    sudo apt update –y
    
  9. 安装CRI-O

    
    sudo apt install cri-o cri-o-runc -y
    
  10. 重新加载守护进程并启用 CRI-O:

    
    sudo systemctl daemon-reload
    
    
    sudo systemctl enable crio --now
    
  11. 检查 CRI-O 是否安装正确:

    
    apt-cache policy cri-o
    
  12. 关闭交换分区:

    
    swapoff -a
    
  13. 配置sysctl设置和ip表:

    
    sudo modprobe overlay
    
    
    sudo modprobe br_netfilter
    
    
    sudo tee /etc/sysctl.d/kubernetes.conf<<EOF
    
    
    net.bridge.bridge-nf-call-ip6tables = 1
    
    
    net.bridge.bridge-nf-call-iptables = 1
    
    
    net.ipv4.ip_forward = 1
    
    
    EOF
    
    
    sudo sysctl --system
    
  14. 安装 kubeadm:

    
    sudo apt-get install -y kubelet kubeadm kubectl
    

下一步是配置。

配置控制平面

我们需要为kubeadm init命令定义变量。这将包括 IP 地址和 Pod CIDR 范围。根据你的部署位置,可能只有一个公共子网,或者同时有公共和私有子网。

如果你只有一个公共子网,使用相同的ip_addresspublicIP值,以及CIDR范围。如果你有公共子网和私有子网,使用公共 IP 作为publicIP,私有 IP 作为ip_address,并使用私有 IP 范围作为CIDR


ip_address=10.116.0.9
cidr=172.17.0.0/16
publicIP=146.190.219.123

接下来,在控制平面上初始化kubeadm


sudo kubeadm init --control-plane-endpoint $publicIP --apiserver-advertise-address $ip_address --pod-network-cidr=$cidr --upload-certs

如果你在云端部署,可能会遇到init失败的情况,因为 Kubelet 无法与 API 服务器进行通信。这种情况通常发生在公有云中,由于网络限制。如果发生这种情况,请打开以下端口:

在 Kubeadm init成功之后,你会看到一些命令输出,显示如何加入更多控制平面以及如何加入工作节点。复制工作节点加入命令,并在你配置为工作节点的 Ubuntu 服务器上运行它。

接下来,安装 CNI。

如果你不想使用 Weave,可以查看这里列出的网络框架:。


kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml

接下来,我们将看一下系统规模。

系统规模

关于系统类型、大小和节点数量的考虑对于如何思考本地部署至关重要。最终,关键是你打算在 Kubernetes 集群上运行什么。如果你刚开始你的第一个 Kubernetes 集群,想尝试容器化一个应用程序,看看它是如何工作的,依赖是如何运作的,最终开始你的旅程,那将与运行 50 个以上 Kubernetes 集群、处理股票交易/量化应用的情况不同。归根结底,你使用的系统规模将完全取决于你正在运行的工作负载。

在你考虑创建一个本地 Kubernetes 集群之前,你必须考虑两个重要方面:

  • 我有可用的硬件吗?如果没有,我需要购买什么硬件?

  • 我计划在接下来的 3 到 6 个月里运行什么类型的应用程序?

例如,假设你购买了 10 台服务器,并计划在上面运行你的应用程序。那么这些服务器需要多大?如何进行扩展?你是有内存密集型应用和标准日常应用的组合吗?

另一个重要的考虑因素是扩展。如果你是水平扩展,意味着会创建更多的 Pod,因此会消耗更多的虚拟化硬件。如果你是垂直扩展,意味着你的 Pod 的内存和 CPU 会增加,而不需要创建更多的 Pod(这就是垂直自动扩展)。你不仅要规划一开始运行什么应用程序,还要规划这些应用程序如何被使用。如果今天你有 500 个用户,并且根据公司预测在 3 个月内你会有 2,000 个用户,那就意味着 Pod 的使用速度会加快,可能需要更多的 Pod。更多的使用意味着自动扩展,自动扩展意味着需要更多的资源。

尺寸考虑

以下是构建 Kubernetes 集群时标准尺寸的考虑事项:

  • 标准工作节点:这些是你日常使用的 Web 服务器 Pod 或中间件,仍然需要虚拟化硬件资源,但不像更强大的应用那样要求高。它们可以算是你的通用应用。这里运行的工作节点中等到较大的尺寸。如果你刚开始搭建 Kubernetes 集群,并且可能在开始时将一两个容器化应用迁移到 Kubernetes,标准工作节点完全可以帮助你起步。

  • 内存密集型工作节点:你知道将需要比其他应用程序更多内存/内存的应用程序,应该为这些应用程序安排更多 RAM 的工作节点,而不是标准的工作节点。你需要确保,如果一个 Pod 需要进行副本扩展,或者添加了更多 Pods,你有足够的内存。否则,Pods 将无法启动,并且会一直处于待定状态,直到内存分配给它们,调度器才能重新尝试将 Pod 调度到节点。

  • CPU 密集型工作节点:某些应用程序会要求更多的 CPU 和可用线程来运行应用程序。与内存密集型应用程序相同的规则适用于此——如果调度器由于资源不足而无法调度 Pods,它将等待直到资源释放。

  • 特殊情况工作节点:特殊情况的 Pod 通常是运行图形密集型工作负载的应用程序,这些工作负载需要特定类型的 图形处理单元GPU),这意味着工作节点需要一个专用的 GPU,或者是一个需要更快带宽的应用程序,因此它需要某种类型的 网络接口卡NIC)。

系统位置

运行 Kubernetes 时,主要有两种类型的节点:

  • 控制平面:控制平面是 API 服务器所在的地方。没有 API 服务器,你在 Kubernetes 中几乎无法做什么。它还包含调度器(Pods 如何知道该去哪个节点)、控制器管理器(用于 Pods、Deployments 等的控制器,以确保达到所需的状态)以及集群存储/数据库(etcd)。

  • 工作节点:工作节点是 Pods 安装的地方,在控制平面之后,并且在工作节点上运行。它们运行 kubelet(代理)、容器运行时(容器的运行方式)、kube-proxy(Kubernetes 网络)以及正在运行的任何其他 Pod。

牢记这两种节点类型后,你需要弄清楚想要使用哪些自动化技术来运行它们,最终决定在哪里/如何运行它们。这是一个不容小觑的考虑因素。你最不希望发生的事情就是部署了 Pods,然后才意识到它们运行的节点无法处理正在运行的容器化应用程序。正如前面提到的,控制平面是 Kubernetes API 所在的位置。Kubernetes API 是你在 Kubernetes 中做一切事情的方式。如果没有它,Kubernetes 就无法存在。话虽如此,选择在哪里以及如何运行控制平面是正确使用 Kubernetes 与每天都在不断排查故障之间的分水岭。

重要说明

解释控制平面和工作节点可能需要一章内容。由于本书已假设你了解 Kubernetes,我们不会深入探讨 Kubernetes 节点的类型。然而,如果你对控制平面和工作节点不了解,我们强烈建议你在继续之前花时间学习它们。一个很好的起点是 Kubernetes 文档。

数据中心和区域位置

数据中心会宕机,区域会宕机,互联网服务提供商ISP)会宕机。当你架构 Kubernetes 运行位置时,有几个因素必须考虑。

第一件事是你在哪里运行。例如,如果你的客户在英国,而你决定在新泽西州运行数据中心,那么就会出现大量带宽问题和延迟问题。相反,最好是在英国拥有一个数据中心和几个联合位置。

说到联合位置,你必须确保没有单点故障。现实情况是,数据中心确实会出现故障。它们可能会面临互联网问题、洪水、电力问题和停电等。如果发生这些问题,最后你最不希望的就是只有一个位置。相反,你应该至少考虑两个数据中心位置。如果其中一个数据中心发生故障,则需要启用高可用性来启动另一个数据中心。例如,你可以采用热/热热/冷的高可用性方案。推荐使用热/热,因为所有数据默认会复制到第二个数据中心。如果第一个数据中心出现故障,第二个数据中心会接管第一个数据中心的工作。另一个需要考虑的因素是数据中心的位置。如果两个数据中心之间仅有 5 英里远,而一场暴风雨来临时,两个数据中心都可能受到影响。因此,你需要在数据中心之间保持一定的距离。

Kubernetes 的运行位置和方式

如你在前面的部分看到的,弄清楚本地 Kubernetes 基础设施的第一步是决定你需要什么硬件和资源来运行你计划运行的应用程序。接下来,重点是弄清楚你需要什么类型的工作节点。它们很可能是标准工作节点和需要更多 CPU 和内存的高强度工作节点的组合。最后一步(至少对于本节来说)是弄清楚如何以及在哪里运行它。

还有其他一些选项,但以下是一些流行的选项:

  • OpenStack:尽管今天的许多工程师认为 OpenStack 已经死掉,但许多大型组织仍在使用它。例如,几乎每个电信服务提供商都在使用 OpenStack。奔驰(截至写作时)正在托管超过 900 个(没错,900 个)运行在 OpenStack 上的 Kubernetes 集群。OpenStack 给你一种云端的感觉,但其实一切都在本地,你自己在托管私人云。开放基础设施基金会在使用工具(如 Magnum)在 OpenStack 上运行 Kubernetes 上投入了大量精力,Magnum 是运行编排平台(如 Kubernetes、Mesos、Docker Swarm 等)的标准,而 Loki 则是 Linux/OpenStack/Kubernetes/基础设施栈。

  • Kubeadm:如果你不想走 OpenStack 路线,并且使用的是像虚拟化管理程序这样的工具,那么 kubeadm 无疑是最佳选择。虽然有一些不同的自动化方式可以让 Kubernetes 集群快速搭建,但 kubeadm 基本上是最为精细的。使用 kubeadm,你可以创建一个符合最佳实践的 Kubernetes 集群。除了在 Linux 服务器上安装 Kubernetes 运行的前提条件外,kubeadm 基本上是自动化的。kubeadm 提供了一组命令,你可以运行这些命令,它会对 Linux 服务器进行多个检查,以确认是否具备所有前提条件,然后安装控制平面。之后,终端会输出一个命令,告诉你如何运行更多的控制平面和/或运行工作节点。你只需将输出的命令复制,并粘贴到另一个通过终端 SSH 连接的服务器上运行即可。kubeadm 的另一个优点是,它让你意识到在本地运行 Kubernetes 是非常简单的。你甚至可以在你的笔记本电脑或 Raspberry Pi 上运行它。要运行它的门槛并不高,尤其是在开发环境中。

  • Rancher:Rancher 既是 Kubernetes 集群创建工具,又是 Kubernetes 集群管理工具。在 Rancher 中,你可以创建 Kubernetes 集群并将其托管在 Rancher 上,或在云中创建 Kubernetes 集群,或者通过提供 Linux 虚拟机来创建原生 Kubernetes 集群。你还可以通过 Rancher 管理你的 Kubernetes 集群。例如,如果你有一个使用 kubeadm 或 OpenShift 运行的裸金属 Kubernetes 集群,你可以通过 Rancher 管理它。你还可以管理云中的 Kubernetes 集群。

  • Kubespray:虽然(在我们看来)Kubespray 并不是最佳的生产级选择,但它仍然是一个选项。Kubespray 使用 Ansible 或 Vagrant 部署一个生产就绪的 Kubernetes 集群,可以在虚拟机或云环境中运行。因为你只需要 kubeadm,而不是像 Vagrant 或 Ansible 这样的 中间件,所以直接使用 kubeadm 可以省去创建集群时的额外步骤。有趣的是,Kubespray 在幕后使用了 kubeadm 来创建集群(github.com/kubernetes-sigs/kubespray/blob/master/docs/comparisons.md),这更进一步证明了不需要使用 Kubespray 的额外步骤,直接使用 kubeadm 更加高效。

操作系统

要运行 Kubernetes 平台,你需要一个操作系统来承载它。你有以下两种选择:

  • 运行裸金属服务器,并让操作系统直接在服务器上运行

  • 具有虚拟化管理程序,如 ESXi,可以虚拟化硬件并允许你在其上安装操作系统

在今天的世界里,您很可能会使用虚拟化管理程序。除非有特殊需求需要运行裸机服务器并直接在服务器上运行操作系统,否则工程师通常会选择虚拟化管理程序。这种方法更易于管理,具有很好的可扩展性,并且能让您充分利用硬件资源。

在操作系统选项方面,大致上通常有两个可选项。一个肯定比另一个使用得更多,但另一个正在获得越来越高的关注度。

Linux

很可能,您将以 Linux 发行版的形式运行工作节点。经过实践验证的最流行的发行版是 Red Hat、CentOS 和 Ubuntu。Linux 通常是 Kubernetes 工作节点的 开箱即用 解决方案,并且在写本书时,您只能在 Linux 服务器上运行 Kubernetes 控制平面。

Windows

尽管不太常见,尤其是在 .NET 开源和跨平台版本的影响下,您仍然可以将 Windows Server 作为 Kubernetes 工作节点来运行。如果您想将 Windows Server 作为工作节点运行,需要考虑一些问题。首先,您必须运行 Windows Server LTSC 2019 或更高版本。在写本书时,当前可用的两个选项是 Windows Server 2019 和 Windows Server 2022。

使用 Windows Server 选项时,您需要购买许可证并考虑 客户端访问许可证CAL)的问题。

现在,您已经了解了本地 Kubernetes 集群的整体操作系统和基础设施组件,在下一部分中,您将学习如何排查您正在构建的环境。

本地 Kubernetes 集群的故障排除

如果您来自系统管理员/基础设施背景,故障排除 Kubernetes 集群会对您来说非常自然。归根结底,Kubernetes 集群由服务器、网络、基础设施和 API 组成,这些正是基础设施工程师日常工作的内容。

如果您是开发人员,其中一些概念可能对您来说是新的,比如故障排除网络。然而,您也会对一些故障排除技术非常熟悉,例如查看和分析日志。

故障排除 Kubernetes 集群的整体思路是从两个方面进行观察:

  • 集群本身

  • 集群内部运行的 Pods

集群本身,包括网络、服务器、操作系统和可扩展性,将更多地从基础设施的角度来考虑,此时类似 认证 Kubernetes 管理员CKA)的概念非常适用。Pods、Deployments、容器错误以及 Pods 无法正常启动等问题,将更多地从开发人员的角度来考虑,因此学习 认证 Kubernetes 应用开发者CKAD)的概念将是一个很好的起点。

在这一部分中,你将学习如何以易于理解的方式思考 Kubernetes 集群的故障排除,并找出问题的关键方法。

服务器日志和基础设施故障排除

尽管本书中有一整章内容讲解日志记录和可观察性(第七章),但我们还是先来谈谈集群中的日志记录。通常,在 Kubernetes 环境中,工程师主要关注应用程序的日志。这些日志帮助他们排查应用程序故障并弄清楚问题的根源。然而,从 Kubernetes 本地部署的角度来看,服务器日志是一个至关重要的部分。

大多数情况下,除非另有说明,来自控制平面和工作节点的所有日志通常都会保存在 Linux 服务器的 /var/log 中。控制平面的日志位于以下路径:

  • /``var/log/kube-apiserver.log

  • /``var/log/kube-scheduler.log

  • /``var/log/kube-controller-manager.log

对于工作节点,日志位于以下路径:

  • /``var/log/kubelet.log

  • /``var/log/kube-proxy.log

默认情况下,Kubernetes 并没有使用特定的日志机制。这基本上是由你来决定的。工程师收集集群日志的两种主要方式如下:

  • 使用在集群每个节点上运行的节点日志代理

  • 使用日志聚合器捕获 /var/log 中的日志并将其发送到日志平台

你可以在 kubernetes.io/docs/tasks/debug/debug-cluster/ 上找到更多关于故障排除集群的文档。

网络可观察性

Kubernetes 集群的网络部分,稍后你将了解,它本身是 Kubernetes 中一个极为复杂的组件。Kubernetes 内部的网络和 Kubernetes API 以及其他所有组成 Kubernetes 的部分一样重要。

你需要关注的两个方面是集群延迟Pod 延迟。对于集群延迟,问题很可能归结为标准的系统管理故障排除,主要是检查带宽、路由器的 QoS、数据包传输量、网卡等。从 Pod 延迟的角度来看,问题可能会从集群层面开始,涉及集群可能存在的问题,但要进行故障排除,你很可能需要查看类似服务网格的内容。

服务网格本身是一个庞大的话题,可能需要涵盖一整本书/课程,但你将在探索网络和系统 组件部分学习如何开始使用它。

Kubernetes 指标

在 Kubernetes 中创建的大多数资源(如部署、Pod、服务等)都包含一个指标端点,可以通过 API 调用 Kubernetes 资源时的 /metrics 路径找到。

指标服务器收集来自 kubelets 的日志,kubelet 是运行在每个 Kubernetes 节点上的代理,并通过指标 API 将它们暴露给 Kubernetes API 服务器。然而,这主要用于自动扩缩的需求。例如,指标会告诉 Kubernetes,嘿,Pods 的内存利用率很低;我们需要一个新的 Pod 来处理应用程序的负载。然后,垂直或水平自动扩展器会启动并完成任务,创建一个新的 Pod 或垂直扩展当前的 Pod。

如果你想收集例如用于可观察性的监控平台的度量指标,你需要直接从/metrics/resource/resource_name kubelet 收集这些度量数据。像 Prometheus 这样的许多可观察性平台会接收这些度量数据,并用它们进行故障排除和性能调试。

crictl

在每个 Kubernetes 集群内部,特别是在工作节点上,都有一个容器运行时。像 containerd 和 CRI-O 这样的容器运行时在 Kubernetes 环境中被广泛使用。容器运行时帮助容器和 Pod 运行。因此,确保容器运行时按预期工作非常重要。

crictl帮助你排查容器运行时的问题。你可以运行一些针对 Pod 的命令,帮助你理解容器内部发生了什么。请记住,crictl仍处于 beta 阶段,但它依然是一个非常好的故障排除工具。

在以下示例中,crictl列出了一个 Pod 集合:


crictl pods

然后,它可以查看每个 Pod 内部运行的容器:


crictl pods -name pod_name

你还可以列出正在运行的容器,并绕过 Pod:


crictl ps -a

你可以在github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md找到更多关于crictl的信息。

kubectl

在本节结束时,让我们讨论一下kubectl命令,这是工程师通过 CLI 与 Kubernetes 互动的典型方式。你会看到用于故障排除的三条主要命令:

  • kubectl describe:该命令告诉你 Kubernetes 部署的具体构成,例如它是如何运行的,里面运行了哪些容器,使用了哪些端口等。这是理解部署内部可能出问题的好第一步。

例如,如果你有一个名为nginx-deployment的 Deployment,你需要运行以下命令:


kubectl describe deployment nginx-deployment

以下输出展示了describe在 Deployments 中的表现。

![图 4.1 – Kubernetes Deployment 输出]

](https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/50-k8s-ccp/img/B19116_04_01.jpg)

图 4.1 – Kubernetes Deployment 输出

  • kubectl cluster-info dump:该命令是集群中每一项已记录事件的文字转储。默认情况下,所有输出都会发送到 STDOUT,因此你最好将输出发送到文件并查看它,因为它非常冗长,包含大量数据。

以下屏幕截图为简化起见有所裁剪,但它展示了通过kubectl cluster-info dump命令显示的信息:

图 4.2 – 集群转储输出

图 4.2 – 集群转储输出

  • kubectl logs:此命令是理解 Pod 内部发生情况的基本命令。例如,假设您有一个名为nginx-deployment-588c8d7b4b-wmg9z的 Pod。您可以运行以下命令来查看 Pod 的日志输出:

    
    kubectl logs nginx-deployment-588c8d7b4b-wmg9z
    

以下屏幕截图显示了 Pod 日志的示例。

图 4.3 – Nginx Pod 日志输出

图 4.3 – Nginx Pod 日志输出

无论 Kubernetes 集群在哪里运行,您总是需要排查它的某些方面。本节中的提示应该有助于解决本地和有时云端的相关场景。

引入混合服务

从 2014 年到 2015 年,大多数组织和工程师看到的内容大致是数据中心将消失云是未来所有不使用云的公司将被淘汰。组织开始感受到向云迁移的压力,而工程师们则开始感到紧张,因为他们多年来培养的技能变得过时了。回到现在,也就是本书写作时的 2022 年,主机仍然存在……所以,是的,许多组织仍在运行本地工作负载。拥有基础设施和系统背景的工程师在新的云原生时代中过得相当不错。原因是,除了机架和堆叠服务器之外,他们所学到的 100%的技能仍然非常适用于云和 Kubernetes。

理解操作系统和基础设施部分,您可能还记得关于本地工作负载的内容,以及它们在今天的世界中仍然具有的重要性。尽管技术营销可能让你产生不同的感觉,事实是,本地工作负载在今天仍然被广泛使用。它们的使用量如此之大,以至于像 AWS、微软和谷歌这样的组织也意识到了这一点,并且正在构建服务和平台来支持真正的混合环境需求,这意味着同时使用本地和云端工作负载,并且通常在同一个位置进行管理。

在本节中,您将学习主要云服务提供商的混合服务,以及一些其他公司在这一领域的努力。

Azure Stack HCI

Azure Stack HCI 是微软的混合云产品。它使您能够将本地环境连接到 Azure。Azure Stack HCI 通常运行在供应商提供的服务器上,尽管您也可以在兼容的服务器和硬件上自行安装。它的安装过程与其他操作系统相似,但其要求有很大的复杂性。以下是一些要求:

  • 至少一台服务器,最多 16 台服务器

  • 必须部署到两个不同的站点

  • 所有服务器必须来自相同制造商,并使用相同型号

  • 至少 32GB RAM

  • 硬件需要支持虚拟化(你必须在 BIOS 中启用此功能)

你可以深入了解一些需求,你会发现它涉及非常深入。从时间的角度来看,你可能更适合从供应商那里购买一台适配 Azure Stack HCI 的服务器。

Azure Stack HCI 的一个有趣之处在于其背后的原理——它实际上就是运行 Windows Admin Center 的 Windows Server 2022。因此,你可以完全绕过 Azure Stack HCI,而选择做以下操作:

  1. 运行一堆 Windows Server 2022 Datacenter 服务器。

  2. 将它们集群化。

  3. 安装 Windows Admin Center。

  4. 将服务器连接到 Azure。

  5. 在服务器上运行 AKS。

Google Anthos

Anthos可以说是目前最成熟的混合云解决方案。比如,可以使用 Ansible 等工具自动化安装 Anthos,其硬件需求相比 Azure Stack HCI(在写书时)轻量得多。

硬件需求如下:

  • 最低四核 CPU,推荐八核

  • 最低 16GB RAM,推荐 32GB

  • 最低 128GB 存储,推荐 256GB

与 Azure Stack HCI 类似,Anthos 在本地数据中心运行,并连接到 GCP,通过 GCP 的 UI 或命令/API 进行管理。这里的目标是通过 GCP 管理本地和云端的 Kubernetes 集群。

关于其他基础设施管理工具的简要说明

尽管它本身作为一个平台可能不被视为混合云,但有几个平台可以帮助你在任何地方管理工作负载。在写作时,以下是两个主要的:

  • Azure Arc:顾名思义,Azure Arc 需要 Azure 订阅。不过,它的一个亮点是,你可以在任何地方管理 Kubernetes 集群。如果你在 AWS 中有 Kubernetes 集群,可以通过 Azure Arc 管理它们。如果你有本地的 Kubernetes 集群,也可以用 Azure Arc 来管理。

  • Rancher:Rancher 是一个供应商无关的解决方案,提供类似 Azure Arc 的所有管理功能,还包括一些其他关键特性,如日志记录、Kubernetes 服务器部署和安全功能,帮助你全面管理运行在任何地方的 Kubernetes 集群。

在下一部分,你将了解 Kubernetes 集群中所需的整体网络管理。

探索网络和系统组件

Kubernetes 集群中的网络,除了 Kubernetes API 本身,才是真正让 Kubernetes 运转 的关键。网络在多个方面发挥作用,包括以下几点:

  • Pod 间通信

  • 服务间通信

  • 节点如何在集群内部相互通信

  • 用户如何与容器化应用程序交互

没有网络,Kubernetes 将无法执行任何操作。即使从控制平面/工作节点的角度来看,工作节点也无法成功与控制平面通信,除非设置了适当的网络。

这一部分至少可以分成两章来讲解。由于我们只有一个章节来讲解这些知识点,让我们谈谈关键组件。

kube-proxy

当你第一次开始学习 Kubernetes 内部网络是如何工作的,以及所有资源如何相互通信时,一切都从 kube-proxy 开始。kube-proxy 几乎就像数据中心中的交换机/路由器。它运行在每个节点上,负责本地集群的网络。

它确保以下内容:

  • 每个节点都会获得一个唯一的 IP 地址

  • 它实现了本地的 iptables 或 IPVS 规则

  • 它处理流量的路由和负载均衡

  • 它使 Pods 之间的通信成为可能

简而言之,它是 Kubernetes 集群中每个资源如何通信的方式。

CNI

第一步是 kube-proxy,但为了部署它,需要有一个后端。这个后端就是容器网络接口CNI)。试图在没有 CNI 的情况下运行 kube-proxy,就像在没有 Cisco 设备的情况下尝试在 Cisco 上运行网络——根本行不通。

CNI 是一个网络插件,有时被称为网络框架,负责将网络框架插入 Kubernetes 集群,以启用通信,也就是说,启用 kube-proxy。

有很多流行的 CNIs,包括以下几种:

  • Weave

  • Flannel

  • Calico

Kubernetes 资源通信

当你部署 Pods,尤其是微服务时,微服务是由X数量的 Pods 组成一个应用程序,你需要确保 Pod 到 Pod 以及服务到服务的通信能够正常工作。

Pods 通过 IP 地址相互通信,这个地址是由 kube-proxy 提供的。服务之间的通信方式是通过主机名和 IP 地址,而这些都是由 CoreDNS 提供的。服务为与该服务相关联的一组 Pods 提供一致的 DNS 名称和 IP 地址。CoreDNS 确保主机名到 IP 地址的转换。

DNS

在背后,Kubernetes 运行 CoreDNS,这是一个流行的开源 DNS 平台,用于将 IP 地址转换为名称。当一个 Pod 或服务有一个 DNS 名称时,是因为 CoreDNS 服务(这是一个运行中的 Pod)在 Kubernetes 上正确运行。

服务网格与 Ingress

就像本章中的许多其他主题一样,服务网格本身可以成为一整章——这里提到的两个主题甚至可以写成一本书。然而,让我们尝试将其简化为一节内容。

Ingress 控制器允许你通过一个控制器或负载均衡器访问多个 Kubernetes 服务。例如,你可以有三个名为 App1、App2 和 App3 的 Kubernetes 服务,它们都连接到同一个 Ingress 控制器,并可以通过 /app1/app2/app3 路径进行访问。这是通过为 Ingress 控制器创建路由规则来实现的。

服务网格简而言之,帮助你加密东西-东西流量或服务与服务之间的流量,并解决网络延迟问题。

有时候,取决于你使用的服务网格,你可能不需要 Ingress 控制器,因为服务网格可能自带一个。

对于 Ingress 控制器,可以查看 Nginx Ingress、Traefik 和 Istio。对于服务网格,可以查看 Istio。

在下一部分,你将了解如何思考虚拟化裸金属的各个方面,并了解一些帮助你在这条路上前进的供应商。

了解虚拟化裸金属

如果/当你计划在本地运行 Kubernetes 时,可能会出现两个问题:

  • 我们在哪里运行 Kubernetes?

  • 我们如何运行 Kubernetes?

在今天的世界里,你很可能不会直接在裸金属上运行 Kubernetes(尽管你可以这样做,并且一些公司确实这么做)。你可能会在像 ESXi 这样的虚拟化管理程序上运行 Kubernetes,或者在像 OpenStack 这样的私有云上运行。你也可能会在虚拟化裸金属上运行 Kubernetes,这与在虚拟化管理程序上运行是不同的。

在这一部分,让我们了解什么是虚拟化裸金属以及如何运行它的几种方式。

虚拟化你的环境

当谈到虚拟化裸金属时,很多工程师很可能会想到像 VMware 的 ESXi 或 Microsoft 的 Hyper-V 这样的虚拟化管理程序。这两者都是非常好的解决方案,可以让你将曾经只能运行一个操作系统的裸金属服务器,变成可以运行多个操作系统的服务器。虚拟化环境还有许多其他组成部分,如虚拟化硬件、网络等,所有这些都是广泛的主题,足以成为一本完整的书。

这不仅帮助你尽可能多地利用服务器的资源,还能帮助你节省成本,因为服务器很昂贵。

另一种解决方案是尽可能接近裸金属运行,但你不进行托管。相反,你租用来自托管提供商的裸金属服务器。当你租用这些服务器时,提供商会给你 SSH 或 RDP 访问权限,你可以像服务器位于你的数据中心一样访问它们。你可以使用一个用户界面来创建这些服务器,如果平台允许的话,可能还会有一些自动化方式,并且你可以像在任何其他平台上创建服务器一样,创建 Windows 和/或 Linux 服务器,就像你在创建 Web 服务或托管应用程序的服务器时那样。

在哪里运行 Kubernetes

当考虑在哪儿运行 ESXi 或 Hyper-V 时,这通常取决于您当前拥有的服务器、您公司与供应商的合作关系,以及您对服务器所需资源(如 CPU、RAM 等)的需求。

关于“尽可能接近裸金属”的选项,虽然有很多供应商,但在 Kubernetes 领域,有两个供应商尤为突出:

  • Equinix:Equinix 允许您不仅从 UI 角度,而且从自动化角度,使用像 Terraform 这样的工具,为 Linux 和 Windows 发行版创建虚拟化的裸金属服务器。您还可以管理网络组件,如 BGP 和其他路由机制,并使用按需、预留和竞价服务器:

图 4.4 – Equinix Metal 服务器创建

图 4.4 – Equinix Metal 服务器创建

在以下截图中,您可以看到在 Equinix 中启动服务器部署的一般起点:

图 4.5 – Equinix Metal 部署页面

图 4.5 – Equinix Metal 部署页面

  • OpenMetal:OpenMetal 是一个完整的虚拟化裸金属解决方案,用于运行 OpenStack。OpenMetal 的一个很酷的特点是,您可以通过 SSH 访问运行 OpenStack 的实际服务器,因此您可以像在任何其他 OpenStack 环境中一样,获得极大的灵活性和定制选项。唯一不能访问的就是实际的硬件,因为它由 OpenMetal 管理,但如果您需要一个虚拟化的裸金属解决方案,您可能也不想直接访问硬件:

图 4.6 – OpenMetal 仪表板

图 4.6 – OpenMetal 仪表板

以下截图显示了在 OpenStack 中的标准 UI,该 UI 运行在 OpenMetal 上。这表明,使用 OpenStack 在任何其他环境中没有什么不同,对于那些已经习惯它的工程师来说,这是一个好消息:

图 4.7 – OpenStack 概览仪表板

图 4.7 – OpenStack 概览仪表板

如果您有兴趣在本地运行 Kubernetes,但仍然希望拥有类似 环境的感觉,运行在 OpenMetal 上的 OpenStack 是一个很好的起点。

总结

本章中有很多内容没有涉及——存储、不同的接口类型、硬件类型、Kubernetes 集群的来龙去脉等等。原因是本章的篇幅有限,许多主题可能需要一个完整的章节来讲解。

然而,本章的目标是为您提供一个起点。

正如你在这一章中学到的内容,并且可能已经意识到的那样,在本地管理 Kubernetes 几乎可以感觉像是管理一个完整的数据中心。你需要考虑网络问题、可扩展性问题、存储问题、网络问题……问题层出不穷。然而,如果你希望自己管理 Kubernetes 而不依赖云服务提供商,那么本章已经介绍了你从一开始就需要考虑的事项。

在本地运行 Kubernetes 绝非易事。你很可能需要一支工程师团队——至少需要两到三名具有非常强的系统管理和网络管理背景的工程师。如果你还没有这些技能,或者你的团队没有,这一章应该为你提供了一个很好的起点,帮助你找到需要学习的方向。

在下一章中,你将开始了解在 Kubernetes 上部署应用程序的方式原因

进一步阅读

要了解更多关于本章涉及的主题,可以查看以下资源:

第二部分:接下来的 15 个 Kubernetes 概念 – 应用策略与部署

Kubernetes 集群管理是整体管理 Kubernetes 的一个重要部分。事实上,这也许就是为什么认证 Kubernetes 管理员CKA)项目如此受欢迎,并且是认证 Kubernetes 安全CKS)考试的先决条件。尽管认证并不是一切,且你在使用 Kubernetes 时完全不需要它们,但它能让你了解一个关键组件,那就是基础设施和整体集群管理。

第二个重要的部分,是容器化应用的部署,这是至关重要且绝对必要的。第二组 50 个概念将深入讨论整体部署内容,你将在这些章节中学习到相关知识。

通常,当一名工程师初次接触 Kubernetes 时,他们会部署一个简单的 Web 应用,比如 nginx 或 Hello World。他们会获取容器镜像,将其放入 Kubernetes 清单中,运行 kubectl apply –f,然后完成部署。关键点在于,将容器镜像放入清单并执行命令是简单的部分。困难之处在于考虑你所使用的实际策略。你会实现 GitOps 吗?CI/CD 吗?服务网格?你如何管理自动化和可重复性?需要部署多少个应用?从战略和架构角度来看,思考过程中包含了很多内容。

到本章节结束时,你应该能理解多种部署容器化应用的方法。尽管我们无法涵盖每一个具体的使用案例,但你应该能够从这两章中提取信息,并为在生产环境中使用什么做出清晰的决策。

本书的这一部分包括以下章节:

  • 第五章像真正的云原生一样部署 Kubernetes 应用

  • 第六章Kubernetes 部署 – 相同的游戏,下一层级

第五章:像真正的云原生应用一样部署 Kubernetes 应用

当工程师开始听说 Kubernetes 或者想要开始实施它时,通常是出于管理和部署应用的开发视角。Kubernetes 使工程团队生活更轻松的整个前提,无论是开发还是运维,都是基于应用部署的。

部署应用是每个企业头脑中的重点,不管是网站、移动应用,还是任何公司的内部应用,从软件公司到汽车零件公司再到啤酒制造商。无论行业如何,几乎每家公司都部署某种类型的应用和软件。正如所有工程师所知道的那样,成功部署和维护一个应用并不是一件容易的事情。无论你是在裸金属上、虚拟机上、云端,还是甚至在容器中运行应用,这个应用可能是(而且很可能是)决定一个成功的企业和破产公司的关键。

在本章中,你会发现很多话题会让你想起其他应用部署的方式。从实际部署到扩展和升级,整体概念是相同的。例如,扩展应用就是扩展应用。在 Kubernetes 中没有什么神奇的新方法。然而,Kubernetes 给你带来的好处是扩展的简易性。话虽如此,你会注意到的一个重要点是,Kubernetes 并没有重新发明轮子。它只是让我们已经做了 30 多年的事情变得更加简单。

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

  • 云原生应用

  • 控制器、控制器部署和 Pods

  • 隔离和命名空间

  • 无状态应用和有状态应用

  • 升级部署

技术要求

为了跟上本章的内容,你应该已经通过 Kubernetes 清单部署了一个 Kubernetes 应用。本章将详细讲解部署应用的过程以及什么是 Kubernetes 清单,但要完全理解本章,你应该熟悉部署过程。可以这样想——你应该处于 Kubernetes 部署过程的初级/中级水平,而本章将帮助你达到生产级水平。

本章的代码可以在以下 GitHub 链接中找到:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch5

理解云原生应用

尽管今天的云原生概念可能感觉有些像流行词,但构建云原生应用的想法还是有一定价值的。应用的架构方式很重要,因为它关系到应用后期如何部署、管理和维护。平台的构建方式也很重要,因为这是应用如何部署和运行的起点。

在技术发展多年的过程中,关于应用架构和构建方法,曾经有过多种不同的思路。最初的方法围绕着本地系统形成,比如大型主机和服务器。之后,应用开始针对虚拟化硬件平台进行架构设计,比如 ESXi 和其他虚拟化产品,目的是利用服务器的更多资源,而不是像裸金属时代那样仅运行单一工作负载。虚拟化之后,应用的架构和规划开始围绕云工作负载展开,这引入了云原生的概念,即如果应用仅在云中运行,它们将如何工作。带宽、服务器大小以及整体成本等因素引发了许多关于云工作负载的讨论,而且至今仍在讨论之中。

现在,我们正面临着第四阶段——容器化工作负载。容器化工作负载真正推动了云原生应用和部署的关注,特别是随着微服务概念开始成为许多组织的现实,五年前它们可能还认为这是不可能的。

在这一部分,你将学习云原生应用是什么,以及应用架构、云部署和微服务的简要历史。

什么是云原生应用?

在以云原生方式部署应用之前,让我们先退一步,思考一个核心计算机科学概念——分布式计算。

分布式计算是研究分布式系统的领域,而分布式系统是指那些其组件位于不同网络连接的计算机上的系统。这些不同的网络连接的计算机随后相互通信,来来回回地发送数据或数据包。

这里重要的是——分布式系统等于多个系统上的多个软件组件,它们作为一个单一系统运行。分布式计算听起来像微服务,对吧?(稍后会详细介绍微服务。)

云原生将分布式计算的概念扩展到一个全新的层次。想象一下从 AWS 或 Azure 的角度来看。AWS 和 Azure 从定义上来说就是分布式系统。当你登录 AWS 控制台时,数不清的服务触手可及——EC2、数据库、存储,还有更多其他服务。你与这些服务的互动来源于一个单一系统,但构成该单一系统的网络组件跨越了全球多个数据中心,涵盖了成千上万台服务器。然而,云原生并不仅仅意味着公共云。请记住,云是服务的分布。举个例子,云原生的东西也可以是一个完整的 OpenStack 服务器集群。

结合云原生和分布式计算的整体概念,你会得到一个主要概念——云原生应用。云原生应用旨在赋予你设计和构建以下类型应用的能力:

  • 易于扩展

  • 韧性

  • 弹性

需要记住的重要一点是,这些概念与我们在工程领域中已经有的并没有什么不同。我们已经有了分布式计算很长时间。我们已经有了分布式应用一段时间。我们以前没有的是轻松实现分布式计算的能力。回想之前提到的 AWS 或 Azure 的例子,我们需要多久才能构建出像 Azure 或 AWS 那样的基础设施?再想想,管理和维护这些基础设施需要多少人。借助云级别的分布式计算,所有的第一天配置都已经被抽象出来,你只需要担心第二天的复杂性,构建云原生/分布式计算应用。

在云中,如果你想要扩展应用,只需要点击几个按钮,写几行代码,瞬间就能创建自动扩展组。如果你想构建韧性强的应用,只需要指点并点击选择数据中心来运行你的应用,而无需物理建设这些数据中心。再次强调,分布式计算的概念与云原生及云原生应用是相同的。不同之处在于,你无需担心构建数据中心,你只需关注应用的扩展。

云特定的云原生

无论是在云中进行标准应用部署,还是在 Kubernetes 中进行应用部署,必须记住的一个重要点是,云原生并不仅仅意味着。它更多的是一个整体概念,但再说一次,云原生的核心思想是分布式计算,而无需关注第一天的实现和配置。

例如,假设我们以 OpenStack 为例。OpenStack 是一个私有云。你可以在自己的数据中心部署 OpenStack,并像使用任何其他云服务一样与之互动。然而,问题在于——一些团队可能会将其视为云原生,而另一些团队则可能将其视为一般的分布式计算。正在构建 OpenStack 的基础设施团队将看到幕后的配置,例如构建硬件并在多个数据中心之间扩展。对他们来说,这与标准的分布式计算环境没有什么不同。对于构建、管理和维护 AWS 或 Azure 基础设施的工程师们来说,也是如此。然后,还有一些团队会在 OpenStack 已经搭建好后与其互动。他们通过 UI 登录并通过 CLI 以及其他 API 方法与 OpenStack 通信,因此他们体验到的是真正的云原生环境的完整满足感,就像许多工程师在与 AWS 和 Azure 互动时,能够充分满足对基础设施和本地服务无需担心的需求。

另一个例子是混合云。如果你在本地运行 Azure Stack HCI,那么意味着你正在使用某些服务器运行 Azure Stack HCI 操作系统,并与 Azure 云进行交互。管理 Azure Stack HCI 的工程师们看到的是幕后发生的事情。而那些仅仅与Azure Kubernetes 服务AKS)互动的工程师则看不到底层基础设施。他们只知道他们去特定的位置创建一个新的 Kubernetes 集群。

无论平台或应用程序在哪里部署,它对于某些人来说可能是云原生的,而对于另一些人来说则是标准的分布式计算。你可以是一个正在构建云原生平台的工程师,以便其他人可以以云原生的方式与之互动。

什么是微服务?

将分布式计算和云原生的概念提升到一个新高度,就得到了微服务。按定义,微服务是一种松耦合架构,组件之间没有相互依赖。

假设你有五个部分组成你的应用程序:三个后端 API,一些中间件用于连接后端和前端,和一个由多个路径组成的前端网站。在单体式环境中,你会将整个应用程序打包,部署到服务器上并运行二进制文件。然后,如果你需要更新或升级该应用程序的任何部分,例如某个后端 API,你就必须关闭其他部分的应用程序。这样不仅会导致生产环境停机,还会减缓推出新更新和新功能的能力,因为你必须指定一个特定的时间窗口来关闭整个平台。

微服务允许你将应用程序的这五个部分拆分成各自独立的部分。然后,你可以单独管理这些部分,而不是担心将它们组合起来进行部署并确保平台正常工作。

在 Kubernetes 环境中,你将拥有以下内容:

  • 后端 API 1 的一个容器镜像

  • 后端 API 2 的一个容器镜像

  • 后端 API 3 的一个容器镜像

  • 中间件的一个容器镜像

  • 前端的一个容器镜像

然后,每个容器镜像都可以分别进行更新、升级、部署和管理。

重要的是要注意,微服务不仅适用于容器和 Kubernetes。之前讨论的相同概念在五个不同的 Ubuntu 虚拟机上也可以很好地工作。只是从微服务的角度管理容器要比从虚拟机的角度管理容器更容易。在 Kubernetes 中做同样的事情,比在虚拟机上做需要的自动化和可重复实践要少得多。虽然这完全可行,但需要更多的努力。

在下一节中,你将把本节所学的内容应用到基于 Kubernetes 的场景中。

学习 Kubernetes 应用程序部署

当工程师首次开始将应用程序部署到 Kubernetes 集群时,它看起来像这样:

  1. 创建一个 Kubernetes 清单。

  2. 运行类似kubectl apply -fkubectl create -f的命令,针对清单进行操作。

  3. 确保应用程序有 Pods 正在运行。

  4. 访问你的应用程序,以确保它按照预期的方式运行。

虽然这是一种很好的开始部署应用程序到 Kubernetes 的方法,但我们必须深入探讨,以便完全理解应用程序部署过程是如何进行的,为什么它以这种方式工作,清单如何与 Kubernetes 交互以确保应用程序部署,以及 Kubernetes 如何保持 Pods 的期望状态。

看起来 Kubernetes 部署应用程序的方式简直像是在平台上发生的魔法,因为它就是这样构建的,应该就是这种方式,但实际上,Kubernetes 背后有着大量的组件,它们使得 Kubernetes 看起来像是一个神奇的部署平台。

在本节中,你将从头到尾学习 Kubernetes 中应用程序部署的工作原理以及成功部署所需的一切内部细节。

Kubernetes 清单

在实际部署应用程序之前,你需要了解大多数应用程序是如何部署到 Kubernetes 的——一个Kubernetes 清单。这个概念是你已经知道什么是 Kubernetes 清单,但可能不了解 Kubernetes 清单的内部结构。

Kubernetes 清单是一个基于 YAML 或 JSON 的配置,它以不同的方式与 Kubernetes API 进行交互。Kubernetes 清单是你指定想要使用哪个 API,以及从该 API 中要操作哪个资源的地方。API 分为两大类:

  • /api/v1

  • /apis/$GROUP_NAME/$VERSION

例如,以下是一个代码片段,展示了 Deployment 资源位于 /``api/v1 组中:


apiVersion: apps/v1
kind: Deployment

以下是另一个 Ingress 控制器的示例,你可以看到它位于 /apis/networking.k8s.io/v1


apiVersion: networking.k8s.io/v1
kind: Ingress

apiVersion 是你正在使用的 Kubernetes API,kind 是你正在创建、更新或删除的 Kubernetes 资源。

一个 Kubernetes 清单由四个关键部分组成:

  • apiVersion: 你用来创建对象/资源的 Kubernetes API 版本

  • Kind: 你想要创建、更新或删除的对象类型

  • Metadata: 帮助唯一标识资源/对象的数据

  • Spec: 你希望资源的样子

以下是一个 Kubernetes 清单:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80

让我们来解析一下。

首先是 API 版本。你可以看到 API 版本表示它正在使用核心组中的资源。接下来是 kind,它指定你正在创建/更新/删除的资源类型。然后是 metadata,它为部署指定了一个名称,以便通过元数据唯一标识它。最后是 spec,它表示你希望容器化应用的样子。例如,之前显示的 spec 指示清单正在使用最新版本的 Nginx 容器镜像并使用端口 80

为了总结这一部分,你需要了解关于 Kubernetes 清单以及 Kubernetes 的一般工作方式的一点是,它是声明式的。声明式意味着“告诉我做什么,而不是如何做”。命令式意味着“告诉我做什么以及如何做”。

例如,假设你在教别人如何做蛋糕。如果是命令式,你会告诉这个人使用什么食材、每种食材的分量以及如何一步步操作,最终引导他们完成成品。声明式则意味着你告诉他们需要哪些食材,剩下的由他们自己想办法完成。

Kubernetes 清单是声明式的,因为你告诉 Kubernetes 你想创建什么资源,包括资源的名称、端口、卷等,但你并不告诉 Kubernetes 如何创建该资源。你只需定义你想要的内容,而不告诉它怎么做。

常见但非唯一的方式

十有八九,当你将资源部署到 Kubernetes 时,你很可能会使用 Kubernetes 清单。然而,正如你在本书中所学到的那样,Kubernetes 的核心是一个 API。因为它是一个 API,所以你可以使用任何编程方法与它进行交互。

例如,以下是使用 Pulumi(一种流行的 IaaS 平台)在 Kubernetes 内创建 Nginx 部署的代码片段。此代码需要更多步骤才能运行,所以不要尝试运行它。这只是一个伪代码示例。

这里没有 YAML,也没有配置语言。它是原生的 Go (Golang) 代码与 Kubernetes API 交互:


        deployment, err := appsv1.NewDeployment(ctx, conf.Require("deployment"), &appsv1.DeploymentArgs{
            Spec: appsv1.DeploymentSpecArgs{
                Selector: &metav1.LabelSelectorArgs{
                    MatchLabels: appLabels,
                },
                Replicas: pulumi.Int(2),
                Template: &corev1.PodTemplateSpecArgs{
                    Metadata: &metav1.ObjectMetaArgs{
                        Labels: appLabels,
                    },
                    Spec: &corev1.PodSpecArgs{
                        Containers: corev1.ContainerArray{
                            corev1.ContainerArgs{
                                Name:  pulumi.String(conf.Require("containerName")),
                                Image: pulumi.String(conf.Require("imageName")),
                            }},
                    },
                },
            },
        })
        if err != nil {
            return err
        }

虽然你不会经常看到这种方法,但你应该知道这种方法的存在,你可以在 Kubernetes 中以任何编程方式创建任何资源,只要你通过 Kubernetes API 来操作。

控制器和操作员

Kubernetes 默认提供了一种方式来确保已部署应用程序的当前状态是期望的状态。例如,假设你部署了一个 Kubernetes 部署,它应该有两个副本。然后,由于某种原因,其中一个 Pod 被删除。部署控制器会察觉到这一点,并做出相应处理,确保创建第二个副本/Pod。所有可以在 Kubernetes 中创建的资源(如 Pods、Services、Ingress、Secrets 等)都有一个 控制器

操作员是控制器的一种特殊形式。操作员实现了控制器模式,它们的主要工作是将 Kubernetes 集群中的资源移动到期望的状态。

操作员还通过使用 CustomResourceDefinitions (CRDs) 扩展了 Kubernetes API,CRD 是一种让工程师可以利用现有 Kubernetes API 的方式,而无需构建整个控制器,而是直接使用 CRD 控制器。你会看到许多与 Kubernetes 集成的产品/平台来创建 CRD。

构建你自己的操作员和控制器的流行方式是使用 Kubebuilder:book.kubebuilder.io/

图 5.1 – 控制器

](https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/50-k8s-ccp/img/B19116_05_01.jpg)

图 5.1 – 控制器

你会听到 操作员控制器 这两个术语交替使用。为了帮助理解,只需记住,操作员就像是高层的大老板,确保组织运作良好,而控制器则像是工程师,亲自操作,确保组织得到所需的资源。

其中一种日益流行的形式是 GitOps。GitOps 查看存储在源代码管理中的 Kubernetes 清单的期望状态,而不是像控制器那样查看 Kubernetes 集群中当前部署的内容。

使用更高层次控制器的不同部署方式

当你单独部署一个 Pod 时,清单可能看起来如下所示:


apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP

这种方法的问题在于你没有一个高层次的控制器来管理 Pod。如果没有像 Deployment 或 DaemonSet 这样的高层次控制器,当 Pod 失败时,kubelet 会监视静态 Pod 并在其失败时重新启动它。并且也没有对当前状态和期望状态的管理。从生产角度来看,你永远不应该单独部署一个 Pod 资源。如果你想测试一个容器镜像,那是可以的,但就仅此而已。它应该仅用于测试/开发目的。

并非总是如此,但大多数情况下,你会看到以下这些管理 Pods 的生产级控制器:

  • Deployments:部署是 Core API 组中最高层次的控制器之一。它让你能够控制一个 Pod 或多个副本,并在集群中进行扩展。Deployments 还让你具备自我修复能力,并确认已部署的容器化应用的当前状态是期望的状态。以下是一个部署资源的示例代码:

    
    apiVersion: apps/v1
    
    
    kind: Deployment
    
    
    metadata:
    
    
      name: nginx-deployment
    
    
    spec:
    
    
      selector:
    
    
        matchLabels:
    
    
          app: nginxdeployment
    
    
      replicas: 2
    
    
      template:
    
    
        metadata:
    
    
          labels:
    
    
            app: nginxdeployment
    
    
        spec:
    
    
          containers:
    
    
          - name: nginxdeployment
    
    
            image: nginx:latest
    
    
            ports:
    
    
            - containerPort: 80
    
  • DaemonSets:这类似于一个部署资源,但它是集群范围的。它确保所有节点或你选择的节点都运行 Pod 的副本/复制品。你会看到 DaemonSet 的一个关键区别是没有副本字段。这是因为 Pod 的副本数不能超过工作节点的数量,也就是说,如果你只有三个工作节点,就不能有五个 Pod 副本。在这种情况下,你只能有三个 Pods。以下是一个 DaemonSet 示例代码:

    
    apiVersion: apps/v1
    
    
    kind: DaemonSet
    
    
    metadata:
    
    
      name: nginx-deployment
    
    
    spec:
    
    
      # nodeSelecter: Field that you can specify what worker nodes you want the Pod to deploy to
    
    
      selector:
    
    
        matchLabels:
    
    
          app: nginxdeployment
    
    
      template:
    
    
        metadata:
    
    
          labels:
    
    
            app: nginxdeployment
    
    
        spec:
    
    
          containers:
    
    
          - name: nginxdeployment
    
    
            image: nginx:latest
    
    
            ports:
    
    
            - containerPort: 80
    
  • StatefulSet

    
    apiVersion: apps/v1
    
    
    kind: StatefulSet
    
    
    metadata:
    
    
      name: nginx-deployment
    
    
    spec:
    
    
      selector:
    
    
        matchLabels:
    
    
          app: nginxdeployment
    
    
      serviceName: nginxservice
    
    
      replicas: 2
    
    
      template:
    
    
        metadata:
    
    
          labels:
    
    
            app: nginxdeployment
    
    
        spec:
    
    
          containers:
    
    
          - name: nginxdeployment
    
    
            image: nginx:latest
    
    
            ports:
    
    
            - containerPort: 80
    
    
    ---
    
    
    apiVersion: v1
    
    
    kind: Service
    
    
    metadata:
    
    
      name: nginxservice
    
    
    spec:
    
    
      selector:
    
    
        app: nginxdeployment
    
    
      ports:
    
    
        - protocol: TCP
    
    
          port: 80
    
    
      clusterIP: None
    

在选择使用哪个控制器时,这将取决于你的使用场景。除非是一些直接明了的情况,比如,如果你有一个需要保持其网络 ID 的容器化应用,那么你将使用 StatefulSet。

扩展

Kubernetes 的一个关键组件是它能够轻松地实现水平和垂直扩展。从生产角度来看,这大大减轻了你的负担。在标准的虚拟机环境中,你需要担心部署新的服务器、安装操作系统、更新软件包并部署应用程序二进制文件,最后,应用才会运行。

水平 Pod 自动扩展是最常见的方式,这意味着会创建更多的 Pods 来处理负载。垂直自动扩展意味着提升 Pod 的 CPU/内存。垂直 Pod 自动扩展并不常见,但也是可行的。

在扩展时,你可以使用标准的 ReplicaSets,但在生产环境中,副本数可能并不那么简单。例如,如果你有三个副本,但可能需要四个或十个副本,你需要一种方法来应对这种情况。你能做的最好的事情是至少从三个到四个副本开始,如果需要,逐步增加。如果你必须扩展到五个或十个副本,你可以更新 Kubernetes 清单,并使用 GitOps 解决方案或其他可重复的方式,使用kubectl apply -f name_of_manifest.yaml命令重新部署。

当你在扩展一个 Pod,或者说,当你在进行任何 Pod 部署操作时,永远不要使用像kubectl patch这样的命令,或者在命令行上的其他快速修复方法。如果你这么做,每次 Pod 被重新部署时,你的配置将不复存在,因为你是在命令行上临时/手动修改的。始终在 Kubernetes 清单中进行更改,并正确部署(记住,当前状态与期望状态的关系)。

如何水平扩展 Pods

当你水平扩展 Pods 时,关键在于副本数。例如,假设你有一个像下面这样的 Kubernetes 清单,其中包含两个副本:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80

你在前述清单上运行kubectl apply -f nginx.yaml,然后你意识到,由于 Nginx 前端的用户负载,你需要将副本数从两个增加到四个。此时,你可以更新 Kubernetes 清单,将副本数从两个更改为四个:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 4
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80

这种方法不会重新创建任何东西,因为你使用的是kubectl apply -f而不是kubectl create -fcreate用于创建全新的资源,而apply用于更新/修补现有资源。

如何垂直扩展 Pods

如前所述,垂直扩展 Pods 并不是常见的做法。然而,它是可行的。典型的方法是使用来自autoscaling.k8s.io API 的VerticalPodAutoscaler资源。它使你能够指向现有的部署,以便该部署由自动扩展器管理。例如,以下 Kubernetes 清单显示了一个名为nginxdeployment的部署的目标引用:


apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: nginx-verticalscale
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind:       Deployment
    name:       nginxdeployment
  updatePolicy:
    updateMode: "Auto"

请注意,启用垂直 Pod 自动扩展器并将更新模式设置为Auto时,它将具备以下能力:

  • 删除 Pods

  • 调整 CPU

  • 调整内存

  • 创建一个新的 Pod

这需要重新启动在 Pod 内运行的应用程序。

多容器 Pods

Sidecar,有时也叫做多容器 Pod,是一种将容器紧密耦合到一个 Pod 中的方式。通常,尤其从应用程序的角度来看,一个 Pod 运行一个容器。然而,也有一些使用场景需要运行 sidecar。最大的使用场景是当你运行某种日志收集器/聚合器时。许多工程师会将运行日志收集器的容器放到与应用程序一起运行的同一个 Pod 中。这样,容器之间可以直接通信,并且可以从应用程序中拉取日志,因为 Pod 内的容器共享相同的 IP 地址,但可以通过不同的端口进行访问。

你绝对不应该做的一件事是将多个应用程序运行在一个 Pod 中。例如,你绝对不想把前端应用和后端应用放在同一个 Pod 中。这完全违背了容器和微服务的初衷。Sidecar 仅适用于非常特定的用例,并且只有在绝对必要的情况下才使用。我个人的看法是,除非你真的必须使用,否则绝不要使用 sidecar。其他工程师可能不同意我,但我认为一个 Pod 应该只运行一个工作负载。这正是微服务架构的目的。我认为唯一绝对必要的情况是,当你运行服务网格并且需要将服务网格代理放入 Pod 中时。

以下是如果你在 Pod 中有多个容器,Kubernetes 清单将是什么样子。注意在spec.containers下有container1container2


apiVersion: v1
kind: Pod
metadata:
  name: testsidecar
spec:
  containers:
  - name: container1
    image: nginx
  - name: container2
    image: debian
    command: ["/bin/sh", "-c"]

活跃性和就绪性探针

每当你部署任何类型的应用程序时,无论它是否容器化,你都需要确保该应用程序按预期运行。在容器化环境中,情况也是一样的。假设你有一个运行 Nginx 前端的 Pod。Pod 可能已经启动并运行,具备所有适当的资源,等等。然而,这并不意味着 Pod 内运行的二进制文件按预期工作。为了确保实际应用程序按预期运行,你可以使用活跃性探针就绪性探针

活跃性探针表示容器是否正在运行。它帮助 Kubernetes 了解 Pod 的整体健康状况。kubelet 会不断向容器发送类似ping的信号,确保容器按预期运行。如果活跃性探针认为容器不健康,kubelet 会重启 Pod。

就绪性探针表示容器是否准备好接收请求。从应用程序的角度来看,就绪性探针更为重要,因为它告诉 Kubernetes 是否将服务流量路由到 Pod。如果服务尝试将流量路由到一个 Pod,而这个 Pod 已经宕机或不健康,应用程序将无法访问。就绪性探针告诉服务哪些 Pod 已准备好接收请求,哪些没有。

以下是一个就绪性探针的示例:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 80
        readinessProbe:
          tcpSocket:
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10

以下是一个活跃性探针的示例:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        imagePullPolicy: Never
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            scheme: HTTP
            path: /index.html
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

所有生产级的 Kubernetes 部署都应该使用就绪探针(readiness probes)。

在下一节中,你将深入探讨一个重要的话题——如何隔离你的容器化应用程序,以及几种不同的实现方式。

探索隔离和命名空间

一旦应用程序部署完成,工程师们会擦去额头上的汗水,与团队击掌庆祝胜利。然而,部署之后发生了什么呢?更重要的是,如果你需要重新部署这些应用程序呢?或者部署其他类型的应用程序?或者部署到不同的位置或隔离点?(隔离将在本章后面讨论。)让一个应用程序上线本身就是一项心理挑战,但你问自己的接下来会发生什么的问题通常是最重要的。这些问题包括以下内容:

  • 下一次部署会是自动化且可重复的吗?

  • 如果你需要重新部署应用程序,这次部署会有效吗?

  • 应用程序能(或应该)相互靠得这么近一起运行吗?

  • 哪些工程师应该访问哪些应用程序,为什么?

部署应用程序是一项巨大的胜利,但设计应用程序应该如何运行以及在哪运行,是决定成功与失败生产环境之间的关键。关于应用程序隔离和多租户的问题常常让工程师们彻夜难眠,因为它们不仅仅是工程工作,更是规划和架构工作。它们更少涉及实际的键盘操作,而更多是需要从更高层次进行批判性思考,而非埋头写代码。

在本节中,你将学习一些最流行的隔离技术。让我们开始吧!

命名空间

隔离的第一个层次通常是命名空间。当你部署 Pods 时,最不希望做的事就是将所有东西都部署到默认命名空间。相反,你希望确保应用程序有各自的命名空间。在网络层面,一个命名空间中的 Pods 可以与另一个命名空间中的 Pods 通信。然而,如果你有一个用于在某个命名空间中部署 Pods 的服务账户,而另一个命名空间有一个用于部署 Pods 的服务账户,那么同一个服务账户就不能用于管理所有 Pods。这从 Pod 管理的角度提供了一定程度的隔离,并确保有适当的身份验证和授权。但从网络的角度来看,Pods 仍然可以与其他命名空间中的 Pods 进行通信。

请注意图 5.2中有三个命名空间:

  • argoCD

  • kube-system

  • monitoring

图 5.2 – Kubernetes 资源

](https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/50-k8s-ccp/img/B19116_05_02.jpg)

图 5.2 – Kubernetes 资源

上面的截图显示,argocd命名空间中的所有内容都与kube-system命名空间中的所有内容隔离/分开。如果工程师运行kubectl get pods,他们将只会看到他们有权限访问的命名空间中的 Pods。

单一租户

将隔离和分离推向更深层次的是租户模型。首先,让我们从单租户开始,但在深入之前,先讨论一下 Kubernetes 中的租户模型是什么意思。

通过租户隔离可以涉及用户、工程师、应用以及各种不同的资源。例如,单租户可能意味着在集群上运行一个容器化的应用,或者可能意味着确保一个工程师可以访问一个集群,而其他人无法访问,但他们可以运行任意多的应用。

单租户的一个典型场景是隔离开发环境。假设你是一个开发人员,需要一个 Kubernetes 集群来测试应用栈。场景是,平台工程团队,或任何管理 Kubernetes 集群的团队,给你一个专属的 Kubernetes 集群来测试应用栈。这是执行单租户的一个好方法,因为它允许不同技术栈的所有工程师在不干扰或影响其他应用栈的情况下测试他们的代码。

多租户

相对而言,多租户则是另一种情况。多租户指的是多个工程师、用户或应用在同一个 Kubernetes 集群上运行。如果你再次查看显示命名空间的Figure 5**.2,你会看到 Prometheus、ArgoCD 和 Nginx 运行在同一个集群上。这被认为是一个多租户集群。如果 ArgoCD、Nginx 和 Prometheus 分别运行在不同的集群上,那就是单租户。

在现实世界中,很少看到应用程序运行在不同的集群上,或者说每个集群运行一个应用程序。相反,你通常会看到应用程序使用多租户模型,而开发人员测试应用栈时使用单租户模型,一旦应用栈经过测试,它就会与其他应用一起迁移到 Kubernetes 集群中。

在下一节中,你将学习如何理解无状态应用、有状态应用和卷。

调查无状态和有状态应用

从高层次来看,应用有两种形式——需要存储数据的应用和不关心数据是否存储状态的应用。让我们思考两个不同的场景。

当你登录到 Gmail 帐户或其他电子邮件服务提供商时,一切都会保持在应该存在的位置。你可以看到收件箱中的邮件、已发送的邮件、垃圾桶中的邮件,等等。应用程序/平台保持正常工作,因为数据是有状态的。现在,换一个角度来讲,考虑一下www.google.com。当你在网页浏览器中访问www.google.com时,你总是从头开始。输入问题的框在那儿,但是你之前向 Google 提出的问题的结果却没有显示。它总是一个全新的、干净的页面。这是因为www.google.com是无状态的,也就是说,它不会在每次搜索后将你的数据保存到网页浏览器中(当然,它确实会保存一些数据,但那是另一个话题)。

当然,有状态与无状态的问题是一个更为深刻的讨论,但这只是你可以用来理解这两种不同类型应用的一个高层次定义。

在下一节中,你将学习 Kubernetes 中有状态和无状态应用的不同部署方法,以及包括 Pod 的限制、配额和请求在内的资源考虑因素,以确保你运行的生产级环境是可持续的。

有状态与无状态

在本节开头,我分享了 Gmail 的例子,实际上展示了什么是有状态应用,什么是无状态应用。从 Kubernetes 的角度来看,关键区别在于无状态应用不需要存储数据。 有状态应用需要后端存储,例如卷。另一个关键区别是,有状态应用需要保持唯一的 ID,所以如果一个 Pod 宕机,替代它的 Pod 必须拥有相同的唯一 ID。而无状态应用则不需要保持唯一的 ID。

一个常见的误解是,无状态应用从不使用卷,但事实并非如此。例如,你可以有一个无状态应用,它可能需要一个后端数据库或一个卷/硬盘来存储值。

卷和硬盘并不是构成有状态应用的因素,唯一的 ID 才是构成有状态应用的关键。

容器存储接口

为了使 Kubernetes 与非本地的外部组件进行交互,必须有一种插件。你在前几章中学习了容器网络接口CNI),它是一个插件,可以在 Kubernetes 中使用不同的网络框架。容器存储接口CSI)与此类似,但它是针对存储设备的。例如,你可以为 NetApp、AWS S3、Azure 存储以及许多其他存储提供商使用 CSI。

在有了这些接口之前,组织必须将代码放入以连接不在核心 Kubernetes 代码中的资源。仅举个例子,假设 Azure 想允许 Kubernetes 工程师在 Kubernetes 内部使用 Azure 存储来存储 Pod 的输出。在 CSI 出现之前,Azure 必须将使这一切成为可能的代码放入核心 Kubernetes 代码中。这是一个很大的麻烦,因为 Azure 不仅需要等待 Kubernetes API 的新版本发布才能推出该功能,而且如果存在 Bug 或需要推出新更新,它们还必须等待下一个 Kubernetes API 发布。

CSI,以及 Kubernetes 中的通用接口/插件,确保组织可以单独为 Kubernetes 创建插件,而不是依赖核心 Kubernetes 代码。

如果你想看一个 CSI 的例子,可以在 GitHub 上查看:github.com/kubernetes-sigs/azuredisk-csi-driver.

Volumes 是硬盘,简单明了。

有了卷,你可以让一个 Pod 或多个 Pod 在某个位置存储数据。该位置可以是 Azure、AWS、NetApp、其他某个存储提供者,甚至是 Pod 运行的 worker 节点(绝对不推荐。这只是一个例子)。

当为 Pod 创建卷时,通常有三个步骤:

  • StorageClass: 存储类是向某个存储供应商(动态地)请求硬盘的一种方式。例如,你可以创建一个连接到 EBS 的存储类。然后,稍后可以使用卷(你马上就会学到的)调用该存储类,并利用与存储的连接。你可以在 Azure、GCP 和所有其他云提供商,包括大多数存储提供者中做同样的事情:

    
    kind: StorageClass
    
    
    apiVersion: storage.k8s.io/v1
    
    
    metadata:
    
    
      name: azurefile-csi
    
    
    provisioner: file.csi.azure.com
    
    
    allowVolumeExpansion: true
    
    
    mountOptions:
    
    
      - dir_mode=0777
    
    
      - file_mode=0777
    
    
      - uid=0
    
    
      - gid=0
    
    
      - mfsymlinks
    
    
      - cache=strict
    
    
      - actimeo=30
    
    
    parameters:
    
    
      skuName: Premium_LRS
    
  • PersistentVolume: 持久化卷由工程师手动创建,使用存储类从可用源利用存储。例如,持久化卷将连接到先前示例中的 EBS 存储类:

    
    apiVersion: v1
    
    
    kind: PersistentVolume
    
    
    metadata:
    
    
      name: azure-pv
    
    
    spec:
    
    
      storageClassName: " azurefile-csi "
    
    
      claimRef:
    
    
        name: azurefile
    
    
        namespace: default
    
  • PersistentVolumeClaim: 最后一部分是持久化卷索取,这是用户通常在创建 Pod 的 Kubernetes 清单中提出的请求,以使用存储类中可用的一些存储空间。工程师可以说:“嘿,我想要从这个存储类中为我的 Pod 使用 10 GB 存储空间”:

    
    apiVersion: v1
    
    
    kind: PersistentVolumeClaim
    
    
    metadata:
    
    
      name: azurefile
    
    
    spec:
    
    
      accessModes:
    
    
        - ReadWriteMany
    
    
      storageClassName: azurefile-csi
    
    
      resources:
    
    
        requests:
    
    
          storage: 10Gi
    

这时,你可能会想:“如果我可以通过声明自动请求一些存储,那为什么还需要持久化存储卷呢?”这是个好问题。答案取决于你的环境。如果你使用的是 NetApp 存储,且你有 1,000 GB 的存储空间,你需要工程师创建持久化存储卷并管理这些存储卷,因为你只有 1,000 GB 的存储。如果你尝试超过 1,000 GB,故障就会开始发生,因此让有人来管理它是有意义的。反过来,如果你使用的是云存储,如 Azure 或 AWS,那么这些存储对于用户来说是无限的(当然,你需要为此付费),所以直接申请一个持久化存储卷而不是让工程师创建持久化存储卷会更合理。

资源请求和限制

在任何生产环境中,你都有 Kubernetes 集群在服务器上运行,无论它是在本地还是托管的 Kubernetes 服务中。因为它们运行在服务器上,所以这些服务器有硬件资源,而所有服务器都有其资源限制。服务器上没有无限的 CPU无限的内存。服务器的资源是有限的,服务器也能达到 100%的容量。

因此,当你创建 Pod 时,你应该指定资源限制和请求。你绝不希望让任何东西——无论是虚拟化的虚拟机还是容器化的应用程序——在环境中自由地占用尽可能多的 CPU 和内存。如果你不控制资源(例如内存和 CPU),每个应用程序都可能会随意占用它所需要的资源。

让我们考虑一个基本的例子。假设你有一个存在内存泄漏的应用程序。如果你将其容器化,那么它所在的 Pod 将持续消耗越来越多的内存,直到工作节点最终失败或应用程序崩溃,而你只能在为时已晚时才会知道。

在深入了解之前,我们先定义一下限制请求之间的区别。

限制是告诉 Pod:“你不能超过这个限制。”例如,如果你为 Pod 指定了X数量的 CPU 或内存,那么该 Pod 就不能超过这个限制。它被完全封锁。

以下是限制的示例。如你所见,Nginx 应用程序的内存限制为 128 Mi。超过这个值,Kubernetes 会说:“不行,你不能拥有更多”:


    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        resources:
          limits:
            memory: "128Mi"
        ports:
        - containerPort: 80

请求是 Pod 能够保证获得的资源。如果 Pod 请求某个资源,Kubernetes 只会将它调度到能够提供该资源的工作节点上。

以下是请求的示例。在这个例子中,Kubernetes 会说:“好吧,你想要 64 Mi 内存和 250m 的 CPU。让我把你调度到一个可以处理这些的工作节点上”:


    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
        ports:
        - containerPort: 80

以下是完整的清单示例:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      namespace: webapp
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
        ports:
        - containerPort: 80

你应该选择哪个?

对于请求和限制的工作方式,存在一些困惑。

当 Pods 使用完内存后,它们会将内存归还给工作节点,然后这些内存会回到资源池中供其他 Pods 使用。而 CPU 则不同,它不会被归还。Pod 会继续占用这个 CPU。因为这个原因,最佳实践不是让 Pod 一直占用 CPU 直到它被删除,因为它可能并不总是需要这么多的 CPU。这实际上是在浪费 CPU 资源。

那么,您应该选择哪一个呢?

在每个生产环境中,您应该始终设置请求,但您只应限制 CPU。

命名空间配额

当涉及到限制和请求时,您可以做的其中一件非常棒的事情就是为命名空间设置它们。例如,您可以为一个命名空间设置 1,000 Mi 的限制和 512 Mi 的请求。这样,所有在该命名空间中运行的节点会自动被限制到所需的资源,这意味着您不需要在每一个 Kubernetes Pod 清单中都设置限制和请求。以下代码块展示了资源配额:


apiVersion: v1
kind: namespace
metadata:
  name: test
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: memorylimit
  namespace: test
spec:
  hard:
    requests.memory: 512Mi
    limits.memory: 1000Mi

在接下来的最后一节中,您将学习如何升级应用程序以及不同类型的更新方法。

升级 Kubernetes 应用

在本章中,您学到了一些非常重要的知识:

  • 如何部署应用程序

  • 如何在 Kubernetes 上部署不同类型的应用

  • 如何确保应用程序正确扩展

  • 如何确保应用程序按预期运行

一旦您将应用程序部署到理想的状态,那将是一个巨大的成就。然后,不久之后,您就需要升级或更新应用程序,而您必须重新开始这整个过程。您必须测试新版本的应用程序,在不影响整个生产环境的情况下进行部署,并重新测试所有组件,以确保它按预期运行。

可能还有一些情况下,尤其是非常常见的情况,您必须将更新或升级回滚到先前的应用版本。也许它在预发布环境中没有经过充分测试,或者出现了 QA/回归测试没有捕获到的问题。无论如何,您都需要有一个可靠的计划和方法来进行回滚操作。

在本节中,您将学习几种不同的方式来测试应用程序的更新和升级,如何升级和更新在 Kubernetes 中运行的应用程序,以及如何在必要时回滚更新和升级。

升级类型

首先,让我们解析 Kubernetes 中的典型升级类型。

A/B 测试是一种将一组用户放在应用程序的一个版本上,另一组用户放在另一个版本上的方法。例如,假设您正在测试应用程序的两个版本,v1.1 和 v1.2。一个用户组将使用 v1.1,另一个用户组将使用 v1.2。此时,您可以测试诸如性能、用户如何与新版本应用程序交互、漏洞和问题等内容。这种测试类型是一种受控实验。

金丝雀发布与 A/B 测试几乎相同,只不过它是通过真实用户进行的。以之前的例子为例,假设你有 v1.1 和 v1.2 两个版本的应用。你将 v1.2 版本投入生产,并将一部分用户分配到 v1.2 版本,而另一部分用户仍然使用 v1.1 版本。通过这种方式,你可以观察用户如何与生产环境中的新版本进行互动。

蓝绿测试是指你有两个生产环境,一个运行 v1.1 版本,一个运行 v1.2 版本。所有用户仍然在 v1.1 上,但你会逐步将所有用户迁移到 v1.2 版本。一旦确认 v1.2 版本运行正常,所有用户都会迁移到 v1.2 版本。

在 Kubernetes 中,最常用的升级方法是滚动更新,根据之前的解释,它属于蓝绿部署。

升级中的应用会发生什么?

当你在 Pod 中升级容器镜像时,发生的过程是新 Pod 启动并经过测试,旧 Pod 随后被删除。

让我们通过以下图示,回顾之前提到的 v1.1 和 v1.2 版本的例子:

图 5.3 – 滚动更新

图 5.3 – 滚动更新

在之前的架构图中,发生的情况是 v1.1 在 IP 地址为 10.0.0.5 的 Pod 上运行。然后,运行 v1.2 的新 Pod 启动,并与旧 Pod 同时运行。一旦部署确认 v1.2 版本的 Pod 正常工作并按预期进行,用户将开始迁移到新 Pod 上。一旦所有用户都迁移到运行 v1.2 的新 Pod,旧 Pod(运行 v1.1)将被删除。

滚动更新

前一节中解释的是滚动更新。让我们从代码角度看一下。以下是一个 Kubernetes 清单,运行的是一个部署规范,使用的是 v1.1 版本的容器化 Nginx 镜像:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:1.1
        ports:
        - containerPort: 80

然后,容器化应用的升级时刻到来了。要使用RollingUpdate(蓝绿部署)升级应用,你需要将nginx:1.1容器镜像版本替换为nginx:1.2RollingUpdate配置包含progressDeadlineSecondsminReadySeconds配置,用于确认容器化应用的新版本能够正确启动。在策略映射中,你需要指定RollingUpdate类型,并确保在更新进行时,始终有一个副本运行旧的容器化应用版本。这样,用户就不会被迫退出应用。以下代码将执行正确的滚动更新操作:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  revisionHistoryLimit: 3
  progressDeadlineSeconds: 300
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  replicas: 4
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:1.2
        ports:
        - containerPort: 80

然后,你将运行kubectl apply -f命令,执行 Kubernetes 清单,滚动更新就会开始。

回滚

如果你想回滚RollingUpdate,你需要执行两个命令。

首先,通过以下命令获取你想要回滚的修订版本号:


kubectl rollout history deployment nginxdeployment

接下来,撤销RollingUpdate


kubectl rollout undo deployment/nginxdeployment --to-revision=whichever_revision_number_youd_like

更新和回滚不仅在教育角度非常重要,而且在你的组织转向更多微服务驱动的方式时,你很可能会经常遇到这些操作。

摘要

在 Kubernetes 中,有许多类型的资源部署方式,通常没有明确的“对”与“错”之分,选择哪种方式取决于具体情况。只有在部署特定应用时,才会有真正的“对”与“错”。如果你有一个有状态的应用程序,你应该使用 StatefulSet。关于你应该使用哪种控制器,实际上没有什么神秘的,也没有好坏之分。这仅仅取决于你需要部署和管理的应用程序和工作负载类型。

在下一章,我们将从更高级的角度深入探讨不同类型的部署。

进一步阅读

第六章:Kubernetes 部署– 同样的游戏,下一阶段

在上一章中,你深入探讨了不同的部署场景,以及如何不仅仅考虑控制器,还要考虑应用的升级、要部署的不同类型的应用,以及让应用运行起来的不同方法。在本章中,你将进一步深入探讨不同的部署风格以及故障排除,而不仅仅是进行部署。

部署的第一步是搞清楚你要做什么——它是什么类型的应用,想使用哪种类型的 Kubernetes 资源,以及你可能想使用的不同插件,比如 CSI。了解了你要部署的内容后,下一步就是思考如何部署。

使用 Kubernetes 有很多不同的部署方法——自动化部署、手动部署,以及介于自动化和手动之间的一些方法。执行部署的方法有很多种,因此你不会学到所有方法,因为那可能会占据六章书的篇幅,但你会学习主要的部署方式以及如何打包 Kubernetes Manifests。

学习了部署之后,思考当某些事情不可避免地出错时如何进行故障排除是一个好的、合乎逻辑的下一步。通常,工程师会在实际操作中学习故障排除技巧,但在问题发生之前就思考故障排除技术是一种很好的方法。

在学习了故障排除和部署容器化应用后,你将学习如何管理运行在 Kubernetes 上的应用之间的网络连接,以及如何迁移现有的、更为单体化的应用。

在本章中,我们将涵盖以下主题:

  • 了解 Helm charts 和 Kustomize

  • 使用 CI/CD 和 GitOps 进行部署

  • 故障排除应用部署

  • 服务网格和 Ingress

技术要求

在本节中,你将基于上一章中学习到的不同类型的部署和 Kubernetes 资源思考方法的知识,进一步拓展这一知识。你应该对自动化部署方法如 CI/CD 有一个简要了解,并对服务网格的概念有一个高层次的理解,同时也具备一些应用架构的知识。如往常一样,你会在 GitHub 上找到本章的代码:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch6

了解 Helm charts 和 Kustomize

当你在使用 Kubernetes 时,除非是用于测试的开发环境,否则几乎不可能只有一个 Kubernetes 清单。你很可能有多个清单,涉及各种资源,例如部署(Deployments)、服务(Services)、守护进程集(DaemonSets)、配置映射(ConfigMaps)、入口(Ingresses),以及其他许多 Kubernetes 资源。几乎每个部署到集群的 Kubernetes 平台或工具都使用 Kubernetes 清单。

在所有这些 Kubernetes 清单中,你需要传递许多不同的值和参数才能让一切正常工作。在这一部分,你将学习两种不同的 Kubernetes 清单管理方法——Helm chartsKustomize

为什么要考虑清单的部署方法?

在深入探讨不同的部署方法之前,首先理解为什么你会考虑使用其他部署方法而不是仅仅通过终端部署清单,是有意义的。

有三个主要的要点,我们将在接下来的子部分中讨论。

扩展性

当考虑到扩展性时,如果工程师总是从他们的笔记本电脑进行部署,根本无法扩展部署。工程师可能使用不同的插件、不同的 IDE、不同的终端设置,甚至是不同的操作系统。所有这些环境的不确定性本身就可能导致大量的错误。如果每个工程师都依赖于自己的计算机来部署环境,那如果他们的笔记本电脑崩溃了怎么办?或者在一天中的某个时刻突然有了更新?又或者有些人不在办公室?有如此多的变量影响,依赖本地计算机进行部署是一个糟糕的主意。相反,从一个中心化的位置进行部署更加合理。环境保持一致,每个人都能使用,你可以根据团队的需求进行定制,而且不必担心有人不在办公室。

任何事情都有可能出错

进入第二个要点,这与第一个要点有些相似,任何事情都有可能出错。每个组织的目标都是确保每次部署都能成功,零差错,并能够随时进行部署。市场团队在我们的脑海中描绘出这样的场景:“使用这个工具每天可以部署 20 次,而且每次都能成功。”但正如所有工程师所知道的那样,这并不是现实。任何像网络波动或者在输入变量名时犯错误这样的简单事情,都可能导致部署失败,进而导致应用程序宕机。因此,拥有一个正确的部署策略对于 Kubernetes 的可重复性至关重要,而对于一般的可重复性也是如此。拥有一个正确的流程和规则来规定某个部署任务如何进行、何时进行或在哪里进行,是成功更新和团队成员通宵修复问题之间的分水岭。

它是手动的

最后一点需要明确的是,在终端上运行命令来部署配置是一个非常手动的过程。在今天的世界里,工程师们希望花时间专注于价值驱动的工作,而不是解决问题。事实上,这正是自动化和可重复性存在的重要原因。工程师们希望节省时间,停止处理单调的任务。如果您经常在计算机上部署到环境中,那么您正在重复手动工作的“糟糕”体验。当然,有些情况下,您可能希望从 localhost 部署。例如,当我部署到开发环境或第一次测试新配置时,我不会创建一个可重复使用的解决方案,因为我不确定它是否有效。然而,一旦我知道它有效并且我的初始开发测试完成,我将自动化工作流程。

在本章中,继续前进时,请记住,思考部署工作流的原因是尽可能减少上述三点的影响。

Helm 图表

可重复部署策略的理念是为了让您的生活更轻松,但随着新策略的出现,您需要了解不同的实施方法。了解的第一种方法是 Helm 图表。

Helm 是一个开源项目,最初由 DeisLabs 创建,并捐赠给 CNCF;现在由 CNCF 维护该项目。Helm 最初发布时的目标是为工程师提供一种更好的方式来管理所有创建的 Kubernetes Manifests。Helm 是为 Kubernetes 设计的工具和平台,所以它使用的仍然是您熟悉的 YAML,只是以不同的方式打包 - 确切地说,只是 YAML。Kubernetes 的初衷是提供一种声明式地部署容器化应用程序的方法。它并非旨在为您提供一种有意义地打包一堆 Kubernetes Manifests 以便一起使用的方式。这就是 Helm 的作用所在。此外,Helm 还会保留所有已部署图表的发布历史记录。这意味着如果出现问题,您可以返回到先前的一个发布版本。

2016 年 1 月,该项目与名为 Kubernetes 部署管理器的 GCS 工具合并,并将项目移到 Kubernetes 之下。Helm 于 2018 年 6 月从 Kubernetes 子项目晋升为 CNCF 项目。

简而言之,Helm 是一种将一堆 Kubernetes Manifests 打包成应用程序并部署的方法。

使用 Helm 图表

现在您已经了解了 Helm,让我们从实践的角度深入了解它:

  1. 首先,您需要安装 Helm。由于基于操作系统的不同,您可以在这里找到几种不同的安装方法:helm.sh/docs/intro/install/

  2. 安装 Helm 后,请找到或创建一个目录,您希望您的第一个 Helm 图表存在于其中。最好是一个空目录:

    
    mkdir myfirsthelmchart
    
  3. 接下来,进入终端中的那个目录。

  4. 在新目录中,运行以下命令创建一个 Helm chart:

    
    helm create name_of_chart
    

一旦你这样做,你应该会看到一个类似以下截图的目录结构。在这个例子中,chart 被命名为 newchart

图 6.1 – Helm chart

图 6.1 – Helm chart

如果你打开 templates 目录,你会看到一堆关于部署、Ingress 和其他很多内容的示例。

图 6.2 – 示例 Helm

图 6.2 – 示例 Helm

如果你打开 values.yaml 文件,你会看到可以开始添加你希望传递到模板中的值的位置。

图 6.3 – 值文件

图 6.3 – 值文件

  1. 要部署 Helm chart,请运行以下命令:

    
    helm install nginxapp .
    
  2. 要安装 Helm chart,请运行以下命令。. 符号表示当前目录,即 Helm chart 所在的目录:

    
    helm install mynewapp .
    

当然,这不是关于 Helm 所有的知识。实际上,关于 Helm 的书籍有很多。这一节的目标是让你走上正确的道路。

Helm Chart 最佳实践

以下是使用 Helm 在生产环境中遵循的最佳实践列表:

  • 在存储 Helm Charts 时,确保根据需要将其设置为公开或私有。你最不希望的就是将 Helm chart 推送到本不该公开的公共注册表。

  • 记录你的 charts 做了什么。

  • 确保将 Charts 存储在源代码控制中。

  • 在做出更改后,始终测试 Helm charts。

Kustomize

Helm 和 Kustomize 很相似,但也有一些独特的差异。Helm 的一个主要用例是拥有一个 values.yaml 文件来存储要传递到 Kubernetes Manifest 的值。Kustomize 也有类似的概念。

使用 Kustomize 时,你有一个模板,通常称为基础模板。这个基础模板是你想要使用的模板,可能是 Kubernetes 的部署、服务、Pod,或任何你想要的东西。模板就是你传入值的实际基础。除了模板之外,你还有一个 kustomization.yaml 文件,它告诉 Kustomize 使用哪些模板。例如,假设你有一个 deployment.yamlservice.yaml 文件,你会将这两个文件名放入 kustomization.yaml 文件中,这样 Kustomize 就会知道应该将值推送到这两个文件中。

已经提到过几次,但没有彻底解释。值可以是你在运行时希望传递的任何内容。例如,假设你有三个环境——开发(dev)、预发布(staging)和生产(prod)。在开发环境中,你有一个副本;在预发布环境中,你有两个副本;在生产环境中,你有三个到四个副本。你可以使用 Kustomize 将这些值传递到一个模板中,这样你就不需要拥有三个具有不同副本值的清单,而是通过一个模板来传递这些值。

但是如何传递这些值呢?

在 Kustomize 目录中,通常有两个目录——base 和 overlays。base 是模板存放的地方,overlay 目录是每个环境存放特定值的地方。例如,假设你有devstagingprod等环境。

图 6.4 – 基础配置

图 6.4 – 基础配置

dev覆盖目录,以及其他目录,都应包含一个kustomization.yaml文件。

图 6.5 – 开发环境覆盖配置

图 6.5 – 开发环境覆盖配置

kustomization.yaml文件中,你可以找到副本数的配置。

图 6.6 – Kustomization 文件

图 6.6 – Kustomization 文件

请注意,resources映射指向的是base目录,replicas映射指定了部署以及副本数。

Helm 和 Kustomize 的主要区别在于,Helm 的主要目的是将多个 Kubernetes 清单打包并像应用程序一样进行部署,而 Kustomize 的主要目的是拥有一个模板,将你的值(如副本数)推送进去。Helm 也可以做到这一点,但这不是 Helm 的主要目的。

使用 Kustomize 配置

现在你了解了 Kustomize,让我们从实践的角度深入了解它:

  1. 首先,你需要安装 Kustomize。由于安装方法因操作系统而异,你可以在这里找到几种不同的安装方法:kubectl.docs.kubernetes.io/installation/kustomize/

  2. 安装完 Kustomize 后,找到或创建一个目录,存放你的 Kustomize 配置。你可以将其命名为kustomize

  3. kustomize目录下创建两个新目录,分别命名为overlaysbase。在overlays目录下,创建一个名为dev的新子目录。它应该类似于以下截图所示。

图 6.7 – 开发环境覆盖配置

图 6.7 – 开发环境覆盖配置

  1. base目录下,创建一个名为deployment.yaml的新文件,并将以下代码粘贴进去:

    
    apiVersion: apps/v1
    
    
    kind: Deployment
    
    
    metadata:
    
    
      name: nginx-deployment
    
    
    spec:
    
    
      selector:
    
    
        matchLabels:
    
    
          app: nginxdeployment
    
    
      replicas: 2
    
    
      template:
    
    
        metadata:
    
    
          labels:
    
    
            app: nginxdeployment
    
    
        spec:
    
    
          containers:
    
    
          - name: nginxdeployment
    
    
            image: nginx:latest
    
    
            ports:
    
    
            - containerPort: 80
    
  2. 接下来,在base目录下创建一个名为kustomization.yaml的新文件,并将以下配置粘贴进去,告诉 Kustomize 使用哪个 Kubernetes 清单:

    
    apiVersion: kustomize.config.k8s.io/v1beta1
    
    
    kind: Kustomization
    
    
    resources:
    
    
      - deployment.yaml
    
  3. 在最后一步,在overlays | dev目录下创建一个新文件,命名为kustomization.yaml,并将以下清单粘贴进去:

    
    apiVersion: kustomize.config.k8s.io/v1beta1
    
    
    kind: Kustomization
    
    
    resources:
    
    
    - ../../base/
    
    
    replicas:
    
    
    - name: nginx-deployment
    
    
      count: 1
    
  4. 配置和目录准备好后,cd进入base | dev目录并运行以下命令:

    
    kubectl kustomize
    

你将看到类似以下截图的输出,它为你提供了一个副本数为 1 的配置,而不是模板中包含的 2 个副本。

图 6.8 – Kustomize 输出

图 6.8 – Kustomize 输出

与 Helm 图表一样,Kustomize 的主题可以写成一本小书,这意味着本节内容无法涵盖所有内容。然而,它应该能帮助你朝着正确的方向迈出第一步。

Kustomize 最佳实践

以下是使用 Kustomize 时在生产环境中应遵循的最佳实践清单:

  • 确保将overlays放入各自的目录中。你不必这样做,但这样可以使配置更整洁。

  • 确保所有代码都存储在源代码控制中。

  • 遵循标准目录结构——base是模板所在的目录,overlays是你希望传递给模板的值。

在下一节中,你将学习关于容器化应用的两种主要部署方法。

使用 CI/CD 和 GitOps 进行部署

Kubernetes 部署分为三个阶段:

  • 在本地计算机上部署清单

  • 使用自动化解决方案(如 CI/CD)部署清单,最终只会运行kubectl apply -f命令,和你本地计算机上的操作相同

  • 一种全新的、完全自动化的解决方案,从部署角度来看,它(通常)是 100%免操作的

在第一阶段,它根本不可扩展。一堆工程师在自己的本地机器上运行命令来部署一个容器化应用,而且每个人的做法都不一样,使用不同的代码编辑器和插件。那是一团糟,无法支持部署过程的扩展性,也阻碍了工程师们进行价值驱动的工作,反而他们不得不整天呆在终端上运行命令。

在本节中,你将学习更多常见的、自动化的以及新的应用部署方法,这些方法主要围绕 CI/CD 和 GitOps 展开。

什么是 CI/CD?

在 CI/CD 方面,假设你正在阅读这本书,你已经在 CI/CD 中工作并知道它是什么。因此,本书不会对 CI/CD 进行全面的拆解,而是提供一个简要概述。

从定义上讲,CI/CD 是一种创建应用程序工件并将其自动化地部署到目标目的地的方法。随着 CI/CD 的普及,工程师们开始将其用于其他目的——例如,将 Terraform 代码打包成工件并运行,以便自动创建基础设施。

在 CI 过程中,工程师们关注以下问题:

  • 测试代码

  • 打包代码

  • 确保所有前提条件和依赖项都已满足

  • 构建容器镜像

在 CD 过程中,工程师们关注以下问题:

  • 部署工作负载

  • 确保它们到达正确的目的地

  • 确保应用或服务和基础设施按预期启动并运行

使用 CI/CD 进行 Kubernetes 部署

和其他技术一样,几乎每件事都有成千上万种方式可以做。因此,我们不能在这里列出每种 CI/CD、自动化和云场景。为了简化起见,本文将展示 GKE 的 Terraform 代码和 GitHub Actions 的 YAML 流水线。这被视为伪代码,但它在正确的环境中是可以工作的。

首先,让我们从 Terraform 代码开始,并逐步拆解:

  1. 首先,你将从 Google 提供者开始,指定区域:

    
    provider "google" {
    
    
      project     = var.project_id
    
    
      region      = var.region
    
    
    }
    
  2. 接下来,指定google_container_cluster,以便你可以实现你想要使用的 VPC、子网和工作节点数量:

    
    resource "google_container_cluster" "primary" {
    
    
      name     = var.cluster_name
    
    
      location = var.region
    
    
      remove_default_node_pool = true
    
    
      initial_node_count       = 1
    
    
      network    = var.vpc_name
    
    
      subnetwork = var.subnet_name
    
    
    }
    
  3. 最后一个资源是google_container_node_pool,它实现了 GKE 所需的 Google API,包括节点数量、节点名称和节点大小或类型:

    
    resource "google_container_node_pool" "nodes" {
    
    
      name       = "${google_container_cluster.primary.name}-node-pool"
    
    
      location   = var.region
    
    
      cluster    = google_container_cluster.primary.name
    
    
      node_count = var.node_count
    
    
      node_config {
    
    
        oauth_scopes = [
    
    
          "https://www.googleapis.com/auth/logging.write",
    
    
          "https://www.googleapis.com/auth/monitoring",
    
    
        ]
    
    
        labels = {
    
    
          env = var.project_id
    
    
        }
    
    
        machine_type = "n1-standard-1"
    
    
        tags         = ["gke-node", "${var.project_id}-gke"]
    
    
        metadata = {
    
    
          disable-legacy-endpoints = "true"
    
    
        }
    
    
      }
    
    
    }
    

使用 Terraform 代码时,你需要一种部署方法。在当今的世界里,最好的方法是使用 CI/CD。谈到基础设施和服务的部署,CI/CD 是一种非常好且可重复的过程。

要部署代码,你可以使用任何你选择的 CI/CD 平台,但这里的代码是如何通过 GitHub Actions 部署 Terraform 代码的示例。

流水线执行以下操作:

  • 指定workflow_dispatch,这意味着代码只有在你点击Deployment按钮时才会运行

  • 使用 Ubuntu 容器运行流水线

  • 将代码(克隆)签出到 Ubuntu 容器中

  • 在 Ubuntu 容器中配置 Terraform

  • 在 Ubuntu 容器中配置 GCP SDK

  • 运行terraform init,并将其格式化、规划并应用到 GKE 代码所在的目录:


name: GKE Kubernetes Deployment
on:
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1
      - name: Set up gcloud Cloud SDK environment
        uses: google-github-actions/setup-gcloud@v0.6.0
        with:
          service_account_email:
          service_account_key:
          project_id:
      - name: Terraform Init
        working-directory: where_the_gke_code_lives
        run: terraform init
      - name: Terraform Format
        working-directory: where_the_gke_code_lives
        run: terraform fmt
      - name: Terraform Plan
        working-directory: where_the_gke_code_lives
        run: terraform plan
      - name: Terraform Apply
        working-directory: where_the_gke_code_lives
        run: terraform apply -auto-approve

使用 CI/CD 时,最合理的方式是在 Kubernetes 中使用它。你几乎不会用 CI/CD 来部署 Kubernetes 清单,而是想使用像 GitOps 这样的解决方案,因为它更高效,管理状态,监控工作负载,等等。

什么是 GitOps?

根据定义,GitOps 是一组工具,利用 Git 仓库作为可信来源,以代码的形式交付 Kubernetes 资源。它是应用开发、协作、合规和 CI/CD 的最佳操作实践,并将最佳实践应用于基础设施自动化。现在,让我们来看一个更简单的解释。这就是 Kubernetes 的配置管理,仅此而已,简单明了。配置管理就是确保所需状态与当前状态一致,而这正是 GitOps 提供给我们的。

现在你已经了解了 GitOps 的定义,让我们来讨论它到底对 Kubernetes 做了什么。首先,你有一个源代码控制库。该库包含你希望部署到 Kubernetes 上运行应用程序的 Kubernetes 清单。你还有一个 Kubernetes 集群,它可以运行在你喜欢的任何环境中。它可以是本地部署的、原始 Kubernetes 集群,甚至可以是基于云的服务,如 GKE 或 EKS。现在,你有了希望在生产环境中运行的 Kubernetes 清单,以及你希望在其上运行 Kubernetes 清单的 Kubernetes 集群,那么如何部署它们呢?传统的方式是使用类似 kubectl apply -f 的命令针对 Kubernetes 清单进行操作,但这需要手动操作,并且存在很多不足之处。相反,你可以实施 GitOps。要实施 GitOps,有几种解决方案。你决定实现一个 GitOps 解决方案,而这个 GitOps 解决方案需要访问你正在运行的 Kubernetes 集群以及源代码控制系统,例如 GitHub 或任何其他存储源代码的 Git 系统。为此,你需要在 Kubernetes 集群上安装 GitOps 解决方案,同时将 GitOps 解决方案与源代码控制系统连接,并进行一些类型的 kubectl apply -fkubectl create -f 操作。你不再需要手动执行这些命令,而是通过 CLI 或 GitOps 平台提供的其他解决方案来部署 Kubernetes 清单——而且“砰”的一声,你就完成了应用程序的部署!当然,我们都希望它能这么简单。几句话解释一下,啪嗒一下,你就能在生产环境中运行了。然而,事实并没有那么简单,这也是为什么 GitOps 需求如此高,并且它并不是那么容易实现的原因。

在撰写本文时,最流行的 GitOps 平台是ArgoCD和 Flux。

使用 GitOps 进行自动化部署

了解了创建 Kubernetes 基础设施的过程后,你现在可以使用 GitOps 部署和管理容器化应用程序了。要完成本节内容,你需要有一个正在运行的 Kubernetes 环境,因为 ArgoCD 将被部署到该集群中。

本节将提供一个更为详细的步骤指南,因为无论你在什么地方运行 Kubernetes,以下步骤都可以帮助你启动 ArgoCD。与 CI/CD 部分不同,这里没有大量不同的平台、云环境或配置代码选择,因此,以下解决方案可以在任何环境中使用。

配置 ArgoCD

  1. 首先,在你的 Kubernetes 集群中为 ArgoCD 创建一个命名空间:

    
    kubectl create namespace argocd
    
  2. 使用来自 ArgoCD 的预配置 Kubernetes 清单安装 ArgoCD,该清单提供了一个高度可用的安装:

    
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/ha/install.yaml
    

    图 6.9 – ArgoCD 创建输出

图 6.9 – ArgoCD 创建输出

  1. 获取 ArgoCD 的初始管理员密码以便登录:

    
    kubectl get secret -n argocd argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
    
  2. 通过 Kubernetes 端口转发打开 ArgoCD 的界面。这样,您可以访问 ArgoCD 的前端,而无需将负载均衡器附加到服务上:

    
    kubectl port-forward -n argocd service/argocd-server :80
    
  3. 现在您知道 UI 可用,登录到服务器通过 CLI。这样,您可以通过 CLI 使用 ArgoCD 部署容器化应用程序,创建一个可重复的过程,而不是通过 UI 执行,因为 UI 操作是手动且重复的。

图 6.10 – ArgoCD 门户

图 6.10 – ArgoCD 门户

  1. 端口是 ArgoCD 从您在上一步中运行的 kubectl port-forward 命令中托管的端口。使用以下命令登录 ArgoCD:

    
    argocd login 127.0.0.1:argocd_port_here
    

图 6.11 – 登录输出

图 6.11 – 登录输出

  1. 在 Argo CD 界面中,前往 用户信息 | 更新密码。将密码从初始的管理员密码更改为您选择的密码。

现在您已经正式部署了 ArgoCD,并且可以在终端和本地计算机上与 GitOps 平台进行交互。

部署应用程序

在本节中,您将部署一个应用程序。您将使用的应用程序是一个非常流行的示范应用,许多人用它来展示一个环境如何工作:

  1. 为您的新应用程序创建一个命名空间:

    
    kubectl create namespace sock-shop
    

Sock Shop 是一个流行的微服务示范应用,您可以在这里找到它:microservices-demo.github.io/deployment/kubernetes-start.html

  1. 在 ArgoCD 中部署 Sock Shop。要部署该应用程序,您需要执行以下操作:

    1. 创建一个新的 ArgoCD 应用程序。

    2. 指向应用程序所在的仓库。

    3. 指向目标服务器,即运行 Kubernetes 的服务器或服务。

    4. 指定目标命名空间:

    
    argocd app create socks --repo https://github.com/microservices-demo/microservices-demo.git --path deploy/kubernetes --dest-server https://kubernetes.default.svc --dest-namespace sock-shop
    
  2. 现在应用程序已经部署,您可以检查应用程序的状态:

    
    argocd app get socks
    

图 6.12 – Sock Shop 资源

图 6.12 – Sock Shop 资源

现在您可以检查应用程序是否已在 ArgoCD 界面中部署。

图 6.13 – Sock Shop 应用程序连接

图 6.13 – Sock Shop 应用程序连接

您将看到应用程序的健康状况、是否同步,以及应用程序的状态是否符合预期。

CI/CD 和 GitOps 的生产使用案例

在生产中考虑 CI/CD 和 GitOps 的两种方式如下:

  • CI/CD 应用于部署集群。

  • GitOps 应用于管理集群内部的 Kubernetes 资源。

换句话说,CI/CD 部署基础设施和集群,而 GitOps 部署并管理应用程序。使用最适合的工具进行工作,这就是基础设施部署类型的工作流程。

无论您使用哪种 GitOps 和 CI/CD 解决方案,您始终要记住,目标是自动化并创建对您和您的团队有效的可重复工作流程。无论现在是什么 热门 工具或平台,您都要使用最适合您团队的工具,而不是追随 新兴事物

在下一节中,你将深入了解在 Kubernetes 集群中运行的容器化应用程序的多种故障排除方法。

故障排除应用程序部署

故障排除环境和应用程序通常总是看起来相似,并遵循典型的顺序:

  • 上次部署是什么时候?

  • 发生了什么变化?

  • 查看日志

  • 谁可以访问这个应用,谁不能访问,如果有的话?

对于 Kubernetes,应用程序故障排除也非常相似。通常的工作流程如下:

  1. 检查容器中运行的应用程序。

  2. 检查 Pod(s) 的整体健康状态。

  3. 检查服务或路由。

通过这三个步骤,你通常可以找出发生了什么问题,因为实际上,其他问题不可能存在。要么是应用本身没有工作,要么是 Pod 本身有问题,要么是服务或路由没有按预期工作。

尽管从高层次来看,可能只有三个潜在问题,但当你深入探讨这些问题时,可能会有多种方式来排除你当前遇到的故障,你将在本节中了解这些方法。

与所有故障排除技术一样,你应该按以下顺序考虑问题:

  • 问题是什么?

  • 有什么变化吗?

  • 问题可能出在哪里?也就是说,Pod 可能出现故障,但它可能不是因为应用程序的问题。它可能是由于复制控制器的问题。

故障排除 Pods

下面是帮助你调试 Pods 的两个命令:

  • kubectl describe

  • kubectl logs

使用以下 Kubernetes Manifest 并进行部署。注意,容器标签拼写为 lates。这是故意为之,因为你希望容器失败:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:lates
        ports:
        - containerPort: 80

使用以下命令获取 Pod 的名称:


kubectl get pods

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

图 6.14 – 容器镜像拉取错误

图 6.14 – 容器镜像拉取错误

请注意,一开始你就可以开始故障排除过程。状态显示拉取镜像时出错。现在你知道问题出在镜像上,接下来我们可以更深入地探讨。

运行以下命令:


kubectl describe pods pod_name

你将看到类似以下的屏幕截图输出:

图 6.15 – Pod 描述

图 6.15 – Pod 描述

describe 命令的一个优点是,它会在 Events 部分下方显示日志输出。你现在可以看到问题出在它无法根据你提供的名称和标签拉取容器镜像。

最后一步是运行 logs 命令,查看是否有其他数据可以使用:


kubectl logs pod_name

图 6.16 – Pod 日志

图 6.16 – Pod 日志

从这里的屏幕截图可以看到,除了 describe 命令提供的信息外,几乎没有更多线索,因此故障排除已成功完成。

故障排除服务

在故障排除服务时,首先要确认的是服务是否存在。如果在 Kubernetes 集群中没有运行服务,你可以使用这个示例清单:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginxservice
spec:
  selector:
    app: nginxdeployment
  ports:
    - protocol: TCP
      port: 80
  type: LoadBalancer

由于 Pod 网络与主机网络是分开的,你需要有一个 Pod 来执行或通过 SSH 进入,以便进行故障排除。以下是你可以使用的命令,用于基于 busybox 容器镜像配置一个用于故障排除的 Pod,这是一种常用于故障排除的镜像:


kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh

首先,查看服务是否正在运行。你将在 busybox 容器镜像外部执行此操作:


kubectl get service

你应该会得到以下输出。

图 6.17 – 服务配置

图 6.17 – 服务配置

如果服务正在运行,请确认你可以通过 DNS 访问该服务:


nslookup service_name

你将看到类似于以下配置的输出:

图 6.18 – Pod 的 nslookup

图 6.18 – Pod 的 nslookup

如果标准的 nslookup 命令无效,或者你需要其他类型的确认,请尝试 FQDN:


nslookup service_name.namespace_name.svc.cluster.local

你将看到类似于以下截图的输出。

图 6.19 – FQDN 服务查找

图 6.19 – FQDN 服务查找

检查并确认服务是否正确定义:


kubectl get service name_of_service -o json

你将看到类似于以下截图的输出。

图 6.20 – 服务的 JSON 输出

图 6.20 – 服务的 JSON 输出

检查服务是否有端点——即确认服务指向的 Pods 是否存在:


kubectl get pods -l app=name_of_deployment

你将看到类似于以下截图的输出:

图 6.21 – 基于标签检索 Pods

图 6.21 – 基于标签检索 Pods

最后,虽然应该已经知道,但为了保险起见,检查并确认服务指向的 Pods 是否正常工作:


kubectl get pods

最后一部分,你将在下一节中学习到,它涉及到实现服务网格以进行故障排除。服务网格有几个功能,其中一个功能是简化服务之间延迟问题的故障排除,同时确保服务按预期运行。

故障排除部署

帮助你调试部署的主要命令类似于调试 Pods:

kubectl describe deployment

除非 Deployment 控制器本身有问题,否则通常不会出现实际的 Deployment 问题。通常是部署内的 Pods 存在问题。然而,你仍然可能需要检查 Deployment 本身。

为此,你需要运行以下命令:


kubectl describe deployment deployment_name

你应该会得到类似于以下截图的输出:

图 6.22 – 描述 nginx 部署

图 6.22 – 描述 nginx 部署

describe命令的目标不是告诉你日志、事件或发生了什么——而是帮助你完全理解已部署的内容及其部署方式。这样,你就可以追溯并查看部署的内容是否是实际应该存在的。

在下一节中,我们将通过讨论什么是服务网格,什么是 Ingress,以及如何思考实施它们来总结本章内容。

服务网格和 Ingress

几乎每个容器化应用程序都需要以某种方式进行路由——无论是让外部用户使用应用程序、应用程序之间进行通信,还是一个应用程序需要连接到另一个应用程序。在 Kubernetes 中,路由和服务至关重要,这也是服务网格和 Ingress 在其中发挥巨大作用的原因。

在许多情况下,你需要更好地可视化服务的运行状况、流量是如何路由的,以及应用程序如何路由到不同的负载均衡器和 IP 地址。你最终还需要一种加密服务间流量的方式,而 Kubernetes 默认并不提供。

服务网格和 Ingress 通常是更高级的主题,但在本书中,也许在你职业生涯的这个阶段,你已经准备好深入了解并完全理解使用这两个工具、插件和平台的优缺点。

为什么需要 Ingress?

在 Kubernetes 之旅的这一阶段,你几乎可以确定已经见过 Kubernetes 服务。事实上,你在本书中已经多次见到它们。很多时候,Kubernetes 服务会附带一个前端应用,需要为用户提供与 Kubernetes 服务交互的方式。它通常位于负载均衡器前面。

这样做的问题在于,如果你在服务前面有负载均衡器,则需要执行以下操作:

  • 如果你使用的是云 Kubernetes 服务,则需要额外支付云负载均衡器的费用

  • 如果你使用的是本地 Kubernetes 集群,则需要设置虚拟负载均衡器

  • 需要管理一堆负载均衡器

有了 Ingress 控制器,你就不必担心这个问题了。

你可以拥有多个不同的 Kubernetes 服务,并让 Ingress 控制器指向它们,且每个服务都可以通过不同的路径进行访问。

Ingress 控制器为工程师节省了时间、金钱、管理和精力。

使用 Ingress

现在你了解了 Ingress 控制器,让我们来看看如何使用(在撰写时)最流行的选项 Nginx Ingress 来配置它。

首先,你需要一个 Kubernetes 部署和服务来进行部署。如果你还没有这些,且希望保持简单,可以使用以下 Kubernetes 清单,它是一个示例 Azure 应用。它不一定要在 Azure 上运行才可以工作:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: aks-helloworld-one
spec:
  replicas: 1
  selector:
    matchLabels:
      app: aks-helloworld-one
  template:
    metadata:
      labels:
        app: aks-helloworld-one
    spec:
      containers:
      - name: aks-helloworld-one
        image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
        ports:
        - containerPort: 80
        env:
        - name: TITLE
          value: "Welcome to Azure Kubernetes Service (AKS)"
---
apiVersion: v1
kind: Service
metadata:
  name: aks-helloworld-one
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    app: aks-helloworld-one

一旦应用程序本身部署完成,你就可以部署 Ingress 控制器。Ingress 控制器是 Kubernetes 原生 API 集的一部分,因此你无需担心安装其他 CRD 或控制器:


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-world-ingress-static
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: aks-helloworld-one
            port:
              number: 80

最后的步骤是将端口转发到应用服务,以便你可以在本地访问该应用:


kubectl port-forward service/aks-helloworld-one :80

你应该能得到类似以下截图的输出:

图 6.23 – 端口转发服务通信

图 6.23 – 端口转发服务通信

你应该能够通过本地主机访问该应用。

图 6.24 – AKS 应用

图 6.24 – AKS 应用

现在你从理论和实践的角度了解了什么是 Ingress,让我们继续探讨服务网格,并看看如何使通信更安全。

为什么选择服务网格?

当你将容器化应用程序部署到 Kubernetes 集群时,有两种主要的通信方式:

  • 服务

  • Pod 到 Pod 的通信

不推荐 Pod 到 Pod 的通信,因为 Pod 是短暂的,这意味着它们不是永久性的。它们设计为随时可能停止运行,只有当它们是 StatefulSet 的一部分时,才会保留任何类型的唯一标识符。然而,Pod 仍然需要能够相互通信。后端需要与前端通信,中间件需要与后端和前端通信,等等。

下一种通信方式,也是主要的通信方式,是服务到服务。服务到服务是首选方法,因为服务不是短暂的,只有在手动删除时才会被删除。Pod 可以通过选择器或标签连接到服务。如果一个 Pod 停止运行,但部署该 Pod 的 Kubernetes 清单中的选择器没有改变,新的 Pod 将会连接到该服务。

目前为止,所有描述的内容存在一个主要问题——所有的流量都是未加密的。Pod 到 Pod 的通信,或者有些人喜欢称之为东-西流量,都是未加密的。这意味着如果因为某种原因一个 Pod 被攻破,或者你有一些隔离问题,默认情况下你无法采取任何措施。

这就是服务网格发挥作用的地方。服务网格具有以下功能:

  • 加密微服务之间的流量

  • 帮助排查网络延迟问题

  • 安全连接 Kubernetes 服务

  • 执行可观察性以进行跟踪和警报

使用服务网格

现在你了解了服务网格,让我们学习如何设置一个。市面上有很多不同的服务网格平台,它们各自有不同的安装和配置方法。

由于这是一个复杂的话题,无法在这一部分全部讲解。事实上,关于服务网格有专门的书籍。让我们学习如何设置一个 Istio 服务网格。

首先,下载 Istio:


curl -L https://istio.io/downloadIstio | sh

接下来,导出 $PATH 变量的路径:


export PATH=$PWD/bin:$PATH

输出 $PATH 变量到 bashrc


echo "export PATH=$PATH:$HOME/istio-1.15.0/bin" >> ~/.bashrc

在你的 Kubernetes 集群上安装 Istio。注意,Ingress 设置为 false。如果你想使用 Istio Ingress,可以将其设置为 true。如果你使用的是其他 Ingress 控制器,如 Nginx Ingress,可以将其保持为 false


istioctl install --set values.gateways.istio-ingressgateway.enabled=false

Istio 是一个优秀的服务网格,但开箱即用时没有 UI。查看服务网格图形化界面的一种流行方式是使用 Kiali,这是一种简单的安装方式:


kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.15/samples/addons/kiali.yaml

设置端口转发到 Kiali,以便你可以在本地访问 UI:


kubectl port-forward -n istio-system service/kiali :20001

最后一步是获取一个 Kubernetes 清单,就像你在本章中使用的那样,并将 sidecar(服务网格容器)注入到你的 Kubernetes 部署中:


istioctl kube-inject -f nginx.yaml | kubectl apply -f –

到此为止,你已经拥有了理论基础,并且掌握了一些实践知识,了解如何在你的服务网格之旅中向前迈进。

总结

总体而言,容器化应用程序的部署、故障排除以及第三方工具将成为你 Kubernetes 集群的核心组成部分。如果没有适当的故障排除,你将无法成功部署。如果没有像 Ingress 控制器这样的第三方工具,你将无法正确管理前端应用程序。Kubernetes 开箱即用,提供了大量工具来使事情顺利运行。然而,你还需要采取更多步骤。无论是好是坏,Kubernetes 不是那种你可以轻松部署后就不管的个平台。它需要管理和工程技能来确保其按预期工作。

在下一章中,你将学习如何监控你在本书中部署的工作负载。

进一步阅读

第三部分:Kubernetes 最后的 15 个概念——安全与监控

到目前为止,你已经学习了 Kubernetes 中的一些关键组成部分。首先,你了解了不同的集群方法。你学会了如何部署本地环境,如何部署各种托管的 Kubernetes 服务,以及部署时可用的不同选项。

之后,你将学习不同的部署方式,既包括 基础 的视角,也包括 进阶 的视角。

唯一缺失的部分, arguably 是构建一个合适的 Kubernetes 环境中最重要的环节,就是监控、可观察性和安全性。本书的第三个也是最后一个 50 个概念的部分将讨论今天的世界中监控与可观察性的具体样貌。

在接下来的两章中,你将全面了解监控、可观察性和安全性。你将深入了解监控和可观察性的理论,并通过实践部分,帮助你真正理解如何实现不同的工具。接下来,你将深入研究安全性,这通常是被忽视的领域,尤其是在 Kubernetes 环境下。

通过这些内容,你将对监控、可观察性和安全性有一个扎实的理论概览,并且通过真实的代码示例,学习如何实现不同的工具和平台。

本书的这一部分包括以下章节:

  • 第七章Kubernetes 监控与可观察性

  • 第八章安全性现实检查

第七章:Kubernetes 监控与可观察性

对于运维(Ops)和开发(Dev)团队来说,监控和可观察性一直是至关重要的。运维团队以前主要关注基础设施的健康(虚拟机、裸机、网络、存储等),而开发团队则专注于应用程序的健康。随着 Kubernetes 的出现,这些界限变得模糊。在传统的数据中心环境中,监控和可观察性的责任通常很容易划分。但是 Kubernetes 混合了这些界限,因为例如,Pod 在某种意义上是基础设施的一部分,因为它们需要扩展,并且在传统意义上它们有点像虚拟机。它们是容纳应用程序的地方。然而,应用程序运行在 Pod 中,因此如果你在监控 Pod,你实际上也在监控 Pod 内运行的容器。

由于这些界限已经模糊,两个团队都在进行监控过程的各个部分。在平台工程或 DevOps 工程团队中,这些团队将同时监控应用程序和基础设施的各个部分。

不再有明确的界限来划分哪个团队负责监控并为 Kubernetes 的特定部分创建可观察性实践。相反,现在的目标是拥有一个更加统一的前线,确保整个环境和应用程序按预期工作。

本章将从理论和实践两方面深入探讨 Kubernetes 中的监控和可观察性。目标是让你能够将你在本章中学到的内容和在实验室中实现的内容,真正应用到生产环境中。首先,你将了解监控和可观察性到底是什么。接下来,你将了解监控和可观察性在基础设施层的意义,即运行 Kubernetes 环境的虚拟机,以及关于控制平面和工作节点监控的具体内容。之后,你将深入了解 Kubernetes 资源的监控和可观察性,例如 Pods 和 Services。最后,你将了解当今常用的监控和可观察性工具和平台。

如果没有监控,工程师将无法知道系统或应用程序内部发生了什么。DevOps 和平台工程师的工作就是获取这些信息,并利用这些信息修复任何出现的问题。

本章将涵盖以下主要内容:

  • 监控与可观察性的区别

  • Kubernetes 的监控和可观察性工具

  • 可观察性实践

  • Kubernetes 资源监控

技术要求

本章不会对监控进行详细的解释。虽然会有一些简短的说明作为回顾或起点,但你需要对监控和可观察性有一定的经验。例如,或许你之前使用过 Kubernetes Dashboard,或者你曾查看过 Azure 或 AWS 中预设的监控。甚至可能有过在本地桌面进行监控的经验。

你可以在这里找到 GitHub 仓库:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch7/prometheus/helm

监控和可观察性有什么不同?

监控和可观察性是两个最常被混淆的工作流,它们在语言和解释上最为接近。虽然本章并不专门讨论可观察性,但要真正理解监控与可观察性之间的差异,你必须了解这两者,并最终看到它们是如何工作的。在本节的解释后,你会发现监控和可观察性之间有关键的区别,以及它们在使用方式、使用时机和最佳实践上的不同。

在一些组织中,你可能会遇到监控和可观察性被混为一类的情况,这取决于其工程团队的成熟度。它们要么被看作相同的事物,要么工程团队认为自己在做可观察性,其实他们只是在做监控。本章的目标之一是帮助你区分这两者,因为根据你使用的平台和工具,可能会有一些界限模糊的情况。例如,我们来看两个最流行的平台——Datadog 和 New Relic。这两个平台都被视为监控平台和可观察性平台。它们都能进行监控和可观察性,而且做得很好。但并非总是如此。例如,像 Prometheus 这样的平台仅用于可观察性和收集指标,但你可以将其与监控平台/工具(如 Grafana)配合使用,提供环境内部发生的情况的可视化。

监控和可观察性都是相当复杂的话题,尤其是在 Kubernetes 中。Kubernetes 中的监控和可观察性与其他平台和系统的概念类似,但也有很大的不同。

在接下来的章节中,你将了解监控和可观察性的定义,以及如何判断应该使用哪一个。我们还将探索一些监控与可观察性的例子。

什么是监控?

你是否曾经在 Windows 中打开过任务管理器,进入性能设置,查看内存和/或 CPU 使用情况?或者在 macOS 上使用过活动监视器,查看哪些应用程序和程序正在使用内存和 CPU?如果你做过这些事情,可以合理推测大多数工程师都在某个时刻做过,你就已经在进行系统监控了!现在,你可能会想,检查台式机或笔记本电脑的内存和 CPU 使用情况与在服务器上监控是截然不同的,但实际上并没有那么大差异。无论是台式机还是整个服务器机架,RAM 就是 RAM,CPU 就是 CPU,存储就是存储。这在不同系统之间是不会改变的。唯一的区别是 CPU、内存和存储的数量。

那么,什么是监控?

监控是实时查看系统资源、性能和使用情况的能力。你可以监控 Kubernetes 集群中的任何内容,包括以下内容:

  • 工作节点

  • 控制平面

  • Pod

  • 部署

  • 配置映射

除了这些,你还可以监控集群中运行的任何其他 Kubernetes 资源。从应用层到基础设施层,再到网络层,都可以进行监控。

通过监控,你还可以创建警报。我记得当我刚进入技术行业,拿到我的第一个实习时,最酷的事情就是走进网络运维中心NOC),看到大屏幕上显示着各种监控画面。那感觉就像我们在保护核发射代码一样。看到每个系统都可以被监控,工程师们能够理解系统内部发生了什么,真是太棒了。

在今天的世界里,工程师们仍然使用网络运维中心的大屏幕进行监控,但随着在家工作和远程办公成为新常态,工程师们也可以登录到监控平台,查看系统的运行情况。工程师可以登录到像 Datadog、CloudWatch 或 Azure Monitor 这样的工具,查看每个服务的运行情况。

让我们看看图 7.1中来自 Azure 的截图。如你所见,有许多可用的监控选项。

图 7.1 – AKS 监控选项

图 7.1 – AKS 监控选项

监控部分中看到的监控选项也包含了一些可观察性实践(例如指标),这与本章前面提到的一个观点相呼应——在分离监控和可观察性实践时,确实存在一些混淆。

从监控的角度来看,你应该关注的是实际的监控工具。

图 7.2 – AKS 监控

图 7.2 – AKS 监控

你可以从 AKS 或几乎任何其他 Azure 服务中提取的监控信息,使你能够查看当前发生的情况或过去一段时间发生的情况。这使你能够从临时的角度理解系统的性能。

图 7.3 – 硬件指标

](https://github.com/OpenDocCN/freelearn-devops-pt3-zh/raw/master/docs/50-k8s-ccp/img/B19116_07_03.jpg)

图 7.3 – 硬件指标

这种监控的目的是查看和理解集群资源(如 CPU、内存、存储和带宽(进出流量))的表现,以确保你能够做出关于如何管理集群的决策。

你还可以监控正在运行的应用程序,查看它们的运行时间、消耗的资源量以及应用程序的整体性能。

监控 Kubernetes 集群的具体情况

你应该监控的控制平面组件包括 API 服务器、etcd(集群存储)、控制器和调度器。你应该监控的工作节点组件包括 Kubelet、容器运行时、kube-proxy 和 DNS。还有需要监控 Pods,但你将在本章末尾进一步学习这部分内容。

在任何情况下,无论是控制平面的组件还是工作节点上的组件,你都应该确保 Metrics Server 正在运行。你可以通过 /metrics/resource 端点(例如:/metrics/pods)来技术性地获取指标,但那意味着你必须查询每个资源。Metrics Server 会访问每个资源,获取指标并将其暴露出来,而不需要你一个一个地获取它们。你可以在这里找到可以在任何 Kubernetes 集群中使用的 Metrics Server:github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Metrics Server 端点来源于 Kubernetes 特定兴趣小组 (SIG),可以部署在任何地方。无论是运行在 AWS 上的 Kubernetes 集群,还是运行在你 Windows 10 笔记本上的 Kubeadm 集群,集群存在的地方并不重要。

监控的缺点是什么?

监控的缺点,尽管它非常强大,是除非实时发生,否则你对数据的处理能力有限。当然,如果资源出现问题,你可以收到警报,但这意味着工程师必须处于待命状态来解决问题。他们必须停下手头的工作来处理突发事件。随着科技世界的发展,这种模式已经不再可持续。

与此相伴,工程师希望能够花更多时间创造有价值的工作。他们不希望在凌晨 2 点因收到警报而醒来,也不希望因为警报而停止编写新功能。相反,他们希望能够为警报创建自动化和可重复的处理流程。例如,如果警报触发,工程师希望能够创建一个自动化的流程,在出现问题时能及时修复。这样,他们就不必停止手头的工作去处理突发问题,而可以继续专注于创造有价值的工作。

这就是可观察性发挥作用的地方。

什么是可观察性?

由于在解释监控和可观察性时,有时这两个词是可以互换使用的,因此了解它们之间的区别非常重要。这样,当你深入了解监控时,就能更容易理解这两者的差异。

可观察性主要出现在 Kubernetes 和几乎所有其他云原生系统中。然而,监控和可观察性在含义上开始有所交融。例如,在图 7.1中,你看到了监控部分。在监控部分下,有一个指标子部分。问题是,指标从技术上来说属于可观察性范畴。

监控和可观察性之所以开始混为一谈,换句话说,可观察性之所以越来越受欢迎,是因为通过可观察性,你可以基于接收到的数据做出决策并自动化工作负载。

可观察性实践的关键数据点包括日志、指标和追踪。

再次强调,这一部分我们不想讲得过于深入,因为可观察性有一整章内容。只需记住三点:

  • 可观察性使你能够根据接收到的数据采取实际行动。这个行动可能是自动修复导致问题的资源。

  • 它正变得越来越流行,超越了传统的监控方式。

  • 可观察性有三个关键方面:日志、指标和追踪。

关于指标的简短说明

大多数 Kubernetes 资源的指标是公开的。它们通过 /metrics/resource 端点进行公开。例如,/metrics/pods 就是 Kubernetes 中 Pods 资源的指标。

为了简化操作,Metrics Server(它不会默认安装在 Kubernetes 中,具体取决于云服务提供商,但默认指的是一个原始的 Kubernetes 集群安装)可以抓取并整合 Kubernetes 资源的所有指标端点。这样,你就不必一一尝试通过每个资源去获取指标。

为了更进一步提升功能,可以使用 kube-state-metrics 工具,它可以安装在 Kubernetes 服务器上;它的主要任务是专注于集群中 Kubernetes 资源/对象的健康状况。例如,kube-state-metrics 会检查并确认 Pods 是否实际上可用且准备就绪。

如果你在想 Metrics Server 和 kube-state-metrics 之间的区别,Metrics Server 显示的是集群资源使用情况,例如 CPU 和内存。另一方面,kube-state-metrics 关注的是 Kubernetes 资源的健康状态。

监控与可观测性示例

当考虑如何实现监控、可观测性,或两者时,最好从场景的角度思考实现细节。

让我们举两个场景 – 一个从监控角度看待容器化应用,另一个则是从可观测性角度看待相同的容器化应用。

以下示例并不是一个完整的逐步指南。代码是可用的,但不会详细解释如何具体部署和运行它。你可以在自己的系统中试试看,但本章的目标是展示工作流的示例,而不是一个完整的逐步教程。

监控使用案例

第一个场景可以看作是一个前端应用。例如,它可以是一个简单的 Nginx Web 应用,用于托管网站。它可以是如下的简单 Nginx 配置:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80

使用前面的 Kubernetes 清单,你可以想象一个正在 Kubernetes 集群中运行并有两个副本的应用。要获取 Pod 的内存和 CPU 信息,可以运行 kubectl top 命令:


kubectl top pod pod_name

图 7.4 – top 命令

图 7.4 – top 命令

如果没有启用 Metrics API,可能会出现错误,因为它默认是禁用的。如果你想启用它,请查看你所运行的 Kubernetes 集群的文档。例如,这里是如何在 minikube 上启用 Metrics API 的:


minikube addons enable metrics-server

为了进行压力测试负载,你可以使用压力/性能测试工具,如 k6。以下是你可以使用的示例配置:


import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
  http.get('https://test.k6.io');
  sleep(1);
}

然后,你可以保存前面的配置,并使用以下命令作为压力测试,指定 100 个虚拟用户并运行 30 秒:


k6 run --vus 100 --duration 30s test.js

图 7.5 – 基准测试

图 7.5 – 基准测试

再次运行 kubectl top 命令,你可以看到内存增加了:

图 7.6 – Pod 的 kubectl top 命令

图 7.6 – Pod 的 kubectl top 命令

登录到一款监控软件后,例如 Kubernetes Dashboard(你将在接下来的章节中学习到),你将能够看到两个 Pods 的 CPU 和内存使用情况。

图 7.7 – 正在运行的 Pods

图 7.7 – 正在运行的 Pods

这些信息让你能够监控当越来越多的用户访问你的应用时发生的情况,这对于前端应用来说是很常见的。

可观测性使用案例

第二个场景将围绕查看前一部分中 Nginx 配置创建的 Nginx Pods 和服务。最终,你将能够看到如何在一个可观察性工具中捕获和查看指标数据。尽管图 7.8 显示了 Prometheus,但无论你使用哪种可观察性工具,你仍然会看到相同的数据,因为它是通过 Kubernetes 指标 API 获取的。

当在 Kubernetes 集群上启用 Metrics Server 时,它会暴露几个资源指标端点。其中一个资源指标端点是 Pods。你可以根据服务发现来确认你的 Pod 指标是否已被导入到 Prometheus 中。

图 7.8 – 一个 Pod 的发现

图 7.8 – 一个 Pod 的发现

然后,你可以根据 Prometheus 允许你检查的不同查询来确认 Pods 的运行情况。例如,以下截图显示了 Kubernetes 服务的资源信息,你可以看到 Nginx 服务正在运行。

图 7.9 – Kubernetes 服务指标

图 7.9 – Kubernetes 服务指标

你还可以深入挖掘,并根据特定的硬件资源(如内存和 CPU)进行查询。这样,你就能了解每个 Pod 占用了多少资源(内存、CPU 等)。

例如,以下片段是一个查询,用于查看内存使用情况:


avg((avg (container_memory_working_set_bytes{pod="nginx-deployment-588c8d7b4b-6dm7m"}) by (container_name , pod ))/ on (container_name , pod)(avg (container_spec_memory_limit_bytes>0 ) by (container_name, pod))*100)

注意如何指定 Pod 名称;这将展示与指定 Pod 的内存相关的可观察性指标。

Kubernetes 的监控和可观察性工具

通常,在任何技术书籍中,理论/实践知识首先出现,然后才是工具。然而,监控和可观察性稍有不同,因为如果不提及或展示某些工具/平台,实际上你无法讨论具体的内容。因此,在深入探讨如何监控和实现可观察性之前,你需要先了解一些关键工具。

本节的目标是帮助你首先了解这些工具的样子,然后将你学到的理论应用到这些工具中。当你将工具的知识和视觉界面(UI)与对真正的监控和可观察性的理解相结合时,你就能在你的环境中成功地实现它们。

监控的一大有趣之处在于,你可以从理论角度完全理解它,但实际实现起来可能会是一个挑战。例如,你可以理解 Kubernetes 中的指标端点是什么,它是如何工作的,暴露了哪些指标,以及你可以从这些指标中监控哪些资源。然而,实际上设置一个平台来监听这些指标并配置这个监听器,与阅读有关指标如何工作的内容截然不同。

虽然本节不会涵盖所有用于监控 Kubernetes 的工具和平台,但这个列表是一个很好的起点,因为它们在组织中是最广泛使用的。好消息是,即使你遇到的监控工具本节没有介绍,监控就是监控。这意味着,一旦你理解了监控及其如何与 Kubernetes 配合工作,学习其他监控工具也就不成问题了。归根结底,它们的原理是一样的。监控的基本组件不会改变,唯一的不同是仪表板的显示方式。

在本节中,你将学习以下内容:

  • 内置的 Kubernetes Dashboard

  • 云特定的监控和可观察性工具

  • Grafana/Prometheus

  • 如何使用和设置监控工具

Kubernetes Dashboard

Kubernetes Dashboard 是一种原生的监控和可观察性工具。虽然它并未开箱即用,但几乎可以在任何环境中轻松进行配置。它是查看 Kubernetes 集群内部发生的事情的最快方式。

重要提示

我们使用minikube是因为它简单明了。如果你决定在其他 Kubernetes 集群上使用 Kubernetes Dashboard,仪表板的视觉效果不会有什么不同。唯一的区别是你所看到的 Kubernetes 资源。

首先,启动minikube。如果你还没有安装minikube,可以在这里安装:minikube.sigs.k8s.io/docs/start/


minikube start

图 7.10 – 启动 minikube

图 7.10 – 启动 minikube

接下来,运行以下命令以启动仪表板:


minikube dashboard –url

图 7.11 – 默认的 Kubernetes Dashboard

图 7.11 – 默认的 Kubernetes Dashboard

此时,你可以看到有关minikube集群的多个不同信息,从 Pod 信息到其他 Kubernetes 资源。你可以看到正在运行且健康的 Pods,以及可能需要修复的工作负载。

图 7.12 – 一个部署示例

图 7.12 – 一个部署示例

接下来,你可以看到整体部署状态。

图 7.13 – Pod 状态

图 7.13 – Pod 状态

之后,你可以深入查看部署选项卡中正在运行的 Pods。

图 7.14 – 运行中的 Pods

图 7.14 – 运行中的 Pods

需要指出的一点是,Kubernetes Dashboard 几乎从未用于生产级别的场景。它通常用于快速查看一些信息。如果需要进行真实的可观察性和警报设置,通常会使用更合适的(生产就绪型)监控和可观察性工具,接下来你将看到这些工具。

Azure Monitor

如果您严格使用 Azure 工作负载,或甚至在 Azure 外部的工作负载,并且您正在使用 Azure Arc(例如本地部署),Azure Monitor 是一个非常好的内建解决方案。您可以捕获日志和指标,创建警报,并实时查看您的环境中发生的情况。例如,您可以查看集群的 CPU 和内存使用情况,以及 Pod 和其他 Kubernetes 资源的数据。

第二章中,您学习了如何使用 Terraform 创建 AKS 集群。您可以使用相同的代码来完成本节内容。为了更快参考,以下是链接:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch2/AKS

一旦您的 AKS 集群配置完成,登录 Azure 门户并进入Kubernetes 服务。然后,您应该可以在监控下看到一个洞察选项卡。

通过点击蓝色的配置 Azure 监视按钮启用洞察。

图 7.15 – Azure 洞察

图 7.15 – Azure 洞察

Azure 洞察使您能够从整个环境到节点,再到 Pod 和容器,全面监控您的 AKS 集群中的一切。

图 7.16 – 洞察数据

图 7.16 – 洞察数据

例如,通过深入容器(Pod),您可以查看状态、利用率和正常运行时间。

图 7.17 – 容器数据

图 7.17 – 容器数据

节点中,您可以看到每个工作节点上运行的特定 Pod,包括 Pod 的健康状况。

图 7.18 – 节点数据

图 7.18 – 节点数据

Azure Monitor 和洞察是 Kubernetes 工作负载的一个很好的整体解决方案。如果您在 Azure 生态系统中,我不建议考虑其他解决方案。坚持使用本地的。

AWS 容器洞察

容器洞察是 AWS CloudWatch 家族的一部分,使您能够查看容器化工作负载的性能和监控相关操作。您可以基于容器洞察创建警报,并提取日志和指标,以便从自动化和可重复的角度对可能发生的任何情况采取行动。

第二章中,您学习了如何使用 Terraform 创建 EKS 集群。您可以使用相同的代码来完成本节内容。为了更快参考,以下是链接:github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch2/AWS

在运行 EKS Terraform 配置后,运行以下命令从 EKS 集群中获取 Kubernetes 配置(kubeconfig):


aws eks update-kubeconfig –region region_where_cluster_exists –name name_of_your_cluster

要确认当前上下文已设置,运行以下命令,你应该会看到类似的输出:


kubectl get nodes
NAME                             STATUS   ROLES    AGE    VERSION
ip-192-168-16-238.ec2.internal   Ready    <none>   18m    v1.23.9-eks-ba74326

接下来,为你的集群配置 AWS 容器洞察:


ClusterName= name_of_your_cluster
RegionName= region_where_cluster_exists
FluentBitHttpPort='2020'
FluentBitReadFromHead='Off'
[[ ${FluentBitReadFromHead} = 'On' ]] && FluentBitReadFromTail='Off'|| FluentBitReadFromTail='On'
[[ -z ${FluentBitHttpPort} ]] && FluentBitHttpServer='Off' || FluentBitHttpServer='On'
curl https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/k8s-deployment-manifest-templates/deployment-mode/daemonset/container-insights-monitoring/quickstart/cwagent-fluent-bit-quickstart.yaml | sed 's/{{cluster_name}}/'${ClusterName}'/;s/{{region_name}}/'${RegionName}'/;s/{{http_server_toggle}}/"'${FluentBitHttpServer}'"/;s/{{http_server_port}}/"'${FluentBitHttpPort}'"/;s/{{read_from_head}}/"'${FluentBitReadFromHead}'"/;s/{{read_from_tail}}/"'${FluentBitReadFromTail}'"/' | kubectl apply -f –

在前面的代码运行后,你将看到类似于终端输出的结果,如下所示:


namespace/amazon-cloudwatch created
serviceaccount/cloudwatch-agent created
clusterrole.rbac.authorization.k8s.io/cloudwatch-agent-role created
clusterrolebinding.rbac.authorization.k8s.io/cloudwatch-agent-role-binding created
configmap/cwagentconfig created
daemonset.apps/cloudwatch-agent created
configmap/fluent-bit-cluster-info created
serviceaccount/fluent-bit created
clusterrole.rbac.authorization.k8s.io/fluent-bit-role created
clusterrolebinding.rbac.authorization.k8s.io/fluent-bit-role-binding created
configmap/fluent-bit-config created
daemonset.apps/fluent-bit created

此时,如果你登录到 AWS 并进入 CloudWatch | 容器洞察,你将看到容器洞察已正确配置。

图 7.19 – 容器洞察输出

图 7.19 – 容器洞察输出

接下来,我们将深入了解 Kubernetes 领域中非常流行的技术栈——Grafana 和 Prometheus。

Grafana/Prometheus

可以说,Grafana 和 Prometheus 是 Kubernetes 监控/可观测性场景中最流行的实现。Grafana 和 Prometheus 也可以在 Kubernetes 以外的环境中工作,但它们在 Kubernetes 生态系统中变得非常流行。事实上,Kubernetes 甚至有一个 Prometheus 操作员。

除了标准的监控和可观测性好处外,工程师们真的很喜欢这个组合,因为它是 100% 开源的。例如,在 Grafana 中,你可以使用一点代码创建任何你想要的仪表板,而且这一切都是免费的。Grafana 和 Prometheus 也可以在任何地方运行。这个栈可以在你的 Kubernetes 集群内部运行,或者完全在独立的服务器上运行。

尽管你可以单独配置 Prometheus 和 Grafana,添加所有的功能,但我们将利用 Prometheus 社区 Helm 图表 的强大功能。原因是它从自动化和可重复的角度简化了 Prometheus 和 Grafana 的安装。它会安装 Prometheus 和 Grafana,并为我们设置仪表板。

在开始之前,有一件事无论你使用哪个监控和可观测性平台,你都必须确保你正在以期望的方式收集指标。例如,Kubernetes 的指标服务器或类似的适配器。例如,Prometheus 有一个适配器可以代替指标服务器。你也可以通过使用 /metrics/resource(例如,/metrics/pods)的指标端点直接访问源数据,但通常工程师选择使用指标服务器。

图 7.20 – 指标 Pods

图 7.20 – 指标 Pods

如果你没有暴露指标端点,Kubernetes 将不会允许系统使用这些指标。在启用指标服务器方面,这取决于你运行 Kubernetes 的环境。例如,在 AKS 中,它会自动为你暴露。如果你在 kube-system 命名空间中看不到 Kubernetes 集群的指标 Pods(这取决于你部署 Kubernetes 的环境),请查看该类型 Kubernetes 环境的文档,了解如何启用指标端点。

首先,为 prometheus-community 添加 helm repo


helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

接下来,确保你的仓库是最新的:


helm repo update

在最后一步,安装 Helm chart 到 monitoring 命名空间:


helm install prometheus prometheus-community/kube-prometheus-stack –namespace monitoring –create-namespace

安装完成后,你应该可以看到在 monitoring 命名空间中创建的几个 Kubernetes 资源。要访问 Grafana,你可以使用端口转发:


kubectl -namespace monitoring port-forward svc/prometheus-grafana :80

Grafana 的默认用户名/密码是 admin/prom-operator

登录 Grafana 后,查看 kube-system 命名空间中的 Pods 仪表板。你可以看到,来自所有命名空间的指标正被 Prometheus 获取并推送到 Grafana。

要查看指标,进入 仪表板 | 浏览

图 7.21 – 浏览仪表板

图 7.21 – 浏览仪表板

点击 Kubernetes / 计算资源 / 命名空间(****Pods) 选项:

图 7.22 – Pods 仪表板

图 7.22 – Pods 仪表板

将命名空间更改为已有 Pod 的命名空间,例如 kube-system,你可以在以下截图中看到 Pod 的指标:

图 7.23 – 命名空间选择

图 7.23 – 命名空间选择

Prometheus/Grafana 是一个强大的组合,允许你保持供应商中立,并作为开源选项获取所需的一切。

可观察性实践

现在,让我们通过查看日志、跟踪和指标来定义可观察性到底是什么。当你使用像 Prometheus 这样的工具时,你是在做可观察性的一部分。当你使用其他工具,例如 Logz.io 或其他日志聚合器时,你也在使用可观察性的另一部分。

日志记录

日志记录是聚合和存储由程序和系统写入的事件日志消息。正如你所想象的那样,根据应用程序中日志的详细程度,可能会有大量的事件。系统管理员最喜欢的工具就是日志,因为它从事件的角度几乎能显示任何可能发生的事情。然而,单纯通过眼睛查看所有日志并不高效。相反,通过使用可观察性实践,你可以将日志发送到日志聚合器,并确保某个特定类型的日志触发警报或某种自动化,进而修复问题。

图 7.24 – 日志服务发现

图 7.24 – 日志服务发现

在容器日志记录方面,有一些日志记录实践:

  • 应用程序转发:通过应用程序直接发送日志。例如,假设你在应用程序中使用了 Prometheus 库来收集日志、指标和跟踪,并将其发送到你使用的后端日志平台。

  • Sidecar:使用 sidecar 容器管理应用程序的日志。例如,你可以将一些日志系统容器化,以便作为辅助/sidecar 容器在 Pod 内部运行。sidecar 容器的任务是做一件事:获取并发送关于 Pod 上发生的事情的日志。

  • 节点代理转发:在每个工作节点上运行一个 Pod,将所有容器日志转发到后端。

指标

度量数据用于收集时间序列数据,这些数据可以用来预测预期范围和预测值,展示在仪表板上(如 Grafana 或其他以 UI 为中心的仪表板),并进行告警。度量端点会提供一堆可以操作的信息。从纯 Kubernetes 的角度来看,度量端点收集每个工作节点上运行的 kubelet 的 Kubernetes 资源数据,并通过度量 API 将其暴露给 API 服务器。

如本章所述,有一个作为 Pod 运行的度量端点。根据你运行的 Kubernetes 集群类型,Pod 可能默认启用,或者你可能需要手动开启。

例如,在 AKS 集群中,度量 Pod 正在运行,这意味着所有 Kubernetes 资源都有可以消费的度量端点。

图 7.25 – 度量 Pod

图 7.25 – 度量 Pod

对于另一种类型的 Kubernetes 集群,例如运行在 Kubeadm 上的集群,你需要通过部署 Pod 来启用度量端点。你可以通过部署 GitHub 上 kubernetes-sigs 仓库中的 Kubernetes 清单来做到这一点:


kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

然而,这还不够。因为像 Kubeadm 这样的集群配置有节点 IP 地址,这些 IP 地址不在集群证书的 SAN(Subject Alternative Name)中,度量端点会因 TLS 连接错误而失败。

为了绕过这个问题,你需要在一些配置中添加以下行:


serverTLSBootstrap: true

你需要将它添加到两个地方。

首先,Kubeadm 配置。你可以通过运行 kubectl edit cm -n kube-system kubeadm-config 来编辑它,然后添加 serverTLSBootstrap: true 行。

图 7.26 – kubeadm 配置

图 7.26 – kubeadm 配置

接下来,你需要更新每个节点(所有控制平面和工作节点)上的 kubelet,使用相同的命令行。要编辑每个节点上的 kubelet,你可以运行以下命令并添加配置:


sudo vim /var/lib/kubelet/config.yaml

图 7.27 – kubelet 配置

图 7.27 – kubelet 配置

跟踪

跟踪数据主要是告诉你从端到端的角度,应用程序的健康状况。

你可以把一个跟踪看作是请求在系统中的路径旅程。例如,当你访问 www.google.com 时,尽管这一过程发生得非常快,但背后有大量的工作在进行。总体上,你创建的 GET 请求会经过前端,可能经过一些中间件,再到后台。当你搜索诸如 夏季十大旅游地点 时,后台数据库会通过多个请求获取相关信息。

从你执行 Google 搜索请求到信息显示给你的整个过程——这就是一个跟踪(trace)。

因为这是一个漫长的旅程,尽管对我们人类来说只有几秒钟,但它可以从工程角度为我们提供大量关于应用程序性能的信息。我们可以基于可重复的方法论来处理这些性能问题,而不是手动修复问题,或从故障排除的角度进行处理。如果你正在查看一个跟踪记录,并意识到旅程在到达后端时停止或遇到阻碍,你现在就知道该从哪里开始故障排除。

监控 Kubernetes 资源

在上一节中,你了解了从整体可观测性角度进行监控,特别是如何设置某些工具并确保它们对你有效。现在是时候深入 Kubernetes 的内部,开始思考从资源角度可以监控什么了。记住,Kubernetes 资源(有时称为对象)可以是任何东西,从服务到入口控制器,再到 Pods。因此,有很多东西需要监控。

从这个角度思考。你正在运行一个 Pod,而该 Pod 内部运行着一个容器。Pod 本身运行得很好。容器镜像正常工作,没有 CPU 或内存问题,所有事件状态显示 Pod 正在成功运行。然而,存在一个问题——容器内部运行的二进制文件(即应用程序入口点)可能已停止,或者没有按预期工作。因此,你需要一种方法来真正地深入了解 Pod 本身的内部状态!

正如你在本书中学到的,无论你在哪里运行 Kubernetes,都不重要。它如何运行的核心组件以及你如何与它交互是相同的。监控也不例外。因此,本章的这一部分将展示在 AKS 中的监控。然而,正如你很快会看到的,无论这些 Pods 是否在 AKS 中运行,都没有区别。即使你使用不同的监控系统,这些 Pods 的监控方式也是一样的。

本节中的代码,以及正在部署的演示应用程序,可以在任何 Kubernetes 集群上使用。

监控 Pods

在 Pod 内部可以是一个或多个容器。无论是一个容器还是多个容器,实际运行应用程序的是这些容器。也许它是一个核心应用程序,一个日志软件,甚至是像 HashiCorp Vault 或服务网格代理这样的东西。这些与主应用程序并列的容器被称为边车容器。由于一个 Pod 内可能运行多个容器,因此你必须确保每个容器都按预期正常运行。否则,Pod 本身可能运行正常,主应用程序也许也在正常运行,但完整的工作负载(如边车容器)可能没有正常运行。

首先,确保 HashiCorp Consul Helm 图表存在:


helm repo add hashicorp https://helm.releases.hashicorp.com

接下来,创建一个名为 consul 的新命名空间:


kubectl create namespace consul

一旦consul命名空间存在,将 Consul 部署到 consul 命名空间中的 Kubernetes 内:


helm upgrade –install -n consul consul hashicorp/consul –wait -f - <<EOF
global:
  name: consul
server:
  replicas: 1
  bootstrapExpect: 1
connectInject:
  enabled: true
EOF

最后一步是部署演示应用,并确保存在用于注入 consul 作为 sidecar 的注解:


Curl -sL https://run.linkerd.io/emojivoto.yml \
  | sed 's|    metadata: |    metadata:\n      annotations:\n         consul.hashicorp.com/connect-inject" "tr"e'|' \
  | se' 's|targetPort: 8080|targetPort: 2000'|' \
  | kubectl apply -f -

部署应用后,你应该能看到类似以下截图的输出:

图 7.28 – Linkerd 部署

图 7.28 – Linkerd 部署

登录到 Azure 门户,进入你的 AKS 集群,并开启 Azure Insights(如果它尚未开启):

图 7.29 – 启用容器 Insights

图 7.29 – 启用容器 Insights

一旦Insights启用,你应该能够看到几个可用的资源。点击Controllers按钮。

图 7.30 – Controllers 仪表盘

图 7.30 – Controllers 仪表盘

查看 Controllers 仪表盘,你可以看到所有正在运行的 Kubernetes 资源,及其状态、正常运行时间和每个资源中存在的容器数量。

图 7.31 – Kubernetes 资源正在运行

图 7.31 – Kubernetes 资源正在运行

更深入地查看,你可以看到对于每个有多个 Pod 的资源,你可以查看不同的容器。

图 7.32 – Pods 中的资源

图 7.32 – Pods 中的资源

但和往常一样,事情可能会出错。你可以在以下截图中看到,Kubernetes 资源正在运行,但某些容器并未按预期运行:

图 7.33 – 警告资源

图 7.33 – 警告资源

当你深入挖掘时,你会看到容器的状态是等待创建。

图 7.34 – 警告说明

图 7.34 – 警告说明

因此,即使 Pod 可能已启动并运行,如容器中的应用程序运行,其他 sidecar 容器可能没有启动。从外部看,应用已经运行,所以看起来一切正常。然而,仔细查看后,你会发现其实并不是。 这就是监控服务器上运行的应用与 Pod 之间的巨大区别。在一个 Pod 内,可能有多个二进制文件需要关注。

总结

本章内容涵盖了很多,约 35 页,实际上,这些话题本身可能会需要两三本书来详细讲解。由于这个原因,并不是所有内容都被覆盖到最可能需要的深度。然而,好消息是,你现在已经对如何开始在生产环境中实现这些平台、技术和方法有了坚实的理解。

本章我们讨论了很多话题,涵盖了监控是什么,观察性是什么,以及它们之间的总体区别。接着,你深入了解了使监控和观察性在 Kubernetes 环境中生效的具体工具和平台。

在下一章,也是最后一章,你将学习从 Kubernetes 的角度看待安全性。

进一步阅读

第八章:安全现实检查

安全性,尤其是在 Kubernetes 中的安全性,是一个具有讽刺意味的问题。每个人都知道它很重要,但它并没有像开发人员那样被视为同等必要的事物。事实上,如果你看一下比例,可能是 1 个安全工程师对 100 个开发人员。环境本身并不安全,尤其是在访问控制方面,然而安全性无疑是 Kubernetes 中最被忽视的部分之一。由于缺乏关于 Kubernetes 的安全意识,本章将重点讨论在保护 Kubernetes 环境时你应该考虑的各个方面。

从理论角度来看,你将学习如何思考 Kubernetes 中的安全性。从实践角度来看,你不仅将学习如何实施安全实践,还将学习使用哪些工具和平台。

在考虑生产环境时,本章可能是整本书中最重要的一章。你必须先学会走路,然后才能跑步,因此,在你能够保护它之前,必须先学会如何在生产中使用 Kubernetes。本书第 1 至第七章的重点是帮助你达到这个阶段。然而,本章的重点是将事情提升到一个新的层次,就像信息技术IT)的许多领域一样,下一个层次就是安全性。

本章结束时,你将知道在保护 Kubernetes 环境时应采用哪些实践,从集群本身到运行在集群内的容器化应用程序。你还将知道使用哪些工具和平台来完成任务。

在本章中,我们将讨论以下主要内容:

  • 默认 Kubernetes 安全性

  • 调查集群安全性

  • 理解基于角色的访问 控制RBAC

  • Kubernetes 资源(对象)安全性

  • Kubernetes Secrets

重要提示

就像所有与工程相关的书籍和研究分析师的分析一样,本章中使用的数字/百分比基于不同领域的经验。在本书中,如果有一些数字没有具体的关联数据源,这些数据是从作者 Michael Levan 的生产经验中汇总的。

技术要求

本章与本书中的大多数章节一样,你需要运行一个 Kubernetes 集群。虽然你可以在 Minikube 等环境中进行测试,但强烈建议你在云中创建一个 Kubeadm 集群或一个托管的 Kubernetes 服务集群,例如Azure Kubernetes ServiceAKS)、Amazon Elastic Kubernetes ServiceAmazon EKS)或Google Kubernetes EngineGKE)。原因是你应该从生产环境的角度了解运行 Kubernetes 安全测试的真正情况,这将让你看到它默认情况下是多么不安全,或者你可以做些什么来降低这些风险。

如果你想部署一个 Kubeadm 集群,可以查看这个 Git 仓库获取帮助:

github.com/AdminTurnedDevOps/Kubernetes-Quickstart-Environments/tree/main/Bare-Metal/kubeadm

本章所用的整体代码可以在这里找到:

github.com/PacktPublishing/50-Kubernetes-Concepts-Every-DevOps-Engineer-Should-Know/tree/main/Ch8

开箱即用的 Kubernetes 安全性

到目前为止,通常有两类人群——那些完全新接触 Kubernetes 的人和那些几乎可以称为专家的人。

对于那些刚接触 Kubernetes 的群体,他们只是想了解环境的构成。他们甚至还没有到思考安全性的阶段。

对于那些高级的群体——是的,他们正在实施安全实践。问题在于,相对于那些刚接触 Kubernetes 的群体,先进的群体是非常小的。

然后,还有一些工程师介于两者之间。他们不是完全新手,但也没有达到非常高级的水平。这是很多工程师所处的群体,坦白说,这个群体正是开始思考安全性问题的群体。

与大多数平台一样,没有任何系统可以开箱即用就做到 100%的安全。事实上,无论你花多长时间来保护一个环境,它永远也不可能是 100%安全的。安全的整体目标是尽可能降低风险,但你永远无法消除 100%的风险。

从理论角度来看,我们来讨论一下整体安全性和 Kubernetes 安全性的一些问题。

安全漏洞

网络安全从定义上来说是保护系统和网络免受系统泄露。这意味着保护从物理服务器/计算机本身到操作系统,再到服务器/计算机或网络上的任何数据和元数据。如果你仔细想一想,这涉及的信息量非常大。你认为每天有多少邮件通过 Gmail 发送?Gmail 的具体数字不确定,但所有电子邮件提供商的总数是 3196 亿(B)。从理论上讲,但也很可能准确地猜测,至少有 25%的邮件是通过 Gmail 发送的。

重点是什么?

仅仅是邮件就包含了大量的信息,那么其他的一切呢?从一个国家到另一个国家通过网络传输的信息;硬盘上的数据:这些都属于系统保护 和网络保护的一部分。

Norton 在最近的一篇博客中提到(us.norton.com/blog/emerging-threats/cybersecurity-statistics#),每天大约有 2200 起网络安全攻击。老实说,这个数字似乎有点低。然而,即使这个数字准确,也意味着每年有 80 万次网络攻击。这个数字可不小。

基于这些知识,作为工程师,我们必须为这种类型的行为准备好我们的系统和网络。随着云计算的持续发展以及 Kubernetes 的日益普及,未来会有更多直接与 Kubernetes 相关的攻击。

正如本节开头所讨论的那样,安全的理念并不是阻止所有风险。事实上,你永远无法阻止一切。专注于安全实施的安全工具、平台和工程师有一个共同的目标——尽可能多地阻止安全威胁。如果一个系统是安全的,操作系统可能不安全;如果操作系统是安全的,网络可能不安全;如果网络是安全的,应用程序可能不安全……如此循环往复。安全是永远无法做到 100% 的,但工程师可以采取预防措施,使安全性尽可能接近 100%。

考虑到本节中的所有内容,问题回到这里:什么是安全? 简而言之,它是一种保护数据的方法。

Kubernetes 安全

来自 Red Hat 的 Kubernetes 状态 安全报告(www.redhat.com/en/resources/state-kubernetes-security-report)突出了与 Kubernetes 安全领域直接相关的安全问题:

  • 93% 的受访者在过去 12 个月里在其 Kubernetes 环境中经历过至少一次安全事件。

  • 超过一半的受访者(55%)因为安全问题而不得不推迟应用程序的发布。

  • Kubernetes 中约有 70% 的安全问题源于配置错误(根据 Gartner 的数据,实际比例是 99%)。

当你查看来自 Red Hat 的安全报告中的这些统计数据时,有一个趋势是每个人都能轻易看出的——安全在 Kubernetes 领域是一个巨大的问题。

事实上,正如许多工程师和高管所言,Kubernetes 领域的安全问题现在确实很乱。没有特别明确的原因,但有一个合理的猜测。如果你看一下 Red Hat 之前提到的统计数据,70% 的安全问题源于配置错误,那意味着主要原因是工程师们仍在摸索如何使用 Kubernetes。

正如你在本书中学到的内容,正如我相信你在网上看到的,几乎每个人仍然在尝试弄清楚 Kubernetes。没有 Kubernetes 专家,因为它的格局每天都在变化。没有 Kubernetes 的终极目标,因为它不断变化。这不像数学方程式,解开它就完成了。一旦你解决了 Kubernetes,围绕 Kubernetes 的 10 个问题就会浮现出来。因此,如何可能不经常发生配置错误呢?特别是对于那些不仅仅专注于 Kubernetes,而是关注多个领域的工程师而言。如果 Kubernetes 总是在变化,工程师如何才能尽可能接近 Kubernetes 的专家?配置错误总是不可避免的。

因此,Kubernetes 安全的现状非常混乱。事实上,可能会持续很长时间。要确保一个持续变化的东西是非常困难的。

然而,隧道的尽头还是有一些曙光的。像所有平台和环境一样,确实有一些最佳实践可以遵循。再说一次,考虑安全性,目标是什么?不是修复所有问题,而是尽可能地减轻更多的风险。本章的目的是做到这一点:尽可能减轻 Kubernetes 环境中的安全风险。

让我们开始吧!

调查集群安全性

摒除 Kubernetes 来看,让我们思考整体基础设施和/或云安全。在高层次上,你需要考虑网络、服务器、连接到服务器的方式、用户访问权限以及确保服务器上安装的应用程序是安全的。在云计算的世界里,你不必担心物理安全方面的问题。但如果你的集群在数据中心,你就必须考虑物理安全。数据中心机架上的锁可以确保没有人能插入任何 USB 密钥,也没有人能够直接把服务器从机架上取下来并带走。

服务器安全是由运行在服务器内外的内容组成的——运行的应用程序、执行的程序以及整个操作系统本身。例如,假设你运行的是一个较旧版本的 Ubuntu。那么你应该绝对检查并确认没有安全漏洞。这对任何运行在 Ubuntu 上的 Kubernetes 集群来说依然非常重要。然而,Kubernetes 自己有一套标准。

从网络的角度来看,安全性在 Kubernetes 中同样适用,就像在任何其他环境中一样。如果你有一个前端或后端的 Kubernetes 服务,接受来自任何地方的流量,这基本上意味着你有一个完全开放的防火墙。如果你没有用像服务网格或以安全为中心的容器网络接口CNI)这样的方式加密 Pod 到 Pod 和/或服务到服务的通信,你可能会面临更多的风险。

例如,Kubernetes 本质上是一个 API。像所有 API 一样,它可能存在安全风险。这意味着,最大的安全重点之一就是确保您当前使用的 Kubernetes API 版本没有重大安全风险,因为这可能会让您的整个环境陷入瘫痪。

Kubernetes 安全的一个重要部分是基准和其他自动化测试,您将在本节中学习到这些内容。

集群加固与基准

互联网安全中心CIS)多年来一直是系统加固的事实标准。CIS 基准是一组全球公认的标准和最佳实践,旨在帮助工程师设置他们的安全防御措施。无论是在云端、本地环境,还是特定的应用/工具,都有相应的最佳实践,CIS 正是帮助您确定这些最佳实践的工具。

由于 CIS 本质上是一个最佳实践的清单,您必须想象它覆盖了数千种不同的最佳实践,分布在各种平台和环境中。如果您考虑到像 Ubuntu 这样的 Linux 发行版,它就有专门针对该发行版的最佳实践。如果考虑到像 亚马逊 Web 服务AWS)这样的整个平台,那最佳实践就更多了。

当您了解 CIS 时,您会发现有大量预先配置的 CIS 环境。例如,在 AWS 中,有 CIS 加固的 亚马逊机器镜像AMIs):

图 8.1 – 加固的 AMI

图 8.1 – 加固的 AMI

在其他云平台,如 谷歌云平台GCP)或 Azure 中,也有类似的内容。即使在像 iPhone 这样的手机上,也有 CIS 基准:

图 8.2 – iOS 加固

图 8.2 – iOS 加固

CIS 实际上可以成为一本完整的书籍,所以这里是要点——CIS 基准是一个最佳实践和标准的清单,旨在从安全角度为各种系统、平台、应用和环境提供指导。

由于 Kubernetes 的普及,CIS 在 2017 年与社区合作,创建了专门针对 Kubernetes 的基准:

图 8.3 – 安全化 Kubernetes

图 8.3 – 安全化 Kubernetes

甚至有针对特定 Kubernetes 环境的 CIS 基准,例如 GKE。

当您浏览本章内容时,以及在您的 Kubernetes 安全之旅中,您会看到许多工具和平台使用 CIS 基准进行容器镜像扫描和集群扫描。像 Checkov、kube-bench、Kubescape 以及其他一些流行的安全工具,都是基于 CIS 和国家漏洞数据库NVD)进行扫描的。

您可以免费下载最新的 Kubernetes CIS 基准,只需在 https://www.cisecurity.org/benchmark/kubernetes 上填写您的姓名和电子邮件。

浏览 Kubernetes CIS 基准

Kubernetes 中的 CIS 基准是一个庞大的 PDF,您可以下载并浏览,确保您实现的 Kubernetes 环境符合您所运行的 Kubernetes API 版本的最佳标准和最佳实践。

让我们学习如何下载 Kubernetes CIS 基准的 PDF。请按照以下步骤操作:

  1. 访问此链接并填写您的信息:www.cisecurity.org/benchmark/kubernetes

  2. 填写完信息后,您应该会收到一封电子邮件,下载 PDF 文件。文件将会很多,所以请搜索 Kubernetes。然后您应该会看到所有的 Kubernetes 基准。

  3. 选择第一个选项,目前为 Kubernetes API 版本 1.23,并点击橙色的下载 PDF按钮:

图 8.4 – Kubernetes CIS 信息

图 8.4 – Kubernetes CIS 信息

该文档有 302 页,因此实际情况是,您可能不希望通篇阅读,尤其是在阅读完本章之后(或者您可能想要读!)。您可以浏览并搜索感兴趣的内容。我个人喜欢 Kubernetes Secrets 部分,它明确指出您应该考虑使用外部 Secrets 存储。

关于一般服务器加固的说明

服务器加固应该是任何环境中的首要任务。无论您是运行 Windows 服务器、Linux 服务器,还是两者的混合,系统加固是减少系统层面安全漏洞的关键。

由于 CIS 已经存在了很长时间,因此几乎所有的内容都有相应的基准。例如,这里有一张截图展示了可用的一些基准:

图 8.5 – 基准选项

图 8.5 – 基准选项

即便是从桌面的角度来看,您也可以对某些应用程序和工具(如 Google Chrome 或 Microsoft Office)运行 CIS 基准:

图 8.6 – 桌面基准选项

图 8.6 – 桌面基准选项

要查看完整列表,请访问 www.cisecurity.org/cis-benchmarks/

系统扫描

虽然这不是专门针对 Kubernetes 或 Kubernetes 扫描的工具,但事实上,如果您正在运行任何类型的系统,且该系统位于 Kubernetes 环境中的控制平面、工作节点或两者兼有,您应该运行系统扫描,确保环境配置正确。操作步骤如下:

  1. learn.cisecurity.org/cis-cat-lite 下载 CIS-CAT® Lite 工具(它是免费的)。

  2. 接下来,解压并打开 Assessor-GUI 二进制文件:

图 8.7 – GUI 二进制文件

图 8.7 – GUI 二进制文件

  1. 在 GUI 工具中,选择高级选项,以便您可以指定远程主机:

图 8.8 – 高级选项

图 8.8 – 高级选项

  1. 选择一个能够让你添加远程系统的选项:

图 8.9 – 添加目标系统

图 8.9 – 添加目标系统

  1. 输入你希望扫描的主机信息,如 IP 地址、名称、系统类型和用户名/密码(或 SSH 密钥):

图 8.10 – 目标系统信息

图 8.10 – 目标系统信息

  1. 如你在下一个截图中看到的那样,并没有专门的 Kubernetes 扫描功能。希望未来会添加这个功能,尽管你稍后会看到本章中有专门用于扫描 Kubernetes 是否符合 CIS 的工具。在这种情况下,你可以选择 Ubuntu Linux 选项:

图 8.11 – 可用基准

图 8.11 – 可用基准

  1. 点击保存按钮:

图 8.12 – 添加目标系统

图 8.12 – 添加目标系统

  1. 为确保你能正确扫描服务器,测试连接:

图 8.13 – 指定控制平面

图 8.13 – 指定控制平面

  1. 点击下一步,测试应该开始:

图 8.14 – 运行安装

图 8.14 – 运行安装

  1. 然后,你将看到一个界面,要求你选择一个位置来保存报告。保留默认设置,然后开始评估:

图 8.15 – 评估结果

图 8.15 – 评估结果

一旦评估完成,你将看到报告输出在先前步骤中看到的默认报告位置:

图 8.16 – 基准报告

图 8.16 – 基准报告

集群网络安全

在 Kubernetes 中,将有两种不同类型的网络安全——内部安全和主机安全。主机安全当然可以包括你的云 VPC 和安全组,或者在本地环境中运行的防火墙。内部安全则包括 Pod 安全、服务安全,以及总体上 Kubernetes 资源如何相互通信。

为了保持 Kubernetes 相关性,你将学习内部安全而非主机安全。如果你想了解主机安全,强烈建议你查看整个网络如何运作以及防火墙、端口映射、网络路由等安全相关主题。

本节的其余部分,你将学习以下内容:

  • CNI 安全方法

  • 扩展的 Berkeley 数据包 过滤器 (eBPF)

CNI 安全

在本书的内容中,你已经学习了服务网格,在接下来的章节中,你将学习 eBPF。然而,从 CNI 角度来看,你还可以采取另一种安全方法。在你查看不同的 CNI 时,你会看到多种不同类型的插件。一些插件,如 Flannel,适用于只需启动并运行的初级工程师。它没有任何复杂的功能,功能简化且非常基础,这正是它的目的。

然后,你会看到其他插件,如 Calico,它是一个更高级的 CNI,并且非常注重安全性。实际上,你可以使用 Calico 和 WireGuard 加密 Pod-to-Pod 通信,而无需实现服务网格,这也是工程师实现服务网格的主要原因之一。

当你开始探索内部网络安全时,你应该问自己一个主要问题,那就是你希望如何实现 CNI,以及为什么要实现它。你是希望一个“开箱即用”的 CNI,还是希望一个可能需要更多配置和时间的 CNI,但它具备正确的安全组件,从而在长远来看让你的工作更轻松?

你可以在 projectcalico.docs.tigera.io/security/encrypt-cluster-pod-traffic 了解更多关于 Calico 和 WireGuard 的信息。

eBPF

eBPF 本身可以写成一本书,但简而言之,它是一种不需要更新 Linux 内核代码就能运行某些程序的方法。从 Kubernetes 的角度来看,它还可以去除 kube-proxy 的职责。

当谈到 Kubernetes 和 eBPF 时,让我们关注几个关键部分:

  • 移除 kube-proxy

  • 更简便的扩展

  • 安全性

kube-proxy 有助于使 Kubernetes 可用。如果没有它,Kubernetes 就无法正常工作。然而,也存在一个问题。kube-proxy 使用 iptables。尽管 iptables 已经在 Linux 中存在很长时间,但它的扩展性并不好。iptables 规则以列表的形式存储,当 Pods 建立与 Kubernetes 服务的连接时,它们会逐条检查每个 iptable 规则,直到找到特定的规则。虽然这对少量规则来说可能不算什么,但如果有成千上万条规则(你很可能会有),那么这将是一个性能问题。

从可扩展性的角度来看,随着 Kubernetes 服务(任何类型的 Kubernetes 服务)在集群中的数量增加,连接性能会下降。原因之一是,当你创建 iptable 规则时,它们并不是增量式的,这意味着每次更新时,kube-proxy 都会重写整个表格。这会造成巨大的性能影响。

现在你已经了解了一些 eBPF 重要性的理论,尽管这些内容本身可以写成一本书,让我们深入探讨 eBPF 的实践实现:

  1. 首先,这一切取决于你使用的集群。和其他所有 Kubernetes 环境一样,如果你使用的是云中的托管 Kubernetes 服务,使用 eBPF 将根据你为 Kubernetes 托管服务部署指定的 CNI 来变化。

如果你打算运行 Kubeadm,例如,以下命令是你应该使用的来移除kube-proxy。即使你不使用所有标志,也请确保使用--skip-phases=addon/kube-proxy标志,因为这是防止安装kube-proxy所必需的:


sudo kubeadm init --skip-phases=addon/kube-proxy --control-plane-endpoint $publicIP --apiserver-advertise-address $ip_address --pod-network-cidr=$cidr --upload-certs
  1. 接下来,如果你还没有安装 Helm,请安装 Helm:

    
    curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
    
    
    chmod 700 get_helm.sh
    
    
    ./get_helm.sh
    
  2. 添加 Cilium Helm 仓库:

    
    helm repo add cilium https://helm.cilium.io/
    
  3. 添加仓库后,你可以使用 Helm 安装 Cilium。注意设置kube-proxy替代项的标志:

    
    helm install cilium cilium/cilium \\
    
    
    --namespace kube-system \\
    
    
    --set kubeProxyReplacement=strict \\
    
    
    --set k8sServiceHost=ip_address_of_control_plane \\
    
    
    --set k8sServicePort=6443
    
  4. 几分钟后,运行以下命令检查 Cilium Pods 是否成功运行:

    
    kube get pods -n kube-system
    

输出应类似于以下截图:

图 8.17 – Cilium Pods

图 8.17 – Cilium Pods

使用 eBPF 仍然是一个非常新的话题,你可能在所有环境中都看不到它。然而,我可以向你保证,随着 eBPF 的日益普及以及它带来的好处,你将会看到它越来越多的应用。

升级 Kubernetes API

在每个 Kubernetes 环境中,你必须跟踪 Kubernetes API。你最不想做的事情就是让任何软件或平台的 API 过时,尤其是 Kubernetes。所有的 API,甚至是 Kubernetes,都可能最终存在需要修补的安全漏洞。你必须确保你的环境做好准备。

当你跟踪 Kubernetes API 时,不可避免地会发生一件事:你将不得不升级 API。这不仅仅是为了功能和保持系统的最新,更从安全的角度来说,你不希望落后太远,因为每个软件的旧版本都会停止修补,安全漏洞会被暴露出来。

在本节的其余部分,你将学习如何在运行 Kubeadm 的集群上执行 Kubernetes 升级。如果你没有 Kubeadm,也没关系——仍然可以跟着做。最终,你将不得不对一个原始的 Kubernetes 集群进行升级,所以了解这些还是很有用的。

重要说明

对于任何类型的升级,特别是在生产环境中,你不仅需要积极地测试升级路径,还应备份你的环境组件。

升级控制平面

让我们从升级 Kubeadm 控制平面开始。请按照以下步骤操作:

  1. 运行upgrade命令,查看可用的升级路径:

    
    kubeadm upgrade plan
    

图 8.18 – Kubernetes 升级

图 8.18 – Kubernetes 升级

  1. 在以下输出中,你将看到每个可用升级的目标版本,以及需要运行的命令。输出还会显示当前的 Kubernetes API 版本,以及哪些控制平面组件将会被升级:

图 8.19 – 升级路径

图 8.19 – 升级路径

  1. 在运行升级之前,你需要下载最新版本的 API,并确认将 Kubeadm 保持在暂停状态,以避免一次性升级所有控制平面组件。注意,运行以下命令可能会导致你需要重启服务器:

    
    apt-mark unhold kubeadm && apt-get update && apt-get install -y kubeadm=1.25.x-00 && apt-mark hold kubeadm
    
  2. 完成后,运行升级命令,如下所示:

    
    kubeadm upgrade apply v1.25.x
    

你会看到类似以下截图的输出:

图 8.20 – 升级输出

图 8.20 – 升级输出

以下是前面截图中的第二部分输出:

图 8.21 – 升级输出继续

图 8.21 – 升级输出继续

现在你已经升级了控制平面,让我们学习如何升级工作节点。

升级工作节点

  1. 在运行升级之前,你需要下载最新版本的 API,并确认将 Kubeadm 保持在暂停状态,以避免一次性升级所有控制平面组件。注意,运行以下命令可能会导致你需要重启服务器:

    
    apt-mark unhold kubeadm && apt-get update && apt-get install -y kubeadm=1.25.x-00 && apt-mark hold kubeadm
    
  2. 接下来,升级工作节点,如下所示:

    
    sudo kubeadm upgrade node
    

图 8.22 – 节点升级

图 8.22 – 节点升级

就这样!与控制平面相比,这个过程相对更简单直接。

升级 kubelet

最后的步骤是升级控制平面和工作节点上的 kubelet。请按照以下步骤操作:

  1. 运行以下命令来升级 kubelet:

    
    apt-mark unhold kubelet kubectl && apt-get update && apt-get install -y kubelet=1.25.x-00 kubectl=1.25.x-00 && apt-mark hold kubelet kubectl
    
  2. 接下来,重新加载 kubelet,如下所示:

    
    sudo systemctl daemon-reload
    
    
    sudo systemctl restart kubelet
    
  3. 运行以下命令后,你应该看到 Kubernetes 集群已成功升级:

    
    kubectl get nodes
    

虽然这看起来不像是纯粹与安全相关的内容,也许它确实不是,但它对安全来说依然至关重要。你不能让旧版本的软件闲置不管,就像你不能让旧版本的 API 闲置不管一样。对平台工程团队而言,这没有什么不同。

审计日志记录和故障排除

Kubernetes 生成了多个日志。事实上,大多数 Kubernetes 资源都启用了度量端点。这意味着,任何由该 Kubernetes 资源生成的内容——如身份验证、访问、Pod 崩溃、容器启动、最终用户访问等——都会被记录下来。

问题在于审计日志记录——有时甚至是度量服务器——默认情况下并未启用或安装。你有能力在 Kubernetes 中安装和配置审计日志记录,但它不是开箱即用的。

这意味着 Kubernetes 的审计日志 API 是可用的并且开箱即用。它只是不会开始生成任何你能看到的日志,因为你首先需要通过audit.k8s.io/v1 API 设置一个策略,但策略默认是不存在的——需要工程师自己创建这些策略。这个策略可以是从展示所有内容展示特定 Kubernetes 资源的读取操作,可以是高层次的,也可以是非常细致的,取决于你的需求。

有很多策略,包括审计日志,可以被启用。事实上,这可能是一个涉及整个集群的主题。由于这个原因,本节我们将重点讨论审计日志。然而,以下截图展示了 开放 Web 应用安全项目OWASP)为 Kubernetes 提出的前 10 大问题,其中之一就是适当的日志记录:

图 8.23 – OWASP 前 10 名

图 8.23 – OWASP 前 10 名

您可以在此处查看更多相关信息:github.com/OWASP/www-project-kubernetes-top-ten/blob/main/2022/en/src/K05-inadequate-logging.md

在进入实际操作部分之前,让我们先谈谈审计日志是什么。审计日志由 Kubernetes API 服务器记录。通过这些记录(就是日志),它按时间顺序记录了 Kubernetes 集群上操作的顺序。

它生成:

  • 用户执行的操作

  • Kubernetes 资源执行的操作

  • 控制平面本身

从本质上讲,审计日志让你可以提出以下问题:1)发生了什么? 2)什么时候发生的? 3)怎么发生的? 任何问题都不应该留下未解答的,因为您可以通过审计日志获取 Kubernetes 集群中的任何信息。

有了这些,让我们学习如何设置它们。按照以下步骤操作:

  1. 创建一个网络策略,如下所示。为了本节的目的,您可以将其存储在 /etc/kubernetes/simple-policy.yaml 下:

    
    apiVersion: audit.k8s.io/v1
    
    
    kind: Policy
    
    
    rules:
    
    
    - level: Metadata
    

重要提示

如果您使用的是托管 Kubernetes 服务,如 AKS 或 EKS,您需要以不同的方式启用审计日志,具体方式取决于您使用的服务器。然而,您仍然应该阅读这一部分,因为在 Kubernetes 使用过程中,您最终会接触到裸金属/虚拟机环境中的审计日志(尤其是在混合云越来越流行的今天)。

  1. 接下来,通过 Vim 或您选择的编辑器打开以下位置:/etc/kubernetes/manifests/kube-apiserver.yaml

  2. 按照图 8.24所示,添加以下代码。这将使您能够设置审计日志的消费方式,并设置日志保留的时间、审计日志输出的路径以及审计策略的位置:

    
    - --audit-log-maxage=7
    
    
    - --audit-log-maxbackup=2
    
    
    - --audit-log-maxsize=50
    
    
    - --audit-log-path=/var/log/audit.log
    
    
    - --audit-policy-file=/etc/kubernetes/simple-policy.yaml
    

图 8.24 – 审计策略路径

图 8.24 – 审计策略路径

  1. volumeMounts 下,添加以下代码,如图 8.25所示。对于 Kubernetes,审计日志的策略和路径需要在集群中挂载:

    
    - mountPath: /etc/kubernetes/simple-policy.yaml
    
    
      name: audit
    
    
      readOnly: true
    
    
    - mountPath: /var/log/audit.log
    
    
      name: audit-log
    
    
      readOnly: false
    

图 8.25 – 策略挂载

图 8.25 – 策略挂载

  1. hostPath 下,添加以下内容:

    
    - hostPath:
    
    
        path: /etc/kubernetes/simple-policy.yaml
    
    
        type: File
    
    
      name: audit
    
    
    - hostPath:
    
    
        path: /var/log/audit.log
    
    
        type: FileOrCreate
    
    
      name: audit-log
    

图 8.26 – 策略主机路径

图 8.26 – 策略主机路径

  1. 通过运行以下命令重新启动 kubelet:

    
    sudo systemctl restart kubelet
    
  2. 确认 kubelet 仍在运行,如下所示:

    
    kubectl get nodes
    

现在,你可以通过执行以下命令查看控制平面上的审核日志,路径/位置是你存储 audit.log 文件的地方:


tail -f /var/log/audit.log

你应该会看到一大堆日志输出。出于安全考虑,我没有包含展示输出的截图。

如前所述,这种配置类型适用于 Kubeadm 集群或本地部署的集群。对于云环境,将会有所不同。不过,了解这个过程仍然非常重要。记住——云平台将很多东西抽象化,但工程师仍然需要理解系统的底层组件,以便正确地与之协作。

理解 RBAC

当涉及到用户、组和服务账户时,你必须问自己两个问题。第一个是:谁可以访问你的集群? 哪些用户、服务账户和组有能力在开发、预发布和生产环境中的集群上运行 kubectl 命令?这些用户中,哪些有一个 Kubeconfig 配置文件,赋予他们访问特定集群的权限?他们可以连接到哪些环境?

第二个问题是:他们进入集群后能做什么? 他们能列出 Pods 吗?创建 Pods 吗?查看 Ingress 控制器吗?创建 Ingress 控制器吗?他们在每个环境中可以与哪些 Kubernetes 资源进行交互?

在设置 Kubernetes 环境时,你还必须考虑身份验证和授权。谁可以访问你的集群,他们能做些什么?此外,你还需要思考用户在每个环境中可以执行的操作。例如,考虑你在前一章学到的单租户模型,一个工程师可能在一个集群上拥有完全的管理员权限,而在另一个集群上仅有只读访问权限。基于此,你还需要考虑在授权方法方面,应该授予用户哪些权限。

在本节中,你将学习如何从权限角度管理 Kubernetes 中的用户、组和团队,使用 RBAC。

请注意,虽然本节内容不多,但它应该能为你指引正确的方向,帮助你思考 RBAC 并开始实施它。

什么是 RBAC?

RBAC,和本书中的许多其他主题一样(此时我像个老唱片一样重复),本身可以成为一本完整的书。因此,我们先进行简短的理论解释,然后再深入实践部分。

RBAC 的定义是,确保用户、组和服务账户只拥有它们所需权限的授权方式。RBAC 不进行身份验证——它进行的是授权。身份验证是在 RBAC 之前完成的。一旦用户、组或服务账户被创建,RBAC 就可以启动并开始创建权限。

在 Kubernetes 的 RBAC 中,你有四个主要资源需要使用:

  • Roles

  • ClusterRoles

  • RoleBindings

  • ClusterRoleBindings

你将在接下来的章节中了解更多关于它们的内容。

当你在考虑 RBAC 时,思考一下:我允许这个人在 Kubernetes 内部做什么?

需要记住的一点是,RBAC 通常是每个安全工程师的噩梦。它是 Kubernetes 中让每个人都想撞墙的话题之一,因为它可能变得非常复杂,而且没有中央方式来管理成百上千的 RBAC 角色和权限。市场上有一些工具和平台在尝试缓解这一问题,例如 Kubescape 的 RBAC 可视化工具。

要继续本章内容,你需要一个用户、组或服务账户。因为 Kubernetes 没有现成的创建用户和组的方法,所以我们使用一个服务账户。

运行以下命令创建一个名为 miketest 的新服务账户:


kubectl create sa miketest

服务账户创建完成后,可以在接下来的部分中使用它。

Roles 和 ClusterRoles

Roles 是你可以赋予用户、组和服务账户的权限,并且它们是命名空间范围的。这意味着,假设你创建了一个名为 readpods 的角色。该角色将绑定到一个命名空间——例如,名为 ingress 的命名空间。这意味着 readpods 角色只在 ingress 命名空间中有效,而不与其他命名空间绑定。

如果你想为一个在集群中所有命名空间中使用的用户/组/服务账户设置角色/权限呢?这时 ClusterRoles 就派上用场了。ClusterRoleRole 相同,唯一的区别是它没有命名空间范围。

让我们深入学习如何创建 RolesClusterRoles

Roles

以下代码片段是一个 Role 的示例,你可以创建它。它的作用范围是 ingress 命名空间,并为 Pod Kubernetes 资源设置只读权限。注意在动词中,它全是读取权限——getwatchlist


kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: ingress
  name: reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

实现前面的 Role 将确保你创建了一个适当的角色,以便为用户/组/服务账户在 ingress 命名空间中提供只读权限。

ClusterRoles

与前面的 Role 一样,以下 ClusterRole 创建了一个名为 readerClusterRole,用于对 Pods 进行只读权限。关键区别在于,它并不限制于某个特定的命名空间:


kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: reader-cluster
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

实现前面的 ClusterRole 将确保你创建了一个适当的角色,以便为用户/组/服务账户在集群的所有命名空间中提供只读权限。

接下来,让我们学习如何将 Roles 绑定到特定的服务账户。

RoleBindings 和 ClusterRoleBindings

RoleBinding 是将 Role 绑定/附加到用户/组/服务账户的一种方式。例如,假设你有一个名为 podreadersRole,并且你想将该角色绑定/附加到 miketest 服务账户。你可以使用 RoleBinding 来执行这个操作。

就像 RolesClusterRoles 一样,唯一的区别是 RoleBindings 是命名空间范围的,而 ClusterRoleBindings 则没有,它们可以在整个集群中使用。

让我们学习如何实现 RoleBindingsClusterRoleBindings

RoleBinding

以下 RoleBinding 将您在上一节中创建的 Role 附加到 miketest 服务账户。看到这里有一个 kind 并且指定了服务账户的种类了吗?在这里,您可以指定一个 groupuser。由于这不是一个 ClusterRoleBinding,它的作用范围也仅限于 ingress 命名空间:


apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: reader-pod
  namespace: ingress
subjects:
- kind: ServiceAccount
  name: miketest
  apiGroup: ""
roleRef:
  kind: Role
  name: reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding

与前面的 RoleBinding 类似,以下的 ClusterRoleBinding 会将 miketest 服务账户附加到 ClusterRole 并引用以下的 ClusterRole


apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-pod-global
subjects:
- kind: ServiceAccount
  name: miketest
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: reader-cluster
  apiGroup: rbac.authorization.k8s.io

现在您已经了解了整体的身份验证和授权权限,是时候了解 Kubernetes 资源的整体安全性,以及您可以采取的初步措施,以确保成功的安全部署。

Kubernetes 资源(对象)安全性

在本章中,您了解了一些 Kubernetes 资源安全性的知识。请记住,Kubernetes 资源可以是从 Pods 到 Ingress 控制器到服务等任何东西。实质上,任何您通过 API 访问的运行在 Kubernetes 集群中的东西都是 Kubernetes 资源。

在本节中,您将学习当前保障 Kubernetes 资源安全的最佳方法,这些方法适用于 Kubernetes 内部和使用第三方工具。

Pod 安全性

当谈到 Kubernetes 环境中的网络安全时,分为两部分——主机网络和内部网络。由于每个环境都不同,本节不讨论主机网络。无论是不同的物理硬件还是虚拟硬件设置,都没有 一刀切 的网络环境。

然而,有一些适用于所有环境的有用建议:

  1. 确保您有适当的防火墙规则。

  2. 确保您正在实施正确的路由协议,而不仅仅是开放整个网络。

  3. 确保您已正确设置端口。

  4. 确保您正在记录并观察网络流量。

对于 Kubernetes 网络安全,有网络策略。

Kubernetes 通过networking.k8s.io/v1 API 内置了网络策略(Network Policies)。网络策略就像是针对入口和出口流量的防火墙规则。不过,网络策略不仅仅是关于允许或禁止特定 IP 地址和端口的问题。你可以通过策略做更多的事情。例如,你可以阻止从特定网络到特定命名空间、从特定命名空间或者到/从特定应用程序的流量。由于网络策略提供了大量选项,你可以有很多选择,但你需要确保设置了正确的策略。一不小心写错了162.x.x.x而不是172.x.x.x,可能会完全打乱网络策略中的整个工作流程,并且完全停止应用程序的工作负载。

让我们深入了解一下网络策略是什么样子。

要测试这一点,在你的 Kubernetes 环境中运行以下 Pods:


kubectl run busybox1 --image=busybox --labels app=busybox1 -- sleep 3600
kubectl run busybox2 --image=busybox --labels app=busybox2 -- sleep 3600

上述新的 Pods 将运行一个名为busybox的容器镜像,这是一个通常用于测试的小型尺寸。

接下来,获取 Pods 的 IP 地址,像这样:


kubectl get pods -o wide

图 8.27 – Pod 输出

图 8.27 – Pod 输出

busybox1 Pod 运行 ping 命令:


kubectl exec -ti busybox2 -- ping -c3 ip_of_busybox_one

图 8.28 – Ping 输出

图 8.28 – Ping 输出

现在你知道了有一个正确的busybox1 Pod:


kubectl create -f - <<EOF
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: web-deny-all
spec:
  podSelector:
    matchLabels:
      app: busybox1
  ingress: []
EOF

再次对busybox1运行 ping 命令:


kubectl exec -ti busybox2 -- ping -c3 ip_of_busybox_one

现在应该有 100%的数据包丢失。

如果你在一个类似标准 Minikube 环境中这样做失败了,原因很可能是你使用的 CNI 不支持或未启用网络策略。

要了解如何启用网络策略,你需要快速搜索如何为你的特定 CNI 实现网络策略。

策略执行

在前一节中,你了解了在网络层面上的安全性,这当然是必需的。在网络层之后(或之前)是应用层,在这里围绕 Pods 和容器的策略执行变得至关重要。

策略执行的整个理念是为你提供保护你的 Pods、确保最佳安全实践并为你的组织设置标准的能力。

例如,在生产环境中的一个最佳实践是确保你不在生产环境中使用最新的容器镜像版本。相反,你总是要使用一个经过适当版本化和经过实战测试的容器镜像版本来保护你的应用程序。通过 Kubernetes 中的策略执行,你可以实现这一点。

现在,实施策略执行的两个最重要的方法是使用开放策略代理OPA)和 Kyverno。从策略执行的角度来看,它们都是相同的,但最大的区别在于 Kyverno 只能在 Kubernetes 内部工作。因此,许多工程师倾向于使用 OPA,这样他们可以在整个环境中使用它,而不仅仅是在 Kubernetes 中。

因此,在实际操作部分将使用 OPA。

那么,Pod 安全策略(Pod Security Policies)怎么办?

如果你听说过 Pod 安全策略(Pod Security Policies),它们本质上与 OPA 相同。然而,它们在 Kubernetes 的 v1.21 版本中已被弃用,并在 v1.25 版本中被完全移除。

OPA

当你想配置一个特定的策略时,可以使用像 OPA 这样的策略代理。OPA 允许你使用一种名为 Rego 的 OPA 专用语言编写策略(你将在本节中看到)。当你编写策略时,来自其他 Kubernetes 资源或外部实体的任何请求或事件都会被查询。OPA 的决策代理会给出 通过失败 的结果。

那么,OPA 如何知道如何实现策略呢?

这就是 OPA Gatekeeper 的作用所在。Gatekeeper 是一个 中间层,它允许 Kubernetes 与 OPA 交互。Gatekeeper 安装在 Kubernetes 上,并启用 OPA 策略的使用。

让我们从动手实践的角度来深入了解 OPA 的设置。第一部分将是部署 OPA Gatekeeper,第二部分将是实现策略。按如下步骤进行:

  1. 添加 Gatekeeper 的 Helm chart,如下所示:

    
    helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
    
  2. 通过运行以下命令安装 Helm chart:

    
    helm install gatekeeper/gatekeeper --name-template=gatekeeper --namespace gatekeeper-system --create-namespace
    
  3. 确认 Gatekeeper 的所有 Kubernetes 资源是否已部署:

    
    kubectl get all -n gatekeeper-system
    

现在 Gatekeeper 已安装,接下来我们开始实施 OPA 策略。

配置是 OPA Gatekeeper 被允许创建策略的定义/输出。在下面的 config.yaml 文件中,由于其编写方式,Gatekeeper 知道它只能为 Pods 指定策略,而不能为其他 Kubernetes 资源指定策略。运行以下代码:


kubectl create -f - <<EOF
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: "gatekeeper-system"
spec:
  sync:
    syncOnly:
      - group: ""
        version: "v1"
        kind: "Pod"
EOF

约束模板是你为环境配置的策略。它是一个模板,因此你可以在多个地方使用它。

以下约束模板中的 Rego 代码/策略确保没有人能使用容器镜像的最新标签。运行以下代码:


kubectl create -f - <<EOF
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: blocklatesttag
  annotations:
    description: Blocks container images from using the latest tag
spec:
  crd:
    spec:
      names:
        kind: blocklatesttag # this must be the same name as the name on metadata.name (line 4)
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package blocklatesttag
        violation[{"msg": msg, "details": {}}]{
        input.review.object.kind == "Pod"
        imagename := input.review.object.spec.containers[_].image
        endswith(imagename,"latest")
        msg := "Images with tag the tag \"latest\" is not allowed"
        }
EOF

接下来,你需要创建约束。约束使用你之前创建的模板,允许你在 Kubernetes 集群内使用该模板创建策略:


kubectl create -f - <<EOF
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: blocklatesttag
metadata:
  name: nolatestcontainerimage
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    annotation: "no-latest-tag-used"
EOF

OPA 策略现在已经创建。

为了确认策略是否按预期工作,你可以使用以下两个 Kubernetes 清单来测试:

  • 以下带有容器镜像最新标签的清单不应该工作,因为你之前创建的策略。部署本身会部署,但 Pods 不会被调度,也不会上线。

尝试运行以下代码:


kubectl create -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginxdeployment
  replicas: 2
  template:
    metadata:
      labels:
        app: nginxdeployment
    spec:
      containers:
      - name: nginxdeployment
        image: nginx:latest
        ports:
        - containerPort: 80
EOF

等待几分钟,当你看到它没有上线时,删除它,方法如下:


kubectl delete deployment nginx-deployment
  • 接下来,尝试以下清单。它会成功,Pods 会上线,因为指定了容器镜像版本:

    
    kubectl create -f - <<EOF
    
    
    apiVersion: apps/v1
    
    
    kind: Deployment
    
    
    metadata:
    
    
      name: nginx-deployment
    
    
    spec:
    
    
      selector:
    
    
        matchLabels:
    
    
          app: nginxdeployment
    
    
      replicas: 2
    
    
      template:
    
    
        metadata:
    
    
          labels:
    
    
            app: nginxdeployment
    
    
        spec:
    
    
          containers:
    
    
          - name: nginxdeployment
    
    
            image: nginx:1.23.1
    
    
            ports:
    
    
            - containerPort: 80
    
    
    EOF
    

OPA 本身是一个庞大的话题。我强烈建议深入了解它。我们在本书中只有几页的篇幅来探讨它,但它的内容远比这深入。

扫描容器镜像

许多工程师开始他们安全之旅的一个流行安全类型的切入点是通过扫描容器镜像。扫描容器镜像意味着你使用工具或平台查看容器镜像内部,看看是否存在任何漏洞。漏洞列表通常来自 NVD 和 Kubernetes 的 CIS 基准。两者都是从安全角度整理出的最佳实践清单,并且包含已知的漏洞。

在这个领域有很多工具。在这一节中,我们将坚持使用一个尽可能内建的工具:Snyk。

Snyk 用于从预定义的最佳实践列表(如前所述)扫描容器中的漏洞。此前,Docker 和 Snyk 合作,确保安全功能本地集成到任何容器化工作负载中。通过这种合作,当你运行 docker scan 命令时,实际上是在后台使用 Snyk。

要使用 Snyk,确保你已安装 Docker CLI 并运行以下命令:


docker scan containerimage:containerversion

例如,假设你想扫描 ubuntu:latest 容器镜像,如下所示:


docker scan ubuntu:latest

一旦你运行 docker scan 命令,你可以浏览所有漏洞。你将看到发现的漏洞的摘要,测试了什么内容,以及使用了哪个平台。

漏洞的严重程度从非常基础,最终只是一个需要修复的最佳实践,到非常关键,可能会使你的环境暴露于攻击之中。

Kubernetes Secrets

本章及整本书的总结,你将学习 Kubernetes Secrets。

简而言之,Secrets 是任何你不希望以明文形式存在的内容。通常,它们是像密码和 API 密钥这样的东西。然而,它们也可以是用户名。任何你不希望以明文形式存在、静态存储或传输的数据都可以被视为 Secret。

在你的工程旅程中,此时假设你不需要学习关于 Secrets 的内容,因此我们将跳过这部分,直接进入实践操作。

创建 Kubernetes Secrets

要创建 Kubernetes Secret,你将使用 v1 核心 API 组中的 secret 资源。

例如,以下是一个名为 testsecret 的 Secret,包含用户名和密码:


apiVersion: v1
kind: Secret
metadata:
  name: testsecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

通过运行以下命令确认 Secret 是否已创建:


kubectl get secrets

接下来,通过将 secret 放入 Pod 中来使用它,如下所示:


apiVersion: v1
kind: Pod
metadata:
  name: nginxpod
spec:
  containers:
  - name: mypod
    image: nginx:latest
  volumes:
  - name: foo
    secret:
      secretName: testsecret

不要使用 Kubernetes Secrets

虽然你刚刚在几秒钟前创建了一个新的 Kubernetes Secrets,但事实是——这并不是一个推荐的做法。

最大的原因是 Kubernetes Secrets 的默认不透明标准会以明文形式存储机密信息。没错——就是这样。机密信息将以明文形式存储在 etcd 数据库中。从另一个角度来看,考虑一下 Kubernetes Manifests。即使机密信息在 Etcd 中不是明文存储,它仍然会以明文形式出现在创建机密信息的 Kubernetes Manifest 中,如果它是明文的,你会把它存在哪里?你不能把 Manifest 推送到 GitHub 上,因为那样你的机密信息就会泄露。因此,许多工程师——坦率地说,甚至 Kubernetes 的官方文档——都强烈推荐使用第三方密钥提供者。目前,Kubernetes 最受欢迎的密钥提供者是 HashiCorp Vault。

总结

在阅读本章时,你的脑海中可能会有一些纯粹的困惑。这没关系——我们在面对安全性问题时,尤其是在 Kubernetes 中,大家都会试图去理解

Kubernetes 安全性是一个高级主题,这也是为什么我们将其放在本书的最后一章来讨论的原因。没有 Kubernetes 安全性,环境将继续成为攻击者的目标。然而,在理解 Kubernetes 安全性之前,你必须充分理解如何在生产中使用 Kubernetes。《第 1 至第七章》的目标是帮助你理解 Kubernetes 在生产中的应用。

下一步目标,一旦你读完本书,就是将你在本章学到的内容以及书中提到的各种方法论,在生产环境中应用,以获得最佳效果。

进一步阅读

posted @ 2025-06-26 15:33  绝不原创的飞龙  阅读(42)  评论(0)    收藏  举报