Rancher-深入浅出-全-
Rancher 深入浅出(全)
原文:
annas-archive.org/md5/3124cbcf4179fcde79eaf7221d1b2413译者:飞龙
前言
Rancher 和 Kubernetes 在推动 DevOps 采用浪潮方面发挥了重要作用,涵盖了本地和云端工作负载。本书将引导您了解 Rancher 和 Kubernetes 的历史及其如何诞生。我们将深入讨论如何设计、构建和管理您的 Rancher 环境。接着,我们将在 Rancher 的基础上,部署多种集群类型,包括 RKE、RKE2、k3s、EKS 和 GKE。对于这些集群类型,我们将讲解它们的工作原理,围绕它们设计解决方案,最后使用 Rancher 部署它们。
接下来,我们将着手让您的集群具备生产环境的准备。包括如何备份和恢复不同类型的集群,如何监控集群和其中托管的应用程序的健康状态。然后,我们将深入讨论如何提供核心 Kubernetes 服务之外的额外服务,包括通过 Longhorn 实现持久存储,通过 OPA Gatekeeper 实现安全性/合规性,以及如何为我们的集群带来动态扩展。
接下来,我们将通过介绍如何在 Rancher/Kubernetes 生态系统中构建和部署应用程序来结束本书,使用诸如 Drone CI 等工具来实现 CI/CD 流水线,并使用 Harbor 来托管构建工件。然后,我们将深入探讨 Helm charts 的深层次话题,以及它们如何为我们的集群带来包管理。最后,我们将通过讨论资源管理和成本报告来结束,以实现将 IT 从一个金钱黑洞转变为一个盈利中心的目标。
本书适用对象
本书主要面向希望使用 Rancher 部署 Kubernetes 的 DevOps 工程师,包括如何使用 RKE(Rancher Kubernetes Engine)和 RKE2/k3s 改变集群的构建与管理方式。它也适合那些希望了解 Kubernetes 和 Rancher 生态系统中 Day 2 任务的人。
本书内容
第一章,Rancher 和 Kubernetes 简介,探讨了 Rancher 的历史及其早期产品,以及 Kubernetes 如何改变了整个局面。
第二章,Rancher 和 Kubernetes 高级架构,讨论了构成 Rancher 生态系统的不同产品,包括 Rancher 服务器、RKE1/2 和 k3s。
第三章,创建单节点 Rancher,深入探讨单节点 Rancher 安装的相关内容,包括使用它的限制以及如何迁移到高可用(HA)架构。
第四章,创建 RKE 和 RKE2 集群,讲解了 RKE1 和 RKE2 的工作原理,以及使用它们架构解决方案的规则。
第五章,在托管 Kubernetes 集群上部署 Rancher,介绍了如何在托管的 Kubernetes 集群上安装 Rancher,如 Google Kubernetes Engine(GKE)、亚马逊 弹性容器服务(EKS) for Kubernetes、Azure Kubernetes Service(AKS)或 Digital Ocean 的 Kubernetes 服务(DOKS)。
第六章,使用 Rancher 创建 RKE 集群,演示了如何使用 Rancher 部署下游 RKE 集群,并讲解此类集群架构的规则。
第七章,使用 Rancher 部署托管集群,介绍了如何利用云提供商通过 Rancher 部署托管 Kubernetes 集群,并通过 Rancher 管理集群的生命周期。
第八章,将外部管理的集群导入 Rancher,展示了如何将任何类型的 Kubernetes 集群导入 Rancher,并展示 Rancher 如何访问导入的集群。
第九章,集群配置备份与恢复,描述了如何使用 etcd 备份来备份 RKE1/2 集群,以及如何从备份中恢复集群。
第十章,监控与日志记录,解释了如何使用 Rancher 监控来部署 Prometheus、Grafana 和告警管理器,以监控集群的健康状况,并介绍了如何使用 Banzai Cloud Logging 来捕获 Pod 日志。
第十一章,使用 Longhorn 将存储引入 Kubernetes,探讨了为何需要在 Kubernetes 集群中使用持久化存储,以及 Longhorn 如何解决这个问题,包括 Longhorn 的工作原理和如何使用 Longhorn 架构解决方案。
第十二章,使用 OPA Gatekeeper 实现安全与合规性,讨论了如何使用 OPA Gatekeeper 和 NeuVector 等工具在 Kubernetes 集群中强制执行标准和安全策略。
第十三章,在 Kubernetes 中进行扩展,深入探讨了如何使用 水平 Pod 自动扩展器(HPA)、垂直 Pod 自动扩展器(VPA)和集群自动扩展器来动态扩展环境。
第十四章,负载均衡器配置与 SSL 证书,解释了如何使用入口和负载均衡器将托管在 Kubernetes 中的应用发布到外部世界。
第十五章,Rancher 和 Kubernetes 故障排除,解释了如何从一些最常见的故障和问题中恢复,包括如何检测并防止它们的发生,以及如何在实验环境中重现这些问题。
第十六章,设置 CI/CD 管道和镜像注册中心,解释了什么是 CI/CD 管道,以及如何使用它来以标准化和受控的流程部署应用程序,并部署 Drone CI 和 Harbor 以支持你的应用程序。
第十七章,创建和使用 Helm Charts,描述了 Helm Charts,以及如何使用 Helm 将应用程序从公共和私有仓库打包并发布到 Kubernetes 集群中。
第十八章,资源管理,解释了如何在 Kubernetes 集群中管理资源,并监控和控制托管在 Kubernetes 中的应用程序成本。
充分利用本书
本书假设你对 Linux 服务器管理有基本了解,包括基本的 Bash 脚本编写、安装软件包以及在大规模环境下自动化任务。此外,我们假设你对大多数云平台,如 AWS、GCP、vSphere 或 Azure,也有基本的了解。

同时,建议有一个实验环境来部署 Rancher 和 RKE1/2 集群。一个重要的提示:大多数云服务提供商提供的试用信用应该足够用来创建小型实验集群。
最后,Kubernetes 和 Rancher 是不断变化的,因此需要记住,随着时间的推移,版本号也需要相应调整。因此,强烈建议在选择版本之前查看每个产品和软件的发布说明。
下载彩色图像
我们还提供了一个 PDF 文件,包含本书中使用的截图/图表的彩色图像。你可以在这里下载:static.packt-cdn.com/downloads/9781803246093_ColorImages.pdf。
下载示例代码文件
你可以从 GitHub 下载本书的示例代码文件,链接为github.com/PacktPublishing/Rancher-Deep-Dive。如果代码有更新,更新内容将同步到现有的 GitHub 仓库。
我们还有其他代码包,来自我们丰富的图书和视频目录,均可在github.com/PacktPublishing/上获取。快来看看吧!
使用的约定
本书中使用了一些文本约定。
文本中的代码:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。例如:"将本地集群重命名为更有帮助的名称,如rancher-prod或rancher-west"是非常常见的。
一段代码的展示方式如下:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm upgrade –install -n monitoring monitoring prometheus-community/kube-prometheus-stack
粗体:表示新术语、重要词汇或您在屏幕上看到的词语。例如,菜单或对话框中的词汇会以这种方式出现在文本中。示例如下:“使用 Rancher 日志记录时,建议通过 Rancher UI 中的应用市场进行部署,进入Cluster Tools并点击Logging应用。”
提示或重要说明
以这种方式出现。
联系我们
我们欢迎读者的反馈。
一般反馈:如果您对本书的任何方面有疑问,请在邮件主题中注明书名,并通过 customercare@packtpub.com 联系我们。
勘误:尽管我们已尽力确保内容的准确性,但错误有时仍会发生。如果您在本书中发现任何错误,我们将非常感谢您向我们报告。请访问 www.packtpub.com/support/errata,选择您的书籍,点击“勘误提交表格”链接,并填写相关细节。
盗版:如果您在互联网上发现我们的作品的任何非法版本,我们将非常感谢您提供该地址或网站名称。请通过版权@packt.com 与我们联系,并提供该材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识并有兴趣撰写或贡献书籍,请访问 authors.packtpub.com。
分享您的想法
阅读完Rancher Deep Dive后,我们很希望听到您的想法!请点击此处直接进入 Amazon 的评论页面并分享您的反馈。
您的评价对我们和技术社区都很重要,它将帮助我们确保提供优质的内容。
第一部分 – Rancher 背景、架构与设计
到本部分结束时,你应该能够架构一个符合你应用需求的 Rancher/Kubernetes 解决方案。
本书的这一部分包括以下章节:
-
第一章,Rancher 和 Kubernetes 简介
-
第二章,Rancher 和 Kubernetes 高级架构
第一章:第一章:Rancher 和 Kubernetes 简介
本章将重点介绍 Rancher 和 Kubernetes 的历史。我们将讲解 Rancher 和 Kubernetes 之前出现过哪些产品和解决方案,以及它们是如何演变成今天的样子的。在本章结束时,你应该能够清楚了解 Rancher 和 Kubernetes 的起源及其核心概念。这个知识对你理解 Rancher 和 Kubernetes 为什么会发展成现在的样子至关重要。
在本章中,我们将涵盖以下主要内容:
-
Rancher Labs 作为一家公司历史
-
Rancher 早期的产品
-
Rancher 的核心理念是什么?
-
Kubernetes 是从哪里来的?
-
Kubernetes 试图解决什么问题?
-
比较 Kubernetes 与 Docker Swarm 和 OpenShift
Rancher Labs 作为一家公司历史
Rancher Labs 于 2014 年在加利福尼亚州库比蒂诺由 Sheng Liang、Shannon Williams、Darren Shepherd 和 Will Chanas 创立。它在 Kubernetes 之前就是一个容器管理平台。从一开始,Rancher 就基于“所有内容都应该是开源且由社区驱动”的理念构建。作为一家开源公司,Rancher 发布的所有产品(包括 Rancher、RancherOS、RKE、K3s、Longhorn 等)都是 100% 开源的。Rancher Labs 的旗舰产品是 Rancher。Rancher 主要是一个容器化工作负载的管理和编排平台,适用于本地和云端。Rancher 能做到这一点是因为它始终保持供应商中立;也就是说,Rancher 可以使用数据中心的物理硬件、AWS 云中的虚拟机,甚至远程位置的 Raspberry Pi 来部署工作负载。
Rancher 早期的产品
当 Rancher v1.0 于 2016 年 3 月发布时,它仅支持 Docker Swarm 和 Rancher Cattle 集群。Docker Swarm 是早期的集群编排工具,它创造了许多今天我们仍在使用的核心概念;例如,应用程序应该被定义为一组可以随时创建和销毁的容器。另一个概念是,容器应该生活在一个虚拟网络中,集群中的所有节点都能访问。你可以通过负载均衡器公开你的容器,在 Docker Swarm 中,这只是一个基本的 TCP 负载均衡器。
在 Rancher 服务器创建的同时,Rancher Labs 还在开发他们自己的 Docker 集群软件,叫做 Cattle,这也是 Rancher 在推出 v1.0 版本时实现正式发布(GA)的时刻。Cattle 的设计旨在解决 Docker Swarm 的局限性,这些局限性涉及多个领域。
第一个问题是网络。最初,Docker Swarm 的网络覆盖是在 node01 上构建的,IP 地址为 192.168.11.22,想要连接到托管在 node02 上的另一个容器,其 IP 地址为 192.168.12.33。网络 Swarm 使用基本的 Linux 路由,将 192.168.12.0/24 子网内的任何数据包路由到 node02,通过 IPsec 隧道传输。这一核心概念至今仍被大多数 Kubernetes 的 CNI 提供者使用。主要问题在于如何随着时间的推移管理这些隧道的健康状况,并处理节点之间的兼容性问题。Cattle 通过将 IPsec 移动到容器中,并为其包装一个管理层来处理隧道的创建、删除和监控,解决了这个问题。
第二个主要问题与负载均衡有关。在 Docker Swarm 中,我们只能使用非常基础的 TCP/层 4 负载均衡。我们没有会话、SSL 或连接管理功能。这是因为负载均衡完全通过 iptable 规则来完成的。Cattle 通过在集群中的所有节点上部署 HAProxy 解决了这个问题。此后,Cattle 使用一个名为 rancher-metadata 的自定义容器,每次创建或删除容器时动态构建 HAProxy 的配置。
第三个问题是存储。在 Docker Swarm 中,除了绑定挂载到主机文件系统外,没有其他存储选项。这意味着你必须创建一个集群文件系统或共享网络,然后手动将它们映射到所有 Docker 主机。Cattle 通过创建 rancher-nfs 来解决这个问题,这是一款可以在容器内挂载 NFS 共享并创建绑定挂载的工具。随着 Rancher 的发展,其他存储提供商也被加入其中,如 AWS 和 VMware。
随着时间的推移,Rancher 迈出了下一步的重要发展,当时增加了身份验证提供商,因为 Rancher 通过集成外部身份验证提供商(如 Active Directory、LDAP 和 GitHub)提供对 Rancher 管理的集群的访问。这是 Rancher 独有的,因为 Kubernetes 至今仍与外部身份验证提供商集成得不好。
Rancher 的核心理念是什么?
Rancher 的构建基于几个核心设计原则:
-
开源:构成 Rancher 或与 Rancher 一起提供的所有代码、组件和服务必须是开源的。因此,Rancher 拥有一个庞大的社区,用户提供反馈、文档,并贡献代码。
-
无锁定:Rancher 设计时考虑了无供应商锁定,包括在 Rancher 内部也不被锁定。随着容器化技术快速演变,Rancher 需要使用户能够尽可能少地影响下改变技术。Rancher 提供的所有产品和解决方案的核心要求是,它们可以与或无 Rancher 服务器一起使用。Longhorn 就是一个例子;Rancher 和 Longhorn 之间没有任何依赖关系。这意味着用户可以随时卸载其中一个而不影响另一个。这包括卸载 Rancher 而不会丢失集群的能力。Rancher 通过实施一个过程,让用户直接接管集群的管理并将 Rancher 排除在外来实现了这一点。
-
clusters.management.cattle.io和与nodes.management.cattle.io下的对象一样,它们仅限于具有集群 ID 的命名空间。因此,用户和应用程序可以直接查询 Rancher 对象而无需与 Rancher 的 API 交互。这种改变的原因主要与可伸缩性有关。使用 Cattle 和 MySQL 时,所有与集群相关的任务都必须返回到 Rancher 服务器。因此,随着集群规模和集群数量的增加,必须同时扩展 Rancher 服务器。这导致客户遇到诸如“任务风暴”的问题,即集群中的单个节点重新启动会导致发送到 Rancher 服务器的请求洪水,进而导致其他任务超时,然后引起更多请求。最终,你只能关闭一切,然后慢慢重新启动。 -
一切都是无状态的:因为一切都是 Kubernetes 对象,Rancher 不需要数据库。所有 Rancher pods 都是无状态的,意味着可以因任何原因随时销毁。此外,Rancher 可依赖 Kubernetes 控制器简单地启动新的 pods 而无需 Rancher 做任何事情。
-
控制器模型:所有 Rancher 服务都围绕 Kubernetes 控制器模型设计。控制循环始终在运行,监视当前状态并将其与期望状态进行比较。如果发现任何差异,它会应用应用程序逻辑使当前状态与期望状态匹配。同时,它与 Kubernetes 核心组件使用相同的领导选举过程。这确保只有一个真实数据来源,并确保某些控制器在故障后能够进行故障切换。
Kubernetes 是从哪里来的?
Kubernetes 这个名字来源于希腊语,意为 舵手 或 驾驶员。Kubernetes 被简称为 k8s,因为在 K 和 S 之间有 8 个字母。最初,工程师们在 Google 基于一个内部项目 Borg 创建了 Kubernetes。Google 的 Borg 系统是一个集群管理器,旨在运行 Google 内部的应用程序。这些应用程序由成千上万的微服务组成,这些微服务托管在全球的集群中,每个集群由成千上万台机器组成。Borg 提供了三个主要好处。第一个好处是资源和故障管理的抽象化,使得应用程序设计者可以专注于应用程序的开发。第二个好处是其设计的高可靠性和可用性。Borg 的所有组件从一开始就设计为高可用性。这是通过使应用程序无状态来实现的。这样,任何组件都可以在任何时间因任何原因被销毁而不会影响可用性,同时还可以横向扩展到跨集群的数百个实例。第三个好处是有效的工作负载;Borg 被设计为对计算资源的管理开销最小化。
Kubernetes 可以直接追溯到 Borg,因为许多曾在 Google 从事 Kubernetes 开发的工程师,之前都参与过 Borg 项目的开发。由于这一点,许多 Borg 的核心概念被融入到了 Kubernetes 中,唯一的实际区别在于 Borg 是为 Google 定制的,而 Kubernetes 需要更加通用和灵活。然而,从 Borg 衍生出的四个主要特性如下:
- Pod:Pod 是 Kubernetes 中最小的调度单元。这个对象可以包含一个或多个容器,Pod 中的每个容器共享一些资源,如 IP 地址、卷和其他本地资源。其主要设计原则之一是,Pod 应该是可丢弃的,且在创建后不应发生变化。另一个主要原则是所有应用程序配置应该在 Pod 层面进行处理。例如,数据库连接字符串应作为 Pod 定义的一部分,而不是在应用程序代码中定义。这样,任何应用程序配置的更改都不需要重新编译和重新部署代码。此外,Pod 从 Borg 中引入了配对进程的概念,经典示例是日志收集器。这是因为,通常情况下,容器内部应该只运行一个主进程。
一个例子是 Web 服务器:服务器会生成日志,但如何将这些日志传输到像 Splunk 这样的日志服务器呢?一种选择是向您的应用程序 Pod 添加一个自定义代理,这很简单。但现在,您在容器中管理多个进程,这会导致环境中存在重复代码,最重要的是,您现在必须同时为主应用程序和这个附加的日志代理进行错误处理。这时,Sidecar 的概念就派上用场,它允许您以可重复且一致的方式将容器组合在一起,形成一个 Pod。
-
服务:Borg 的主要作用之一是应用程序和 Pod 的生命周期管理。因此,Pod 的名称和 IP 地址是短暂的,可能随时因任何原因发生变化。于是,服务的概念应运而生,作为一种抽象层,您可以通过使用标签来定义一个服务对象,该对象引用一个或多个 Pod。Kubernetes 将处理服务记录与其 Pod 的映射。这些好处在于,能够在组成该服务的 Pod 之间负载均衡流量。服务记录允许 Kubernetes 添加或移除 Pod 而不干扰应用程序,因为服务与 Pod 的映射可以简单地改变,而请求的客户端并不察觉。
-
"application=web_frontend," "environment=production," "department=marketing"。请注意,这些键是不同的标签选择规则,可以创建服务记录。这还有一个副作用,就是使得报告和使用跟踪变得更加容易。 -
每个 Pod 都有一个 IP:当 Borg 创建时,宿主机上的所有容器共享宿主机的 IP 地址,并通过不同的端口来区分每个容器。这使得 Borg 能够使用标准的 IP 网络。然而,这也给基础设施和应用程序团队带来了负担,因为 Borg 需要为容器调度端口。这要求应用程序为其容器预定义一组端口。
Kubernetes 试图解决什么问题?
Kubernetes 的设计旨在解决几个问题,主要包括以下几个方面:
-
可用性:从应用程序所有者到开发人员,再到最终用户,每个人都期望系统实现 24x7x365 的正常运行时间,停机和故障在 IT 领域几乎是禁忌。随着容器化和微服务的普及,这一标准变得更高。Kubernetes 通过在节点间调度容器,并使用期望状态与实际状态的对比来解决这一问题。其理念是,任何故障都只是实际状态发生变化,触发控制器调度 Pods,直到实际状态与期望状态匹配。
-
CI/CD:传统开发使用单片开发进行,每年发布几次重大版本。这需要大量开发人员几个月来测试其发布,并构建大量手动流程来部署其应用程序。Kubernetes 通过由期望状态和配置文件驱动来解决此问题。这意味着实施 DevOps 工作流程,允许开发人员自动化步骤并持续集成、测试和部署代码。所有这些都将使团队能够快速失败和快速修复。
-
效率:传统 IT 是公司投入资金的黑洞之一。其背后的原因之一是高可用性。对于一个应用程序,您至少需要为生产应用程序的每个组件的两个服务器。此外,您还需要为每个较低的环境(如 DEV、QAS、Test 等)的每个服务器额外的服务器。如今,公司希望尽可能高效地利用其 IT 支出。Kubernetes 通过简化启动环境来满足这一需求。通过 CI/CD,您可以简单地创建一个新的命名空间,部署您的应用程序,运行您想要的任何测试,然后销毁该命名空间以回收其资源。
-
自动化扩展:传统上,您会根据最高工作负载来设计和构建环境。例如,假设您的应用主要在工作时间繁忙,在非高峰时段处于空闲状态。您会因为在 100%和 1%的计算资源上支付相同费用而浪费资金。然而,传统上,要花费几天甚至几周来启动新服务器,安装您的应用程序,配置它,最后更新负载均衡器。这使得无法快速扩展或收缩。因此,一些公司决定只进行扩展并保持状态。Kubernetes 通过简单更改期望状态来解决此问题,从而轻松实现扩展或收缩。
假设一个应用程序当前有两个 Web 服务器,并且您希望添加一个 Pod 来处理负载。只需将副本数更改为三,因为当前状态与期望状态不匹配。控制器会启动并开始启动一个新的 Pod。可以使用 Kubernetes 内置的水平 Pod 自动缩放器(HPA)自动化此过程,该缩放器使用多种度量标准,从简单的 CPU 和内存到整体应用程序响应时间的自定义度量标准。此外,Kubernetes 还可以使用其垂直 Pod 自动缩放器(VPA)随时间自动调整您的 CPU 和内存限制。随后,Kubernetes 可以使用节点扩展来根据需要动态添加和删除节点到您的集群中。这意味着白天您的应用程序可能有 10 个 Pod 和 10 个工作节点,但在下班后,它可能只有 1 个 Pod 和 1 个工作节点。这意味着您可以节省 16 小时每天加上周末的 9 个节点的成本,所有这些都无需您的应用程序做任何事情。
比较 Kubernetes 与 Docker Swarm 和 OpenShift
我们将在接下来的部分进行比较。
Kubernetes 与 Docker Swarm
Kubernetes 和 Docker Swarm 是两个开源容器编排平台,它们具有几个相同的核心功能,但也有显著差异。
可扩展性
Kubernetes 是一个复杂的系统,包含多个组件,这些组件需要协同工作才能使集群正常运行,因此它的设置和管理较为复杂。Kubernetes 需要你管理一个数据库(etcd),包括备份和为所有不同组件创建 SSL 证书。
Docker Swarm 要简单得多,所有功能都包含在 Docker 中。你所需要做的就是创建一个管理节点并将其他节点加入 Swarm。但由于一切都已集成,你不能获得诸如自动伸缩、节点配置等高级功能。
网络
Kubernetes 使用平面网络模型,所有 Pod 共享一个大型网络子网,并且使用另一个网络来创建服务。此外,Kubernetes 允许你自定义和更改网络提供商。例如,如果你不喜欢某个特定的网络,或者无法进行网络级加密,你可以切换到另一个提供商,比如 Weave,它可以进行网络加密。
Docker Swarm 网络是基础。默认情况下,Docker Swarm 在集群中的所有节点之间创建 IPsec 隧道,并使用 IPsec 进行加密。由于现代 CPU 提供对 AES 的硬件加速,因此这种速度可能不错;然而,依赖于你的硬件和工作负载,你仍然可能会遇到性能下降的问题。此外,在 Docker Swarm 中,你无法切换网络提供商,因为你只能使用默认提供的网络。
应用部署
Kubernetes 使用 YAML 和其 API 使用户能够定义应用程序及其资源。因为如此,像 Helm 这样的工具允许应用程序所有者以模板化格式定义他们的应用程序,这使得应用程序能够以用户友好的格式发布,称为 Helm charts。
Docker Swarm 基于 Docker CLI,使用最小的 API 进行管理。唯一的包管理工具是 Docker Compose,但由于其定制性有限且部署过程需要大量手动工作,因此并未被广泛采用。
高可用性
Kubernetes 从一开始就被构建为高度可用,并具备处理各种故障的能力,包括通过运行命令来检查 Pod 的健康状况,进而检测不健康的 Pod。这包括所有管理组件,如 Kube-scheduler、Kube-apiserver 等。这些组件设计为无状态,并内置领导选举和故障转移管理。
Docker Swarm 主要通过其在节点之间克隆服务的能力来实现高可用性,在出现故障时,Swarm 管理节点会处于主备配置。
负载均衡
Kubernetes Pod 可以通过浅层的 4 层(TCP/UDP 模式)负载均衡服务暴露出来。然后,对于外部访问,Kubernetes 有两种选择。第一种是 node-port,它作为一种简单的端口转发方法,将节点的 IP 地址转发到内部服务记录。第二种适用于更复杂的应用,Kubernetes 可以使用 ingress 控制器提供 7 层(HTTP/HTTPS 模式)负载均衡、路由和 SSL 管理。
Docker Swarm 的负载均衡基于 DNS,这意味着 Swarm 使用轮询 DNS 将传入请求分配到容器之间。因此,Docker Swarm 仅限于 4 层,并没有办法使用任何更高级的功能,如 SSL 和基于主机的路由。
管理
Kubernetes 提供了多个工具来管理集群及其应用,包括用于命令行访问的 kubectl,以及通过 Kubernetes 仪表盘服务提供的 Web UI。它甚至还提供了更高层次的 UI,如 Rancher 和 Lens。这是因为 Kubernetes 是围绕一个高度灵活的 REST API 构建的。这意味着应用程序和用户可以轻松地将他们的工具集成到 Kubernetes 中。
Docker Swarm 不提供内建的仪表盘。虽然有一些第三方仪表盘,如 Swarmpit,但这些工具的采用率很低,且缺乏标准化。
安全性
Kubernetes 提供了内建的 RBAC 模型,允许对 Kubernetes 资源进行细粒度的控制。例如,你可以授予一个 Pod 仅访问一个密钥,而另一个 Pod 则可以访问命名空间中的所有密钥。这是因为 Kubernetes 的授权是基于 SSL 证书和令牌进行身份验证的。这使得 Kubernetes 可以简单地将证书和令牌作为文件挂载到 Pod 内。这使得应用程序可以轻松访问 Kubernetes API。
Docker Swarm 的安全模型主要基于网络,使用 TLS(mTLS),且缺少许多细粒度的控制和集成,Docker Swarm 只具有内建的角色,如无权限、只读、受限控制、调度器和完全控制。这是因为 Docker Swarm 的访问模型是为集群管理而设计的,而不是应用集成。此外,最初 Docker API 仅支持基本身份验证。
Kubernetes 与 OpenShift
Kubernetes 和 OpenShift 共享许多功能和架构。两者都遵循相同的核心设计原则,但它们在执行方式上有所不同。
网络
Kubernetes 本身没有内建的网络解决方案,而是依赖第三方插件,如 canal、flannel 和 Weave,为集群提供网络服务。
OpenShift 提供了一个内建的网络解决方案,称为 Open vSwitch。这是一个基于 VXLAN 的软件定义网络栈,可以轻松地与 RedHat 的其他产品集成。虽然也支持第三方网络插件,但这些插件的支持有限且较难维护。
应用程序部署
Kubernetes 采用尽可能灵活的方式来部署应用程序到集群,允许用户部署他们选择的任何 Linux 发行版,包括支持基于 Windows 的镜像和节点。这是因为 Kubernetes 是与供应商无关的。
OpenShift 采用了将整个技术栈标准化的方式,基于如 RHEL 这样的 RedHat 产品作为节点的操作系统。从技术上讲,OpenShift 运行在其他 Linux 发行版上(如 Ubuntu)几乎没有任何障碍。此外,OpenShift 对允许在集群中运行的容器镜像类型设置了限制。同样,从技术角度来看,用户并没有太多障碍去在 OpenShift 集群中部署 Ubuntu 镜像,但他们很可能会遇到与可支持性相关的问题。
安全性
Kubernetes 拥有一个内建的工具用于 pod 级别的安全性,称为 Pod 安全策略(PSPs)。PSPs 用于执行对 pod 的限制,例如阻止 pod 以 root 用户身份运行或绑定到主机的文件系统。由于该工具存在多个限制,PSPs 在 v1.21 版本中被弃用。现在,PSPs 被一个名为 OPA Gatekeeper 的第三方工具取代,它允许使用相同的安全规则,但采用不同的执行模型。
OpenShift 在安全性方面有着更加严格的理念,默认提供安全选项,并且不像 Kubernetes 那样需要进行集群硬化。
摘要
在本章中,我们了解了 Rancher 的历史及其起步过程。接下来,我们讨论了 Rancher 的核心理念以及它如何围绕 Kubernetes 进行设计。然后,我们回顾了 Kubernetes 的起源以及它的核心理念。接着,我们深入探讨了 Kubernetes 想要解决的核心问题。最后,我们分析了 Kubernetes、Docker Swarm 和 OpenShift 的优缺点。
在下一章中,我们将介绍 Rancher 及其产品(包括 RKE、K3s 和 RancherD)的高层架构和流程。
第二章:第二章:Rancher 和 Kubernetes 高级架构
本章将介绍Rancher、Rancher Kubernetes Engine(RKE)、RKE2(也称为RKE 政府版)、K3s和RancherD的高级过程。我们将讨论这些产品的核心设计理念,并探讨它们的不同之处。我们将深入了解 Rancher 的高级架构,并查看 Rancher 服务器的 Pod 如何使用 Cattle 代理(包括 Cattle-cluster-agent 和 Cattle-node-agent)与下游集群进行通信。我们还将探讨 Rancher 服务器如何使用 RKE,以及 Rancher-machine 如何为下游节点和 Kubernetes(K8s)集群提供资源。接下来,我们将介绍 K8s 的高级架构,包括kube-api-server、kube-controller-manager和kube-scheduler。我们还将讨论这些组件如何保持集群的状态。最后,我们将检查最终用户如何更改期望状态,以及控制器如何更新当前状态。
本章我们将覆盖以下主要内容:
-
Rancher 服务器是什么?
-
什么是 RKE 和 RKE2?
-
什么是 K3s(比 K8s 少五个)?
-
什么是 RancherD?
-
Rancher 服务器的 Pod 中运行了哪些控制器?
-
Cattle 代理做什么?
-
Rancher 如何提供节点和集群?
-
什么是 kube-apiserver、kube-controller-manager、kube-scheduler、etcd 和 kubelet?
-
当前状态和期望状态是如何工作的?
Rancher 服务器是什么?
Rancher 服务器构成了 Rancher 生态系统的核心,它包含了任何其他组件、产品或工具所需的几乎所有内容,这些组件、产品或工具依赖于或通过 Rancher API 连接到 Rancher 服务器。Rancher 服务器通常简称为Rancher,在本节中,当我说到Rancher时,我指的就是Rancher 服务器。
Rancher 的核心是其 API。Rancher API建立在一个名为Norman的自定义 API 框架之上,Norman 充当 Rancher API 和 K8s API 之间的翻译层。Rancher 中的一切都通过 Rancher 或 K8s API 进行通信,包括 100% 由 API 驱动的 Rancher 用户界面(UI)。
那么,如何连接到 Rancher API?Rancher API 是一个标准的 CustomResource 对象。当然,因为所有内容都存储在 K8s 的 CustomResource 对象中,所以 Rancher 请求流程是无状态的,不需要会话持久性。最后,一旦 CustomResource 对象被创建、修改或删除,该对象类型的控制器将接管并处理该请求。我们将在本章后面深入探讨不同的控制器。
什么是 RKE 和 RKE2?
我需要什么,是 RKE 还是 RKE2?传统上,在构建 K8s 集群时,您需要执行几个步骤。首先,您需要生成根CA 密钥以及各个 K8s 组件的证书,并将它们推送到集群中每台服务器上。其次,您需要安装/配置etcd,这包括在管理节点上设置systemd服务。接下来,您需要启动 etcd 集群,并验证所有 etcd 节点是否正确地进行通信和复制。此时,您将安装 kube-apiserver 并将其连接回 etcd 集群。最后,您需要安装 kube-controller-manager 和 kube-scheduler,并将它们连接回 kube-apiserver 对象。如果您想要启动集群的控制平面,您还需要更多步骤将工作节点加入集群。
这个过程被称为K8s the hard way,之所以叫这个名字,是因为这个过程非常复杂,并且随着时间的推移可能会发生变化。在 K8s 的早期,这是创建 K8s 集群的唯一方式。正因为如此,用户需要编写大型脚本或Ansible Playbooks来创建 K8s 集群。这些脚本需要大量的关注和维护才能顺利启动,并且随着 K8s 不断变化,还需要更多的工作来保持其正常运行。
Rancher 看到了这个问题,并意识到为了让 K8s 成为主流,构建集群需要对最终用户和 Rancher 服务器来说是极其简单的。最初,在 Rancher v1.6 时代,Rancher 会使用它的容器集群软件Cattle来构建 K8s 集群。因此,所需的一切都必须作为容器运行,这也是 RKE 的起点。
那么,什么是 RKE 呢?
RKE 是 Rancher 的集群编排工具,用于在各种操作系统上创建和管理云原生计算基金会(CNCF)认证的 K8s 集群,并支持多种配置。RKE 的核心概念是,构成 K8s 集群的所有组件都应该完全在Docker容器中运行。因此,RKE 不关心它部署在哪个操作系统上,只要它运行在 Docker 容器中即可。这是因为 RKE 并不会在主机上安装二进制文件、配置服务或类似的操作。
RKE 是如何工作的?
RKE 是一个cluster.yml(见图 2.1)。RKE 随后使用该配置文件来创建启动集群所需的所有容器,即 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 和 kubelet。有关节点和集群的更多详细信息,请参阅本章中的Rancher 如何配置节点和集群?部分。

图 2.1 – cluster.yaml 文件中的代码片段
什么是 RKE2?
RKE2 是 Rancher 的下一代 K8s 解决方案,也被称为 RKE Government。RKE2 的设计旨在更新和解决 RKE 的一些不足,并且它还引入了 K3s 的超简单安装方法来提高功能性。RKE2 也是一个完全 CNCF 认证的 K8s 发行版。不过,RKE2 是专为 Rancher 的美国联邦政府及其客户创建的,因为他们对 K8s 的使用有若干特别要求——第一个要求是默认情况下它必须具有高度安全性。
在设置 RKE 时,你必须遵循加固指南,并采取若干手动步骤以符合CIS 基准。而 RKE2 则设计成在几乎不需要集群管理员采取任何行动的情况下就能确保安全。美国联邦客户需要他们的 K8s 集群是启用 FIPS的(FIPS代表美国联邦信息处理标准)。此外,由于 RKE2 是基于 K3s 构建的,它继承了 K3s 的多个特性——其中第一个是支持ARM64架构的系统。所以,如果你愿意,可以在Raspberry Pi上设置 RKE2。这为用户提供了在同一个集群中混合使用 ARM64 和AMD64节点的灵活性,这意味着客户可以在集群内运行如Drone 持续集成(CI)平台等多架构构建的工作负载。它还为低功耗和成本效益高的 ARM64 节点提供支持。
从 K3s 继承的第二个特性是自引导。在 RKE 中,你需要定义集群的 YAML 文件,然后使用 RKE 二进制文件来尝试创建和管理集群。但在 RKE2 中,一旦创建了第一个节点,所有其他节点只需通过在主节点上运行的注册端点加入集群。需要注意的是,这确实需要一个外部负载均衡器或轮询 DNS 记录才能成功。因为 RKE2 可以自我管理,所以它允许你执行非常酷的任务,例如使用 kubectl 定义 K8s 升级,并让集群自行处理。
RKE2 从 K3s 继承的第三个特性是内置的kubectl -n kube-system get pods,你可以查看你的 etcd 容器,甚至可以像操作其他 Pod 一样,打开一个 shell 或者捕获日志。
最后但同样重要的是,RKE2 的最关键特性是它是完全开源的,没有付费墙——就像 Rancher 的其他所有产品一样。
K3s 是什么(比 K8s 少五个版本)?
K3s 是一个完全通过 CNCF 认证的 K8s 发行版。这意味着在 K3s 中,你部署的 YAML 文件就是一个标准的 K8s 集群,只不过它被部署在 K3s 集群中。K3s 的诞生是因为传统的 K8s 集群——甚至是 RKE 集群——是为大规模运行而设计的,意味着它们需要三个 etcd 节点、两个控制平面节点以及三个或更多的工作节点,才能配置一个标准的集群。在这种情况下,节点的最小配置大约是四个核心,etcd 对象和控制平面节点需要 8 GB 的内存,而工作节点需要两个核心和 4 GB 的内存。这些仅仅是在谈论像 IE 50 节点集群这样规模的 K8s 集群时的背景要求,工作节点的配置可能为 64 个核心和 512 GB 的内存。但当你开始考虑在边缘部署 K8s 时,考虑到物理空间、电力和计算资源都很紧张,标准的 K8s 和 RKE 就显得过于庞大。所以,问题是:我们如何缩小 K8s 的规模?
K3s 基于以下核心原则:没有遗留代码、重复代码或多余的内容。对于 RKE 和其他标准的 K8s 发行版,每个组件都是独立的代码,并有各自的运行时。在 Rancher,他们问了自己一个问题:
嘿,这里有很多重复的代码在运行。如果我们将 kube-apiserver、kube-controller-manager、kube-scheduler 和 kubelet 合并成一个单一的二进制文件会怎么样?
于是,K3s 就这样诞生了。K3s 只有 master 节点和 worker 节点,主节点运行所有的核心组件。下一个重大的突破是他们对 etcd 的处理。etcd 对象并不小,它吃内存的速度快得像要过时一样,而且在单节点集群中它并不好用。这时,kind 就进入了画面。
这种数据库适配器使得标准SQL数据库如SQLite3、MySQL或Postgres看起来像是一个 etcd 数据库。因此,从 kube-apiserver 的角度来看,它是在与 etcd 集群通信。CPU 和内存占用的足迹要小得多,因为你可以运行像 SQLite3 这样的数据库代替 etcd。值得注意的是,Rancher 并未定制任何核心组件中的标准 K8s 库,这使得 K3s 可以与上游 K8s 保持同步。K3s 下一个重大节省领域是树内存储驱动程序和云提供商。上游 K8s 在核心组件中内置了多个存储驱动程序。例如,RKE 有存储驱动程序,允许 K8s 连接到AWS API,并直接为 Pod 提供Amazon EBS卷存储。如果在 AWS 上运行,这非常棒,但如果在VMware上运行,这段代码将浪费资源。同样的情况也适用于 VMware 的vSphere,它有一个存储提供程序将虚拟机磁盘 (VMDKs)挂载到节点上。这个想法是大多数这些存储和云提供商是不被使用的。例如,如果我在 Amazon 上运行集群,为什么我需要 Azure 的库和工具?此外,有替代方案可以作为 Pod 部署,而不是作为内置功能。此外,大多数主要的存储提供商正在转向树外的供应方式。因此,K3s 将它们移除了。这消除了显著的开销。由于所有这些优化,K3s 集群可以放在一个 40 MB 的二进制文件中,并在只有 512 MB RAM 的节点上运行。
K3s 对 K8s 的另一个重要改变是,它应该非常简单地启动一个 K3s 集群。例如,创建一个单节点的 K3s 集群只需要运行curl -sfL https://get.k3s.io | sh -命令,唯一的依赖是它在安装了curl的 Linux ARM64 或 AMD64 操作系统上。由于这种易用性,K3s 经常被部署在单节点集群中,用户希望使用 K8s 提供的所有管理工具,但规模不大。例如,开发人员可能会在他们的笔记本上通过虚拟机(VM)快速部署一个 K3s 集群,以部署他们的应用程序,就像在生产 K8s 集群中一样。
K3s 的另一个优秀用例是在零售环境中部署,可能在全国甚至全球各地都有数百甚至数千个位置,每个位置上都运行着一个小型 PC 上的单个 K3s 节点。K3s 在这种情况下非常有帮助,因为它体积很小,因此常见的问题如慢速互联网连接并不是大问题,此外,即使失去与企业数据中心的连接,K3s 仍然可以继续运行。对于 K3s 更加特别的部署是在偏远地区的风力发电机上,仅有一个长期演进(LTE)连接用于互联网访问。这些是 K3s 设计的部署类型。
RancherD 是什么?
在 Linux AMD64 或 ARM64 服务器上执行 curl -sfL https://get.rancher.io | sh – 命令。这个二进制文件类似于 RKE2,但经过优化以托管 Rancher 服务器。RancherD 还包括额外的工具来支持 Rancher 服务器应用程序。例如,rancherd reset-admin 命令将重置 Rancher 服务器的管理员密码。
若要在常规的 RKE 或 RKE2 集群中更改此密码,你需要找到 Rancher 服务器 pod 并进入容器的 shell。然后你可以运行 reset-admin 命令。RancherD 背后的主要理念是让管理 Rancher 变得非常简单。它通过使用 RKE2 Helm 操作符来处理 Rancher 服务器 pod 的部署实现这一点。而且因为它使用的是你在 RKE 集群中也会使用的相同 Helm chart,所有定制化选项仍然可用(最棒的功能是 SSL 证书的管理便利性)。在标准的 Rancher 服务器部署中,你必须配置并管理支持 Rancher API 的 SSL 证书。当使用内部签名证书时,这可能会很麻烦,因为你需要编辑集群内的一个 secret,这对新手 K8s 用户来说可能很困难。RancherD 通过简单地让用户将证书文件放入 /etc/rancher/ssl/ 目录中的一个 RancherD 节点来解决这个问题,此时它会接管该过程并为你处理更新。大多数时候,当你不想管理托管 Rancher 的 K8s 集群,但又不能使用诸如 AWS EKS、Azure AKS 或 Google GKE 等托管 K8s 选项,或者如果你需要管理大量不同的 Rancher 安装时,你将使用 RancherD。例如,如果你在托管环境中提供 Rancher 作为服务,你可能会使用 RancherD 来简化大规模管理这些集群。
Rancher 服务器的 pod 内运行了哪些控制器?
Rancher 由一组 pod 组成——默认情况下是三个 pod——它们在 K8s 集群中运行。这些 pod 可以通过默认的 ingress-nginx 入口控制器处理请求,并使用 Norman 将 Rancher API 请求转换为 K8s API 请求,以访问 Rancher 使用的自定义资源对象。但是,Rancher 服务器的 pod 还托管多个控制器,主要控制器如下:
-
TokenReview请求来自 K8s。该服务会调用 LDAP 服务器,验证用户名和密码。如果验证通过,服务将以200 OK响应返回,所有其他响应码表示身份验证失败。由于这一点,设置过程可能非常复杂且不可靠。因此,Rancher 选择了构建其控制器的方法,通过外部身份验证提供者(如 LDAP、AD、GitHub、Okta 等)验证用户名和密码。一旦用户验证通过,Rancher 将为用户提供一个承载令牌,用户可以直接用它向 K8s API 进行身份验证。控制器通过提前在下游集群中创建匹配的服务帐户、角色和角色绑定来实现这一点。控制器还通过 Rancher 的 项目 概念提供一些更高层次的控制。你可以定义一组命名空间作为一个 项目,并在项目级别管理权限,而不是仅在集群或命名空间级别管理权限。 -
Rancher 目录控制器:该控制器负责管理 Rancher 内部的目录。那么什么是 目录 呢?Rancher 使用 目录 的概念作为 Helm 图表的存储库。Rancher 称它们为目录,因为它们为用户提供了一个可以部署到集群中的应用程序目录。默认目录包含一些很棒的应用程序,包括 WordPress、MySQL、Rancher、Longhorn、Datadog Cluster Agent 等等。所有这些目录都汇集在 Rancher 中,组成了所谓的 应用和市场 功能,允许用户将基于 Helm 的应用程序部署到集群中。你还可以将自己的存储库添加为目录,这对于希望为其应用程序团队提供标准化工具集的 DevOps 团队来说非常有用。例如,如果应用程序团队希望拥有自己的监控系统,他们可以根据自己的偏好修改和调整系统。你可能会创建一个 Prometheus Helm 图表,提供基本配置,应用程序团队只需点击就可以在其集群上部署。
使用 Helm 图表的另一个很好的例子是在可能有一个主应用程序的环境中——例如,业务核心应用程序,其他团队必须编写应用程序以连接并与其一起工作。你可以为这个单体应用程序创建一个 Helm 图表,应用程序团队可以快速启动进行集成测试,然后关闭以节省成本。在这种情况下,所有这些操作将由 Rancher 目录控制器管理,该控制器负责缓存目录(出于速度考虑),以及对于传统应用程序,部署该应用程序,即在 Rancher 服务器 pod 内运行 helm install 命令。
但是,在 Rancher v2.6 中,这个过程已转移到 Fleet 来处理部署过程,Fleet 将在下游集群上启动一个 Helm 操作员 Pod 并运行 Helm 命令。请注意,这对速度、可扩展性和灵活性非常有利,因为 Fleet 为您提供了许多自定义 Helm 图表的选项,是 Rancher 大规模 DevOps 的一部分。Fleet 的设计能够一次管理最多百万个集群。需要注意的是,Rancher 目录控制器仅在 Rancher 领导者 Pod 上运行。如果该 Pod 被删除或丢失,则需要重新构建缓存,但这个过程通常只需要几分钟。此控制器还会按照计划(默认每 6 小时)同步缓存,但同步过程可以被强制更新,执行helm repo update…命令,但以 Go 代码的形式运行。
-
cluster.yaml和cluster.rkestate文件由您处理,并在 Rancher 领导者 Pod 内部运行rke up。请注意,在故障排除集群时,如果在更新状态问题时卡住,这是我们最常查看的控制器。更多关于该控制器如何工作的细节,请参见本章稍后的Rancher 如何配置节点和集群?部分。 -
Rancher 节点控制器:该控制器负责管理 Rancher 配置的节点。该控制器仅用于 Rancher 配置的集群,在虚拟化平台上,如Amazon EC2、Google GCP、VMware vSphere 等。该控制器构建在 Rancher 机器的 Go 代码之上,而 Rancher 机器本身是基于Docker Machine构建的。该控制器的主要功能是处理节点池中虚拟机的创建和删除。更多关于此过程的细节,请参见本章稍后的Rancher 如何配置节点和集群?部分。注意,在故障排除节点配置错误时,如 SSH 超时或配置验证错误时,这是我们最常查看的控制器。
-
rancher-pipelines.yml文件用于配置管道。注意
从 Rancher v2.5 开始,推荐使用基于 Git 的部署管道来处理 Rancher 持续交付,该功能由 Fleet 提供支持。因此,这个控制器在 Rancher v2.6 中被移除了。
-
vanilla上游 Prometheus 监控栈部署,而不是 Rancher 定制的 Prometheus 部署。 -
Rancher 日志控制器:这个控制器管理 Rancher UI 中日志系统与 Banzai Cloud Logging 操作员之间的集成。这个控制器是一个翻译层,允许用户通过 Rancher UI 定义日志流和集群日志流(ClusterFlows),并将其转换为 自定义资源定义(CRD)对象,这些对象被 Banzai Cloud Logging 操作员用于配置应用程序和集群级别的日志记录。在 Rancher v2.5 之前,Rancher 使用多个不同的日志提供者,包括 Syslog、Splunk、Apache Kafka 和 Fluentd。然而,这是一种定制的 Rancher 解决方案,灵活性较差。因此,作为日志 v2 迁移的一部分,一切都转移到了 Banzai,以更好地与行业的发展趋势对接。
-
istioctl二进制文件或 Istio 操作员。这个控制器还负责部署Kiali,用于在服务网格中绘制流量图。这使得用户可以看到哪些应用程序连接到其他应用程序,包括 pod 之间的流量速率和延迟。这对应用程序所有者和团队来说非常有价值。 -
--insecure-bind-address标志设置为除localhost以外的其他值,在 kube-apiserver 上。请注意,这个设置允许请求绕过身份验证和授权模块,且不能暴露到节点外部。在这种情况下,Sonobuoy 将收集这个设置,然后 kube-bench 会将该值标记为检查失败。最后,rancher-cis-benchmark 工具会将所有检查汇总成一个优秀的报告,并通过电子邮件发送给安全团队。
Cattle 代理做什么?
Rancher 在下游集群(即 Rancher 正在管理的集群)上部署的 Cattle 代理为 Rancher 提供了访问该集群及其节点的权限。此操作通过两组不同的 Pod 完成:
-
localhost。这个隧道将允许 Rancher 服务器 Pod 与下游集群建立连接。因此,Rancher 无需在 Rancher 服务器和下游集群之间打开防火墙规则,也无需端口转发,这通常会带来安全问题。这个 WebSocket 连接由 Rancher 和 Cattle-cluster-agent 保持打开,因为如果连接中断,Rancher 将失去对集群的访问权限,直到连接恢复。 -
Cattle-node-agent:这个代理作为 DaemonSet 在所有节点上运行,并具有忽略几乎所有内容的容忍性。这个 Pod 使用与前面示例相同的 WebSocket 连接方式,并通过 TCP 隧道连接回 Rancher。不过,RKE 在 Rancher 服务器 Pod 内部使用此连接,以提供与节点上运行的 Docker 引擎的套接字连接。这是 RKE 启动非 K8s 容器以构成 RKE 集群所需的。
注意
Cattle-node-agent 仅在 Rancher 管理的集群中使用,也就是说,当 Rancher 使用 RKE 构建集群时需要使用该工具。对于导入的集群,如 Amazon EKS 集群,则不需要 Cattle-node-agent。
两个代理都使用 HTTPS 连接到 Rancher API。它们通过将一些环境变量传递到 pod 中来实现。第一个变量是 CATTLE_SERVER;该变量是 Rancher API 的主机名。例如,主机名可以是 rancher.example.com。请注意,此变量中不包含 HTTP 或 HTTPS,因为代理需要通过 HTTPS 连接到 Rancher。第二个变量是 CATTLE_CA_CHECKSUM,只要它们匹配,代理将信任该 HTTPS 连接。需要注意的是,如果您在不更改链的情况下更新证书,也就是说,只更换证书的颁发机构,CATTLE_CA_CHECKSUM 变量不会改变—例如,如果您从自签名证书切换到一个像 CATTLE_CA_CHECKSUM 这样的公共签名证书,则需要手动更新代理,因为它们会匹配更长时间。该过程可以参考文档 github.com/rancherlabs/support-tools/tree/master/cluster-agent-tool。
Rancher 如何配置节点和集群?
Rancher 可以使用以下方法配置不同的节点和集群。Rancher 中有三种主要类型的集群。使用 RKE 创建的 Rancher 集群、使用托管提供商创建的 Rancher 集群 和 导入的集群。每种类型都有子类型,以下是详细说明。
使用 RKE 构建的 Rancher 创建的集群如下:
- Rancher 创建的节点:Rancher 的一大优点是,如果您选择 Rancher,它可以为您构建集群,并且它们可以自己管理虚拟机。这是通过使用一个名为 Rancher-machine 的工具来实现的。该工具基于 Docker Machine,允许您创建虚拟机并安装 Docker。Docker Machine 通过使用驱动程序插件来实现这一点。这些驱动程序插件充当 Docker Machine 和虚拟化提供商之间的翻译层—例如,Amazon AWS、Linode、OVHcloud 或 VMware vSphere。
Docker Machine 的工作方式是,您向它提供虚拟化提供商的凭据,并定义虚拟机的规格,例如 CPU 核心数、内存等。然后,驱动程序插件接管并调用云提供商的 API 端点来配置虚拟机。接着,Docker Machine 为每个虚拟机创建一对 SSH 密钥,并使用驱动程序插件将 SSH 密钥推送到虚拟机。它随后等待 SSH 连接可用。
一旦 SSH 连接建立,Docker Machine 就会安装 Docker。这时,Rancher-machine 便发挥作用。Rancher-machine 是在 Docker Machine 的基础上,通过添加额外的驱动插件,如DigitalOcean和Rackspace,来扩展功能。它还提供了更多功能,如实现 cloud-init。在节点配置过程中,你可以执行其他步骤,比如为 Docker 创建文件系统或对 Docker 引擎应用自定义设置。Rancher 提供了更高层次的功能,如定义节点模板,以便在可重复的过程中部署节点,并通过定义节点池(使用节点模板的节点组)进一步扩展这一过程。节点池使 Rancher 能够随时向组中添加或移除节点。例如,如果池中的某个节点崩溃并且在默认的 15 分钟超时(可定制)内没有恢复,Rancher 可以创建一个新的虚拟机替代节点并销毁崩溃的节点。这个过程还可以用于执行节点的滚动替换,适用于你不希望就地修补而是希望更新基础镜像并以滚动方式重建所有节点的使用场景。
-
自带节点:这些节点适用于你希望或需要自己创建虚拟机或使用物理服务器的场景。在这种情况下,你将会在 Rancher 中定义集群配置。然后,Rancher 会为你生成一个命令,你只需运行这个命令,如下所示:
docker run -d --privileged --restart=unless-stopped --net=host -v /etc/kubernetes:/etc/kubernetes -v /var/run:/var/run rancher/rancher-agent:v2.6.0 --server https://rancher-lab.support.tools --token abcdefghijkmn123456789 --etcd --controlplane --worker.
让我们来分解这个命令。首先,这是一个可以在任何已安装 Docker 的 Linux 主机上运行的 Docker 命令。接下来是run,它表示创建一个新的容器,紧接着的标志是-d,表示以分离模式运行。这将启动容器并将其放在后台。–privileged标志告诉 Docker 这是一个特权容器——意味着该容器可以访问主机上的所有设备。可以把它理解为直接在主机操作系统上运行进程,几乎没有任何限制。--restart=unless-stopped标志仅表示 Docker 会不断重启这个容器,直到我们告诉它停止。接下来是--net=host标志,它为容器分配与主机相同的网络。因此,容器的 IP 将是主机的 IP。接下来的两个标志将/etc/kubernetes和//var/run目录挂载到容器内。/etc/kubernetes目录用于存储节点级的配置文件,最重要的是存储 K8s 组件所使用的 SSL 证书。
以下部分是容器镜像和标签。该镜像将与 Rancher 版本相匹配,并且该镜像包含启动此节点所需的所有二进制文件。--server 标志是 Rancher API 服务器路径。这将被传递到容器中,并返回连接到 Rancher 领导者 Pod(有关更多详细信息,请参阅本章前面的Cattle 代理做了什么?部分)。接下来,我们有 –token 标志。它用于将代理认证到 Rancher 服务器并将该代理与集群绑定。每个集群都有一个唯一的令牌,但集群中的所有代理将共享相同的令牌。最后,我们有 role 标志。该标志用于为节点分配 RKE 集群的不同角色。请注意,节点可以拥有多个角色,但集群至少需要每个角色有一个节点:一个 etcd 节点,一个控制平面节点和一个工作节点。您可以根据需要混合和匹配角色,但有一些最佳实践应遵循。
在 Rancher 创建的节点和 自带节点 中,一旦引导代理成功启动,代理将回传到 Rancher 主节点,并在 Rancher RKE 中注册新节点。然后,它使用已注册的节点动态创建 cluster.yaml 文件,利用已注册或正在注册的节点将其加入集群。如果该集群之前已经成功启动过一次,Rancher 还会从 CRD clusters.management.cattle.io 对象中拉取 cluster.rkestate 文件。此文件包含集群的当前状态、根证书和服务器证书,以及 RKE 用于与集群通信的身份验证令牌。然后,集群控制器将使用 Rancher 主节点上的端口绑定连接节点上的 Docker 引擎。在此过程中,RKE 将创建证书和配置文件,部署到节点,并开始创建/更新 etcd 集群。RKE 会按顺序执行此过程,每次只处理一个节点,如果遇到问题,它将抛出错误并退出功能。同时,在对现有集群进行任何更改之前,RKE 会在每个 etcd 节点上执行备份。等到 etcd 平面成功启动后,RKE 将开始处理控制平面,此时 RKE 会启动 kube-apiserver 对象、kube-controller-manager 和 kube-scheduler,依然按顺序操作,每次运行一个节点,并在执行过程中进行健康检查。同样,如果过程中任何步骤失败,RKE 也会失败。最后,RKE 将进入工作节点平面。这个过程有所不同,因为它旨在并行处理多个工作节点,即使发生故障,也会继续进行,只要没有违反 零停机 配置中定义的设置。默认设置为在任何给定时间最多允许一个 etcd 或控制平面节点宕机,最多 10% 的工作节点宕机。请注意,该数字会向下取整至最近的节点,且每批次至少有一个节点。
-
使用托管提供商创建的 Rancher 集群:Rancher 的一个优点是,如果你不想处理虚拟机,只想让云提供商管理虚拟机,你可以使用托管的 K8s 集群,如 AWS EKS、Google GKE 或 Azure AKS。Rancher 可以通过使用云提供商的软件开发工具包(SDK)为你提供集群。这主要是出于便捷性和一致性的考虑,因为 Rancher 没有独特或隐藏的选项是你自己无法完成的。作为 v2 版本中新托管集群选项的一部分,Rancher 还允许 Rancher、云提供商和最终用户之间的三方配置同步。值得注意的是,如果你想更改 AWS EKS 集群的一些设置,你可以直接在 AWS 控制台管理,它的更改会在 Rancher 中反映出来。请注意,RKE 集群也可以这样做,但需要额外的步骤。
-
导入的 K8s 集群:最后,如果你不希望 Rancher 管理你的集群,但又希望 Rancher 作为你集群的友好 Web UI,你可以利用 Rancher 的优秀便捷功能,例如Active Directory(AD)身份验证、Web kube-proxy 访问等。你可以导入集群,Rancher 会在集群上部署 cluster-Cattle-agent,但不会访问诸如 etcd、kubelet、Docker CLI 等项。在这种情况下,Rancher 只能访问 kube-apiserver 端点。请注意,Rancher 支持任何已认证的 K8s 发行版作为导入集群选项,这包括K8s the hard way、EKS、自管理 RKE 集群,甚至是 K3s/RKE2 集群。自 Rancher v2.6.0 起,K3s 和 RKE2 集群有其独特之处,它们可以被导入到 Rancher 中,Rancher 随后可以接管集群的管理。请注意,这仍然是一个新过程,存在一些限制和 bug。
什么是 kube-apiserver、kube-controller-manager、kube-scheduler、etcd 和 kubelet?
etcd 对象是一个分布式且一致的键值对数据库。CoreOS 最初在 2013 年开发 etcd,用于处理集群管理系统中的操作系统升级并存储配置文件。因此,etcd 需要具备高可用性和一致性。etcd 对象目前隶属于 CNCF,并已在业界广泛采用。etcd 集群基于在节点之间保持一致性的理念——大多数集群包含三个或五个节点,并且有一个要求,即节点数必须为奇数。这是由于 Raft 共识算法的要求。该算法选择一个主节点,etcd 称之为 leader。该节点负责在节点之间同步数据。如果主节点失败,将会进行另一次选举,另一个节点将接管这个角色。这里的理念是,etcd 构建在 法定人数(quorum)的概念上。这意味着集群中超过一半的节点必须达成一致。在一个标准的三节点集群中,如果单个节点失败,etcd 集群将继续接受写入操作,但如果有两个节点失败,幸存的 etcd 节点将采取最安全的选项,进入只读模式,直到集群能够恢复法定人数。五节点集群也是如此,但需要三个节点失败才会失去服务。所有写入操作都由 etcd leader 节点处理,这些写入会记录到 Raft 日志中,并在操作过程中广播到所有集群节点。一旦大多数节点成功确认写入(即三节点集群中两个节点,五节点集群中三个节点),Raft 日志条目就会被提交,写入操作会反馈给客户端。如果大多数节点没有确认写入,则写入会失败,且不会被提交。由于 Raft,向集群中添加更多节点会增加容错能力,但这也会增加 leader 节点的负载,而不会改善性能。
目前,etcd 存储数据是因为 etcd 基于 BoltDB 构建,BoltDB 将数据写入一个单一的内存映射文件。这意味着操作系统负责处理数据缓存,并尽可能将数据保留在内存中——这就是为什么 etcd 可能会成为 内存消耗大户 并且需要高速磁盘,最好是 SSD 或 NVME。对于数据,etcd 使用 多版本并发控制(MVCC)来安全地处理并发写操作。MVCC 与 Raft 协作,每次写操作都会通过修订版本进行跟踪。通过保留修订历史,etcd 可以提供所有键的版本历史。这会影响读取性能,因为键值对是按事务日志中的创建顺序写入磁盘,而不是按索引(如传统数据库中)。这意味着同时写入的键值对比不同时间写入的键值对更快读取。然而,随着 etcd 随时间保留修订,磁盘和内存的使用可能会变得非常大。即使从 etcd 删除大量键,空间也会继续增长,因为这些键的历史仍然会被保留。这就是 etcd 压缩和碎片整理的作用,etcd 会删除被替代的修订,即已被 覆盖 的旧数据,内存映射文件会出现多个空洞,etcd 会运行碎片整理以将空闲页面释放回操作系统。然而,必须注意的是,在碎片整理过程中,所有传入的读取和写入都会被阻塞。
kube-apiserver 是 K8s 集群中的关键组件,因为它是为整个集群提供 REST API 端点的服务器。kube-apiserver 是唯一与 etcd 集群连接的 K8s 组件,并充当所有其他 K8s 组件的访问点。目前,kube-apiserver 旨在保持相对简单,大部分业务逻辑由其他控制器和插件完成。但它的主要责任之一是身份验证和 RBAC(基于角色的访问控制)。默认的访问控制行为是,所有客户端都必须通过身份验证才能与 kube-apiserver 交互。
kube-apiserver 的另一个核心功能是管理秘密的加密。默认情况下,K8s 将秘密以明文存储在 etcd 数据库中。这可能会带来安全问题,因为秘密存储了像密码、数据库连接字符串等信息。为了保护秘密,kube-apiserver 支持加密提供者。具体来说,每当创建或更新一个秘密时,kube-apiserver 会调用加密提供者来访问加密算法和密钥以加密数据,然后将数据发送到 etcd 集群。然后每当从 etcd 集群读取秘密时,kube-apiserver 会使用反向过程解密数据,再将响应发送回客户端。因此,客户端无法察觉秘密已被加密,唯一的影响是性能。Rancher 的标准是使用 aescbc 作为其加密算法,因为它在性能和强度之间提供了良好的平衡,而且大多数现代 CPU 都支持硬件加速 AES CBC 模式。因此,加密和解密性能通常不是问题。
关于 kube-apiserver 需要记住的另一个关键点是它是无状态的;除了某些内存缓存外,kube-apiserver 不存储任何数据。这意味着 kube-apiserver 非常适合水平扩展。它也没有领导者选举过程,因为没有领导者节点。因此,通常情况下,集群至少会有两个节点运行 kube-apiserver,但在更大的集群中,你可以有更多的节点。你也不需要像 etcd 那样面临老旧节点数量的限制。kube-apiserver 还是许多核心集群配置操作发生的地方。例如,当使用 AWS 或 VMware vSphere 等云提供商时,你需要创建一个配置文件,并将该文件路径作为命令行标志传递给 kube-apiserver 组件。请注意,kube-apiserver 不支持热更改设置,修改其配置需要重启。
K8s 控制器管理器 kube-controller-manager 是 K8s 的核心控制器,通常被称为 控制器的控制器,因为它的主要任务是处于一个非终止循环中,调节集群的状态。它连接到 kube-apiserver 组件,并创建多个监视句柄,监控当前集群的状态,并将其与期望的状态进行比较。kube-controller-manager 组件通过拥有多个较小的控制器来实现这一点。
第一个较小的控制器是副本控制器。该控制器确保在任何给定时间,ReplicaSet中指定数量的 pods 都在运行。例如,如果一个 ReplicaSet 期望有五个 pod 处于所需状态,但当前只有四个 pod,副本控制器将创建一个新的 pod 对象,处于未调度状态,从而使 ReplicaSet 恢复到所需的五个 pod。另一个例子是当一个节点发生故障时——在这种情况下,副本控制器会发现 pod 已被禁用、删除或终止,然后它会再次创建一个新的 pod 对象。副本控制器还负责在当前状态大于所需状态时终止 pods。在终止过程中有一些业务逻辑。这里的主要规则是,当前不处于就绪状态的 pods(即挂起或失败的 pods)会首先被设置为终止,接下来是最旧的 pods。需要注意的是,副本控制器不会直接删除 pods 或调用节点,也不会与其他控制器进行连接。所有通信都是在控制器和 kube-apiserver 之间进行的。
第二个控制器是端点控制器,负责维护连接服务的端点及其分配的 pods,其中端点和服务记录是集群 DNS 系统的一部分。K8s 需要跟踪在集群中创建和删除的 pods,以便用正确的 IP 地址更新这些记录。
第三个控制器是服务账户和令牌控制器。该控制器负责在集群中创建和管理服务账户,并为服务账户创建令牌,以便用于身份验证 kube-apiserver 组件。需要注意的是,令牌作为机密存储在服务账户所在的命名空间中。
第四个控制器是节点控制器。这个控制器负责通过监视 kubelet 定期更新的节点租约来观察集群中节点的状态。假设一个节点租约违反了默认的五分钟节点超时时间,节点控制器将决定该节点必须处于宕机状态,并启动 Pod 驱逐过程,其中控制器会将该节点上运行的 Pod 的状态更新为Unknown,并会将unschedulable的污点标记到节点对象上。这将触发副本控制器开始删除无法容忍该污点的 Pod。记住以下几点是非常重要的。首先,K8s 无法知道一个节点是否真的宕机,还是仅仅与 kube-apiserver 通信出现了问题,这意味着如果节点上的 kubelet 崩溃或卡住,那么该节点上运行的 Pod 将继续运行,不会有问题,即使节点控制器已经标记它们,并且副本控制器已经删除了它们。此外,因为 K8s 没有办法阻止集群对失败节点的 I/O 操作,可能会遇到“脑裂”问题,即相同的 Pod/应用程序在两个位置同时运行。还需要记住,具有unschedulable污点容忍的 Pod 将不会被重新调度。
一个例子是一个能够容忍节点上任何污点的 canal Pod,这意味着无论如何这个 Pod 都会被调度到节点上。接下来需要记住的是,驱逐过程是有限速的,默认情况下,Pod 驱逐速率为每秒十个 Pod。这是为了防止在集群中重新调度大量 Pod。最后,任何时候只允许一个 kube-controller-manager 处于活动状态。它通过使用领导选举过程来实现这一点。所有 kube-controller-manager 进程都会尝试在 kube-apiserver 中抢占租约。一个进程成为领导者,允许其他控制器开始在集群中采取行动。领导者将继续刷新此租约,而其他节点则继续监视租约,将最后的续约时间戳与到期时间戳进行比较。如果租约允许过期,备用的 kube-controller-manager 将争夺成为新的领导者。综上所述,水平扩展 kube-controller-manager 仅提高了容错性,并不会提高性能。
kube-scheduler 是处理将 pods 分配到节点的控制器。它通过监控未调度的 pods 来执行此操作,此时 kube-scheduler 正在评估节点。kube-scheduler 首先构建一个满足所有要求的节点列表。例如,如果一个 pod 需要节点选择器规则,则只有具有该标签的节点才会被添加到节点候选列表中。接下来,kube-scheduler 会评估节点和 pods 的污点和容忍度。例如,默认情况下,pod 不会容忍污点调度已禁用的节点。该污点通常应用于 master、etcd 或控制平面节点。在这种情况下,该节点不会被添加到节点候选列表中。接下来,kube-scheduler 会创建它所称的节点 评分。这个评分是基于节点上可用资源的可用性,比如 CPU、内存、磁盘、网络等。例如,一个未充分利用的节点,即有大量可用的 CPU 和内存,其评分会高于一个高度利用的节点,即几乎没有可用的 CPU 或内存。一旦所有的评分都计算完成,kube-scheduler 会按从高到低的顺序进行排序。如果只有一个节点拥有最高的评分,那么该节点会被选中。如果存在平局,kube-scheduler 会从平局的节点中随机选择一个胜者。最后,kube-scheduler 会在 kube-apiserver 中更新 pod 对象的节点分配。一个重要的要点是,kube-scheduler 可以进行调优,甚至可以用其他第三方调度器来替代它。这主要是在处理突发工作负载的环境中进行的,这些环境中会同时创建多个 pods。因为 kube-scheduler 并不知道资源的利用情况以及它的 pods 随时间的变化。它只会获取调度时的当前值。因此,可能会发生一个节点被新的 pods 填满并崩溃。然后这些 pods 会被重新调度到一个新的节点,而这个节点也会崩溃。但在此事件期间,第一个节点会恢复,所以现在所有新的 pods 都会去这个节点,因为它已经空了,而这个过程会反复循环。还有一个必须理解的重要概念是,kube-scheduler 只会在 pod 创建时接触该 pod。一旦 pod 被调度到一个节点上,事情就结束了。kube-scheduler 组件不会重新平衡或移动 pods。还有像 kube-descheduler 这样的工具可以填补这一空白,帮助你平衡集群。
简单来说,kubelet 是 K8s 中的节点代理(node agent)。kubelet 在每个工作节点上运行,在 RKE 的情况下,所有节点上都有 kubelet。kubelet 承担了多个不同的角色和职责。首先,它获取在 kube-apiserver 中的 Pod 规范,这些规范包含了 kubelet 正在运行的节点的节点分配信息。然后,kubelet 将这些 Pod 规范与节点的当前状态进行比较。kubelet 通过连接到 Docker 或 containerd 来收集当前在节点上运行的容器信息。如果存在差异,kubelet 将创建或销毁容器以使其匹配。kubelet 组件的第二个职责是Pod 健康。
Pods 可以在其规范中定义探针(probes)。这些探针包括存活探针(liveness probes),用于检查 Pod 内部的应用程序是否健康。这些检查是每 5 秒发送一次简单的 HTTP GET 请求到 /,以确认 NGINX 是否正常运行并响应请求。
注意
kubelet 组件只接受 200 OK 响应作为健康请求的证据。所有其他响应代码都会返回失败。另一种类型的探针称为启动探针(startup probe)。这种探针类似于存活探针,但主要告诉 kubelet Pod 已成功启动。
一个例子可能是一个位于 Pod 内的数据库,其中数据库可能需要几分钟才能完全启动。如果你只使用了存活探针(liveness probes),你需要将时间安排间隔开来,以便在 kubelet 完全杀死 Pod 之前允许数据库启动。因此,你可能需要使用一个启动探针(startup probe),并设置一到两分钟的延迟,之后一旦启动探针成功,存活探针可以接管,并每隔几秒钟运行一次。最后,就绪探针(readiness probe)与启动探针非常相似,但它用于控制 K8s 何时可以开始向 Pod 发送流量。一个例子可能是一个已经启动且健康的 Web 服务器,但无法连接到后台数据库。在这种情况下,你不希望 kubelet 杀死 Pod,因为它是健康的,但你也不希望在它能够连接到数据库之前开始向 Pod 发送流量。
当前状态与期望状态是如何工作的?
期望状态 是 Rancher 和 K8s 的核心概念之一。其理念是,你应该声明对象的状态(例如,Pod、部署或存储卷)应当是什么样子。然后,集群应报告该对象的当前状态,在此时,控制器(对于大多数 K8s 核心对象而言,通常是 kube-controller-manager 组件)的角色是比较这两种状态。如果没有发现差异,则不做任何操作;但如果发现不一致,控制器的工作是制定一个计划,使当前状态与期望状态一致。
例如,如果你在集群中部署了一个包含三个副本的部署,ReplicaSet 控制器将看到副本数设置为三个(期望状态),并且镜像标签是v2。然后,控制器会调用 kube-apiserver 组件,拉取当前状态和期望状态的副本,在这个过程中,控制器将开始比较设置。在此示例中,当前状态将有三个使用v1镜像标签的健康 Pod,因为 Pod 在创建后无法修改。控制器需要创建新的替代 Pod,并销毁旧的 Pod。它通过创建一个带有更新镜像标签的新 Pod 对象来完成这一点。这个 Pod 对象的状态将是等待调度。此时,kube-scheduler 接管,负责将该 Pod 分配到一个节点。接着,kubelet 接管,创建所需的容器、IP 地址、挂载卷等。然后,一旦所有内容启动完成并且探针成功,kubelet 会更新 Pod 对象为 Ready 状态。接着,这将回到 ReplicaSet 控制器,后者将检测到某个 Pod 已成功启动。如果是,它会选择一个不符合规格的最旧 Pod,并通过将状态设置为 terminating 来终止该 Pod。这将触发 kubelet 销毁该 Pod 及其资源。然后,一旦所有资源清理完毕,kube-controller-manager 会在 kube-apiserver 中删除已终止的 Pod 对象。该过程将重新开始,并且会一直重复,直到 ReplicaSet 中的所有 Pod 都与期望状态匹配。
控制器的设计目标始终是确保当前状态与期望状态保持一致。如果控制器遇到问题,例如新创建的 Pod 不断崩溃、无法拉取镜像或缺少 configmap 对象,经过几次失败的尝试后,控制器将继续尝试,但会将该对象置于CrashLooping状态。这会告诉控制器在设定的时间内停止修复该状态(默认是 5 分钟)。控制器这样做是为了防止不断向集群发送请求以修复失败的资源(例如,如果你在镜像标签中输入了错别字)。我们不希望控制器一直以最快的速度创建和删除相同的 Pod,因为这会给 kube-apiserver、etcd、kube-scheduler 等带来负担,可能会导致大量 Pod 同时进入崩溃循环状态,从而使集群崩溃。
总结
在本章中,我们学习了 Rancher、RKE、RKE2、K3s 和 RancherD。我们讨论了每个产品的一些优缺点。接着,我们了解了它们的设计方式及工作原理。然后,我们介绍了构成 Rancher 的所有控制器,并探讨了它们在后台的工作机制。之后,我们深入了解了 Rancher 如何使用其 Cattle 代理与集群和节点进行通信。最后,我们详细讲解了 K8s 的不同核心组件,包括 kube-apiserver、kube-controller-manager 和 kube-scheduler。
在下一章中,我们将学习如何在单节点环境中安装 Rancher。
第二部分 – 安装 Rancher
本部分将涵盖所有部署 Rancher 的不同方式,从单节点 POC 到完整的企业解决方案,甚至包括如何在托管集群上部署 Rancher。
本书的这一部分包括以下章节:
-
第三章,创建单节点 Rancher
-
第四章,创建 RKE 或 RKE2 集群
-
第五章,在托管的 Kubernetes 集群上部署 Rancher
第三章:第三章:创建单节点 Rancher
本章将涵盖将 Rancher 安装为单个 Docker 容器的过程。这是一个非常适合概念验证、开发或测试目的的选项。本章将介绍单节点 Rancher 的要求与限制,以及构建一个合适的企业解决方案所需的核心架构规则。最后,本章将讲解如何迁移到 高可用性(HA)集群。
本章将涵盖以下主要内容:
-
什么是单节点 Rancher 安装?
-
要求与限制
-
架构解决方案的规则
-
安装步骤
-
迁移到 HA 配置
什么是单节点 Rancher 安装?
Rancher 可以通过运行一个 Docker 容器进行安装。这个过程可以追溯到 Rancher v1.6 版本,当时 Rancher 服务器是一个基于 Java 的应用程序,通过 Docker 容器运行,并使用外部 MySQL 数据库,或者在单节点模式下使用运行在 Rancher 服务器容器内的 MySQL 服务器。随着 Rancher v2.x 的发布,Rancher 中的一切都转向使用 Kube-apiserver,并且设置了 KUBECONFIG。Kubernetes 默认在所有 Pod 中设置这个变量,因此如果缺少它,Rancher 就知道它必须在单节点模式下运行。此时,Rancher 服务器进程将开始检查 Kubernetes 组件是否有 SSL 证书,如果证书缺失或已过期,Rancher 将负责创建它们。接下来,Rancher 会启动一个单节点的 etcd 集群,并启动 Kube-apiserver 和所需的控制器。这里需要注意的是,这个集群是非常精简的。例如,这个集群没有 CoreDNS、ingress-controller,甚至没有一个 true 集群,且没有任何标准配置。早期版本中出现了几个问题。例如,在 Rancher v2.3.x 之前,无法在 Rancher 服务器容器内旋转证书,最初,Rancher 会创建一个有效期为 1 年的证书。这意味着一年后,Rancher 服务器会崩溃并无法启动,因为 Kubernetes 组件与过期证书无法正常工作,这是一种 Go 库中的安全措施,不允许任何没有验证证书的 HTTPS 连接。而且,过期的证书显然不是有效的证书。在 Rancher v2.3.x 版本中,Rancher 添加了一个过程,用于查找过期或即将过期的证书并旋转它们。这是通过在 Docker 容器内启动一个独特的 K3s 集群并将 Rancher 服务器作为 Pod 部署来实现的。
要求与限制
以下是单节点 Rancher 的 要求:
-
运行 Docker 18.06.3、18.09.x、19.03.x、20.10.x 的 Linux 主机
-
至少需要两个核心,但强烈推荐四个核心
-
8 GB RAM
-
10 GB 的 SSD 存储,延迟不超过 10 毫秒
-
在终端用户和管理的集群之间,
80和443端口需要开放
以下项不是必须的,但强烈推荐:
-
使用服务器主机名时,可以使用像
rancher.example.com这样的 DNS 记录 -
由受认可的证书颁发机构(CA)签名的证书,如 DigiCert 和 GoDaddy
-
在 Rancher 服务器前面放置一个 HTTP 或 TCP 负载均衡器
-
服务器备份,可以是文件或快照级别的备份
-
为 Docker 文件系统
/var/lib/docker提供专用的文件系统/磁盘 -
为 Rancher 持久数据
/var/lib/rancher提供专用的文件系统/磁盘 -
Linux 主机应为虚拟机(VM),其中虚拟化管理程序或云提供商会提供冗余,以防硬件故障。
以下是单节点 Rancher 的已知限制:
-
单节点 Rancher 仅建议用于开发和测试目的,不应用于生产环境。
-
此主机上应仅安装 Rancher 服务器。该服务器不应托管任何其他应用程序。
-
单节点 Rancher 并非为高可用(HA)设计。
-
从单节点迁移到高可用(HA)并未正式支持,也不能保证能够成功。
-
单节点 Rancher 功能将被移除,并且不再提供。
-
Rancher v2.5.x 及更高版本需要特权选项,因此不能以无根模式运行 Docker。
-
单节点 Rancher 可以安装在台式机/笔记本电脑上,但当主机的 IP 地址发生变化时,会遇到问题,并且需要创建 DNS 记录。
架构解决方案的规则
优点如下:
-
单节点 Rancher 设置非常简单,只需部署一个容器即可。
-
启动非常快。与 RKE 需要 10-15 分钟才能启动相比,单节点 Rancher 只需几分钟即可启动。
-
与 RancherD 和完整的 RKE 集群相比,它的资源利用率较低。单节点 Rancher 使用的 CPU、内存和存储都要少得多。
-
如果只需要服务器主机名或 IP 地址,则无需负载均衡器或 DNS。
-
单节点 Rancher 可以在笔记本电脑上运行。(注意:Rancher Desktop 是更适合此方案的产品。)
缺点如下:
-
单节点 Rancher 并非为生产环境设计。
-
Rancher 官方和社区支持非常有限。
-
由于 K3s 设置已经嵌入代码中,且无法在不构建新版本的情况下更改,因此故障排除选项非常有限。
-
单节点 Rancher 的长期前景不确定,未来的版本中将被移除。
-
如果主机下线,则没有扩展性或冗余。Rancher 会宕机。
-
默认情况下,单节点 Rancher 将其数据存储在容器内部,如果容器丢失,数据也会丢失。
-
没有内置的备份解决方案;RKE、RKE2、K3s 和 RancherD 可以备份到本地磁盘或 S3。
架构规则如下:
-
你应该规划从单节点 Rancher 迁移到 HA 的方案。
-
Rancher 需要一个 SSL 证书,没有证书将无法工作。
-
使用公开签名的证书可以使脚本和工具更方便,因为 Rancher URL 默认会被信任。
-
Rancher 将管理的所有集群/节点需要通过 SSL 连接到 Rancher URL。
-
Rancher 确实支持隔离环境,但需要额外的步骤来提供代理访问互联网,或者你需要通过内部托管服务提供 Docker 镜像和目录。
安装步骤
我们假设以下情况:
-
确保你已经创建并更新了一个 Linux 虚拟机(在本示例中,我们将使用运行 Ubuntu 20.04 的 VMware 虚拟机)。
-
确保 Linux 虚拟机可以访问互联网,并且不需要 HTTP 代理进行访问。注意,如果你没有互联网访问权限,请参考位于
rancher.com/docs/rancher/v2.5/en/installation/other-installation-methods/air-gap/的隔离步骤。 -
你拥有对 Linux 虚拟机的 SSH 和 root 访问权限。
-
你正在使用默认配置和存储位置安装 Docker。
-
确保文件系统
/var/lib/docker和/var/lib/rancher已经创建并挂载。 -
你已经为 Rancher 创建了 DNS 记录。在本示例中,我们将使用
rancher.support.tools和一个由受信任的 CA 签发的 SSL 证书。
安装 Docker
在本节中,我们将安装并配置 Docker:
-
通过 SSH 进入 Linux 虚拟机,并使用
sudo su -命令切换为 root 用户。 -
运行
curl https://releases.rancher.com/install-docker/20.10.sh | bash命令来安装 Docker。 -
通过运行
systemctl enable docker命令设置 Docker 在系统启动时启动。 -
通过运行
docker info来验证 Docker 是否正在运行。输出应如下所示:

图 3.1 – Docker 信息输出
- 配置日志轮转 – 我们需要启用 Docker 日志的轮转。创建/编辑
/etc/docker/daemon.json文件,使其包含以下内容:

图 3.2 – 启用 Docker 日志轮转
- 使用
systemctl restart docker命令重启 Docker 以应用更改。
准备 SSL 证书
在本节中,我们将准备用于 Rancher 服务器的 SSL 证书和密钥。这些文件将被称为 tls.crt 和 tls.key。步骤如下:
-
要创建
tls.crt,我们需要完整的证书链。这包括根证书和中间证书。大多数公共根证书机构会在其网站上发布这些证书。 -
我们希望所有证书文件都是隐私增强邮件(PEM)格式的。请注意,有时这也称为Base64。如果你的证书是其他格式,应该访问
knowledge.digicert.com/solution/SO26449.html获取关于格式转换的更多信息。 -
一旦所有文件都为 PEM 格式,我们需要创建一个文件,其中包含每个证书的内容,按以下顺序排列。请注意,有些证书可能包含多个中间证书。如果你有任何问题,请联系你的证书颁发机构(CA)。此外,如果你使用的是内部 CA,可能没有中间证书,你可能只拥有根证书和服务器证书。

图 3.3 – 创建存储证书的文件
- 对于私钥,我们需要确保它没有密码短语。我们通过检查文件的顶部来做到这一点;请参见以下示例了解详细信息。
这些是有密码短语的密钥示例:

图 3.4 – 密码短语示例 1

图 3.5 – 密码短语示例 2
这是一个没有密码短语的密钥示例:

图 3.6 – 没有密码短语的密钥示例
-
如果你的密钥有密码短语,你需要使用
openssl rsa -in original.key -out tls.key命令来移除它,并在提示时输入密码短语。 -
一旦此过程完成,你应该会有两个文件,
tls.crt和tls.key。 -
你需要使用
mkdir -p /etc/rancher/ssl/命令创建/etc/rancher/ssl/目录,并将这两个文件放入该目录中。请注意,这些文件应由 root 用户拥有。
启动 Rancher 服务器
在本节中,我们将创建 docker run 命令并启动 Rancher 服务器。
以下是一个示例命令:

图 3.7 – docker run 命令示例
现在,我们来逐步解析这个命令:
-
docker run -d将创建一个新的容器,并以分离模式启动它。 -
--name rancher_server将容器的名称设置为rancher_server。这样会使未来的命令更加简洁,因为没有这个名称时,Docker 会生成一个随机名称。 -
--restart=unless-stopped将告诉 Docker 确保该容器一直运行,除非你手动停止它。 -
-p 80:80将主机上的端口80(HTTP)映射到容器内的端口80。 -
-p 443:443将主机上的端口443(HTTPS)映射到容器内的端口443。请注意,如果你在负载均衡器上进行 SSL 卸载,则不需要此操作。 -
v /etc/rancher/ssl/tls.crt:/etc/rancher/ssl/cert.pem和-v /etc/rancher/ssl/tls.key:/etc/rancher/ssl/key.pem标志将把我们之前创建的证书文件传递到 Rancher 服务器。 -
-v /var/lib/rancher:/var/lib/rancher标志将把 Rancher 的数据目录绑定到主机的文件系统上。 -
--privileged将赋予 Rancher 服务器容器在主机上的 root 权限。这是必要的,因为我们将在容器内部运行 K3s,且该容器会有额外的容器。 -
rancher/rancher:v2.5.8将设置 Rancher 镜像,并指定 Rancher 服务器的版本。 -
--no-cacerts将禁用 Rancher 服务器的证书生成过程,因为我们将使用我们自己的证书。 -
最后,一旦我们启动 Rancher 服务器,我们需要等几分钟,直到 Rancher 完全启动。
-
你可以通过运行
docker logs -f rancher_server命令来观看服务器启动过程。 -
然后,你需要在浏览器中打开 Rancher URL。请注意,如果你打算使用 CNAME 或负载均衡器,应该使用该 URL,而不是使用主机的 IP/主机名。
-
要获取管理员密码,你需要运行以下命令:
docker logs rancher_server 2>&1 | grep “Bootstrap Password:”

图 3.8 – 第 14 步的代码片段
- 登录到 Rancher 后,你需要设置密码和 URL:

图 3.9 – Rancher 登录页面
设置 URL 时要特别注意,确保选择一个你希望长期使用的 URL,因为更改 URL 是非常困难且耗时的。密码是本地管理员的密码,这是一个具有完全访问权限的 root 级帐户。此密码应该是安全的。
迁移到 HA 设置
从单节点 Rancher 迁移到 HA 安装时,我们需要以下内容:
-
你应该运行 Rancher 和 RKE 的最新版本。
-
你已经有了三个新的 Linux 虚拟机(在本示例中,我们将使用一台运行 Ubuntu 20.04 的 VM)。请注意,在此过程结束时,原始的虚拟机可以被回收。
-
我们假设 Linux 虚拟机可以访问互联网,并且不需要 HTTP 代理。如果没有互联网访问,请参见位于
rancher.com/docs/rancher/v2.5/en/installation/other-installation-methods/air-gap/的隔离环境步骤。 -
通过 SSH 连接并获取 Linux 虚拟机的 root 权限。
-
在三台新虚拟机上安装 Docker。
-
Rancher 的 DNS 记录不是服务器主机名/IP 地址。
-
你将需要 30 到 60 分钟的维护窗口。在此期间,Rancher 及其 API 将不可用。这可能导致 CICD 管道无法工作,应用程序团队可能无法管理其应用程序。请注意,这不会影响下游应用程序,唯一的影响是管理方面。
-
在接下来的步骤中,我们假设单节点 Rancher 服务器容器被称为
rancher_server。如果名称不同,请更新接下来的命令。 -
本节假设你了解什么是 RKE 以及如何使用它。请注意,我们将在下一章中详细讨论 RKE。
备份当前的 Rancher 服务器
在本节中,我们将备份当前的 Rancher 单节点服务器,包括 Kubernetes 证书、etcd和 Rancher URL 的 SSL 证书。步骤如下:
-
通过 SSH 连接到当前的 Rancher 服务器节点。
-
使用
sudo su –命令切换为 root 用户。 -
使用
docker stop rancher_server命令停止当前的 Rancher 服务器。 -
使用
docker create --volumes-from rancher_server --name rancher-data-<DATE> rancher/rancher:<RANCHER_CONTAINER_TAG>命令从当前服务器创建一个卷。请替换日期和标签占位符。 -
使用
docker run --volumes-from rancher-data-<DATE> -v $PWD:/backup:z busybox tar pzcvf /backup/rancher-data-backup-<RANCHER_VERSION>-<DATE>.tar.gz /var/lib/rancher命令创建一个tar.gz文件备份。请替换日期和标签占位符。 -
使用
ls -lh命令验证备份文件是否已经创建。备份文件将在当前目录中创建。 -
使用
docker start rancher_server命令重启 Rancher 服务器备份。 -
使用
docker exec -it rancher_server /bin/bash命令进入 Rancher 服务器容器的 shell。 -
使用
tar -zcvf pki.bundle.tar.gz /etc/kubernetes/ssl命令备份当前的证书。 -
使用
exit命令退出 shell。 -
使用
docker cp rancher_server:/var/lib/rancher/pki.bundle.tar.gz命令将备份文件从 Rancher 服务器中复制出来。 -
使用
docker run --net=container:rancher_server -it -v $(pwd):/cwd --name etcd-utility rancher/rke-tools:v0.1.20命令创建一个临时容器。 -
使用
mkdir ssl; cd ssl; cp /cwd/pki.bundle.tar.gz .; tar -zxvf pki.bundle.tar.gz --strip-components 3命令为etcd设置证书。 -
使用
cd /; ETCDCTL_API=3 etcdctl snapshot save --cacert=/ssl/kube-ca.pem --cert=/ssl/kube-etcd-127-0-0-1.pem --key=/ssl/kube-etcd-127-0-0-1-key.pem single-node-etcd-snapshot命令进行etcd备份。 -
使用
exit命令退出 shell。 -
使用
docker cp etcd-utility:/single-node-etcd-snapshot命令从临时容器中复制etcd备份。 -
使用
docker stop rancher_server命令停止当前的 Rancher 服务器。 -
将
pki.bundle.tar.gz和single-node-etcd-snapshot文件复制到你将使用rke命令的任何服务器/工作站上。有些人会使用集群中的第一个节点来执行此任务。
开始切换到新集群
此时,我们将开始将备份恢复到新集群并迁移 Rancher URL:
-
你应该更新 DNS 或负载均衡器,将流量从旧的单节点 Rancher 服务器重定向到新集群。注意,DNS 可能需要一些时间才能完全传播,因此我们现在要进行这个操作。
-
我们确实只想修改
cluster.yaml文件,以包括第一个节点。接下来的步骤中,我们假设主机名是node01。你可以使用位于raw.githubusercontent.com/PacktPublishing/Rancher-Deep-Dive/main/ch03/migrating_from_single_to_ha/cluster.yml的示例文件。 -
将
single-node-etcd-snapshot和pki.bundle.tar.gz文件通过 SCP 传输到node01。 -
使用
sudo su -命令 SSH 进入 node01 主机并以 root 身份登录。 -
使用
mkdir -p /opt/rke/etcd-snapshots命令创建目录快照目录。 -
将这两个备份文件移动到
/opt/rke/etcd-snapshots目录。这些文件应该由 root 用户拥有。 -
现在你可以退出
node01上的 SSH 终端。 -
使用
rke etcd snapshot-restore --name single-node-etcd-snapshot --config cluster.yaml命令开始恢复。
此过程可能需要 5 到 10 分钟才能完成。示例命令输出位于raw.githubusercontent.com/PacktPublishing/Rancher-Deep-Dive/main/ch03/migrating_from_single_to_ha/restore_command_output.txt。
-
此时,你可以使用
rke up --config cluster.yaml命令启动集群。 -
如果遇到任何错误,尝试第二次运行
rke up命令。 -
一旦成功完成,你可以编辑
cluster.yaml文件,添加node02和node03。 -
再次运行
rke up命令以添加额外的节点。 -
此时,您可以登录到 Rancher 并验证一切是否可访问且健康。请注意,可能需要几分钟时间,直到下游集群重新连接并在 UI 中显示为活动状态。
-
需要注意的是,在确保不会回滚到旧服务器之前,应避免进行任何集群级别的更改,例如创建新集群、编辑现有集群或删除集群。
清理/回滚
如果迁移不成功,请按照以下步骤操作:
-
将 Rancher URL 的 DNS 记录更改回原始服务器。
-
使用
docker start rancher_server命令启动 Rancher 单节点容器。 -
需要注意的是,目前无法将 HA 中所做的更改迁移回单节点。因此,在回滚时,自关闭单个 Rancher 节点以来的所有更改将会丢失。
-
经过几天的烧机后,您可以删除旧的虚拟机或使用
raw.githubusercontent.com/rancherlabs/support-tools/master/extended-rancher-2-cleanup/extended-cleanup-rancher2.sh脚本清理旧服务器。
总结
本章中,我们了解了单节点 Rancher 的工作原理及其优缺点。接着,我们讲解了在单节点模式下安装 Rancher 所需的步骤和命令。最后,我们详细讨论了迁移到 HA 设置的过程,包括备份当前数据并进行恢复。
在下一章中,我们将介绍 RKE 和 RKE2,包括它们的起源、工作原理以及如何使用它们设计解决方案。
第四章:第四章:创建 RKE 和 RKE2 集群
部署 Rancher 的标准方式是创建一个Rancher Kubernetes Engine (RKE)集群,然后使用 Helm 在集群上安装 Rancher。使用 RKE2 部署 Kubernetes 集群的新方式建立在 K3s 和新的内部集群管理模型之上。通过这种方式,Rancher 可以直接管理其所在的集群,而无需像 RKE 这样的外部工具。本章将讨论何时使用 RKE2 而非 RKE 是合理的,以及如何引导第一个节点并将其他节点加入集群。此时,我们将使用Helm工具在集群上安装 Rancher,这将把 Rancher 服务器工作负载安装到集群中。最后,我们将介绍如何配置负载均衡器以支持 Rancher URL。
在本章中,我们将覆盖以下主要主题:
-
什么是 RKE 集群?
-
什么是 RKE2 集群?
-
什么是 RancherD?
-
要求和限制
-
解决方案架构规则
-
安装步骤(RKE)
-
安装步骤(RKE2)
-
配置外部负载均衡器(HAProxy)
-
配置 MetalLB
开始吧!
什么是 RKE 集群?
RKE 是 Rancher 的 Kubernetes 发行版,完全运行在 Docker 容器内。当然,RKE 是 CNCF 认证的发行版,因此所有标准的 Kubernetes 组件和 API 资源都可以使用。了解 RKE 集群的最简单方法是知道它的起源以及它是如何工作的。
RKE 的来源是什么?
最初,当 Rancher 开始创建 Kubernetes 集群时,Rancher 使用其名为Cattle的集群软件,目的是将 Kubernetes 视为集群中的另一个应用程序。这导致了一些问题,从 kubelet 与 Cattle 争夺控制容器,到需要基于 HAProxy 构建自定义负载均衡器解决方案。对于大多数组件来说,最主要的问题是 Rancher 服务器管理集群中的 Cattle 部分,而 Kubernetes 管理 pod 部分。这意味着集群依赖于 Rancher 来获取 pod 的 IP,并且在 pod 变化时需要 Rancher 来更新负载均衡器。随着 Rancher v2.x 和 RKE 的出现,这一切发生了变化,RKE 的理念是它将独立于 Rancher 服务器创建和管理集群。当然,Cattle 的一些核心思想被带入了 RKE,主要的理念是,如果一切都只是容器,那么 Rancher 就不需要关心操作系统。RKE 不需要任何库或软件包。RKE 的核心是管理独立的 Docker 容器来提供核心的 Kubernetes 服务。这些服务包括 etcd、kube-apiserver、kube-scheduler、kube-controller-manager、kubelet 和 kube-proxy。
RKE 是如何工作的?
RKE 的核心设计原则之一是具有所需的状态配置,形式是一个名为 cluster.yaml 的配置文件。通过这个文件,RKE 知道您希望构建什么样的集群,使用哪些节点,以及每个 Kubernetes 组件如何配置。这个文件是 YAML 格式的,因此在创建或编辑时,您需要遵循 YAML 标准。一个常见的陷阱是制表符(Tab)。YAML 使用空格而非制表符,虽然它们看起来一样。如果开始遇到语法错误,可能是因为制表符。接下来是一个 cluster.yaml 示例,以及接下来的部分。我们将逐步分析配置文件的不同部分。

图 4.1 – 示例 cluster.yml
第一部分是 nodes。在此部分中,您将定义用于创建集群的节点。如果我们拆解节点定义,会看到第一行是 address。这是 RKE 用于连接节点的主机名或 IP 地址。通常情况下,使用服务器的 FQDN(完全合格的域名)是标准做法。
重要提示
运行 RKE 的服务器必须能够解析主机名。
在没有先从集群中移除节点并将其作为新节点重新加入之前,不应更改此地址。定义节点时,address 字段是必需的。
以下部分是 hostname_override,用于在 Kubernetes 中设置节点名称。大多数人会将其设置为短主机名。此名称不需要在 DNS 中注册,因为它只是 Kubernetes 中的一个标签。例如,AWS 使用的命名约定是 ip-12-34-56-78.us-west-2.compute.internal,但您可能想将其重写为更有帮助的名称,例如 etcd01 或 prod-worker01。
注意
与 address 字段一样,节点配置后,主机名字段不应更改。
如果您想更改主机名、IP 地址或角色,您应当先移除、清理节点,然后重新加入。如果此字段未设置,RKE 将默认使用 address 字段。
以下字段是 user。此字段在 RKE 创建 SSH 隧道到节点时使用。该账户应具有在不使用 sudo 的情况下运行 docker 命令的权限。此用户必须是 Docker 组的成员或是 root 用户。如果节点级别未定义用户,RKE 将默认为当前运行的用户。标准做法是使用 root 用户或服务账户。Rancher 推荐通常不要使用个人账户,因为您需要设置 SSH 密钥。
这引出了下一个字段:ssh_key_path。这个字段应该是用于连接节点的 SSH 私钥的路径。RKE 要求在运行 RKE 二进制文件的服务器与所有节点之间设置 SSH 密钥。当你 SSH 连接到节点时,如果没有设置ssh_key_path,或者提示输入密码时,它会默认使用集群的默认选项,默认情况下ssh_key_path未设置时,RKE 会默认为~/.ssh/id_rsa。
这与下一个部分port相关,port是 RKE 用于连接 SSH 服务器的端口。RKE 默认使用端口22。通常情况下,这个端口不会更改,但在少数情况下,当使用端口转发时,多个服务器可以共享同一个公共 IP 地址,而不需要 RKE 直接访问节点的 IP。接下来的字段是docker_socket,这是 Docker 套接字的文件路径。这个文件是一个 Unix 域套接字,有时也称为 IPC 套接字。这个文件提供了访问 dockerd 的 API。
请注意,这个 API 没有认证或加密,并且具有对 Docker 及其容器的完全控制权。这个文件必须得到保护,因此默认情况下,文件的拥有者是 root 用户和 Docker 组。RKE 使用这个文件连接到 Docker 引擎以执行命令、创建容器等操作。
最后,我们进入role字段。这些字段定义了分配给节点的角色(etcd、controlplane或worker)。你可以根据需要混合和匹配这些角色分配。例如,标准的三节点集群将所有三个节点分配有这三种角色。我们将在架构解决方案的规则部分详细介绍。
以下部分是我所说的全局设置部分。在这一部分,你可以定义集群级别的设置。我们将涵盖最常见的设置,完整的集群设置列表可以在rancher.com/docs/rke/latest/en/config-options/找到。第一个字段是cluster_name,用于设置集群的名称。此设置不会影响集群,唯一的实际变化是 RKE 生成的kubeconfig文件中会包含集群名称,这样将kubeconfig映射到集群时会更加直观。默认情况下,RKE 会将此设置为 local,并且可以随时更改。
下一个最常见的设置是ignore_docker_version。此设置告知 RKE 是否应该忽略节点上不支持的 Docker 版本。RKE 具有一个内置的元数据文件,映射了所有经过 Rancher 测试并批准的支持版本。通常,Docker 会随着操作系统的标准修补程序进行升级,这可能会导致如果 RKE 发布版本不够新,RKE 无法升级集群。将此设置为true是很常见的,这样 RKE 仍然会在日志中抛出警告信息,但会继续构建集群。
下一个字段可能是你可以设置的最重要的设置,那就是 kubernetes_version。默认情况下,当创建 RKE 时,它会设置一个默认的 Kubernetes 版本。这通常是构建时官方支持的最高版本。例如,RKE v1.2.3 会默认使用 Kubernetes 版本 v1.19.4-rancher1-1,在创建集群时是可以的。但是,如果后来有人将 RKE 升级到 v1.3.1,而这个版本的新默认 Kubernetes 版本是 v1.21.5-rancher1-1,假设你没有在 cluster.yaml 文件中设置 Kubernetes 版本。下一个 RKE 升级事件将导致我所称的意外升级。这可能没问题,但已知会引发一些问题。我们不想在没有测试和规划的情况下升级集群。因此,Rancher 通常建议在 cluster.yaml 中设置 kubernetes_version,作为一种安全措施。
注意
此设置还会设置所有 Kubernetes 组件的镜像版本标签,例如 etcd、kube-apiserver、canal、ingress-nginx-controller 等等。
这引出了下一个字段,system_images。该部分列出了所有不同组件的所有 Docker 镜像及其标签。例如,etcd: rancher/coreos-etcd:v3.1.12 设置了用于 etcd 的 Docker 镜像。请注意,默认情况下,Docker 会从 Docker Hub 拉取没有注册表名称的镜像。你可以使用 --registry-mirror 标志来改变这种行为,强制 Docker 使用私有注册表。这通常用于与外网隔离的环境,在这些环境中,服务器无法从 Docker Hub 拉取镜像。如果你想了解更多关于如何设置,请参见 Rancher 的文档:rancher.com/docs/rke/latest/en/config-options/system-images/#air-gapped-setups。
最后,我们来到了 services 部分。在该部分,我们将定义每个 Kubernetes 组件的设置,例如,如果你想配置 etcd 备份。请注意,你应该启用 etcd 备份,并且较新的 RKE 版本默认开启本地备份。你可以进入服务、etcd 和 backup_config,在那里你可以通过将 enabled 设置为 true 来启用定期的 etcd 快照。你还可以使用 interval_hours 设置备份的时间间隔。RKE 并不像 cron 那样使用计划任务来安排备份时间。其备份计划是基于 etcd-tools 容器启动的时间。基本过程是,etcd-tools 会在容器启动后立即进行一次备份,然后按照 interval_hours 中定义的小时数休眠,直到进行下一次备份并重复此过程。目前,RKE 没有办法让你在预定时间进行备份。
下一个设置是retention,它设置了etcd-tools在清除快照之前将保留多少小时。当前默认值是 6 小时。但通常会将此设置增加到 72 小时左右。这样做的主要目的是防止备份过快地滚动。例如,如果某个更改在周五晚间进行,你可能要等到周一才能发现。使用默认设置时,你会丢失那个恢复点。但如果你将其设置为 72 小时,你仍然有机会恢复。需要注意的是,etcd 快照是数据库的完整副本,因此,如果你的 etcd 数据库大小为 1 GB,那么备份在压缩之前将是 1 GB,压缩后的备份大多数在 100~200 MB 范围内。默认情况下,RKE 会将备份保存在 etcd 节点的本地目录/opt/rke/etcd-snapshots中,每个 etcd 节点都会有一个完整的备份副本。这对于易用性非常好,但也带来了一个问题:你将备份存储在同一个备份服务器上。当然,你可以设置rsync脚本,将这些数据复制到另一台服务器,或者使用备份工具如 TSM 来备份该目录,甚至可以使用 Veeam 等工具对整个服务器进行镜像备份。但我通常推荐使用 S3 备份选项。
这将引导我们进入下一个部分,即s3backupconfig。这些设置允许你配置etcd-tools将 etcd 快照发送到 S3 存储桶,而不是本地存储。这对于灾难恢复情况非常有帮助,例如丢失数据中心或有人误删了 vCenter 中的所有 etcd 节点,因为通过cluster.yaml、cluster.rkestate和 etcd 备份,我们可以从零重建一个集群。有关此过程的更多详细信息,请查看我的 Kubernetes 灾难恢复大师课程,位于github.com/mattmattox/Kubernetes-Master-Class/tree/main/disaster-recovery。同样重要的是要注意,尽管这是 S3,但这并不意味着你必须使用 AWS 的 S3 服务。任何符合 S3 标准的 S3 提供商都可以使用。
什么是 RKE2 集群?
RKE2,也称为 RKE Government,是 Rancher 的新 Kubernetes 发行版。RKE2 与 RKE 有几个不同之处,第一个就是它更注重安全性。
注意
为了简化本节内容,我们将原始的 RKE 发行版称为RKE1,新的发行版称为RKE2。
在 RKE1 集群中,集群默认并不完全安全,这意味着你需要采取一些步骤(《Rancher 硬化指南》: https://rancher.com/docs/rancher/v2.6/en/security/#rancher-hardening-guide)才能通过 CIS Kubernetes 基准测试。由于这个过程的复杂性以及一些人甚至不知道这件事,导致许多 RKE1 集群没有得到充分保护。RKE2 改变了这一模型,它默认是安全的,要求你通过多个复杂步骤来降低安全性。RKE2 还像 RKE1 一样通过了 CIS Kubernetes 基准测试 v1.5 和 v1.6,并符合 FIPS 140-2 标准。最后,在安全方面,RKE2 过程从一开始就构建了 CVE 扫描,作为构建流水线的一部分,这使得在产品中包含已知的 CVE 问题变得非常困难。
第二个重大区别是从 Docker 转向 containerd。在 RKE1 中,一切都是围绕 Docker 和基于 Docker 的命令/APIs 构建的。而随着 Kubernetes v1.22 宣布不再支持 Docker 运行时,这一迁移对于 Kubernetes 的长期支持是必须的。我需要指出的是,Dockershim 是 Kubernetes CRI(容器运行时接口)和 Docker 之间的适配器,它允许人们在可预见的未来继续在 Kubernetes 中使用 Docker。Rancher 也将维护 cri-dockerd 的一个分支。有关更多细节和官方声明,请参见 github.com/rancher/rancher/issues/30307。话虽如此,RKE2 和 K3s 之所以转向 containerd,是因为它在速度和管理开销方面的优势。Docker 带来了许多 Kubernetes 主机并不需要的工具和库,浪费了资源。因为 Docker 本身就在底层运行 containerd,那么为何不去掉这层,并直接让 kubelet 管理 containerd 呢?
第三个重要变化是从将所有内容运行在容器中,转变为允许一些低级组件(如 kubelet)作为二进制文件直接在主机上运行。由于 kubelet 等项目不在 containerd 中运行,它可以执行如升级 containerd 这样的任务,而不会遇到 RKE1 中的“先有鸡还是先有蛋”的问题。Kubelet 不能升级 Docker,因为第一步是停止 Docker,这会停止 kubelet,进而停止升级。正如下图所示,RKE2 有管理的进程,这些进程直接运行在操作系统上,而不是作为容器运行。

图 4.2 – RKE2 服务器和代理的高级架构图
第四个重大变化是不再使用非 Pod 容器。在 RKE1 中,许多核心组件,如 kubelet、etcd、kube-apiserver、kube-controller-manager 和 kube-scheduler,都是 Docker 容器,但不是 Kubernetes Pod。这是因为这些容器是由 RKE 二进制文件创建和管理的,而不是由 kubelet 管理的。随着 RKE2 的出现,kubelet 成为主机操作系统上的一个二进制文件,kubelet 可以使用静态 Pod。这些 Pod 很特别,因为它们可以在没有 etcd、kube-apiserver 等的情况下创建和启动。这个过程是通过预先创建包含通常由集群提供的所有项的清单文件来完成的,例如 Pod 名称、IP、MAC 地址、机密、卷等,在此时,kubelet 可以像管理任何其他 Pod 一样启动并管理该 Pod。一旦 kubelet 能够连接到集群,该 Pod 就可以被发现并添加到 etcd 中。
这引出了 RKE2 相对于 RKE1 的最重要变化:从集中式控制转向分布式控制模型。在 RKE1 中,所有核心的集群管理任务都是由 RKE1 二进制文件本身管理的。即使 Rancher 管理集群,它也会在 Rancher 服务器内部创建 cluster.yml 文件并运行 RKE1 二进制文件。因此,在 RKE1 中,您必须更新 cluster.yaml 并运行 rke up 命令。这个过程会导致 RKE1 二进制文件进行集群的协调。RKE2 将这一过程转移到集群内部,这意味着 RKE2 集群能够自我管理。RKE2 使用的方法是先引导集群中的第一个主节点,然后将其他主节点和工作节点加入集群。
在 RKE1 迁移到分布式模型的过程中,您可以选择将简单的 YAML 文件部署为附加作业。RKE2 在此基础上进行扩展,允许您在部署过程中部署 YAML 文件和 Helm 图表。这使得您能够将多个任务迁移到集群创建过程中。例如,您在部署 Rancher 的 Longhorn 产品时,它作为集群创建的一部分提供分布式存储服务。这一点非常重要,因为许多环境在开始部署像 Prometheus 这样的服务时需要存储。RKE2 集成了 K3s 的 Helm 控制器,允许您通过 YAML 定义 Helm 图表的安装。然后,Helm 控制器将启动一个 Pod 来处理该图表的部署和升级,最终目标是将大多数集群任务(例如将节点添加到集群或从集群中移除节点)从 Rancher 服务器内的过程转移到下游集群本身的过程。这帮助 Rancher 支持大规模集群管理(单个 Rancher 部署可支持 100 万+ 集群),并通过降低 Rancher 服务器与边缘集群之间的流量来改善边缘集群的数据处理。
最后,将节点加入现有 RKE2 集群的过程与加入 RKE1 集群的过程有很大不同。在 RKE1 中,二进制文件负责将容器部署到新节点上。而在 RKE2 中,存在一个简单的过程,首先启动第一个节点时使用特殊标志 cluster-init,该标志告诉 RKE2 创建一个新集群。这包括创建一个新的 etcd 集群并创建一个新的根 CA(kube-ca)。如果没有设置,RKE2 会为集群生成一个令牌。一旦第一个节点创建完成,Kubernetes API 应该就可以使用,其他节点可以用它来加入集群。因此,你需要为 Kubernetes API 端点设置一个轮询 DNS 记录或负载均衡器。接下来,你需要在集群中的每个新节点上设置 RKE2 集群令牌以及服务器端点。为了使 RKE2 启动,这两个设置都是必需的。这些设置定义在文件 /etc/rancher/rke2/config.yaml 中。请注意,在 RKE2 中,这个文件用于总体配置 RKE2。所以如果你想定义 kubelet 设置、节点标签/污点等,必须保护该文件,因为文件中存储的令牌用于多个安全相关任务,如 Kube-API 访问和 etcd 加密。因此,令牌应视为密码,即它应是唯一的、随机的且足够长。默认长度为 32 个字符。你还应避免使用特殊控制字符,如美元符号、反斜杠、引号等。
什么是 RancherD?
RancherD 是一个特殊的二进制文件,旨在引导 Kubernetes 集群(K3s/RKE2)和 Rancher。其目的是简化创建直接支持 Rancher 服务器应用及其组件的 K3s/RKE2 集群的过程。RancherD 设计为仅在节点上运行一次,运行时,它负责设置 K3s/RKE2 以及为 Rancher 所需的清单文件。当你希望在大规模部署 Rancher 时,这个过程非常有用。例如,如果你希望每个 Kubernetes 集群都有自己的 Rancher 仪表板,而不同的环境不共享同一个 Rancher 部署。很多提供 Rancher 服务的托管公司都会使用这种方式。
需要注意的是,RancherD 在后台使用 RKE2,因此相同的要求和限制适用。我们将在下一节中介绍这些要求和限制。
要求和限制
在这一节中,我们将讨论 RKE 和 RKE2 集群的基本要求及其限制。
基本要求
让我们先来看一下 RKE 的基本要求:
-
在 RKE 将主机加入集群之前,必须先在主机上安装 Docker。
-
RKE 可以在几乎任何 Linux 操作系统上运行,但你应该参考位于
rancher.com/support-maintenance-terms/的 Rancher 支持矩阵。 -
RKE 需要使用 SSH 密钥访问集群中的所有节点。
-
RKE 需要权限以在不使用 sudo 或密码的情况下运行 Docker 命令。
-
集群中的所有节点必须能够相互路由,这意味着你可以将节点放在直接子网中,但必须能够在节点之间直接连接,而无需使用 NAT。
-
RKE 需要根据节点角色在节点之间开放防火墙规则。有关更多详情,请参见
rancher.com/docs/rke/latest/en/os/#ports。 -
一个 RKE 集群至少需要一个节点来承担每个角色,包括 etcd、controlplane 和 worker。集群在满足这一要求之前不会上线。请注意,一个节点可以承担多个角色,包括同时拥有这三个角色。
-
RKE 只需要至少一个核心,具体取决于集群大小和节点角色选择。建议至少使用两个核心,标准配置为四个核心。
-
内存要求取决于节点的角色,其中 etcd 角色需要大约 4 GB,kube-apiserver 需要 4 GB,工作节点角色需要大约 2 GB。
-
对于存储,你需要大约 10 GB 的一级/二级存储(推荐 SSD,但不是必需的)。
-
对于文件系统,RKE 依赖 Docker 存储驱动程序,因此请查看 Docker 的文档:
docs.docker.com/storage/storagedriver/。 -
RKE 不需要自己的文件系统,但建议将
/var/lib/docker存放在独立的文件系统/磁盘上,以防止 Docker 填满根文件系统。 -
在 etcd 节点上,数据库存储在位于
/var/lib/etcd的绑定挂载中,不需要放在专用磁盘上。此外,在更大的集群中,建议将这些数据存储在一级存储上,因为 etcd 对存储延迟较为敏感。 -
如果你使用 RKE 的本地备份选项,
/opt/rke/etcd-snapshots目录应使用独立的文件系统或 NFS 共享,以确保安全。
以下是 RKE2 的要求:
-
RKE2 可以运行在几乎所有的 Linux 操作系统上,但你应参考位于
rancher.com/support-maintenance-terms/的 Rancher 支持矩阵。 -
安装和配置 RKE2 需要主机上的 root 权限。
-
集群中的所有节点必须能够相互路由,这意味着你可以将节点放在直接子网中,但必须能够在节点之间直接连接,而无需使用 NAT。
-
RKE2 需要根据节点角色在节点之间开放防火墙规则。有关更多详情,请参见
rancher.com/docs/rancher/v2.5/en/installation/requirements/ports/。 -
RKE2 需要集群中的主节点和工作节点。请注意,这两个角色可以在同一个节点上。
-
RKE2 只需要至少一个核心,具体取决于集群大小和节点角色选择。建议至少使用两个核心,标准配置为四个核心。
-
内存需求取决于节点的角色,主节点需要 4 GB,而工作节点/代理节点大约需要 2 GB。
-
RKE2 将其数据存储在挂载点
/var/lib/rancher下,包括 containerd 数据、镜像、etcd 等。 -
对于主节点,etcd 存储在
/var/lib/rancher/rke2/server/db/etcd下;建议将此数据存储在一级存储上,因为 etcd 对存储延迟较为敏感。 -
RKE2 默认启用 etcd 备份,并将备份存储在
/var/lib/rancher/rke2/server/db/snapshots下,建议将其存储在独立的文件系统中,或者出于安全原因,使用 NFS 共享。
设计限制和考虑事项
现在,让我们讨论一下两个集群的设计限制。
以下列出了 RKE 的设计考虑事项:
-
创建集群后,无法更改网络提供者(CNI),在 RKE 文档中称为网络插件,相关文档位于
rancher.com/docs/rancher/v2.5/en/faq/networking/cni-providers/。 -
默认情况下,RKE 将使用子网
10.42.0.0/16和10.43.0.0/16作为 Pod 覆盖和服务网络。集群创建后无法更改此设置,因此,如果这些网络与您的当前网络重叠,您需要为集群选择不同的子网。 -
对 Windows 工作节点的支持目前仅限于 Rancher 管理的 RKE 集群。您无法直接使用 RKE 将 Windows 节点加入集群。
-
RKE 目前不支持 ARM64 操作系统。
-
RKE 支持空网环境,但需要执行一些额外步骤,相关步骤可参见 https://rancher.com/docs/rke/latest/en/config-options/system-images/#air-gapped-setups。
-
将 RKE 切换到空网环境可以通过重新配置集群、部署和应用程序来完成。
-
只要数据中心数量为奇数,并且每个数据中心中都有 etcd 和控制平面节点,RKE 集群可以跨数据中心构建。
-
etcd 节点之间的网络延迟应小于 10 毫秒。
以下是 RKE2 的设计考虑事项:
-
创建集群后,无法更改网络提供者(CNI),在 RKE2 文档中称为网络插件,相关文档位于
docs.rke2.io/install/network_options/。 -
默认情况下,RKE2 将使用子网
10.42.0.0/16和10.43.0.0/16作为 Pod 覆盖和服务网络。集群创建后无法更改此设置,因此,如果您当前的网络使用了这些子网之一,您应该更改此设置。 -
截至目前,RKE2 提供对 Windows 节点的实验性支持,但您仍然需要在集群中提供 Linux 节点。
-
截至目前,RKE2 不支持 ARM64 操作系统,尽管 K3s 支持。
注意
有一个公开的功能请求(
github.com/rancher/rke2/issues/1946),要求提供对 ARM64 的支持。 -
RKE2 支持气隙环境,但您需要执行一些额外的步骤,这些步骤位于
docs.rke2.io/install/airgap/。 -
RKE2 在气隙环境中可以通过私有注册表模式和 tarball 模式工作,相关步骤位于
docs.rke2.io/install/airgap/。 -
只要数据中心数量为奇数,并且每个数据中心都有一个主节点,RKE2 集群可以跨数据中心构建。
-
etcd 节点之间的网络延迟应该小于 10 毫秒。
现在我们已经了解了 RKE1 和 RKE2 的限制,我们将利用这些信息以及一系列规则和示例,帮助我们使用 RKE1 和 RKE2 设计解决方案。
解决方案架构规则
本节将介绍一些标准设计及其优缺点。需要注意的是,每个环境都是独特的,需要进行调整,以实现最佳的性能和体验。同时,所有的 CPU、内存和存储大小是推荐的起始值,可能需要根据工作负载和部署过程的需求进行增加或减少。
在设计解决方案之前,您应该能够回答以下问题:
-
是否有多个环境共享同一集群?
-
生产环境和非生产环境的工作负载是否会共享同一个集群?
-
该集群需要什么级别的可用性?
-
该集群是否将在多个数据中心之间跨越多个数据中心的环境中运行?
-
集群中节点之间的延迟是多少?
-
集群中将托管多少个 Pod?
-
您将部署到集群中的 Pod 的平均大小和最大大小是多少?
-
您是否需要为某些应用程序提供 GPU 支持?
-
您是否需要为应用程序提供存储?
-
如果需要存储,您只需要RWO(一次读写),还是需要RWX(多次读写)?
RKE 集群
以下是 RKE 集群的设计方案。
单节点集群
在此设计中,我们将在单个节点上部署一个包含所有角色的 RKE 集群。

图 4.3 – RKE 单节点集群
单节点集群的优点如下所示:
-
设置简单。
-
创建速度快且容易。
-
不需要外部负载均衡器。
-
这是一个适合 CI/CD 管道的优秀集群,适用于需要 Kubernetes 集群进行测试且之后可以销毁的场景。
-
适用于不需要高可用性和扩展的沙箱测试。
-
可以安装在资源有限的开发者笔记本电脑上。
-
单节点 RKE 集群可以后续转换为高可用集群(HA 集群)。
缺点如下所示:
-
无高可用性(HA)。
-
在打补丁和升级过程中需要停机。
-
使用服务器的 IP 地址或主机名作为应用程序端点,而不是 VIP 或 CNAME,可能会导致不良的应用程序行为。
-
许多 Kubernetes 组件通过集群本身获得其高可用性功能,因此许多组件无法像在 HA 集群中那样干净地处理故障。
-
用户应用程序与管理服务共享相同的节点,这意味着一个失控的应用程序可能会导致集群崩溃。
以下是硬件要求:
-
服务器:1 台物理/虚拟服务器
-
CPU:4 核
-
内存:4-8GB
小型三节点集群
在此设计中,我们将部署最小的 RKE 集群,具有完整的高可用性(HA),一个三节点集群,所有节点都具有所有角色。

图 4.4 – 标准三节点 RKE 集群
小型三节点集群的优点如下所示:
-
完整的高可用性 – 您可以丢失集群中的任何节点,仍然可以保持集群和应用程序的可用性。
-
管理简单,因为所有节点具有相同的角色,所以所有节点都是一样的。
-
在打补丁和升级过程中不需要停机。有关更多详细信息,请参阅 Rancher 的零停机文档:
rancher.com/docs/rke/latest/en/upgrades/maintaining-availability/.
缺点如下所示:
-
需要外部负载均衡器或轮询 DNS 记录来访问外部应用程序。
-
用户应用程序与管理服务共享相同的节点,这意味着一个失控的应用程序可能会导致集群崩溃。
-
只有
N+1的可用性,因此在维护任务期间,如果失去一个节点,服务也会中断。
以下是硬件要求:
-
服务器:3 台物理/虚拟服务器
-
CPU:每台服务器 4 核
-
内存:每台服务器 4-8GB
中型集群
在这个设计中,我们将部署标准的 RKE 集群,其中我们已将 Kubernetes 的核心管理服务迁移到它们的节点上。这样做的原因是,随着集群规模的扩大,保护 Kubernetes 的管理服务变得更加关键。此设计尝试在高可用性(HA)和成本之间取得平衡。通过将 etcd 和控制平面共享相同的节点,同时将工作节点角色移到专用节点上来实现。这种设计适用于 2 到 10 个工作节点的集群。

图 4.5 – 具有独立节点的 RKE 集群用于管理服务
中型集群的优点如下:
-
完全高可用 – 你可以丢失集群中的任何一个管理节点(etcd 和控制平面),仍然能够保持完整的集群管理。
-
用户工作负载和管理服务运行在不同的节点上,防止失控的应用程序导致集群崩溃。
-
由于 etcd 的可扩展性限制,etcd 节点超过五个时,性能会下降。因此,通常建议采用纵向扩展设计,而非横向扩展。
-
如果剩余的工作节点有足够的 CPU 和内存,多个工作节点可以故障而不丧失服务。
-
在修补和升级过程中无需停机。详细信息请参阅 Rancher 的零停机文档:
rancher.com/docs/rke/latest/en/upgrades/maintaining-availability/。
缺点如下:
-
外部应用访问需要外部负载均衡器或轮询 DNS 记录。
-
仅有
N+1的可用性,因此在维护任务期间,管理平面(etcd 和控制平面)的节点发生故障时,无法保证服务不中断。 -
创建节点时的额外复杂性,因为你可能需要根据管理节点和工作节点的不同需求来调整节点大小。
以下是 etcd 和控制平面的硬件要求:
-
服务器:3 台物理/虚拟服务器
-
CPU:每台服务器 8 核
-
内存:8-16 GB
注意
工作节点的大小应该根据你的工作负载及其要求来确定。
大型集群
在此设计中,我们扩展了中型集群的设计,但将 etcd 和控制平面分开,并调整了节点的大小和数量。

图 4.6 – RKE 集群,etcd 和控制平面使用独立节点
大型集群的优点如下:
-
完整的高可用性 – 集群中可以丢失任意两个管理节点(etcd 和控制平面),仍然可以完全管理集群。
-
用户工作负载和管理服务在不同节点上运行,防止失控的应用程序使集群崩溃。
-
由于 etcd 的扩展性限制,超过五个 etcd 节点会导致性能下降。因此,通常建议将 etcd 设计为垂直扩展,而非横向扩展。
-
控制平面并不设计为通过增加更多节点来扩展,因为 kube-apiserver 在所有节点上都是活动的,但每个节点都有缓存层以提高性能,因此水平扩展会导致缓存效率降低。
-
N+2可用性,因此在进行维护任务时,如果一个节点发生故障,管理平面(etcd 和控制平面)不会丢失服务。 -
如果剩余工作节点的 CPU 和内存资源足够,多个工作节点可以失败而不丧失服务。
-
补丁和升级期间无需停机。有关更多详细信息,请参见 Rancher 的零停机文档,网址为
rancher.com/docs/rke/latest/en/upgrades/maintaining-availability/。
缺点如下所示:
-
需要外部负载均衡器或轮询 DNS 记录来访问外部应用程序。
-
控制平面中的控制器不可扩展,每次只有一个控制器是领导者。
-
创建节点时需要更多的复杂性,因为您可能需要根据工作节点与管理节点的不同需求来调整节点大小。
以下是 硬件要求:
-
etcd 平面:
-
服务器:5 台物理/虚拟服务器。
-
CPU:每台服务器 8-16 核心。
-
内存:每台服务器为管理平面配置 32-64 GB 内存。
-
存储:推荐使用 NVME 存储。
-
-
控制平面:
-
服务器:4 台物理/虚拟服务器。
-
CPU:每台服务器 8-16 核心。
-
内存:每台服务器为管理平面配置 32-64 GB 内存。注意:建议控制平面节点的内存配置与 etcd 节点相匹配作为起始点。
注意
工作节点的规格应根据您的工作负载及其要求来确定。
-
RKE2 集群
以下是 RKE2 集群的设计建议。
单节点集群
在这种设计中,我们将在单个节点上部署一个包含所有角色的 RKE2 集群。

图 4.7 – 单节点 RKE2
优点如下:
-
简单易于设置。
-
快速且容易创建。
-
无需外部负载均衡器。
-
非常适合 CI/CD 管道任务,需要一个 Kubernetes 集群来测试其部署,测试完成后集群将被销毁。
-
非常适合沙箱测试,在这种测试中不需要高可用性和扩展性。
-
可以安装在资源有限的开发者笔记本环境中。
-
一个单节点的 RKE2 集群可以在以后转换为高可用集群。
缺点如下:
-
无高可用性。
-
在修补和升级过程中需要停机。
-
使用服务器的 IP 或主机名作为应用程序端点,而不是 VIP 或 CNAME,可能会促使应用程序出现不良行为。
-
许多 Kubernetes 组件通过集群本身获得其高可用性特性,因此,许多组件在处理故障时不会像高可用集群那样顺利。
-
用户应用与管理服务共享相同的节点,意味着一个失控的应用可能会导致整个集群崩溃。
以下是硬件要求:
-
服务器:1 台物理/虚拟服务器
-
CPU:2 核
-
内存:4 GB
小型三节点集群
在此设计中,我们将部署一个最小的 RKE2 集群,具备完全的高可用性,一个三节点集群,所有节点都承担所有角色。

图 4.8 – 带有高可用性的三节点 RKE2 集群
优点如下:
-
完全高可用性 – 你可以失去集群中的任何节点,仍然保持集群和应用的完全可用性。
-
管理简单,因为所有节点具有相同的角色,因此所有节点都相同。
-
在修补和升级过程中无需停机。详情请参考 Rancher 的零停机文档:
rancher.com/docs/rke/latest/en/upgrades/maintaining-availability/.
缺点如下:
-
需要外部负载均衡器或轮询 DNS 记录来实现外部应用访问和 RKE2 管理。
-
用户应用与管理服务共享相同的节点,意味着一个失控的应用可能会导致整个集群崩溃。
-
仅有
N+1的可用性,因此在进行维护任务时,若节点发生故障,服务不会中断。
以下是硬件要求:
-
服务器:3 台物理/虚拟服务器
-
CPU:每台服务器 2-4 核
-
内存:每台服务器 4-8 GB
中型集群
在此设计中,我们将部署标准的 RKE2 集群,其中我们已将 Kubernetes 的核心管理服务迁移到专用节点。这是因为随着集群规模的增长,保护 Kubernetes 的管理服务变得愈加关键。此设计旨在在高可用性(HA)与成本之间取得平衡。通过将主节点和工作节点分别部署在独立的节点上来实现。这种设计适用于 2 到 10 个工作节点的集群。

图 4.9 – RKE2 集群,其中管理服务使用独立节点
优点如下:
-
完全高可用性 – 即使集群中的任何一个主节点发生故障,仍然可以保持完整的集群管理。
-
用户工作负载和管理服务运行在不同的节点上,防止失控的应用程序使集群崩溃。
-
由于 etcd 的可扩展性限制,超过五个 etcd 节点会导致性能下降。因此,通常建议垂直扩展设计,而不是水平扩展。
-
即使多个工作节点发生故障,只要剩余工作节点上有足够的 CPU 和内存可用,服务也不会中断。
-
在修补和升级期间无需停机。请参见 Rancher 的零停机文档,了解更多详情:
rancher.com/docs/rke/latest/en/upgrades/maintaining-availability/。
缺点如下:
-
需要外部负载均衡器或轮询 DNS 记录来访问外部应用程序和 RKE2 管理。
-
仅具有
N+1的可用性,因此在进行维护任务时,无法在不损失管理平面(etcd 和控制平面)服务的情况下承受节点故障。 -
创建节点时的额外复杂性,因为您可能需要根据管理节点与工作节点的不同需求进行不同的节点规模配置。
以下是硬件要求:
-
主节点:
-
服务器:3 台物理/虚拟服务器
-
CPU:每台服务器 4 核
-
内存:8 GB
注
工作节点的大小应根据工作负载及其要求进行调整。
-
大型集群
对于较大的 RKE2 集群,设计上有一定的限制,因为在 RKE2 集群中,etcd 和控制平面服务是绑定在一起的,无法分离到不同的平面。唯一可以做的真正变化是将主节点数量从 3 增加到 5,然后开始增加节点的规模。
安装步骤(RKE)
一旦你创建了cluster.yaml文件,你可以通过让 RKE 为你创建一个集群。方法是运行rke up --config cluster.yaml命令。RKE 会寻找cluster.rkestate文件。如果找不到该文件,RKE 将假定你正在创建一个新的集群,这会导致 RKE 创建一个新的根 CA 证书,存放在/etc/kubernetes/ssl目录下。
然后,RKE 会检查集群中是否有 etcd 节点正在添加或移除。如果 RKE 检测到 etcd 的停机设置当前被违反,默认情况下,RKE 只允许一个 etcd 节点处于离线状态。RKE 将处理移除 etcd 节点的过程,包括停止 etcd 容器并将 etcd 成员从 etcd 领导者中移除。接下来,RKE 将处理将任何新的 etcd 节点添加到集群的过程。需要注意的是,这个过程被设计得既缓慢又安全——RKE 一次只处理一个 etcd 节点,并在更改每个节点之前进行 etcd 快照。
一旦 etcd 平面成功完成,RKE 将负责启动控制平面。此过程包括 kube-apiserver、kube-controller-manager 和 kube-scheduler。RKE 将依次在每个控制平面节点上启动各个组件。RKE 将检查每个组件的健康检查端点是否可用,对于大多数组件来说,端点是/healthz。值得注意的是,RKE 遵循与 etcd 平面相同的过程,验证最大不可用设置当前没有被违反。如果在此过程中设置被违反,RKE 将停止并报告错误。
接下来,RKE 将处理创建工作平面。与 etcd 平面和控制平面不同,这一过程是并行进行的。这样做主要是为了处理较大的集群,在这些集群中,可能会有数百个工作节点。因此,默认情况下,RKE 会一次处理 10%的工作节点。对于现有节点,RKE 会将该节点标记为不可用,以防止对节点进行更改。需要注意的是,在此过程中,应用程序的 Pods 会继续运行,唯一的影响是 CNI 提供程序可能需要重新启动。这个过程的影响类似于暂时拔掉节点的网卡几秒钟后再插回去。这可能会影响使用长期连接的应用程序,这些连接需要保持打开状态。通常,这些应用程序会使用数据库连接池,应用程序会创建多个数据库连接并保持它们处于打开状态。根据应用程序的不同,这些连接可能不会自动重新连接,可能需要重新启动。
安装步骤(RKE2)
与 RKE1 不同,RKE2 在集群创建过程中采用了非常不同的处理方式。在 RKE2 中,集群中的第一个主节点是独一无二的,因为它负责集群的引导过程。引导过程会创建一个根 CA 证书,如果集群令牌尚未设置,RKE2 会处理创建一个令牌。接着,RKE2 会初始化 etcd 集群。最后,RKE2 会基于集群令牌创建 etcd 加密密钥。RKE2 然后会将集群状态存储在 etcd 中一个名为 bootstrap 的唯一引导密钥对中。这些引导数据包括 Kubernetes 证书、私钥以及 etcd 加密密钥。一旦集群完成引导,其他主节点就可以使用 Kubernetes API 端点连接到集群中的第一个节点。RKE2 将使用集群令牌进行身份验证并解密引导数据。最后,一旦所有主节点都创建完成,工作节点的设置过程也会按照相同的步骤进行,唯一的区别是 INSTALL_RKE2_TYPE="agent" 安装选项,这告诉 RKE2 将该节点配置为工作节点。
以下是一些示例命令,用于创建一个标准的三主节点集群以及工作节点。关于这些命令的更多细节可以参考 docs.rke2.io/install/ha/。
# 1st master node
curl -sfL https://get.rke2.io | sh -
mkdir -p /etc/rancher/rke2/
cat << EOF > /etc/rancher/rke2/config.yaml
kube-apiserver-arg: "kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname"
tls-san:
- mgmt01.support.tools
- mgmt02.support.tools
- mgmt03.support.tools
- rke2-vip.support.tools
node-taint:
- "CriticalAddonsOnly=true:NoExecute"
EOF
systemctl enable rke2-server.service
systemctl start rke2-server.service
这段前置代码处理了集群中第一个节点的引导工作,并设置了 Kubernetes API 端点的 SAN 证书以及节点污点,以防止用户的工作负载在该服务器上运行。
# Capture the node token from the first node
cat /var/lib/rancher/rke2/server/node-token`
我们需要在前面的代码块中使用这个令牌,以便其他节点能够加入集群。
# 2nd, 3rd master nodes
curl -sfL https://get.rke2.io | sh -
mkdir -p /etc/rancher/rke2/
cat << EOF > /etc/rancher/rke2/config.yaml
kube-apiserver-arg: "kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname"
tls-san:
- mgmt01.support.tools
- mgmt02.support.tools
- mgmt03.support.tools
- rke2-vip.support.tools
node-taint:
- "CriticalAddonsOnly=true:NoExecute"
server: https://<<Cluster DNS record>>:9345
token: <<Node Token goes here>>
EOF
systemctl enable rke2-server.service
systemctl start rke2-server.service
这段前置代码处理了将额外的主节点加入到现有集群中的过程。
# Worker nodes
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" sh -
mkdir -p /etc/rancher/rke2/
cat << EOF > /etc/rancher/rke2/config.yaml
server: https://<<Cluster DNS record>>:9345
token: <<Node Token goes here>>
EOF
systemctl enable rke2-agent.service
systemctl start rke2-agent.service
这段前置代码接着处理了将工作节点加入到我们刚刚创建的集群中的过程。
现在我们已经成功创建了集群,下一步将是准备一个外部负载均衡器,作为集群的前端端点。在接下来的部分中,我们将配置 HAProxy 的 HTTP 和 TCP 模式。这些设置非常标准,你应该可以将它们作为其他负载均衡技术的模板,例如 F5 或 A10。
配置外部负载均衡器(HAProxy)
使用 RKE/RKE2 集群,您将得到一个 ingress-nginx-controller。这是一个守护进程集,默认情况下会在所有工作节点上运行。默认情况下,nginx 将监听端口 80 和 443。然后,nginx 将充当第 7 层(HTTP/HTTPS 模式)负载均衡器,用于集群内托管的应用程序。这对于集群内应用的负载均衡非常有用,但您会遇到如何在节点之间提供冗余的问题。最简单的方法是为集群中的所有工作节点的 IP 地址创建一个 DNS A 记录,并使用轮询 DNS 在节点之间进行负载均衡并处理故障容忍。缺点是轮询 DNS 更新速度可能非常慢,且您必须依赖客户端来操作故障转移。在现实世界中,这个过程可能非常不可靠。为了解决这个问题,我们将在集群前放置一个 HAProxy 服务器。对于其他负载均衡器,如 A10、F5、nginx 等,过程是非常相似的。接下来,我们将介绍配置 HAProxy 的两种不同方式。
TCP 模式
这种模式仅负责在传输协议层转发数据,在此情况下,我们只关注 TCP/80 和 TCP/443。HAProxy 不终止连接,因此诸如基于主机的路由和 SSL 等功能不可用。因此,TCP 模式有时被称为 第四层 负载均衡,因为它只是转发流量。所以,在这种情况下,我们将有一个前端 虚拟 IP 地址(VIP),它会对 TCP 端口进行一对一映射。需要注意的是,默认情况下,TCP 模式没有启用任何会话管理。使用源 IP 匹配来允许 TCP 模式中的粘性会话是正常的。这对于使用基于服务器的会话管理的应用程序来说是必要的。

图 4.10 – HAProxy 示例设计
图示:github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch04/haproxy/diagrams/tcp_mode.md
这是一个示例配置:

图 4.11 – 在 TCP 模式下的 HAProxy 配置示例
完整配置:github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch04/haproxy/config/tcp.cfg
请注意,在这个示例配置中,我在 HAProxy 中暴露了两个额外的端点,第一个是 Prometheus 指标端点,它允许 Prometheus 服务器抓取 HAProxy 的指标。更多细节请参见 www.haproxy.com/blog/haproxy-exposes-a-prometheus-metrics-endpoint/。
第二个是 stats 端点,它允许你查看前端和后端部分的当前状态。在故障排除时,这非常有帮助。请参阅 www.haproxy.com/blog/exploring-the-haproxy-stats-page/ 获取更多详细信息。需要注意的是,这些端点应该通过基本用户登录页面和防火墙规则进行保护。
HTTP/HTTPS 模式
此模式负责终止 HTTP 和 SSL 连接。因此,HAProxy 可以修改请求或做出路由决策。例如,dev.example.com 可以被路由到 dev RKE 集群,而 prod.example.com 被路由到生产 RKE 集群,尽管它们共享相同的 VIP。

图 4.12 – HTTP 模式下的 HAProxy 配置示例
图表:github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch04/haproxy/diagrams/http_mode.md
对于不希望使用外部负载均衡器的环境,MetalLB 是一种替代选项,接下来我们将介绍在最简单的形式下安装和配置 MetalLB,即 Layer2 模式。
配置 MetalLB
MetalLB 替代了外部负载均衡器的需求。它通过使用 VIP 或 边界网关协议(BGP) 宣布外部 IP 地址,然后使用端口映射将流量转发到 Kubernetes 服务。每个由 MetalLB 暴露的服务都有其自己的 IP 地址,该地址从在 ConfigMap 中定义的 IP 地址池中提取。MetalLB 使用名为 speaker 的 daemonset 来处理节点上 IP 地址的分配,而控制器则处理协调。有关此过程如何工作的更多详细信息,请参见 MetalLB 文档 metallb.universe.tf/concepts/。
安装
以下步骤将安装 MetalLB 的控制器及其 speaker。关于此过程的更多详细信息,请参见 metallb.universe.tf/installation/:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.3/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.3/manifests/metallb.yaml
配置
在此步骤中,我们将定义 IP 地址池。关于此过程的更多详细信息,请参见 metallb.universe.tf/configuration/。
使用以下值创建一个 configmap:
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.1.240-192.168.1.250
最后,要将 MetalLB IP 添加到集群服务中,只需在服务定义中添加以下注释。关于此过程的更多详细信息,请参见 metallb.universe.tf/usage/:
annotations:
metallb.universe.tf/address-pool: default
总结
本章中,我们学习了 RKE、RKE2 和 RancherD,包括这些工具的工作原理。然后,我们讨论了每个工具的要求和限制。我们还讲解了架构 RKE 和 RKE2 集群的规则,包括一些示例配置以及每种解决方案的优缺点。最后,我们详细介绍了使用我们之前创建的配置文件创建集群的步骤。随后,我们结束本章内容,讲解了如何安装和配置 HAProxy 和 MetalLB 作为 RKE 和 RKE2 集群的负载均衡器。完成本章后,你应该能够设计一个符合环境需求的解决方案,并部署集群类型。此外,通过了解每个集群的工作方式,你应该能够排除大多数基本问题。
在下一章中,我们将介绍如何在托管集群上部署 Rancher,以及需要遵循的一些限制和规则。
第五章:第五章:在托管 Kubernetes 集群上部署 Rancher
Rancher 的一个优点是可以部署在任何认证的 Kubernetes 集群上。这意味着 Rancher 可以安装在托管的 Kubernetes 集群上,比如Google Kubernetes Engine(GKE)、亚马逊的Elastic Container Service(EKS) for Kubernetes、Azure Kubernetes Service(AKS)或Digital Ocean's Kubernetes Service(DOKS)。这可以简化 Rancher 的管理,但托管的 Kubernetes 解决方案存在一些限制。接下来,我们将介绍托管 Kubernetes 集群的设计规则以及一些标准设计。到那时,我们将使用Helm工具在集群上安装 Rancher,从而在集群上安装 Rancher 服务器工作负载。最后,我们将介绍如何在托管的 Kubernetes 集群上备份 Rancher。
在本章中,我们将涵盖以下主要主题:
-
了解托管的 Kubernetes 集群
-
要求与限制
-
架构解决方案的规则
-
创建托管的 Kubernetes 集群
-
安装和升级 Rancher
-
Rancher 备份操作员
让我们深入了解吧!
了解托管的 Kubernetes 集群
在云中部署 Kubernetes 集群时,常见的问题之一不仅仅是使用托管的 Kubernetes 集群,而是托管的 Kubernetes 集群是什么。简而言之,它是一个由外部方部署和管理的集群。通常,这种集群由云服务提供商提供,诸如亚马逊的 AWS、谷歌的 GCP、微软的 Azure 等。这种服务有时被称为Kubernetes as a Service(KaaS),因为这些类型的集群作为服务提供。作为消费者,托管的 Kubernetes 集群相比自己构建的集群有一些限制:
-
控制:使用托管的 Kubernetes 集群时,你是最终用户。你无法完全控制集群。像将 Kubernetes 升级到更高版本这样的任务通常由你的提供商为你处理。通常情况下,这是通过你进入云服务提供商的仪表板并选择一个更新的 Kubernetes 版本来触发的。然而,大多数云服务提供商有强制升级的选项,可能没有你的参与。例如,在 2020 年初,EKS 开始逐步淘汰 Kubernetes v1.14,并在 2020 年 11 月停止官方支持。一旦支持结束,亚马逊开始自动升级集群,你几乎无法阻止该升级。如果升级破坏了你的应用程序,就无法回退或降级。你唯一的选择就是修复你的应用程序。谷歌和 Azure 也有类似的流程,他们的理由是集群端点通常位于公共互联网(在大多数情况下),因此保持安全补丁更新是必须的。
-
访问权限:通过托管的 Kubernetes 集群,你将能够访问 Kube API 端点,用于 kubectl、Helm 甚至 Rancher 等工具。但在大多数情况下,你无法访问 Kubernetes 节点本身。因此,你不能直接 SSH 进入节点并安装如监控代理和备份软件等软件。而且,即使云提供商为你提供了对节点的 SSH 访问权限,通常也仅限于对工作节点进行故障排除。他们的支持团队不会支持你对节点所做的任何自定义操作。此外,你根本不应该进行任何更改,因为云提供商可以根据需要更换节点,且通常不会提前通知。
注意
所有主要云提供商都允许你设置首选的维护窗口,但如果需要,他们可以在该窗口之外进行紧急维护。
这通常用于替换故障节点或应用关键的安全修复。
-
自定义:对于大多数托管的 Kubernetes 集群,云提供商定义了如 etcd、kube-apiserver 和 kubelet 等项目。例如,如果你的应用程序正在访问 Kube API 端点并创建大量请求,在自托管的 Kubernetes 集群中,你可以简单地增加 kube-apiserver 可用的 CPU 和内存。而在托管的 Kubernetes 集群中,由于该服务由云提供商拥有,你无法进行这样的更改。定制安全设置(如 etcd 加密)也一样。在自托管的 Kubernetes 集群中,你可以根据需要设置加密。而在托管的 Kubernetes 集群中,你只能使用他们提供的服务。例如,EKS 支持使用 AWS 密钥管理服务 (KMS) 进行 etcd 加密。但在 AKS 中,Azure 默认启用加密,但不允许你更改或强制轮换密钥。对于其他云提供商(如 DigitalOcean),他们根本没有提供 etcd 加密。
注意
前述陈述在撰写时有效,但 Azure 已表示这是其未来计划的一部分,因此未来可能会发生变化。
-
tarball文件,然后将其推送到备份位置。
现在我们了解了托管的 Kubernetes 集群是什么,接下来我们将探讨一些最受欢迎的云提供商的要求和限制。
要求和限制
本节将讨论 Rancher 在各种集群上的基本要求,以及它们的限制和设计考虑因素。
Amazon EKS
Amazon EKS 的 基本要求如下:
-
Rancher 至少需要两个工作节点,但强烈建议使用三个节点。
-
每个工作节点应该至少有两个核心和 4 GB 内存。
-
Rancher 需要一个网络负载均衡器来访问 Rancher 控制台。
-
一旦 EKS 集群创建完成,你需要按照
docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html上的步骤生成一个 kubeconfig 文件,用于访问集群。 -
Rancher 要求 EKS 集群中安装 nginx-ingress-controller。有关详细信息,请按照
rancher.com/docs/rancher/v2.5/en/installation/install-rancher-on-k8s/amazon-eks/#5-install-an-ingress上的步骤进行操作。 -
443/TCP端口应该对所有需要 Rancher UI/API 访问的下游节点、集群和最终用户开放。注意
80端口将把最终用户重定向到 HTTPS URL。因此,虽然80端口不是必需的,但为了最终用户的方便,建议使用。
设计限制和注意事项如下:
-
集群应该跨越三个可用区。
-
默认情况下,EKS 使用在 VPC 中定义的 DNS 服务器。如果你需要通过 DNS 访问本地资源,应该按照
docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html上的步骤进行操作。 -
假设你正在为集群阻止外部互联网访问,在这种情况下,如果你计划使用 Amazon 弹性容器注册表(ECR)来完成此角色,你将需要提供一个私有注册表。你需要按照
docs.aws.amazon.com/AmazonECR/latest/userguide/ECR_on_EKS.html上的步骤配置集群的 IAM 权限。 -
你可以使用节点自动扩展组,但集群的上下扩展可能会导致 Rancher UI 中断,并且集群操作会在短时间内失败,包括通过 Rancher API 访问下游集群的丧失。
-
如果你使用 AWS 证书管理器,应该选择一个会自动续期并具有相同根 CA 的证书。这是因为 Rancher 需要根 CA 的校验和用于代理。因此,更改根 CA 会涉及大量工作,我们将在后续章节中详细介绍。
-
Rancher 服务器确实提供基于 ARM64 的镜像。因此,你可以在集群中使用 ARM64 节点,但你可能仍然需要一个 AMD64 节点来运行其他服务和容器,如 Prometheus,目前它不支持 ARM64。
-
EKS 不会自动从 kubelet 故障中恢复,可能需要用户干预。
-
EKS 根据节点的大小限制每个节点的 Pod 数量。有关详细信息,请参见 Amazon 的文档,位于
github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt。
谷歌的 GKE
GKE 的基本要求如下:
-
Rancher 要求集群中至少有两个工作节点,但强烈推荐使用三个节点。
-
每个工作节点应至少有两个核心和 4GB 内存。
-
Rancher 要求使用网络负载均衡器来访问 Rancher 控制台。
-
创建 GKE 集群后,您需要按照
cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl上的步骤生成一个 kubeconfig 文件来访问集群。 -
Rancher 要求 GKE 集群上安装 nginx-ingress-controller。详细步骤请参见
rancher.com/docs/rancher/v2.5/en/installation/install-rancher-on-k8s/gke/#5-install-an-ingress。 -
入站端口
443/TCP 应为所有下游节点、集群和需要访问 Rancher UI/API 的终端用户打开。注意:端口80会将终端用户重定向到 HTTPS URL,因此虽然不是必需的,但为了方便起见,建议开启。
设计限制和考虑因素如下:
-
集群应跨三个可用区。
-
您不能自定义服务器配置。必须使用他们提供的两种服务器类型之一:Container OS 或 Ubuntu。您不能选择 Kubernetes 版本或内核版本。
-
集群附加服务,如 Kube-DNS 和 ip-masq-agent,在可配置性方面非常有限。
-
GKE 目前不支持 ARM64 架构。
Azure 的 AKS
AKS 的基本要求如下:
-
Rancher 要求集群中至少有两个工作节点,但强烈推荐使用三个节点。
-
每个工作节点应至少有两个核心和 4GB 内存。
-
Rancher 要求使用网络负载均衡器来访问 Rancher 控制台。
-
创建 AKS 集群后,您需要按照
cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl上的步骤生成一个 kubeconfig 文件来访问集群。 -
Rancher 要求 AKS 集群上安装 nginx-ingress-controller。详细步骤请参见
rancher.com/docs/rancher/v2.5/en/installation/install-rancher-on-k8s/gke/#5-install-an-ingress。 -
入站端口
443/TCP 应为所有下游节点、集群和需要访问 Rancher UI/API 的终端用户打开。注意:端口80会将终端用户重定向到 HTTPS URL,因此虽然不是必需的,但为了方便起见,建议开启。
设计限制和考虑因素如下:
-
集群应跨三个可用区。
-
相比于 EKS 和 GKE,AKS 相对较新,因此许多功能仍然没有正式发布(GA)。
-
操作系统的选择仅限于 Ubuntu 和 Windows Server。
注意
Rancher 服务器无法在 Windows 节点上运行。
-
节点升级不像 GKE 那样自动化,需要手动进行操作。
-
AKS 不会自动从 kubelet 故障中恢复,可能需要用户干预。
-
AKS 目前不支持 ARM64 架构。
我们现在了解了在托管 Kubernetes 集群上运行 Rancher 的限制。接下来,我们将利用这些限制和一套规则及示例,帮助我们设计使用主要云提供商的解决方案。
解决方案架构设计的规则
在本节中,我们将介绍一些标准设计以及每种设计的优缺点。需要注意的是,每个环境都是独特的,需要进行调整以获得最佳的性能和体验。还需要注意的是,所有的 CPU、内存和存储大小都是推荐的起始点,可能需要根据 Rancher 管理的节点和集群数量进行调整。
在设计解决方案之前,你应该能够回答以下问题:
-
你是否会将非生产集群和生产集群分隔到各自的 Rancher 环境中?
-
对于混合云环境,你是否会按提供商将集群分开?例如,你是否会为所有 AWS 集群部署一个 Rancher 服务器实例,为所有本地集群部署另一个 Rancher 服务器实例?
-
你是否需要为 Kubernetes 节点配置公有和私有 IP 地址?
-
你是否会在 Rancher 集群上托管任何额外的应用程序?如果是,CPU、内存和存储的要求是什么?
-
你需要在不同区域之间进行站点到站点的复制吗?
-
你计划支持多少节点和集群?
注意
Rancher 的官方服务器尺寸指南可以在
rancher.com/docs/rancher/v2.5/en/installation/requirements/#rke-and-hosted-kubernetes找到。
亚马逊 EKS
在本节中,我们将讨论一些主要的 EKS 集群设计方案。
EKS 小型集群
在这个设计中,我们将部署能够运行 Rancher 的最小 EKS 集群。请注意,这个设计仅适用于测试或实验环境,不建议用于生产部署,且只能处理几个集群,每个集群最多包含十几个节点。

图 5.1 – 带有两个工作节点的 EKS 小型集群
优点如下:
-
节点级冗余;你可以失去一个工作节点而不会导致 Rancher 宕机。
-
在 EKS 补丁和升级过程中无需停机。更多细节请参见
docs.aws.amazon.com/eks/latest/userguide/update-managed-node-group.html。
缺点如下:
-
如果你运行像 Prometheus 或 Grafana 这样的额外应用程序,节点可能会资源不足。
-
只有
N+1的资源可用性,因此在维护任务期间,你不能承受一个节点的故障而不会中断服务。注意
在节点组升级期间,Amazon 会在删除旧节点之前先添加一个新节点。
-
你需要定制 Rancher 安装,只使用一个副本,而不是默认的三个副本。
节点规模要求如下:
-
一个节点组,组内有两个节点
-
CPU:每个节点 2 核
-
内存:每个节点 4 GB
EKS 使用典型的集群大小和可用区冗余
在这个设计中,我们将通过增加一个工作节点来扩展 EKS 小型设计,使我们拥有三个工作节点。我们还将利用 AWS 的 可用区 (AZ) 冗余,在三个 AZ 中之一部署一个工作节点。通过这样做,集群能够处理一个 AZ 的故障而不影响 Rancher。我们还将增加工作节点的大小,以管理最多 300 个集群和 3000 个节点。

图 5.2 – EKS 标准设计,包含三个工作节点和 AZ 冗余
优点如下:
-
节点级冗余:你可以失去一个工作节点,而不会导致 Rancher 中断。
-
可用区冗余:你可以在 Rancher 中丧失一个完整的可用区而不会导致中断;这也包括在负载均衡器级别。
-
在 EKS 修补和升级期间无需停机。详情请参见
docs.aws.amazon.com/eks/latest/userguide/update-managed-node-group.html。 -
N+2可用性:在维护任务期间,你可以承受一个节点的故障而不会中断服务。
缺点如下:
-
额外的工作节点成本。
-
设置期间的额外复杂性,因为每个可用区都有自己的节点组。
-
NLB 的额外复杂性,因为它必须在每个可用区中都有一个接口。
-
升级期间的额外复杂性,因为每个节点组需要独立升级。
节点规模要求如下:
-
三个节点组,每个组中有一个节点
-
CPU:每个节点 8 核
-
内存:每个节点 16 GB
Google 的 GKE
本节将介绍一些 GKE 集群的主要设计方案。
GKE 小型集群
在此设计中,我们将部署最小的 GKE 集群,它仍然可以运行 Rancher。请注意,该设计仅适用于测试或实验环境,不建议用于生产部署,且只能处理几个集群,每个集群约有十几个节点。

图 5.3 – GKE 小型集群,包含两个工作节点
优点如下:
-
节点级冗余:你可以失去一个工作节点,而不会导致 Rancher 中断。
-
GKE 修补和升级期间无需停机。更多详细信息请参见
cloud.google.com/kubernetes-engine/docs/concepts/cluster-upgrades。
缺点如下:
-
如果您运行额外的应用程序,如 Prometheus 或 Grafana,节点可能会耗尽资源。
-
只有
N+1的可用性,因此在进行维护任务时,您不能在没有服务中断的情况下丧失一个节点。注意
在集群升级期间,Google 会在移除旧节点之前添加新节点。
-
使用 GCP 的集群升级自动驾驶时,可能会卡住,无法终止 Rancher 服务器 Pods。如果维护窗口过小,升级将被暂停,导致集群处于部分升级状态。我建议维护窗口至少为 4 小时。
-
您确实需要自定义 Rancher 安装,只使用一个副本,而不是默认的三个副本。
节点配置要求如下:
-
一个节点池,池内有两个节点
-
CPU:每个节点 2 个核心
-
内存:每个节点 4 GB
GKE 使用典型的集群大小,并具备区域冗余
在这种设计中,我们将通过增加一个工作节点来扩展 GKE 的小型设计,使我们拥有三个工作节点。我们还将通过在三个可用区之一中放置一个工作节点,利用 GCP 的区域冗余。这样,集群可以在不影响 Rancher 的情况下处理一个可用区的故障。我们还将增加工作节点的大小,以支持最多 300 个集群,3,000 个节点。

图 5.4 – GKE 标准配置,包含三个工作节点和区域冗余
优点如下:
-
节点级冗余:您可以在 Rancher 中失去一个工作节点而不会发生故障。
-
区域冗余:您可以在 Rancher 中失去整个可用区(AZ),而不会发生故障;这也包括负载均衡器层面。
-
GKE 修补和升级期间无需停机。更多详细信息请参见
cloud.google.com/kubernetes-engine/docs/concepts/cluster-upgrades。 -
N+2的可用性:在进行维护任务时,您可以在不影响服务的情况下丧失一个节点。 -
升级时无需额外复杂操作,因为自动驾驶功能会为您处理所有升级。
缺点如下:
-
增加额外的费用以支持额外的工作节点。
-
设置时增加复杂性,因为每个可用区都有自己的节点池。
节点配置要求如下:
-
三个节点池,每个池中有一个节点
-
CPU:每个节点 8 个核心
-
内存:每个节点 16 GB
Azure 的 AKS
在本节中,我们将讨论一些主要的 AKS 集群设计。
AKS 小型集群
在这个设计中,我们将部署一个最小的 AKE 集群,该集群仍然可以运行 Rancher。AKS 的一个特殊之处在于它支持只有一个节点的集群。正如前面提到的,这个设计仅适用于测试或实验室环境,不建议用于生产部署,且只能处理几个集群,每个集群大约有十几个节点。需要注意的是,AKS 确实支持 Windows 节点池,但 Rancher 必须运行在 Linux 节点上。

图 5.5 – AKS 单节点集群
优点如下:
-
由于只需支付单个节点的费用,因此成本较低。
-
Azure 确实支持在升级过程中增加节点,这意味着在隔离和排空旧节点之前,Azure 会先为集群提供一个新节点。
缺点如下:
-
节点出现故障时,您不能失去服务。
注意
在集群升级过程中,Azure 会先添加一个新节点,然后再移除旧节点。
-
如果您运行如 Prometheus 或 Grafana 等额外应用程序,节点可能会耗尽资源。
-
如果节点故障,Azure 将停止升级而不回滚。
-
您确实需要自定义 Rancher 安装,仅使用一个副本而不是默认的三个副本。
节点大小要求如下:
-
一个节点池中只有一个节点
-
CPU:每个节点 2 核
-
内存:每个节点 4 GB
AKS 使用典型集群大小并支持区域冗余
在这个设计中,我们将通过增加两个工作节点来扩展 AKS 单节点设计,最终形成三个工作节点。我们还将通过将一个工作节点放在三个可用区中的一个来利用 Azure 的区域冗余。通过这样做,集群可以在不影响 Rancher 的情况下处理一个 AZ 的故障。我们还将增加工作节点的大小,以支持最多 300 个集群和 3000 个节点。

图 5.6 – AKS 标准集群,包含三个节点
优点如下:
-
节点级冗余:您可以失去一个工作节点而不会导致 Rancher 中断。
-
区域冗余:您可以失去整个区域而不会导致 Rancher 中断;这也包括负载均衡器级别。
-
在 AKS 修补和升级期间无需停机。有关更多细节,请参阅
docs.microsoft.com/en-us/azure/aks/upgrade-cluster。 -
N+2的可用性:在维护任务期间,您可以承受一个节点的故障,而不会导致服务中断。
缺点如下:
-
为额外的工作节点增加额外的成本。
-
由于每个区域都有其节点池,因此在设置过程中会增加额外的复杂性。
-
区域可用性支持仅限于某些区域。有关更多详情,请参阅
docs.microsoft.com/en-us/azure/aks/availability-zones#limitations-and-region-availability。 -
如果你使用的是 Azure 磁盘存储,卷不能跨可用区进行附加。请参见
docs.microsoft.com/en-us/azure/aks/availability-zones#azure-disks-limitations获取更多详细信息。
节点大小要求如下:
-
三个节点池,每个池中有一个节点
-
CPU:每个节点 8 核
-
内存:每个节点 16 GB
现在我们有了集群的设计,接下来的部分将介绍创建每种主要集群类型的步骤。
创建托管的 Kubernetes 集群
本节将介绍创建每个托管 Kubernetes 集群的命令。
Amazon EKS
本节将介绍如何使用命令行工具创建带有入口的 EKS 集群。
注意
以下步骤是一般指南。请参考aws-quickstart.github.io/quickstart-eks-rancher/获取更多详细信息。
前提条件
你应该已经拥有一个具有管理员权限的 AWS 账户,并且已创建 VPC 和子网。
你应该在工作站上安装以下工具:
-
AWS CLI v2: 请参考
docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html获取更多详细信息。 -
eksctl: 请参考
docs.aws.amazon.com/eks/latest/userguide/eksctl.html获取更多信息。 -
kubectl: 请参考
docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html获取更多信息。 -
Helm: 请参考
helm.sh/docs/intro/install/获取更多信息。
创建集群
接下来我们来看一下步骤:
-
运行
aws configure命令并输入你的访问密钥和秘密密钥,这将为你提供访问 AWS 账户的权限。 -
运行以下命令来创建 EKS 集群:
eksctl create cluster --name rancher-server --version 1.21 --without-nodegroup注意
在这个示例中,我们将创建一个标准的三节点集群,每个可用区(AZ)有一个节点。
-
运行以下命令将第一个节点池添加到集群:
eksctl create nodegroup --cluster=rancher-server --name=rancher-us-west-2a --region=us-west-2 --zones=us-west-2a --nodes 1 --nodes-min 1 --nodes-max 2
这将创建us-west-2a中的节点池。
-
运行以下命令将第二个节点池添加到集群:
eksctl create nodegroup --cluster=rancher-server --name=rancher-us-west-2b --region=us-west-2 --zones=us-west-2b --nodes 1 --nodes-min 1 --nodes-max 2
这将创建us-west-2b中的节点池。
-
运行以下命令将第三个节点池添加到集群:
eksctl create nodegroup --cluster=rancher-server --name=rancher-us-west-2c --region=us-west-2 --zones=us-west-2c --nodes 1 --nodes-min 1 --nodes-max 2
这将创建us-west-2c中的节点池。
-
要验证集群,运行以下命令:
eksctl get cluster注意
集群上线可能需要 5 到 10 分钟。
-
接下来,使用以下命令安装
nginx-ingress-controller:helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm upgrade --install \ ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --set controller.service.type=LoadBalancer \ --version 3.12.0 \ --create-namespace
创建负载均衡器
如果你只是测试,可以运行kubectl get service ingress-nginx-controller -n ingress-nginx命令来捕获外部 DNS 记录。然后你可以创建一个CNAME DNS 记录指向此记录。
注意
不应在生产环境中使用此方法。
要创建前端负载均衡器,请参考 docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-elb-load-balancer.html 以获取更多信息。
此时,集群已准备好安装 Rancher。我们将在下一节中介绍此步骤。
Google 的 GKE
本节将介绍如何通过命令行工具创建一个带有 ingress 的 GKE 集群。
注意
以下步骤为一般指南。更多详细信息,请参考 cloud.google.com/kubernetes-engine/docs/quickstart。
先决条件
你应该已经拥有一个具有管理员权限的 GCP 账户。本节将使用 Cloud Shell,其中大部分工具已安装。
设置 Cloud Shell
-
前往 GCP 控制台右上角并点击 Terminal 按钮。
-
运行
gcloud components install kubectl命令以在 GCP 终端中安装 kubectl 客户端。 -
运行
gcloud init命令以配置权限。
创建集群
-
要在每个可用区创建一个节点,请运行以下命令:
gcloud container clusters create rancher-server --zone us-central1-a --node-locations us-central1-a,us-central1-b,us-central1-c --num-nodes=3 -
使用以下命令获取
kubeconfig文件:gcloud container clusters get-credentials rancher-server
集群上线可能需要 5 到 10 分钟。
-
使用以下命令安装
nginx-ingress-controller:helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm upgrade --install \ ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --set controller.service.type=LoadBalancer \ --version 3.12.0 \ --create-namespace
创建负载均衡器
如果你只是进行测试,可以运行 kubectl get service ingress-nginx-controller -n ingress-nginx 命令以获取外部 IP。然后,你可以创建 DNS 记录并指向该 IP。
注意
不应在生产环境中使用此方法。
要创建前端负载均衡器,请参考 cloud.google.com/kubernetes-engine/docs/concepts/ingress 以获取更多信息。
此时,集群已准备好安装 Rancher。我们将在下一节中介绍此步骤。
Azure 的 AKS
本节将介绍如何通过命令行工具创建一个带有 ingress 的 AKS 集群。
注意
以下步骤为一般指南。更多详细信息,请参考 docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal。
先决条件
你应该已经拥有一个具有管理员权限的 Azure 账户。
你的工作站上应安装以下工具:
-
Azure CLI:更多详细信息,请参考
docs.microsoft.com/en-us/cli/azure/。 -
kubectl:有关更多信息,请参考
kubernetes.io/docs/tasks/tools/#kubectl。 -
Helm:有关更多信息,请参考
helm.sh/docs/intro/install/。
登录到 Azure
运行 az login 命令。此命令用于登录到 Azure。
注意
如果您使用的是双因素认证(2FA),可能需要登录到网页浏览器。
创建集群
-
运行以下命令创建资源组:
az group create --name rancher-server --location eastus -
接下来,运行以下命令创建集群:
az aks create --resource-group rancher-server --name rancher-server --kubernetes-version 1.22.0 --node-count 3 --node-vm-size Standard_D2_v3 -
使用以下命令获取
kubeconfig文件:az aks get-credentials --resource-group rancher-server --name rancher-server
集群可能需要 5 到 10 分钟才能上线。
-
使用以下命令安装
nginx-ingress-controller:helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helm upgrade --install \ ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --set controller.service.type=LoadBalancer \ --version 3.12.0 \ --create-namespace
创建负载均衡器
如果您只是进行测试,可以运行kubectl get service ingress-nginx-controller -n ingress-nginx命令来捕获外部 IP。然后,您可以创建 DNS 记录指向该 IP。
注意
这不应在生产环境中使用。
要创建前端负载均衡器,请参见docs.microsoft.com/en-us/azure/aks/load-balancer-standard了解更多详细信息。
此时,集群已准备好安装 Rancher。我们将在下一节中覆盖此步骤。
安装和升级 Rancher
在本节中,我们将介绍如何在托管集群上安装和升级 Rancher。这个过程与在 RKE 集群上安装 Rancher 非常相似,唯一的区别是需要 Rancher Backup Operator,我们将在下一节中讲解。
安装 Rancher
-
运行以下命令添加 Helm Chart 仓库:
helm repo add rancher-latest https://releases.rancher.com/server-charts/latest -
运行
kubectl create namespace cattle-system命令为 Rancher 创建命名空间。注意
命名空间名称应始终为
cattle-system,并且无法更改,否则会破坏 Rancher。 -
现在我们将安装 Rancher。在这种情况下,我们将部署带有三个 pod 的 Rancher,并使用负载均衡器来处理 SSL 证书:
helm upgrade --install rancher rancher-latest/rancher \ --namespace cattle-system \ --set hostname=rancher.example.com \ --set ingress.tls.source=external \ --set replicas=3 \ --version 2.6.2
有关安装 Rancher 的更多细节和选项,请参见rancher.com/docs/rancher/v2.5/en/installation/install-rancher-on-k8s/#install-the-rancher-helm-chart。
升级 Rancher
在开始升级之前,您应该使用下一节中提到的备份步骤进行备份:
-
运行
helm repo update命令以拉取最新的 Helm Charts。 -
要获取当前的值,请运行以下命令:
helm get values rancher -n cattle-system注意
如果您保存了
install命令,可以重新使用它,因为该命令带有upgrade --install标志,告诉 Helm CLI 如果部署已存在则升级它。如果部署不存在,则安装它。唯一需要更改的是版本标志。
有关升级 Rancher 的更多细节和选项,请参见rancher.com/docs/rancher/v2.5/en/installation/install-rancher-on-k8s/upgrades/。
此时,Rancher 已启动并运行。下一节中,我们将介绍一些常见任务,如使用 Rancher Backup Operator 备份 Rancher。
Rancher-Backup-Operator
由于托管的 Kubernetes 集群无法访问 etcd 数据库,因此我们需要以不同的方式备份 Rancher 数据。这时,Rancher-Backup-Operator 工具发挥了作用。这个工具能够在任何 Kubernetes 集群上备份和恢复 Rancher 数据。它接受一个资源列表,指定某个应用需要备份的资源。然后,它通过查询 Kubernetes API 服务器来收集这些资源,将其打包为一个 tarball 文件,并将其推送到配置的备份存储位置。由于它通过查询 API 服务器来收集资源,因此可以备份来自任何类型 Kubernetes 集群的应用。
安装
让我们来看一下安装这个工具的步骤:
-
运行以下命令以添加 Helm Chart 仓库:
helm repo add rancher-charts https://raw.githubusercontent.com/rancher/charts/release-v2.5/ -
运行
helm repo update命令以获取最新的 chart。 -
要安装 Rancher-Backup-Operator 所需的 CRDs,请运行以下命令:
helm install --wait --create-namespace -n cattle-resources-system rancher-backup-crd rancher-charts/rancher-backup-crd -
最后,使用以下命令安装应用程序:
helm install --wait -n cattle-resources-system rancher-backup rancher-charts/rancher-backup
创建备份
要配置备份计划、加密和存储位置,请参阅 rancher.com/docs/rancher/v2.5/en/backups/configuration/backup-config/ 中的文档。
做一次性备份——在执行维护任务(例如升级 Rancher)之前,你应该进行备份:
-
创建一个名为
backup.yaml的文件,内容如下:apiVersion: resources.cattle.io/v1 kind: Backup metadata: name: pre-rancher-upgrade spec: resourceSetName: rancher-resource-set -
运行
kubectl apply -f backup.yaml命令以备份 Rancher 数据。
你可以在 github.com/rancher/backup-restore-operator/tree/master/examples 找到更多示例。
总结
在本章中,我们学习了托管 Kubernetes 集群,如 EKS、GKE 和 AKS,包括每种集群的要求和限制。接着,我们讨论了架构每种类型集群的规则,包括一些示例设计以及每种解决方案的优缺点。最后,我们详细讲解了使用之前设计的方案创建每种类型集群的步骤。本章的结尾,我们安装并配置了 Rancher 服务器和 Rancher 备份操作员。此时,你应该已经能够启动 Rancher 并准备好为应用工作负载部署下游集群。
下一章将讲解如何使用 Rancher IE 创建一个托管的 RKE 集群,即下游集群。我们将介绍 Rancher 如何创建这些集群以及它们的限制。
第三部分 – 部署 Kubernetes 集群
本部分将涵盖 Rancher 能够构建和管理的所有不同类型的 Kubernetes 集群,以及这些集群的所有限制和要求。
本书的这一部分包括以下章节:
-
第六章,使用 Rancher 创建 RKE 集群
-
第七章,使用 Rancher 部署托管集群
-
第八章,将外部管理的集群导入 Rancher
第六章:第六章:使用 Rancher 创建 RKE 集群
安装 Rancher 后,你首先要做的事情之一就是开始构建下游集群。Rancher 中有三种主要类型的集群:Rancher 管理的 Rancher Kubernetes Engine (RKE) 集群、Rancher 管理的托管集群和导入的集群。
在本章中,我们将讨论如何部署一个下游集群,使用现有运行 Docker 的服务器。我们将看到 Rancher 如何使用一组代理为 Rancher 提供对这些服务器的访问,以便创建一个 RKE 集群。接下来,我们将讨论这种类型集群的要求和限制。然后,我们将讨论设计 Rancher 管理的 RKE 集群的规则,届时我们将通过在 Rancher 中注册节点的过程。最后,我们将讨论持续的集群管理所需的维护任务。
本章将涵盖以下主要内容:
-
什么是 Rancher 管理的集群?
-
要求和限制
-
解决方案架构设计规则
-
准备节点加入 Rancher
-
准备基础设施提供商
-
使用 Rancher 创建 RKE 集群的步骤
-
使用节点池部署集群
-
持续的维护任务
什么是 Rancher 管理的集群?
Rancher 可以代表最终用户管理集群。这可以通过使用现有节点或 Rancher 创建的节点来完成。需要注意的是,在写作时,Rancher v2.6 将 RKE2 支持作为技术预览功能。但在本章中,我们将讨论使用 RKE 的 Rancher 管理集群。
Rancher 管理的集群来自哪里?
自 Rancher 起步以来,它始终采用在 Rancher 内定义集群的技术,然后使用 Rancher 代理提供对下游节点的访问以创建集群。在 Rancher v1.6 中,这用于部署 Cattle 集群,而在 Rancher v2.x 中,采用相同的理念来部署 RKE 集群。
Rancher 如何管理节点?
在某些环境中,你可能不想管理 虚拟机 (VMs) 。为了解决这个问题,Rancher 提供了所谓的 节点驱动程序。这些驱动程序允许 Rancher 启动和管理 Rancher 用来创建集群的虚拟机。Rancher 使用的一个节点驱动程序称为 Rancher 机器,它基于 Docker Machine。其主要思想是,Docker Machine 为大多数主要基础设施提供商(如 Amazon Web Services (AWS)、Azure、DigitalOcean 和 vSphere)提供几个不同的 软件开发工具包 (SDKs)。
基本过程是集群控制器创建一个机器对象,用来定义正在创建的服务器。然后,机器控制器接管并调用 Rancher 机器,开始使用 Rancher 中定义的节点模板,通过基础设施提供者发起 API 调用。作为节点创建过程的一部分,Rancher 会创建一个cloud-init来定制基础镜像,并将 SSH 密钥推送到服务器。需要注意的是,基础镜像默认使用 Ubuntu,但可以更换为任何在www.suse.com/suse-rancher/support-matrix/all-supported-versions/上支持的操作系统。主要要求是支持cloud-init和 Docker。一旦cloud-init成功完成,Rancher 将通过 SSH 连接到节点,并运行docker run命令来处理将 Rancher 代理推送到节点的操作。
Rancher 如何管理集群?
在 Rancher v2.x 中,一旦你定义了集群(我们将在本章后面详细讲解),Rancher 将创建一个docker run命令。请参见下图中的示例。现在我们将逐部分解析这个命令:

图 6.1 – 用于将节点加入集群的 docker run 命令
首先,docker run命令会创建一个新的容器。通常这被称为-d标志,它告诉 Docker 在后台启动该容器,下一个标志是--privileged。这个标志非常重要,因为 Rancher 代理需要访问主机及其资源,以便启动 Rancher 和 RKE 所需的附加工具和容器。--restart=unless-stopped标志用来确保容器即使崩溃也能继续运行。接下来,--net=host标志告诉 Docker 使用主机网络。这样做是为了能够获取主机的 IP 地址和主机名等信息。
我们接着来看-v /etc/kubernetes:/etc/kubernetes和-v /var/run:/var/run这两个标志。这两个标志会在引导容器中为主机文件系统创建一个绑定挂载。第一个目录用于存储 RKE 组件使用的 SSL 证书和一些config文件。第二个目录则用于提供对多个主机级命令的访问,包括 Docker 命令行界面(CLI)访问,Rancher 用来创建额外容器。
下一部分是image标签。这个标签当然会与 Rancher 的版本匹配。接下来是传递给 Rancher 代理二进制文件的命令行选项。第一个选项是-server,它是 Rancher 的 API 端点,应该在连接回 Rancher 时使用。需要注意的是,这必须是一个HTTPS URL。下一个选项是--token,它是 Rancher 用来认证代理并将其与集群绑定的特殊令牌。需要注意的是,这个令牌在集群中的所有节点之间是相同的。同时,应该像对待密码一样对待这个令牌。
下一个选项是--ca-checksum,它是 Rancher API 端点根证书的 SHA256 校验和。之所以使用这个选项,是因为用户通常会为他们的 Rancher 服务器使用自签名或私签名证书,并且容器内的根证书可能不是最新的。Rancher 代理会从 Rancher URL 请求根证书,并将该证书的校验和与--ca-checksum进行比较,假设它们匹配。代理将假设根证书可以被信任。需要注意的是,这些只处理信任根证书的问题。证书的其余部分仍然必须有效——即证书没有过期且具有正确的主机名。这也是为什么不应更改 Rancher API 端点的根 CA 的原因。官方上并不支持更改 Rancher API 端点或根 CA,但 Rancher 支持提供了一些工具,例如集群代理工具,可以帮助你处理这些问题。该工具位于github.com/rancherlabs/support-tools/tree/master/cluster-agent-tool。
最后,在命令的末尾,我们到达了需要根据节点的角色和设置进行自定义的部分。在图 6.2中所示的示例中,我们有一些用户常用的标准代理选项,第一个是--node-name,这是一个允许你覆盖节点主机名的选项。之所以使用这个选项,是因为默认情况下,Rancher 代理会将服务器的短主机名作为节点名,在 Rancher 和 Kubernetes 中都使用它。对于某些环境来说,这样是可以的,且该选项可以跳过,但在像 AWS 这样的云环境中,服务器主机名如ip-10-23-24-15可能很难阅读,并且与控制台中的服务器名称不匹配,这时候设置一个更易读的节点名就变得很有帮助。
需要注意的是,Rancher 和 RKE 不使用此主机名进行网络通信,因此节点名称不需要是有效的 DNS 记录,但建议它是有效的,以便帮助未来的故障排除。此外,必须记住,节点在注册到 Rancher 后不应更改主机名,因为主机名作为一个关键字,如果更改主机名,Rancher 会将其视为新节点进行注册。这可能会破坏集群,因为它处于未知状态,因此如果你想更改节点名称,建议先将其从集群中移除,使用 github.com/rancherlabs/support-tools/blob/master/extended-rancher-2-cleanup/extended-cleanup-rancher2.sh 清理节点,然后将其作为新节点重新加入集群。
下一个选项是 --address,用于设置节点的外部 IP 地址。通常,只有当节点位于 --internal-address 后面时才需要此选项,该选项用于设置 Kubernetes 用于主机间通信的 IP 地址。如果一个节点有多个 网络接口卡(NIC),则必须使用此设置,以避免网络被错误路由。
例如,假设你有一个用于管理的 1 GB 网络接口卡(NIC)和一个用于数据的 10 GB 网络接口卡。我们希望 RKE/Kubernetes 使用 10 GB 网络接口卡的 IP 地址来提高速度。如果未设置此选项,kubelet 将尝试通过使用默认网关和节点主机名的 DNS 记录来自动检测节点的正确 IP。建议如果一个节点有多个 IP 地址时,手动设置这些选项。
还有一些可以在代理级别设置的额外标志。例如,--labels 将设置节点的标签,–taints 将在节点创建时设置节点的污点,但需要注意的是,这些选项在此时已被锁定,如果需要在稍后更改,可能会引发问题。其他代理选项可以在 rancher.com/docs/rancher/v2.5/en/cluster-provisioning/rke-clusters/custom-nodes/agent-options/ 找到。
在命令的最后,我们有 role 选项。这些标志告诉 Rancher/RKE 此节点被分配了什么角色,例如 --etcd、--controlplane 和 --worker。当节点第一次向 Rancher 注册时,role 选项会被发送给 Rancher,并在生成集群配置时被使用。需要注意的是,这些角色在节点注册到 Rancher 后不应更改。如果需要更改节点的角色,建议将节点移除,进行清理,并重新加入:

图 6.2 – 带有节点自定义的 Docker 运行命令
节点注册后会发生什么?一旦 Rancher 代理成功启动,它将注册该节点到 Rancher,节点将进入 等待与 Kubernetes 注册 状态。此时,代理将创建一个 WebSocket 连接并等待。这会触发 Rancher 内部的集群控制器更新集群配置。该对象相当于 RKE 使用的 cluster.yaml 和 cluster.rkestate 文件,但它是在 Rancher 容器内使用的。这是因为集群控制器使用与 RKE 相同的代码,差异主要较小,最大的不同是添加了一个拨号器来处理通过 WebSocket 隧道传输 Docker 套接字连接。集群控制器将遵循与 RKE 命令相同的过程。
现在我们了解了什么是 Rancher 托管的集群,让我们来看看这些类型集群的要求和限制。
要求和限制
在本节中,我们将讨论 Rancher 在各种节点上的基本要求以及其限制和设计注意事项。
Rancher 创建的托管节点
这些是基本要求:
-
支持的操作系统。官方支持的操作系统可以在
www.suse.com/suse-rancher/support-matrix/all-supported-versions/找到。 -
Rancher 创建的节点有一个特殊要求,即 Rancher 服务器必须能够通过 SSH 连接到该节点。
-
所需的防火墙规则和端口可以在 https://rancher.com/docs/rancher/v2.5/en/installation/requirements/ports/#ports-for-rancher-launched-kubernetes-clusters-using-node-pools 找到。
-
Docker 不需要预先安装。
这些是设计限制和注意事项:
-
用于创建节点的基础镜像应该尽可能小,并且启动时间应少于 10 分钟。
-
Rancher 没有 IP 地址池,也没有与任何IP 地址管理(IPAM)解决方案的集成。Rancher 依赖基础设施提供商来分配 IP 地址给节点。如果你使用的是动态主机配置协议(DHCP),则分配给这些节点的 IP 地址应具有非常长的租期,并且实际上是静态的——即这些 IP 地址不应发生变化。
-
节点的主机名在节点池级别定义,节点名称通过在模板名称末尾添加一个数字并在每次创建节点时递增来依次生成。
重要提示
Rancher 将重用已成功回收的旧主机名。
-
如果节点部署在隔离网络环境中,Rancher 将要求在
cloud-init中配置代理服务器,或者包管理器应该能够从其仓库中拉取如 curl 和 Docker 等软件包。即使这些软件包已经安装,Rancher 仍会运行yum install curl或apt install curl命令。 -
对于包含 etcd 和 controlplane 节点的节点池,Auto 应设置为
0。
现有节点
这些是基本要求:
-
一个受支持的操作系统。官方支持的操作系统可以在
www.suse.com/suse-rancher/support-matrix/all-supported-versions/找到。 -
所需的防火墙规则和端口可以在
rancher.com/docs/rancher/v2.5/en/installation/requirements/ports/#ports-for-rancher-launched-kubernetes-clusters-using-custom-nodes找到。 -
节点上应已安装 Docker,建议使用位于
github.com/rancher/install-Docker的安装脚本。 -
如果您使用自动扩展组,必须确保每次只有一个 etcd 节点或 controlplane 节点下线。您需要确保不会丢失 etcd 的法定人数,或者在多个 controlplane 节点宕机时,集群更新不会卡住。
这些是设计限制和考虑事项:
-
在注册新集群的节点时,Rancher 要求每个角色(如 etcd、controlplane 和 worker)至少有一个节点。可以是单个节点,也可以是为每个角色单独设置节点,或者混合使用不同角色的节点。
-
在向集群中添加节点时,至关重要的是确保每次只添加一个新的 etcd 和 controlplane 节点。技术上可以一次性添加所有节点,但这样可能会导致新集群出现稳定性问题。
-
如果您使用私有注册表托管 Rancher 使用的 Docker 镜像,您应该按照
rancher.com/docs/rke/latest/en/config-options/private-registries/中的步骤配置集群中的注册表设置。
现在我们已经了解了要求和限制。在下一部分,我们将结合这些知识以及额外的规则和示例设计,帮助我们架构出满足需求的解决方案。
架构解决方案的规则
在本节中,我们将介绍一些标准设计及其优缺点。需要注意的是,每个环境都是独特的,需要调整以获得最佳性能和体验。还需要注意的是,所有的 CPU、内存和存储大小都是推荐的起始点,可能需要根据工作负载和部署过程进行调整。此外,我们将介绍适用于主要基础设施提供商(AWS 和谷歌云平台(GCP))的设计,但您应该能够将核心概念转化为其他基础设施提供商的设计。
在设计解决方案之前,您应该能够回答以下问题:
-
是否有多个环境共享同一个集群?
-
生产和非生产工作负载是否会在同一个集群中运行?
-
这个集群需要什么级别的可用性?
-
这个集群是否会跨多个数据中心,在一个城域集群环境中运行?
-
集群中节点之间的延迟是多少?
-
集群中将托管多少个 Pods?
-
集群中部署的 Pods 的平均大小和最大大小是多少?
-
你是否需要为某些应用程序提供 GPU 支持?
-
你需要为应用程序提供存储吗?
-
如果你需要存储,你是只需要 一次读写(RWO),还是需要 多次读写(RWX)?
注意
Rancher 官方的服务器规格指南可以在
rancher.com/docs/rancher/v2.5/en/installation/requirements/#rke-and-hosted-kubernetes找到。
AWS
在这个设计中,我们将使用 Rancher EC2 节点驱动程序在 AWS 上部署一个标准大小的集群,设计与我们在第四章中为中型 RKE 集群创建的设计非常相似,创建 RKE 和 RKE2 集群。基本思路是尝试在 高可用性(HA)和成本之间进行平衡,并利用 AWS 的内网区域网络速度和延迟非常优秀这一事实,来将其视为单个数据中心。
注意
这需要进行测试,因为某些区域的速度比其他区域慢,一些用户报告了更高的延迟。
这个设计适用于集群中的 2 到 50 个工作节点。这比中型 RKE 集群要大,因为 AWS 中的 非易失性存储(NVM)能够处理比大多数本地存储更多的吞吐量。
注意
根据环境的不同,你可能需要扩展管理节点。

图示
图 6.3 – 一个跨区域的 Rancher 管理的 AWS 集群
图示: github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch06/standard_designs/AWS/README.md
优点如下:
-
节点级冗余 – 你可以失去一个工作节点而不发生应用程序宕机。
-
完整的 HA – 你可以失去集群中的任何一个管理节点(etcd 和控制平面),仍然能够进行完整的集群管理。
-
用户工作负载和管理服务运行在不同的节点上,从而防止失控的应用程序导致集群宕机。
-
可用区冗余 – 你可以失去整个可用区而不发生宕机。
-
更安全的主节点修补和升级,因为节点池跨区域部署。因此,我们可以简单地将三个节点池中的每一个从一个节点扩展到两个节点并行运行,然后逐个缩减每个池。
-
使用区域反亲和规则,确保通过 Pod 拓扑分布约束将应用程序分布在不同的可用区,您可以在此了解更多:
kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/。
缺点如下:
-
额外的工作节点成本。
-
设置过程中由于每个可用区都有自己的节点组而增加了额外的复杂性。
-
由于 NLB 必须在每个可用区都有一个接口,因此增加了额外的复杂性。
-
升级过程中每个可用节点组需要单独升级,增加了额外的复杂性。
-
AWS 不支持跨可用区的弹性块存储(EBS)卷,因此如果计划使用 AWS 的存储类,您需要确保应用数据在可用区之间冗余存储。您可以使用 AWS 的 EFS,但成本可能非常高昂。
节点大小要求如下:
-
服务器:三台 EC2 实例
-
CPU:每个服务器八个核心
-
内存:8-16 GB
-
存储:固态硬盘(SSD)或非易失性内存快速接口(NVMe)10-15 GB
重要提示
延迟是我们在监控 etcd 时最重要的指标。
工作节点大小应基于您的工作负载及其要求。
GCP
在本设计中,我们将像在 AWS 上一样,在 GCP 上部署一个标准大小的集群:

图 6.4 – 跨可用区的 Rancher 管理 AWS 集群
图表:github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch06/standard_designs/GCP/README.md
注意
GCP 的优缺点和节点大小要求与 AWS 完全相同。您可以参考Amazon 的 AWS部分以获取更多详情。
现在我们已经创建了集群设计。接下来的部分,我们将开始创建集群的过程,第一步是准备节点。
准备节点加入 Rancher
在创建集群之前,我们需要准备将用于创建集群的节点/镜像。本节假设您使用的是基于 Ubuntu 或 Red Hat/CentOS 的镜像/节点,因为这两者是最常用的。
对于 Rancher 管理的节点,我们需要创建一个基础镜像,并在环境中进行克隆和部署。创建此镜像有两种主要方式:
- 首先,从一个公有镜像开始,比如可以在
cloud-images.ubuntu.com/focal/current/找到的 Ubuntu ServerW 云镜像。请注意,此镜像必须来自一个受信任的源,直接来自 Ubuntu 网站或您的基础设施提供商的官方镜像。这些镜像设计为小巧轻量,因为它们只包含必要的工具和预安装的包。对于大多数人来说,这就是整个过程的结束,因为大多数您想要进行的定制都可以通过cloud-init工具来完成。如果您需要安装任何额外的工具或更改设置,应参考您的基础设施提供商的文档,以打开该镜像并进行定制。
我们通常建议尽量减少更改,不安装像 Puppet、Chef 或备份客户端这样的工具,因为这些服务器设计为可丢弃并容易替换。此外,我们通常建议对基础镜像进行补丁更新,以便进行小规模更新。然而,对于重大升级,我们更推荐返回官方镜像源并拉取新版本。最后,我们建议不要在 cloud-init 文件中更新/升级节点,因为我们希望从该镜像部署的所有节点都保持一致。此外,Rancher 在节点创建过程中有 10 分钟的超时时间,更新/修补可能导致节点超过此时间限制。
-
第二种方式是从一个黄金镜像开始,该镜像是您或您的团队已经在环境中用于其他应用程序的镜像。例如,如果您的 Linux 团队已经有一个基于 Red Hat 的镜像,并且已经针对您的环境做了所有定制,那么没有必要重新发明轮子,您可以直接使用该现有镜像。请注意,您可能需要对该镜像进行额外的测试和调整,以确保它完全受支持。
注意
就
cloud-init设置而言,您仍然应该遵循公有镜像选项下列出的相同建议。
此外,我们还应确保任何自动化修补工具都被禁用,因为我们不希望节点在创建后发生变化。
对于自定义节点,过程与公有节点大不相同,因为 Rancher 与节点创建过程或操作系统无关。在这种情况下,您或您的 Linux 团队需要负责创建服务器、配置服务器、安装 Docker,并将其注册到 Rancher 中。这一方式的优点是您可以对服务器拥有更大的控制权。您仍然应该遵循公有镜像选项下列出的相同建议。不同之处在于,由于 Rancher 不管理操作系统,像 Puppet 或 Chef 这样的工具是被支持的。
此时,如果您计划将自有节点带入 Rancher,您应该已经构建并准备好节点。在下一节中,我们将介绍如果希望 Rancher 为我们构建节点时的步骤。
准备基础设施提供商
现在我们已经创建了节点镜像,接下来需要在 Rancher 中配置该镜像。
注意
这仅适用于 Rancher 管理的集群。如果您正在使用现有节点,则可以跳过此部分。
第一步是在基础设施提供商中创建一个服务账户,该账户需要具备 Rancher 用来创建、管理和删除节点的权限。出于安全考虑,我们建议此账户应为专用账户,不与其他应用程序或用户共享,并且该账户的权限应仅限于所需的权限。有关不同基础设施提供商的权限详情,可以参考rancher.com/docs/rancher/v2.5/en/cluster-provisioning/rke-clusters/cloud-providers/。
需要记住的是,Rancher 和基础设施提供商仍在不断发展,因此这些权限可能会随时间变化。一旦创建了该账户,您需要登录 Rancher v2.6.x UI,进入集群管理页面并选择云凭证页面。这将弹出一个设置向导,如图 6.5所示。
注意
Rancher UI 会测试凭证是否正确,但不会验证该账户是否具备 Rancher 所需的所有权限。

图 6.5 – 亚马逊云凭证设置向导
有关云凭证的更多详细信息,请访问rancher.com/docs/rancher/v2.5/en/user-settings/cloud-credentials/。
下一步是创建节点模板。这是我们定义节点配置的方式。包括选择镜像、位置以及其他任何基础设施设置。我们通过进入集群管理页面,展开RKE1 配置,然后选择节点模板来完成。这将会弹出一个设置向导,如下图所示。
注意
Rancher UI 将在您点击不同页面时动态查询基础设施提供商。

图 6.6 – 亚马逊节点模板向导 – 步骤一
以下两页根据基础设施提供商有所不同。

图 6.7 – 亚马逊节点模板向导 – 实例设置
设置向导的最后一页是你进行大部分节点自定义的地方——例如,设置服务器大小、根磁盘大小和标签。大部分这些设置可以保留默认值;我通常推荐更改的唯一设置是根磁盘大小,默认值为 16 GB。这对于实验室/沙盒环境来说足够了,但对于实际的生产节点,我建议设置为 30-40 GB。另外,名称字段通常不会更改,所以我建议使用一个非常具描述性的名称。还有一个描述字段可以用于输入备注。最后,标签字段可能有些让人困惑(参见图 6.8)。页面的底部部分是用于设置 Docker/Kubernetes 标签、污点和引擎选项:

图 6.8 – 亚马逊的节点模板向导 – 节点设置
有关节点模板的更多详细信息,请访问rancher.com/docs/rancher/v2.5/en/user-settings/node-templates/。
到此为止,我们已经完成了 Rancher 为我们创建和管理节点所需的所有准备工作。在下一部分,我们将开始在 Rancher 中实际创建集群的过程。
使用 Rancher 创建 RKE 集群的步骤
在这一部分,我们将使用默认设置创建一个自定义集群。在下一部分,我们将介绍如何使用基础设施提供商创建 RKE 集群。
第一步是进入 Rancher 用户界面(UI)并访问集群管理页面。在此页面中,进入集群页面,并点击页面右上角的创建按钮。这样会带你到一个展示所有主要集群类型的页面。请参见下面的示例图。我们将在此页面中点击自定义按钮:

图 6.9 – 集群创建页面
下一页是你可以定义集群的地方。你需要填写的第一个字段是集群名称。集群名称最多限制为 253 个字符,且必须是全小写字母、数字,支持点号和破折号。关于此页面上其他设置的详细信息,请参考rancher.com/docs/rancher/v2.5/en/cluster-provisioning/rke-clusters/custom-nodes/:

图 6.10 – 集群设置页面
现在我们已经有了一个集群,接下来需要为集群添加节点。由于我们正在创建一个自定义集群,下一页将显示我们需要用来加入不同类型节点的docker run命令。稍后你可以通过进入集群管理页面并从列表中选择集群来检索此页面:

图 6.11 – 自定义节点运行命令向导
此时,Rancher 应该正在创建我们的新集群。你可以通过点击集群仪表板或节点标签来通过 Rancher UI 监控该过程。在此过程结束时,你将拥有一个 Kubernetes 集群。在下一节中,我们将介绍如何使用节点池创建集群。
使用节点池部署集群
现在我们有了一个自定义集群,我们将按照相同的步骤创建一个包含节点池的集群。首先,选择 docker run 命令向导,而不是选择其他选项。
使用这个向导,你可以为每种不同类型的节点添加一个节点池,关键字段是名称前缀,用于设置该池中节点的主机名。确保这些名称具有意义且不会重复非常重要。其他主要字段是角色的复选框。UI 会提醒你每种节点类型所需的最小节点数量。如果未满足要求,Rancher 将不允许你创建集群:

图 6.12 – 节点池创建向导
此时,Rancher 将接管,开始创建服务器并在其上部署 RKE。你可以通过进入节点标签来监控节点的创建,并查看每个节点的状态消息。集群的整体状态也会显示在页面顶部。在此过程结束时,你将拥有一个可以开始部署应用程序的 Kubernetes 集群。在下一节中,我们将介绍一些需要执行的任务,以保持集群的健康。
持续维护任务
创建集群后,需要进行一些持续的维护任务,以保持集群处于健康状态。
我推荐的第一个任务是设置定期的 etcd 备份,在 Rancher v2.4 及更高版本中,该功能默认启用。默认行为是每个 etcd 节点会拍摄一个 etcd 快照,并将其存储在 /opt/rke/etcd-snapshots 本地。etcd 备份是一个时点快照,存储了集群的配置。在发生故障时,这个备份非常重要,因此,通常会将备份配置为 S3 选项,因为我们不希望将备份存储在与被备份的服务器相同的位置。你可以在 rancher.com/docs/rke/latest/en/etcd-snapshots/recurring-snapshots/#options-for-the-etcd-snapshot-service 找到详细的 S3 设置列表。
注意
Rancher/RKE 支持任何与 S3 兼容的存储。因此,对于本地环境,您可以使用诸如 MinIO 之类的工具。如果您已经拥有企业存储解决方案,可能需要审查一下,看看它是否支持 S3,因为一些较新的企业存储子系统提供了开箱即用的 S3 支持。
我建议测试和记录的第二项任务是如何对集群中的节点进行补丁/升级。主要的两种方式是在原地进行补丁或替换节点。自定义集群的最常见方式是在原地进行补丁,具体过程是创建一个脚本,逐个浏览集群中的所有节点,并使用以下步骤:
-
排空并隔离节点。
-
然后,在节点上应用任何所需的补丁/升级/重启。
-
一旦所有任务完成,节点就会取消隔离,并处理下一个节点。可以在
github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch06/cluster_patching/rolling_reboot.sh找到示例脚本。注意
该脚本设计为具有大量的休眠周期,因为它旨在无人参与模式下运行。对于具有节点池的集群,通常会替换节点而不是更改现有节点。这通过扩展节点池,然后逐个删除旧节点并替换它们来完成。
我建议测试和记录的第三项任务是如何升级 Kubernetes。基本过程是查看新版本的发布说明。然后,在升级集群时,您需要进行 etcd 快照,因为这是回滚升级的唯一方式。有关此升级的规则和过程,可以在 https://github.com/mattmattox/Kubernetes-Master-Class/tree/main/rancher-k8s-upgrades#rke-upgrade--prep-work 找到,以及进行深入研究的大师课程。
摘要
在本章中,我们了解了 Rancher 管理的不同类型的集群,包括每种类型的要求和限制。然后,我们讨论了为每种类型的集群架构规则,包括一些示例设计以及每种解决方案的优缺点。最后,我们详细介绍了使用我们早期制定的设计创建每种类型集群的步骤。我们通过讨论主要的维护任务结束了本章。
下一章将介绍如何在 Rancher 中创建托管集群,即下游集群。我们将讨论 Rancher 如何创建这些集群以及其限制。
第七章:第七章:使用 Rancher 部署托管集群
对于不想管理任何服务器的团队,Rancher 提供了部署和管理托管 Kubernetes 服务的能力,如 Google Kubernetes Engine(GKE)、Amazon Elastic Container Service for Kubernetes(Amazon EKS)或 Azure Kubernetes Service(AKS)。本章将讨论使用托管集群与 RKE 集群的优缺点。然后,我们将介绍此类集群的要求和限制。接下来,我们将讲解如何准备云服务提供商。然后,我们将介绍如何使用 Rancher 设置 EKS、GKE 和 AKS 集群。最后,我们将讨论集群管理所需的维护任务。
在本章中,我们将涵盖以下主要内容:
-
Rancher 如何管理托管集群?
-
要求和限制
-
架构解决方案的规则
-
准备云服务提供商
-
安装步骤
-
持续的维护任务
Rancher 如何管理托管集群?
我经常收到的第一个问题是,什么是托管集群? 简单来说,它是一个由云服务提供商(如 Google、Amazon 或 Azure)创建和管理的 Kubernetes 集群,但由 Rancher 管理集群的配置。Rancher 使用云服务提供商的 API 和 SDK 来创建集群,就像你作为终端用户通过它们的 Web 控制台或命令行工具创建集群一样。自 Rancher v2.6 以来,当前支持的云服务提供商如下:
-
GKE
-
Amazom EKS
-
AKS
-
阿里云容器服务 for Kubernetes(Alibaba ACK)
-
腾讯 Kubernetes 引擎(Tencent TKE)
-
华为云容器引擎(Huawei CCE)
Rancher 通过在 Rancher 主控 pod 中设置一组控制器来实现这一点。每个云服务提供商都有自己的控制器,每个控制器使用 Go 库与云服务提供商进行通信。Rancher 采用一种流程,其中 Rancher 将集群的配置存储为集群对象中的规范。例如,EKS 配置存储在 Spec.EKSConfig 下。对于本节内容,我们将首先讲解 v1 控制器,然后讲解新的 v2 控制器。
使用最初的 v1 控制器(在 Rancher v2.0–2.4 版本中)时,集群配置存储在此对象中,只有在 Rancher 或用户进行更改时才会更新。如果你在 Rancher 中创建了一个 EKS 集群,然后在 AWS 控制台中进行更改,那么这些更改不会反映在 Rancher 中,Rancher 会在下一次更新时覆盖这些更改。这意味着,对于这些类型的集群,Rancher 是唯一的真实来源,而在撰写本文时,这些集群无法从 Rancher 中分离并进行外部管理。
新的 v2 控制器仅适用于 EKS 和 GKE,已添加到 Rancher v2.5.8 及以后的版本中。配置同步的概念被引入,以允许在 Rancher 外部所做的更改同步到 Rancher 中。这是通过两个操作器来完成的,分别是 eks-operator 和 gke-operator。操作器将云提供商的配置存储为Spec.EKSStatus和Spec.GKEStatus。这些对象每 5 分钟从云提供商处刷新一次。集群的本地配置存储为Spec.EKSConfig和Spec.GKEConfig,它们表示集群的期望状态,其中大多数字段在config section中为NULL。Rancher 会保持这些值为NULL,直到它们在 Rancher 中被设置。一旦在 Rancher 中设置了该值,操作器就会使用云提供商的 SDK 来更新集群。一旦云端被更新,Status规格将会被更新。如果你在 Rancher 外部更改了集群,这些更改会被 Rancher 捕获,如果managed字段不同,它将被覆盖。
一个经常被问到的问题是,在 Rancher 中构建托管集群和在 Rancher 外部构建集群再导入之间有什么区别? 这个问题的答案取决于集群的类型。如果是 EKS 或 GKE 集群,你将导入该集群,Rancher 会自动检测集群类型。然后,假设 Rancher 具有正确的权限,Rancher 将转换该集群为托管集群。此时,该集群可以像 Rancher 创建的集群一样进行管理。我们将在下一章中详细讨论如何将集群导入 Rancher。
要求和限制
现在我们了解了什么是托管集群以及它在 Rancher 中的工作原理,接下来我们将讨论托管集群在 Rancher 中的要求和限制,以及选择托管集群时的设计限制和约束。
基本要求
Rancher 需要来自云提供商的权限,才能创建集群及其相关服务。所需的权限会根据云提供商的不同而有所不同。每种云提供商类型的官方 Rancher 文档链接如下所示:
-
Google Kubernetes Engine:
rancher.com/docs/rancher/v2.6/en/cluster-provisioning/hosted-kubernetes-clusters/gke/。 -
Amazon EKS:
rancher.com/docs/rancher/v2.6/en/cluster-provisioning/hosted-kubernetes-clusters/eks/。 -
AKS:
rancher.com/docs/rancher/v2.6/en/cluster-provisioning/hosted-kubernetes-clusters/aks/。 -
其他云提供商的信息可以在
rancher.com/docs/rancher/v2.6/zh/cluster-provisioning/hosted-kubernetes-clusters/找到。
建议使用最少权限的专用服务账户配置 Rancher。
Rancher 需要访问云提供商的 API 端点,这意味着 Rancher 需要直接或通过 HTTP(S) 代理访问互联网。如果您使用像 AWS 的 API 网关这样的私有 API,那么需要在 Rancher 中进行配置。
Rancher 需要从 Rancher 服务器访问集群的 Kubernetes API 端点。
建议在 Rancher 中配置云服务账户,使用专用的服务账户如本地管理员,并且此账户应该在 Rancher 中拥有管理员权限。
设计限制和考虑
一些设置,如可用区域,在 Rancher 中是硬编码的,这意味着如果云提供商添加了新的区域,可能在您升级 Rancher 之前在 Rancher UI 中不可用。
重要提示
对于 v2 控制器,您可以通过在 Rancher 外部创建集群,然后导入来绕过 Rancher UI 中的限制。
在 Rancher UI 中可用的 Kubernetes 版本可能与云提供商允许的版本不匹配。例如,如果您运行较旧版本的 Rancher,您可能在下拉菜单中看到 v1.13,但因为 Amazon 不再支持此版本,您将在 Rancher 中收到集群创建失败的错误。
更多的云提供商将假定正在构建的集群具有公共互联网访问和为节点、负载均衡器分配公共 IP 地址,以及 Kube-API 端点,如果要设置无网络访问或仅私有 IP 的集群。您需要与云提供商合作配置额外的防火墙规则、路由和其他必需的设置。以下是在 Rancher 中使用私有端点的文档:
-
对于仅限私有的 EKS 端点,Rancher 提供了所需的额外步骤的文档,位于 https://rancher.com/docs/rancher/v2.5/zh/cluster-admin/editing-clusters/eks-config-reference/#private-only-api-endpoints。
-
有关 GKE 私有端点的文档可以在 https://rancher.com/docs/rancher/v2.5/zh/cluster-admin/editing-clusters/gke-config-reference/#private-cluster 找到。
注意
在撰写时,此类配置并不十分成熟,并且存在一些 bug。
快照和备份并不存在。与 RKE/2 集群不同,大多数托管集群不会提供访问 etcd 备份,也没有 etcd 备份选项。如果集群丢失或用户犯了错误(例如,删除了错误的命名空间),你唯一的选择就是重新部署。有一些第三方工具,例如 Velero,可以解决这个不足,我们将在本章稍后介绍。
Rancher 所需的权限可能对一些安全团队来说太大,难以批准。Rancher 提供了最小 EKS 权限的列表,位于rancher.com/docs/rancher/v2.5/en/cluster-provisioning/hosted-kubernetes-clusters/eks/permissions/。需要注意的是,某些功能可能无法在较低权限设置下正常工作,可能需要进行调优。
托管集群的负载均衡器成本可能比 RKE/2 集群更高。这是因为大多数云提供商会部署外部负载均衡器,而不是 RKE/2 使用的共享负载均衡器——Ingress NGINX Controller。需要注意的是,你可以通过在其前面部署 nginx-ingress 和外部负载均衡器来绕过这一限制。
在本节中,我们已经讨论了需求和限制。在下一节中,我们将结合这些知识以及额外的规则和示例设计,帮助我们构建一个满足你需求的解决方案。
架构解决方案的规则
在本节中,我们将介绍一些标准设计及其优缺点。需要注意的是,每个环境都是独特的,需要进行调优以达到最佳性能和体验。同样需要注意的是,所有的 CPU、内存和存储大小都只是建议的起始点,可能需要根据你的工作负载和部署过程进行增加或减少。此外,我们还将介绍主要基础设施提供商的设计(Amazon EKS 和 GKE),但你应该能够将核心概念迁移到其他基础设施提供商。
在设计解决方案之前,你应该能够回答以下问题:
-
多个环境是否会共享同一个集群?
-
生产工作负载和非生产工作负载是否会在同一个集群中?
-
这个集群需要什么级别的可用性?
-
这个集群是否会跨多个数据中心部署在城市集群环境中?
-
集群中节点之间的延迟有多大?
-
集群中将托管多少个 Pod?
-
集群中部署的 Pod 的平均大小和最大大小是多少?
-
你是否需要为某些应用程序提供 GPU 支持?
-
你是否需要为你的应用程序提供存储?
-
如果你需要存储,你只需要
ReadWriteOnce(RWO) 还是需要ReadWriteMany(RWX)?
让我们从 Amazon EKS 开始。
Amazon EKS
EKS 是最成熟的云服务提供商,特别是在Kubernetes 即服务(KaaS)方面。因此,EKS 是最灵活的解决方案之一,但在 Rancher 中创建 EKS 集群时需要遵守一些限制和规则。
亚马逊 EKS 的优点如下:
-
EKS 支持巨大的集群,当前的限制为每个集群 3,000 个节点,每个节点 737 个 Pods(具体取决于节点大小)。
-
EKS 支持第三方容器网络接口(CNI)提供商,如 Calico。
-
EKS 本地支持
ReadWriteOnce存储。该存储类的提供者已预安装。你可以在docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html找到关于此存储类的更多细节。 -
对于需要
ReadWriteMany的工作负载,EKS 支持弹性文件系统(EFS),该系统由亚马逊的 NFS 共享进行管理。你可以在docs.aws.amazon.com/eks/latest/userguide/efs-csi.html找到更多细节。 -
由于亚马逊同时控制了云网络和集群覆盖网络,你可以直接将虚拟私有云(VPC)中的 IP 地址分配给集群内的 Pods。这允许其他亚马逊服务直接与 Pods 通信。你可以在
docs.aws.amazon.com/eks/latest/userguide/pod-networking.html找到更多细节。 -
EKS 与 AWS 负载均衡器之间有直接的集成。这使得你可以部署应用负载均衡器(ALB)作为第 7 层/HTTP(S)负载均衡器,以及网络负载均衡器(NLB)作为第 4 层/TCP 负载均衡器。
亚马逊 EKS 的缺点如下:
-
EKS 根据节点大小限制每个节点的 Pods 数量。例如,
t2.nano每个节点仅支持四个 Pods。在这种可用 Pods 数量极少的情况下,大多数服务(如 CNI、节点监控程序和日志收集器)将在任何应用 Pods 启动之前占满节点的所有可用插槽。通常建议使用t2.large或更大的节点。你可以在github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt找到所有节点大小与最大 Pods 数量的对照列表。 -
在撰写本文时,EKS 没有自动节点修复过程,因此如果节点崩溃并且无法恢复,该节点不会被替换,直到你强制替换它。你可以在
aws.amazon.com/premiumsupport/knowledge-center/eks-node-status-ready/找到有关此限制的更多细节。 -
升级过程中需要一些手动步骤。亚马逊已在
docs.aws.amazon.com/eks/latest/userguide/update-cluster.html文档中列出了这些步骤。
现在,让我们来讨论 GKE。
GKE
GKE 是 KaaS 领域中第二成熟的云服务提供商。这是因为 Kubernetes 由谷歌创建,并且谷歌仍然推动 Kubernetes 核心的集成和开发工作。
GKE 的优点如下:
-
GKE 支持最广泛的 Kubernetes 版本(三个主要版本),并且通常非常及时地推出新版本。你可以在
cloud.google.com/kubernetes-engine/docs/release-notes#latest_versions查看有关发布计划和版本的更多详细信息。 -
使用 GKE,你可以在集群中启用自动驾驶功能,并且可以完全自动化集群的升级过程,包括控制平面和工作节点的升级。你可以在
cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview查看自动驾驶功能的文档。 -
你可以将集群的发布通道设置为快速发布通道,这样你每隔几周就会收到常规通道的更新(这是默认选项),并且每 2 到 3 个月提供一次更新。最后,你可以选择稳定通道,这与常规通道非常相似,不同之处在于它是最后一个获得更新的通道。这为准确的用户测试提供了更多时间。你可以在
cloud.google.com/kubernetes-engine/docs/concepts/release-channels查看更多详细信息。 -
GKE 提供自动节点修复功能;如果某个节点出现故障,GKE 会自动替换它。它通过节点状态来实现这一点,当 kubelet 崩溃而操作系统继续运行时,帮助处理节点在集群中的“死亡”状态。你可以在
cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair了解更多关于此过程的信息。 -
GKE 的独特之处在于,你可以选择谷歌的容器优化操作系统,这是一种为安全性和稳定性设计的精简版操作系统。或者,你也可以选择基于 Ubuntu 的镜像。你还可以在一个集群中混合使用不同的镜像。完整的节点镜像列表可以在
cloud.google.com/kubernetes-engine/docs/concepts/node-images找到。 -
GKE 支持 Windows Server 容器。这是通过将 Windows 工作节点添加到你的 GKE 集群中实现的。GKE 中 Windows 节点的限制可以在
cloud.google.com/kubernetes-engine/docs/concepts/windows-server-gke找到。 -
GKE 允许你使用 SSH 客户端远程访问工作节点,这对于排查节点和应用程序问题非常有帮助。
GKE 的 缺点 如下:
-
GKE 仅在使用区域集群时提供 99.95% 的 服务级别协议(SLA),而使用区域集群需要额外付费。关于这个费用的详细信息可以在 https://cloud.google.com/kubernetes-engine/pricing#cluster_management_fee_and_free_tier 找到。
-
截至撰写时,GKE 没有政府云选项。所有当前支持的区域可以在
cloud.google.com/compute/docs/regions-zones找到。
最后,我们将讨论 AKS。
Microsoft Azure Kubernetes 服务(AKS)
AKS 是在 KaaS 领域中的新秀,但 Microsoft 已经为 AKS 投入了大量资源,并且快速缩小了功能差距。
AKS 的 优点 如下:
-
AKS 遵循 Microsoft 的标准月度补丁计划,就像他们对待操作系统一样。他们还会在其 GitHub 页面上发布版本,页面地址为
github.com/Azure/AKS/releases。 -
AKS 具有自动节点修复功能,而 Microsoft Azure 则通过节点代理和集群中的节点状态来触发修复。Azure 的恢复过程不如其他云服务提供商先进,因为它会尝试先重启节点,若无效则重新镜像,最后如果还失败,Azure 工程师会调查问题。你可以在
docs.microsoft.com/en-us/azure/aks/node-auto-repair阅读更多关于此过程的内容。 -
AKS 完全支持与 Azure Active Directory(Azure AD)的集成。这允许你使用 Azure AD 用户和组在集群内分配权限。更多细节,请访问
docs.microsoft.com/en-us/azure/aks/managed-aad。 -
AKS 具有 Visual Studio Code 扩展,允许开发人员像在 AKS 集群中一样,在笔记本电脑上直接运行和调试代码。Bridge to Kubernetes 基本上就像是在你的集群中创建一个 VPN 连接,使得在你的电脑上运行的 Pod 可以直接与集群及集群中其他 Pod 通信。你可以在
docs.microsoft.com/en-us/visualstudio/bridge/overview-bridge-to-kubernetes?view=vs-2019了解更多关于如何工作的内容。
AKS 的 缺点 如下:
-
AKS 的升级过程包含一些手动步骤。
注意
自动集群升级目前处于公开预览阶段。你可以在
azure.microsoft.com/en-us/updates/public-preview-automatic-cluster-upgrades-in-aks/查看当前状态。 -
一些设置,如网络策略,只能在创建集群时设置,之后无法启用。你可以在 https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy 中找到更多详细信息。
-
如果你使用的是区域集群,AKS 仅提供 99.95% 的 SLA,这会增加集群的成本。关于该成本的详细信息,可以在
azure.microsoft.com/en-us/pricing/details/bandwidth/找到。
现在我们已经了解了每个主要托管提供商的优缺点,我们将深入探讨如何在云服务提供商和 Rancher 中设置一切,以便开始创建集群。
准备云服务提供商
在 Rancher 中创建托管集群之前,我们需要为 Rancher 准备云服务提供商。在本节中,我们将涵盖如何在三个主要的托管 Kubernetes 集群中设置权限,分别是 EKS、GKE 和 AKS。
我们将从 Amazon EKS 开始。
Amazon EKS
前提条件如下:
-
你应该已经创建并可以使用 AWS 订阅。
-
你需要在 AWS 中拥有权限,以便能够创建 身份和访问管理 (IAM) 策略。
-
你的 Rancher 服务器应该能够访问 AWS API 的公共或私有端点。你可以在
aws.amazon.com/blogs/compute/introducing-amazon-api-gateway-private-endpoints/阅读更多关于 Amazon API Gateway 私有端点的信息。 -
EKS 需要创建一个 VPC,你应该与网络团队合作来完成这项工作。亚马逊提供了一个教程,位于
docs.aws.amazon.com/eks/latest/userguide/create-public-private-vpc.html,内容涵盖如何创建 VPC。 -
你应该在 AWS 中为 Rancher 创建一个专用的服务账户。
-
你应该在 Rancher 中拥有一个专用的服务账户,并且该账户应该具备管理员级别的权限。你可以使用本地管理员账户来执行此角色。在本节中,我们假设你将使用本地管理员账户。
设置权限
以下是 Rancher 的设置权限:
-
如果你还没有在 AWS 中创建专用服务账户,你应该按照
docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html.上的步骤创建。在本节中,我们将使用rancher作为此服务账户的名称。 -
现在我们已经拥有了服务账户,我们将为该账户分配一个 IAM 策略。此策略赋予 Rancher 创建 EKS 集群所需的权限。最小所需权限可以在 https://rancher.com/docs/rancher/v2.6/en/cluster-provisioning/hosted-kubernetes-clusters/eks/#minimum-eks-permissions 找到,创建 IAM 策略并将其附加到服务账户的步骤可以在
docs.aws.amazon.com/eks/latest/userguide/EKS_IAM_user_policies.html找到。 -
现在我们需要创建访问密钥和秘密密钥对,操作步骤可以在 https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey 找到。需要注意的是,根据 Amazon 的访问密钥最佳实践指南,你应该为访问密钥设置过期时间。这样,你将需要定期更换密钥。最佳实践指南可以在
docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html找到,而旋转访问密钥的文档可以在docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#rotating_access_keys_console找到。如果将来需要使用该密钥,建议将其存储在安全的位置。 -
此时,你应该以本地管理员或专用服务账户身份登录到 Rancher Web UI。
-
对于 Web UI,导航到集群管理,然后转到云凭证。
-
然后,点击创建按钮,并从列表中选择Amazon。
-
填写以下表格。你应该为该凭证命名,使其能够标识这是针对 Amazon 的,并且是哪个订阅的一部分——例如,你可以将其命名为
AWS-Prod。Rancher UI 将测试凭证是否正确,但不会验证该账户是否具备 Rancher 所需的所有权限。另外,默认区域无关紧要,可以随时更改。还需要注意的是,访问密钥将会显示,但秘密密钥是加密的,无法从 Rancher 快速恢复:

图 7.1 – Amazon 云凭证设置向导
有关云凭证的更多详情,请访问rancher.com/docs/rancher/v2.5/en/user-settings/cloud-credentials/。
现在,让我们继续处理 GKE。
GKE
前提条件如下:
-
你应该已经创建并准备好使用 Google 云项目。
-
你需要在 GCP 中具有创建和分配角色的权限。
-
你的 Rancher 服务器应该能够访问 GCP API 的公共或私有端点。你可以在
cloud.google.com/vpc/docs/private-access-options阅读更多关于私有访问选项的信息。 -
与 AWS 一样,你应该在 GCP 和 Rancher 中都拥有专用的服务账户。
-
如果你想使用私有的 GKE 集群,应该查看 Rancher 的文档,
rancher.com/docs/rancher/v2.6/en/cluster-admin/editing-clusters/gke-config-reference/private-clusters/,了解这种类型的集群所需的额外步骤和费用。
设置权限
以下是 Rancher 的设置权限:
-
如果你还没有在 GCP 中创建专用服务账户,应该按照
cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances中的步骤操作。在这一部分,我们将使用rancher作为该服务账户的名称。 -
现在我们已经有了服务账户,我们将为 Rancher 服务账户分配以下默认角色:
compute.viewer、viewer、container.admin和iam.serviceAccountUser。 -
与 API 密钥对不同,GCP 使用服务账户的私钥。你需要将该密钥以 JSON 格式保存。你可以在 https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating_service_account_keys 找到详细的操作指南。你必须保留此密钥以备将来使用。
-
此时,你应该以本地管理员身份或专用服务账户登录到 Rancher Web UI。
-
导航到 集群管理 的 Web UI,然后选择 云凭证。
-
然后,点击 创建 按钮,并从列表中选择 Google。
-
填写以下表单。你应该为这个凭证起个名字,能够让你知道它是用于 Google 的,并且属于哪个项目——例如,你可以称之为
GCP-Prod。Rancher UI 会测试凭证是否正确,但不会验证该账户是否拥有 Rancher 所需的所有权限:

图 7.2 – Google 的云凭证设置向导
最后,让我们深入了解 AKS。
AKS
先决条件如下:
-
你应该已经创建并可以使用 Azure 订阅。
-
你需要在 Azure AD 中拥有权限,才能创建应用程序注册。
-
你的 Rancher 服务器应该能够访问 Azure API 的公共或私有端点。你可以在
docs.microsoft.com/en-us/azure/api-management/api-management-using-with-internal-vnet?tabs=stv2阅读更多关于私有访问选项的信息。 -
Azure 不需要专用服务账户,但像 AWS 和 GCP 一样,Rancher 应该有一个。
-
你应该已经安装了 Azure 命令行工具。
-
你应该为 AKS 集群和相关服务创建一个资源组。
设置权限
下面是 Rancher 的设置权限:
-
运行以下命令。你需要记录输出内容,因为稍后我们会用到:
az ad sp create-for-rbac --skip-assignment -
现在,我们想要使用以下命令为服务主体分配贡献者角色。请注意,你需要从之前的命令中获取应用程序和订阅 ID:
az role assignment create --assignee $appId --scope /subscriptions/$<SUBSCRIPTION-ID>/resourceGroups/$<GROUP> --role Contributor -
此时,你应该以本地管理员或专用服务账户登录到 Rancher Web UI。
-
在 Web UI 中导航到集群管理,然后进入云凭证。
-
然后,点击创建按钮,从列表中选择Azure。
-
填写以下表单。你应该给这个凭证起一个名称,以便你知道它是针对 Azure 的,以及它属于哪个项目——例如,你可以把它命名为
AZ-Prod。Rancher UI 会测试凭证是否正确,但不会验证该账户是否具有 Rancher 所需的所有权限。对于环境字段,AzurePublicCloud是最常见的选项,除非你正在使用政府订阅:

图 7.3 – Azure 的云凭证设置向导
对于其他云提供商,你可以在rancher.com/docs/rancher/v2.6/en/cluster-provisioning/hosted-kubernetes-clusters/找到步骤。此时,Rancher 应该已经能够访问云提供商。接下来的部分我们将介绍如何创建一些托管集群。
安装步骤
在本节中,我们将创建一个托管集群,主要使用默认设置。示例中,我们将继续使用 EKS、GKE 和 AKS。这些设置大部分可以应用于其他云提供商。需要注意的是,你必须已经为每个提供商和环境配置了云凭证。还建议你保持 Rancher 的最新版本,因为云提供商不断变化,你可能会遇到一些 bug,原因仅仅是你使用的是旧版本的 Rancher。最新的稳定版本可以在github.com/rancher/rancher#latest-release找到。
我们将从 Amazon EKS 开始。
Amazon EKS
以下步骤展示了如何通过 Rancher 设置 EKS:
-
使用我们在创建云凭证步骤中使用的服务账户登录到 Rancher。
-
浏览集群管理页面,点击集群,然后点击创建按钮。
-
然后,从列表中选择Amazon EKS,此时应该会弹出一个集群设置向导。
-
你需要为集群指定一个名称。这个名称可以以后更改,但建议不要更改,因为这样可能导致名称不匹配,从而导致用户删除错误的资源。此外,描述字段是一个自由格式字段,可以提供额外的信息,例如谁拥有此集群或应该联系谁;一些用户会在此字段中发布维护信息,例如
每周五晚上 7 点 CDT 定期维护。这个字段可以随时更改。底部部分将该云凭证分配给此集群:
![图 7.4 – Amazon EKS 集群创建向导]
图 7.4 – Amazon EKS 集群创建向导
-
向导的其余部分将填写默认值。你可以根据需要进行更改,但你应该知道自己在更改什么。
-
最后一步是定义节点组。这包括节点的大小、Amazon 机器镜像(AMI)镜像和池大小等设置。定义完集群后,你应该点击创建按钮,此时 Rancher 将开始集群创建过程。
-
所有不同设置的详细信息可以在
rancher.com/docs/rancher/v2.6/en/cluster-admin/editing-clusters/eks-config-reference/找到。 -
集群将进入更新中状态,具体取决于集群的大小和 Amazon 的请求队列。这个过程可能需要 2 到 60 分钟不等。请注意,等待时间主要取决于 Amazon 及其繁忙程度。
接下来我们来看一下 GKE。
GKE
现在,我们来看看 GKE 的安装步骤:
-
按照你为 EKS 所做的相同步骤进行操作,但这次从选项菜单中选择Google GKE。
-
主要的区别在于账户访问部分,因为它可能会要求你重新输入云凭证和 Google 项目 ID。
-
所有不同设置的详细信息可以在
rancher.com/docs/rancher/v2.6/en/cluster-admin/editing-clusters/gke-config-reference/找到。 -
再次,点击创建按钮的最后一步将导致 Rancher 开始集群创建过程。
-
集群将进入更新中状态,具体取决于集群的大小和 Google 的请求队列。这个过程通常需要大约 15 分钟。
最后,我们来看一下 AKS。
AKS
最后,AKS 的安装过程如下:
-
按照相同的步骤进行 EKS 和 GKE 的操作,但这次从选项菜单中选择Azure AKS。
-
所有不同设置的详细信息可以在
rancher.com/docs/rancher/v2.6/en/cluster-admin/editing-clusters/aks-config-reference/找到。 -
需要注意的是,网络策略是只能在创建集群时启用的设置。你可以在 此链接 查找有关不同选项的详细信息。
-
同样,点击创建按钮的最后一步将导致 Rancher 启动集群创建过程。
-
集群将进入更新状态,具体取决于集群的大小和微软的请求队列。此过程通常需要约 60 分钟。从经验来看,订阅中的第一个集群需要的时间最长,后续集群会更快。
到此为止,我们应该已经拥有一个来自一个或多个云服务提供商的 Kubernetes 集群,并且能够轻松地为需要的不同集群重复此过程。这将引导我们进入最后一部分,讨论集群启动并运行后应该做什么。
持续维护任务
在创建集群后,需要进行一些持续的维护任务,以保持集群的健康状态。
第一个推荐任务是设置备份。但由于这是托管集群,我们不能像在 RKE1/2 集群中那样进行 etcd 备份。因此,我们需要使用第三方工具,如对备份文件执行 kubectl apply。我们将在后续章节中介绍这些工具。
第二个推荐任务是测试和记录升级如何影响你的应用程序。由于大多数云服务提供商在计划升级期间会强制驱逐节点,你需要测试你的应用程序如何处理这种类型的驱逐。例如,如果你使用的是像 MariaDB Galera Cluster 这样的多主数据库,你的数据库 pod 重建速度是否比工作节点的驱逐速度更快?测试这一点的典型方法是通过更改节点镜像来模拟 Kubernetes 升级的影响。这是因为大多数提供商不允许你降级集群。因此,无法反复进行此测试。
概述
在本章中,我们了解了 Rancher 可以部署的不同类型的托管集群,包括每种类型的要求和限制。接着我们讨论了架构每种类型集群的规则,包括每种解决方案的一些优缺点。最后,我们详细介绍了创建每种类型集群的步骤。本章结束时,我们回顾了主要的持续维护任务。
下一章将介绍如何将外部管理的集群导入到 Rancher。
第八章:第八章:将外部管理的集群导入 Rancher
在之前的章节中,我们涵盖了由 Rancher 创建的集群和托管的集群。本章将涵盖由 Rancher 导入的集群,以及在执行此操作时的要求和限制。接着,我们将深入探讨几个示例设置,最后在本章结束时探讨 Rancher 如何访问一个导入的集群。
在本章中,我们将涵盖以下主要内容:
-
什么是导入的集群?
-
要求和限制
-
构建解决方案的规则
-
如何让 Rancher 访问集群?
什么是导入的集群?
创建第一个 etcd 备份后,Rancher 无法访问它。原因是 Rancher 只能通过 kube-api 端点访问集群。Rancher 无法直接访问节点、etcd 或集群中更深层次的内容。
在我的新 Rancher 实例中,这个本地集群是什么?
Rancher 将自动导入安装 Rancher 的集群。需要注意的是,这是默认行为。在 Rancher v2.5.0 之前的版本中,曾有一个名为 addLocal=false 的 Helm 选项,允许你禁用 Rancher 导入本地集群。但在 Rancher v2.5.0 中,这个功能被移除,并用 restrictedAdmin 标志取而代之,该标志限制对本地集群的访问。同样需要注意的是,默认情况下本地集群被称为 local,但你可以像对 Rancher 中的任何其他集群一样重命名它。通常会将本地集群重命名为更有帮助的名称,比如 rancher-prod 或 rancher-west。在本节的其余部分,我们将称此集群为本地集群。
为什么本地集群是一个导入的集群?
本地集群是在 Rancher 外部构建的,并且 Rancher 不管理它。这是因为你会遇到鸡生蛋问题。如果没有 Kubernetes 集群,你如何安装 Rancher,但你需要 Rancher 来创建一个集群?因此,我们将进一步解释这个问题。在 Rancher v2.5.0 之前,你需要创建一个 RKE 集群(有关创建 RKE 集群的详细安装说明,请参考 第四章,创建 RKE 和 RKE2 集群)。但是因为这个集群由 RKE 管理,而不是由 Rancher 管理,所以本地集群需要是一个导入的集群。
为什么有些导入的集群特殊?
现在,随着 Rancher v2.5.0+ 和 RKE2 的发布,情况发生了变化。当 Rancher 安装在 RKE2 集群上时,你可以将 RKE2 集群导入到 Rancher 中,并允许 Rancher 接管该集群的管理。这是因为引入了一种新的工具,叫做系统升级控制器,它通过一组名为计划的自定义资源定义(CRD)来帮助 RKE2 和 k3s 集群在集群内部进行管理。该控制器允许你定义操作,例如升级集群中节点的操作系统、升级 Kubernetes 版本,甚至管理新的 k3OS 操作系统。这些设置只是 Kubernetes 对象,你可以根据需要进行修改。有关系统升级控制器的更多详情,请访问 github.com/rancher/system-upgrade-controller。
可以导入哪些类型的集群?
使用 Rancher,你可以导入任何通过 云原生计算基金会(CNCF)认证的 Kubernetes 集群,只要该集群遵循官方 Kubernetes 仓库中定义的标准,仓库地址为 github.com/kubernetes/kubernetes。这包括像 kubernetes-the-hard-way 这样的完全自定义集群,它是一个 100% 手动构建的 Kubernetes 集群,没有 RKE 等工具来为你处理繁重的工作。请注意,这种集群是为学习优化的,不应视为生产就绪的集群。你可以在 github.com/kelseyhightower/kubernetes-the-hard-way 上找到更多关于创建这种集群类型的详细信息和步骤。除了完全自定义集群,你还可以导入使用 RKE 或 VMware 的 Tanzu Kubernetes 产品(基于 vSphere 产品)构建的集群,甚至是 Docker Kubernetes Service(DKS),这是 Docker 企业解决方案的一部分。需要注意的是,像 OpenShift 这样的 Kubernetes 发行版并非 100% CNCF 认证,虽然它可能仍然可以使用,但 Rancher 不会官方支持它。关于 OpenShift 和 Rancher 的更多详情,请访问 rancher.com/docs/rancher/v2.5/en/faq/。
为什么我要导入一个 RKE 集群,而不是在 Rancher 中创建一个?
这个问题的答案归结为控制。假设你希望对 Kubernetes 集群拥有完全的控制权,并且不希望 Rancher 为你定义集群。这包括导入一些已经不再受 Rancher 支持的遗留集群,或者从一些第三方云提供商那里导入的集群,而这些集群当前 Rancher 不支持。
Rancher 可以对导入的集群做什么?
即使在将集群导入 Rancher 时存在一些限制,Rancher 仍然可以为集群提供价值,首个好处是为所有 Kubernetes 集群提供单一视图。我们将在下一节中介绍这些限制。即使 Rancher 的 kubectl 没有直接访问集群的权限。
要求和限制
现在我们了解了在 Rancher 中导入的集群是什么以及它在 Rancher 中的工作原理,我们将继续讨论托管集群在 Rancher 中的要求和限制,以及在选择托管集群时的设计限制和约束。
基本要求
在本节中,我们将介绍 Rancher 需要的 Kubernetes 集群的基本要求。这些在这里概述:
-
Rancher 需要对集群拥有完整的管理员权限,推荐权限级别为
cluster-admin的默认集群角色。 -
导入的集群将需要访问 Rancher API 端点。
-
如果您正在导入 Elastic Kubernetes Service (EKS) 或 Google Kubernetes Engine (GKE) 集群,Rancher 服务器应具有服务账户和云提供商所需的权限。有关托管集群和所需权限的详细信息,请参阅前一章节。
-
Rancher 发布了每个 Rancher 发布版本支持的当前 Kubernetes 版本列表。您可以在
www.suse.com/suse-rancher/support-matrix/all-supported-versions/找到此列表。 -
cattle-node-agent将使用主机的网络,需要禁用systemd-resolved和dnsmasq。此外,应该在/etc/resolv.conf中配置 DNS 服务器,而不是127.0.0.1回环地址。 -
对于 k3s 和 RKE2 集群,在将集群导入 Rancher 之前,您需要在集群上安装
system-upgrade-controller。 -
从 Rancher v2.6.1 开始,Harvester 集群也可以导入。但是,必须使用位于 https://rancher.com/docs/rancher/v2.6/en/virtualization-admin/#feature-flag 的步骤启用此功能标志。
接下来让我们看看设计考虑事项。
设计限制和考虑事项
在本节中,我们将讨论将要导入 Rancher 的集群的限制和注意事项。这些在这里概述:
-
一个集群一次只能导入一个 Rancher 安装。
-
可以在 Rancher 安装之间迁移集群,但是在移动后需要重新创建项目和权限。
-
如果您在为 Rancher API 端点提供访问的过程中使用 HyperText Transfer Protocol/Secure (HTTP/S) 代理,则需要添加额外的代理环境变量详细信息,可以在
rancher.com/docs/rancher/v2.5/en/cluster-provisioning/registered-clusters/找到。 -
如果集群有
cattle-cluster-agent和cattle-node-agent,则需要一个不受限制的策略,因为节点代理将挂载主机文件系统,包括根文件系统。集群代理将需要访问集群中的所有对象。 -
如果集群有
cattle-system命名空间,建议将其加入忽略列表。这是因为代理不会设置限制和请求,部署后所做的任何更改将被覆盖。更多详细信息,请参见 OPA Gatekeeper 文档:github.com/open-policy-agent/gatekeeper和www.openpolicyagent.org/docs/latest/kubernetes-tutorial/。 -
需要注意的是,从 Rancher v2.6.2 开始,k3s 和 RKE2 集群仍处于技术预览阶段,因此它们可能缺少某些功能并存在严重的 bug。
-
/etc/rancher/rke2/config.yaml文件中定义的 RKE2 配置设置无法被 Rancher 覆盖,因此你应尽量减少对该文件的自定义修改。 -
对于使用外部管理数据库(如 MySQL、Postgres 或非嵌入式
etcd数据库)的导入 k3s 集群,Rancher 和 k3s 将无法访问和使用所需的工具进行数据库备份。此类任务需要在外部进行管理。 -
如果集群已导入到 Rancher 中,然后又被重新导入到另一个 Rancher 实例中,通过 Rancher 目录部署的任何应用程序都将被导入,并需要重新部署或直接使用 Helm 管理。
-
如果导入的集群使用 Rancher Monitoring v1,你需要在重新启用 Rancher UI 中的监控之前,卸载并清理所有监控命名空间和 CRD。
假设你在集群导入 Rancher 之前已经部署了一个 fleet。在导入 Rancher v2.6.0 之前,应该先卸载该 fleet,因为 fleet 已经集成在 Rancher 中,两个不同的 fleet 代理将相互冲突。
到目前为止,我们已经了解了将外部管理的集群导入 Rancher 的所有需求和限制。我们将在下一节中使用这些内容开始创建我们的集群设计。
架构解决方案的规则
在本节中,我们将介绍一些标准设计方案及其优缺点。需要注意的是,每个环境都是独特的,需要根据最佳性能和体验进行调优。还需要指出的是,所有中央处理单元(CPU)、内存和存储的大小都是推荐的起始点,可能需要根据你的工作负载和部署流程进行增减。此外,我们将涵盖外部管理的 RKE 集群和 Kubernetes The Hard Way 的设计,但你应该能够将核心概念应用到其他基础设施提供商。
在设计解决方案之前,你应该能够回答以下问题:
-
是否会有多个环境共享同一个集群?
-
生产和非生产工作负载是否会在同一个集群上运行?
-
该集群需要什么级别的可用性?
-
该集群是否会跨多个数据中心运行,形成一个城市集群环境?
-
集群中节点之间将有多少延迟?
-
集群中将托管多少个 Pod?
-
您将在集群中部署的 Pod 的平均大小和最大大小是多少?
-
您的某些应用程序是否需要图形处理单元(GPU)支持?
-
您需要为您的应用程序提供存储吗?
-
如果您需要存储,您只需要只读写一次(RWO),还是需要读写多次(RWX)?
外部管理的 RKE
在这种类型的集群中,您使用 RKE 工具以及cluster.yaml文件手动创建和更新 Kubernetes 集群。从本质上讲,Rancher 启动的集群和外部管理的 RKE 集群都使用 RKE 工具,区别在于谁负责集群及其配置文件的管理。请注意,如果这些文件丢失,将很难继续管理集群,您将需要恢复它们。
优点在此列出:
-
控制权归您所有,因为您在集群上手动运行 RKE。您可以控制节点的添加与删除。
-
集群不再依赖于 Rancher 服务器,因此,如果您想从环境中移除 Rancher,可以按照
rancher.com/docs/rancher/v2.5/en/faq/removing-rancher/中的步骤,将 Rancher 从环境中移除,而无需重新构建集群。
缺点在此列出:
-
您需要负责保持 RKE 二进制文件的最新状态,并确保 RKE 版本与您的集群匹配。如果不遵循此要求,RKE 可能会进行意外的升级或降级,这可能会破坏您的集群。
-
您需要负责维护
cluster.yaml文件,随着节点的添加和移除进行更新。 -
您必须拥有一台可以通过安全外壳协议(SSH)访问集群中所有节点的服务器或工作站。
-
在任何集群创建或更新事件后,您需要负责保护
cluster.rkestate文件,它包含集群的密钥和证书。没有这个文件,RKE 将无法正常工作。请注意,您可以通过github.com/rancherlabs/support-tools/pull/63中的步骤,从运行中的集群恢复该文件。
Kubernetes The Hard Way
该集群设计用于希望学习 Kubernetes 且不想自动化集群创建和维护的人员。通常在实验环境中使用这种配置,您可能需要运行一些非常非标准的配置。有关此类集群的详细信息和步骤,请参见github.com/kelseyhightower/kubernetes-the-hard-way。
优点在此列出:
-
知识—由于 Kubernetes The Hard Way 是为了学习而优化的,你将亲自处理集群创建和管理过程中的每个步骤。这意味着没有幕后黑手为你管理集群。
-
定制化—因为你需要部署每个组件,所以你拥有完全的控制权来选择版本、设置,甚至可以将标准组件替换为定制解决方案。
-
运行最新版本的能力,因为大多数 Kubernetes 发行版在上游 Kubernetes 发布新版本后,会有一段时间的滞后才能提供给最终用户。这是因为需要进行测试、代码更改、发布计划等原因。
缺点在这里列出:
-
Kubernetes The Hard Way 并非为生产环境设计,且社区支持极为有限。
-
集群的维护非常困难,因为像 RKE 这样的发行版提供了许多维护服务,例如自动化
etcd备份、证书创建和轮换。而在 Kubernetes The Hard Way 中,你需要自己编写脚本来处理这些任务。 -
版本匹配—在 Kubernetes The Hard Way 中,你需要选择每个组件的版本,这需要大量的测试和验证。而发行版会为你处理好这些问题。
k3s 集群
该集群是一个完全认证的 Kubernetes 发行版,专为边缘计算和远程位置设计。其核心卖点是支持 ARM64 和 ARMv7 架构,使得 k3s 能够在树莓派或其他低功耗服务器上运行。有关 k3s 的详细信息可以查看rancher.com/docs/k3s/latest/en/和k3s.io/。我们也在前面的章节中更加深入和详细地介绍了 k3s。
优点在这里列出:
-
截至目前,k3s 是唯一支持在 ARM64 和 ARMv7 节点上运行的 Rancher 发行版。未来 RKE2 应该会添加对 ARM64 的全面支持。
重要提示
官方支持正在通过
github.com/rancher/rke2/issues/1946进行跟踪。 -
k3s 被设计成在集群创建时非常快速。因此,你可以创建一个 k3s 集群,将其导入到 Rancher 中,运行一些测试,然后删除集群,这一切都可以作为一个流水线的一部分,用于测试集群软件,例如特殊控制器和其他集群级别的软件。
-
假设你在一个网络连接较差的远程位置部署了 k3s。你仍然可以将其导入 Rancher,提供统一的管理面板和其他相关功能,但如果 k3s 集群与 Rancher 之间的连接中断,集群将继续运行,应用程序也不会察觉到任何变化。
缺点在这里列出:
-
导入的 k3s 集群在 Rancher v2.6.2 中仍处于技术预览阶段,仍然缺少节点创建等功能。
-
k3s 集群仍然需要在 Rancher 外部先行构建,然后再导入到 Rancher,这需要额外的工作和脚本支持。
RKE2 集群
这种集群是 Rancher 中 Kubernetes 集群的未来,因为 RKE2 从零开始设计,旨在将集群的管理从外部迁移到内部。具体来说,RKE 使用外部工具(RKE 二进制文件),你需要负责配置文件和状态文件,这会带来相当大的管理开销。Rancher 最初通过让 Rancher 服务器接管这一过程来解决这个问题,但这个方法在扩展性上存在问题。如果你有成千上万的集群由 Rancher 管理,仅仅保持所有连接的正常和健康就是一场噩梦,更不用说在每个集群发生变化时运行 rke up。RKE2 使用为 k3s 创建的引导过程将这一任务转移到集群本身。在前几章中,我们深入探讨了 RKE2。
优点如下:
-
从 Rancher v2.6.0 开始,你可以在 Rancher 外部创建一个 RKE2 集群,导入它,并让 Rancher 接管集群的管理。
-
通过导入一个 RKE2 集群,你不再需要
cattle-node-agent,因为rke2-agent已经取代了这个功能,而且该代理不需要 Rancher 才能工作。 -
一个 RKE2 集群可以导入到 Rancher 中并删除,而不会影响集群本身。
缺点如下:
-
RKE2 仍处于技术预览阶段,支持和功能有限。
-
在将集群导入 Rancher 之前,你仍然需要引导集群中的第一个节点,这需要额外的工具或脚本支持。
-
RKE2 不支持 k3OS 操作系统,但通过 Harvester,这个功能目前正在开发中。你可以在
github.com/harvester/harvester/issues/581查看更多详情。 -
截至目前,导入的 RKE2 集群已正式支持 Windows 节点。你可以在
docs.rke2.io/install/quickstart/#windows-agent-worker-node-installation找到将 Windows 工作节点加入 RKE2 集群的文档化流程。如果你要将此集群导入到 Rancher 中,你必须在集群中拥有一个 Linux 节点来支持 Cattle 代理。
到此为止,我们应该已经确定了设计,并准备好部署集群并将其导入 Rancher。
Rancher 如何访问一个集群?
在我们深入探讨 Rancher 如何访问导入的集群之前,我们首先需要了解将集群导入 Rancher 的步骤。这个过程相对简单,你只需要在集群上运行 kubectl 命令。此命令将会在集群上部署所需的代理。
导入的集群访问下游集群的方式与 Rancher 对其他类型集群的访问方式相同。cattle-cluster-agent 进程在下游集群的一个工作节点上运行。然后,代理连接 Kubernetes API 端点,默认情况下使用内部服务记录,但也可以通过 KUBERNETES_SERVICE_HOST 和 KUBERNETES_PORT 环境变量进行覆盖。不过,通常情况下不需要这样做。集群代理将使用 Cattle 服务账户中定义的凭证连接到 kube-api 端点。如果代理连接失败,它将退出,Pod 会重试直到连接成功。然而,值得注意的是,在这个过程中,假设节点仍处于 Ready 状态,Pods 不会被重新调度到其他节点。这可能会导致“僵尸节点”问题,这些节点未正确报告其节点状态。例如,如果节点上的 DNS 出现问题,集群代理将无法建立连接,但节点可能仍处于 Ready 状态。需要注意的是,在 Rancher v2.6.0 版本中,两个集群代理具有节点调度规则,确保它们运行在不同的工作节点上。
一旦集群代理成功连接到 Kubernetes API 端点,代理将连接到 Rancher API 端点。代理通过首先向https://RancherServer/ping发送 HTTPS 请求,收到 200 OK 响应以及 pong 输出来完成此操作。这样做是为了验证 Rancher 服务器是否正常运行、健康并准备好接受连接。作为建立此连接的一部分,代理要求连接使用 HTTPS 协议并且具有有效的证书,如果您使用的是来自已知根证书机构的公共签名证书,这没有问题。然而,如果使用自签名证书或内部签署的证书,或者代理的基础镜像不信任该证书机构,便会出现问题。在这种情况下,连接将会失败。为了解决这个问题,代理使用名为 CATTLE_CA_CHECKSUM 的环境变量,比较该变量的值是否相同。然后,代理会将该根证书添加到其受信任的根证书列表中,从而允许连接过程继续。如果此检查失败,代理将休眠 60 秒并重新尝试。这就是为什么在更改 Rancher 服务器的根证书之前,务必先更新代理的原因。
注意
如果您想更改 Rancher 服务器的根证书,请按照文档中的过程操作,链接在github.com/rancherlabs/support-tools/tree/master/cluster-agent-tool。该脚本将使用更新后的值重新部署代理。
一旦代理成功连接到 Rancher,代理将把集群令牌发送给 Rancher,使用该令牌将代理与其集群匹配并处理认证。此时,代理将建立一个 WebSocket 连接到 Rancher,并使用此连接绑定到 Rancher leader pod 内的一个随机回环端口。然后,代理将通过发送探测请求来打开该连接,以防止连接超时。这个连接不应该断开,但如果断开,代理将自动尝试重新连接,并持续重试直到成功。Rancher 服务器随后使用回环端口连接到下游集群。
总结
在本章中,我们学习了导入集群及其工作原理,包括代理在导入集群上与其他集群的工作方式不同。我们了解了此类型集群的局限性以及为何可能需要这些局限性。接着,我们讨论了每种解决方案的优缺点。最后,我们详细讲解了创建每种类型集群的步骤。我们以介绍 Rancher 如何提供对导入集群的访问作为本章的结束。
下一章将介绍如何在 Rancher 中管理集群配置,随着时间的推移并按规模扩展。
第四部分 – 使你的集群具备生产环境能力
本部分将涵盖将一个空白的 Kubernetes 集群转变为生产就绪集群的所有步骤。这包括诸如 etcd 备份和 Kubernetes 升级、监控集群健康状态、收集应用日志以及使用 OPA Gatekeeper 和 Longhorn 等工具扩展集群功能等维护任务。
本书的这一部分包括以下章节:
-
第九章,集群配置备份与恢复
-
第十章,监控与日志记录
-
第十一章,使用 Longhorn 为 Kubernetes 带来存储
-
第十二章,使用 OPA Gatekeeper 进行安全性和合规性管理
-
第十三章,在 Kubernetes 中进行扩展
-
第十四章,负载均衡器配置与 SSL 证书
-
第十五章,Rancher 与 Kubernetes 故障排除
第九章:第九章:集群配置备份与恢复
前几章讲解了如何将外部管理的集群导入到 Rancher 中。本章将重点介绍如何在 Rancher 中管理 RKE1 和 RKE2 集群,尤其是在集群的备份和恢复方面。我们将包括设置备份的一些最佳实践。然后,我们将演示一个 etcd 恢复,最后讨论 etcd 备份的限制。
本章将涵盖以下主要内容:
-
什么是 etcd 备份?
-
为什么我需要备份我的 etcd?
-
etcd 备份是如何工作的?
-
etcd 恢复是如何工作的?
-
何时需要进行 etcd 恢复?
-
etcd 备份无法保护哪些内容?
-
如何配置 etcd 备份?
-
如何进行 etcd 备份?
-
如何从 etcd 备份中恢复?
-
设置实验环境以测试常见故障场景
什么是 etcd 备份?
正如我们在第二章《Rancher 和 Kubernetes 高级架构》中所讲的,etcd 是 Kubernetes 的数据库,用于存储集群的配置。RKE1 和 RKE2 都使用 etcd 来完成这一角色,但其他发行版,如 k3s,可能使用不同的数据库,如 MySQL、PostgreSQL 或 SQLite。对于本章内容,我们将仅关注 etcd。在 Kubernetes 中,所有组件都设计为无状态的,并且不在本地存储任何数据。唯一的例外是 etcd,它的唯一任务就是为集群存储持久化数据。这包括集群的所有设置,以及所有部署、Secrets 和 ConfigMap 的定义。这意味着如果 etcd 集群丢失,你将丢失整个集群,这也是为什么从可用性角度来看,保护 etcd 集群至关重要的原因,我们在第二章《Rancher 和 Kubernetes 高级架构》和第四章《创建 RKE 和 RKE2 集群》中都有提到。
为什么我需要备份我的 etcd?
当人们开始 Kubernetes 之旅时,常见的一个问题是:“为什么我需要备份 etcd?”接下来常问的问题是:“如果发生任何问题,我不能重新部署集群吗?”我回答这个问题的方法是:“是的,在理想的情况下,你应该能够通过重新部署一切来从零开始重建集群。但我们并不生活在完美的世界里。如果你丢失了集群,在现实世界中重新部署 100% 的应用程序是非常困难的。”
我经常举的例子是,假设现在是星期五的深夜,你刚刚进行了一次 Kubernetes 升级,但现在一切都失败了。应用程序崩溃,你无法找到解决方法来继续升级。如果你有一次升级前的 etcd 备份,使用 Rancher 只需要几次点击,就能将集群恢复到升级前的状态,而不是花费数小时启动一个新集群,然后再花几个小时在集群上部署所有核心服务,比如监控、日志和存储。更何况,谁知道你能多快地重新部署所有应用程序,前提是启动过程已经完备记录,或者仍然有效。
强烈建议无论在什么环境下,包括开发和测试环境,都进行 etcd 备份,因为它为你提供了更多选择。我一直遵循的原则是 没人因为备份太多而被解雇。值得注意的是,在 Rancher 部署的集群中,备份默认是开启的。这是因为 etcd 备份通常只占用几百兆存储,在灾难发生时非常有价值。
常见的问题之一是:“如果我有虚拟机快照,我还需要 etcd 备份吗?”虽然有额外的备份总是好的,但问题在于从快照恢复后如何恢复 etcd。问题是,所有节点必须在快照时处于同步状态,恢复才会成功。如果这是你唯一的选择,你仍然可以从虚拟机快照中恢复 etcd,但你需要恢复一个 etcd 节点,清理其他 etcd 节点,并从恢复的节点重新同步 etcd 数据。你可以在 github.com/rancherlabs/support-tools/tree/master/etcd-tools 找到此过程和脚本。需要知道的是,这个过程可能非常困难且耗时,并且它不是官方支持的解决方案。
etcd 备份是如何工作的?
在本节中,我们将了解 etcd 备份如何在 RKE 和 RKE2 集群中工作。
RKE 集群
对于 RKE 集群,单次快照的过程由 RKE 二进制文件控制,定期快照则由一个由 RKE 部署的独立容器 etcd-rolling-snapshots 管理。两个过程遵循相同的基本步骤,第一步是依次进入集群中的每个 etcd 节点,并启动一个名为 etcd-snapshot-once 或 etcd-rolling-snapshots 的容器,具体取决于备份类型。这个容器将承担大部分的工作。需要注意的是,这是一个 Kubernetes 外部的 Docker 容器,且对这个容器的定制非常有限。一旦容器启动,它会运行一个名为 rke-etcd-backup 的工具,这是 Rancher 的 rke-tools 之一,可以在 github.com/rancher/rke-tools/ 找到。
该工具主要是一个实用脚本,用于查找证书文件,并在此时执行 etcdctl snapshot save 命令。此命令将整个 etcd 数据库导出为一个单一文件。需要特别注意的是,这是一个完整备份,而不是增量或差异备份。此外,etcd 不像其他数据库那样有日志翻译功能,因此快照文件包含了整个数据库作为一个单一文件。
一旦数据库完成备份,RKE 会备份一些额外的文件,以便于集群恢复。这包括从 kube-system 命名空间中的 configmap full-cluster-state 提取 cluster.rkestate 文件。在 v1.0.0 之前的 RKE 版本中,RKE 会备份 /etc/kubernetes/ssl/ 证书文件夹,但现在不再需要,因为 rkestate 文件已经将所有证书及其私钥包含在 JSON 文件中。所有文件创建完毕后,rke-tools 会将所有文件压缩成一个备份文件,并存储在主机的 /opt/rke/etcd-snapshots/ 目录下。然后,如果已配置 S3 备份,rke-tools 会将备份文件上传到 S3 存储桶。
重要注意事项
默认情况下,rke-tools 会保留本地备份副本,以防万一。
最后,rke-tools 会清理备份。此操作是通过计算所有计划备份文件的总数来完成的。如果该数量超过了保留设置(默认设置为 6),它将开始删除最旧的备份,直到符合保留设置。需要特别注意的是,任何一次性快照将不会被计入和删除。因此,这些备份通常会保留在节点上,直到手动清理。此过程完成后,RKE 将开始处理集群中的下一个 etcd 节点。
注意
存在一个已知的设计特点,对于 S3 备份,集群中的所有 etcd 节点都会进行备份,并且每个节点将把其备份文件上传到 S3 存储桶,且文件名称相同。这意味着在备份过程中,文件会被多次覆盖。
RKE2/k3s 集群
对于 RKE2 和 k3s 集群,它们共享一个 etcd 备份和恢复的代码。与 RKE 的主要区别在于,etcd 备份过程直接集成在 rke2-server 二进制文件中,而不是作为单独的容器存在。对于 RKE2/k3s,etcd 备份默认启用,并通过服务器选项进行配置,我们将在本章后面讲解。另一个主要区别是,在 RKE2 中,备份的唯一文件就是 etcd 快照文件,因为 RKE2 不需要像 RKE 那样的 rkestate 文件。对于 RKE2,集群状态存储在引导密钥中,并直接存储在 etcd 数据库中。需要特别注意的是,引导密钥使用 AES SHA1 加密算法,以服务器令牌作为加密密钥进行加密,而不是存储在 etcd 中。您需要在备份过程之外存储并保护令牌。如果丢失了令牌,将无法在不破解加密的情况下恢复集群。
另一个区别是备份的配置方式,因为每个主节点都是独立配置的,这意味着你可以为每个节点设置不同的备份计划。这也包括定期快照的运行方式,实际上它使用了 cronjob 格式,允许你在设定的时间强制执行备份,例如,每晚午夜或每小时的整点。为了解决 RKE 存在的 S3 覆盖问题,RKE2 在备份文件名中使用了节点的主机名。这意味着集群中的每个节点仍会进行 etcd 备份并将其上传到 S3 存储桶中,但不会被覆盖。因此,你的 S3 存储桶中会有重复的备份文件,这意味着如果你在 RKE2 集群中有三个主节点,你将会在 S3 中有三个 etcd 备份文件的副本。再次强调,在 etcd 中,备份通常很小,因此增加的存储通常只是背景噪音。
etcd 恢复是如何工作的?
接下来,让我们看看在不同集群中,etcd 恢复是如何工作的。
RKE 集群
对于 RKE 集群,恢复 etcd 的过程是使用rke etcd snapshot-restore命令完成的,该命令使用与 RKE 二进制文件用于备份的相同独立容器(rke-tools)。主要区别在于,所有 etcd 节点都需要相同的备份文件来进行恢复。这意味着,当你将快照名称提供给 RKE 二进制文件时,集群中的所有节点必须拥有该文件的副本。
恢复过程的第一步是在每个节点上创建文件的 MD5 哈希值,并比较哈希值以验证所有节点是否一致。如果此检查失败,恢复将停止,并要求用户手动在节点之间复制备份文件。可以向 RKE restore 命令添加一个名为--skip-hash-check=true的标志,但这是一个安全功能,除非你知道自己在做什么,否则不应禁用。如果你使用的是 S3 选项,RKE 会在每个节点上从 S3 存储桶下载备份文件,然后才会运行此过程,此时哈希验证过程是相同的。
一旦备份文件经过验证,RKE 将拆除 etcd 集群,这意味着 RKE 会停止所有节点上的 etcd 和控制平面容器,此时 RKE 将启动一个名为etcd-restore的独立容器,该容器将在每个节点上恢复 etcd 数据目录。这就是为什么所有节点必须拥有相同的快照文件的原因。 一旦所有节点上的恢复容器成功完成,RKE 将运行标准的 RKE up 过程来重新构建 etcd 和控制平面,包括创建新的 etcd 集群,然后启动控制平面服务。
最后,通过更新工作节点来结束这个过程。在此任务期间,集群将会停机大约 5 到 10 分钟,直到恢复过程完成。大多数应用程序 Pods 应该会继续运行而不受影响,前提是它们不依赖于 kube-api 服务。例如,ingress-nginx-controller 在恢复过程中将保持运行,但配置会出现暂停。
RKE2/k3s 集群
恢复过程在 RKE2/k3s 中与 RKE 非常不同,因为在 RKE 中,集群中的一个主服务器将作为新的引导节点来重置集群。这个过程会停止集群中所有主节点上的 rke2-server 服务。新的引导节点 rke2 将运行以下命令:
rke2 server --cluster-reset --cluster-reset-restore-path=<PATH-TO-SNAPSHOT>
这将创建一个新的 etcd 集群 ID,并将 etcd 快照恢复到新的单节点 etcd 集群中。此时,rke2-server 将能够启动。其余的 rke2 主节点需要清理并重新加入集群,作为新的节点。一旦所有主节点都恢复并健康运行,工作节点应该会自动重新加入,但这可能较慢且不可靠,因此在恢复后重启 rke2-agents 是标准做法。
重要提示
需要注意的是,恢复过程将使整个集群回滚。这包括任何 Deployments、ConfigMaps、Secrets 等内容。因此,如果你正在恢复以解决应用程序问题,你需要重新应用集群中其他应用程序的任何更改。
你什么时候需要进行 etcd 恢复?
当然,以下问题总是会出现:“我什么时候应该进行 etcd 恢复?”和“etcd 恢复仅仅是应急情况吗?”一般来说,etcd 恢复主要用于灾难恢复和回滚 Kubernetes 升级。例如,你不小心删除了集群中大部分或所有的 etcd 节点,或遇到基础设施问题,如停电或存储故障。如果集群能够自行恢复,从事件发生前的最后备份恢复等会是恢复集群服务的最快方式。
进行恢复的另一个主要原因是 Kubernetes 升级失败。和 RKE 一样,没有办法在不从升级前的 etcd 备份恢复集群的情况下回退集群。这就是为什么我们总是建议在升级前拍摄快照。需要注意的是,RKE 二进制文件允许你设置旧的 Kubernetes 版本,并尝试将该版本推送到集群。这一过程通常会破坏集群,并且没有官方支持。在这两种情况下,集群会停机或处于失败状态,我们的目标是尽快恢复服务。
当然,下一个问题是,“什么时候我不应该执行 etcd 恢复?”答案是,你不应该通过恢复来回滚失败的应用程序更改。例如,如果一个应用程序团队推送了一个更改,导致应用程序失败,也就是说,他们的代码中有一个 bug,或者他们在应用程序中配置错误。执行 etcd 恢复到更改之前的状态可以回滚这些更改,但你也会影响集群中所有其他部署的应用程序,并且重新启动集群来回滚本应通过重新部署带有旧代码/设置的应用程序来修复的更改。
注意
为应用程序团队建立回滚部署的流程应该是你环境中的必需项。大多数 CI/CD 系统通常都能选择旧的提交并将其推送出去。
另一个不应该执行 etcd 恢复的主要原因是恢复旧的快照。例如,如果你从几周前的备份恢复,很有可能令牌已经过期,因此恢复后集群无法启动。解决这个问题将需要手动操作来刷新损坏服务的令牌。而且,最大的问题是:“这个集群自从备份以来发生了什么变化?”谁知道自从快照拍摄以来,这个集群发生了哪些升级、部署、代码更改等。你可能在修复一个团队的问题时,破坏了其他团队的应用程序。我遵循的规则是 72 小时。如果快照超过 72 小时,我需要权衡是否恢复它,也就是说,是否大部分时间是在没有进行更改的周末?太好了,我对从周五恢复到周一的快照没问题。但如果我知道应用程序团队喜欢在周四部署,而我正在从周三的快照恢复,那么我应该停下来与应用程序团队沟通,然后再继续操作。
最后,在 Kubernetes 升级后恢复时,我的规则是:升级是恢复的“分界线”,应该只在升级后不久进行恢复。例如,假设我将集群从 v1.19 升级到 v1.20,并且在几分钟内,我的应用程序开始出现问题。那么,太好了,我们可以恢复到升级前的快照。但如果我在周五晚上做了这个升级,而在周二,一个应用程序团队的成员找到了我,说:“嘿,我们看到一些奇怪的错误。你能回滚这个升级吗?”我的回答将是“不行”。自上次升级以来已经过去太长时间,回滚将对集群造成过多影响。当然,我接下来会问他们,“为什么你们的冒烟测试在升级后没发现这个问题?”因为在环境发生重大变化后,冒烟测试应用程序是一个标准流程。
etcd 备份不能保护什么?
当然,etcd 备份并不覆盖集群中的所有数据,正如我们在本章前面讨论过的,etcd 只存储集群的配置。但集群中还有其他数据没有存储在 etcd 中。最主要的就是卷和卷数据中存储的数据。假设你有一个PersistentVolumeClaim(PVC)或PersistentVolume(PV),其中包含卷内的数据。该数据不会存储在 etcd 中,而是存储在存储设备上,即网络文件系统(NFS)、本地存储、Longhorn 等。etcd 中唯一存储的内容是卷的定义,即卷的名称、大小、配置等。这意味着如果在删除卷后从 etcd 备份恢复集群,根据存储提供商及其保留策略,卷内的数据将丢失。因此,即使你执行 etcd 恢复,集群会创建一个新卷来替代删除的卷,但该卷将是空的,里面没有数据。如果你需要备份卷或其他更高级的备份功能,应该考虑使用像 Veeam 的 Kasten 或 VMware 的 Velero 这样的工具。
另一个在 etcd 备份中没有备份的大项是容器镜像,这意味着使用自定义镜像的部署。etcd 只存储镜像配置,即镜像示例:docker.io/rancherlabs/swiss-army-knife:v1.0。但这不包括镜像本身的数据。这通常出现在某人使用自定义镜像部署应用程序后,之后失去对该镜像的访问权限。一个很好的例子是将容器镜像托管在集群中需要的仓库服务器中,例如 Harbor 或 JFrog。
如何配置 etcd 备份?
让我们来看看如何为 RKE 和 RKE2 集群配置 etcd 备份。
RKE 集群
对于 RKE 集群,etcd 备份配置存储在cluster.yml文件中。RKE 有两种主要类型的 etcd 备份。第一种是一次性备份,由用户事件手动触发,例如手动运行rke etcd snapshot-save命令、升级 Kubernetes 版本或更改集群中的 etcd 节点。第二种是定期快照,这种方式在 RKE v0.1.12 版本发布时默认开启。默认过程是每 12 小时备份一次所有内容。需要注意的是,这个时间表不像 cronjob 那样固定,总是在相同的时间运行,而是基于自上次备份以来经过的时间。
以下是一组用于本地和 S3 等备份的cluster.yaml示例文件:
-
仅本地备份 –
github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch09/rke/local-backups.yaml -
本地和 S3 备份 –
github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch09/rke/s3-backups.yaml -
要查看完整的选项和设置列表,请参阅官方 Rancher 文档,网址为
rancher.com/docs/rke/latest/en/etcd-snapshots/recurring-snapshots/#options-for-the-etcd-snapshot-service。
RKE2/k3s 集群
正如我们在本章前面提到的,RKE2 和 k3s 在节点级别处理 etcd 备份,而不是在集群级别处理,这意味着您需要在集群中的每个主节点上定义 etcd 备份计划和其他设置,而不是在集群级别定义。这使您可以做一些有趣的事情,例如为每个节点调整备份计划。例如,第一个节点在 12 A.M.、3 A.M.、6 A.M. 等时间进行备份。第二个节点在 1 A.M.、4 A.M.、7 A.M. 等时间进行备份,第三个节点则在 2 A.M.、5 A.M.、8 A.M. 等时间进行备份。请注意,这通常仅在大型集群中进行,以防止所有 etcd 节点同时备份,因为在备份过程中 etcd 的性能会略微下降。因此,我们希望每次只影响一个 etcd 节点。在低环境中,您也可以仅在第一个节点上配置备份,其中备份很优秀但不是必需的。
要查看完整的选项和设置列表,请参阅 RKE2 的官方 Rancher 文档,网址为 https://docs.rke2.io/backup_restore/ 或 k3s 的文档。有关 k3s 的官方文档,请参见rancher.com/docs/k3s/latest/en/backup-restore/。需要注意的是,k3s 中的内嵌 etcd 仍处于实验阶段。
注意
如果您需要通过 HTTP 代理访问您的 S3 存储桶,请按照docs.rke2.io/advanced/#configuring-an-http-proxy中的文档配置代理设置。
以下是本地和 S3 etcd 备份的示例 rke2 配置文件:
-
仅限本地备份 –
github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch09/rke2/local-backups.yaml -
本地和 S3 备份 –
github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch09/rke2/s3-backups.yaml
如何进行 etcd 备份?
本节将介绍如何为 RKE 和 RKE2 集群进行 etcd 备份。
RKE 集群
对于自定义集群,您可以使用以下命令进行一次性备份:
rke etcd snapshot-save --config cluster.yml --name snapshot-name
第一个选项设置 cluster.yml 文件名。只有在你没有使用默认的 cluster.yml 文件名时才需要此选项。第二个选项指定备份的名称。技术上这是可选的,但强烈建议将其设置为具有实际意义的名称,例如 pre-k8s-upgrade 和 post-k8s-upgrade。也强烈建议避免在文件名中使用特殊字符。如果你使用 S3 备份,设置将默认为 cluster.yml 文件中定义的内容。你可以使用命令行标志覆盖这些设置,具体文档见 rancher.com/docs/rke/latest/en/etcd-snapshots/one-time-snapshots/#options-for-rke-etcd-snapshot-save。如果你正在使用通过 Rancher 部署的 RKE 集群,请参阅文档 rancher.com/docs/rancher/v2.6/en/cluster-admin/backing-up-etcd/。
RKE2/k3s 集群
RKE2 和 k3s 使用相同的命令来进行备份,只需将 rke2 替换为 k3s,即可用于 k3s 集群。对于一次性备份,你将运行以下命令:
rke2 etcd-snapshot save –name snapshot-name
name 标志与 RKE 相同,唯一的主要区别是,对于 config.yaml 中与 S3 设置无关的所有选项,你可能会遇到 FATA[0000] flag provided but not defined: ... 错误。为了解决这个问题,建议仅将 S3 设置复制到一个新的文件 s3.yaml 中,路径为 /etc/rancher/rke2/,并将 –config /etc/rancher/rke2/s3.yaml 标志添加到命令中。
如何从 etcd 备份中恢复?
现在让我们来看一下如何从 etcd 备份中恢复数据。
RKE 集群
对于 RKE 的恢复,你需要运行 rke etcd snapshot-save --config cluster.yml --name snapshot-name 命令。至关重要的是,你必须将快照名称设置为你要恢复的快照的文件名,去掉 .zip 文件扩展名。假设你是从一个定期快照中恢复。在这种情况下,文件名将包含时间戳的控制字符,因此建议你再次用单引号括起文件名,并确保去除文件扩展名。
注意
如果你正在将 etcd 备份恢复到一个新的集群中,即所有节点都是新的,你将遇到一些令牌问题,并需要解决此问题。你可以使用以下脚本 github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch09/rke/restore-into-new-cluster.sh 删除机密并回收服务。此脚本是为一个三节点集群设计的,并假设使用默认设置。
RKE2/k3s 集群
对于 RKE2 的恢复,工作量比 RKE 稍大一些。第一步是通过systemctl stop rke2-server命令停止所有主节点上的rke2-server。然后,从一个主节点上重置集群,并使用rke2 server --cluster-reset --cluster-reset-restore-path=<PATH-TO-SNAPSHOT>命令恢复 etcd 数据库。恢复完成后,运行systemctl start rke2-server命令启动新的etcd 集群。接下来,你需要去集群中的其他主节点,并运行rm -rf /var/lib/rancher/rke2/server/db命令来删除节点上存储的 etcd 数据,然后重新启动rke2-server,使用systemctl start rke2-server命令让节点重新加入集群。这将导致一个新的 etcd 成员加入 etcd 集群,并从引导节点同步数据。建议你一次只重新加入一个节点,在该节点进入Ready状态后,再重新加入下一个节点。最后,一旦所有主节点重新加入,工作节点应该也能恢复。但 5 分钟后,你可能需要使用systemctl restart rke2-agent命令重启 rke2-agent,以加快恢复过程。
设置实验环境以测试常见的故障场景
最后,我们通过练习一些常见的故障场景来结束本章。我创建了一个关于此主题的 Kubernetes 课程,名为使用 Rancher 和 Kubernetes 从灾难中恢复,可以在 https://github.com/mattmattox/Kubernetes-Master-Class/tree/main/disaster-recovery 上找到,YouTube 视频位于 https://www.youtube.com/watch?v=qD2kFA8THrY。我在这个课程中介绍了一些我创建的训练场景。每个场景都有一个部署实验集群并故障的脚本。然后,我深入讲解了每个场景的故障排除和恢复步骤。最后,课程以一些预防性任务结束。我通常建议新客户至少在将 Rancher/RKE 投入生产前完成这些场景一次。这应该是你感到舒适并且有文档化流程的内容。这包括验证你是否拥有正确的权限,或者是否有文档化的流程来获取这些权限。通常,你需要在所有 etcd、控制平面和主节点上拥有 root/sudo 权限。
总结
本章我们学习了 RKE、RKE2、k3s 以及 etcd 的备份与恢复,包括备份和恢复过程的工作原理。我们了解了 etcd 备份的局限性。接着,我们介绍了如何配置定期备份。最后,我们详细讲解了一次性备份和从快照恢复的步骤。本章的结尾,我们讨论了使用 Rancher 和 Kubernetes 从灾难中恢复的课程。到此为止,你应该已经能够轻松地备份和恢复你的集群,包括使用 etcd 备份从灾难性故障中恢复。
下一章将讲解 Rancher 中的监控和日志记录。
第十章:第十章:监控与日志
前面的章节介绍了集群配置、备份和恢复。本章将介绍 Rancher 监控,以及 Rancher 如何使用 Prometheus 和 Grafana 来收集集群的指标并进行展示。接下来,我们将讨论 Rancher 日志,以及 Rancher 如何使用 Banzai Cloud Logging 操作符和 Fluent Bit 来收集 Kubernetes 组件的日志和应用日志,包括日志过滤。
在本章中,我们将涵盖以下主要内容:
-
什么是 Prometheus 和 Grafana?
-
部署 Rancher 的监控栈
-
向 Prometheus 添加自定义应用指标
-
在 Prometheus 中创建告警规则
-
创建一个 Grafana 仪表盘
-
什么是 Banzai Cloud Logging 操作符?
-
什么是 Fluent Bit?
-
部署 Rancher 日志
-
过滤应用日志
-
将日志写入多个日志服务器
什么是 Prometheus 和 Grafana?
在本节中,我们将介绍 Kubernetes 集群最流行的监控解决方案。
Prometheus 是一个开源的监控和告警框架,Kubernetes 社区广泛采用它。Prometheus 最初由 SoundCloud 于 2012 年创建,后来被 云原生计算基金会(CNCF)接受,成为继 Kubernetes 之后的第二个孵化项目。Prometheus 从零开始构建,旨在与 Kubernetes 一起使用,核心思想是所有内容都应该通过 Kubernetes API 可发现。此时,Prometheus 会拉取指标并将它们存储为时间序列键值对。
当然,首先总是会有一个问题,什么是指标? 简单来说,指标是对某个资源的数值度量。例如,它可以是某个 Pod 当前的内存使用量,或者是当前数据库服务器的连接数。需要注意的是,Prometheus 只支持整数或浮点数作为指标的值。你不能将值设置为像 up 或 down 这样的词语。例如,如果你想检查某个任务是否成功或失败,你可能会将成功的状态输出为 0,将失败的状态输出为 1。
与度量指标相关的另一个核心问题是,它们应该是某一时刻的值。例如,你可能希望知道某一时刻的平均连接数。因此,你需要在 Pod 级别定义一个度量指标端点。新手常见的陷阱之一是将度量指标端点添加到服务记录中。这可能会更容易,但从长远来看并不推荐这样做,因为将来你可能会希望有不同的规则。例如,你一开始只想知道过去 5 分钟内的平均连接数,之后又想把时间范围改为 15 分钟。那么,你是改变当前度量指标的值,这可能会影响历史报告,还是添加一个新的度量指标,这样就意味着你在收集重复的数据呢?最佳的方法是将原始数据输出为度量指标,然后在 Prometheus 和 Grafana 中处理这些数据。
接下来的问题是,Prometheus 是如何获取数据的? 由于 Prometheus 使用的是pull模型而不是push模型,这通过运行一个 Web 服务器来实现,该服务器将度量指标作为简单的键值对文本输出。这通常在 Prometheus 中被称为导出器(exporter)。这些导出器可以直接嵌入到应用程序中,就像 Kubernetes 的核心组件大多数情况一样。例如,etcd 有一个内置的度量指标导出器,运行在不同的端口2379上。将度量指标运行在与主应用程序不同的端口上是常见的做法,因为 Prometheus 默认会尝试发起没有认证的GET请求。Prometheus 可以查询需要认证的端点,但设置令牌或凭证需要额外的工作和维护。因此,大多数用户会避免这种情况,并利用度量指标仅对集群内部公开而不对外部开放的事实,认为这就足够了的安全性。
当然,Prometheus 使用导出器来收集度量指标,因此就会出现关于有哪些导出器可用的问题。幸运的是,得益于开源社区,几乎所有标准应用程序都有相当多的第三方导出器。例如,几乎所有主要的开源数据库都有对应的导出器,如 MySQL、CouchDB、MongoDB、MSSQL、Oracle DB 和 PostgreSQL。你可以在 https://prometheus.io/docs/instrumenting/exporters/#databases 找到官方列表。标准的 Web 服务器,如 Apache 和 NGINX 也是如此,完整的列表可以在 https://prometheus.io/docs/instrumenting/exporters/#http 找到。当然,几乎所有 Kubernetes 原生应用程序,如 CoreDNS、Longhorn、Linkerd 和 OPA Gatekeeper,也都将 Prometheus 导出器直接嵌入到应用程序中。
对于应用开发人员,Go、Java/JVM、Python 和 Node.js 等语言有多个库可供使用,即使是自定义应用程序也可以内置支持 Prometheus。当然,如果您找不到适用于您的应用程序的现成导出程序,Prometheus 上游提供了出色的资源,帮助您编写导出程序,包括命名标准、示例代码以及处理用例的不同技术方面。所有这些内容都可以在 prometheus.io/docs/instrumenting/writing_exporters/ 找到。
最后,Prometheus 新增的一个功能是告警。由于 Prometheus 已经在收集您的环境和应用数据,因此通过使用 AlertManager 将告警集成到 Prometheus 中是合乎逻辑的。其核心概念是,您定义一组查询,这些查询将在 Prometheus 服务器内部运行,如果条件被触发,将激活一个告警,并将告警发送到 AlertManager,后者会将告警转发到多个外部服务,如电子邮件、Slack 和 PagerDuty。在本章稍后的内容中,我们将介绍如何在 Prometheus 中创建告警,并提供一些示例。
Prometheus 缺少的一个主要功能是可视化数据的方法。这正是 Grafana 发挥作用的地方。Grafana 允许您可视化存储在 Prometheus 及其他数据源(如 MySQL、Loki 和 InfluxDB)中的数据。Grafana 的主要思想是,您创建一个仪表盘,它会查询一个数据源(在本例中为 Prometheus),然后使用这些数据来开发各种图表、图形、仪表等。需要注意的是,Grafana 不会存储任何数据,除了缓存查询结果外。Grafana 还支持从 Loki 和 Elasticsearch 等来源中探索日志。它还具有通知系统,可以基于查询触发告警,正如 Prometheus 所做的那样。这对于应用程序团队创建自定义告警非常有帮助。
部署 Rancher 的监控栈
使用 Rancher,监控有两个主要版本,v1 和 v2。最初的 v1 监控版本是与 Rancher 2.0 到 2.4 一起发布的,基于 Prometheus 和 Grafana。但由于 Rancher 服务器和 UI 管理监控栈的部署和配置,基本思路是将 Prometheus 部署到集群级别,并为每个 Rancher 项目额外部署 Prometheus 服务器。如果您的项目数量较少,并且不需要通过自动化进行控制,这种方法是可行的。最初,所有 Prometheus 服务器的配置都是通过更改 configmap 完成的。随着集群和应用的规模和复杂性增长,这种方法需要大量工作来管理监控设置。
随着 Prometheus 操作符的创建,一切发生了变化。核心思想是,Prometheus 操作符监控一组 自定义资源定义 (CRDs)。这包括 Prometheus 服务器及其相关服务(如 node-exporter 和 Alertmanager)的描述。需要注意的是,v1 和 v2 监控都有内置的规则和仪表板,但最重要的是,探针、警报和其他相关的 Prometheus 设置的配置由 Prometheus 操作符处理,操作符会创建并更新 Prometheus 使用的配置文件。
在 2020 年 10 月,Rancher 2.5 从 v1 监控迁移到 v2,v2 是基于操作符模型构建的。需要注意的是,Prometheus 和 Grafana 都迁移到了这个新模型。这也包括 Rancher 使用标准的上游 Prometheus 和 Grafana 镜像,而非 Rancher 自定义的镜像。
如果你当前正在使用旧的 v1 监控,建议迁移到新的 v2 监控。官方过程可以在 rancher.com/docs/rancher/v2.5/en/monitoring-alerting/guides/migrating/ 中找到,但过程可以总结如下:
-
你需要删除所有当前的设置和配置。
-
然后,卸载旧的 Prometheus 服务器及其组件。
-
此时,你可以安装 v2 监控并重新配置所有设置。
在安装 v2 之前,你必须确保 v1 监控的所有内容都已删除。幸运的是,Rancher 的一位工程师 Bastian Hofmann 创建了一个脚本,处理收集所有警报和仪表板并将它们迁移到 v2 的过程(github.com/bashofmann/rancher-monitoring-v1-to-v2)。需要注意的是,这不是一个官方脚本,在开始此过程之前,你应该先备份 etcd。
要部署 v1 监控,请登录到 Rancher UI,进入 工具 | 监控,并点击 启用 按钮。此时,Rancher 服务器将负责部署 Prometheus 服务器和节点出口程序。然后,所有监控配置将通过 Rancher UI 完成。例如,如果你想查看某个 Pod 的 CPU 使用情况,你可以在 Rancher UI 中浏览到该 Pod,并且 Grafana 图表将直接显示在 UI 中。有关可以收集的工作负载指标的更多详细信息,请参见官方 Rancher 文档:rancher.com/docs/rancher/v2.0-v2.4/en/cluster-admin/tools/cluster-monitoring/cluster-metrics/。
需要注意的是,集群监控仅供具有集群完全查看权限的用户使用。如果你希望将监控范围限定为单个项目,你需要启用项目监控,方法是进入项目并从 工具 菜单中选择 监控。这将导致 Rancher 服务器在项目内部署一个具有自己命名空间的 Prometheus 服务器。这个 Prometheus 服务器的作用范围仅限于该项目及其命名空间:

图 10.1 – Rancher 监控 v1
要部署监控 v2,你有几种不同的选择。第一种是进入 集群资源管理器 | 集群工具,然后点击 监控 旁边的 安装:

图 10.2 – Rancher 监控 v2
这将通过应用目录部署 Rancher 的监控 chart。这个 chart 只是对上游镜像的重新打包,没有代码更改。唯一的真正区别是使用 Rancher 的 Docker Hub 仓库来替代上游的镜像。同时,默认命名空间被设置为 cattle-monitoring-system,但如果需要,可以自定义该命名空间。由于监控 v2 是一个 Helm chart,你可以选择直接通过 helm 命令进行部署,这对于使用像 Rancher 的 Fleet 这样的工具来管理大规模集群非常有用。以下是一个示例命令:
helm repo add rancher-charts https://git.rancher.io/charts
helm repo update
helm upgrade –install -n cattle-monitoring-system rancher-monitoring rancher-charts/rancher-monitoring
你可以通过 helm 命令在 github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch10/rancher-monitoring-v2 找到安装 Rancher 监控的完整命令和 values.yaml 文件。
第二种选择是部署上游的 Helm chart,通常称为kube-prometheus-stack:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm upgrade –install -n monitoring monitoring prometheus-community/kube-prometheus-stack
需要注意的是,在写作时,这个 chart 仍处于测试阶段,并且可能会有所更改,并且 Rancher 并不支持上游 chart 中的所有版本。因此,建议查看 Rancher 的支持矩阵,网址为 www.suse.com/suse-rancher/support-matrix/all-supported-versions/。你还可以通过查看 chart 值来找到完整的配置选项,网址为 https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack#configuration。
此时,你应该已经在集群上安装了 Prometheus 和 Grafana。需要注意的是,Prometheus 启动所需的所有 pod 和服务大约需要 5–10 分钟才能完全启动。还需要注意的是,在写本文时,Rancher 并不完全支持 Prometheus 联邦——也就是你可以拥有一个中央 Prometheus 服务器,扫描你其他集群中的所有 Prometheus 服务器。如果你想了解更多信息,我推荐查看官方文档prometheus.io/docs/prometheus/latest/federation/,但需要注意的是,这仍然是一个新特性,仍在发展中。
将自定义应用程序指标添加到 Prometheus
当然,现在你已经安装并使 Prometheus 和 Grafana 正常工作,接下来问题就变成了,我们如何将应用程序的指标导入 Prometheus? 在这一部分,我们将介绍两种主要的做法。
最简单的方法是使用社区创建的图表,比如 Bitnami 的 MariaDB 图表,并包含metrics.enabled=true选项。这个选项启用一个 sidecar,它将mysqld-exporter添加到部署中,许多社区创建的图表使用这种将导出器作为 sidecar 容器与主容器一起使用的模型。需要注意的是,你应该阅读 Helm 图表的文档,以查看启用指标时是否需要采取任何额外步骤,因为有些应用程序需要为导出器设置服务帐户或权限才能正常工作。
除了启用指标外,你还会看到一个带有prometheus.io/scrape=true和prometheus.io/port=9000键值对的注释部分。端口可能不同,但通常设置为这个范围内的某个值是标准做法。这两个注释非常重要,因为它们是 Prometheus 在发现所有应该被抓取的不同 pod 时使用的。
假设你正在使用一个定制的应用程序,并希望从这个应用程序捕获指标。以下是安装了指标导出器的不同应用程序的几个示例。
使用 GoLang 时,Prometheus 提供了一个官方库,位于github.com/prometheus/client_golang/。这个库处理了生成度量输出时的大部分繁重工作。你可以在github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch10/examples/go找到示例 Go 应用程序和部署文件。你需要运行 kubectl apply -f deploy.yaml 命令来部署示例应用程序。如果你使用 curl 命令访问 pod 的 IP 地址并附带路径 /metrics,你会发现应用程序返回了不同度量的列表(例如,curl 10.42.7.23:8080/metrics)。一旦应用程序启动并运行,你可以发送一个 GET 请求到 /ping,它将返回 pong 字样。然后,应用程序内部会增加一个名为 ping_request_count 的计数器,这是一个正在暴露的自定义度量。
这是度量输出的一个示例:
# HELP ping_request_count No of request handled by Ping handler
# TYPE ping_request_count counter
ping_request_count 4
….
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 2
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0
完整的输出可以在github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch10/examples/go/output.txt找到。
使用 Apache2 时,我们需要使用 sidecar 选项将导出器添加到部署中。在我们的示例中,我们将使用一个流行的第三方导出器,叫做apache_exporter。你可以在github.com/Lusitaniae/apache_exporter了解更多关于这个项目的信息。这个项目的基本思路是充当 Apache mod_status 模块与 Prometheus 之间的翻译层。我们需要在示例部署中的主 Web 服务器容器上安装/启用 mod_status 模块。然后,我们需要将 server-status 页面暴露给托管导出器的 sidecar 容器。你可以在github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch10/examples/apache找到示例和部署文件。你需要运行 kubectl apply -f deploy.yaml 命令来部署示例应用程序。
这是度量输出的一个示例:
# HELP apache_accesses_total Current total apache accesses (*)
# TYPE apache_accesses_total counter
apache_accesses_total 6
# HELP apache_connections Apache connection statuses
# TYPE apache_connections gauge
apache_connections{state="closing"} 0
apache_connections{state="keepalive"} 0
apache_connections{state="total"} 1
apache_connections{state="writing"} 0
# HELP apache_cpu_time_ms_total Apache CPU time
# TYPE apache_cpu_time_ms_total counter
apache_cpu_time_ms_total{type="system"} 30
apache_cpu_time_ms_total{type="user"} 0
# HELP apache_cpuload The current percentage CPU used by each worker and in total by all workers combined (*)
# TYPE apache_cpuload gauge
apache_cpuload
完整的输出可以在github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch10/examples/apache/output.txt找到。
使用 NGINX 时,我们将采用与使用 Apache 时类似的过程,但这次我们将使用 NGINX 提供的导出器。你可以在github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch10/examples/nginx找到示例和部署文件。你只需运行 kubectl apply -f deploy.yaml 命令。
这是度量输出的一个示例:
# HELP nginx_connections_accepted Accepted client connections
# TYPE nginx_connections_accepted counter
nginx_connections_accepted 1
# HELP nginx_connections_active Active client connections
# TYPE nginx_connections_active gauge
nginx_connections_active 1
# HELP nginx_connections_handled Handled client connections
# TYPE nginx_connections_handled counter
nginx_connections_handled 1
# HELP nginx_connections_reading Connections where NGINX is reading the request header
# TYPE nginx_connections_reading gauge
nginx_connections_reading 0
# HELP nginx_connections_waiting Idle client connections
# TYPE nginx_connections_waiting gauge
nginx_connections_waiting 0
# HELP nginx_connections_writing Connections where NGINX is writing the response back to the client
# TYPE nginx_connections_writing gauge
nginx_connections_writing 1
# HELP nginx_http_requests_total Total http requests
# TYPE nginx_http_requests_total counter
nginx_http_requests_total 9
# HELP nginx_up Status of the last metric scrape
# TYPE nginx_up gauge
nginx_up 1
完整的输出可以在 github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch10/examples/nginx/output.txt. 找到。
到目前为止,我们可以监控不同应用程序的指标,但我们还缺少根据这些指标创建告警的能力。在接下来的部分,我们将深入探讨 Prometheus 的告警规则。
在 Prometheus 中创建告警规则
Prometheus 运算符通过 CRD PrometheusRule 定义告警规则。从本质上讲,告警只是一个带有触发器的表达式。让我们看一下下面的示例告警。这个告警来自 Longhorn,我们将在下一章中讨论。正如你所看到的,表达式由 expr 字段表示,它包含一个公式,将卷的实际大小除以容量并转换为百分比。然后,如果该值大于 90%,表达式为 true,就会触发告警。description 部分主要是面向最终用户的。值得注意的是,你可以在描述中使用变量,因为告警将包含与摘要相同的解释,通常用于主题行。例如,当发送邮件告警时,邮件的主题将设置为告警的主题,而邮件正文则是描述内容。
这是一个告警的示例:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
labels:
prometheus: longhorn
role: alert-rules
name: prometheus-longhorn-rules
namespace: monitoring
spec:
groups:
- name: longhorn.rules
rules:
- alert: LonghornVolumeActualSpaceUsedWarning
annotations:
description: The actual space used by Longhorn volume {{$labels.volume}} on {{$labels.node}} is at {{$value}}% capacity for
more than 5 minutes.
summary: The actual used space of Longhorn volume is over 90% of the capacity.
expr: (longhorn_volume_actual_size_bytes / longhorn_volume_capacity_bytes) * 100 > 90
for: 5m
labels:
issue: The actual used space of Longhorn volume {{$labels.volume}} on {{$labels.node}} is high.
severity: warning
需要注意的是,默认情况下 Prometheus 只会查找与服务器位于同一命名空间中的规则。这可能会导致问题,因为应用团队可能需要访问该命名空间以添加/编辑/删除他们的告警。为了解决这个问题,你需要在 values.yaml 文件中添加以下设置。
这是 values.yaml 的示例:
prometheusSpec:
podMonitorNamespaceSelector: {}
serviceMonitorNamespaceSelector: {}
ruleNamespaceSelector: {}
到目前为止,我们已经启动了 Prometheus,并且它正在收集集群的所有数据,但内置的 UI 并没有提供一个有用的方式来可视化这些数据。在接下来的部分,我们将深入探讨 Grafana,以便为我们的数据创建仪表板。
创建 Grafana 仪表板
到目前为止,我们应该已经启动了 Prometheus 和 Grafana,服务器正在收集有关集群的所有数据。然而,除非你使用 Rancher 的监控图表,否则大部分数据是不可见的,这些图表包括一些预构建的仪表板,主要与集群及其核心服务(如 etcd、kube-apiserver 和 CoreDNS)相关。当然,问题也随之而来:我如何创建自己的仪表板?
最直接的答案是找到预制仪表板,并让其他人为您完成所有繁重的工作。Grafana Labs 仪表板存储库是最全面的资源,位于grafana.com/grafana/dashboards/。它们的搜索工具允许您按应用程序、数据源等筛选结果。但最酷的部分是它们的仪表板 ID 系统。官方网站上的所有仪表板都有一个 ID 号码,例如 NGINX Ingress 控制器仪表板的 ID 是9614,要使用此仪表板,您只需复制该 ID 号码并转到 Grafana UI。浏览到admin/prom-operator,然后粘贴 ID 号码,完成。
当然,Rancher 监控提供了一些示例仪表板,捆绑在 rancher-monitoring 图表中。您可以在github.com/rancher/system-charts/tree/dev-v2.6/charts/rancher-monitoring/v0.3.1/charts/grafana/dashboards找到原始 JSON 文件。此外,您还可以通过 ID 添加基础仪表板,其中一些最重要的仪表板包括组件和 etcd 仪表板,可用于深入了解集群性能问题。
但假设您要部署的应用程序是社区创建的,而官方网站上没有仪表板。大多数存储库将仪表板定义为 JSON 文件。您可以将其复制并粘贴到 UI 中,并使用此文件导入它。但是假设您要部署的是自定义内部应用程序,并希望从零开始。我建议观看grafana.com/go/webinar/guide-to-dashboard-design/的《Getting started with Grafana dashboard design》视频。我使用了很多社区创建的仪表板,并根据需要进行调整。您可以在顶部单击共享按钮,并将仪表板导出为 JSON 文件,然后复制和粘贴您喜欢的部分。在更改仪表板时,通过单击右上角的保存图标保存您的工作非常重要。如果关闭页面而未单击该图标,则所有更改将丢失。
到目前为止,在本章中,我们一直在讨论监控和警报。在接下来的部分中,我们将转变关注点,专注于等式的另一半,即日志记录。
什么是 Banzai Cloud Logging 操作员?
随着 Rancher 2.5 迁移到监控 v2,Rancher 也因大致相同的原因迁移到了日志记录 v2。在 v1 中,日志记录是基于 Fluentd 构建的,并使用插件将日志发送到不同的日志服务,如 Elasticsearch、Splunk、Kafka 和 Syslog。在 v1 中,Rancher 服务器完全控制日志部署,这使得定制和调整日志解决方案变得复杂。大多数设置是硬编码在 Rancher 中的。这就是 Banzai 的 Logging 操作符进入的地方。
Logging 操作符使用与 Prometheus 操作符相同的 CRD 模型,在该模型中,你通过 CRD 定义 Fluent Bit 的部署及其设置。操作符会接管推送你的更改。因为一切都是 CRD,包括设置,你可以让应用程序团队定义他们的日志设置。例如,一个团队可能希望将他们的日志发送到云日志服务,如 Splunk,而另一个团队可能有法律要求必须保持所有日志在 RKE 或其他托管在本地的 K8s 集群上运行,你可以通过 Logging 操作符做到这一点。其理念是,你有一组应用程序 Pod 流,它们将日志发送到一个输出,输出可以是任何数量的日志服务器/服务。
什么是 Fluent Bit 和 Fluentd?
在谈到日志记录时,两个问题是,什么是 Fluent Bit? 和 什么是 Fluentd?
在深入 Fluent Bit 之前,我们先来谈谈先出现的 Fluentd。Fluentd 是一个开源项目,由 Treasure Data 团队于 2011 年使用 Ruby 编写。它的核心思想是所有日志都应该是 JSON 对象。使用 Fluentd 和 Docker 时,收集容器日志的基本过程是使用默认的 Docker 日志驱动程序,将容器日志写入磁盘上的文件。然后,Fluentd 将读取整个日志文件并将事件转发到服务器,此时 Fluentd 将打开一个尾文件处理程序,保持日志文件打开并读取所有写入的内容。
需要注意的是,Fluentd 有一个处理日志轮换的流程,因此建议在 Docker Engine 中启用日志轮换。以下是一个配置示例。你可以在docs.docker.com/config/containers/logging/configure/找到完整的文档。Docker 会等到logs文件达到 100 MB 后再进行轮换,如以下示例所示。这么做是为了防止丢失生成大量事件的应用程序的事件。Fluentd 需要在轮换前读取所有事件并将其转发到日志服务器。
在以下示例中,我们正在定义json-file日志驱动程序的日志选项,该驱动程序是 Docker Engine 默认内置的。
这里是/etc/docker/daemon.json的一个示例:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
在这种情况下,我们允许所有容器的日志文件在达到最大 100 MB 大小时才开始轮换文件。然后,我们将只保留最后三个轮换的文件。
现在,让我们来了解 Fluent Bit。Fluentd 本质上是一个设计简洁、快速且轻量级的工具,而 Fluent Bit 是建立在 Fluentd 基础之上的,旨在提供 Banzai 日志所需的额外过滤和路由功能。你可以在 docs.fluentbit.io/manual/about/fluentd-and-fluent-bit 了解更多关于两者之间差异的信息。
部署 Rancher 日志
使用 Rancher 日志时,建议通过 Rancher UI 中的“应用程序和市场”进行部署,方法是进入 cattle-logging-system 命名空间。需要注意的是,安装完成后,你将在已安装部分看到两个应用程序,分别是 rancher-logging 和 rancher-logging-crd。另外,根据集群的大小,所有 pod 启动并进入准备就绪状态可能需要 5 到 15 分钟。一旦 Rancher 日志安装完成,我们将能够配置过滤和日志流,接下来的两节内容将详细介绍这一点。
由于日志 v2 是一个 Helm 图表,你可以选择通过 helm 命令直接部署它,这在使用 Rancher 的 Fleet 等工具管理大规模集群时非常有用。以下是一个示例命令:
helm repo add rancher-charts https://git.rancher.io/charts
helm repo update
helm upgrade –install -n cattle-logging-system rancher-logging rancher-charts/rancher-logging
你可以在 github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch10/rancher-logging-v2 找到通过 helm 命令安装 Rancher 监控所需的完整命令和 values.yaml 文件。
过滤应用程序日志
大多数人配置 Rancher 日志时的第一个设置是 ClusterFlows 和 ClusterOutput。ClusterFlow 设计为作用于所有命名空间,并可以为整个集群设置默认日志策略。要配置此设置,您需要进入 Rancher UI,浏览到日志,然后选择ClusterFlows。接下来,您需要填写表单。完成后,您将定义 ClusterOutput,在这里定义日志的目标位置,如 ElasticSearch、Splunk、Syslog 等。关于不同日志提供程序的示例,请参见 Rancher 官方文档:rancher.com/docs/rancher/v2.5/en/logging/custom-resource-config/outputs/。
一旦你配置了 ClusterFlows 和 ClusterOutput,就可以认为设置完成了。但是,如果你想为一个应用程序定制日志记录,你需要重复这个过程。不过这一次,你将配置 Flows 和 Outputs,主要的区别是设置选择器规则,或者文档中所称的 include 或 exclude 标签,你可以使用这些标签来限制 Flows 和 Outputs 的范围。以下是一个 NGINX 应用程序在默认命名空间中的 YAML 示例。需要注意的是,Flows 是命名空间范围的:
这是一个 Flow 示例:
apiVersion: logging.banzaicloud.io/v1beta1
kind: Flow
metadata:
name: flow-sample
namespace: default
spec:
filters:
- parser:
remove_key_name_field: true
parse:
type: nginx
- tag_normaliser:
format: ${namespace_name}.${pod_name}.${container_name}
localOutputRefs:
- s3-output
match:
- select:
labels:
app: nginx
到目前为止,我们应该已经在集群中配置了日志记录并将日志转发到日志服务器。在下一节中,我们将介绍一些更高级的设置,许多用户在他们的环境中使用这种设置来将日志记录到多个服务器。
向多个日志服务器写入日志
因为你可以定义任意数量的输出,比如如果你想将日志发送到多个日志服务器,例如发送到本地的 Syslog 和 Splunk 服务器。需要特别注意的是,这会导致日志重复,因此不推荐这样做,因为 failed to send 的日志可能会导致内存压力问题,尤其是在日志服务器配置错误或日志服务器长时间离线的情况下。
你可以在 Rancher 官方文档中找到 YAML 示例,文档地址为 rancher.com/docs/rancher/v2.6/en/logging/custom-resource-config/outputs/。需要注意的是,截至 Rancher 2.6.3,Rancher UI 中的日志设置仍然存在一些问题(例如,github.com/rancher/rancher/issues/36516,其中 ClusterOutput 字段未能在 UI 中更新),因此建议尽可能使用 YAML 文件。
总结
在本章中,我们了解了 Rancher 的监控和日志记录。这包括了 Prometheus、Grafana、Fluentd 和 Fluent Bit 的工作原理。我们学习了如何安装 Rancher 监控和日志记录。最后,我们详细介绍了一些示例仪表板,并通过讨论定制应用程序日志记录及其流结束了本章内容。
下一章将介绍 Rancher 的存储项目,以为 Kubernetes 集群提供存储。
第十一章:第十一章:使用 Longhorn 将存储引入 Kubernetes
之前的章节已经涵盖了监控和日志记录。本章将介绍 Rancher 的分布式块存储解决方案 Longhorn,包括其优缺点,以及如何使用 Longhorn 架构存储解决方案。接着,我们将深入探讨 RKE 和 RKE2 集群的标准设计。然后,我们将介绍安装 Longhorn 的不同方法以及如何进行升级。最后,我们将总结一些维护任务和常见问题的故障排除步骤。
本章将涵盖以下主要内容:
-
什么是持久存储,我们为什么需要它在 Kubernetes 中?
-
什么是 Longhorn?
-
Longhorn 是如何工作的?
-
Longhorn 的优缺点
-
架构 Longhorn 解决方案的规则
-
安装 Longhorn
-
Longhorn 升级是如何工作的?
-
保持 Longhorn 100% 的关键维护任务
-
常见 Longhorn 问题的故障排除
什么是持久存储,我们为什么需要它在 Kubernetes 中?
在创建 Kubernetes 集群后,用户将开始向集群中部署应用程序,但总是会出现这样的问题,我的数据在哪里? 或者 我该把数据存储在哪里? 哲学性的回答是,你不应该存储任何数据;所有容器都应该是无状态的,但这毕竟是现实世界。一些应用程序需要存储持久化的数据;也就是说,当 Pod 被终止时,数据将可供替换 Pod 使用。例如,假设你部署了一个 MySQL 数据库作为 Pod。你很可能希望该数据是持久化的,这样当节点重启或者部署更新时,你就不会丢失所有的数据。
自从容器被创建以来,持久数据的问题就一直存在。Docker 解决这个问题的第一个方法是通过添加来自早期操作系统级虚拟化软件的绑定挂载,例如 2000 年时 FreeBSD 中的 jail。绑定挂载作为目录树的替代视图,挂载会在根目录树中创建一个存储设备的视图作为目录对象。而绑定挂载则是将现有的目录树复制到另一个位置。对于容器来说,你有一个根文件系统,该文件系统是为了与主机文件系统隔离而创建的。例如,在容器内,你会看到文件系统看起来像正常的 Linux 服务器,包含二进制文件和库文件等,但你会注意到根文件系统并不是与主机文件的根文件系统相同的目录。
假设你在主机上有一个目录 /mnt/data(例如),你想将其作为 /data 传递到容器中。在 Linux 上,Docker 提供绑定挂载作为内核特性。因此,你可以运行类似 docker run -v /mnt/data:/data Image:Tag 的命令,关键标志是 -v,这在容器创建过程中告诉 Docker 进行等效的系统调用,就像 mount –bind /mnt/data /var/lib/docker/…/data 命令一样。重要的是要注意,在旧版本的 3.x 内核中,绑定挂载是无法区分的。这意味着诸如 df(磁盘空间使用情况查询)之类的工具将看到与原始设备细节相同的内容。
例如,如果你从 nfs.example.com 服务器上绑定挂载 /mnt/nfs 到 /data,当你在容器内运行 df 命令时,你会看到 nfs.example.com 服务器上的 NFS 文件系统被挂载到 /data。
在本节中,我们已经了解了持久化存储是什么,以及在 Kubernetes 中为什么需要它。接下来,我们将深入了解 Longhorn,它的工作原理以及如何满足我们的存储需求。
什么是 Longhorn?
Longhorn 是一个为 Kubernetes 集群提供分布式块存储系统的解决方案。Longhorn 就像 Rancher 一样,因为它是免费的、开源的,甚至作为一个孵化项目开发,支持 ReadWriteOnce 和 ReadWriteMany 卷。Longhorn 为其卷提供了备份解决方案,并提供跨集群卷的灾难恢复功能。
但是,关于 Longhorn 最显著的一点是它简化了分布式块存储。Longhorn 通过构建为微服务应用程序来实现这一点。传统的大型存储子系统是大块存储,带有少量控制器,应用数据从共享磁盘池进行配置。Longhorn 扭转了这一过程的思路,每个卷都有专用的存储控制器,形成其微服务。这就是所谓的 Longhorn Engine,将在 Longhorn 如何工作? 部分介绍。
另一个人们喜欢 Longhorn 的主要原因是它将存储集成到 Kubernetes 中。大多数其他外部存储提供商,如 NFS 或云提供的存储,都需要存储团队进行大量的配置和管理工作。而这也引出了 Longhorn 的另一个重要卖点:你不再依赖云提供商的块存储。例如,在 亚马逊 Web 服务 (AWS) 中,你可能会使用亚马逊的 弹性块存储 (EBS) 或 弹性文件系统 (EFS) 存储提供程序,它们通过 AWS 的 API 连接,创建 EBS 卷并将其附加到节点上,这很棒,直到你遇到 小型计算机系统接口 (SCSI) 设备限制,或者在将 EBS 卷迁移到不同区域时遇到限制。当然,既然是亚马逊,你还需要提供备份解决方案。Longhorn 解决了所有这些限制,因为它不在乎你是否在数据中心的物理服务器上运行 Longhorn,或者在云端的虚拟机上运行。
当然,由于 Longhorn 是一种分布式块存储解决方案,它的一个重要优势是可以在磁盘和主机之间复制数据。对于传统的大型存储子系统,你需要定义 独立磁盘冗余阵列 (RAID) 组,规划 逻辑单元号 (LUN) 并提前做出这些需要锁定的决策。相比之下,Longhorn 可以在每个磁盘上呈现为单一文件系统,不需要 RAID,并且通过基于节点的复制来保护你的数据。同时,你可以随时更改存储布局,轻松地调整数据,而应用程序无需知道或关心这些变化。
这将引出 Longhorn 的 高可用性 (HA) 有何不同。对于传统的大型存储子系统,你通常会有一对存储控制器,它们以主动/待机或非活动状态运行,直到发生控制器故障,或者你需要将一个控制器下线进行维护。此时,如果另一个控制器出现问题或无法承载负载,你就没有冗余了。一个简单的软件升级就可能导致整个环境崩溃。作为一名曾经做过企业存储管理员的人,存储升级总是需要提前安排,获得所有批准,然后在周六晚上人少的时段才能进行升级。Longhorn 让这些日子成为过去,因为升级只需推出一个新的容器镜像,并缓慢地滚动到环境中。我们将在 Longhorn 升级是如何工作的? 章节中详细介绍这一点。
现在我们已经了解了 Longhorn 的概念,在接下来的部分,我们将深入探讨 Longhorn 的工作原理,包括其中的各个组件。
Longhorn 是如何工作的?
Longhorn 可以分为两个层次:控制平面和数据平面,其中 Longhorn 管理器位于控制平面,存储引擎位于数据平面。
控制面是由一组称为 Longhorn Manager 的 pod 构建的。这是一个 DaemonSet,运行在集群中的所有节点上。它的主要任务是处理集群中卷的创建和管理。这些 pod 还会接收来自 Longhorn UI 和卷插件的 API 调用。当然,Longhorn 遵循与 Rancher 相同的操作员模型,使用由 Longhorn Manager 部署的 CustomResourceDefinitions (CRDs)。然后,Longhorn Manager 连接到 Kubernetes API 服务器并监视卷任务,例如创建新卷。需要记住的是,Kubernetes 并不会主动访问 Longhorn 的 API,而是等待 Longhorn 控制器检测 CRD 对象规范的变化。
除了监视 Kubernetes API,Longhorn 管理器还负责处理 Longhorn pod 上的调度工作。这包括运行在集群中每个节点上的 Longhorn Engine pod。Engine pod 将控制面与数据面连接在一起,负责创建副本并将存储呈现给 pod。
如果我们跟踪一个卷的创建过程,首先,用户在集群中创建了一个 node01。当 engine 获取到一个卷后,它会创建一组副本,每个副本会被分配到一个节点及该节点上的一个磁盘/文件系统。默认情况下有三个副本,但你可以选择将副本数量设置为任何你喜欢的数字,甚至只设置一个副本。需要特别注意的是,副本只是文件系统上的一个磁盘映像文件。然而,当 Longhorn engine 创建该文件时,它使用了名为 fallocate 的 Linux 系统命令,该命令创建了一个与卷大小相同的空文件,但并没有实际分配文件系统中的空间。这意味着你可以在一个节点上创建一个 1TB 的副本,但除了少量元数据外,节点上并没有使用任何空间,直到你开始写入数据为止;也就是说,如果你向一个 1TB 的卷写入了 500 GB 数据,你只会在节点上使用 500 GB 的空间。
你必须理解,一旦分配了一个块,它将在整个卷的生命周期内被消耗。如果你将一个 500 GB 的文件写入卷并删除它,你仍然会在文件系统中消耗 500 GB。现在有一个开放的功能请求,旨在支持所谓的修剪或打孔功能,以便能够回收已删除的空间。在写这本书时,这个功能还没有分配到任何发布版本,并且仍在规划阶段。你可以在 github.com/longhorn/longhorn/issues/836 找到这个功能请求。还需要注意的是,Longhorn 需要存储磁盘上的 Fallocate,而目前只有第四扩展文件系统(Ext4)和扩展文件系统(XFS)提供此功能。其他文件系统,如Zettabyte 文件系统(ZFS),正在添加 Fallocate 支持,但仍缺少文件映射(filemap),这是 Longhorn 用来检测文件中孔洞的功能。如果你想了解更多有关此问题的信息,请参阅OpenZFS GitHub 问题 github.com/openzfs/zfs/pull/10408,了解该功能的当前状态,因为它仍在积极开发中。
当然,副本的创建默认发生在总共三个节点上,每个副本都是数据的完整副本。需要注意的是,每个副本在 Longhorn 引擎内都有自己的 Linux 进程。此外,没有副本是唯一的,因为所有的读取和写入都可以发送到任何副本。这是因为 Longhorn 使用一种过程,所有的写入都会发送到所有副本,且在所有副本确认写入之前,客户端(在此情况下是 pod)不会收到写入确认。如果某个副本在写入过程中超时,Longhorn 会假设卷丢失并丢弃该副本,这将触发重建。这是因为 Longhorn 没有事务日志,所以 Longhorn 引擎无法知道哪个副本缺失了写入数据。
有一种特殊情况,如果 pod 位于与其副本相同的节点上,那么该副本将优先处理所有读取请求。这是通过设置数据本地化来实现的,它会尽量保持本地副本与 pod 位于同一节点上。需要注意的是,Longhorn 调度默认采用最佳努力原则,这意味着如果可能,它会将副本放置在同一节点上,但这并非总是可行。这通常是因为 pod 被分配到一个没有足够空间、磁盘标签不兼容,或者该节点没有被分配为存储节点的节点上。
现在我们为卷创建了所有副本,需要将其暴露给 pod 以供使用。这是通过 Longhorn 引擎创建一个open-iscsi包来作为 iSCSI 发起器来完成的,然后将 iSCSI 设备作为设备附加到/dev/longhorn/pvc-###下。然后,Longhorn 使用 CSI 插件获取块设备,格式化并将其挂载到节点上,kubelet 将在 pod 内执行绑定挂载。
除块存储外,自 Longhorn v1.1.0(于 2021 年 1 月发布)起,Longhorn 还支持导出ReadWriteMany(RWX)卷。这些卷建立在块存储之上,Longhorn 创建一个共享管理器 pod,挂载块设备并作为 pod 内运行的 NFS 服务器。然后,此 pod 将卷导出为 NFS 到集群。最后,Longhorn 使用其 CSI 插件将 NFS 共享挂载并绑定挂载到 pod 中。
现在我们了解了 Longhorn 的工作原理,我们将在下一节中涵盖其优缺点,包括与其他存储提供商的比较,我们将在本章后面讨论。
Longhorn 的优缺点
现在让我们看看现在的优缺点。
优点如下:
-
内置备份解决方案。Longhorn 支持为运行备份和外部备份到 S3 或 NFS 目标的快照。
-
支持跨集群灾难恢复卷,可以在一个集群上备份并在另一个集群上恢复。
-
随着 v1.1.1 的发布,Longhorn 现在支持使用系统快照从现有数据重建副本。您可以在
github.com/longhorn/longhorn/issues/1304了解更多信息。 -
可扩展性。由于 Longhorn 是一个微服务应用程序,可以从三个节点扩展到数万个节点。
-
使用相同存储类支持 RWO 和 RWX 卷;唯一需要更改的是为卷设置访问模式。
-
基础设施/云提供商无关,这意味着您可以在物理服务器、VMware、AWS 和 GCP 上部署 Longhorn,所有这些都使用标准化的存储平台,使您可以根据需要移动卷。
-
默认情况下是薄配置的。与大多数云提供商(如 AWS EBS 和 GCP 卷)不同,即使从未向其写入数据块,您也需要支付卷的大小。使用 Longhorn,您可以超额配置您的云存储并节省一些成本。
-
Longhorn 调度具有区域/区域感知性,这意味着您可以定义容错域,例如跨 AWS 可用性区域(例如,us-west-2a、us-west-2b 和 us-west-2c),使用 Longhorn 复制卷,以便在 AWS 中丢失整个区域而不会丢失任何数据。您可以在 https://longhorn.io/docs/1.2.3/volumes-and-nodes/scheduling/#scheduling-policy 了解更多信息。
缺点如下:
-
在创建或删除新 pod 时,卷卡在附加和分离状态的情况是 Longhorn 中非常常见的问题,因为它无法确定卷是否已经正确挂载到节点上。
-
Longhorn 对大容量卷的官方支持,因为 Longhorn 具有硬编码的重建限制,最长为 24 小时。虽然没有硬编码的大小限制,但一般来说,卷的最大大小是 1~2 TB。
-
网络使用量较大,因为所有数据都需要写入所有副本。假设你从一个 pod 向 Longhorn 卷写入 50 MBps 数据,那么你可能会在节点之间产生高达 150 MBps 的网络流量。因此,建议节点之间使用 10 GB 的网络连接。
-
磁盘延迟可能会导致卷/副本超时,因为 Longhorn 使用远程确认。卷的写入速度仅能达到最慢副本的速度。因此,强烈建议使用 SSD 或一级存储来部署 Longhorn。
-
如果你在 VMware 上使用共享存储子系统或 vSAN 部署 Longhorn,嵌套存储虚拟化可能会导致浪费。Longhorn 会在你的数据存储上存储三份数据,因此 1 TB 的数据将变为 3 TB。
注意
如果你的存储子系统支持数据去重,建议启用该功能,以便 Longhorn 存储节点能够绕过此问题。
此时,你应该已经了解了 Longhorn 的优缺点。我们将在下一节中利用这些优缺点来设计我们的 Longhorn 解决方案。
架构 Longhorn 解决方案的规则
在本节中,我们将介绍一些标准设计及其优缺点。需要注意的是,每个环境都是独特的,可能需要根据最佳性能和体验进行调优。同时还需要注意,所有的 CPU、内存和存储大小都是推荐的起始点,具体大小可能需要根据你的工作负载和部署过程进行调整。
在设计解决方案之前,你应该能够回答以下问题:
-
这个集群及其应用程序将需要什么级别的可用性?
-
这个集群是否会跨多个数据中心部署在 MetroCluster 环境中?
-
集群中节点之间的延迟是多少?
-
如果你需要存储,你只需要 RWO 吗,还是需要 RWX?
-
你是否有应用程序提供自己的应用数据复制/冗余功能?
注意
Longhorn 发布了一份官方的性能可扩展性报告,地址是
longhorn.io/blog/performance-scalability-report-aug-2020/,该报告稍显过时,但仍然提供了不同规模集群的硬数据。
接下来,我们将介绍三种标准设计(最小、中等和大型设计),你可以将它们作为设计 Kubernetes 集群与 Longhorn 配合使用的起点。
最小
在此设计中,我们将部署最小配置的 Longhorn,但仍保持完全的高可用性(HA)。该集群基于我们在第四章中介绍的 RKE 小型设计,创建 RKE 和 RKE2 集群,可在github.com/PacktPublishing/Rancher-Deep-Dive/blob/main/ch04/standard_designs/rke/01_small_cluster/README.md中找到。
优点如下:
-
完全高可用,因此你可以失去集群中的任何一个节点,但仍然能保证所有存储的完全可用性。
-
管理简单,因为所有节点都将是存储节点,并同等支持 Longhorn。
缺点如下:
-
所有三个节点都需要部署 Longhorn 文件系统。
-
只有N+1的可用性/冗余(你只有一个备用副本),因此在进行操作系统打补丁等维护任务时,如果节点发生故障,无法保证服务不中断。因为你已经将备用节点下线进行维护。
-
任何节点维护,如操作系统打补丁和重启,都需要重建所有卷,因为每个节点将存储每个卷的第三个副本。
硬件要求如下:
-
服务器:三台物理/虚拟服务器。
-
CPU:每台服务器六核(其中两个核心将专用于 Longhorn)。
-
内存:每台服务器 4~8 GB。
-
磁盘:推荐使用 SSD。
-
网络:节点之间推荐使用 10 GB 网络。
对于 RKE 集群,请参见以下设计。基本思路是这是一个三节点集群,所有节点共享所有角色。任何小于此设计的配置都无法实现高可用性。

图 11.1 – RKE 三节点集群,所有节点,所有角色
更多关于 RKE 的信息请见: https://github.com/PacktPublishing/Rancher-Deep-Dive/tree/ch10/ch11/standard_designs/rke/01_small_clusterFor RKE2 集群。
在 RKE2 中,基本思路与 RKE 集群相同,但主节点也分配了Worker角色。

图 11.2 – RKE2 三节点集群,所有节点为主节点或工作节点
更多关于 RKE2 的信息请见: github.com/PacktPublishing/Rancher-Deep-Dive/tree/ch10/ch11/standard_designs/rke2/01_small_cluster。
共享节点的中型配置
在此设计中,我们将部署一个 RKE 中型集群,主服务迁移到其专用节点上,基于此,我们需要使用节点选择器强制 Longhorn 只使用工作节点。这样做是因为我们不希望 Longhorn 影响核心 Kubernetes 服务。我们通过参考longhorn.io/docs/1.2.3/advanced-resources/deploy/node-selector/文档,配置每个 Longhorn 组件的nodeSelector规则来实现这一点。
优点如下:
-
完整的高可用性,因此即使丧失集群中的任何节点,所有存储仍然具有完全可用性。
-
Longhorn 的额外负载不能影响 RKE 的管理服务。
缺点如下:
-
工作节点和管理节点之间的文件系统配置不同,因为只有工作节点需要使用 Longhorn 存储文件系统。
-
只有N+1的可用性,因此在维护任务期间,不能承受节点故障而不丧失服务。
-
任何节点维护任务,如操作系统补丁安装和重启,都需要重建所有卷,因为每个节点将在工作节点层存储每个卷的第三个副本。
硬件要求如下:
-
节点角色:Etcd/控制平面
-
服务器:三台物理/虚拟服务器
-
CPU:每台服务器八个核心
-
内存:8~16 GB
注意
工作节点的规模应基于工作负载和要求。你需要为每个工作节点增加两个核心来支持 Longhorn。
对于 RKE 集群,请参见以下设计。基本思路是该集群有三个管理节点,并将ETCD和Control-plane角色分配给它们。对于工作节点,它们也将作为 Longhorn 存储节点。此设计的主要目的是将管理服务分配到其节点上,以防止应用程序影响集群的管理服务。

图 11.3 – RKE 三管理节点,工作节点为 Longhorn 存储节点
更多关于 RKE 的信息,请见此链接:github.com/PacktPublishing/Rancher-Deep-Dive/tree/ch10/standard_designs/rke/02_medium_cluster。
对于 RKE2 集群,请参见以下设计。基本思路与 RKE 集群相同,只不过主节点有自己的负载均衡器来支持后端服务。

图 11.4 – RKE 三主节点,工作节点为 Longhorn 存储节点
关于 RKE2 的更多信息,请访问:github.com/PacktPublishing/Rancher-Deep-Dive/tree/ch10/ch11/standard_designs/rke2/02_medium_cluster。
大型集群与专用节点
在此设计中,我们扩展了中型集群的设计,但将 Longhorn 独立出来,分配给专用节点集。我们还将 Longhorn 节点的数量从三个增加到五个,以允许 Longhorn 卷的N+2配置。您可以在维护时停用任何 Longhorn 节点,并且依然可以再丢失一个节点,而不会影响 Longhorn 的服务或冗余。我们将使用中型设计中的节点选择规则,但会添加更多的节点污点和容忍设置。有关这些步骤的详细信息,请参见longhorn.io/docs/1.2.3/advanced-resources/deploy/taint-toleration/#setting-up-taints-and-tolerations。
优点如下:
-
完全高可用,因此可以丢失集群中的任何两个 Longhorn 节点,依然能保证所有存储的完全可用性。
-
通过使用专用的 Longhorn 节点,用户应用程序无法影响 Longhorn。
缺点如下:
-
通过使用专用节点,您可以将它们配置为相对静态。例如,您可能会为工作平面使用自动伸缩组,但对于 Longhorn 平面,建议这些节点在添加/删除时,确保所有卷在继续到下一个节点之前已重新构建。
-
需要额外的步骤来强制工作负载(例如日志收集器和监视器)处理 Longhorn 节点上的节点污点,以适应 Longhorn。
对于 RKE 集群,您可以在下图之后找到一个示例集群配置文件的链接:

图 11.5 – RKE 五个 ETCD 节点,四个控制平面节点,Longhorn 拥有专用存储节点
(github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch11/standard_designs/rke/03_large_cluster.)
注意
该配置旨在通用,应该根据您的需求和环境进行定制。
对于 RKE2 集群,您可以在下一个图形后找到一组示例命令。需要注意的是,由于 RKE2 主服务器既是控制平面节点又是 ETCD 节点,因此该设计与 RKE 设计不同。

图 11.6 – RKE2 三主节点与 Longhorn 专用存储节点
(github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch11/standard_designs/rke2/03_large_cluster.)
此时,你应该完成所有创建 Longhorn 设计所需的操作。我们将在下一节中使用此设计来部署 Longhorn。
安装 Longhorn
在 Kubernetes 集群上部署 Longhorn 有三种方式。首先,我们来介绍一些基本要求,每个节点都需要满足以下标准:
-
一个兼容的运行时,即 Docker v1.13 或更高版本。
-
集群必须至少运行 Kubernetes v1.18 或更高版本。
要通过 Rancher 目录安装 Longhorn,请按照以下步骤操作:
-
导航到你将安装 Longhorn 的集群。
-
导航到
Longhorn,然后点击安装按钮。 -
此时,你可以使用默认设置。如果你希望自定义值,请参见
longhorn.io/docs/1.2.3/deploy/install/install-with-rancher/。
Longhorn 完全启动需要 5 到 10 分钟。
- 你可以通过点击侧边菜单中的Longhorn来访问 Longhorn UI。
要通过 kubectl 安装 Longhorn,请按照以下步骤操作:
-
运行命令
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.2.3/deploy/longhorn.yaml。 -
使用命令
kubectl get pods -n longhorn-system -w等待 pod 启动。 -
然后,你可以按照
longhorn.io/docs/1.2.3/deploy/accessing-the-ui上的步骤,通过创建负载均衡器或从本地工作站进行端口转发来访问 Longhorn UI。
要通过 Helm 安装 Longhorn,请按照以下步骤操作:
-
使用命令
helm repo add longhorn https://charts.longhorn.io添加 Helm 仓库。 -
使用命令
helm repo update更新你的 Helm 图表。 -
使用命令
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace安装 Longhorn。
你可以在github.com/longhorn/longhorn/blob/master/chart/values.yaml找到完整的 Helm 值列表。
- 一旦 Longhorn 启动,你可以按照
longhorn.io/docs/1.2.3/deploy/accessing-the-ui/longhorn-ingress上的步骤,配置带有身份验证的 ingress。
此时,你应该已经拥有一个启动的 Longhorn 集群,准备被应用程序使用。在下一节中,我们将讨论如何升级 Longhorn。
Longhorn 的升级是如何工作的?
升级 Longhorn 类似于升级大多数 Kubernetes 应用程序,但请理解 Longhorn 被故意设计为不可降级。一旦开始升级,必须完成升级。因此,您应查看github.com/longhorn/longhorn/releases上的发布说明;在升级生产/关键业务环境之前,您还应在低环境中测试所有升级。最后,必须使用您用于安装 Longhorn 的任何方法(如 Rancher 目录、kubectl 或 Helm)来执行任何未来的升级。
一旦开始升级过程,Longhorn 将升级管理器 Pod,但不会升级引擎。引擎的升级由 Longhorn 管理器处理,默认可以手动执行,具体步骤可以参见longhorn.io/docs/1.2.3/deploy/upgrade/upgrade-engine,或者使用位于longhorn.io/docs/1.2.3/deploy/upgrade/auto-upgrade-engine的自动化过程。对于引擎升级,可以选择离线或在线升级。离线升级时,卷将从工作负载中分离并重新附加,通常是更快的选择,但需要一些停机时间。另一种选择是在线升级,这会将副本数量加倍,例如,在升级过程中,三个副本将变成六个副本。因此,您需要在存储节点上具备额外的容量,并且该升级需要重建所有卷,这将需要额外的空间和 I/O。
保持 Longhorn 100% 健康状态的关键维护任务
在运行 Longhorn 时,您必须完成一些额外的维护任务,以保持 Longhorn 运行在健康状态。
首先,在升级/修补存储节点时,您必须按照以下步骤执行作为升级的一部分。在开始任何操作系统级别的工作之前,以下步骤应逐个节点执行:
-
使用以下命令将节点设置为禁用状态:
kubectl cordon NodeName -
使用以下命令排空节点:
kubectl drain NodeName –ignore-daemonsets --pod-selector='app!=csi-attacher,app!=csi-provisioner'
此命令将导致 Longhorn 在集群中的新节点上重建副本。
注意
排空过程将等待副本重建。
默认情况下,如果节点上某个卷有最后一个健康副本,Longhorn 会阻止该节点完成排空操作,以保护最后一个副本并防止工作负载中断。您可以在设置中覆盖此行为,或在排空之前将副本驱逐到其他节点。
-
此时,您可以执行任何节点维护操作,包括修补和重启。
-
一旦完成所有节点维护,您需要使用以下命令取消节点的“禁用”状态:
kubectl uncordon NodeName
其次,Longhorn 依赖于底层文件系统来检测损坏。您可以使用以下步骤检测损坏的副本,详见 longhorn.io/docs/1.2.3/advanced-resources/data-recovery/corrupted-replica/。该过程会断开卷连接,计算每个副本的校验和并进行比较。如果发现单个损坏的副本,您应该通过 Longhorn UI 删除它并重建。如果发现多个损坏的副本,您可以从备份恢复该卷,或者使用以下步骤挂载副本并手动查看数据:
-
首先,您需要通过 SSH 登录到节点并使用命令
sudo su -以 root 用户身份执行,然后运行以下列出的命令:cd into /var/lib/longhorn/replicas/pvc-... -
运行以下命令:
docker run -it -v /dev:/host/dev -v /proc:/host/proc -v <The replica data path on host>:/volume --privileged longhornio/longhorn-engine:v1.2.3 launch-simple-longhorn <volume name> <volume size> & mkdir /mnt/recovery mount -o ro /dev/longhorn/pvc-... /mnt/recovery
到此为止,我们已经涵盖了保持 Longhorn 健康运行的步骤,但正如我们所知,任何应用程序都不可能完美,因此在接下来的章节中,我们将讨论一些常见的问题以及如何解决它们。
排查常见的 Longhorn 问题
在 Longhorn 中,最常见的两个问题是运行节点直到磁盘满了,以及恢复卡住的卷。
首先,节点会变满,因为 Longhorn 使用的是共享文件系统,如 root 或 /var,而其他应用或 Pod 会填满这些空间。鉴于此,建议将 Longhorn 部署在独立的文件系统上。要从这个故障中恢复,您需要使用以下步骤:
-
禁用磁盘满时的调度。
-
扩展当前的文件系统,使已使用的容量低于 80%。
-
如果无法增加存储空间,您需要删除副本直到节点不再处于错误状态。
第二个常见问题是卷卡住,因为如果 Longhorn 卷在 mount 命令时超时,Pod 无法启动。要从这个问题中恢复,您将使用以下步骤:
-
首先,将工作负载扩展为零并删除 Pod。
-
如果卷进入分离状态,请重试扩展操作。
-
如果卷仍然卡在附加状态,您可以尝试通过 Longhorn UI 使用维护标志手动将卷附加到主机:
-
如果卷成功附加,尝试再次分离它并重新扩展。
-
如果卷无法附加,那么节点上存在故障;通常,重启节点可以解决该问题,或者您可以强制将 Pod 重新调度到另一个节点上。
-
此时,您应该能够解决 Longhorn 的大多数问题,并且拥有保护数据所需的工具。
总结
在本章中,我们学习了 Longhorn,包括它是如何工作的。接着,我们在 架构设计 部分讨论了要求和限制。我们还介绍了一些常见的按规模划分的集群设计。然后,我们深入探讨了安装、升级和自定义 Longhorn 的不同方式。
在下一章中,我们将讨论如何使用 OPA Gatekeeper 将安全性和合规性引入到您的 Rancher 集群中。
第十二章:第十二章:使用 OPA Gatekeeper 进行安全性和合规性管理
在本章中,我们将介绍如何使用OPA Gatekeeper将安全性和合规性引入我们的 Kubernetes 集群,并探讨它为何在大规模管理集群时至关重要。(OPA 代表开放策略代理。)由于有这么多不同的团队在你的集群上部署应用程序,在你的环境中强制执行标准(例如,阻止公共镜像仓库、阻止不符合规则的部署,例如设置 Pod 的 CPU 和内存限制)变得极其困难。我们还将讨论 Rancher 的互联网安全中心(CIS)扫描工具,该工具用于扫描 Kubernetes 集群中的已知漏洞,以及 Rancher 的硬化指南,帮助应用对 RKE 和 RKE2 集群的更改,以执行 CIS 基准中定义的额外安全标准。我们还将探讨如何持续维护集群,以及像 NeuVector 这样的企业级解决方案。
在本章中,我们将涵盖以下主要主题:
-
为什么我需要关注 Kubernetes 中的安全问题?
-
我如何在 Kubernetes 中强制执行标准和安全策略?
-
什么是 OPA Gatekeeper?
-
我如何从市场中安装 OPA Gatekeeper?
-
最佳实践和标准政策。
-
我如何扫描我的集群以检查安全问题?
-
我如何锁定我的集群?
-
部署 Rancher CIS 扫描。
-
用于保护集群的额外安全工具。
为什么我需要关注 Kubernetes 中的安全问题?
我经常被问到一个问题:为什么我需要关注安全问题?容器化不是已经解决了这个问题吗? 简短的回答是,不是的。容器化并不能解决所有 IT 安全问题,但如果回顾我们传统上如何处理服务器安全问题,它确实改变了游戏规则。
首先,我们将在所有服务器上部署杀毒软件(AV),以检测和阻止恶意软件、蠕虫和特洛伊木马等攻击我们的服务器并窃取数据。现在,容器化给这一过程带来了很大挑战,因为我们在另一个服务器中虚拟化了应用程序,而大多数杀毒软件无法理解或支持在 Docker 环境中运行。官方的 Docker 文档中也讨论了这一点,指出推荐将 Docker 进程及其目录排除在扫描之外,网址是docs.docker.com/engine/security/antivirus/。此外,大多数流行的杀毒软件供应商,例如赛门铁克(Symantec Endpoint Protection),会完全阻止 Docker 的运行。值得注意的是,像 Aqua 这样的安全软件支持在主机级别运行 AV 扫描。我们将在本章后续部分深入讨论这一话题。
其次,我们会在服务器之间创建防火墙规则,只提供最基本的访问权限。例如,你可能只允许从有限数量的跳板服务器访问安全外壳(SSH)协议,这样,如果某人攻破了一个面向公众的 Web 服务器并获得了远程访问权限,他们将无法通过 SSH 进入其他服务器,比如数据库服务器。Kubernetes 改变了这一点,因为大多数容器网络接口(CNI)提供商,如 Canal、Calico 和 Weave,默认是开放的,这意味着集群中的任何 Pod 都可以直接连接到集群中的任何其他 Pod。这意味着,如果某人攻破了运行 Web 服务器的 Pod,他们现在可以直接攻击同一集群中的其他 Pod。需要注意的是,Kubernetes 有网络策略(NetworkPolicies),允许你将类似防火墙的规则引入集群。然而,并不是所有的 CNI 提供商都支持网络策略。你可以在 https://rancher.com/docs/rancher/v2.6/en/faq/networking/cni-providers/#cni-features-by-provider 查找 Rancher 支持的不同 CNI 提供商的列表。此表格包括支持 NetworkPolicies 资源类型的提供商,因为并非所有 CNI 提供商都支持此功能。
第三是对环境中允许安装的软件的控制。大多数企业环境不允许应用团队直接访问生产服务器。他们要求应用团队记录如何安装他们的应用程序,以及这一过程如何经过外部团队(如安全和合规团队)的审查。这主要是为了防止应用团队对服务器进行可能引发安全问题的更改。例如,如果没有控制措施,应用团队可能会直接禁用防病毒软件,而不是通过将他们的进程列入白名单并解决应用程序的安全问题来解决。除此之外,某些团队可能会使用过时的软件,例如有已知漏洞的 Java,且大多数安全团队会对此进行拦截。容器化改变了这种情况,因为应用团队自己构建镜像;也就是说,如果需要,他们可以安装自己想要的软件和库,包括过时和有漏洞的软件。
第四,我们来谈谈补丁管理。大多数企业服务器环境都有定期的补丁计划,例如每月应用操作系统补丁。因此,已知的漏洞会定期从你的环境中移除。而容器化默认情况下使得像每月打补丁这样的任务不再是必要的。容器的一个核心概念是,你的镜像应该是静态的,只有在重新部署时才能更改。
例如,一个应用团队部署了一个运行当前补丁的基于 Ubuntu 的镜像的 pod。然后他们将其保持不变长达六个月。现在,基于 Ubuntu 的镜像已过时,需要更新。当然,你可以连接到 pod 并运行 apt upgrade 来应用补丁,但只要该 pod 被重新调度,你的更改就会被清除。你需要依赖应用团队通过按计划重新部署应用来保持镜像的更新,我们都知道这将如何进行,因为如果一切正常,他们不愿意更改任何内容。重要的是要注意,通过将图像扫描添加到您的流水线中,可以解决这个问题,我们将在本章后面讨论这个主题。
最后但并非最不重要的是,在传统企业环境中限制访问。生产服务器的访问受到限制,应用团队不被允许访问。但是,对于 Kubernetes 来说,组织通常会开始其 Kubernetes 之旅,以便为开发人员提供对生产集群的访问权限,以提高速度和效率。
在接下来的部分,我们将深入探讨如何开始解决这些问题,主要集中在 OPA Gatekeeper 和 Rancher CIS 扫描上。
我如何在 Kubernetes 中强制执行标准和安全策略?
当然,既然我们现在知道所有这些安全问题和限制,我们需要问的是我们能做什么?
首先是 AV 软件问题,问题是,为什么我们首先需要 AV 软件?我们需要它是因为我们需要检测恶意软件在我们的环境中运行的情况。例如,黑客通过找到一个漏洞来获取远程代码执行(RCE,也称为ACE),然后黑客将开始安装远程访问工具(RAT)以开始横向移动到其他服务器,试图获取更多访问权限。容器化通过缩小攻击面来解决这个问题。例如,如果服务器没有 shell,那么很难在其上获取远程 shell。这就是为什么大多数容器镜像只包含绝对必要的软件包和库。Kubernetes 也通过以非特权帐户运行容器来解决此问题。例如,我们可能将 Apache 作为非 root 用户运行。因此,即使有人获取了对 pod 的访问权限,他们也没有权限安装额外的软件和库。这也包括如果他们找到一种方法来打破容器的限制,他们仍然将作为几乎没有权限的帐户运行。
第二个问题是防火墙规则,正如前一节所讨论的。Kubernetes 默认情况下在 Pod 之间是开放的,但因为你需要显式地将端口和服务暴露给外部,所以你默认能够获得对外部世界的安全保护。例如,如果我们在集群中以 Pod 的形式运行一个 Apache 服务器,默认情况下我们并没有直接将该 Pod 暴露给外部世界。但仍然,通过 Ingress 控制器,你可以启用安全设置,比如ModSecurity,这是一种Web 应用防火墙(WAF),可以保护你免受跨站脚本、SQL 注入和会话攻击等威胁。你可以通过访问github.com/SpiderLabs/ModSecurity了解更多关于 ModSecurity 的信息,关于如何在 Ingress NGINX 中启用 ModSecurity 的详细信息,请访问 https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity。
第三个问题是已部署软件的控制。我们将管理端点或服务器上的软件,特别是在过渡环境中。容器化将这个过程从端点转移到了构建管道中。这是通过将诸如 Clair 之类的软件集成到构建脚本中实现的。Clair 的想法是,在你运行docker build命令后,将镜像传递给 Clair,Clair 会下载并扫描镜像中的已知漏洞。Clair 会生成一份报告,列出漏洞、CVE(公共漏洞和暴露)编号以及严重性等级。根据你的持续集成/持续部署(CI/CD)软件,你可以选择在镜像有过多漏洞时阻止构建。
第四个问题是应用程序的补丁更新。不幸的是,这个问题没有简单的解决办法;正确的方法是定期更新你的容器,作为持续开发过程的一部分。但正如我们所知,定期修补镜像可能会滞后。因此,一些人做法是设置定期构建任务。例如,你可以创建一个定期任务,每月重新构建你的应用镜像,使用当前的代码版本并自动在你的环境中部署和测试。通过这种方式,你每个月都会对容器进行补丁更新,就像我们对服务器进行更新一样。
最后,我们面临访问问题。最简单的方式是给每个人访问权限,让他们可以部署到生产环境并随意修改。但这种做法会带来不必要的问题和安全隐患,因为当保护措施挡在路上时,人们通常更容易选择关掉保护,而不是解决被阻挡的原因。为了防止这种行为,建议强制通过 CI/CD 管道进行所有更改,并使用如 GitHub/GitLab 拉取请求(PR)审批来跟踪和控制,且不允许非集群管理员访问生产环境。Rancher 可以通过其 Fleet 产品解决此问题,您可以通过访问 fleet.rancher.io 了解更多信息。此外,从 Rancher v2.6 起,Fleet 已集成到 Rancher 本身中。
到目前为止,我们应该了解如何将传统 IT 安全需求转化为 Kubernetes 对应项的基本概念。在下一部分,我们将深入了解 OPA Gatekeeper,以强制执行我们集群的安全标准。
什么是 OPA Gatekeeper?
OPA 是一个为云环境构建的开源策略引擎。此外,OPA 是一个 云原生计算基金会(CNCF)毕业项目,就像我们讨论过的其他工具一样。OPA 使用声明式语言在环境中强制执行策略。基本思路是,所有内容都应调用 OPA 并询问,嘿,我能做 XYZ 吗? 这时,OPA 会根据其策略评估请求,决定是否批准或拒绝该请求。值得注意的是,OPA 设计上是通用的,旨在与 Kubernetes 和其他系统(如 Terraform、Docker 和 SSH)集成。
当然,问题接踵而至,那么 Gatekeeper 是什么呢?简而言之,OPA Gatekeeper 是一个 Kubernetes 控制器,它允许你将 OPA 策略定义为 Kubernetes 对象。这主要包括约束和约束模板。通过这种方式,用户可以将其策略定义为 YAML,并像其他 Kubernetes 部署一样将其应用于集群。接下来,我们将重点讨论 OPA Gatekeeper。
现在我们知道了什么是 OPA Gatekeeper,接下来的问题是它是如何工作的? 最好的回答方式是通过一个请求流程来进行讲解。请参阅下图来理解这一过程。

图 12.1 – OPA Gatekeeper 请求流程图
接下来,我们详细看看这些步骤:
-
第 1 步:开发者创建请求,在这个例子中,请求是针对 testing 命名空间中的新部署 webserver。
-
第 2 步:开发者将请求以 YAML 或 JSON 文件的形式提交给 Kube-Apiserver。
-
第 3 步:Kube-Apiserver 接收请求并开始处理,验证用户是否具有有效凭据、请求语法是否有效,并且用户是否有权限访问请求的资源。在这一点上,通常 Kube-Apiserver 会响应开发者,接受或拒绝请求。但由于我们已将 OPA Gatekeeper 部署到该集群,请求将走不同的路径。
-
ValidatingAdmissionWebhook用于将某些请求路由到定义好的 webhook。需要注意的是,并不是所有请求都会被转发到 webhook。例如,kubectl get pods请求是只读请求,因此不会被转发到 webhook。但创建 pod 的请求会被转发,因为它是对环境的更改。在这种情况下,OPA Gatekeeper 已将ValidatingAdmissionWebhook添加到 Kube-Apiserver 中。 -
billing-code,您可以用它向您的应用程序团队进行费用分摊。 -
billing-code标签,创建将被阻止,拒绝将被转发到下一步骤。但假设请求已被批准,OPA Gatekeeper 将响应一个 HTTP 成功代码200。 -
默认情况下,
fail close by default。这意味着如果请求的响应不是200,则假定为拒绝,请求将被拒绝。即使是有效的部署,如果 OPA Gatekeeper pod 宕机或不可用,也会被拒绝。这意味着您的集群被阻塞,无法启动任何 pod。所以,Rancher 的 OPA Gatekeeper 部署将默认策略切换为failurePolicy: Ignore,表示如果 webhook 请求收到错误(如超时),Admission Controller 将无法打开,这意味着控制器会假设请求在 OPA Gatekeeper 离线时已被批准。您应该与您的安全团队一起审查此设置,并确认在故障期间是否更重要的是可用性,而不是可能允许不符合标准的部署。
最后,此时,最终用户将收到其请求的响应。由于前面列出的步骤,用户在部署过程中可能会看到额外的延迟。但通常这非常快速,以至于大多数环境中它成为了背景噪音。还需要注意的是,这个过程适用于终端用户以及来自集群内部其他控制器的内部请求,例如 kube-scheduler。
在这一点上,您应该理解请求是如何通过 kube-apiserver 流向 Admission Controller,然后转发到 OPA Gatekeeper,最后通过 kube-apiserver 返回给开发者的。在下一节中,我们将深入探讨在集群中安装 OPA Gatekeeper 的过程。
如何从市场安装 OPA Gatekeeper
使用 Rancher 有两种主要方式可以部署 OPA Gatekeeper,分别是通过 Rancher 中的应用市场和通过 Rancher Helm Chart。当然,你也可以在没有 Rancher 的情况下直接部署上游的 OPA Gatekeeper。通常建议通过应用市场来部署 OPA Gatekeeper,主要是为了方便。安装 OPA Gatekeeper 的步骤在本节中针对每个主要的 Rancher 版本进行列出。
在讨论安装步骤之前,先列出一些安装所需的先决条件:
-
你应该具有全局角色管理员或集群所有者权限。
-
OPA Gatekeeper 需要 Kubernetes v1.16 或更高版本。
-
你应该查看官方支持矩阵,网址是
www.suse.com/suse-rancher/support-matrix/all-supported-versions/,以确认你部署的是完全兼容且验证过的系统解决方案。
在 Rancher v2.4 中安装 OPA Gatekeeper,请按照以下步骤操作:
-
在 Rancher UI 中,进入Cluster仪表盘。
-
进入Tools菜单,并从下拉菜单中选择OPA Gatekeeper;参见图 12.2:

图 12.2 – Rancher v2.4 从工具菜单安装 OPA Gatekeeper
- 通常建议使用默认设置,相关设置可以在
rancher.com/docs/rancher/v2.0-v2.4/en/cluster-admin/tools/opa-gatekeeper/找到。

图 12.3 – Rancher v2.4 OPA Gatekeeper 安装向导
接下来,我们来看看 Rancher v2.5 的安装步骤:
-
在 Rancher UI 中,进入Cluster Explorer。
-
点击Apps & Marketplace选项。
-
选择OPA Gatekeeper图表。

图 12.4 – Rancher v2.5 从应用市场安装 OPA Gatekeeper
通常建议使用默认设置,相关设置可以在rancher.com/docs/rancher/v2.5/en/opa-gatekeper/找到。
现在,我们将讨论 Rancher v2.6 的安装:
-
在 Rancher UI 中,进入Cluster Management。
-
在Clusters页面,进入你希望启用 OPA Gatekeeper 的集群,并点击Explore。
-
在左侧导航栏中,点击Apps & Marketplace。

图 12.5 – Rancher v2.6 从应用市场安装 OPA Gatekeeper
- 点击Charts,然后点击OPA Gatekeeper,再点击Install。

图 12.6 – OPA Gatekeeper 安装页面
- 通常建议使用默认设置,可以在
rancher.com/docs/rancher/v2.6/en/opa-gatekeper/找到。

图 12.7 – Rancher v2.6 OPA Gatekeeper 安装选项
此时,我们应该已经在环境中安装了 OPA Gatekeeper,但没有约束,它并不会发挥太大作用。在接下来的部分,我们将介绍一些你可以用来入门的标准约束模板。
最佳实践和标准策略
现在我们已经安装了 OPA Gatekeeper,是时候开始创建模板并将其应用到你的集群中了。在本节中,我们将讨论一些最受欢迎的模板。需要注意的是,这些模板大多数来自上游的 OPA Gatekeeper。
我喜欢部署第一个叫做containerlimits的模板规则,它确保所有的 pod 都设置了 CPU 和内存限制。你可以在 github.com/open-policy-agent/gatekeeper-library/tree/master/library/general/containerlimits 找到该模板。这里的主要思想是,默认情况下,所有 pod 都没有 CPU 和内存限制,这意味着单个 pod 可以占用节点上所有的资源,强行将其他 pod 挤出,甚至导致节点卡死然后崩溃。这就造成了我所说的 失控应用,它会消耗一个节点,导致该节点崩溃,然后转移到另一个节点,重复这个过程,直到节点用尽。你可以在 github.com/mattmattox/Kubernetes-Master-Class/tree/main/disaster-recovery/run-away-app 找到这种应用的示例。
该链接还包括如何解决“失控应用”问题。这个模板确保它被设置为一个有效的值。它可能是 1 MB 的内存,意味着 pod 永远不会被调度,或者它可能是 100 TB,意味着 pod 永远无法被调度,因为没有足够大的节点可以启动它。这个方法还有一个好处,就是能够让你洞察集群所需的容量,因为如果你看到集群报告了 100% 的资源分配,意味着两种情况中的一种:
-
你的限制设置得太高,浪费了资源。
-
你需要开始添加节点,因为你将用完资源。
OPA Gatekeeper 可以通过阻止缺少 CPU 和内存限制的 pod/deployment 来保护我们免受失控应用的影响,这意味着失控应用根本无法进入集群。
注意
我们将在下一章讨论扩展问题。
要应用这个模板,只需运行以下命令:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/template.yaml
但是,请确保首先在非生产集群中进行测试。
另一个我喜欢使用的模板叫做requiredlabels,它是一个强制所有命名空间都有标签的模板。例如,你可能希望将所有命名空间都设置一个账单代码,以便进行展现费用和收费回退。或者,也许你希望强制命名空间中列出技术联系人,以便在部署出现问题时,方便联系应用团队。
要应用这个模板,只需运行以下命令:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/requiredlabels/template.yaml
注意
这只会应用于新创建的命名空间,因此你需要回去为现有命名空间设置标签。
有关此模板的完整详细信息,请访问github.com/open-policy-agent/gatekeeper-library/tree/master/library/general/requiredlabels。
最后,我看到许多安全团队要求使用的模板是httpsonly,它强制所有 ingress 必须具有kubernetes.io/ingress.allow-http = false注解,这意味着所有 ingress 必须在 ingress 配置中的TLS(传输层安全性)部分定义 SSL 证书。许多安全团队要求所有流量,包括后端服务,必须仅支持 HTTPS。
要应用这个模板,只需运行以下命令:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/httpsonly/template.yaml
有关此模板的完整详细信息,请访问github.com/open-policy-agent/gatekeeper-library/tree/master/library/general/httpsonly。需要注意的是,默认情况下,Rancher 集群会为 ingress 配置自签名证书,作为在未定义其他证书时的默认证书。
当然,开源社区还创建了更多模板,可以在github.com/open-policy-agent/gatekeeper-library找到。我还建议使用这些模板作为创建自定义模板的基础。
到此为止,你应该已经在集群中安装了 OPA Gatekeeper,并定义了一些规则,允许你执行标准。在接下来的部分,我们将深入探讨如何采取下一步措施,通过锁定集群来防止与安全相关的问题。
我如何扫描集群中的安全问题?
Rancher 有一个基于 kube-bench 的 Rancher CIS 扫描或 Rancher-cis-benchmark 工具,kube-bench 是 Aqua Security 提供的开源软件。kube-bench 旨在扫描 Kubernetes 集群,并检查 Kubernetes 组件(如 kube-apiserver、etcd 和 kubelet)的设置。kube-bench 使用非营利组织 CIS 的 CIS 基准报告,CIS 创建了一份最佳实践设置的标准列表,用于保护你的 Kubernetes 集群。这些报告是在 Kubernetes 进行重大更改后发布的,旨在尽可能保持厂商中立。你可以通过访问learn.cisecurity.org来了解更多关于 CIS 及其报告的信息。但是,由于该报告旨在保持厂商中立,某些设置不适用于 Rancher 及其集群。因此,Rancher 发布了一份自我评估和加固指南,解决报告中的所有项目。此评估设计为可以交给你的安全团队/审计员,当他们开始提问时使用。
但是,重要的是要注意,默认情况下,Rancher 及其集群将无法通过 CIS 报告,除非对你的环境和节点进行更改,其中一些步骤需要手动操作。在下一节中,我们将深入探讨如何使 Rancher 通过报告。
你可以在rancher.com/docs/rancher/v2.5/en/security/rancher-2.5/找到 Rancher 的报告和评估。需要注意的是,随着 Kubernetes 和 Rancher 的升级,以及发现安全问题,这些指南会不断变化,因此建议定期审查这些评估。
如何锁定我的集群?
在上一节中,我们讨论了 CIS 扫描和 Rancher 的自我评估,但当然,我可以做些什么来处理这个报告?这个问题会随之而来,Rancher 对此问题的回答就是 Rancher 所称的加固指南。这些指南涵盖了 Rancher 拥有的三种 Kubernetes 发行版:RKE、RKE2 和 k3s。我们在本节中不会深入细节,因为 Rancher 已经有了相关文档。在这里,我们将提供每种集群类型的指南链接。
对于 RKE 集群,加固指南与 Rancher 服务器和 Kubernetes 版本相关联。以下是高层次的步骤:
-
配置 Linux 内核以安全处理内存不足(OOM)和内核崩溃。
-
创建一个本地用户和组供 etcd 使用,以将数据库与其他进程隔离,从而保护数据。
-
禁用默认的服务帐户挂载到每个 pod 上。
-
启用
NetworkPolicy默认限制所有 pod 之间的流量,要求你根据需要打开规则。 -
开启秘密加密功能,默认情况下,秘密以明文存储在 etcd 中,但启用 etcd 加密后,秘密将被加密存储。
-
最后,通过启用
PodSecurityPolicy来限制 Pod 的安全设置,例如禁止 Pod 以 root 身份运行。
完整的指南可以在rancher.com/docs/rancher/v2.5/en/security/rancher-2.5/1.6-hardening-2.5/中找到。
对于 RKE2 集群,这个过程要容易得多,因为 RKE2 默认是安全的,但您仍然需要应用一些更改,具体如下:
-
设置与 RKE 相同的 Linux 内核参数。
-
创建一个 etcd 用户。
-
最后,我们只需要通过添加
profile: cis-1.5/6选项,在主服务器上启用我们想要的 CIS 配置文件。
完整的指南可以在docs.rke2.io/security/hardening_guide/中找到。
对于 k3s 集群的硬化过程与 RKE2 类似,但仍然需要一些额外的步骤:
-
设置与 RKE2 相同的 Linux 内核参数
-
跳过 etcd 步骤,因为 k3s 默认不使用 etcd
-
启用
PodSecurityPolicy来限制 Pod 的安全设置,例如禁止 Pod 以 root 身份运行
需要注意的是,k3s 不像 RKE2 那样具有 CIS 配置文件,并且默认情况下不安全,因为它被设计为轻量级且快速。
完整的指南可以在rancher.com/docs/k3s/latest/en/security/hardening_guide/中找到。
在此阶段,假设您已按照前面列出的指南操作,您应该能够开始安装 Rancher CIS 扫描并顺利通过它。
部署 Rancher CIS 扫描
安装 Rancher CIS 扫描的唯一推荐方式是通过 Rancher 中的应用和市场。但首先,让我们了解一些基本要求:
-
Rancher v2.4 或更高版本。
-
集群所有者权限到下游集群(或全局角色管理员权限)。
-
下游集群必须是 RKE、RKE2、EKS 或 GKE 集群才能完全支持。
注意
对于其他集群,可以使用通用配置文件,但可能会出现误报或其他权限问题。
以下是在 Rancher v2.4.x 和 v2.5.x 中通过集群浏览器安装rancher-cis-benchmark的方法:
-
在 Rancher UI 中,转到集群浏览器。
-
点击应用。
-
点击rancher-cis-benchmark。
-
点击安装。
通过集群管理面板在 Rancher v2.6.x 中安装 CIS 基准,操作如下:
-
在 Rancher UI 中,转到集群管理。
-
导航到您将安装 CIS 基准的集群。
-
在左侧导航栏中,点击应用和市场 | Charts。
-
点击CIS 基准。
-
点击安装。
注意
对于 Rancher 创建的集群,您应该使用默认设置。对于通用集群,请查看
github.com/rancher/cis-operator/tree/master/examples中的示例设置。
到此时,你应该已经锁定了集群,并且 Rancher 的 CIS 扫描正在你的集群中运行,以确保集群能够随着时间的推移保持安全。在接下来的部分,我们将深入探讨一些额外的工具来保护你的集群,包括一些付费解决方案。
保护集群的额外安全工具
本章我们主要讨论了 OPA Gatekeeper,因为它是最受欢迎的开源解决方案之一。当然,也有其他付费解决方案,如 NeuVector,它提供镜像和集群扫描。
注意
随着SUSE(德语:Software- und System-Entwicklung)收购 NeuVector,已宣布 NeuVector 的容器运行时安全平台将在 GitHub 上以 Apache 2.0 许可证公开发布,网址为github.com/neuvector/neuvector。
此外,Aqua 平台对于付费客户来说无处不在,因为它是一个闭源产品,需要授权。
对于 NeuVector,我建议在 YouTube 上观看大师班PCI 合规性和 Kubernetes 漏洞管理(www.youtube.com/watch?v=kSkX5MRmEkE),以了解更多关于 NeuVector 的信息,并学习如何将其集成到 Rancher 中。
我还建议观看关于 Aqua 的Kubernetes 大师班:Kubernetes 企业部署的预防性安全,视频地址为:www.youtube.com/watch?v=2Phk7yyWezU。
注意
这两部 YouTube 视频都托管在 Rancher 的 YouTube 频道上,并附有它们的幻灯片和脚本链接。
摘要
本章教会了我们容器和 Kubernetes 所解决的一些安全性和合规性问题。我们了解了什么是 OPA Gatekeeper 以及它如何工作。然后,我们深入探讨了一些最佳实践和标准模板。我们学习了 OPA Gatekeeper 如何通过部署这些模板来强制执行集群的不同规则。这一点非常重要,因为掌握这一技能是创建适合自己环境及其需求的规则所必需的。接着,我们介绍了如何锁定集群并确保通过 Rancher CIS 扫描保持锁定状态。
下一章将介绍如何为集群的 Pods 和节点提供扩展功能,包括扩展集群时的所有限制和关注点。
第十三章:第十三章:Kubernetes 中的扩展
在本章中,我们将讨论如何扩展 Kubernetes 集群。我们将介绍三种主要的扩展方式:水平 Pod 自动扩展器(HPA)、垂直 Pod 自动扩展器(VPA)和集群自动扩展器。我们将介绍每种方式的优缺点,并提供一些示例,同时深入探讨每种方法的最佳实践。
在本章中,我们将涵盖以下主要话题:
-
什么是 HPA?
-
什么是 VPA?
-
什么是 Kubernetes 集群自动扩展器?
什么是 HPA?
HPA 是控制器管理器中的一个控制器。HPA 根据 CPU 使用率(或自定义指标)自动扩展复制控制器、部署、副本集或有状态集中的 Pods。无法扩展的对象(例如 DaemonSets)不会受到水平 Pod 自动扩展的影响。在控制器管理器中,horizontal-pod-autoscaler-sync-period标志的默认值为15秒,决定了 HPA 运行的频率。每个周期中,控制器管理器会检查相关工作负载的资源利用率。控制器使用自定义指标端点和度量服务器来收集其统计数据。
本质上,HPA 监控当前和期望的指标值,如果它们与规范不匹配,HPA 会采取行动。HPA 遵循以下算法:
desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]
举个例子,假设你有一个应用程序需要保持 CPU 使用率在 50%。目前,这个部署的 CPU 请求为 1000m(毫核心),相当于节点上的一个核心。需要注意的是,HPA 使用的是 CPU 和内存请求的指标,而不是限制值。HPA 通过将请求值除以当前使用值来计算currentMetricValue对象类型,得到一个百分比。HPA 会对部署中的每个 Pod 进行此计算,然后对它们进行平均,以创建一个部署的currentMetricValue对象类型。然后,HPA 将currentMetricValue对象类型与desiredMetricValue对象类型进行比较,若两者都是 50%,则 HPA 不会进行任何改变。但如果比率过高(目标是 1.0),它会触发扩展事件,增加更多的 Pods。需要注意的是,未准备好或正在终止的 Pods 不会被计入。默认情况下,Pod 生命周期的前 30 秒内的度量值会被忽略,这一行为由horizontal-pod-autoscaler-initial-readiness-delay标志定义。此外,HPA 一次可以扩展多个 Pods,但通常这需要显著的比率差异,因此大多数情况下,工作负载仅会扩展一个 Pod。
到目前为止,我们已经了解了 HPA 的工作原理,包括 HPA 如何决定何时扩展和缩减工作负载。当然,接下来的问题是何时应该使用 HPA,何时不应该使用 HPA 来处理你的工作负载,在接下来的部分,我们将深入探讨这个话题。
什么时候应该使用 HPA?
现在我们已经了解了 HPA 是如何工作的,接下来我们来看看什么时候应该使用 HPA。一个使用 HPA 的典型应用程序是 Web 服务器。那么,为什么 Web 服务器是一个很好的例子呢?答案是,传统上,甚至在 Kubernetes 之前,Web 服务器的设计方式就是能够随时添加或删除,而不会影响到应用程序。
以下是适合使用 HPA 的应用程序特征列表:
-
无状态 – 应用程序必须能够随时添加或删除。
-
快速启动 – 通常情况下,Pod 应该在 30 秒内启动并准备好处理请求。
-
基于 HTTP 的 – 大多数会使用 HPA 的应用程序是基于 HTTP 的,因为我们希望利用 Ingress 控制器内置的负载均衡能力。
-
批处理作业 – 如果你有可以并行运行的批处理作业,你可以使用 HPA 根据负载动态扩展或缩减工作 Pod 的数量。例如,一个 Pod 从队列中获取任务,处理数据,然后发布输出。假设可以同时运行多个作业,你应该根据队列的长度设置 HPA,以扩展或缩减工作 Pod 的数量,也就是部署。
接下来,我们来学习一下什么情况下不应该使用 HPA。
什么时候不应该使用 HPA?
当然,对于所有应用程序都使用 HPA 是没有意义的,实际上使用 HPA 可能会导致应用程序出现问题,例如数据库集群,你不希望在集群中频繁添加和删除 Pod,因为这可能导致应用程序错误。需要注意的是,HPA 支持扩展 StatefulSets,但你应该小心,因为大多数需要 StatefulSets 的应用程序并不喜欢频繁的扩缩容。还有其他一些原因,可能会导致你不希望在应用程序中使用 HPA,以下是一些最常见的原因:
-
StatefulSets – 需要存储和有序扩展的应用程序,例如数据库,通常与 HPA 不兼容,因为 HPA 会根据需求随意添加和删除 Pod。
-
需要存储的 Pod – 需要PersistentVolumeClaim(PVC)的应用程序通常不建议使用 HPA,因为提供和连接存储需要一定的时间,且随着时间推移可能会导致问题。
-
需要在规模变化时重新配置的应用程序 – 这种工作负载的一个典型例子是使用外部数据库的 Java 应用程序,它使用数据库连接池。这是因为每次创建 pod 时,Java 都需要开发多个新的连接,这可能会给数据库服务器带来较大的负载,并导致连接耗尽,当数据库连接耗尽时,pod 将失败。这反过来会导致创建一个新的 pod,同时,由于数据库运行缓慢,pods 上的负载可能会增加,导致更多的扩展。问题会变得越来越严重,产生越来越多的 pods,最终导致集群停机。
-
突发工作负载 – 如果你有一个应用程序大多数时间处于非常低的利用率,然后在短时间内突然跳到高利用率,使用 HPA 是没有意义的,因为 HPA 会缩减部署,直到资源需要时再扩展。问题出现在短暂的突发事件中,因为当 HPA 反应过来并启动新的 pods 时,事件已经过去,使得 HPA 变得毫无价值。
注意
你可以设置一个极高的最小规模,但在这种情况下,需要问自己一个问题:如果 HPA 永远不会扩展或缩小,为什么还需要它?
在本节中,我们已经讨论了 HPA 的优缺点。需要注意的是,每个应用程序都是不同的,你应该与应用程序开发人员合作,决定是否添加 HPA 会有帮助。我们来看一个例子。
示例 – 简单的 Web 服务器与 CPU 利用率
要部署此示例,请运行以下命令:
kubectl apply -f https://raw.githubusercontent.com/PacktPublishing/Rancher-Deep-Dive/main/ch13/examples/simple/deploy.yaml
该命令将部署一个名为 hpa-example-simple 的命名空间,其中包含一个名为 hello-world 的测试应用程序和一个将在 CPU 利用率达到 50% 时触发的 HPA。我们可以通过 load-generator 部署来测试 HPA,默认情况下其规模为 0。
要给 hello-world 应用程序制造负载,只需运行以下命令启动负载:
kubectl -n hpa-example-simple scale deployment load-generator --replicas=1
运行以下命令将其关闭:
kubectl -n hpa-example-simple scale deployment load-generator --replicas=0
如果你运行 kubectl -n hpa-example-simple describe hpa hello-world 命令,你可以看到以下事件以及 HPA 执行的操作:

图 13.1 – HPA 事件
在本节中,我们介绍了水平扩展工作负载,也就是添加和移除 pods。在下一节中,我们将讨论垂直方向的扩展。
什么是 VPA?
如果有 HPA,那是否也有 VPA?是的,有。VPA 与 HPA 类似,但不是通过增加或减少 pod 数量来进行扩展,而是根据实际的 CPU 使用情况自动设置资源请求和限制值。VPA 的主要目标是减少管理容器资源请求和限制的维护开销,并提高集群的利用率。
即使 VPA 与 HPA 类似,但重要的是要知道 VPA 有不同的工作方式,我们将在下一节中讨论这一点。
VPA 是如何工作的?
VPA 由三个不同的组件组成:
-
VPA 准入钩子 – 每个提交到集群的 Pod 都会通过这个 Webhook 进行检查,看看它的父对象是否引用了该 Pod(例如副本集、部署等)。
-
VPA 推荐器 – 通过与 metrics-server 应用程序的连接,基于每个启用 VPA 的 Pod 的历史和当前使用数据(CPU 和内存),提供有关扩展或缩减请求和限制的建议。
-
VPA 更新器 – 每分钟运行一次 VPA 更新器,它将驱逐那些不在推荐范围内的正在运行的 Pod 版本,以便 Pod 可以重启并通过 VPA 准入 Webhook 在启动前调整 CPU 和内存设置。
这意味着,如果你运行的是像 Argo CD 这样的应用,VPA 更新器将不会检测到部署中的任何变化,两个组件也不会互相争夺调整规格。
接下来,让我们了解为什么我们需要 VPA。
为什么需要 VPA?
在深入了解 VPA 之前,我们需要先了解资源请求和限制。当 kube-scheduler 将 Pod 分配到一个节点时,它并不知道该 Pod 需要多少内存和 CPU。节点上只有 16 GB 的空闲内存,但 Pod 需要 64 GB。一旦 Pod 启动,就会内存不足,驱逐其他 Pods。一个具有正确内存大小的集群节点可能会支持该 Pod。此时,资源请求起作用,我们可以指定 kube-scheduler 在特定节点上为这个 Pod 分配多少 CPU 和内存。通过将这种智能加入调度过程中,kube-scheduler 可以做出更明智的决定,选择将 Pod 调度到哪里。我们还可以设置资源限制,当 Pod 超过其限制时,资源限制将作为硬限制进行限制或终止 Pod。
当然,设置资源请求和限制可能需要大量工作,因为你需要对应用程序进行负载测试、审查性能数据,并设置请求和限制。然后你必须不断监控这些设置,以便进行调整。这就是为什么我们看到许多环境中,所有设置都是无限制的,集群管理员只会把硬件投向问题的原因。这时,VPA 就发挥作用了,它为我们设置资源请求并随着时间推移进行微调。
例如,我们可以使用以下设置构建一个 Pod:
requests:
cpu: 50m
memory: 100Mi
limits:
cpu: 200m
memory: 250Mi
VPA 推荐器会判断你需要 120 MB 的 CPU 和 300 MB 的内存来使 Pod 正常运行。推荐的设置如下:
requests:
cpu: 120m
memory: 300Mi
limits:
cpu: 480m
memory: 750Mi
由于 VPA 会按比例扩展它们,限制也会随之增加。因此,务必将限制设置为实际的数字,而不是一些不切实际的值,比如 1TB 的内存,尤其是在节点只有 128GB 内存的情况下。作为起点,可以将你的限制设置为请求大小的两倍,例如,如果请求是 100MB,那么你的限制应该是 200MB。但不要忘记,限制本身没有多大意义,因为调度决策(也就是资源争用)总是基于请求来做的。限制只有在资源争用时才有帮助,或者用于避免无法控制的内存泄漏。
如何编写 VPA 清单
你永远不应该为同一个 Pod/ReplicaSet/Deployment/StatefulSet 定义多个 VPA——在这种情况下,行为将变得不可预测。VPA 不应该与 HPA 在同一个 Pod 上使用。
你首先需要为目标应用创建一个updateMode: off的 VPA 对象,这将使 VPA 进入干运行模式(即推荐模式)。
以下是具有最小所需设置的 VPA 示例:
apiVersion: autoscaling.k8s.io/v1beta2
kind: VerticalPodAutoscaler
metadata:
name: hello-world
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: hello-world
updatePolicy:
updateMode: "Off"
大约 5 分钟后,你将能够查询数据并开始看到一些建议:
kubectl describe vpa hello-world
如下图所示,status部分为我们的目标提供了一些建议,接下来的屏幕截图将详细说明这些部分的含义:

图 13.2 – 示例 VPA 描述
你可以在raw.githubusercontent.com/PacktPublishing/Rancher-Deep-Dive/main/ch13/vpa/describe-vpa.yaml找到完整的输出。为了分解这个输出,你将看到以下几个部分:
-
未限制的目标:当 VPA 定义中没有配置上限时,Pod 上的资源请求将不受限制。 -
目标:这是在后续执行入场 Webhook 时将配置的量。如果已经有这个配置,则不会发生变化(你的 Pod 不会进入重启/驱逐循环)。否则,Pod 将被驱逐并使用这个目标设置重新启动。 -
下限:当你的 Pod 的使用量低于这个值时,它将被驱逐并缩减规模。 -
上限:当你的 Pod 的使用量高于这个值时,它将被驱逐并扩展规模。
此时,你可以使用这些信息来创建并设置你的部署请求限制。但如果你想使用自动预测,你需要将updateMode的值改为Auto。
现在,如果你想为 VPA 设置最小和最大限制,可以在 VPA 配置中添加以下部分:
minAllowed:
cpu: "300m"
memory: "512Mi"
maxAllowed:
cpu: "1800m"
memory: "3600Mi"
到目前为止,我们主要关注的是伸缩 Pod,但图像的另一半是伸缩节点。在下一部分中,我们将深入了解节点自动伸缩。
什么是 Kubernetes 节点自动伸缩?
随着新工作负载和 Pod 的部署,所有集群工作节点的资源可能会被耗尽。这将导致 Pod 无法在现有的工作节点上调度。在某些情况下,Pod 会处于等待资源分配的挂起状态,可能会导致服务中断。当然,手动添加或移除工作节点可以解决这个问题,因为集群自动扩展器会根据挂起的 Pod 和节点使用情况指标增加或减少 Kubernetes 集群的规模。
现在我们已经知道了节点自动扩展器是什么,了解它应该何时使用或不使用至关重要,我们将在下一部分详细讨论。
什么时候应该使用 Kubernetes 节点自动扩展器?
在 Rancher/Kubernetes 环境中,设置节点自动扩展器有两个主要原因:
-
成本控制/效率 – 当将工作负载迁移到云端时,许多人常犯的一个大错误是将云端虚拟机(VM)当作本地虚拟机(VM)来对待。我的意思是,本地环境中,如果你配置了一个八核虚拟机,但实际上只使用了四个核心的资源,那么物理硬件的成本仅仅是实际使用的四个核心。但是在云端,例如,如果你在 AWS 中配置了一个八核虚拟机,不管你是否使用了 100% 的 CPU,账单的费用都是一样的。因此,我们希望将节点的使用率保持尽可能接近 100%,同时不影响应用程序。一般的经验法则是 CPU 使用率保持在 80%,内存使用率保持在 90%。这是因为这些是默认的节点压力限制。节点自动扩展能够在需要时,向集群中添加足够的节点以满足需求。这对全天变化的工作负载非常有帮助。例如,你的应用程序可能在周一至周五的 8 点到 17 点非常繁忙,但在非工作时间的利用率非常低。因此,自动扩展和在早晨启动节点、晚上关闭节点将有助于减少成本。
-
节点修补/升级 – 节点重启是自动扩展节点时的副作用之一。你必须创建一个流程来轻松地将节点添加到集群或从集群中移除,而不是在原地修补或升级节点。这意味着你需要一个一个地排空并隔离节点,然后应用操作系统补丁和升级,最后重启节点。你还需要等待节点重新上线并解除隔离。接着,重复这一过程,直到集群中的每个节点都完成。这当然需要脚本和自动化,以及检查和测试。使用自动扩展时,你只需要更新基础虚拟机镜像并验证其健康状况。然后,触发节点池的滚动更新。在此时,自动扩展将接管工作。
接下来,让我们了解何时不应该使用 Kubernetes 节点自动扩展器。
什么时候不应该使用 Kubernetes 节点自动扩展器?
节点自动扩展器有一些实际限制和最佳实践,如果不遵循,可能会导致集群不稳定:
-
Kubernetes 和操作系统匹配 – Kubernetes 是一个不断发展的平台,定期发布新功能和版本。为了确保最佳性能,请确保使用建议版本部署 Kubernetes 集群自动扩展器。为了使 Kubernetes 与您的操作系统保持同步,您必须定期升级它们。有关建议版本的列表,请访问 Rancher 的支持矩阵,网址为
www.suse.com/suse-rancher/support-matrix/all-supported-versions/。 -
您的节点必须是正确的大小 – 只有当节点池中的节点具有相同的容量时,集群自动扩展器才能正常运行。其中一个原因是集群自动扩展器的基本假设是节点组中的每个节点具有相同的 CPU 和内存容量。自动缩放决策是基于每个节点组的模板节点进行的。因此,最佳做法是确保自动缩放的节点组中的所有节点和实例都是相同类型的。这对于像 AWS 这样的公共云提供商可能不是最佳方法,因为多样化和可用性因素决定了需要多种实例类型。
-
PodDisruptionBudget标志阻止它们被排空。您可以通过设置.spec.minAvailable和.spec.maxUnavailable字段来指定中断预算。.spec.minAvailable作为绝对值或百分比值指定了驱逐后的最小可用 pod 数。同样,.spec.maxUnavailable指定了驱逐后将不可用的 pod 数,可以作为绝对数或百分比。
到目前为止,我们已经介绍了什么是节点自动缩放以及为什么要使用它。在下一节中,我们将介绍如何在 Rancher 中设置节点自动缩放。
如何在 Rancher 管理的集群中设置自动扩展
目前,Rancher 仅支持 AWS 自动扩展组。详细信息可在 Rancher 的官方文档中找到,位于 rancher.com/docs/rancher/v2.5/zh/cluster-admin/cluster-autoscaler/amazon/。需要注意的是,自动扩展 etcd 和控制平面节点可能存在风险,因为同时删除和添加多个管理节点可能会导致集群故障。此外,必须配置 etcd 的 S3 备份,因为默认情况下 etcd 备份存储在本地 etcd 节点上,这可能导致在回收 etcd 节点时丢失备份。有关配置 S3 备份的详细信息,请访问 rancher.com/docs/rancher/v2.5/zh/cluster-admin/backing-up-etcd/。
如何在托管集群中设置自动扩展
Rancher 并不专注于托管集群的自动扩展,但所有 Kubernetes 提供商都支持 Cluster Autoscaler。请访问 github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler#deployment 查看支持的提供商列表。
此时,你应该能够对你的 Rancher 管理和托管的集群进行自动扩展,从而轻松地向集群中添加和移除节点。
总结
本章介绍了扩展 Kubernetes 集群的三种主要方式:使用 HPA、VPA 和 Cluster Autoscaler。对于 HPA,我们深入探讨了它的工作原理以及在何时应使用它来通过添加和移除 Pods 来扩展工作负载。接着我们讲解了 VPA,它类似于 HPA,但用于向 Pods 添加和移除资源,最后通过讲解 Cluster Autoscalers 来总结本章内容,Cluster Autoscalers 用于向集群添加和移除节点,介绍了不同的自动扩展器以及何时使用它们。
在下一章,我们将讨论负载均衡器和 SSL 证书,这对于将我们的应用程序发布到外部世界非常重要。在那一章中,我们将介绍实现这一任务的不同技术细节。
第十四章:第十四章:负载均衡器配置与 SSL 证书
本章我们将讨论如何使用负载均衡器和 ingress 规则将托管在 Kubernetes 内的应用程序发布到外部世界的这个非常重要的任务。我们将介绍四种主要的技术:循环 DNS、被动外部负载均衡器、主动外部负载均衡器和集成负载均衡器。我们将讨论每种技术的优缺点,并通过示例深入探讨每种方法的最佳实践。最后,我们将介绍如何为集群引入 SSL 证书。
本章中,我们将涵盖以下主要内容:
-
为什么我们需要外部负载均衡器来支持 Kubernetes 集群?
-
架构解决方案的规则
-
配置 F5 的 TCP 和 HTTP 模式
-
配置 HAProxy 的 TCP 和 HTTP 模式
-
安装和配置 MetalLB
-
什么是 Kubernetes 中的 ingress?
-
如何将 SSL 证书添加到 ingress 中
为什么我们需要外部负载均衡器来支持 Kubernetes 集群?
在构建 Kubernetes 集群并部署第一个应用程序之后,下一个问题就是用户如何访问我的应用程序?在传统的企业环境中,我们会将应用程序部署在一台服务器上,然后创建 DNS 记录和防火墙规则,将应用程序暴露到外部世界。当然,我们希望我们的应用程序具有高可用性(HA),因此通常会将应用程序部署在多台服务器上,然后创建一个负载均衡器,负载均衡器位于应用程序服务器前面。我们使用负载均衡器将流量分配到多个服务器,并通过允许根据需要向负载均衡器添加或移除服务器来提高应用程序的可用性。
对于 Kubernetes 集群,我们仍然面临同样的问题。我们需要将应用程序部署到多个节点上,并提供一个统一的接入点,即为我们的应用程序提供一个虚拟 IP(VIP)地址。我们的最终用户将使用这个地址来连接我们的应用程序。当然,有几种不同的方式来解决这个问题,在接下来的部分中,我们将深入探讨这些解决方案。
架构解决方案的规则
本节将介绍将托管在 Kubernetes 集群内的应用程序暴露到外部世界的四种主要方式。
循环 DNS
最直接的负载均衡技术是 1.1.1.1、2.2.2.2 和 3.3.3.3,然后你想发布你的应用程序 hello-world.example.com。你将为 hello-world.example.com 创建三个 A 记录,每个记录对应一个节点的 IP 地址。通过这样做,当客户端初次尝试连接到你的应用程序时,客户端会向其 DNS 服务器发起 DNS 查询,DNS 服务器将返回一个 IP 地址列表。大多数客户端会直接尝试连接列表中的第一个 IP 地址,如果该服务器未响应,则会尝试下一个 IP 地址,直到用完所有 IP 地址。需要注意的是,大多数 DNS 服务器/提供商只允许最多返回六个 IP 地址。
以下是使用轮询 DNS 时,不同终端用户的请求如何进入集群的示例:

图 14.1 – 三节点示例的轮询 DNS
接下来,让我们看看这个设计所带来的优缺点列表。
优点如下:
-
简便性 – 轮询 DNS 是在集群中为应用程序负载均衡最简单的方式,因为你已经需要为应用程序创建 DNS 记录,因此将多个 IP 地址添加到该记录中并不需要太多工作。
-
无需额外的服务器/硬件 – 因为我们仅使用 DNS 基础设施作为负载均衡器,所以不需要在集群前面增加额外的服务器或硬件来平衡集群的负载。
-
成本 – 负载均衡器并非免费的。例如,AWS 中的一个简单的 弹性负载均衡 (ELB) 服务的费用大约为每月 $16。大多数 DNS 解决方案(如 AWS Route53)几乎是免费的(每月 100k 次请求收费 $0.04),甚至像 CloudFlare 这样的提供商也免费;你只需支付费用如果你想要更多的功能。
缺点如下:
-
缓存 – DNS 设计有多个缓存层,包括你和客户端之间的所有 DNS 服务器,甚至终端用户的机器也有内建的缓存。你只能控制 生存时间 (TTL),它告诉 DNS 服务器在请求新的查询之前需要缓存查询的时间。这可以通过将其设置为 1 秒来帮助,但这样你会给 DNS 服务器带来相当大的负担,因为你基本上关闭了该记录的缓存。
-
没有实际的负载均衡 – 轮询 DNS 只是每次查询 DNS 服务器时轮换 IP 列表。因此,服务器负载、响应时间或服务器正常运行时间等因素在路由流量到不同节点时并没有被考虑在内。这意味着如果某个服务器崩溃或负载过重,流量仍然会被路由到该服务器,直到客户端停止尝试使用该服务器并切换到另一台服务器。对于某些客户端来说,这可能需要最多 5 分钟的时间。
-
仅计算硬性故障 – 如果服务器出现故障,比如磁盘空间不足或与数据库连接出现问题,但服务器仍然正常运行并响应请求,DNS 对服务器的健康状况一无所知。因此,请求会以 500 错误返回给客户端。尽管列表中的下一个服务器可能完全正常,客户端仍然会继续使用该服务器。
-
当节点变更时更新 DNS – 每当集群中添加或移除一个节点时,你必须手动进入 DNS 并更新所有托管在该集群上的应用程序的 DNS 记录。当你开始考虑自动扩展集群时,这可能会成为一个重大问题。但你可以通过使用如 ExternalDNS 这样的服务来解决这个问题,使得 DNS 在集群随时间动态变化时得到更新。你可以通过访问
github.com/kubernetes-sigs/external-dns了解更多关于 ExternalDNS 的信息。 -
集群外无安全性 – 因为我们只是使用 DNS 来路由流量,所以无法执行更高级的功能,如强制使用 HTTPS、阻止 SQL 注入攻击和阻止不安全的加密算法。
注意
大多数大规模应用使用一种名为全球服务器负载均衡(GSLB)的轮询 DNS,它通过进行健康检查,并根据服务器负载、响应时间和位置来响应请求,从而将智能引入 DNS。但是,这通常是在负载均衡服务之上进行的,以提供服务器级别的冗余,而 GSLB 提供数据中心级别的冗余。
需要注意的是,由于所有列出的缺点远远超过优点,因此不建议使用轮询 DNS。
被动外部负载均衡器
有时被称为愚蠢的负载均衡器,在这种设置中,你在集群前面创建一个传输控制协议(TCP)负载均衡器。现在,这个负载均衡器不处理你的流量的任何高级功能,即基于主机名的路由、SSL 卸载、缓存和Web 应用防火墙(WAF)。这是因为,OSI 模型中高于第 4 层的任何功能都不由负载均衡器处理,而是由 Kubernetes 集群/应用程序提供。通常,在这种设计中,你会创建一个包含所有工作节点的节点池。然后,你会在负载均衡器上创建一个 VIP,并将其映射到节点池。
注意
我们将在下一节中介绍 HAProxy 的示例。
以下是一个示例,说明最终用户流量如何通过负载均衡器路由到集群中的节点:

图 14.2 – 三节点 TCP 模式下的负载均衡器示例
接下来,让我们来看一下这个设计所提供的优缺点列表。
优点如下:
-
低门槛 – 大多数本地和云环境已经有负载均衡器来支持其他非 Kubernetes 应用程序。因此,向现有负载均衡器请求额外的 VIP 地址和节点池非常容易,并且几乎不会增加项目成本。
-
port 0模式,它将所有 TCP/UDP 端口绑定到负载均衡器的节点上。这在暴露非 HTTP 应用程序时非常有用,使用节点端口。例如,你可以在所有节点上将 MySQL 服务器发布到端口31001,并通过 VIP 使用相同的端口来访问它。 -
简单的持续维护 – 一旦 VIP 地址和节点池创建完成,在添加和移除新应用程序时,无需在负载均衡器上更新证书或站点名称。
缺点如下:
- 源 IP 透明性 – 在 TCP 模式下,当负载均衡器将请求转发到服务器时,它有两个选项。第一个是保留源 IP 地址(即端用户的 IP 地址),并将其传递给服务器。服务器将处理请求和响应,因为源 IP 地址是客户端的 IP 地址。流量不会通过负载均衡器返回,而是直接发送到客户端。这对于某些应用程序可能没问题,但对于其他应用程序,如 HTTP(S)、MySQL 和 SMTP,服务器 IP 地址在请求期间发生变化时可能会出现问题。
另一个选项是所谓的 NAT 模式,它将负载均衡器转变为服务器的默认网关,这样当请求返回客户端时,负载均衡器可以抓取响应数据包并将 IP 地址恢复到原始值,然后再将其发送到客户端。当然,这也有一个缺点,就是负载均衡器需要在网络中的每个虚拟局域网(VLAN)中存在。此外,东到西流量,也就是从集群中的一个节点到另一个节点的流量(假设它们在同一子网中),不会再通过负载均衡器返回,从而导致源 IP 地址被破坏。这也意味着集群的所有网络流量都必须通过负载均衡器,包括操作系统补丁、管理软件和监控工具。
-
每个集群需要一个负载均衡器/VIP 地址 – 在 TCP 模式下,负载均衡器无法进行基于主机的路由;每个集群都需要自己的节点池和 VIP 地址。当然,这会消耗额外的 IP 地址,而且大多数基于云的负载均衡器不支持添加 IP 地址,因此你需要为每个集群创建一个负载均衡器,这会增加你的成本。
-
集群外的安全性有限 – 我们仅仅是在端用户和集群之间传递流量。我们无法执行更高级的功能,如强制 HTTPS、阻止 SQL 注入攻击和阻止不安全的加密方式。
-
仅基本健康检查 – 在此模式下,负载均衡器仅检查端口是否打开并响应,但不会检查服务器是否健康。
需要记住的是,使用被动负载均衡器有一些缺点,只有在不能使用主动外部负载均衡器时,才应该使用它。我们将在下一节中介绍主动外部负载均衡器。
外部主动负载均衡器
在这个设计中,我们在顶部构建了一个被动外部负载均衡器,但通过将其从第 4 层移到第 7 层来增强智能,而不是盲目地在客户端和服务器之间转发流量。负载均衡器充当一个虚拟服务器,接受请求并对其进行解码,包括解密 SSL 加密,这使得负载均衡器能够根据请求的主机名做出路由决策,例如,dev.example.com 和 staging.example.com 共享相同的公共 IP 地址,但被路由到两个集群。或者,您还可以强制使用额外的安全软件,如 ModSecurity,它可以阻止各种攻击。在下图中,您可以看到一个示例设置,其中最终用户的流量通过 DNS A 记录流向负载均衡器,然后最终到达节点:

图 14.3 – HTTP 模式下的负载均衡器,包含三个节点的示例
接下来,我们来看看这个设计所提供的优缺点列表。
优点如下:
-
控制 – 在 HTTP/第 7 层模式下,负载均衡器能提供更多的流量控制,因为负载均衡器充当客户端之间的中间人攻击。服务器允许负载均衡器根据需要检查和修改请求。
-
www.example.com和api.example.com可能是两个独立的应用程序,但它们可以共享相同的 IP 地址和通配符证书,即*.example.com。我们甚至可以通过使用多域名通配符 SSL 来进一步扩展,这样可以为*.example.com、*.example.net等域名提供证书。所有这些都可以节省费用并简化管理,因为我们现在只需一个证书即可管理所有应用程序。 -
80和/healthz路径,只有在入口服务健康时才返回200OK。如果服务器不健康,从负载均衡器中优雅地移除该服务器的几率要高得多。 -
X-Forwarded-For头部,它向 HTTP(S) 请求添加了一组特殊头部,告诉应用程序最终用户的实际 IP 地址,而无需负载均衡器覆盖源 IP 地址,这样可以避免路由问题。
缺点如下:
-
*.example.com,我们添加了一个新的应用程序叫做test.example.net。我们必须确保当前的 SSL 证书和规则涵盖了这个新域名;如果没有,我们需要更新它们。如果所有应用程序都能通过诸如*.example.com的通配符规则来涵盖,这通常不会成为问题。但是,如果你使用了嵌套域名,如qa1.api.example.com和dev.docs.example.com,这两个嵌套域名将不会被*.example.com的通配符涵盖,必须使用多个证书或一个包含*.api.example.com和*.docs.example.com的多域名通配符 SSL 证书。 -
80,并且完成。然而,如果我们需要保持流量通过 SSL 加密,我们需要在 ingress-nginx 控制器和负载均衡器上配置 SSL 证书。我们现在可以通过使用 ingress-nginx 控制器内置的虚拟证书并配置负载均衡器忽略无效证书来轻松处理这一问题。重要的是与安全团队一起审查,以确认是否接受此方法。 -
速度 – DNS 和第四层负载均衡速度很快,因为它们简单。大多数企业级负载均衡器可以使用专用芯片而不是软件来处理第四层,这意味着它们可以以非常高的速度运行。例如,A10 的 7655S ADC 在第四层模式下可以达到 370 Gbps 的速度,但在启用 SSL 的第七层模式下,速度降至 145 Gbps。需要注意的是,由于 CPU 的速度更快以及硬件集成的改进,这个差距正在逐步缩小。
这种方法应在更新和配置外部负载均衡器的过程是自动化的环境中使用,因为随着应用程序添加到集群中,负载均衡器需要进行更新。
集成负载均衡器
之前的解决方案与 Kubernetes 集群管理平面没有集成,这意味着集群及其应用的管理与负载均衡器及其配置的管理没有直接连接。当然,使用支持 Kubernetes 的负载均衡器解决了这一问题。例如,在 Amazon 的 EKS 中,您可以部署 AWS Load Balancer Controller,该控制器将 EKS 集群直接连接到 Amazon 的负载均衡器,并管理负载均衡器作为集群对象。例如,您可以在集群中创建一个 ingress,控制器将检测到这一更改并为您自动配置负载均衡器。需要注意的是,大多数托管的 Kubernetes 集群都提供了与自己托管的负载均衡器集成的解决方案。对于本地环境,像 F5 这样的负载均衡器已经开始提供 Kubernetes 集成解决方案,帮助弥补这个差距,包括完全替代 ingress-nginx 控制器,并让负载均衡器直接加入集群,从而能够直接访问集群内的 Pods。在下图中,您可以看到流量从最终用户流向 DNS A 记录,然后到负载均衡器,负载均衡器处理第七层会话管理,最后将流量转发到后端节点。然而,关键点在于控制器 Pod,它将更改推送回负载均衡器,以确保集群和负载均衡器保持同步。

图 14.4 – 三节点集成负载均衡器示例
接下来,让我们看看这个设计所提供的优缺点列表。
优点如下:
-
简单的持续管理 – 我们添加了一个位于集群和负载均衡器之间的控制器,从管理层面进行管理。现在两者将保持同步。用户无需手动推送负载均衡器,因为应用团队在部署和更改应用时,负载均衡器会自动更新。
-
速度 – 一些负载均衡器将 ingress-nginx 控制器替换为负载均衡器,从而直接消除了额外的开销。
缺点如下:
-
控制 – 应用团队现在可以直接对生产环境的负载均衡器进行更改,这意味着他们可以推送不安全的更改,例如禁用 SSL,而网络/负载均衡团队并未看到这些更改并阻止它们。
-
一个应用程序可能会破坏另一个 – 例如,AWS 的控制器默认允许用户为同一主机名创建两个不同的入口规则,这可能允许不良行为者通过在他们的命名空间中创建入口来从另一个应用程序中劫持流量,例如在实际应用程序上忘记更改入口的主机名并意外地将生产流量路由到应用程序的开发或 QA 实例。需要注意的是,新版本的控制器正在添加保护措施,以防止重复的入口。
如果您的环境支持,这是首选选项。需要注意的是,在大多数云环境中,这可能会增加成本,因为它们会为每个应用程序创建不同的负载均衡器。
到目前为止,我们应该对我们想要/需要的负载均衡器有了一个良好的了解。在下一节中,我们将介绍安装和配置一些最常见的负载均衡器。
配置 F5 在 TCP 和 HTTP 模式下
F5 的 BIG-IP(通常简称为 F5)负载均衡器在企业客户中非常流行。正因如此,Kubernetes 集群普遍使用 F5 作为它们的外部负载均衡器。本节将涵盖两种最常见的配置,即 TCP 和 HTTP 模式。
需要注意的是,本节不涵盖安装和配置 F5 硬件/设备,因为这超出了 Kubernetes/Rancher 管理员的范围。如果想了解更多信息,建议阅读 F5 的官方文档 www.f5.com/services/resources/deployment-guides。同时建议与您的网络/负载均衡团队合作,以最好地定制以下设置以适应您的环境。
TCP 模式
我们将首先创建服务器池,该池应包含您集群的工作节点:
-
从 F5 Web 界面进入 Local Traffic | Pools | Pool List,然后点击 Create。
-
给池命名。我通常以集群名称为池名称,后跟端口号。
-
对于 Health Monitors 选项,选择 http。
-
进入
80 -
服务:HTTP
-
完成时点击 Finish。
-
您需要重复此过程来设置端口
443。

图 14.5 – F5 节点池配置示例
我们还需要创建前端,或者 F5 称之为虚拟服务器:
-
从 F5 Web 界面进入 Local Traffic | Virtual Servers | Virtual Server List 页面,然后点击 Create。
-
给虚拟服务器命名。我通常与池名称相同。
-
对于 类型 选项,选择 Performance (Layer 4)。
-
您需要在 Destination Address/Mask 字段中输入负载均衡器分配的 VIP 地址。
-
对于带有 HTTP 类型的
80。 -
其余设置可以保持默认值,您应点击
443和 HTTPS。

图 14.6 – F5 虚拟服务器设置
此时,我们需要将前端(虚拟服务器)与后端(池)链接起来:
-
转到虚拟服务器并点击 资源 标签。
-
在 负载均衡 部分设置
80池,并点击 更新 以应用更改。 -
对端口
443重复此过程。

图 14.7 – F5 绑定池和虚拟服务器
此时,您应该能够通过 VIP 地址访问您的 Kubernetes/Rancher 集群。需要记住的是,这仍然是 TCP 模式,因此 F5 仅仅是传递流量,这意味着入口控制器需要处理 SSL 等项目。
HTTP 模式
我们将按照在 TCP 模式下为 HTTP 模式创建池所采取的相同步骤进行操作。我们需要进行的唯一更改是在虚拟服务器中:
-
对于 虚拟服务器 类型,请选择 性能 (HTTP),而不是 性能 (Layer 4),然后点击 完成。
-
对端口
443重复此过程,但这次选择服务器类型为 标准,并将 SSL 配置文件(客户端) 指向您的 SSL 证书。
此时,您应该能够像在 TCP 模式下那样访问您的集群,但不同之处在于负载均衡器为您处理 SSL,您不会遇到前一部分讨论过的源 IP 地址问题。
在下一部分,我们将介绍另一款流行的负载均衡软件 HAProxy。
配置 HAProxy 以与 Kubernetes 配合使用
本节将涵盖为内部和外部部署安装和配置 HAProxy。需要注意的是,本节列出的示例是通用的,旨在涵盖最常见的环境。但您应理解,每个环境和工作负载都是不同的,这可能需要对本节中列出的设计进行调优和修改。此外,本节中我们将使用 HAProxy 的社区版,但对于需要支持和附加付费功能的用户,他们也提供 HAProxy 企业版。您可以在 www.haproxy.com/products/community-vs-enterprise-edition/ 查阅有关差异的详细信息。
首先,我们将介绍如何在独立的服务器(非 Kubernetes 集群的一部分)上安装 HAProxy。
注意
在开始此过程之前,我们假设您已经构建好了服务器,并已应用最新的补丁,同时拥有对服务器的root/sudo权限。此外,撰写时 v2.5 是当前的最新稳定版本。您应当在官方 HAProxy 社区网站 www.haproxy.org/ 查看发布说明和版本建议。
在 Ubuntu/Debian 系统上安装 HAProxy
对于 Ubuntu 和基于 Debian 的系统,默认包仓库中捆绑的 HAProxy 版本通常落后当前发布版本一到两个小版本,更重要的是,可能会缺少重要的安全更新,直到下一个主要版本发布。由于我们正在处理一个可能公开访问的负载均衡器,并且它会成为攻击者的重要目标,我们需要确保运行的是最新版本并且已安装最新的安全补丁。因此,我们将使用个人软件包档案(PPA)仓库来进行此安装。
我们需要通过访问haproxy.debian.net/并填写表单来生成安装步骤。这将生成两组命令,第一组命令用于添加 PPA 仓库,第二组命令用于安装 HAProxy。

图 14.8 – PPA 和安装向导
到此,我们应该已经在 Ubuntu 服务器上安装了 HAProxy。在接下来的部分中,我们将介绍 Red Hat/CentOS 服务器的安装步骤。
Red Hat/CentOS
就像 Ubuntu 和基于 Debian 的系统一样,默认包仓库中捆绑的 HAProxy 版本通常落后当前发布版本一到两个小版本,更重要的是,可能会缺少重要的安全更新,直到下一个主要版本发布。因此,通常建议从源代码构建 HAProxy,相关步骤可以在这里找到:
-
通过运行以下命令来安装编译二进制文件的必要先决条件:
yum install gcc pcre-static pcre-devel -y` -
使用以下命令下载源代码:
cd /opt; wget https://www.haproxy.org/download/2.5/src/haproxy-2.5.4.tar.gz"注意
在选择版本之前,您应该查看推荐的版本。
-
运行以下命令来构建和安装 HAProxy:
make clean make -j $(nproc) TARGET=linux-glibc USE_OPENSSL=1 USE_LUA=1 USE_PCRE=1 USE_SYSTEMD=1 make install mkdir -p /etc/haproxy mkdir -p /var/lib/haproxy touch /var/lib/haproxy/stats
到此,我们应该已经安装了 HAProxy,接下来需要为其创建一个配置文件,我们将在以下部分使用列出的示例作为起始点。
TCP 模式
在本节中,我们将介绍一些示例配置文件,这些文件可以作为您环境中 TCP 负载均衡器的起始点。需要注意的是,这是最基础的配置。
完整的配置文件可以在github.com/PacktPublishing/Rancher-Deep-Dive/main/ch14/example-configs/haproxy/tcp-mode.cfg找到。但是,关键部分如下所示,它绑定到80和443端口,并将流量直接传递给后端服务器节点 01/02/03:

图 14.9 – HAProxy TCP 模式
正如我们在配置文件中看到的,我们正在为80和443端口创建前端和后端配置,且两者都在 TCP 模式下运行,因为我们希望负载均衡器将流量直接从前端端口传递到后端端口。
HTTP 模式
在这一部分中,我们将介绍一些可以作为 HTTP 负载均衡器环境起点的示例配置文件。
完整的配置文件可以在 github.com/PacktPublishing/Rancher-Deep-Dive/main/ch14/example-configs/haproxy/http-mode.cfg 找到。此配置文件的关键部分是 80 和 443 端口共用一个前端。然后,在前端中,我们定义了 SSL 证书,它存储在 /etc/haproxy/certs/star.example.com.pem 中。接下来,我们配置了一个 rke-cluster-npd 集群,生产流量会被转发到 rke-cluster-prd。此配置还包括一个运行 SSL 的后端配置示例。
这是配置文件的前端部分:

图 14.10 – HAProxy HTTP 模式前端
需要注意的是,由于我们使用的是 HTTP 模式,我们可以让多个集群和应用程序共享一个负载均衡器。如前面的示例所示,我们将 dev.example.com 指向非生产集群,将 example.com 指向生产集群。
这些是后端设置:

图 14.11 – HAProxy HTTP 模式后端
如你所见,我们创建了两个不同的后端,每个集群一个。我们还将所有后端流量发送到端口 443(SSL),因为 http-request redirect scheme https unless { ssl_fc } 前端规则会将所有 HTTP 流量重定向到 HTTPS。
到此为止,我们应该已经成功启动 HAProxy 并能够访问托管在 Kubernetes 集群上的应用程序。在下一部分中,我们将介绍 MetalLB,它无需外部负载均衡器。
安装和配置 MetalLB
当然,问题总是会出现——如果我不想使用外部负载均衡器,但仍然希望我的集群具有高可用性怎么办?这就是 MetalLB 发挥作用的地方。MetalLB 是一个为运行在裸金属上的 Kubernetes 集群提供的负载均衡器,使用标准的路由协议。该项目仍处于初期阶段,应该被视为 Beta 版本。关于这一点可以参考 项目成熟度 页面:metallb.universe.tf/concepts/maturity/。
MetalLB 可以在两种模式下进行配置。我们将介绍的第一种是第二层模式,这是最简单的配置;第二种是 边界网关协议(BGP)模式,通常由更高级的用户或环境使用;这两种模式的安装步骤相同。
运行以下两个命令来创建命名空间并安装 MetalLB 控制器:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml
注意
你可以在metallb.universe.tf/installation/找到有关为非 Rancher 集群定制此安装的更多详细信息,包括如何使用 Helm 图表。
对于 Layer 2 模式,我们需要为 MetalLB 配置一个 IP 地址范围。确保该范围与其余节点位于同一子网中非常重要。
只需创建以下 configmap:

图 14.12 – MetalLB Layer 2 ConfigMap
你可以在官方文档中找到此配置的详细信息,文档位于metallb.universe.tf/configuration/#layer-2-configuration。
对于 BGP 模式,我们需要一台支持 BGP 的路由器,MetalLB 可以连接到该路由器,并为 MetalLB 提供一个自治系统(AS)号,以及一个集群的网络 CIDR 前缀。BGP 配置也通过 configmap 进行配置;一个示例可以在下图中找到:

图 14.13 – MetalLB BGP ConfigMap
你可以在官方文档中找到此配置的完整详情,文档位于metallb.universe.tf/configuration/#bgp-configuration。
到此为止,我们应该已经启动并运行了 MetalLB。要使用 MetalLB 的 IP 地址,我们需要创建一个类型为LoadBalancer的服务记录,此时 MetalLB 会处理剩下的部分。你可以在metallb.universe.tf/usage/找到完整的细节。
什么是 Kubernetes 中的 Ingress?
Kubernetes ingress 是一个标准对象,定义了一组规则,用于将外部流量路由到 Kubernetes 集群。这包括设置 SSL 证书、名称或基于路径的路由到不同的 pod。Ingress 规则围绕 HTTP 和 HTTPS 流量设计。
以下是一个配置示例,其中配置的中心部分是rules部分,在此示例中为foo.bar.com。此规则将流量引导到server1服务。需要注意的是,rules部分简单且非常通用。此部分必须遵循 Kubernetes 标准,这使得你可以更换入口控制器;例如,RKE1/2 默认带有 nginx,但你可以选择将 nginx 替换为 Traefik。
但当然,如果你需要比rules部分允许的更多自定义入口,你可以使用注解;例如,将nginx.ingress.kubernetes.io/ssl-redirect=true添加到 Ingress nginx 中,将会把所有非 SSL 流量引导到该 Ingress 的 SSL 端口。你可以在官方文档中找到所有注解,网址为kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/。
一个示例的 ingress 配置如下:

图 14.14 – Ingress 示例 YAML
如您所见,我们为两个主机名 foo.bar.com 和 *.bar.com 定义了 ingress,每个主机名将流量路由到不同的后端服务,如部署。此时,我们应该已经设置了 ingress,并能够通过 HTTP 访问测试应用程序。但正如我们所知,公司和浏览器要求站点支持 SSL,否则会弹出关于不安全的警告消息。因此,在下一部分中,我们将讲解如何为此 ingress 添加 SSL 证书。
如何向 ingress 添加 SSL 证书
要在 ingress 中使用 SSL 证书,您必须创建一种特定类型的秘密,称为 kubernetes.io/tls,稍后将显示一个示例。需要注意的是,值必须以 base64 编码 PEM 格式。您可以通过运行以下命令让 kubectl 为您处理此事:
kubectl create secret tls test-tls --key="tls.key" --cert="tls.crt"
建议您在 tls.crt 中包含完整的证书链。此外,此秘密必须位于与 ingress 规则相同的命名空间中:

图 14.15 – TLS 示例 YAML
一旦创建了秘密(secret),只需将以下部分添加到您的 ingress 配置中,包括秘密名称和此秘密所涵盖的主机名。您可以为单个 ingress 规则定义多个证书和主机,但通常建议将 ingress 限制为单个应用程序:

图 14.16 – 将 TLS 添加到 ingress
此时,我们应该能够使用 ingress 规则将托管在集群中的应用程序发布到外部世界,并为我们的应用程序提供 SSL 支持。
总结
本章讨论了四种主要的负载均衡器设计:轮询 DNS、被动外部负载均衡器、主动外部负载均衡器和集成负载均衡器。我们接着讨论了每种设计的优缺点和一些示例,包括在哪些情况下最合适,然后我们深入配置了 F5 中的 TCP 和 HTTP 模式负载均衡器。接着我们讨论了创建 HAProxy 服务器的安装步骤,包括一些示例配置。我们还介绍了一款名为 MetalLB 的新软件,它完全取代了负载均衡器。最后,我们总结了本章内容,讲解了什么是 ingress 以及如何创建它。这非常重要,因为大多数托管在 Kubernetes 中的应用程序需要发布到外部世界,我们需要以高可用的方式进行发布。
在下一章中,我们将深入探讨故障排除 Rancher 和 Kubernetes 集群的问题,包括如何修复一些常见问题,以及如何设置实验环境来练习从这些问题中恢复。
第十五章:第十五章:Rancher 和 Kubernetes 故障排除
在本章中,我们将探讨 Kubernetes 的主组件、它们之间的相互作用,以及如何排除最常见的问题。接下来,我们将探讨一些常见的故障场景,包括识别故障并尽快解决它们,使用 Rancher 支持团队在支持企业客户时使用的相同故障排除步骤和工具。然后,我们将讨论从一些常见的集群故障中恢复。本章包括在实验室环境中重现所有这些故障的脚本和文档(基于实际事件)。
在本章中,我们将涵盖以下主要主题:
-
从 etcd 分裂脑恢复 RKE 集群
-
从 etcd 备份中重建
-
Pod 未能与 OPA Gatekeeper 一起调度
-
一个失控的应用程序在整个集群中横冲直撞
-
轮换 kube-ca 会破坏我的集群吗?
-
命名空间处于终止状态
-
RKE 集群的常规故障排除
从 etcd 分裂脑恢复 RKE 集群
在本节中,我们将讨论什么是 etcd 分裂脑,如何检测它,最后,如何从中恢复。
什么是 etcd 分裂脑?
Etcd 是一个基于领导者的分布式系统。Etcd 确保领导者节点定期向所有跟随者发送心跳,以保持领导者租约。Etcd 要求大多数节点保持在线并健康,以便根据(n+1)/2成员模型接受写入。当不到一半的 etcd 成员失败时,etcd 集群仍然可以接受读/写请求。例如,如果你有一个五节点的 etcd 集群并丢失两个节点,Kubernetes 集群仍然可以运行。但是,如果再丢失一个节点,那么 etcd 集群将失去法定人数,剩余的节点将进入只读模式,直到恢复法定人数为止。
故障发生后,etcd 集群将进入恢复过程。第一步是选举一个新的领导者,验证集群中大多数成员的健康状态——也就是说,响应健康检查。领导者随后将把集群恢复到健康状态,并开始接受写请求。
现在,另一个常见的故障场景是我们所说的网络分区。这发生在 etcd 集群中的大部分或所有节点失去彼此的访问权限,这通常发生在基础设施故障期间,例如交换机故障或存储故障。但如果你有一个偶数个 etcd 节点,也可能发生这种情况——例如,如果你在数据中心A中有三个 etcd 节点,在数据中心B中也有三个 etcd 节点。
重要提示
不建议在两个数据中心运行 etcd。
然后,数据中心之间的网络连接失败。在这种情况下,这意味着所有 etcd 节点将进入只读模式,因为丧失了法定人数。
如果你在上述场景中有奇数个节点,通常不会遇到分脑集群的情况。但它仍然可能发生。当然,出现的问题是,什么是initial-cluster-token。当节点加入该集群时,每个节点将被分配一个唯一的成员 ID,并接收到集群 ID。此时,新节点将从集群中的其他成员同步数据。
集群 ID 发生变化的原因只有三种:
-
第一个原因是数据损坏;这是一种罕见的情况(我之前只在一次有意进行的数据损坏测试中见过),即使用
dd命令将随机数据写入存储 etcd 数据库文件系统的驱动器。大多数情况下,etcd 内置的安全措施和一致性检查可以防止这种情况发生。 -
错误配置是第二个原因,这种情况在有人进行集群更改时更为常见。例如,当 etcd 节点失败时,一些用户会尝试添加新的 etcd 节点,但没有先移除故障节点,导致新节点无法正确加入,从而将集群置于一个奇怪的损坏状态。新节点有时会生成一个新的集群 ID,而不是加入现有的节点。
-
第三个原因是 etcd 恢复失败。在 etcd 恢复过程中,会创建一个新的 etcd 集群,第一个节点作为引导节点来创建新集群,并将原始数据注入到该新集群中。其余的 etcd 节点应加入新的etcd 集群,但如果 Rancher 与集群/节点之间的连接不稳定,或者
Rancher/RKE/RKE2存在 bug,可能会导致此过程失败。另一个原因是恢复过程在途中失败,导致一些 etcd 节点仍然运行旧的数据,而另一些节点则运行更新的数据。
现在我们知道了 etcd 是如何进入分脑状态的。在下一节中,我们将讨论如何在实际环境中识别此问题,包括你应该查找的常见错误消息。
识别常见的错误消息
当 etcd 进入分脑状态时,通常会在集群被发现离线时出现——也就是说,向 kube-apiserver(s)的请求开始失败,通常会表现为集群在 Rancher UI 中显示为离线状态。
你应该运行以下命令以用于RKE(1)集群,并检查输出结果:
Error messages in etcd logs:
`docker logs --tail 100 -f etcd`
2021-05-04 07:50:10.140405 E | rafthttp: 请求的集群 ID 不匹配(得到 ecdd18d533c7bdc3,期望 a0b4701215acdc84)
2021-05-04 07:50:10.142212 E | rafthttp: 发送的请求被忽略(集群 ID 不匹配:peer[fa573fde1c0b9eb9]=ecdd18d533c7bdc3,本地=a0b4701215acdc84)
2021-05-04 07:50:10.155090 E | rafthttp: 发送的请求被忽略(集群 ID 不匹配:peer[fa573fde1c0b9eb9]=ecdd18d533c7bdc3,本地=a0b4701215acdc84)
请注意,输出中fa573fde1c0b9eb9成员响应的集群 ID 与本地副本不同。我们将进入 etcd 容器,然后使用 etcd 命令行工具连接到 etcd 服务器。最后,我们运行member list子命令来显示此 etcd 集群中的所有节点:
Unhealthy members in etcd cluster:
`docker exec -e ETCDCTL_ENDPOINTS=$(docker exec etcd /bin/sh -c "etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ','") etcd etcdctl member list`
15de45eddfe271bb,已启动,etcd-a1ublabat03,https://172.27.5.33:2380,https://172.27.5.33:2379,false
1d6ed2e3fa3a12e1,已启动,etcd-a1ublabat02,https://172.27.5.32:2380,https://172.27.5.32:2379,false
68d49b1389cdfca0,已启动,etcd-a1ublabat01,https://172.27.5.31:2380,https://172.27.5.31:2379,false
请注意,输出显示所有 etcd 成员的状态都是started,这可能会让你认为它们都处于健康状态,但这个输出可能会误导,特别是这些成员已成功加入集群:
Endpoint health:
`docker exec -e ETCDCTL_ENDPOINTS=$(docker exec etcd /bin/sh -c "etcdctl member list | cut -d, -f5 | sed -e 's/ //g' | paste -sd ','") etcd etcdctl endpoint health`
https://172.27.5.31:2379 状态正常:提案提交成功:耗时 = 66.729472ms
https://172.27.5.32:2379 状态正常:提案提交成功:耗时 = 70.804719ms
https://172.27.5.33:2379 状态正常:提案提交成功:耗时 = 71.457556ms
请注意,输出显示所有 etcd 成员都报告健康,尽管其中一个成员的集群 ID 错误。这个输出报告了 etcd 进程正在运行,并响应其健康检查端点。
你应该运行以下命令来检查 RKE2 集群并查看输出:
Error messages in etcd logs:
`tail -f /var/log/pods/kube-system_etcd-*/etcd/*.log`
请注意,输出与 RKE1 集群的输出非常相似,唯一的区别是 etcd 作为 Pod 运行,而不是独立容器。在以下命令中,我们进行一个for循环,遍历每个 etcd 服务器并测试其端点。这个端点将告诉我们 etcd 服务器是否健康或存在问题:
Unhealthy members in etcd cluster:
`for etcdpod in $(kubectl -n kube-system get pod -l component=etcd --no-headers -o custom-columns=NAME:.metadata.name); do echo $etcdpod; kubectl -n kube-system exec $etcdpod -- sh -c "ETCDCTL_ENDPOINTS='https://127.0.0.1:2379' ETCDCTL_CACERT='/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt' ETCDCTL_CERT='/var/lib/rancher/rke2/server/tls/etcd/server-client.crt' ETCDCTL_KEY='/var/lib/rancher/rke2/server/tls/etcd/server-client.key' ETCDCTL_API=3 etcdctl --write-out=table endpoint health"; echo ""; done;`
在以下截图中,我们可以看到我们正在测试五个 etcd 服务器,每个服务器报告的健康状态为true,并显示每个服务器响应健康检查请求所需的时间。最后,最后一个块将显示是否存在已知的 etcd 服务器错误:

图 15.1 – RKE2 端点健康输出表格
请注意,输出显示了每个主节点的健康状态。需要特别注意的是,该脚本使用kubectl连接到每个 etcd Pod,并运行etcdctl endpoint health命令,检查自身健康。
如果kubectl不可用,你可以通过 SSH 登录到每个主节点并运行以下命令:
export CRI_CONFIG_FILE=/var/lib/rancher/rke2/agent/etc/crictl.yaml
etcdcontainer=$(/var/lib/rancher/rke2/bin/crictl ps --label io.kubernetes.container.name=etcd --quiet)
/var/lib/rancher/rke2/bin/crictl exec $etcdcontainer sh -c "ETCDCTL_ENDPOINTS='https://127.0.0.1:2379' ETCDCTL_CACERT='/var/lib/rancher/rke2/server/tls/etcd/server-ca.crt' ETCDCTL_CERT='/var/lib/rancher/rke2/server/tls/etcd/server-client.crt' ETCDCTL_KEY='/var/lib/rancher/rke2/server/tls/etcd/server-client.key' ETCDCTL_API=3 etcdctl endpoint health --cluster --write-out=table"
此命令直接连接到容器进程。
要从这个问题中恢复 RKE(1)集群,您可以尝试以下步骤:
-
通过运行
rke up --config cluster.yml命令触发集群更新过程,或者对于 Rancher 管理的 RKE(1)集群,您需要更改集群设置。 -
如果
rke up命令失败,请使用etcd-tools,可以在github.com/rancherlabs/support-tools/tree/master/etcd-tools找到,手动重建 etcd 集群。 -
如果
etcd-tools失败,您需要从 etcd 快照恢复集群。
到此为止,我们已经知道如何解决类似的 etcd 故障。接下来,我们需要采取措施防止这些问题再次发生。在下一部分中,我们将介绍一些常见的步骤,您可以采取这些步骤来保护您的集群。
以下是需要执行的预防性任务:
-
如果托管在 VMware 中,使用VM 反亲和性规则确保 etcd 节点托管在不同的ESXi主机上。可以在
docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.resmgmt.doc/GUID-FBE46165-065C-48C2-B775-7ADA87FF9A20.html找到VMware 知识库。 -
如果托管在云服务提供商中,例如
etcd1在us-west-2a和etcd2在us-west-2b。 -
仅以滚动方式应用补丁。示例脚本可以在
github.com/mattmattox/Kubernetes-Master-Class/blob/main/disaster-recovery/etcd-split-brain/rolling_reboot.sh找到。
要在实验室环境中重现此问题,您应按照github.com/mattmattox/Kubernetes-Master-Class/tree/main/disaster-recovery/etcd-split-brain#reproducing-in-a-lab中的步骤操作。请注意,此过程仅适用于 RKE(1)集群,因为 RKE2 由于内置的自愈过程,找到一个可重复的过程非常困难。
到此为止,我们已经处理了一个损坏的 etcd 集群,并需要在原地恢复集群。当然,我们需要将此过程推进到下一步,即当集群丢失并需要重建时如何恢复。在下一部分中,我们将讨论从零开始重建集群的步骤。
从 etcd 备份重建
集群数据,包括部署(Deployments)、密钥(Secrets)和配置映射(configmap),都存储在 etcd 中。使用 RKE1/2,我们可以进行 etcd 备份并使用备份初始化集群。此功能在发生灾难(例如大规模存储中断或集群数据意外删除)时非常有用。
对于 RKE v0.2.0 及更高版本,etcd 备份默认启用。在默认设置下,RKE 每 12 小时会备份一次,并在每个 etcd 节点本地保留 6 份备份,保存在 /opt/rke/etcd-snapshots 目录。你当然可以通过在 Rancher UI 的 cluster.yaml 文件中覆盖这些值来自定义这些设置,相关文档可以在 rancher.com/docs/rke/latest/en/etcd-snapshots/recurring-snapshots/#configuring-the-snapshot-service-in-yaml 找到。
最重要的设置是亚马逊 简单存储服务 (S3) 设置,它允许你将 etcd 快照存储在 S3 存储桶中,而不是本地存储在 etcd 节点上。这一点非常重要,因为我们希望将备份存储到备份服务器之外。请注意,RKE 使用标准的 S3 GO 库,支持任何符合 S3 标准的 S3 提供商。例如,你可以使用 Wasabi 替代 AWS S3,但不能使用 Azure Blob,因为它不完全兼容 S3。在一些不允许将数据发送到云的环境中,你可以使用一些企业存储阵列,如 NetApp 和 EMC,它们可以成为 S3 提供商。
RKE 可以将 etcd 快照恢复到同一集群或新集群中。要恢复 etcd,请运行 rke etcd snapshot-restore --name SnapshotName 命令,其余的由 RKE 处理。将快照恢复到新集群略有不同,因为 etcd 快照会恢复所有集群数据,包括诸如旧节点的节点对象等内容。此外,Kubernetes 证书会被重新生成。这导致服务账户令牌失效,从而导致多个服务(如 canal、coredns 和 ingress-nginx-controllers)中断。为了解决这个问题,我创建了一个脚本,删除了所有损坏的服务账户令牌,并回收了服务和节点。这个脚本可以在 github.com/mattmattox/Kubernetes-Master-Class/tree/main/disaster-recovery/rebuild-from-scratch#restoringrecovering 找到。
你可以在 Rancher 官方文档中找到关于备份和恢复过程的更多细节,文档地址为 rancher.com/docs/rke/latest/en/etcd-snapshots/。
在 RKE2 集群中,你可以使用内置的 rke2 命令在主节点上恢复 etcd 快照,步骤如下:
-
使用
systemctl stop rke2-server命令在所有主节点上停止rke2。 -
在其中一个主节点上使用
rke2 server --cluster-reset命令重置集群。此命令会创建一个仅包含单个节点的新 etcd 集群。 -
使用
mv /var/lib/rancher/rke2/server/db/etcd /var/lib/rancher/rke2/server/db/etcd-old-%date%命令清理其他主节点。 -
然后,通过运行
systemctl start rke2-server命令重新将其他主节点加入集群。
你可以在官方 RKE2 文档中找到更多关于此过程的详细信息,地址:docs.rke2.io/backup_restore/.
到此时,你应该能够进行 etcd 备份,并仅使用该备份重新构建集群。此过程包括 RKE1 和 RKE2 集群。
如何解决由于 OPA Gatekeeper 导致的 Pods 无法调度的问题
正如我们在第十二章《使用 OPA Gatekeeper 的安全性与合规性》一章中所述,ValidatingWebhookConfigurations用来筛选发送到 kube-apiserver 的更新请求,以验证它们是否通过 OPA Gatekeeper 策略。如果 OPA Gatekeeper Pod(s)宕机,这些请求将失败,从而导致 kube-scheduler 出现问题,因为所有的更新请求都会被阻止。这意味着所有新的 Pods 将无法创建。
重要说明
OPA Gatekeeper 可以设置为fail open——即如果 OPA Gatekeeper 宕机,假设它会被批准并继续前进。我曾在较大的集群中看到,OPA Gatekeeper 超时造成的延迟给 kube-apiserver 带来了巨大的负载,导致集群离线。
你可以通过查看以下命令的 kube-scheduler 日志来识别此问题:
-
对于 RKE(1)集群,如果输出如下所示,请运行
docker logs --tail 10 -t kube-scheduler命令。这表明 kube-scheduler 在连接 OPA Gatekeeper 服务端点时遇到问题:2021-05-08T04:44:41.406070907Z E0508 04:44:41.405968 1 leaderelection.go:361] Failed to update lock: Internal error occurred: failed calling webhook "validation.gatekeeper.sh": Post "https://gatekeeper-webhook-service.gatekeeper-system.svc:443/v1/admit?timeout=3s": dial tcp 10.43.104.236:443: connect: connection refused -
By running the following command, you can discover which RKE server is currently hosting the kube-scheduler leader:
NODE="$(kubectl get leases -n kube-system kube-scheduler -o 'jsonpath={.spec.holderIdentity}' | awk -F '_' '{print $1}')"
echo "kube-scheduler is the leader on node $NODE" -
For RKE2 clusters, it's a little different because kube-scheduler runs as a pod instead of a standalone container. You can use the following command to show the logs for all the kube-scheduler Pods:
kubectl -n kube-system logs -f -l component=kube-scheduler
To recover from this issue, you need to restore the OPA Gatekeeper Pods, but this is a problem because all new Pod creations are being blocked. To work around this issue, we need to remove the webhook, allowing OPA Gatekeeper to restart successfully before restoring the webhook:
-
First, try setting the failure policy to open using the following command:
kubectl get ValidatingWebhookConfiguration gatekeeper-validating-webhook-configuration -o yaml | sed 's/failurePolicy.*/failurePolicy: Ignore/g' | kubectl apply -f -. -
If the open policy doesn't work, backup and remove all Gatekeeper admission checks, using the following commands:
kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io gatekeeper-validating-webhook-configuration -o yaml > webhook.yaml kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io gatekeeper-validating-webhook-configuration. -
Monitor the cluster and wait for the cluster to stabilize.
-
Restore the webhook using the
kubectl apply -f webhook.yamlcommand.
At this point, you should be able to recover from an OPA Gatekeeper outage. In addition, you should be able to use these steps for recovery of other software that uses webhooks in your cluster.
A runaway app stomping all over a cluster
One question that comes up a lot is, How can a single app bring down my cluster?
Let's say an application was deployed without CPU and memory limits. Pods can consume so much of a node's resources that the node becomes unresponsive, causing the node to go into an unschedulable state – that is, not ready. kube-scheduler is configured to reschedule the Pods running on the node after 5 minutes (default). This will break that node, and the process will repeat until all nodes are broken.
Important Note
Most of the time, the node will crash and self-recover, meaning you'll only see nodes flipping up and down as the Pods are bouncing between nodes. But I have seen environments where the nodes become locked up but don't restart.
You can identify this issue by reviewing the cluster event using the kubectl get events -A command, which shows the Pod events for all namespaces. And what we are looking for is a large number of Pod evictions, which is Kubernetes moving the Pods from the dying/dead node. You can also review the current CPU and memory of the present running Pods by using the kubectl top Pod -A command, which breaks the usage by the Pod. It's also recommended that you review any monitoring software such as Prometheus to watch the node resource usage over time.
To recover from this issue, you need to disable the Pod/workload, with an example being to scale the deployment to zero using the kubectl -n <namespace> scale deployment/<deployment name> --replicas=0 command, and then to prevent the issue from happening again, you should add resource limits and a request to all workloads by adding the following settings:
resources:
limits:
cpu: "800m"
mem: "500Mi"
requests:
cpu: "500m"
mem: "250Mi"
It is important to note that in Chapter 12, Security and Compliance Using OPA Gatekeeper, we covered how to use OPA Gatekeeper to enforce these settings on all Pods in your cluster, and it is highly recommended that you use that policy, which can be found at docs.rafay.co/recipes/governance/limits_policy/.
To reproduce this issue in the lab, you can find an example application, located at github.com/mattmattox/Kubernetes-Master-Class/tree/main/disaster-recovery/run-away-app.
At this point, you should be able to detect a runaway application in your cluster. Then, you should be able to apply resource requests and limits to stop the application from damaging your cluster. Finally, we covered how to use OPA Gatekeeper to prevent this issue in the future.
Can rotating kube-ca break my cluster?
What is kube-ca, and how can it break my cluster?
Kubernetes protects all of its services using SSL certificates, and as part of this, a kube-service-account-token certificate signs as part of the authentication model. This means that if that chain is broken, kubectl and other Kubernetes services will choose the safest option and block the connection as that token can no longer be trusted. And of course, several services such as canal, coredns, and ingress-nginx-controller use service-account-token in order to communicate and authenticate with the cluster.
Typically, with RKE1/2, the kube-ca certificate is valid for 10 years. So typically, there is no need for this certificate ever to be rotated. But it can be for a couple of reasons, the first being because of cluster upgrade. Sometimes, during a Kubernetes upgrade, cluster services change to different versions, requiring new certificates to be created. But most of the time, this issue is accidentally caused when someone runs the rke up command but it is missing, or has an out-of-date cluster.rkestate file on their local machine. This is because the rkestate file stores the certificates and their private keys. When RKE defaults to generating these certificates, i.e., starts building a new cluster if this file is missing. This process typically fails, as some services such as kubelet are still using the old certificates and tokens so never go into a healthy state, causing the rke up process to error out. But RKE will leave the cluster in a broken state.
At this point, you should have a better understanding of what kube-ca is and how rotating it can affect your cluster. In addition, you should be able to fix the cluster using the rke up command.
How to fix a namespace that is stuck in terminating status
Why is my namespace stuck in termination?
When you run kubectl delete ns <namespace> on a namespace, status.phase will be set to Terminating, at which point the kube-controller will wait for the finalizers to be removed. At this point, the different controllers will detect that they need to clean up their resources inside the namespace.
For example, if you delete a namespace with a PVC inside it, the volume controller unmaps and deletes the volume(s), at which point the controller will remove the finalizer. Once all the finalizers have been removed, the kube-controller will finally delete the namespace. This is because finalizers are a safety mechanism built in Kubernetes to ensure that all objects are cleaned up before deleting the namespace. This whole process can take a few minutes. The issue comes into play when a finalizer never gets removed.
We'll see some of the common finalizers and how to resolve them:
- Rancher-created namespaces getting stuck.
- Custom metrics causing all namespaces to be stuck.
- The Longhorn system is stuck terminating.
Rancher-created namespaces getting stuck
In this example, when disabling/uninstalling monitoring in Rancher, the finalizer, controller.cattle.io/namespace-auth, is left behind by Rancher. And because of this, the namespace will get stuck in Terminating and will never self-resolve. You can confirm this issue by running the kubectl get ns NamespaceName -o yaml command.
It is important to note that this issue has mostly stopped since spec.finalizers section, which tells us what finalizers are currently assigned to this namespace:

Figure 15.2 – An example of a stuck namespace YAML output
To resolve this issue, you have two options:
-
Manually remove the finalizer using the
kubectl edit namespace NamespaceNamecommand, delete the line containingcontroller.cattle.io/namespace-auth, and save the edit. -
If you need to make a mass change for all namespaces in the cluster, you can run the following command:
kubectl get ns | awk '{print $1}' | grep -v NAME | xargs -I{} kubectl patch namespace {} -p '{"metadata":{"finalizers":[]}}' --type='merge' -n {}
Custom metrics causing all namespaces to be stuck
A common reason for a namespace getting stuck is the custom metrics endpoint. Prometheus adds an API resource called custom.metrics.k8s.io/v1beta1, which exposes Prometheus metrics to the Kubernetes services such as kubernetes finalizer will be left behind, which is not a very helpful status. You can confirm this issue by running the following command:
kubectl get ns NamespaceName -o yaml.
In the following screenshot, you'll see a namespace with finalizer kubernetes:

Figure 15.3 – A namespace stuck terminating with the Kubernetes finalizer
To resolve this issue, you have a couple of different options.
-
Fix Prometheus because as long as it is up and running, the finalizer should be removed automatically without issue.
-
If Prometheus has been disabled/removed from the cluster, you should clean up the leftover
custom.metricsendpoint using the following commands:- Run
kubectl get apiservice|grep metricsto find the name. - Delete it using the
kubectl delete apiservice v1beta1.custom.metrics.k8s.iocommand.
- Run
-
You can also remove the finalizer by running the following command:
对于 ns in $(kubectl get ns --field-selector status.phase=Terminating -o jsonpath='{.items[*].metadata.name}'); do kubectl get ns $ns -ojson | jq '.spec.finalizers = []' | kubectl replace --raw "/api/v1/namespaces/$ns/finalize" -f -; done.
It is important to note that this command is used to fix all the namespaces that are stuck in Terminating. Also, this does not fix the root cause but is more like a workaround to recover a broken cluster.
- You can use a tool called knsk, which can be found at
github.com/thyarles/knsk. The aim of this script is to fix stuck namespaces and clean up broken API resources.
The Longhorn system is stuck terminating
Another common issue is the longhorn-system namespace being stuck in Terminating after uninstalling Longhorn. This namespace is used by Longhorn and stores several CustomResourceDefinition). You can confirm this issue by running the kubectl get ns longhorn-system -o json command.
In the following screenshot, you'll see the JSON output for the longhorn-system namespace, which is the default namespace for Longhorn:

Figure 15.4 – longhorn-system stuck terminating with the Kubernetes finalizer
To resolve this issue, you have various options:
-
Run the Longhorn cleanup script, which can be found at
longhorn.io/docs/1.2.4/deploy/uninstall/. This script cleans up all the other CRD resources used by Longhorn. -
Run the following command to cycle through all the
api-resourcetypes in the cluster and delete them from the namespace:kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n longhorn-system,
At this point, you should be able to clean up a namespace that is stuck in terminating by finding what finalizer is assigned to it. Then, you should be able to resolve that finalizer or remove it.
General troubleshooting for RKE clusters
This section will cover some common troubleshooting commands and scripts that can be used to debug issues. All these commands and scripts are designed around standard RKE clusters.
Find the current leader node by running the following listed script. This script will review the kube-scheduler endpoint in the kube-system namespace, which includes an annotation used by the leader controller.
This is the script for finding the kube-scheduler leader Pod: curl https://raw.githubusercontent.com/mattmattox/k8s-troubleshooting/master/kube-scheduler | bash.
Here is an output example of a healthy cluster:
kube-scheduler is the leader on node a1ubk8slabl03
Suppose that this node is unhealthy or overlay networking isn't working correctly. In that case, the kube-scheduler isn't operating correctly, and you should recycle the containers by running rke up. And if that doesn't resolve the issue, you should stop the container on the leader node and allow another node to take over.
In order to show the etcd cluster members list, we'll use the following command:
docker exec etcd etcdctl member list
With the preceding command, you can see the current list of members – that is, the nodes in the etcd cluster.
Here is an output example of a healthy cluster from the preceding command:
2f080bc6ec98f39b, started, etcd-a1ubrkeat03, https://172.27.5.33:2380, https://172.27.5.33:2379,https://172.27.5.33:4001, false
9d7204f89b221ba3, started, etcd-a1ubrkeat01, https://172.27.5.31:2380, https://172.27.5.31:2379,https://172.27.5.31:4001, false
bd37bc0dc2e990b6, started, etcd-a1ubrkeat02, https://172.27.5.32:2380, https://172.27.5.32:2379,https://172.27.5.32:4001, false
If this list does not match the cluster – that is, it has a node that should have been removed and a duplicate node – then you know that the etcd cluster is currently misconfigured and needs to be synced using RKE and etcd tools.
To expand the member list command, you can run the following command to show the health status of each etcd node:
curl https://raw.githubusercontent.com/mattmattox/etcd-troubleshooting/master/etcd-endpoints | bash
It is important to note that this health check only shows that etcd is up and running, as the node might be having other issues, such as a full filesystem or low memory, but may still be reporting as healthy.
From the preceding command, this is an output example of a healthy cluster:
Validating connection to https://172.27.5.33:2379/health
{"health":"true"}
Validating connection to https://172.27.5.31:2379/health
{"health":"true"}
Validating connection to https://172.27.5.32:2379/health
Finally, we will wrap up this section and go over some common errors and what they mean:
-
The following error tells us that the etcd is failing to make a connection with the etcd node on port
2380. So, we need to verify that the etcd container is up and running. Your first step is to review the logs of the etcd container:`health check for peer xxx could not connect: dial tcp IP:2380: getsockopt: connection refused` -
This error means that the etcd cluster has lost quorum and it is trying to establish a new leader. Typically, this occurs when the majority of the nodes running etcd go down or cannot be reached – for example, if two out of three etcd nodes are down. This message usually appears following an outage, but if this message is reported multiple times without rebooting etcd nodes, it should be taken seriously. This means that the leader is switching nodes due to etcd timing out leader leases, which should be investigated. This is known by the following error:
`xxx is starting a new election at term x` -
The following error means that the TCP connection to an etcd node is timing out and the request that was sent by the client never received a response. This can be because the node is offline or that a firewall is dropping the traffic:
`connection error: desc = "transport: Error while dialing dial tcp 0.0.0.0:2379: i/o timeout"; Reconnecting to {0.0.0.0:2379 0 <nil>}` -
The etcd service stores the etcd node and cluster state in a directory (
/var/lib/etcd). If this state is wrong for any reason, the node should be removed from the cluster and cleaned; the recommended way to run the cleanup script can be found atgithub.com/rancherlabs/support-tools/blob/master/extended-rancher-2-cleanup/extended-cleanup-rancher2.sh. Then, the node can to readded to the cluster. The following error shows this:`rafthttp: failed to find member.`
你可以在github.com/mattmattox/Kubernetes-Master-Class/tree/main/troubleshooting-kubernetes找到更多的脚本和命令。
到目前为止,你应该能够检测和解决你的 RKE 集群中可能发生的最常见故障。此外,我们还介绍了如何防止这些类型的故障发生。
概要
本章介绍了 RKE1 和 RKE2 集群的主要部分。然后,我们深入探讨了一些常见的故障场景,包括这些场景发生的原因、如何找到它们以及最终如何解决它们。
然后,我们通过介绍了一些常见的故障排除命令和脚本来结束了本章,这些命令和脚本可用于调试其他问题。
在下一章中,我们将深入讨论 CI/CD 流水线和镜像仓库的主题,包括如何安装诸如 Drone 和 Harbor 等工具。然后,我们将介绍如何与我们的集群集成。最后,我们将介绍如何设置我们的应用程序以使用新的流水线。
第五部分 – 部署您的应用程序
本部分将介绍如何设置 CI/CD 管道,将应用程序部署到 Rancher 集群中,包括镜像注册表和 Helm 图表等支持服务。此外,我们还将讲解如何通过 Ingress 规则将应用程序暴露到外部世界,包括使用 SSL 证书来保护应用程序之间的通信。最后,我们将介绍如何在 Rancher 中应用和强制执行性能容量规划和管理。
本书的这一部分包括以下章节:
-
第十六章,设置 CI/CD 管道和镜像注册表
-
第十七章,创建和使用 Helm 图表
-
第十八章,资源管理
第十六章:第十六章:设置 CI/CD 管道和镜像注册表
本章介绍了持续集成/持续交付(CI/CD)管道,以及它们如何与 Rancher 协同工作,通过标准化和可控的流程来部署应用程序,包括如何将数据库凭证等秘密信息注入到部署中,使得应用程序能够使用这些凭证,而无需给应用团队访问原始凭证的权限。然后,本章介绍了如何设置和配置 Harbor 作为 Docker 镜像仓库,包括如何将 Harbor 配置为拉取缓存,以绕过 Docker Hub 的拉取限制。最后,我们将讨论如何将 Harbor 集成作为私有注册表,替代 Docker 默认的注册表。
本章将涵盖以下主要主题:
-
什么是 CI/CD 管道?
-
架构解决方案的规则
-
如何在 Kubernetes 中通过 Rancher 部署 Drone 及其执行器
-
将秘密注入到管道中
-
什么是镜像仓库,以及架构解决方案的规则
-
如何在 Kubernetes 中部署 Harbor
-
将私有注册表集成到 Kubernetes 中
什么是 CI/CD 管道?
CI 指的是一种编码哲学和实践,鼓励开发人员在软件开发中频繁地将代码提交到版本控制系统。其目标是将你的应用程序和环境定义为代码,这通常被称为基础设施即代码(IaC)或平台即代码(PaC)。随着应用程序从传统的单体架构转向更具云原生特性的微服务架构,构建、打包和测试应用程序变得更加复杂。CI 通过专注于一致性和自动化来解决这个问题。通过将这些步骤集成到像 Drone 这样的软件框架中,我们可以自动化整个过程,本章将对此进行讲解。
当然,CI 只是解决方案的前半部分,后半部分是 CD。其思想是,一旦应用程序经过编译、测试和打包,我们需要一种方式以一致且可重复的方式将应用程序及其变更发布到我们的环境中。由于 Kubernetes 使用的是期望状态驱动的技术,我们只需要修改期望配置,如果我们想改变集群中的某些内容,Kubernetes 会自动确保当前状态与期望状态相符。我们在第二章中介绍了这一过程,Rancher 和 Kubernetes 高级架构。CD 通过允许应用程序开发人员将应用定义为 YAML 文件或 Helm 图表,利用 Kubernetes 的优势。我们可以以可重复的方式将应用部署到我们的集群中。
当然,通常将 CI/CD 两个部分组合成我们所说的管道,而我发现的最佳方式是从头到尾逐步讲解管道,如下所示。

图 16.1 – 示例 CI/CD 管道
以下是大多数 CI/CD 管道的高层步骤,并对每个步骤进行了描述:
步骤 1(开发团队):流程从开发者检查仓库(GitHub、GitLab、Git 服务器)的代码开始,开发者进行代码修改,即修复一个 bug、添加一个功能等。在此时,开发者可能会在本地运行应用程序来测试他们的更改。但当开发者准备好后,他们将把更改提交到仓库。
步骤 2(Git 提交):在此步骤中,开发者通常会为新的功能或 bug 修复创建一个分支。根据设置,开发者可能会将仓库进行分叉,并将更改推送到分叉仓库,而不是主仓库。但理解这一步骤的关键是,Git 提交是对你的代码、应用程序、部署和基础设施进行更改的唯一方式。
步骤 3(Git 仓库):一旦 Git 提交被推送到仓库,仓库接管管理。第一步是验证开发者是否有权限将更改推送到仓库。例如,他们可能有权限提交更改到功能分支,但没有权限提交到主分支(main/master)。然后,通常的下一步是创建一个Pull Request(PR),它发起将一个分支/派生库的更改合并到另一个分支的请求。例如,你可能会创建一个 PR 将你的功能合并到主分支。这时,CI/CD 软件通常会参与进来。例如,你正在使用 GitHub 和 Drone。一旦你创建了一个 PR 请求,GitHub 会发送一个 HTTP 请求,称为 webhook,发送到你的 Drone 服务器,进而启动下一步操作。
步骤 4(构建):在这个步骤中,事情开始发生。我们将继续使用 Drone 的例子,在这个例子中,Drone 服务器已经从 GitHub 收到了 webhook,其中包含关于仓库更改的一些细节。例如,它包含仓库名称、分支、提交哈希和提交消息。但需要注意的是,它不包括代码内容。
注意
如果你想查看 webhook 载荷中包含的内容,请参阅 GitHub 的官方文档:docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads。
流程可能会根据您的管道软件而变化,但一般来说,第一步是将代码存储库克隆到临时位置。这就是为什么您的管道服务器需要访问您的代码存储库。第二步是加载 Drone 的管道配置文件,即.drone.yml。此文件存储了构建过程中将使用的步骤。例如,可能会拉取任何依赖项并运行一些命令来编译您的二进制文件。重要的是要注意,大多数管道软件都包含预构建的插件/模块,用于编译诸如 Go、npm、C++ 等常见语言。对于 Drone,您可以在 plugins.drone.io 找到一个列表,但是像 Drone 和 GitHub Actions 这样的现代管道软件之一的好处是它们使用容器来运行这些插件/模块,因此制作自定义步骤可以是一个简单的过程。
您可能会问,此构建步骤在哪里运行?这个问题的答案取决于软件以及它的托管位置。例如,整个过程可能在使用 GitHub Actions 等云端工具时进行,或者如果您正在使用自托管的 Drone,则会在您自己的计算机上运行。这些通常被称为运行器,因为它们运行您的代码。例如,在本章后面我们将涵盖的示例中,我们将在 Kubernetes 集群中部署 Drone 的运行器,它将为每个构建创建临时 pod,并为每个步骤创建 sidecar。
步骤 5(测试):到目前为止,我们已经构建了应用程序,但我们需要测试它并验证它是否按计划工作。这一步骤可以简单到最后的编译测试,也可以复杂到部署到测试集群,然后模拟终端用户使用应用程序,以及中间的一切。这一步骤取决于您的应用程序和编程语言。例如,Go 有内置的测试,可以直接编写到应用程序中进行测试。像 LAMP(Linux、Apache、MySQL 和 PHP)堆栈一样,应用程序可能需要几个不同的组件,包括数据库,才能启动。同时,还有一个问题,即您是否可以轻松地测试您的应用程序。例如,您是否已经有了测试登录、运行报告、进行测试销售等的脚本?如果有的话,在这一步骤中,您将希望创建一个流程来运行这些测试。总的想法是您希望验证此提交的任何更改不会影响生产环境。
步骤 6(发布):一旦我们完成所有测试,并准备好在 Kubernetes 集群中启动应用程序,我们需要将刚刚创建的容器镜像提供给集群。我们通过将镜像发布到某种 Docker 注册表来实现这一点。当然,解决这个问题有多种不同的方案,其中一些将在本章后续讲解。但这里需要理解的关键是,我们需要一个发布镜像的位置,无论是 Docker Hub、简单的私有注册表,还是像 Harbor 这样的企业级解决方案。
对于 Drone 和大多数 CI/CD 软件而言,这一步是相当直接的,主要是为镜像添加版本/构建号标签,然后运行 docker push image… 命令将镜像推送到所选的注册表。作为将镜像发布到注册表的一部分,通常会进行安全扫描,查找已知的漏洞。例如,您可能希望阻止所有包含易受 Log4j(CVE-2021-44228)漏洞影响的软件版本的镜像,大多数 CI/CD 软件使用像 Clair 这样的工具来处理这一过程,并根据找到的 CVE 数量和严重性,发出警报或阻止镜像。
步骤 7(部署):一旦一切准备就绪,应用程序部署到我们的 Kubernetes 集群的有趣部分就开始了。 这一步主要是创建/准备我们的部署文件,文件可以是简单的 YAML 文件,也可以是复杂的 Helm 图表,甚至是两者的混合。在本章中,我们不会深入讨论 Helm 图表,因为这个话题会在下一章讲解。此过程主要包括更新部署文件中的镜像标签,以适应新创建的镜像。
到此为止,我们的 CI/CD 已经开始将应用程序发布到集群的过程。 当然,这可以简单到运行 kubectl apply -f deploy.yaml 命令,或者复杂到为数据库部署公共 Helm 图表,即 helm install mysql,然后在运行 Kubernetes 作业(一个仅运行一次的单次使用 Pod)来创建数据库之前,验证数据库是否已经启动并准备好,最后从 S3 存储桶加载数据。然后,我们最终运行 kubectl apply 来启动我们的应用程序,并运行一些测试命令来验证一切是否按预期工作。需要理解的关键点是,这一步骤是我们如何以安全且受控的方式,给开发人员访问我们的生产 Kubernetes 集群,而不直接给他们访问集群的权限。
此时,管道完成,我们将从头开始处理下一个功能或 bug 修复。核心思想是,管道的设计使得这一过程可以重复且可控,允许开发人员快速推进,但又不会破坏我们的集群。在下一节中,我们将深入探讨创建 CI/CD 解决方案,并进行安装。
架构解决方案的规则
现在我们已经理解了 CI/CD 的概念,我们将讨论如何设计解决方案,包括标准设计的优缺点。需要注意的是,每个环境/应用都是独特的,可能需要进行调整以获得最佳体验。
在设计解决方案之前,你应该能够回答以下问题:
-
你的应用团队是否已经有支持 Kubernetes 的 CI/CD 解决方案?
-
是否只有一个团队使用此 CI/CD 解决方案,还是多个团队共享它?
-
你认为会创建多少个管道/构建?
-
你的开发者遵循什么样的部署计划?
-
他们是否会随时进行更改,还是会进行大规模的补丁更新,也就是说,每个星期五是构建日?
-
构建过程的速度有多重要?如果一个管道需要一个小时运行,可以接受吗,还是它应该在几分钟内完成?
-
你们将使用自托管的还是软件即服务(SaaS)的方案?
-
你们将部署哪种软件栈(Go、LAMP、Node.js、DotNet 等)?
-
你需要哪些工具/软件包来构建你的应用?
-
你的构建工具是否需要特殊的许可?
-
你需要在哪种 CPU 架构和操作系统平台上构建(Linux、Windows、AMD64、ARM)?
-
你的代码是私有的还是公开的?
-
构建过程和输出是否需要公开访问(大多数开源项目是这样)?
-
只有内部员工会创建提交并发布,还是这是一个公开项目,因此外部用户可能会触发构建?
-
你的软件和硬件预算是多少?
一旦我们对这些问题有了答案,我们就可以评估解决方案。
Drone
Drone 是一个基于 Go 的应用程序,执行所有容器内的操作。因此,它非常适合像 Kubernetes 这样的平台,在这里启动容器非常简单。Drone 使用服务器进行管理和编排,运行器负责创建用于构建任务的容器。
优点如下:
-
对于开源项目是免费的。
-
与 GitHub、Bitbucket 和 GitLab 的集成已被验证良好。
-
Drone 服务器及其运行器是轻量级的,可以部署在像 Raspberry Pi 这样的低端硬件上。
-
将管道作为代码,这意味着你的管道成为应用开发的一部分。无需在源代码控制之外更改管道配置。
-
通过 Vault、KMS 和第三方进行内部和外部的秘密管理。
-
使用起来很简单,因为界面易于理解。
-
所有插件都是容器,这意味着如果你想编写自己的插件,只需创建一个 Docker 镜像来执行你想要的任何任务。
-
云和本地软件是相同的,因此在两者之间迁移毫不费力。
-
可扩展性是内建的,因为所有构建都作为容器运行,可以轻松地通过 Kubernetes 扩展。
-
Drone 是为了在公共互联网中运行而构建的,因为许多开源项目使用 Drone。由于这一点,以及 Drone 基于容器的架构,公开暴露 Drone 服务器是安全且正常的,这也让公众能够更好地看到透明性。
-
Drone CLI 内置了对本地构建的支持,允许你在本地机器上触发构建,而无需连接到 Drone 服务器。由于每个任务都是一个容器,Drone CLI 仅通过与运行器相同的 API 调用与 Docker 交互。
缺点如下:
-
Drone 不支持构建缓存。因为每次构建都是一个新的容器/Pod,前一次构建的内容无法使用,这意味着如果你需要下载一组依赖项来运行构建,你每次都会重新下载这些依赖项。像 drone-cache 这样的插件可以帮助解决这一问题,但它们并不是最可靠的解决方案,而且可能迫使你限制构建运行的方式。
-
它存在一个典型的“大空盒”问题,因为 Drone 被构建来支持几乎任何语言/应用堆栈。刚开始时,一切都是空的,几乎没有框架来指导你。可以在 Drone 文档中找到一些示例,但你大多数时候都得从零开始。
-
Drone 拥有一个相对活跃的社区,但它仍然是 CI/CD 市场的新人,创建于 2014 年,但找到具备管理员知识的人可能会很困难。
-
没有可以用于身份验证的外部身份提供者,这意味着你不能将其指向 Active Directory、LDAP 或 SAML 提供者进行身份验证。
-
拥有一个公共的 Drone 服务器实例是正常的……但安全性是相对的。可以使用一些设置来限制对特定 GitHub 组织的访问,也可以对机密的访问进行设置,以防止恶意用户通过恶意的仓库窃取这些机密。这些设置并不是开箱即用的。
-
Drone 存在代码维护问题,因为它需要大量的补丁和自定义插件来支持特定的工作负载。例如,默认的 Kubernetes 插件功能很简陋,用户需要创建自己的插件,例如
github.com/SupportTools/kube-builder。甚至有一个社区分支的 Drone 叫做 Woodpecker,旨在解决这些问题。 -
Drone 服务器没有 高可用性(HA),因为服务器使用的是内存队列系统。Drone 服务器将依赖于 Kubernetes 提供 HA,即重新启动和重新调度失败的 Pod。截至目前,HA 功能仍在测试版中,详细信息可以参考
docs.drone.io/server/ha/overview/。
通常,我推荐将 Drone 用于那些从一开始就考虑容器的云原生和 Kubernetes 原生应用程序,以及希望实现 GitOps 工作流的团队,比如需要公开访问的开源项目。
Jenkins
Jenkins 是一个基于 Java 的 CI/CD 工具,旨在自动化运行脚本,有时被称为支持 CI/CD 的自动化平台。
优点如下:
-
Jenkins 在 DevOps 和 CI/CD 市场中有着深厚的历史,它是此列表中最古老的工具。它最初由 Oracle 于 2011 年创建,2014 年分叉后成为 Jenkins。
-
由于 Jenkins 的发展历史,它已经成为 CI/CD 领域的默认工具,这意味着许多软件、插件和工作流都是围绕 Jenkins 构建的,然后才会适配到其他 CI/CD 工具。
-
Jenkins 拥有丰富的插件列表(截至撰写时超过 1000 个),涵盖从 Nagios 集成监控,到 Puppet 进行配置管理,再到与 Jira 集成进行问题跟踪等功能。
-
因为 Jenkins 是用 Java 编写的,它具有可移植性,这意味着服务器可以根据你的需求在 Windows 或 Linux 上运行。
-
Jenkins 拥有优秀的测试自动化插件,例如
TestComplete Support,它可以模拟用户行为,如打开网页、运行桌面应用程序等。需要注意的是,这是一个第三方插件。 -
Jenkins 内置支持一些企业认证提供商,如 Active Directory、LDAP、SAML 等。
-
Jenkins 内置了高可用性(HA)支持,使用的是主动-备份设置。
-
企业/付费插件通常附带优秀的文档和支持。
缺点如下:
-
Jenkins 是为企业产品构建的,这意味着它假设会有管理员来照顾和维护它,也就是说,需要有人来终止卡住的作业、监控资源、在出现问题时重新启动等。
-
社区/免费插件通常缺乏文档支持,并且没有付费支持选项。
-
Jenkins 为 Kubernetes 提供了运行器,可以让你将任务作为 pods 在集群中运行,但设置和使用这种类型的运行器可能具有挑战性,并且需要使用在
plugins.jenkins.io/kubernetes/上可以找到的详细信息。 -
Jenkins 确实支持本地构建,但你实际上是在运行一个本地副本的 Jenkins 服务器,这意味着你需要完成所有的设置工作,并使本地配置与真实的 Jenkins 服务器相匹配。
-
没有 SaaS/云托管选项,这意味着你需要安装和管理 Jenkins 服务器。
-
用户界面较为笨重,使用起来不太友好,界面看起来像是十年前的 Java 应用程序。
通常情况下,我推荐 Jenkins 有两个原因。第一个原因是它已经在客户的环境中部署并使用。这是因为在不同管道之间切换可能需要大量工作,过程就是需要重新开始。第二个原因是企业插件支持。编写自己的插件可能需要大量时间和资源,这些资源可以用于其他更有价值的事情。因此,企业客户通常直接购买开箱即用的商业插件。
注意
Rancher did have its own pipeline solution called Rancher Pipelines, but as of Rancher v2.5, this feature has been deprecated and replaced by Fleet. Rancher Pipelines was built on top of Jenkins but integrated into Rancher. You can still enable it using the steps at rancher.com/docs/rancher/v2.6/en/pipelines/example-repos/, but it is highly recommended not to use it.
GitHub Actions
GitHub Actions was launched back in 2018 as a workflow automation tool that can also do CI/CD. It shares the core item of all tasks being containers like Drone does, but with the big difference that it's a cloud solution sold as an add-on to your GitHub subscription.
优点如下:
-
It's easy to use for GitHub users as their repositories are just built into the platform, which just needs to be enabled.
-
You can use self-hosted runners to work around costs by providing your own hardware details, which can be found at
docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners. -
Nearly unlimited scale – as a cloud-based solution, you can run as many parallel builds as possible without needing to spin up additional servers/nodes, depending on your GitHub subscription, details of which can be found at
docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions. -
Tight integration with GitHub events allows you to customize your pipelines based on the type of event. For example, you might run one workflow to create a new issue and another one to create a pull request.
-
Built-in caching for the GitHub repo means that if you have a large repository with lots of changes, you can assume that the clone will take little to no time. This is because everything is being done inside the GitHub network.
-
GitHub has a marketplace for Actions, meaning it's effortless to add plugins to your workflow. For example, if you need a K3s cluster to test, just go to
github.com/marketplace/actions/setup-k3d-k3sand click the 使用最新版本 button, then copy and paste the text into your flow.
缺点如下:
-
Actions are sold by the minute. For example, if your task runs for 5 mins, you are billed for 5 mins of usage. GitHub does provide some amount of free usage depending on your subscription and repository type (private versus public).
-
GitHub Actions 一直是黑客和加密货币矿工的热门目标,他们利用公共项目窃取资源。例如,如果你在每个拉取请求上都运行构建,谁能阻止某人将比特币挖矿程序作为工作流的一部分运行呢?这会导致账单暴涨,并且已经让用户损失了成千上万美元。你可以在
www.bleepingcomputer.com/news/security/github-actions-being-actively-abused-to-mine-cryptocurrency-on-github-servers/阅读更多关于这些攻击的内容。 -
在商业插件方面,GitHub 也存在与 Drone 相似的缺点,这些插件称为 Actions,因为它毕竟还没有存在很长时间。
-
GitHub Actions 主要运行在云端,因此访问内部资源(如数据库、服务器和 Kubernetes 集群)可能会变得困难,除非将它们发布到互联网上。你可以通过使用本地运行器来解决这个问题。
-
在本地笔记本电脑上运行构建仅限于使用第三方工具,如
github.com/nektos/act。 -
构建环境再次受到限制,因为 GitHub 为你托管虚拟机。你只能选择他们提供的操作系统和版本。例如,ARM 需要自托管的运行器才能使用。你可以在
github.com/actions/virtual-environments查看当前支持的环境列表。
通常,我推荐 GitHub Actions 用于需要简单构建脚本的小型一次性项目;只需运行 make 命令。进行更高级的测试,如用户模拟,会变得非常困难。
Rancher 确实有一款名为 Fleet 的产品,它在某种程度上适用于这个场景,因为它是一个仅用于持续交付(CD)的工具,不支持持续集成(CI)。你不能在 Fleet 中执行 Docker 构建、应用测试等操作。它只设计用于将更改推送到你的环境。当然,你可以将 Fleet 用于将应用程序发布到集群,但不推荐这么做,因为 Fleet 没有回滚或回退功能,这意味着如果你推送了更改并且它破坏了应用程序,你需要更新你的 Git 仓库并推送新的更改。
在本章的其余部分,我们假设你选择了 Drone 作为 CI/CD 管道工具,但大多数步骤也可以轻松迁移到其他工具。
如何在 Kubernetes 中通过 Rancher 部署 Drone 及其运行器
本节将过程分为三部分:部署 Drone 服务器、启动 Kubernetes 运行器以及将管道连接到外部 Rancher 集群。
前提条件
以下是你在开始安装 Drone 和其运行器之前需要设置的项:
-
一个具有创建集群级资源权限的 Kubernetes 集群(建议是集群管理员)
-
持久存储(更多详情请参见 第十一章,使用 Longhorn 将存储带入 Kubernetes)
-
用于发布 Drone Web UI 的主机名
-
一个公开签名的 SSL 证书(内部或自签名证书可能会导致问题)
-
在 GitHub 中创建 OAuth 应用程序的权限
-
GitHub 上的公开可访问 URL(例如,
drone.example.com) -
可以访问集群的 kubectl 和 Helm,Drone 服务器将在此集群中安装。
安装步骤
在本节中,我们将介绍在安装 Drone 之前如何安装 PostgreSQL。最后,我们将安装 runner。
PostgreSQL
Drone 服务器需要一个后端数据库来存储其设置、配置等。为此,我们将使用 PostgreSQL,因为它是自托管部署唯一支持的数据库。如果您愿意,您可以跳过此步骤,使用外部管理的数据库,如 Amazon RDS for PostgreSQL。
要安装 PostgreSQL,您需要运行以下命令:
-
第一个命令是创建一个命名空间。
-
然后是一个
helm命令,用于添加bitnami仓库。 -
然后,最后,我们运行
helm install命令以部署 PostgreSQL 服务器,并通过附加选项设置用户名和密码。
以下是结果:

图 16.2 – PostgreSQL 安装步骤
您可以在 github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch16/drone/postgresql/install_commands.md 找到完整的命令。您还可以在 github.com/bitnami/charts/tree/master/bitnami/postgresql/ 找到 Helm chart 选项的详细解析。
注意
我们将密码设置为 drone,作为示例。这应该设置为更安全的内容。
Drone 服务器
在本节中,我们将安装 Drone 服务器。但在安装 Drone 之前,您应该按照 docs.drone.io/server/provider/github/ 上的步骤在 GitHub 上创建 OAuth2 应用程序。要安装 Drone 服务器,您需要按照以下步骤进行:
-
我们创建一个新的命名空间,并使用图 16.3 中列出的命令添加 Drone
helm 仓库。 -
然后我们为 RPC 密钥创建 SSL 秘密,Drone 及其 runner 会使用此密钥进行身份验证。
-
接下来,我们创建一个 RPC 密钥,用于身份验证 runners。
-
最后,我们使用带有多个设置的
helm install命令。
第一部分定义了入口设置,接下来是存储,然后我们设置数据库连接字符串。最后一部分是将 GitHub 集成用于身份验证。

图 16.3 – Drone 服务器安装步骤
您可以在github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch16/drone/server/install_commands.md找到完整的命令。您还可以在github.com/drone/charts/blob/master/charts/drone/找到 Helm 图表选项的详细分解。
此时,您应该能够通过访问 Web UI 来登录您的 Drone 服务器。
Drone Kubernetes runner
在本节中,我们将安装 Drone Kubernetes runner。如果您愿意,可以将其安装在与 Drone 服务器相同的集群中。此外,在以下示例中,我们将使用默认命名空间来运行我们的临时 pod。如果需要,您可以进行自定义。
要安装 runner,您需要执行以下步骤:
-
创建一个新的命名空间。
-
添加 Drone
helm repo。 -
最后,安装
drone-runner。
需要注意的是,我们将此 runner 指向我们的 Drone 服务器,并使用 RPC 密钥进行身份验证。

图 16.4 – Drone runner 安装步骤
您可以在github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch16/drone/runner/install_commands.md找到完整的命令。您还可以在github.com/drone/charts/tree/master/charts/drone-runner-kube找到 Helm 图表选项的详细分解。
此时,您应该能够在您的 Drone 服务器中运行构建。在接下来的部分中,我们将连接 Drone 到我们的 Kubernetes 集群,以便部署我们的应用程序。
连接到 Rancher
在本节中,我们将在另一个 Kubernetes 集群中创建一个服务账号,并将其添加为 Drone 服务器的一个密钥。在这个命令中,我们创建了一个服务账号,并分配了 cluster-admin 权限,最后获取了该账号的 token。

图 16.5 – 创建一个 Drone 服务账号
您可以在github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch16/drone/external-cluster/rbac.md找到完整的命令。
接下来,我们需要登录 Rancher UI,浏览相关集群,并点击下载 KubeConfig按钮来获取集群服务器的 URL。你将得到一个类似以下示例的文件。我们需要的是服务器值,在这个示例中是rancher.example.com/k8s/clusters/c-m-abcdefgj。

图 16.6 – KubeConfig 示例
现在,我们可以将凭证作为密钥添加,以便管道可以使用它,这将在下一节中讲解。
向管道注入密钥
在本节中,我们将介绍如何向管道中添加密钥。在这个示例中,我们将使用上一节中创建的 Kubernetes 凭证。
我们将从浏览仓库并点击激活仓库按钮开始。然后,我们需要进入设置标签页,并从侧边菜单中选择密钥。

图 16.7 – 创建密钥向导
我们将创建以下密钥及其值。需要注意的是,如果您使用的是公共仓库,其他人可能会访问您的密钥,特别是如果您勾选了允许拉取请求选项。
重要说明
一旦将密钥添加到 Drone 中,就无法再检索它。因此,建议您将副本存储在 Drone 之外的地方,例如密码管理器。

图 16.8 – 示例密钥
现在我们已经创建了密钥,可以在管道中使用它们。示例中的关键项目是from_secret命令,它将密钥的值与插件的设置绑定在一起,插件是容器内的一个环境变量。这个管道使用默认的 Docker 插件来运行 Docker 构建,然后使用我创建的名为supporttools/kube-builder的自定义插件来运行 kubectl 和 Helm 命令。您必须首先运行bash /usr/local/bin/init-kubectl命令,因为它负责在容器内部设置kubeconfig文件。

图 16.9 – 示例管道
到目前为止,我们已经启动并运行了一个管道,应该能够构建和部署我们的应用程序。在下一节中,我们将深入探讨如何设置一个私有镜像仓库,用于存储我们从 Docker Hub 之外的 Docker 镜像。
什么是镜像仓库以及架构解决方案的规则
容器化的一个关键概念是镜像。镜像是一个只读文件,包含构成容器的所有文件、库和可执行文件。一旦创建了容器镜像,就需要将其存储在一个地方,确保可以从 Kubernetes 节点下载。这个地方称为注册表、容器注册表或镜像仓库。在本章中,我们将其称为镜像仓库。当然,镜像仓库有三种主要类型:Docker Hub、第三方和自托管。
但在设计解决方案之前,您应该能够回答以下问题:
-
这是一个隔离环境吗?也就是说,所有镜像是否都必须来自我们控制的注册表?
-
您的所有镜像都是公开的吗,还是会使用私有仓库?
-
您在软件和硬件上的预算是多少?
-
多个数据中心/站点需要访问您的注册表吗?
-
您是否有任何安全/合规要求,比如需要将数据保存在特定地点(例如,仅限本地存储)或在您所在的国家等?
-
您是否计划扫描镜像并限制允许进入您环境的镜像?
一旦我们解答了大部分这些问题,我们就可以评估解决方案。
Docker Hub
这是大多数 Kubernetes 发行版的官方/默认镜像仓库,包括 Rancher。因为它是默认选项,所以大多数部署、开源项目和软件供应商都会使用 Docker Hub。
优点如下:
-
它是默认的并且适用于公开镜像,您只需为私人镜像付费。
-
信任:从一个随机的第三方镜像仓库拉取镜像可能会给开源项目带来红旗,因为无法确定是谁在托管这些镜像。他们是否会在镜像中注入恶意软件?Docker Hub 通过对所有公开镜像进行安全扫描来解决这个问题,扫描内容包括标记包含病毒和其他恶意代码的镜像。需要注意的是,这个系统并不完美,并不能捕捉到所有问题。
-
速度:Docker Hub 当然是最大的镜像仓库,它使用 Cloudflare 作为 CDN,并与 AWS 一起提供后端的 S3 存储。从 Docker Hub 拉取镜像非常快速,限制因素几乎总是出在你这端,除非你被 Docker Hub 限制了拉取速率。你可以通过查询他们的 API 详情来了解更多信息,相关链接可以在
www.docker.com/blog/checking-your-current-docker-pull-rate-limits-and-status/找到。 -
数据冗余/可用性:同样,这些镜像存储在 AWS S3 中,亚马逊的官方文档表明 S3 对象的耐久性为 99.999999999(11 个 9),持续一年。因此,由于数据丢失而导致镜像丢失的几率几乎为零。
缺点如下:
-
安全要求:一些组织要求所有源代码和数据必须保持在本地。Docker Hub 账户曾被攻击,私人镜像也曾被泄露。
-
Docker 镜像在 Docker Hub 中以纯文本格式存储,并未加密,这意味着任何有权限访问仓库的人都能下载镜像并从中提取数据/源代码。这也包括 Docker Hub 自身,因为它会扫描上传到其网站的镜像。例如,Log4js 问题就是 Docker Hub 触发了对其网站上所有镜像的扫描。相关细节可以在
docs.docker.com/security/#log4j-2-cve-2021-44228找到。 -
拉取限制:Docker Hub 在 2021 年 11 月对镜像增加了拉取速率限制,详细信息可以在其官方文档中找到,
docs.docker.com/docker-hub/download-rate-limit/。当然,你可以购买 Docker Hub 订阅服务,这样你的集群就没有限制。你也可以阅读我的 Kubernetes 大师课程——Docker Hub 限额:解决 Rancher 中的拉取请求数量问题,该课程可以在github.com/mattmattox/Kubernetes-Master-Class/tree/main/docker-hub-limits找到。
我通常建议使用 Docker Hub 来管理所有公共项目,并为托管私人镜像每月支付几美元,除非你在镜像中存储了敏感数据或你的网络连接有限。
第三方管理的镜像仓库
这些平台充当你自己容器镜像的访问中心,简化了存储、管理和保护镜像的过程,而无需在本地运行自己的镜像仓库。许多第三方仓库服务支持 Docker 镜像,包括 Amazon ECR、Azure Container Registry 和 Google Container Registry。
优点如下:
-
控制:这些镜像仓库通常托管在你当前的云环境中——也就是 AWS、Azure、GCP——因此,你对数据拥有控制权。
-
托管服务:由于云服务提供商管理所有这些镜像仓库,你不必进行升级、打补丁或备份。你只需要让他们为你处理这些工作。
-
定价:大多数云服务提供商对进出镜像仓库的流量收费极少或不收费。存储费用通常只是转嫁给存储数据的 S3/对象存储。
缺点如下:
-
对于公共镜像来说并不具成本效益:因为大多数云服务提供商对外部流量(传出到公共互联网的流量)收取额外费用,所以托管公共镜像可能会变得非常昂贵。
-
认证:大多数云服务提供商使用他们自己的认证源来控制对镜像仓库的访问。这会引发问题,因为 Docker 是基于基本认证(静态用户名和密码)构建的,而像 AWS ECR 这样的服务只会提供有效 6 小时的密码。这意味着你需要使用像
github.com/SupportTools/ecr-helper这样的工具来定期更新密码。
我通常只推荐在托管 Kubernetes 集群的情况下使用第三方镜像仓库,特别是当你需要存储私人镜像但不想自己管理服务时。例如,如果你在亚马逊上部署了 EKS 集群,你应该使用 ECR。
自托管仓库
组织可能因各种原因(包括安全性、合规性问题或低延迟要求)更倾向于在自己的本地基础设施上托管容器镜像。
优点如下:
-
控制:你完全掌控,因为你托管了镜像。你可以选择将所有内容对外开放,或者将所有镜像锁定,介于两者之间。
-
带宽:由于镜像托管在你的数据中心,因此你的节点不再需要访问公共互联网来拉取镜像。例如,如果你有一个 100 节点的集群,每个节点需要拉取一个 1 GB 的镜像。你需要通过互联网连接下载 100 GB 数据,而不是为本地服务器下载这些数据。
-
安全性:使用像 Harbor 这样的工具,我们可以将镜像扫描集成到我们的注册表中,并设置规则,如阻止上传具有过多高/关键漏洞的镜像。
-
在空气隔离环境中的要求:在无法拉取外部镜像仓库镜像的空气隔离环境中,必须使用私有镜像仓库。
缺点如下:
-
管理:因为你现在托管了注册表,你直接负责该软件的安全、修补和升级任务。
-
部署变更:大多数部署假设你会使用 Docker Hub 作为镜像仓库。因此,你需要将镜像从公共仓库复制到你的私有注册表中。
我通常只在空气隔离的环境中推荐自托管仓库,或者在通过互联网拉取大量数据可能成为问题的情况下,即低速互联网或多个节点共享连接的情况。
此时,你应该能够选择哪个镜像仓库最适合你的环境。在接下来的章节中,我们将假设你选择了自托管选项。
如何在 Kubernetes 中部署 Harbor
在本节中,我们将在我们的 Kubernetes 集群中安装 Harbor。需要注意的是,这将只是一个基本设置,因为 Harbor 可以通过多种方式安装。
以下是前提条件:
-
一个具有创建集群级资源权限的 Kubernetes 集群(建议是集群管理员)
-
持久化存储(更多细节请参见 第十一章,使用 Longhorn 将存储带入 Kubernetes)
-
发布主 Harbor URL 和公证服务的两个主机名
-
公共签名的 SSL 证书(内部证书或自签名证书可能会导致问题)
-
对将安装 Drone 服务器的集群具有 kubectl 和 Helm 访问权限
要安装 Harbor,我们将运行以下命令:
-
首先,添加 Harbor 的
helm repo。 -
接下来,创建 Harbor 的
namespace。 -
然后,上传用于 Ingress 的
ssl certificate秘密。 -
然后,最后,我们运行
helm install来安装 Harbor chart。
需要注意的是,这包括一些设置:第一部分是设置 ingress,第二部分是设置存储类,即 Longhorn。

图 16.10 – Harbor 安装命令
你可以在 github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch16/harbor/server/install_commands.md 找到完整的命令。你也可以在 github.com/goharbor/harbor-helm 找到 Helm chart 选项的详细解析。
此时,你应该已经让 Harbor 启动并运行,准备好开始上传镜像并在环境中使用它们。在下一部分,我们将配置 Kubernetes 使用私有注册表。
将私有注册表集成到 Kubernetes 集群中
既然你已经有了自己的私有注册表,现在需要将其添加到 Rancher 和 Kubernetes 集群中,以便你开始使用它。
在这一部分,我们假设你使用 Harbor 作为你的私有注册表,并且这是一个 air-gapped 配置,意味着我们的节点无法从公共互联网拉取镜像。我们需要做三件事来完成这个任务:
-
收集/发布镜像
-
设置 Rancher 全局注册表
-
更新 RKE/RKE2
收集/发布镜像
在这一部分,我们需要收集 Rancher 和其集群所需的所有镜像。幸运的是,Rancher 支持一种名为 air-gapped 的安装类型,并已发布一个脚本作为 Rancher 版本的一部分来处理这一过程。你可以在 Rancher 文档中找到关于此过程的详细信息,网址是 rancher.com/docs/rancher/v2.6/en/installation/other-installation-methods/air-gap/populate-private-registry/。
但简而言之,Rancher 有一个名为 rancher-images.txt 的文件,其中包含 Rancher 可能需要的镜像列表。然后,Rancher 有一个名为 rancher-save-images.sh 的脚本,它会遍历该列表,使用具有互联网访问权限的工作站/服务器拉取所有镜像。它将输出一个 tar 文件,可以将该文件物理复制到一个 air-gapped 服务器。最后一步是运行 rancher-load-images.sh,它将所有镜像推送到你的私有注册表中。
该列表会在每次 Rancher 版本更新时进行更新,每次升级 Rancher 时都应运行此过程,以获取新的或更新的镜像。另外,一些客户有一台位于互联网和隔离环境之间的服务器/工作站,并选择修改此过程,在同一台服务器上同时执行这两个步骤。他们通过编辑rancher-save-images.sh并注释掉最后一行 docker save 来跳过 tar 文件的创建。然后,在 rancher-load-images.sh 中,他们删除了 docker load,因为镜像已经在服务器上。
注意
建议您保持相同的注册表结构,即 docker.io/rancher/rancher 应变为 registry.example.com/rancher/rancher。还建议将 Rancher 仓库设置为公开,这样任何人都可以在不进行身份验证的情况下拉取它们。这将使过程更加简单,并且能够模拟这些镜像的 Docker Hub。
Rancher 全局注册表
现在我们已经拥有了所有镜像,我们需要将 Rancher 指向私有注册表。对于此过程,我们需要遵循位于 rancher.com/docs/rancher/v2.6/en/admin-settings/config-private-registry/ 的步骤。基本思路是进入 Rancher UI 的 system-default-registry,并将值更新为您的私有注册表。假设您有一个需要凭证才能从 Rancher 仓库拉取镜像的私有注册表,您需要通过为每个集群添加凭证来采取额外的步骤。
更新 RKE/RKE2
现在您已经让 Rancher 使用新的私有注册表,接下来需要告诉 RKE 和 RKE2 也使用该注册表。对于 RKE,只需要更新 cluster.yaml 文件,加入 private_registries 部分并运行 rke up。您可以在 rancher.com/docs/rke/latest/en/config-options/private-registries/ 中找到该更改的详细信息。RKE2 也是一样,但如果需要,您还可以进行一些附加设置。您可以在 docs.rke2.io/install/containerd_registry_configuration/ 中找到详细信息。
注意
对于 RKE 和 RKE2 集群,这可以在集群创建后进行设置,但强烈建议您在实验室/沙箱集群中测试此过程,因为您可能会遇到防火墙规则、缺失镜像、配置错误的部署等问题,这些问题可能会破坏集群。因此,我们希望在将此过程应用到生产集群之前,先进行充分的测试,确保它非常稳定。
此时,我们应该让集群从我们的注册表拉取镜像,而不是从 Docker Hub 拉取。我们还讨论了如何在主机级别强制重定向所有镜像,以及如何在部署级别强制更改注册表。
概述
本章介绍了什么是 CI/CD 以及它是如何工作的。然后,我们深入探讨了设计和安装 Drone 来作为我们的 CI/CD 系统。接着,我们转向讨论镜像仓库及其不同类型。在此基础上,我们设计并安装了 Harbor 作为我们的私有注册表。最后,我们通过讲解如何配置 Rancher、RKE 和 RKE2 来使用这个新的私有注册表来结束本章内容。
在下一章中,我们将基于本章所学的内容,利用 Helm charts 将我们的部署提升到一个新水平。
第十七章:第十七章:创建和使用 Helm 图表
本章涵盖了 Helm,Helm 作为 Kubernetes 集群的包管理器的使用方式,以及它的工作原理。接下来我们将深入讨论如何从零开始创建一个 Helm 图表,之后将讲解如何将新创建的图表发布到 Kubernetes 集群。最后,我们将讨论如何获取一个公开可用的图表并根据需求进行定制。
本章我们将涵盖以下主要内容:
-
什么是 Helm 图表?
-
Helm 是如何工作的?
-
如何创建 Helm 图表?
-
部署 Helm 图表
-
定制一个公开的 Helm 图表
什么是 Helm 图表?
Helm 是一个用于 Kubernetes 部署的包管理工具。Helm 类似于 Debian/Ubuntu 系统中的 deb/dpkg 包管理器。从这个角度来看,Helm 图表是将一组 Kubernetes 部署文件打包成一个单一模板化文件。在本节中,我们将介绍为什么需要 Helm 以及它是如何在后台工作的。
在开始使用 Kubernetes 和 Rancher 时,一个常见的问题是:为什么我们需要 Helm? 要回答这个问题,我们需要了解 Helm 之前是如何进行部署的。那时我们会在所有集群中使用相同的设置、镜像等来部署 ingress-nginx。将其作为一个简单的 YAML 文件,通过 kubectl apply -f deployment.yaml 来部署可能是有效的。
在这个问题中,关键是:如果你的某个集群有所不同怎么办?例如,你可能有一个 Google Kubernetes Engine (GKE) 集群和一个 Rancher Kubernetes Engine (RKE) 集群,它们可能需要不同的镜像;也就是说,GKE 将使用公共的 Docker Hub 镜像,而你的本地 RKE 集群则需要从私有注册表中获取镜像。如果没有 Helm,你就需要两个不同的 YAML 文件,并且需要确保它们保持同步。当然,在实际应用中,你可能需要更多不同的部署文件,因此管理这些文件并保持同步可能会变得非常复杂。
有些人尝试通过编写脚本,在部署之前查找并替换部署中的值来解决这个问题——例如,你的主部署文件中可能会将镜像值设置为 ImageTagPlaceHolder。然后,脚本会查找这个值,并使用 sed 命令将其替换,例如: sed 's/ImageTagPlaceHolder/my-private-registry/g`。这种方法对于只更新几个值的情况有效,但当你需要做更复杂的定制时,这个过程就会出现问题。例如,你在 host 部分定义了 dev.example.com 的 ingress,但在生产环境中,你需要根据 SSL 证书定义多个主机——也就是 www.example.com、example.com 等等。用 Bash 脚本来做这些事情变得过于混乱,更重要的是,容易出错。
Helm 通过让你将部署文件定义为 YAML 文件,并使用变量和if语句来解决这个问题。关键在于,你不会在部署文件中硬编码像镜像标签这样的内容;相反,你将其设定为一个变量(即image标签)。然后,在部署时,你为这些变量提供值,Helm 会处理构建最终的 YAML 文件并将其发布到集群中。同时,Helm 还提供了在部署文件中定义 if 语句的能力,使我们能够根据用户和集群的输入修改部署。我们将在下一节中讲解这一工作原理。
Helm 是如何工作的?
我发现理解 Helm 工作原理的最佳方法是从了解目录结构开始,但首先,我们需要记住,大多数公开的 Helm 图表都是以 TGZ 文件的形式发布的,这是一种压缩的目录结构。Helm 的第一步是从仓库下载这个文件,并将其解压为图 17.1 中显示的目录结构。请注意,我们将在本章稍后的 自定义公开 Helm 图表 部分详细讨论公开 Helm 图表,但在本节中,我们将介绍这四个目录和文件的作用。然后,我们将把它们联系在一起,并讲解 Helm 如何使用它们来部署应用程序。
你可以在这里查看 Helm 图表目录结构的示例:

图 17.1 – Helm 图表目录结构
Chart.yaml 是一个定义整个图表的文件,包括图表的名称、版本、描述等内容。你可以在 图 17.2 和 github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch17/hello-world/Chart.yaml 中找到一个示例。正如我们所见,这个文件主要用于设置图表的元数据,包括图表的名称、描述、关键字、来源、维护者和图标。Rancher 使用这些数据来构建其目录页面,允许你通过关键字搜索,并查看每个仓库中图表的图标。Rancher 还通过在 catalog.cattle.io 路径下使用一组注解扩展这个文件,以添加如 catalog.cattle.io/certified: rancher 的数据,这告诉用户这是一个经过认证的图表,且涵盖在 Rancher 的 服务水平协议 (SLA) 和支持范围内。你可以在 github.com/rancher/charts/blob/release-v2.6/charts/rancher-monitoring/100.1.2%2Bup19.0.3/Chart.yaml 中找到 Rancher 监控图表的示例。
请查看这里展示的 Chart.yaml 示例:

图 17.2 – Chart.yaml 示例
templates 目录是 Helm chart 中最关键的部分之一,因为该目录是 Helm 存储应用程序所有模板文件的地方。需要注意的是,该目录中的所有文件应该是 YAML 文件。将其他类型的文件混入该目录已知会导致 Helm 出现奇怪的错误。默认情况下,该目录是扁平的,每种对象类型都有自己的文件——例如,您可能有一个名为 services.yaml 的文件,其中包含所有需要创建的服务记录,还有另一个名为 ingress.yaml 的文件。
需要注意的是,文件中的资源需要根据 YAML 标准用三个连字符(---)分隔。这对于每种类型有一个或两个资源的简单部署非常好,但将所有资源放在一个文件中,在大规模管理时可能会遇到挑战,因为 Helm/kubectl 的错误信息会指明行号,但该行号指的不是文件中的行号,而是该部分的行号。例如,假设您在一个文件中定义了 10 个 ingress,总文件大约有 100 行。如果您在该部分的第 2 行遇到第三个 ingress 的问题,错误信息只会显示 错误位于第 2 行,但您不知道哪一行是第 2 行。由于这个原因,通常会将模板文件拆分到嵌套目录中。
例如,您可以为每种资源类型创建一个文件夹,然后为每个资源创建一个文件(请参见图 17.3以了解示例)。另一个例子是按组件拆分我们的文件夹——我们可能为前端应用程序创建一个文件夹,然后将构成该组件的所有资源放在其中:部署、服务、服务帐户、持久卷声明(PVC)等(请参见图 17.4)。我倾向于按组件对任何多层应用程序进行排序,这样当我在处理某个组件时,该组件的所有内容都会集中在一个地方。这样做还可以轻松添加新组件,因为我只需克隆整个文件夹,然后进入文件夹并开始运行查找和替换命令,作用范围限定在该文件夹内。
在以下示例中,您可以看到在这个 Helm chart 中,我们按组件类型进行分组,这意味着所有部署都在同一个文件夹中,所有 ingress 也在同一个文件夹中,依此类推:

图 17.3 – 按资源类型组织的 Helm 文件夹结构
在以下示例中,你会看到我们并不是按类型来分组,而是按应用程序的不同部分来分组。例如,我们有apiserver文件夹,它包含了构成该组件的所有资源——即部署、入口和服务。然后,我们对应用程序的每个组件重复这个过程——在这个例子中是前端和报告服务。就个人而言,我是这样构建 Helm 图表的,因为我觉得这种方式更容易阅读,同时也使得将一个组件从一个应用程序复制到另一个应用程序变得更加容易。例如,我可能会将前端的frontend文件夹重用到一个新的图表中,并开始根据新的应用程序进行定制。按资源类型分组的文件夹结构则要求我进入每个文件夹,查找构成该组件的所有资源。这个过程当然容易出错,因为我可能会漏掉某些资源。
你可以在这里查看结构:

图 17.4 – 按组件分组的 Helm 文件夹结构
作为最终用户,你将最常使用的文件是values.yaml;因为这个文件设置了图表的默认值。当然,这些值可以包括设置全局值,如仓库和镜像拉取策略,也可以包括自定义设置,如设置将成为秘密的数据库连接字符串。在下一节中,我们将更详细地介绍这个文件,但需要理解的关键点是,这个文件是图表的配置文件,因此在这个文件中有良好的文档记录非常重要。此外,这个文件是可定制的,意味着你可以添加自己的部分和设置。例如,你可以为每个组件添加一个部分——即frontend、apiserver、reporting等,如以下截图所示:

图 17.5 – 每个组件的 Helm 自定义值
最后,另一个重要的目录是 charts。这个目录通过允许你为其他图表定义依赖关系,大大扩展了 Helm 的功能。例如,如果我们回到我们的三层应用程序,并且需要一个 MySQL 数据库作为我们部署的一部分,我们不想编写和管理 Kubernetes 中运行的数据库可能需要的所有不同资源文件,特别是当别人已经为我们完成了这项工作时。因此,在 charts 目录中,我们可以将官方 MySQL 图表作为子文件夹添加。这个子文件夹的处理方式与普通的 Helm 文件夹相同,这意味着它包含 Chart.yaml、templates、values.yaml 等。你当然可以使用 helm dependency 命令在 Chart.yaml 文件中定义依赖关系,该命令负责下载和更新 charts 目录中的子图表,这是推荐的方法,而不是手动将外部仓库的更改合并到你的 Helm 图表中,因为那样做容易出错且浪费时间。
需要记住的是,如果你需要在 values.yaml 文件之外自定义图表——即,更改模板文件以绕过一个 bug——那么 helm dependency 命令会覆盖你的更改,因此建议在这种情况下手动将图表下载到该文件夹中。
到这个阶段,我们已经理解了组成 Helm 图表的不同部分。现在,是时候进行部署演练,理解 Helm 的工作原理。以下是发生的过程:
-
Helm 取
values.yaml文件并将其作为变量的基础,此时,任何使用--set选项的命令行值将覆盖这些变量。这里需要理解的关键是,任何命令行设置都会覆盖values.yaml中的内容,并且仅用于没有通过命令行选项设置的配置。 -
Helm 然后开始处理模板文件,将所有变量替换为静态值。在这个步骤中,任何
if语句都会被解析。 -
Helm 会解决所有的流程控制,包括
if/else语句。这使得你可以使用if语句来添加或删除配置文件中的部分内容。例如,你可能会将入口配置包装在if语句中,从而可以通过values.yaml文件启用或禁用它。请注意,对于入口配置,这在大多数公共图表中非常常见。另一个最常见的if语句是关于 Kubernetes 版本的,例如 Kubernetes 的if语句,如下所示:如果 Kubernetes 版本是 v1.19 或以下,使用这个部分;否则,使用这个部分。这样可以使你的图表支持广泛的 Kubernetes 版本。你可以通过查看 Rancher 官方图表模板github.com/rancher/rancher/blob/release/v2.6/chart/templates/ingress.yaml来查看这一示例,或者查看以下截图。我还建议你查看 Helm 的官方文档,网址为helm.sh/docs/chart_template_guide/control_structures/:

图 17.6 – Rancher 服务器 Helm 图表入口示例
- Helm 已经准备好开始对你的集群进行更改,但需要注意的是,如果你进行的是隔离环境部署,它将只是将所有 YAML 文件输出到一个目录中。你可以在 Rancher 安装文档中找到这种部署的示例,文档位于
rancher.com/docs/rancher/v2.6/en/installation/other-installation-methods/air-gap/install-rancher/。但如果你执行的是标准安装,Helm 将按照一定顺序开始对集群应用更改,这个顺序可以在github.com/helm/helm/blob/release-3.0/pkg/releaseutil/kind_sorter.go#L27找到。
理解这一步骤的关键是理解 Helm 使用一组所有权标签/注解来确定哪些对象属于该 Helm 发布管理。Helm 会自动为它创建的每个资源应用app.kubernetes.io/managed-by:标签和meta.helm.sh/release-name以及meta.helm.sh/release-namespace注解。如果这些标签或注解缺失或与 Helm 的预期不同,Helm 会抛出错误,显示如以下截图所示的错误信息。如果你遇到此错误,说明你的图表之间存在冲突——即两个图表试图拥有相同的对象,这是不支持的。要解决这个问题,你需要更新标签和注解,确保它们与正确的发布版本匹配,修改你的图表以避免未来的冲突:

图 17.7 – Helm 所有权错误信息
- 在 Helm 执行相当于
kubectl apply的操作后,你的应用程序应该会启动。你可以运行一个安装后钩子,执行一个作业或创建一个 ConfigMap/Secret,大多数图表会运行一个作业,可能会循环地 ping 它们的应用程序,直到它上线为止,届时作业会成功完成。你可以在官方文档中阅读更多关于此过程的信息,链接:helm.sh/docs/topics/charts_hooks/。
现在我们理解了安装是如何工作的,唯一需要注意的就是 Helm 升级,它遵循相同的流程,但允许已存在的对象不会报错。然而,我通常建议使用 helm upgrade –i 和 --install 标志来运行 Helm,这样你可以使用相同的命令来安装和升级 Helm 图表。
到此,我们已经理解了 Helm 图表的工作原理。在接下来的部分,我们将创建一个 Helm 图表,在之后的部分中,我们将部署该图表。
我如何创建一个 Helm 图表?
Helm 通过使用 helm create mychart 命令使创建图表变得非常简单。
该命令将创建运行基本 Helm 图表所需的所有基础文件。你可以在此处查看示例输出:

图 17.8 – helm create mychart 命令创建一个示例图表
默认情况下,此命令将创建一个与图表同名的目录。当将 Helm 图表添加到 Git 仓库时,通常会将该目录重命名为 chart,或者如果该应用程序将来自同一仓库的多个图表,你可能想要创建一个 charts 目录,并将新图表移动到该目录中,保持名称简洁易懂。当然,这只是个人偏好,一些团队选择将他们的 Helm 图表移动到自己的仓库中。
到这一点,你的工作是将部署文件移到模板文件中。这是你需要设置所有类似 {{ .Values.image.repository }} 的变量的地方。这个变量意味着从根 values 文件开始。然后,进入 image 部分,获取仓库 undertake 部分的值。当然,你可以在模板文件中硬编码这些值,但强烈建议尽可能避免硬编码值,并将这些值定义在 values.yaml 文件中。这对于任何公共 Helm 图表都是必须的,因为总会有人需要根据他们的环境自定义这些值,而硬编码这些值只会让他们更加困难。这也是默认模板文件有很多设置已定义的原因之一。例如,nodeSelector 和 tolerations 设置已经配置好,尽管有相当多的人根本不会使用这些设置。
当然,默认模板文件并没有涵盖所有不同类型的资源,因此你可能需要创建自己的资源文件。我通常遵循的过程是找到一个包含所需资源类型的公共 Helm 图表,然后简单地复制粘贴并修改它以满足我的需求。当然,在你创建图表时,你还需要对其进行测试,这将在下一节中讲解。
部署 Helm 图表
正如我们在上一节中所讲,现在我们已经创建了 Helm 图表,需要将其部署到集群中。这可以通过两种不同的方式进行,第一种是本地图表,第二种是远程图表——我们将在本节中讨论这两种方式。
在发布之前,我们需要下载本地 Helm 图表的文件,这些文件在测试新图表时通常会用到。你会运行类似 helm upgrade --install mychart ./chart --namespace mynamespace -f ./chart/values.yaml 的命令。此命令的关键部分是 ./chart/,它告诉 Helm 图表所在的位置——在此示例中,它位于 ./chart/ 目录中。这个目录应该是图表的根目录,也就是 chart.yaml 文件、values.yaml 文件等所在的地方。这种安装方式在持续集成/持续部署(CI/CD)流水线中也很常见,在这种场景下,你希望使用 Helm 来模板化部署,但不需要将它们发布给公众或最终用户使用。一个例子是在以下截图中显示的 Drone 流水线中使用的。正如你所看到的,它使用了 Drone 构建号的覆盖值来设置镜像标签,并将 Ingress 主机作为流水线级别设置的变量传递:

图 17.9 – 在 Drone 流水线中本地安装 Helm
例如,考虑使用图 17.9所示的 Helm 命令的 Drone 流水线。在此步骤中,我使用的是一个我自己创建的自定义镜像,名为supporttools/kube-builder,它处理设置 kubeconfig 并包括一些标准工具,如kubectl和helm。你可以在以下截图中看到它的示意图:

图 17.10 – kube-builder 示例 Drone 步骤
你可以在 github.com/PacktPublishing/Rancher-Deep-Dive/tree/main/ch17/drone-pipeline-example 找到完整的示例流水线。
另一种 Helm 部署类型是远程图表。主要区别在于你将指向一个存储库(Git 或 helm repo update 命令)。在下一节中,我们将讲解如何自定义公共 Helm 图表。
自定义公共 Helm 图表
在使用公共 Helm chart 时,最终每个人都必须做的一项任务就是根据需要自定义它们,添加一些功能或设置,或者——在更常见的情况下——修复一个 bug。例如,一些公共 Helm charts 并没有包含支持Pod 安全策略(PSP)所需的设置,因为企业和高安全性环境基本上是唯一使用 PSP 的地方。这是一些社区成员没有进行测试的内容。当然,有很多方法可以对公共 Helm chart 进行这些修改。
但我推荐的主要方法是:先 fork GitHub 仓库,创建一个新分支,并应用任何必要的更改。在这个例子中,你需要在部署中添加一个部分,以配置部署文件中的securityContext部分,并在values.yaml文件中添加所需的rbac.enable=false和/或psp.enabled=false,以便用户根据需要启用或禁用此功能。然后,一旦你做出了这些更改,你需要创建一个pull request(PR)将其合并到上游仓库中。在此期间,你可以使用 Helm 的本地安装选项进行部署。
这个想法是在 PR 合并后回馈给开源社区。切换回公共图表只需要通过替换本地路径和仓库名称来简单编辑 Helm 命令。当然,确保在 PR 请求中更新文档和命令示例。
总结
本章介绍了 Helm 是什么,它如何将模板文件与一组值结合,创建 Helm 可以发布到集群的最终部署文件。然后我们深入探讨了如何创建一个 Helm chart 以及如何为方便使用而构建 chart 的最佳实践。接着,我们讲解了如何安装我们新创建的 chart,并将这一过程集成到 Drone 管道中。最后,我们讨论了公共 Helm chart 的主题,以及如何自定义它们。
下一章将涵盖集群资源管理和容量规划;我们都知道,运行开发运维(DevOps)工作负载时,资源消耗很容易失控,下一章将介绍如何在 Kubernetes 中监控和控制成本。
第十八章:第十八章:资源管理
在本书的最后一章中,我们将讨论资源管理主题,其中包括几个内容。首先是 Pod 资源限制和配额,它允许你在 Pod 层面控制资源的使用。我们将讨论这些限制如何在集群中应用和强制执行。然后,我们将逐步深入,讨论命名空间限制和配额的主题,接着再讨论 Rancher 项目限制的工作方式。最后,我们将讲解如何使用 kubecost 监控我们的 Kubernetes 成本变化。
在本章中,我们将涵盖以下主要内容:
-
如何为 Pod 应用资源限制和配额
-
如何计算命名空间的限制/配额
-
如何使用像 Kubecost 这样的工具跟踪使用情况和成本变化
让我们开始吧!
如何为 Pod 应用资源限制和配额
Kubernetes 集群中的物理资源是有限的。资源的衡量标准是每个工作节点分配的 中央处理单元 (CPU) 核心数或 随机存取存储器 (RAM) 的数量。例如,你可能有 10 个工作节点,每个节点 4 个核心,这意味着该集群可以使用 40 个核心。这个集群是可以接受的,直到你开始耗尽资源。正如我们所知,CPU 和内存是有限的,因此我们不能仅仅通过不断添加资源来扩展集群。
在 Kubernetes 中,默认情况下,所有工作负载和命名空间都没有资源限制,意味着没有任何东西可以阻止一个应用程序消耗集群中的所有 CPU 和内存。例如,一个应用程序团队可能会发布一个新工作负载,其中 水平 Pod 自动伸缩器 (HPA) 被错误配置,设定了非常高的上限。需要注意的是,HPA 不允许设置无限制的最大副本数,但没有任何限制阻止你将其设置得非常高。如果 HPA 的指标设置得过低,Kubernetes 会开始扩展工作负载,直到达到最大副本数或者集群资源耗尽。当然,这可能会导致 Kubernetes 集群宕机,尤其是在多个环境之间共享集群时——即在非生产环境与生产环境之间,或在应用程序团队之间共享集群时。我们不希望一个应用程序/团队导致另一个团队的宕机。
为了保护我们的集群,我们需要为工作负载和命名空间设置资源限制和配额,以防止此类宕机事件的发生。我们在两个层级上进行设置。
第一个层级是在工作负载/Pod 层面,我们希望在此设置资源限制和请求。这适用于所有工作负载类型(部署、状态副本集、定时任务、守护进程集等);然而,资源限制并不是应用在工作负载层面,而是应用在 Pod 层面。如果我为部署设置一个核心的限制,那么这个限制适用于该部署中的每个 Pod,意味着每个 Pod 可以使用一个核心。你不能在工作负载层面设置资源限制;例如,你不能限制一个工作负载中的所有 Pod 总共只使用一个核心。
这是一个YAML Ain't Markup Language(YAML)输出的 Pod,其中我们要关注的是资源部分。在该部分中,你会看到我们设置了请求和限制。在这个例子中,我们设置了 CPU 请求(最小值)为四分之一核心,限制(最大值)为半个核心。对于内存,我们设置了请求 64 Mebibytes(Mi)和限制 128 Mi:

图 18.1 – Pod 资源请求和限制 YAML 示例
看一下前面的示例,我们要重点关注resources部分。在这个部分中,我们为这个 Pod 定义了请求和限制。request值设置了这个 Pod 所需的资源。这个示例告诉 Kubernetes,Pod 需要 64 Mi 的内存和四分之一核心(1,000 kube-scheduler使用这些值来查找 Pod 调度过程中所需的可用资源节点。例如,如果我们在 Pod 内运行一个数据库,我们可能会将内存请求设置为 16 kube-scheduler构建其候选节点列表时,它会过滤掉没有所需资源的节点。如果一个 Pod 请求的内存超过了任何节点可用的内存,它将卡在调度中。需要注意的是,Kubernetes 不会重新平衡集群以为这个 Pod 腾出空间。一个名为descheduler的非官方工具,位于github.com/kubernetes-sigs/descheduler,尝试做到这一点。一旦 Pod 被调度到节点上,kubelet 将保留该节点上的请求资源。
资源部分的另一个内容是limits。这里我们设置了这个 Pod 可以使用的节点资源。例如,我们可能只允许这个 Pod 使用 1 个free -g命令,你会看到可用的内存数量会显示在节点上。如果节点有 16 GB 的空闲内存,那么 Pod 将拥有 16 GB 的空闲内存。这就是为什么为你的应用和 Pod 设置内存限制至关重要的原因。
一个经典的例子是 Java 堆大小,因为旧版本的 Java(在 8u191 之前)并不意识到容器的内存和 CPU 限制。Java 会超出其内存和 CPU 限制,因为 Java 认为它有更多的资源可用,因此像垃圾回收这样的操作不会运行以减少内存使用。Red Hat 发布了一篇关于这个话题的精彩博客,详细探讨了这个问题,以及 Java 10 是如何通过+UseContainerSupport设置(默认启用)解决这个问题的。你可以在developers.redhat.com/blog/2017/03/14/java-inside-docker找到这篇博客。
但回到内存限制,kubelet/Docker 无法回收内存——即无法从 Pod 中回收内存;只有应用程序才能释放已使用的内存。kubelet/Docker 只能在top等程序中记录WAIT时间。
Kubernetes 通过添加BestEffort类扩展了资源请求和限制。这个优先级类型用于批处理或报告的 Pods,或者用于其他可以随时停止而不会影响最终用户的任务。下一个类是Burstable,其主要区别在于它允许 Pod 使用超过定义限制的资源,但前提是节点有可用资源。这个类的典型使用场景是当 Pod 相对静态时,比如数据库,因此我们希望允许它在短时间内使用更多的资源。然而,同时我们不想使用 statefulsets,因为这样做意味着我们无法在集群中移动这个 Pod。另一个主要原因是应用程序使用内存会话的情况,如果 Pod 宕机会导致中断。如果该 Pod 被驱逐,所有会话会丢失,用户必须重新登录。接下来的部分将介绍命名空间限制和配额如何在 Pod 的请求和限制之上构建。
如何计算命名空间的限制/配额
设置所有 Pod 的 CPU 和内存请求/限制的一个好处是,您可以定义命名空间的限制和配额,这允许您指定在命名空间中运行的所有 Pod 总共使用的内存和 CPU 数量。这在预算集群资源时非常有用;例如,如果应用程序A为其生产环境购买了 16 个 CPU 和 64GB 的 RAM,您可以限制他们的命名空间,确保他们不会消耗超过他们所支付的资源。当然,这可以通过两种模式来实现,第一种是硬限制,这将阻止该命名空间中所有新 Pod 的创建事件。如果我们回到之前的例子,应用程序团队为我们的集群购买了 64GB 的 RAM。假设您有四个 Pod,每个 Pod 的限制是 16GB 的 RAM。当他们尝试启动第五个 Pod 时,它将卡在调度中,直到配额增加或该命名空间中的其他 Pod 释放空间,CPU 的限制和请求也会以相同的方式处理。
当然,命名空间可以有限制和请求,就像 Pod 一样,但理解限制和请求是如何计算的非常重要。kube-scheduler简单地将命名空间下所有 Pod 的限制和请求加起来,这意味着它不会使用当前的度量标准来决定 Pod 是否可以在命名空间中调度。需要注意的是,这仅适用于硬限制。软限制使用度量服务器来计算每个 Pod 当前使用的资源。
大多数人遇到的最大问题是允许没有请求和限制的 Pods 进入集群,就像硬限制的情况一样。这些 Pods 不需要参与计算,因为它们的值为零。另一方面,只要指标服务器运行,软限制适用于所有 Pods。因此,通常建议为你的命名空间设置硬限制和软限制。
另一个重要的部分是理解,限制和配额并不是作为命名空间定义的一部分(即命名空间的 YAML 文件)来定义的,而是通过 ResourceQuota 类型来定义的,相关示例可以在这里找到:

图 18.2 – 带有更多选项的 ResourceQuota YAML 示例
不过,其中一个很酷的地方是你可以设置的不仅仅是 CPU 和内存的配额,还可以限制诸如 图形处理单元 (GPUs)、Pods、负载均衡器等资源。如果你查看 图 18.2,我们为这个命名空间指定了仅允许四个 Pods。对于大多数集群/环境来说,这个数目非常低,但需要注意的是没有免费的午餐。例如,一个拥有 100 个 Pods 和 64 GB 总内存的命名空间,与另一个仅有四个 Pods 却使用相同内存的命名空间,它们的工作负载是不同的。100 个 Pods 给节点和集群管理服务带来的负担要远远大于四个 Pods。同样,存储大量数据的命名空间,比如存储在 secrets 中的数据与存储在 configmaps 中的数据也是不同的,因为 secrets 通常是加密存储的,并存储在 kube-apiservers 的内存中。由于它们是加密的,因此通常无法压缩,而 configmaps 是以纯文本格式存储的,通常是可以压缩的。用户常常为命名空间设置的另一个限制是负载均衡器。在许多云环境中,当你在 Kubernetes 中部署负载均衡器时,通常是在云服务提供商中部署负载均衡器,这会产生费用。
注意
这涉及的是一个第四层负载均衡器,而不是一个入口,通常它是一个共享资源。
Rancher 的一个优点是它建立在命名空间限制和配额之上,允许你定义项目级别的限制和请求。这使你能够为一个可能拥有多个命名空间的团队定义限制。一个经典的例子是非生产集群,在这种集群中,应用程序团队可能会购买 X 数量的 CPU 和 RAM,然后选择如何在不同环境中分配这些资源。
例如,他们可能会将一半的资源分配给 DEV 和 QAS,但在负载测试期间,他们可能希望关闭 DEV 和 QAS 命名空间,将这些资源转移到TEST命名空间,测试完成后再恢复。作为集群管理员,只要他们保持在项目限制范围内,你不需要关心或进行任何更改。值得注意的是,项目是 Rancher 对象,因为下游集群并不知道什么是项目。Rancher 控制项目,并使用命名空间标签、注释和 Rancher 控制器来同步 Rancher 和下游集群之间的设置。
在下一节中,我们将介绍如何使用工具,如 Kubecost,基于资源配额来构建,允许你进行例如成本回收和费用追踪等操作,以便为集群中的开销提供支持。
如何使用如 Kubecost 等工具跟踪使用情况和成本随时间变化的情况
Kubernetes 的一个关键特点是它使应用团队能够快速移动,并且默认情况下几乎没有限制地消耗资源。这意味着,在 Kubernetes 的初期阶段,许多环境往往会有大量开支。一个完美的例子是在第十三章中介绍的内容——Kubernetes 中的扩展,允许你自动扩展集群和工作负载。这意味着应用团队可能会做出一些改变,导致你的成本显著增加,而你直到账单到来时才知道,而此时你需要回去查找究竟发生了什么变化,当然,随着 Kubernetes 使用一段时间后,资源的回收将变得非常困难。
我们可以通过使用 Kubecost 来解决这个问题,Kubecost 是一款开源的成本监控和报告工具。请注意,Kubecost 有免费社区版和收费的商业产品,后者在开源项目的基础上进行了扩展。Kubecost 连接你的云提供商——如Amazon Web Services(AWS)、Azure 或Google Cloud Platform(GCP)——以获取资源的当前成本,然后将该成本与集群中消耗这些资源的 Pods 关联。例如,你在一些节点上使用最新的高性能 CPU,而其他节点使用较旧的(更便宜的)CPU 版本。Kubecost 允许你将不同的成本与 Pod 相关联。因此,你可以选择切换到更新的高性能 CPU,因为你的应用程序/Pods 在它们上运行得更好,并且比慢速 CPU 使用更少的资源。
Kubecost 使用 Helm 图表或 YAML 清单在监控集群中进行部署。目前,Kubecost 不支持远程监控,这意味着它必须在您环境中的每个集群上进行部署。此外,Kubecost 使用 Prometheus 来收集集群中的度量数据,这些数据可以作为 Helm 图表的一部分进行部署;Grafana 也适用于展示仪表盘。Kubecost 有自己的网络度量收集器,用于收集不同类型流量的流量费用;例如,某些云提供商对于跨区域流量的收费高于本地流量。最高的费用是从公共互联网出口的流量,费用可能非常昂贵。俗话说,AWS 希望您将数据带入他们的环境时几乎不收费,但将数据取回时却会收取高昂费用。
这些步骤可以像执行helm install命令一样简单,该命令使用一个令牌将您的安装与 Kubecost 账户关联,您可以在下图中找到一个示例。Helm 图表选项的完整列表可以在github.com/kubecost/cost-analyzer-helm-chart/blob/master/README.md#config-options中找到。这些选项允许您自定义部署:

图 18.3 – 具有更多选项的 ResourceQuota YAML 示例
在上述示例中,我们使用默认设置安装 Kubecost,这将部署 Prometheus、Node Exporter 和 Grafana 的独立实例。强烈建议重用当前 Rancher 监控的global.prometheus.enabled=false、prometheus.kube-state-metrics.disabled=true和prometheus.nodeExporter.enabled=true选项。我还建议阅读 Kubecost 的文档,网址为guide.kubecost.com,该文档包括添加外部资源,如简单存储服务(S3)和关系型数据库服务(RDS)。此外,他们的指南会引导您完成允许 Kubecost 查询账单信息以获取任何自定义定价的步骤;例如,AWS 的大型客户可以在某些资源类型上获得账户的断开连接。
最后,Kubecost 有一个实验性的托管版本,可以在 guide.kubecost.com/hc/en-us/articles/4425132038167-Installing-Agent-for-Hosted-Kubecost-Alpha- 找到。此外,Kubecost 不是唯一的解决方案,像 Datadog 这样的产品也可以提供成本监控和报告。关键在于,我们希望追踪成本变化,以便知道何时发生变化,当然,由于我们了解每个 Pod 的成本,我们可以为管理层生成报告,展示资金流向,让他们能够回头追究应用团队的资源费用。我们这么做是为了信息技术(IT)支出的预算规划,使我们能够主动应对,而不是被动反应。
总结
本章介绍了 Pod 限制和请求是什么,以及它们如何通过 Kubernetes 强制执行,包括它们是如何计算的。然后,我们介绍了如何在命名空间级别使用资源配额,限制这些配额到团队/应用级别,同时限制其他资源,如负载均衡器、密钥等。接着,我们通过介绍 Rancher 项目进一步深入,允许我们跨命名空间设置限制。最后,我们讨论了如何使用 Kubecost 监控 Kubernetes 成本变化,包括如何安装和自定义它,但更重要的是,我们探讨了为什么要追踪和监控成本。此外,我们还介绍了一些其他解决方案,如 Datadog。
我们共同走过的旅程即将结束。我想祝贺你完成这本书,同时感谢你花时间了解 Rancher 和 Kubernetes。最后一点要提醒的是,Rancher 和 Kubernetes 是一个不断发展的生态系统。最好持续不断地学习,Rancher 官方活动是了解 Rancher 和 Kubernetes 新功能的绝佳资源。你可以通过 rancher.com/events 了解更多内容。

订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,以及行业领先的工具,帮助您规划个人发展并推进您的职业生涯。欲了解更多信息,请访问我们的网站。
第十九章:为什么订阅?
-
通过来自 4,000 多位行业专业人士的实用电子书和视频,减少学习时间,增加编程时间
-
使用特别为你定制的技能计划提升你的学习效果
-
每月获取一本免费的电子书或视频
-
完全可搜索,便于访问重要信息
-
复制和粘贴、打印和收藏内容
你知道 Packt 提供所有已出版书籍的电子书版本吗?PDF 和 ePub 文件可用。你可以在 packt.com 升级到电子书版本,并且作为纸质书籍的客户,你有权享受电子书折扣。有关更多详情,请通过 customercare@packtpub.com 联系我们。
在 www.packt.com 上,你还可以阅读一系列免费的技术文章,订阅各种免费的新闻通讯,并获得 Packt 书籍和电子书的独家折扣和优惠。
你可能喜欢的其他书籍
如果你喜欢这本书,你可能对 Packt 出版的其他书籍感兴趣:
)
Kubernetes 圣经
Nassim Kebbani | Piotr Tylenda | Russ McKendrick
ISBN: 9781838827694
-
使用 Kubernetes 管理容器化应用
-
了解 Kubernetes 架构及其各个组件的职责
-
在 Amazon Elastic Kubernetes Service、Google Kubernetes Engine 和 Microsoft Azure Kubernetes Service 上设置 Kubernetes
-
使用 Helm charts 部署 Prometheus 和 Elasticsearch 等云应用
-
探索 Pod 调度和集群自动扩展的高级技术
-
了解 Kubernetes 中流量路由的可能方法
Go for DevOps
John Doak | David Justice
ISBN: 9781801818896
-
了解 Go 语言的基本结构,开始你的 DevOps 之旅
-
与文件系统交互,读取或流式传输数据
-
通过 REST 和 gRPC 与远程服务进行通信
-
探索在 DevOps 环境中可以使用的编写工具
-
在 Go 中开发命令行操作软件
-
使用流行框架部署生产软件
-
创建简化 CI/CD 流程的 GitHub Actions
-
使用 Slack 编写 ChatOps 应用程序,以简化生产可见性
Packt 正在寻找像你一样的作者
如果你有兴趣成为 Packt 的作者,请访问authors.packtpub.com并立即申请。我们已经与成千上万的开发者和技术专业人士合作,帮助他们与全球技术社区分享见解。你可以进行一般申请,申请我们正在招募的具体热门话题的作者,或者提交你自己的想法。
分享你的想法
现在你已经完成了Rancher 深度剖析,我们很想听听你的想法!如果你是在亚马逊购买的这本书,请点击这里直接前往亚马逊评论页面,分享你的反馈或留下评论。
你的评论对我们以及技术社区非常重要,它将帮助我们确保提供优质的内容。
你可能会喜欢的其他书籍
你可能会喜欢的其他书籍


浙公网安备 33010602011771号