介绍

Kubernetes控制器是一个主动调谐的过程,它会watch一些对象的期望状态,也会watch实际的状态,然后控制器会发送一些指令尝试让对象的当前状态往期望状态迁移。

控制器最简单的实现就是一个循环:

for{
desired := getDesiredState()
current := getCurrentState()
makeChanges(desired,current)
}

Watches 这些都只是这个逻辑的优化。

指南

当你在编写控制器时,有一些准则将有助于确保你得到你需要的结果和性能。

  • 一次只操作一个元素,如果你使用了workqueue.Interface,你可以将某个资源的变化排成队列,随后将他们弹到多个“worker” gofuncs 中,并保证没有两个gofuncs会同时对同一个元素进行操作。

    许多控制器必须触发多个资源(我需要“如果 Y 更改,则检查 X”),但几乎所有控制器都可以根据关系将这些资源折叠到“检查此 X”的队列中。例如,ReplicaSet 控制器需要对被删除的 pod 做出反应,但它通过查找相关的 ReplicaSet 并将它们排队来做到这一点。

  • 资源之间的随机排序。当控制器对多种类型的资源进行排队时,无法保证资源之间的排序。

    不同的watch独立更新。即使使用“created resourceA/X”和“created resourceB/Y”的客观顺序,您的控制器也可以观察到“created resourceB/Y”和“created resourceA/X”。

  • 水平驱动,而不是边缘驱动。就像没有一直运行的shell脚本一样,您的控制器可能会在再次运行之前关闭一段不确定的时间。

    如果一个API对象出现的标记值为true,您不能指望看到它从false变为true,只是您现在观察到它是true。即使是API watch也会遇到这个问题,因此请确保您不要指望看到更改,除非您的控制器还在对象状态中标记了它上次做出的决定信息。

  • 使用 SharedInformers。SharedInformers 提供了回调函数来接收特定资源的添加、更新和删除的通知,它们还提供了访问共享缓存和确定缓存何时启动的便利功能。

    使用 https://git.k8s.io/kubernetes/staging/src/k8s.io/client-go/informers/factory.go 中的工厂方法来确保你和其他人共享同一个缓存实例。

    这样我们就大大减少了 APIServer 的连接以及重复的序列化、重复的反序列化、重复的缓存等成本。

    你可能会看到其他机制,比如反射器和 DeltaFIFO 驱动控制器。这些都是旧的机制,我们后来用它们来构建 SharedInformers,你应该避免在新控制器中使用它们。

  • 永远不要改变原始对象!缓存在控制器之间共享,这意味着如果您改变对象的“副本”(实际上是引用或浅拷贝),您将弄乱其他控制器(不仅仅是您自己的)。

    最常见的失败点是制作一个浅拷贝,然后改变一个映射,比如Annotations. 用于api.Scheme.Copy制作深拷贝。

  • 等待您的二级缓存。许多控制器具有主要和次要资源。主要资源是您将为其更新的资源Status。次要资源是您将要管理(创建/删除)或用于查找的资源。

    在启动主要同步功能之前,使用该framework.WaitForCacheSync功能等待二级缓存。这将确保诸如 ReplicaSet 的 Pod 计数之类的东西不会因已知的过时信息而导致抖动。

  • 系统中还有其他参与者。仅仅因为您没有更改对象并不意味着其他人没有。

    不要忘记当前状态可能随时改变——仅仅观察期望的状态是不够的。如果您使用不存在所需状态的对象来指示应删除当前状态的事物,请确保您的观察代码中没有错误(例如,在缓存填满之前采取行动)。

  • 将错误渗透到顶层以实现一致的重新排队。我们有一个 workqueue.RateLimitingInterface允许简单的重新排队和合理的退避。

