Kubernetes 拥有丰富的 API,提供了构建和运行分布式系统可能需要的大部分功能。然而,该API是特意通用的,针对的是 80% 的用例。利用 Kubernetes 存在的丰富的附加组件和扩展的生态系统,可以为您的集群的用户添加重要的新功能并实现新的体验。您甚至可以选择实现自己的自定义附加组件和扩展,以适合您公司或环境的特殊需求。

Kubernetes 的扩展点

有许多不同的方法来扩展 Kubernetes 集群,每种方法都提供了一组不同的功能和额外的操作复杂性。下面的章节将详细描述这些不同的扩展点,并深入了解它们如何扩展集群的功能,以及这些扩展的额外操作要求。

这四种类型的扩展点是:

  • 用于自动化的集群守护进程
  • 用于扩展功能的集群助理
  • 延长 API Server 的生命周期
  • 增加更多的 API

当然,关于其中一些分类的真相是,它们有些武断,而且有不同的扩展可以结合多种可扩展性,为集群提供额外的功能。这里描述的分类旨在帮助指导你讨论和规划扩展 Kubernetes 集群。它们是指导方针 — 而不是硬性规则。

集群守护进程

集群可扩展性的最简单和最常见的形式是集群守护程序。就像运行在单台机器上的守护进程或代理为单台机器增加自动化功能(例如,日志滚动)一样,集群守护进程为集群增加了自动化功能。集群守护进程有两个定义特征。代理需要运行在 Kubernetes 集群本身,代理需要向集群添加功能,这些功能自动提供给集群的所有用户,而无需他们进行任何操作。

为了能够将集群守护程序部署到它所帮助管理的 Kubernetes 集群上,集群守护程序本身被打包成容器镜像。然后通过 Kubernetes 配置对象进行配置,并通过 DaemonSet 或 Deployment 在集群上运行。通常情况下,这些集群守护进程运行在一个专用的 Namespace 中,因此集群的用户无法访问它们,不过,在某些情况下,用户可能会将集群守护进程安装到自己的 Namespace 中。当需要监控、升级或以其他方式维护这些守护进程时,它们的维护方式与运行在 Kubernetes 集群上的其他应用程序完全一样。以这种方式运行代理更可靠,因为它们继承了所有相同的功能,这使得在 Kubernetes 中运行任何其他应用程序更容易。它也更一致,因为代理和应用程序都使用相同的工具进行监控和维护。

在下面的章节中,我们将探讨在 Kubernetes 集群上运行的程序可以扩展或增强该集群的其他方式。然而,集群代理或守护进程与其他扩展的区别在于,它们提供的功能适用于集群内或 Namespace 内的所有对象,无需额外的用户交互来启用它们。它们是自动启用的,用户往往在不知不觉中就能获得这些功能。

集群守护进程的用例

有许多不同类型的功能,你可能想自动提供给用户。一个很好的例子就是从暴露 Prometheus 的服务器中自动收集指标。当你在 Kubernetes 集群中运行 Prometheus 并配置它进行基于 Kubernetes 的服务发现时,它作为集群守护进程运行,并自动扫描集群中的所有 Pod,以获取它应该摄入的指标。它通过观察 Kubernetes API Server 来发现任何新的 Pod,因为它们来了又走了。因此,在 Kubernetes 集群内运行的任何应用,只要有 Prometheus 集群代理,就会自动收集到指标,而无需开发人员进行任何配置或启用。

集群守护进程的另一个例子是一个代理,它可以扫描部署在集群中的服务是否存在跨站点脚本(XSS)漏洞。这个集群守护进程又会观察 Kubernetes API Server,寻找新的 Ingress(HTTP 负载均衡器)服务何时被创建。当创建此类服务时,它会自动扫描服务中的所有路径,以查找 XSS 漏洞网页,并向用户发送报告。同样,因为它是由集群守护进程提供的,所以这个功能被使用集群的开发人员继承,而不需要他们甚至知道什么是 XSS,也不需要他们知道扫描正在发生,直到他们部署了一个有漏洞的服务。我们在本节最后看到如何构建这个例子。

集群守护进程很强大,因为它们增加了自动功能。开发者需要学习的东西越少,而是可以从环境中自动继承,他们的应用程序就越有可能是可靠和安全的。

安装集群守护进程

