GithubHelp home page GithubHelp logo

jamlee / blog Goto Github PK

View Code? Open in Web Editor NEW
0.0 0.0 0.0 1.1 GB

REPO for experimentation; ISSUE for blog

Makefile 2.44% Roff 2.02% C 24.56% Shell 2.25% M4 0.01% C++ 63.69% Java 0.07% Lex 0.41% Yacc 0.64% Go 0.04% Assembly 1.20% Python 0.99% JavaScript 0.93% Batchfile 0.08% Dockerfile 0.02% HTML 0.45% Standard ML 0.01% Scheme 0.01% Verilog 0.16% Stata 0.02%
blog

blog's People

Contributors

jamlee avatar

Watchers

 avatar  avatar

blog's Issues

【未填坑】源码:k8s controller runtime 源码分析

WIP, 还在编写中

Kubebuilder 和 Operator 是基于Controller Runtime 开发的 k8s 扩展脚手架。Controller Runtime 是用于监控 K8sResource (例如Pod等)的资源事件的go库,本质上是对 client-go 中的 list 和 watch 方法的再次封装。实现了限流消息队列的能力, 保证了我们监控K8s中的资源事件是高性能、高可用的。例如 Pod创建、Pod删除、Pod更新这样的都是资源事件。高性能体现自watch的资源会存在本地的cache,获取已经 watch 的资源描述是会从缓存中取的。在阅读本文时我们应该至少使用过Kubebuilder 和 Operator 写过一些小东西。下文简称 Controller Runtimecr.

cr 小例子

源码下载: https://github.com/kubernetes-sigs/controller-runtime
让我们先看一个CR的小例子:https://github.com/kubernetes-sigs/controller-runtime/blob/master/example_test.go

func Example() {
        // 创建一个全局的日志对象
	var log = controllers.Log.WithName("builder-examples")

        // 创建一个controller manager, 该对象用于管理 controller
	manager, err := controllers.NewManager(controllers.GetConfigOrDie(), controllers.Options{})
	if err != nil {
		log.Error(err, "could not create manager")
		os.Exit(1)
	}

        // 创建一个监控 ReplicaSet 名下的 Pod 资源类型的 controller,对应的处理资源的对象为ReplicaSetReconciler
	err = controllers.
		NewControllerManagedBy(manager). // Create the Controller
		For(&appsv1.ReplicaSet{}).       // ReplicaSet is the Application API
		Owns(&corev1.Pod{}).             // ReplicaSet owns Pods created by it
		Complete(&ReplicaSetReconciler{Client: manager.GetClient()})
	if err != nil {
		log.Error(err, "could not create controller")
		os.Exit(1)
	}

       // 启动 manager 名下所有的 controller
	if err := manager.Start(controllers.SetupSignalHandler()); err != nil {
		log.Error(err, "could not start manager")
		os.Exit(1)
	}
}

总体分析

# 主要的文件在pkg中
...
.
└── pkg
    ├── builder
    ├── cache # 管理多个 informers
    ├── client # 重新封装了client-go 的 client。实现读写分离,typed 和 unstructed 同用一个 client
    ├── controller # 内置controller 的实现
    ├── conversion
    ├── doc.go
    ├── envtest
    ├── event
    ├── handler
    ├── healthz
    ├── internal  # 内置controller 的实现
    ├── leaderelection
    ├── log
    ├── manager 含有 manager 的实现
    ├── metrics
    ├── patterns
    ├── predicate
    ├── ratelimiter
    ├── reconcile
    ├── recorder
    ├── runtime # 含有 manager 的实现
    ├── scheme
    ├── source  # 用于监控资源, 并把资源放到 queue 中
    └── webhook

这里核心的文件夹有: source, cache, client, runtime, controller, reconcile。理清这几个文件夹里的内容是可以理清 controller runtime 工作机制的脉络的,这里让我们避轻就重大概看下 cr 的工作过程。 cr 的启动我们经常看到, 先实例化一个 manager 然后manger 中填入多个 controller 最后启动 manager。实际上 manager.Start函数中启动了循环启动了多个 controller。
image
这里我们用几句话概括下上图的内容:

  1. manager 对象中拥有全局对象 cache, client。
  2. manager 对象中管理多个 controller。
  3. manager 对象把自己的setField设置到每一个controller 上。setFiled 是用于把manager中的全局对象指针赋值给controller 名下的 reconciler, source 等对象的方法。
  4. controller 使用 setFiled 方法 把全局对象传递给 reconciler, source 等对象。
  5. controller 协调 source, reconciler , queue 一起工作。

cr 在启动时初始化了共享的全局变量给 manager,manager 把全局变量分配给名下的多个 controller。

cache 和 client 分别是什么呢?
cache 是 informerCache。informer Watch 时是wacth 的每一类资源例如 Pod,所以如果 Pod已经被 Watch了的话,因为Watch 时得到的返回对象就是 Informer,所以这里把所有的 Informers管理起来了。另外读取Pod时,因为Pod是被Watch的,最新的状态就在本地缓存中,所以会从本地缓存查询 Pod 状态,不访问 kube-apiserver。
client 是 clientCache。意为缓存 client-go 的 restClient 的实例。我们都见过 clientset 吧,clientset 里含有 core, apps 等api组的 client,似乎是在启动时就全部初始化了。cr 没有这么做, cr 只在用到 client 时采取初始化 client。并且 client 对client-go 的 restClient 做了封装。client 有个内部对象是 cache, 读就从 cache中读, 写就是直连。