当需要重新排队时,您的主控制器 func 应该返回错误。如果不是,它应该使用utilruntime.HandleError并返回 nil。这使得审阅者很容易检查错误处理情况,并确信您的控制器不会意外丢失它应该重试的东西。

  • Watches 和 Informers 将“同步”。他们会定期将集群中的每个匹配对象传递给您的Update方法。这适用于您可能需要对对象采取额外操作但有时您知道不会有更多工作要做的情况。

    如果您确定在没有新更改的情况下不需要重新排队项目,您可以比较新旧对象的资源版本。如果它们相同,则跳过重新排队工作。执行此操作时要小心。如果您在失败时跳过重新排队您的项目,您可能会失败,而不是重新排队,然后再也不会重试该项目。

  • 如果您的控制器正在协调的主要资源在其状态中支持 ObservedGeneration,请确保在两个字段之间的值不匹配时将其正确设置为 metadata.Generation。

    这让客户端知道控制器已经处理了资源。确保您的控制器是负责该资源的主控制器,否则如果您需要通过您自己的控制器传达观察,则需要在资源的状态中创建不同类型的 ObservedGeneration。

  • 考虑对导致创建其他资源的资源使用所有者引用(例如,ReplicaSet 导致创建 Pod)。因此,您可以确保一旦您的控制器管理的资源被删除,子资源将被垃圾收集。有关所有者参考的更多信息,请在此处阅读更多信息。

    要特别注意收养的方式。当父母或孩子被标记为删除时,您不应该为资源收养孩子。如果您正在为您的资源使用缓存,您可能需要通过直接读取 API 来绕过它,以防您发现所有者引用已为其中一个孩子更新。因此,您可以确保您的控制器不会与垃圾收集器竞争。

示例

package main