集群守护进程的安装是通过容器镜像和 Kubernetes 配置文件完成的。这些配置可能由集群管理员开发,由包管理器(如 Helm)提供,或由服务的开发者(如开源项目或独立软件供应商)提供。通常情况下,群集管理员使用 kubectl 工具在群集上安装群集守护进程,可能会附带一些额外的配置信息,如许可证密钥或要扫描的 Namespaces。安装完成后,守护进程立即开始在集群上运行,后续的任何升级、修复或删除守护进程都是通过 Kubernetes 配置对象进行的,就像其他应用一样。

集群守护进程的注意事项

虽然集群守护进程的安装通常是微不足道的 — 通常只是一个命令行调用,但添加这样一个守护进程所产生的操作复杂性可能相当大。集群附加组件的自动性质是一把双刃剑。用户很快就会依赖它们,因此集群守护程序附加组件的操作重要性可能很高。也就是说,虽然集群守护程序的部分价值来自于它们的透明性,但用户不太可能注意到它们的故障。例如,想象一下,你的安全机制是基于通过集群守护进程进行自动 XSS扫描,而这个守护进程却默默地卡住了。突然间,你的整个集群的所有 XSS 检测都可能被禁用。安装集群守护进程将这些系统的可靠性责任从开发人员转移到了集群管理员身上。一般来说,这样做是正确的,因为它集中了这些扩展的知识,而且它允许一个团队构建由大量其他团队共享的服务。但至关重要的是,群集管理员知道他们正在签署什么。你不能只是一时兴起或因为用户的请求而安装一个集群守护进程。你必须完全致力于在集群的生命周期内对该集群守护进程进行操作管理和支持。

示例:创建一个集群守护进程

创建一个集群守护进程并不需要太难。事实上,一个简单的 Bash 脚本,你可能会从一台机器上运行,可以很容易地转变为一个集群守护进程。例如,考虑以下脚本:

  1. #!/bin/bash
  2. for service in $(kubectl --all-namespaces get services | awk '{print $0}'); do
  3. python XssPy.py -u ${service} -e
  4. done

这个脚本列出了一个集群中的所有服务,然后使用一个开源的 XSS 扫描脚本来扫描每个服务并打印出报告。

要把这个脚本变成一个集群守护进程,我们只需要把这个脚本放在一个循环中(当然要有一些延迟),并给它一个报告的方式:

#!/bin/bash
# Start a simple web server
mkdir -p www
cd www
python -m SimpleHTTPServer 8080 &
cd ..

# Scan every service and write a report.
while true; do
  for service in $(kubectl --all-namespaces get services | awk '{print $0}'); do
    python XssPy.py -u ${service} -e > www/${service}-$(date).txt
  done
  # Sleep ten minutes between runs
  sleep 600
done

如果你把这个脚本打包到 Pod 中,并在你的集群中运行,你就会从 Pod 中得到一个 XSS 报告的集合。当然,要真正实现生产化,你可能还需要很多其他的东西,包括将文件上传到中央仓库,或者监控/报警。但这个例子表明,对于 Kubernetes 专家来说,构建一个集群守护进程并不一定是一项复杂的任务。一个小小的 Shell 脚本和一个 Pod 就是你所需要的一切。

集群助手

集群助手与集群守护进程十分相似,但与集群守护进程不同的是,守护进程的功能是自动为集群的所有用户启用的,而集群助手则需要用户提供一些配置或其他手势来选择加入助手提供的功能。集群助手不是提供自动体验,而是为集群的用户提供丰富而又容易访问的功能,但这是用户必须意识到的功能,必须提供适当的信息才能启用。

集群助手的用例

集群助手的用例一般是指用户想要启用某些功能,但启用这些功能的工作比必要的工作要难得多,慢得多,或者复杂得多,容易出错。鉴于这样的情况,助手的工作就是帮助实现这一过程的自动化,使其更容易、更自动,更不会出现 “剪贴” 或其他配置错误。助手在集群中简化繁琐或死板的任务,使其更容易消耗概念。

作为这样一个过程的具体例子,考虑一下在 Kubernetes 集群中为 HTTP 服务添加 SSL 证书所需的工作。首先,用户必须获得一个证书。虽然像 Let’s Encrypt 这样的 API 已经使这一点变得非常简单,但这仍然是一个不容易的任务,需要用户安装工具,设置服务器,并申请一个域名。然而,在获得证书之后,你仍然没有完成。你需要想办法把它部署到你的 Web 服务器中。一些开发人员可能会遵循最佳实践,在了解 Kubernetes Ingress 的情况下,制作一个 Kubernetes Secret,将证书与 HTTP 负载均衡器关联起来。但其他开发人员可能会采取简单(而且安全性大大降低)的路线,直接将证书烘焙到他们的容器镜像中。还有一些人可能会对这种复杂性嗤之以鼻,并决定他们的用例实际上并不需要 SSL。

