惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
IT之家
IT之家
C
Check Point Blog
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
人人都是产品经理
人人都是产品经理
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
M
MIT News - Artificial intelligence
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
F
Fortinet All Blogs
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
MyScale Blog
MyScale Blog
爱范儿
爱范儿
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)

又耳笔记

用AI挣钱之AI建站:二维码生成器工具 用AI挣钱之AI建站:https://markdowntopdf.top Cloudflare Workers实战番外一:用Static Assets托管网站,告别传统方案 Cloudflare Workers实战(五):不止JavaScript,拥抱Python与Rust Cloudflare Workers实战(四):托管和分发静态文件 Cloudflare Workers实战(三):实现认证、重定向与缓存 Cloudflare Workers实战(二):动态修改后端响应 Cloudflare Workers实战(一):随心所欲操作客户端请求 Cloudflare workers不完全指南 使用n8n创作短篇小说 水果风波:信任的代价 用Python将PDF文件转换成图片 rust网络框架Pingora源码阅读3 rust网络框架Pingora源码阅读2 rust网络框架Pingora源码阅读1 Pingora快速入门教程1之总览 Rust模板引擎askama快速入门引擎 使用Loco快速搭建自己的后台系统 用Rust发一封图文并茂的邮件 Rust小项目: 写一个简单的恶意流量阻断器 Rust小项目: 写一个简单的网页爬虫 Rust命令行库Clap快速入门教程 Rust小项目:用Rust写一个端口扫描器 Rust真全栈开发快速入门 rust声明宏快速入门教程 Rust文本处理快速入门教程 RUST web框架axum快速入门教程6之测试 白嫖免费的Rust在线运行时shuttle RUST web框架axum快速入门教程5之中间件 用Rust来做以太坊开发5之事件日志及签名
kubernetes kube-apiserver源码阅读8之Hook
About 又耳宁 关于技术与股票的一些零碎想法 · 2023-10-14 · via 又耳笔记

阅读kube-apiserver的过程中,会发现很多的AddPostStartHook的代码,这部分代码用于执行kube-apiserver启动之后的逻辑,因为他们放在启动后执行更适合,所以就提供了两种钩子(Hook), PostStartHookPreShutdownHook。这里只看PostStartHook,并且只看bootstrap-controller对应的钩子函数。

kubernetes service

当集群初次创建的时候,查看k8s的service的时候会发现,有一个叫做kubernetes的service,比如下面这样。

kubectl get service
# 输出如下
NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
kubernetes         ClusterIP   10.96.0.1        <none>        443/TCP             3s

如果不小心删除了,会发现它又自动创建了,那么它是怎么实现的呢?

bootstrapController

这里简单的列一下之前的调用链

func CreateServerChain(*aggregatorapiserver.APIAggregator, error) {
    kubeAPIServer, err := CreateKubeAPIServer(kubeAPIServerConfig, apiExtensionsServer.GenericAPIServer)
}

func CreateKubeAPIServer() {
	kubeAPIServer, err := kubeAPIServerConfig.Complete().New(delegateAPIServer)
}

func (c completedConfig) New() {
    if err := m.InstallLegacyAPI(&c, c.GenericConfig.RESTOptionsGetter, legacyRESTStorageProvider); err != nil {
			return nil, err
		}
}

func (m *Instance) InstallLegacyAPI() error {
    // 1.
	controllerName := "bootstrap-controller"
    // 2.
	coreClient := corev1client.NewForConfigOrDie(c.GenericConfig.LoopbackClientConfig)
    // 3.
	bootstrapController := c.NewBootstrapController(legacyRESTStorage, coreClient, coreClient, coreClient, coreClient.RESTClient())
    // 4.
	m.GenericAPIServer.AddPostStartHookOrDie(controllerName, bootstrapController.PostStartHook)
    // 5.
	m.GenericAPIServer.AddPreShutdownHookOrDie(controllerName, bootstrapController.PreShutdownHook)
	return nil
}

前面的调用链就不细说了。

代码分解如下:

  1. 设置controller名字
  2. 创建k8s的静态客户端
  3. 创建controller
  4. 加入PostStartHook

然后我们继续这个controller的创建和启动逻辑