import (
"flag"
"fmt"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog/v2"
"path/filepath"
"time"
) type Controller struct {
indexer cache.Indexer
queue workqueue.RateLimitingInterface
informer cache.Controller
} func NewController(queue workqueue.RateLimitingInterface, indexer cache.Indexer, informer cache.Controller) *Controller {
return &Controller{
indexer: indexer,
queue: queue,
informer: informer,
}
} func (c *Controller) processNexItem() bool {
// 等待工作队列中有一个新元素
key, quit := c.queue.Get()
if quit {
return false
} // 告诉队列我们已经完成了处理 此 key 的操作
// 这将为其他 worker 解锁该 key
// 这将确保安全的并行处理,因为永远不会并行处理具有相同key的两个pod
defer c.queue.Done(key) // 调用包含业务逻辑的方法
err := c.syncToStdout(key.(string))
// 如果在执行业务逻辑期间出现错误,则处理错误
c.handlerErr(err, key)
return true
} // 控制器的业务逻辑实现
// 在此控制器中,它只是将有关 Pod 的信息打印到 stdout
// 如果发生错误,则简单的返回错误
// 此外重试逻辑不应该成为业务逻辑的一部分
func (c *Controller) syncToStdout(key string) error {
// 从本地存储中获取key对应的对象
obj, exists, err := c.indexer.GetByKey(key)
if err != nil {
klog.Errorf("Fetching object with key %s from store failed with %v", key, err)
return err
}
if !exists {
fmt.Printf("Pod %s does not exists anymore\n", key)
} else {
fmt.Printf("Sync/Add/Update for Pod %s\n", obj.(*v1.Pod).GetName())
}
return nil
} // 检查是否发生错误,并确保我们稍后重试
func (c *Controller) handlerErr(err error, key interface{}) {
if err == nil {
// 忘记每次成功同步时 key 的#AddRateLimited历史记录。
// 这样可以确保不会因过时的错误历史记录而延迟此key更新的以后处理
c.queue.Forget(key)
return
} // 如果出现问题,此控制器将重试5次
if c.queue.NumRequeues(key) < 5 {
// 将 key 重新加入到限速队列中
// 根据队列上的速率限制器和重新入队的历史记录,稍后将再次处理该key
c.queue.AddRateLimited(key)
return
} c.queue.Forget(key)
// 多次重试,我们也无法成功处理该key
runtime.HandleError(err)
klog.Infof("Dropping pod %q out of the queue: %v", key, err)
} func (c *Controller) Run(thread int, stopCh chan struct{}) {
defer runtime.HandleCrash() // 停止控制器后关闭队列
defer c.queue.ShutDown() klog.Info("Starting Pod controller")
// 启动
go c.informer.Run(stopCh) // 等待所有相关的缓存同步,然后再开始处理队列中的项目
if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
return
} for i := 0; i < thread; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
} <-stopCh
klog.Info("Stopping Pod controller")
} func (c *Controller) runWorker() {
for c.processNexItem() { }
} func initClient() (*kubernetes.Clientset, error) {
var err error
var config *rest.Config
var kubeconfig *string if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(可选) kubeconfig 文件的绝对路径")
} else {
kubeconfig = flag.String("kubeconfig", "", "kubeconfig 文件的绝对路径")
}
flag.Parse() // 首先使用 inCluster 模式(需要去配置对应的RBAC权限,默认的sa是default->是没有获取deploy的list权限)
if config, err = rest.InClusterConfig(); err != nil {
// 使用 kubeConfig 文件创建汲取配置 Config 对象
if config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig); err != nil {
panic(err.Error())
}
} // 通过 rest.Config 对象 创建 Clientset 对象
return kubernetes.NewForConfig(config)
} func main() {
clientset, err := initClient()
if err != nil {
klog.Fatal(err)
} // 创建 Pod ListWatcher
podListWatcher := cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", v1.NamespaceDefault, fields.Everything()) // 创建队列
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) // 在 informer 的帮助下,将工作队列绑定到缓存
// 这样,我们确保无论何时更新缓存,都将 pod key 添加到工作队列中
// 注意: 当我们最终从工作队列中处理元素时,我们可能会看到Pod的版本比响应触发更新的版本新
indexer, informer := cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
key, err := cache.MetaNamespaceKeyFunc(obj)
if err == nil {
queue.Add(key)
}
},
UpdateFunc: func(oldObj, newObj interface{}) {
key, err := cache.MetaNamespaceKeyFunc(newObj)
if err == nil {
queue.Add(key)
}
},
DeleteFunc: func(obj interface{}) {
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err == nil {
queue.Add(key)
}
},
}, cache.Indexers{}) controller := NewController(queue, indexer, informer) err = indexer.Add(&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "mypod",
Namespace: v1.NamespaceDefault,
},
})
if err != nil {
panic(err)
} // start controller
stopCh := make(chan struct{})
defer close(stopCh)
go controller.Run(1, stopCh)
select {}
} ======
程序运行结果:
// 如果default有pod,则会输出
Sync/Add/Update for Pod pod名
// 接着会输出
Pod default/mypod does not exists anymore