无论结果如何,开发人员的额外工作 — 以及 SSL 的不同实现 — 都是不必要的风险。相反,添加一个集群助手来自动配置和部署 SSL 证书的过程,可以降低开发人员的复杂性,并可以确保集群中的所有证书都以遵循最佳实践的方式获得、部署和轮换。然而,为了正常运行,集群助手需要集群的最终用户的了解和参与,在这种情况下,证书的域名,以及通过集群助手将 SSL 连接到负载平衡器的明确请求。这样的助手是由开源的 cert-manager 项目实现的。

对于集群管理员来说,集群助手集中了知识和最佳实践,通过简化复杂的集群配置来减少用户提出的问题,并确保部署到集群的所有服务具有共同的外观和感觉。

安装集群助手

因为集群助手和集群守护进程之间的区别来自于交互模式 — 而不是实现,所以集群助手的安装与集群守护进程的安装大致相同。集群助手被打包成容器镜像,并通过标准的 Kubernetes API 对象(如 Deployment 和 Pod)进行部署。和集群守护进程一样,集群助手的维护、操作和移除都是通过 Kubernetes API 来管理的。

集群助手的注意事项

与集群守护进程一样,集群助手需要集群管理员承担操作责任。由于助手向最终用户隐藏了复杂性,这意味着最终用户最终不知道安装证书等任务的实际实现细节,因此助手的正确运作至关重要。终端用户由于缺乏经验和知识,不太可能自己实现类似的任务。然而,由于该功能是选入式的,用户更有可能注意到某些东西没有发挥作用。例如,用户申请了一个 SSL 证书,但它没有到达。然而,这并不意味着集群管理员的操作负担减轻了。你仍然应该主动监控和修复集群助手基础设施,但当事情出现问题时,有人更有可能注意到。

示例:创建一个集群助手

为了让这个问题更具体一些,让我们构建一个集群助手的例子,它可以自动为 Kubernetes Service 添加认证。这个助手的基本操作是,它持续扫描集群中的 Service 对象列表,寻找具有特定注解密钥的对象:managing-k8s.io/authentication-secret。预计这个键的值指向一个包含 .htpasswd 文件的 Kubernetes Secret。例如:

kind: Service
metadata:
  name: my-service
  annotations:
    managing-k8s.io/authentication-secret: my-httpasswd-secret
...

当集群助手找到这样的注解时,它会创建两个新的 Kubernetes 对象。首先,它创建了一个 Deployment,其中包含一个复制的 nginx Web 服务器 Pod。这些 Pod 采用注解中 Secret 引用的 .httpasswd 文件,并将 nginx 配置为反向代理,将流量转发到 my-service 上,但需要一个在 .htpasswd 文件中指定的用户和密码。集群助手还创建了一个名为 authenticated-my-service 的 Kubernetes 服务,将流量引导到这个认证层。这样一来,用户就可以将这个认证服务暴露给外部世界,并进行认证,而不用担心如何配置 nginx。当然,基本的认证是一个非常简单的例子。你可以很容易地想象扩展它以覆盖 OAuth 或其他更复杂的认证端点。

扩展 API Server 的生命周期

前面的例子是运行在你的集群之上的应用程序,但这种集群扩展的可能性是有限制的。更深层次的扩展来自于扩展 API Server 本身的行为。这些扩展可以直接应用于所有的 API 请求,因为它们是由 API Server 本身处理的。这就为您的集群提供了额外的可扩展性。

扩展 API Server 生命周期的用例

