
K8s 控制器和操作员
在 k8s 世界中,每个资源都是通过控制器创建的。例如,pod、部署、副本集等都有内置控制器。所以基本上,控制器只不过是一个控制循环,它持续监视集群的状态并采取行动使集群进入所需状态。资源具有提供所需状态的规范。控制器检查当前状态。如果它与所需状态不匹配,它将进行适当的更改或修改以使其更接近所需状态。
不同类型的 Kube-Controller-Manager
ReplicaSet 控制器:此控制器负责维护一组稳定的 Pod 副本,这些副本在任何给定时间都在运行。它通常与 Deployment 结合使用,以确保始终有指定数量的 Pod 副本在运行,即使在发生节点故障或 Pod 终止的情况下也是如此。部署控制器:此控制器为 Pod 和 ReplicaSet 提供声明式更新。它允许轻松扩展、滚动更新和回滚应用程序。部署控制器管理 ReplicaSet 的创建和删除,以确保始终运行所需数量的 Pod。StatefulSet 控制器:此控制器用于管理有状态应用程序,例如数据库。它为集合中的每个 Pod 提供唯一标识(稳定的主机名),并维护这些 Pod 的顺序和唯一性。当您需要稳定的网络标识符、稳定的持久存储以及有序、优雅的部署和扩展时,它特别有用。服务控制器:此控制器负责为一组 Pod 维护稳定的 IP 地址和 DNS 名称。它充当负载均衡器,并根据服务的选择器将流量路由到适当的 Pod。这可确保服务具有稳定的端点来访问正在运行的 Pod,即使它们在集群中被创建、销毁或移动。行为与建筑
因此,在深入测试之前,我们必须了解标准控制器的基本架构。在 Kubernetes 的客户端-服务器架构中,控制器作为客户端发挥着至关重要的作用,它们向 Kubernetes API 服务器发出 API 调用(主要是 HTTP)。它们的主要目标是协调 Kubernetes API 对象与实际系统资源。此架构中的一个重要组件是使用 Informers。Informers 负责监视集群中的任何变化,这至关重要,因为持续轮询以检索资源信息会显著降低 API 服务器的性能。
Informers 的工作方式是查询资源数据并将其存储在本地缓存中。存储数据后,只有当对象(或资源)的状态发生变化时才会生成事件。这种方法可确保系统不会被不必要的事件所淹没,并且仅在发生相关更改时才通知控制器。
此架构中的另一个重要概念是资源版本。此版本随每次写入操作而变化,用于乐观并发控制。它确保以避免冲突和保持整个系统一致性的方式管理资源更新。通过理解和利用这些机制,Kubernetes 控制器可以有效地管理和协调集群中的资源状态。