controller的简单介绍的更多相关文章

  1. 关于如何在其他包中写controller和简单介绍@SpringBootApplication

    本文参考博客:https://blog.csdn.net/u013473691/article/details/52353923 关于@Configuration和@Bean参考博客:https:// ...

  2. SpringMVC总结二:Controller的请求映射方式(RequestMapping)简单介绍

    在SpringMVC总结一:快速入门的基础上简单介绍一下请求映射的方式: 1,标准映射规则 1. @RequestMapping可以设置在类上,也可以设置在方法上 2. 请求的映射规则是:类上的Req ...

  3. yii2的权限管理系统RBAC简单介绍

    这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...

  4. angular1.x的简单介绍(二)

    首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块 ...

  5. 【转载】JMeter学习(一)工具简单介绍

    JMeter学习(一)工具简单介绍 一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静 ...

  6. salesforce lightning零基础学习(一) lightning简单介绍以及org开启lightning

    lightning对于开发salesforce人员来说并不陌生,即使没有做过lightning开发,这个名字肯定也是耳熟能详.原来的博客基本都是基于classic基于配置以及开发,后期博客会以ligh ...

  7. ASP.NET MVC 简单介绍①

    ASP.NET  MVC 简单介绍① 只做了重要描述,内容出自菜鸟教程网站内容. 目录 1布局 2HTML 帮助器 3.Razor 语法 4.添加样式 5.Layout 6. Controllers ...

  8. LoadRunner简单介绍----性能自动化测试工具

    在做性能测试中,我认为技术可以说是武功心法,工具则是一把利剑,有一把好的利剑可以帮助自己更好的完成性能测试工作.在这里简单介绍一下LoadRunner,带大家一起来认识一下这把尚方宝剑. 一.性能测试 ...

  9. SpringBoot 中常用注解@Controller/@RestController/@RequestMapping介绍

    原文 SpringBoot 中常用注解 @Controller/@RestController/@RequestMapping介绍 @Controller 处理http请求 @Controller / ...

  10. client高性能组件化框架React简单介绍、特点、环境搭建及经常使用语法

    [本文源址:http://blog.csdn.net/q1056843325/article/details/54729657 转载请加入该地址] 明天就是除夕了 预祝大家新春快乐 [ ]~( ̄▽ ̄) ...

随机推荐

  1. c#数据库操作ORM映射框架

    主要功能介绍 支持Oracle,SQL Server,MySQL,SQLLite等数据库..主要功能: 支持查询返回动态类型Dynamic以及可扩展类型ExpandoDynamic 表拆分,根据某个日 ...

  2. 14 个 Linux 下 CPU 监控工具

    01. top top是最常用的查看系统资源使用情况的工具,包括CPU.内存等等资源. 这里主要关注CPU资源. 1.1 /proc/loadavg load average取自/proc/loada ...

  3. Windows下安装使用OpenLDAP

    LDAP:(轻量级目录访问协议,Lightweight Directory Access Protocol)它是基于 X.500标准的,但是简单多了并且可以根据需要定制.与X.500不同,LDAP支持 ...

  4. Two-Stream Convolutional Networks for Action Recognition in Videos论文精读

    Two-Stream Convolutional Networks for Action Recognition in Videos论文精读 大家好,今天我要讲的论文是一篇视频理解领域的开山之作,这是 ...

  5. Static Timing Analysis Basics

    Preface This note only introduce the essential concepts about Static Timing Analysis, which not cont ...

  6. ARM开发板——实时获取用户点击触摸屏的LCD坐标信息(阻塞式读取)

    ARM开发板--实时获取用户点击触摸屏的LCD坐标信息(阻塞式读取) 目录 ARM开发板--实时获取用户点击触摸屏的LCD坐标信息(阻塞式读取) 1.硬件信息 2.代码需求 3.代码实现 1.硬件信息 ...

  7. datasnap的Restful的接口方法

    //Restful接口测试 //GET function Test(Value: string): string; //POST function updateTest(Value: string; ...

  8. 详细介绍MessageQueueSelector

    一.MessageQueueSelector 详解 MessageQueueSelector 是 RocketMQ 提供的一个接口,用于自定义消息发送时的队列选择策略. 通过实现该接口, 开发者可以控 ...

  9. 面试的信心来源于过硬的基础 viewport、跨域、 渲染优化、数组乱序、盒子垂直水平居中、meta、消除transition闪屏、JS 判断设备来源

    原文:面试的信心来源于过硬的基础 在过去的一年很多人不满于公司没有福利.人际关系不好相处.没有发展前途的境遇等等,想着在开年来换一份工作来重新开始自己,那么 你 准备好了吗? 下面是本人整理的一份面试 ...

  10. 至美!看AXUI如何美化原始HTML标签(reset/normalize)

    前言:不只是重置,而是重塑 在前端开发中,我们每天都会接触大量的原生 HTML 标签,例如 <button>.<input>.<a>.<table>.& ...