由于 API 生命周期扩展存在于 API Server 的路径中,因此您可以使用它们对服务创建的所有 API 对象实施要求。例如,假设您希望确保在集群中运行的所有镜像都来自于您公司的私有注册表,并保持一个命名惯例。例如,您可能希望所有镜像的形式为 registry.my-co.com/<team-name>/<server-name>:<git-hash>,其中 registry.my-co.com 是您公司运行的私有镜像注册表,<team-name><server-name> 是著名的团队和由这些团队构建的应用程序,最后,<git-hash> 是源控制提交哈希,表示映像是由哪个版本构建的。要求这样的镜像名称可以确保开发人员不会将他们的生产映像存储在公共的(未经认证的)镜像仓库上,而命名约定可以确保任何应用程序(例如,我们前面描述的 XSS 扫描器)可以访问发送通知所需的元数据。要求使用 git-hash 可以确保开发人员只从检查过的(因此是代码审查过的)源代码构建镜像,并且很容易从运行的镜像到它正在运行的源代码。

为了实现这个功能,我们可以注册一个自定义的准入控制器。准入控制器在 “请求的生命” 中进行了描述。它们负责确定一个 API 请求是否被接受(或准入)到 API Server。在这种情况下,我们可以注册一个准入控制器,它将为所有包含镜像字段的 API 对象(Pod、Deployment、DaemonSet、ReplicaSet 和 StatefulSet)运行。准入控制器会检查这些对象中的镜像字段,并验证它们是否符合刚才描述的命名模式,以及镜像名称的各个组件是否有效(例如,team-name 与一个已知的团队相关联,git-hash 是团队仓库的发布分支中的一个)。

安装 API 生命周期扩展

安装 API 生命周期扩展有两个部分。第一个是创建一个处理 Webhook 调用的 Service,第二个是创建一个新的 Kubernetes API 对象来添加扩展。为了创建处理来自 API Server 的 Webhook 调用的 Service,你需要创建一个可以适当响应的 Web 服务。有很多方法可以做到这一点,从云提供商提供的函数即服务(FaaS),到集群本身的 FaaS 实现(例如 OpenFaaS),再到用您最喜欢的编程语言实现的标准 Web 应用程序。根据对 Webhook 处理程序的要求和操作/成本要求,您可以做出不同的决定。例如,使用基于云的 FaaS 在设置和操作方面可能是最简单的,但每次调用都会花费一些钱。另一方面,如果你已经有一个开源的 FaaS 实现在你的集群上运行,那是一个运行 Webhook 的合理地方。但是,如果你只有几个 Webhook,安装和维护一个运营支持系统(OSS)FaaS 可能是得不偿失的工作,而运行一个简单的 Web 服务器可能是正确的选择。你需要根据自己的情况,做出这样的选择。

API 生命周期扩展的注意事项

从操作的角度来看,有两个操作上的复杂性需要考虑。第一个也是比较明显的复杂性来自于必须运行一个服务来处理 Webhook。如前所述,这里的操作责任各不相同,取决于你在哪里运行特定的 Webhook。无论如何,你需要监控你的 Webhook,至少要保证应用级的可靠性(例如,不返回 500),也许还要更多。第二个操作复杂性更微妙,它来自于让你自己的代码注入到 API Server 的关键路径中。如果你实现了一个自定义的准入控制器,并且它开始崩溃并返回 500,那么所有使用这个准入控制器的对 API Server 的请求都会开始失败。这样的事件可能会对你的集群的正确运行产生重大影响,它可能会导致各种各样的故障,从而影响部署在控制器之上的应用程序的正确运行。在不太极端的情况下,你的代码可能会给它所影响的 API 调用增加额外的延迟。这种增加的延迟可能会导致 Kubernetes 集群的其他部分(如控制器管理器或调度器)出现瓶颈,或者如果你的扩展偶尔运行缓慢或失败,它可能只是让你的集群看起来片面或缓慢。不管是什么情况,在 API Server 调用路径中放置代码都应该小心翼翼地进行,并进行监控、思考和规划,以确保不会出现任何意想不到的后果。

示例:API 生命周期的扩展

要实现一个准入控制器,你需要实现准入控制 Webhook。准入控制 Webhook 接收一个包含 AdmissionReview 的 JSON 体的 HTTP POST。您可能会发现更详细地探索类型定义是有帮助的。

让我们实现一个简单的 JavaScript 服务来接纳 Pod:

const http = require('http');

const isValid = (pod) => {
  // validate pod here
};

const server = http.createServer((request, response) => {
  var json = '';
  request.on('data', (data) => {
    json += data;
  });
  request.on('end', () => {
    var admissionReview = JSON.parse(json);
    var pod = admissionReview.request.object;

    var review = {
      kind: 'AdmissionReview',
      apiVersion: 'admission/v1beta1',
      response: {
        allowed: isValid(pod)
      }
    };
    response.end(JSON.stringify(review));
  });
});