Kubernetes 中的自定义 CRD 和控制器
Kubernetes 允许创建自定义资源定义 (CRD),这是 Kubernetes API 的扩展,可让用户定义自定义资源。这些自定义资源在默认 Kubernetes 安装中不可用,用于满足特定领域的用例和复杂的应用程序要求。
要管理这些自定义资源,需要一个自定义控制器。自定义控制器、CRD 和 Kubernetes API 服务器形成了一种紧密联系的关系,其中:
CRD 定义自定义资源。API 服务器管理这些资源的生命周期。自定义控制器确保这些资源的状态根据所需的配置进行维护。这种架构实现了 Kubernetes 的可扩展性,让用户能够根据自己的特定需求定制平台。
控制器测试 -
在将 Kubernetes 控制器部署到生产环境之前,确保它已准备好向 Kubernetes API 服务器提供请求至关重要。有几种方法可以测试 Kubernetes 控制器。我提到的其中一些来自这篇文章:
使用客户端伪造或更高级别的抽象:这种方法避免运行任何支持 API,使其适合单独对各个组件进行单元测试。使用 controller-runtime 中的 envtest 包:此包与精简版 API 服务器配合使用,可验证与 API 的实际交互,包括计时和缓存同步,不受其他控制器的干扰。它既支持针对精简版实例进行本地测试,也支持针对功能齐全的集群进行测试。运行真实的 API 服务器:此方法适用于临时环境或 ephemeral kind 或 microk8s 等实例,用于测试真实结果。它允许测试与真实 API 服务器的交互。使用外部进程(例如 envtest 或真实 API 服务器)的优势在于它可以解决分布式系统固有的延迟问题。Gomega 等库可用于在操作发生后等待特定条件。上述方法通常最适合单元测试和集成级别测试,在这些测试中,我们会单独测试特定组件。或者通过编写测试伪造数据
虽然上述技术对于单元测试和集成测试很有效,但它们可能不涵盖端到端 (e2e) 测试,而端到端测试对于确保控制器的整体功能至关重要。端到端测试的一种方法是执行资源更新和其他操作,以在受控环境中测试控制器的整个流程,并在必要时复制该过程。这有助于验证控制器在实际场景中的行为,并确保它已准备好进行生产部署。
总之,在将 Kubernetes 控制器投入生产之前,单元测试、集成测试和端到端测试相结合对于确保其可靠性和有效性至关重要。
为什么要使用 Keploy 测试 Kubernetes 控制器?
在本地构建和测试 Kubernetes 控制器可能具有挑战性,尤其是在处理传出 API 调用时。但是,Keploy 作为一种工具,可以从 API 调用、数据库查询等创建测试用例和数据模拟,提供了一种解决方案。Keploy 允许您记录和重放 Kubernetes 控制器发出的传出调用,这对于测试和确保控制器按预期运行非常有用。
您可能想知道如何在不更改代码的情况下实现这一点。Keploy 使用 eBPF 向内核空间添加探测器并收集网络缓冲区数据。然后,这些数据被发送到 Keploy 的代理,该代理充当用户空间,缓冲区的所有处理均由不同的协议解析器完成。Keploy 可以捕获控制器的出口流量,并将请求和响应存储在该特定事件的 YAML 文件中。在重放模式下,Keploy 不会对真正的 API 服务器进行 API 调用,而是会从存储的 YAML 文件中返回该特定请求的响应。这使得该过程独立于集群或环境,提供了一种方便有效的本地测试 Kubernetes 控制器的方法。
记录拨出的电话
因此,为了在本地或任何实时环境中捕获控制器的测试,您必须首先启动 kubernetes 集群并使自定义控制器与服务器进行一些交互。
要使用 Keploy 记录您的控制器,请按照以下步骤操作:
将您的 Kubernetes *rest.Config 对象设置为不安全且没有 CA 文件:cfg.Insecure = true cfg.CAFile = ""创建自定义 RoundTripper 以添加包含资源版本的标头字段。此标头用作跟踪 ID,用于将同一状态下的请求与记录的模拟进行匹配。以下是示例实现:type customRoundTripper struct { rt http.RoundTripper } func (crt *customRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { ctx := req.Context() rsv := ctx.Value("ResourceVersion") if rsv != nil { req.Header.Add("keploy-header", rsv.(string)) } return crt.rt.RoundTrip(req) } cfg.WrapTransport = func(rt http.RoundTripper) http.RoundTripper { return &customRoundTripper{rt: rt} }确保在同步过程中在 context.Context 中设置资源版本值。这对于将修改后的上下文传递给控制器的更新和创建方法至关重要。例如:func (c *Controller) syncHandler(ctx context.Context, key string) error { // set ResourceVersion in ctx rv := foo.GetResourceVersion() if rv != "" { ctx = context.WithValue(ctx, "ResourceVersion", rv) } }构建 Kubernetes 控制器的 Go 二进制文件: go build -o sample-controller 。要通过 Keploy 记录传出呼叫,请使用 Keploy 的记录命令包装您的控制器命令。注意 - keploy 的此功能处于测试阶段,尚未在主要版本中发布。这是专门为 Kubernetes 爱好者创建的实验,供他们尝试并给出评论。因此,您必须在此特定分支中签出并使用 go build 命令构建 keploy 二进制文件。https://github.com/keploy/keploy/pull/1342。在指定的分支中签出。1. git checkout kube-controller```{% endraw %} 为该分支构建 keploy 二进制文件。{% raw %} go build -o keploy && sudo mv keploy /usr/local/bin根据您的 kube 配置添加必要的标志:sudo -E env PATH=$PATH keploy record -c "./sample-controller -kubeconfig=$HOME/.kube/config" --mtls-cert-path "$HOME/.minikube/profiles/minikube/client.crt" --mtls-key-path "$HOME/.minikube/profiles/minikube/client.key" --mtls-host-name 192.168.49.2:8443您可以看到 Keploy 开始拦截出站调用后立即创建了 keploy/test-set-0/mocks.yaml。每个资源版本都有一个单独的模拟文件,用 mocks_ + " 表示“。**注意 -** 我想澄清的一件事是,上述功能在 TDD(测试驱动开发)中对您没有帮助。但是,您仍然可以在编写单元测试时利用 keploy 的存根生成功能来使用 keploy。因此,您无需制作任何模拟 api 服务器或为特定单元测试编写存根,只需在真实环境中运行该测试即可。Keploy 会将所有交互存储在模拟文件中,并在下次运行测试时使用该数据。
使用录制的模拟进行测试
要使用记录的模拟测试您的控制器:
在测试模式下运行 Keploy,将 mockAssert 标志设置为 true,并提供您的控制器二进制文件。Keploy 将自动为您创建一个虚假的 kube 配置:sudo -E env PATH=$PATH keploy test -c "“--mockAssert true或者,您可以设置自己的重播时间,它将尝试在提供的时间内重播您录制的会话:这里给出了与 keploy 集成的完整示例应用程序。keploy test -c--mockAssert true --replaySession 100请注意,重放过程会将足够大的事件时间间隔修剪为通知器的两个事件之间的精确平均持续时间。这样可以比记录中发生的事件更早地发送事件,从而加快重放速度。这可以帮助您重放控制器产生的整个 api 调用会话,但这次您不需要真正的 k8s 服务器或任何外部源来获取响应。所有响应都将由 keploy 本身返回,就像一个模拟服务器或中间人。这可以让您有信心在您的 CI-CD 管道中运行它。例如 - 您在一家大型云计算组织工作,要部署所有东西,需要大量虚拟化,而且是资源密集型操作。因此,在真实环境中测试它几乎是不可能的。在这里,类似 Keploy 的工具非常有用,因为它已经拥有您在成功推出该资源时想要获得的响应。因此,它可以是快速、可靠且节省成本的操作,因为您只需一次捕获控制器服务的正确流程。并且可以在后续版本中重复使用 keploy 重播。结论
使用 Keploy 等工具可以更高效、更可靠地在本地测试 Kubernetes 控制器。通过记录和重放传出调用,您可以确保控制器在各种情况下都能正常运行,从而提高 Kubernetes 应用程序的整体质量。由于 keploy 原生支持 getest 等测试框架,因此还可以获得任何应用程序甚至 kube 控制器的线路覆盖范围。探索 Keploy 并增强您的 Kubernetes 控制器测试工作流程!
常见问题解答
使用 Keploy 测试 Kubernetes 控制器有哪些优势?
Keploy 通过以下方式简化 Kubernetes 控制器的测试:
记录和重放传出的 API 调用:这消除了测试期间对实时环境的需求。提高效率:通过使用存储的模拟,测试变得更快并且独立于实际的 Kubernetes 集群。节省成本和资源:它减少了对资源密集型环境进行验证的依赖,使其成为大规模运营中 CI/CD 管道的理想选择。Keploy 如何处理 Kubernetes 控制器的传出 API 调用?
Keploy 使用 **eBPF 探测器** 拦截传出的调用,并将请求-响应对存储在模拟文件中。在重放模式下:
通话被拦截并与之前录制的模拟通话进行匹配。响应从这些模拟中返回,而不是联系实际的 API 服务器。此机制确保测试无需实时 Kubernetes 集群即可运行。Keploy 可以与 Kubernetes 控制器一起用于测试驱动开发 (TDD) 吗?
虽然 Keploy 的录制和重放功能不是专门为 TDD 设计的,但它仍然可以有效地使用:
存根生成:在真实环境中运行控制器一次以捕获交互。Keploy 将创建模拟以供后续使用。单元测试支持:通过利用这些模拟,您可以避免手动编写存根并专注于测试执行。Keploy 通过简化模拟创建并减少开发开销来补充现有的 TDD 工作流程。