func (c *completedConfig) NewBootstrapController() *Controller {
    // 1.
	_, publicServicePort, err := c.GenericConfig.SecureServing.HostPort()
	// 2. kube-system, kube-public, kube-node-lease
	systemNamespaces := []string{metav1.NamespaceSystem, metav1.NamespacePublic, corev1.NamespaceNodeLease}

	return &Controller{
		EndpointInterval:   c.ExtraConfig.EndpointReconcilerConfig.Interval,
		SystemNamespaces:         systemNamespaces,
		SystemNamespacesInterval: 1 * time.Minute,
		ServiceIP:                 c.ExtraConfig.APIServerServiceIP,
	}
}

// 3.
func (c *Controller) PostStartHook() error {
	c.Start()
	return nil
}

// 4.
func (c *Controller) Start() {
	//  5.
	endpointPorts := createEndpointPortSpec(c.PublicServicePort, "https", c.ExtraEndpointPorts)
    // 6.
	if err := c.EndpointReconciler.RemoveEndpoints(kubernetesServiceName, c.PublicIP, endpointPorts); err != nil {
		klog.Errorf("Unable to remove old endpoints from kubernetes service: %v", err)
	}
    
    // 忽略验证/修复服务ip的逻辑
    
	// 7.
	c.runner = async.NewRunner(c.RunKubernetesNamespaces, c.RunKubernetesService, repairClusterIPs.RunUntil, repairNodePorts.RunUntil)
	c.runner.Start()
}

// 8.
func (r *Runner) Start() {
	if r.stop == nil {
		c := make(chan struct{})
		r.stop = &c
		for i := range r.loopFuncs {
			go r.loopFuncs[i](*r.stop)
		}
	}
}

代码分解如下:

  1. 获取服务IP地址端的第一个IP, 比如--service-cluster-ip-range=10.96.0.0/12参数的第一个IP是10.96.0.1
  2. 服务启动后要创建的namespace
  3. 注册到钩子函数里的函数
  4. 上一步的具体实现
  5. 创建Service对应的Endpoints,用于删除对应的Endpoint
  6. 因为kube-apiserver可能还没有启动完成,所以先删除对应的Endpoint(如果有的话)
  7. 创建一个Runner对象,传入一串后续要执行的函数
  8. 上一步的具体启动逻辑

RunKubernetesNamespaces

这个实现比较简单,就顺便一起看看

func (c *Controller) RunKubernetesNamespaces(ch chan struct{}) {
	wait.Until(func() {
		// Loop the system namespace list, and create them if they do not exist
		for _, ns := range c.SystemNamespaces {
			if err := createNamespaceIfNeeded(c.NamespaceClient, ns); err != nil {
				runtime.HandleError(fmt.Errorf("unable to create required kubernetes system namespace %s: %v", ns, err))
			}
		}
	}, c.SystemNamespacesInterval, ch)
}

代码比较简单,就是不断的检查是否有必要创建,默认间隔是每分钟。

RunKubernetesService

func (c *Controller) RunKubernetesService(ch chan struct{}) {
	// 1.
	wait.PollImmediateUntil(100*time.Millisecond, func() (bool, error) {
		var code int
		c.readyzClient.Get().AbsPath("/readyz").Do(context.TODO()).StatusCode(&code)
		return code == http.StatusOK, nil
	}, ch)
    
	// 2.
	wait.NonSlidingUntil(func() {
		// 3.
		if err := c.UpdateKubernetesService(false); err != nil {
			runtime.HandleError(fmt.Errorf("unable to sync kubernetes service: %v", err))
		}
	}, c.EndpointInterval, ch)
}

func (c *Controller) UpdateKubernetesService(reconcile bool) error {
	// 4.
	if err := createNamespaceIfNeeded(c.NamespaceClient, metav1.NamespaceDefault); err != nil {
		return err
	}
	return nil
}

代码分解如下:

  1. 等待服务就绪
  2. 重试机制的封装
  3. 不断尝试更新kubernetes service, 如果需要的话
  4. 如果有必要就创建

总结

Kube-apiserver处了处理客户端请求,还会起一堆的controller用于监控必要的资源并镜像相关的操作,这些操作通过注册钩子函数的方式来实现。

参考链接