server.listen(8080, (err) => {
  if (err) {
    return console.log('admission controller failed to start', err);
  }

  console.log('admission controller up and running.');
});

你可以看到,我们取一个 AdmissionReview 对象,从审查中提取 Pod,验证它,然后返回一个 AdmissionReview 对象,并填写响应。

然后,你可以通过创建注册来向 Kubernetes 注册这个动态准入控制器:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
  name: my-admission-controller
webhooks:
- name: my-web-hook
  rules:
  # register for create of v1/pod
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
  clientConfig:
    service:
      # Send requests to a Service named 'my-admission-controller-service'
      # in the kube-system namespace
      namespace: kube-system
      name: my-admission-controller-service

与所有 Kubernetes 对象一样,你可以用 kubectl create -f <web-hook-yaml-file> 实例化这个动态准入控制器注册。但在这样做之前要确保正确的 Service 已经启动并运行,否则后续的 Pod 创建可能会失败。

添加自定义 API

虽然我们已经向您展示了如何扩展现有的 API,但这些修改仅限于编译到 API Server 中的 API 集。有时,你想在你的集群中添加全新的 API 资源类型。特别是,尽管 Kubernetes 自带了一套丰富的 API 类型,你可以用它来实现你的应用,但有时你想在 Kubernetes API 中添加新的 API 类型。Kubernetes 中的这种动态类型能力允许你将一个现有的集群,与内置的 API 类型集合,如 Pod、Service 和 Deployment,并添加新的类型,这些类型的外观和感觉基本上与内置的类型完全一样。这种扩展性是相当灵活和强大的,但它也是最抽象和最复杂的理解。在最高层次上,你可以把这种扩展看作是向 Kubernetes API Server 添加新的 API 对象 — API 对象,这些对象看起来就像已经编译到 Kubernetes 中一样。所有处理现有 Kubernetes 对象的工具都适用于这些扩展。

添加自定义 API 的用例

由于自定义 API 类型非常灵活,它们实际上可以代表任何对象,因此有大量的潜在用例。下面的例子仅仅是这些可能性的表面。在前面的章节中,我们讨论了运行在 Kubernetes 之上的 FaaS 的开源实现。当一个 FaaS 被安装在 Kubernetes 之上时,它就会给集群增加新的功能。有了这些新功能,你需要一个 API 来创建、更新和删除 FaaS 中的功能。虽然你可以为这个 FaaS 实现自己的新 API,但你必须实现 Kubernetes API Server 已经为你实现的许多东西(授权、认证、错误处理等)。因此,将 FaaS 提供的功能建模为 Kubernetes API 扩展要容易得多。事实上,很多流行的开源 FaaS 都是这么做的。当你在 Kubernetes 上安装了一个,它就会转身向 Kubernetes API 注册新的类型。在注册了这些新类型之后,所有现有的 Kubernetes 工具(例如,kubectl)都会直接适用于这些新的函数对象。这种熟悉性意味着,在许多情况下,扩展集群的用户可能甚至不会注意到他们正在使用 API 扩展。

API 扩展的另一个流行用例是 CoreOS 倡导的 Operator 模式。有了 Operator,一个新的 API 对象被引入到集群中,以代表一个软件(以前是人)的 “操作员”(例如,数据库管理员)。为了实现这一点,在 Kubernetes API Server 上添加一个新的 API 对象,代表这个 “操作” 软件。例如,你可能会通过 API 扩展向 Kubernetes 添加一个 MySQLDatabase 对象。当用户创建一个新的 MySQLDatabase 实例时,操作者就会使用这个 API 对象来实例化一个新的 MySQLDatabase,包括适当的监控和在线监督来自动保持数据库的正常运行。因此,通过操作员和 API 扩展,你集群的用户可以直接实例化数据库,而不是碰巧运行数据库的 Pod。

自定义资源定义和聚合 API Servers

因为 API 生命周期和扩展 API 的过程在技术上都很复杂,所以 Kubernetes 实际上实现了两个独立的机制来向 Kubernetes API 添加新类型。第一种被称为 CustomResourceDefinition ,它涉及使用 Kubernetes API 本身向 Kubernetes 添加新类型。所有与新的自定义类型相关的存储和 API 服务都由 Kubernetes 本身处理。正因为如此,到目前为止,自定义资源定义是扩展 Kubernetes API Server 的一种更简单的方式。另一方面,由于 Kubernetes 处理了所有的可扩展性,这些 API 有几个限制。例如,很难对自定义资源定义添加的 API 进行验证和默认;但是,通过将自定义资源定义与自定义准入控制器相结合,就可以实现。