# pkg/manager/manager.go:401
	return &client.DelegatingClient{
		Reader: &client.DelegatingReader{
			// NOTE(JamLee): informerCache,处理结构化和非结构化。这里缓存了 informer
			CacheReader:  cache,
			ClientReader: c,
		},
		// NOTE(JamLee): c 是缓存了 client, 每类资源一个 client
		Writer:       c,
		StatusClient: c,
	}, nil

这里我们一句话感性地总结下:

启动初始化全局变量 cache 和 client 给 manager,manager 把全局变量分配给名下的多个 controller。controller 下发全局变量并协调 source, reconciler, queue 等一起工作。

看到这里我们是否已经清楚了 cr 的脉络呢?接下来把每一个对象搞清楚就是完整的 cr 的分析了。让我们来各个击破吧!!!

逐个击破

cache

工具库,用于缓存 informer, 并实现读写分离是它的真实写照。

client

工具库,用于缓存 client-go 的 restClient, 并实现typed 和 unstructured 都使用同一个 client。这个dynamic 在 client-go 是需要单独处理的。

source

用于监听事件支持 informer 和 webhook 两种方式。informer 这一侧是通过 controller 设置给 source 的 cache(informerCache)了来的,webhook 目前看来是用不到的。

client-go 的重点

cr 调用了 apiserver 的 watch 接口,它是使用了 client-go 的 informer, informer 本质上还是调用了 http 接口。

informer 的原理

这里 Informer 归属于 client-go 的内容。底层调用了 watch 接口, watch接口是长连接,不会主动退出。这里我们再看一个 client-go 中使用 informer的小例子:

package main

import (
    "fmt"
    "flag"
    "time"

    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/pkg/api/v1"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/tools/cache"
    "k8s.io/client-go/pkg/fields"
)

var (
    kubeconfig = flag.String("kubeconfig", "./config", "absolute path to the kubeconfig file")
)

func main() {
    flag.Parse()
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        panic(err.Error())
    }
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }

    watchlist := cache.NewListWatchFromClient(clientset.Core().RESTClient(), "services", v1.NamespaceDefault,
        fields.Everything())
    _, controller := cache.NewInformer(
        watchlist,
        &v1.Service{},
        time.Second * 0,
        cache.ResourceEventHandlerFuncs{
            AddFunc: func(obj interface{}) {
                fmt.Printf("service added: %s \n", obj)
            },
            DeleteFunc: func(obj interface{}) {
                fmt.Printf("service deleted: %s \n", obj)
            },
            UpdateFunc:func(oldObj, newObj interface{}) {
                fmt.Printf("service changed \n")
            },
        },
    )
    stop := make(chan struct{})
    go controller.Run(stop)
    for{
        time.Sleep(time.Second)
    }
}

watch 的原理

最原始的方式是调用 http://apiserver:8080/api/v1/watch/pods?watch=yes 方法,调用 watch 接口后会先将所有的对象 list 一次,然后 apiserver 会将变化的数据推送到 client 端,可以看到每次对于 watch 到的事件都需要判断后进行处理,然后将处理后的结果写入到本地的缓存中,原生的 watch 操作还是非常麻烦的。后来了官方推出的客户端 client-go ,client-go 中的 Informer 对 watch 操作做了封装。我们可以这样尝试这个接口:

kubectl proxy --port=8081 &
curl -v -i http://127.0.0.1:8081/api/v1/watch/pods?watch=yes

你会发现 curl 执行后会一致有新内容推送,这个 http 接口本质上是个长连接,原理是用了Chunked transfer encoding(分块传输编码),它首次出现在HTTP/1.1。

依赖库

cr 中用到一些感觉还不错的库在这里记录一下

  • gink: BDD 测试框架

博客:关于我

关于我

ID: JamLee
Email: [email protected]

我平时喜欢阅读一些源代码,这里有我一些开源项目的代码注解: 代码阅读中心

知识协议

我的博客采用CC-BY协议进行授权。
image

历史博客

也用过一些自建或者博客平台, 管理起来还是繁琐了, 平时用到的过程文件也不好存和阅读。历史的文章不搬运了, 是些没眼看的傻缺内容 😺 哈哈:
jamleecsdn

书籍:我的阅读清单

《数据发展的世纪之桥:希尔伯特的故事》👍👍👍👍👍
19-20世纪,讲述希尔伯特的一生,从哥尼斯堡(俄罗斯加里宁格勒)到哥根庭,那些希尔伯特或主导或见证的数学盛会。

【未填坑】源码:libuv 源码分析

libuv 是什么?

Libuv是nodejs底层的异步io库。它提供的功能包括是io、进程、线程、信号、定时器、进程间通信等,相比 libev, libuv抹平了各个操作系统之间的差异。Libuv提供的功能如下:

  • Full-featured event loop backed by epoll, kqueue, IOCP, event ports.
  • Asynchronous TCP and UDP sockets
  • Asynchronous DNS resolution
  • Asynchronous file and file system operations
  • File system events
  • ANSI escape code controlled TTY
  • IPC with socket sharing, using Unix domain sockets or named pipes (Windows)
  • Child processes
  • Thread pool
  • Signal handling
  • High resolution clock
  • Threading and synchronization primitives

【未填坑】源码:k8s clieng-go 源码分析

WIP, 还在编写中

Kubernetes的client-go库介绍client-go是一个调用kubernetes集群资源对象http API的客户端(是一个典型的web服务客户端库),即通过client-go实现对kubernetes集群中资源对象(包括deployment、service、ingress、replicaSet、pod、namespace、node等)的增删改查等操作。kubelet, kubeproxy, shcheduler 等组件均基于 client-go 来监听 api-server 的事件。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.