由于这些限制,Kubernetes 还支持 API 授权,即把资源的完整 API 调用,包括存储,委托给一个备用服务器。这使得扩展能够实现任意复杂的 API,但同时也带来了很大的操作复杂性,特别是需要管理自己的存储。由于这种复杂性,大多数 API 扩展都使用自定义资源定义。描述如何实现委托的 API Server 超出了本书的范围,本节剩余部分将介绍如何使用 CustomResourceDefinition 来扩展 Kubernetes API。

自定义资源定义的架构

实现自定义资源定义有几个不同的步骤。首先是创建 CustomResourceDefinition 对象本身。自定义资源是具有新类型定义的内置 Kubernetes 对象。在创建 CustomResourceDefinition 之后,Kubernetes API Server 会自己编程,在 API Web 服务器中加入新的 API 组和资源路径,以及新的处理程序,这些处理程序知道如何从 Kubernetes 存储中序列化和反序列化这些新的自定义资源。如果你想要的只是一个简单的 CRUD API,这可能就足够了。但在大多数情况下,当用户创建你的自定义对象的新实例时,你想实际做一些事情。要做到这一点,你需要将 Kubernetes CustomResourceDefinition 与一个控制器应用程序结合起来,该控制器应用程序监视这些自定义定义,然后根据用户创建、更新或销毁的资源采取行动。在很多情况下,这个应用服务器也是注册新的自定义资源定义的应用。图 13-1 示出了这个流程:
image.png
图 13-1 自定义资源定义的三个步骤示意图

对于大多数应用程序来说,自定义资源和控制器应用程序的组合通常是足够的,但您可能希望为您的 API 添加更多的功能(例如,预创建验证或默认)。要做到这一点,您还可以为您新定义的自定义资源添加一个准入控制器,如第 4 章所述,该控制器将自己插入 API 生命周期,并将这些功能添加到您的自定义资源中。

安装自定义资源定义

与所有的扩展一样,管理这些自定义资源所需的代码是在 Kubernetes 集群本身上运行的。自定义资源控制器被打包成容器镜像,并使用 Kubernetes AP I对象安装在集群上。由于自定义资源是一个比较复杂的扩展,一般来说,Kubernetes 配置由多个对象打包成一个 YAML 文件。在许多情况下,这些文件可以从提供扩展的开源项目或软件供应商那里获得。另外,它们也可以通过包管理器安装,比如 Helm。与所有其他扩展一样,自定义资源 API 扩展的监控、维护和删除都是使用 Kubernetes API 进行的。

:::warning 当一个 CustomResourceDefinition 被删除时,所有对应的资源也会从集群的数据存储中删除。它们无法被恢复。所以,在删除自定义资源时,一定要小心,在删除 CustomResourceDefinition 之前,一定要与该资源的所有终端用户进行沟通。 :::

自定义资源定义的注意事项

自定义资源的操作考虑因素一般与其他扩展相同。您正在向您的集群添加一个用户将依赖的应用程序,并且需要对其进行监控和管理。此外,如果您的扩展还使用了准入控制器,那么对于准入控制器的操作考虑也是一样的。然而,除了前面描述的复杂性之外,对于自定义资源定义来说,还有显著的额外复杂性 — 它们使用与所有内置 Kubernetes API 对象相关联的相同存储。因此,有可能通过使用自定义资源在 API Server 中存储过大和/或过多的对象来影响您的 API Server 和集群操作。一般来说,Kubernetes 中的 API 对象旨在成为简单的配置对象。它们不是用来代表大型数据文件的。如果你发现自己在自定义 API 类型中存储了大量数据,你可能应该考虑安装某种专用的键值存储或其他存储 API。

总结

Kubernetes 是伟大的 — 不仅仅是因为它提供的核心 API 的价值,还因为所有的动态扩展点,让用户可以根据自己的需求定制集群。无论是通过动态准入控制器来验证 API 对象,还是新的自定义资源定义,都有一个丰富的外部附加组件生态系统,你可以用它来构建一个完全符合用户需求的自定义体验。而且,如果必要的扩展不存在,本章的细节应该可以帮助你设计和构建一个扩展。