Code Monkey home page Code Monkey logo

sig-kubernetes's People

Contributors

abserari avatar attlee-wang avatar imgbotapp avatar liangyuanpeng avatar miss-you avatar oiar avatar rootsongjc avatar vincenthcui avatar wenhuwang avatar zhaoweiguo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sig-kubernetes's Issues

【提问】Resync 后将 Indexer 本地缓存放入 Delta FIFO 队列中,触发 update 回调,入参的 old 和 new 是一样的吗?

问题描述

Resync 机制会定时将 Indexer 本地缓存重新放入 Delta FIFO 队列中,然后触发 controller 的 update 回调。update 函数的入参有 old 和 new,此时 old 和 new 都是指 indexer 缓存中的同一个元素吗?相关代码我贴在下面,也注释说明了 Resync 的回调逻辑。

相关代码

// k8s.io/client-go/tools/cache/shared_informer.go
func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error {
	s.blockDeltas.Lock()
	defer s.blockDeltas.Unlock()

	// from oldest to newest
	for _, d := range obj.(Deltas) {
		// 判断事件类型,看事件是通过新增、更新、替换、删除还是 Resync 重新同步产生的
		switch d.Type {
		case Sync, Replaced, Added, Updated:
			s.cacheMutationDetector.AddObject(d.Object)
			if old, exists, err := s.indexer.Get(d.Object); err == nil && exists {
				if err := s.indexer.Update(d.Object); err != nil {
					return err
				}
				
				isSync := false
				switch {
				case d.Type == Sync:
					// 如果是通过 Resync 重新同步得到的事件则做个标记
					isSync = true
				case d.Type == Replaced:
					...
				}
				// 如果是通过 Resync 重新同步得到的事件,则触发 onUpdate 回调
				s.processor.distribute(updateNotification{oldObj: old, newObj: d.Object}, isSync)
			} else {
				if err := s.indexer.Add(d.Object); err != nil {
					return err
				}
                                // 这里触发 controller 的 update 回调,传入 new 和 old 对象,是否为同一个元素?
				s.processor.distribute(addNotification{newObj: d.Object}, false)
			}
		case Deleted:
			if err := s.indexer.Delete(d.Object); err != nil {
				return err
			}
			s.processor.distribute(deleteNotification{oldObj: d.Object}, false)
		}
	}
	return nil
}

分享如何调度器framework扩展

问题描述

群里有些小伙伴私聊问我如何基于scheduler framework扩展自己的应用,在这里粗略说一下。

假如是基于打分环节扩展,首先需要实现接口,包括Name,Score和ScoreExtensions。
实现完成之后,可以在cmd/kube-scheduler/scheduler.go里面加上
command := app.NewSchedulerCommand( app.WithPlugin(nodeload.Name, nodeload.New) )
然后编译,待编译完成,由于我是用kubeadm安装的集群,所以我的调度器是读的/etc/kuberxx/manifest/kube-scheduler.yml启动的pod,移走文件之后,然后用编译好的二进制启动./kube-scheduler --config=config.xml,然后进入测试环节。

备注

建议群里大佬出个分享文章?

[提问]CachedDiscoveryClient是如何创建出来的?

今天看DiscoveryClient客户端的缓存,api-server请求/api和/apis接口时调用ServerGroupsAndResources继而调用 ServerGroups

如果有缓存,是先查缓存的,代码里DiscoveryInterface这个接口进行ServerGroups的调用,具体是调用discovery_client.go中的ServerGroups方法,还是调用cached_discovery.go中的ServerGroups方法,则取决于创建的Client的类型是DiscoveryClient还是CachedDiscoveryClient,

但是在查看创建Client这块的源码时,久久不能找到哪里创建的CachedDiscoveryClient,所以来这里问一下,看看能不能得到答案,谢谢!

【活动】【每周研讨讲师招募】启动过程前半部分代码导读(15m)

拆解:(三个人)

  1. 实例化scheduler过程 源码分享 【书8.3.3】#62(把调度算法的注册和实例化部分重点介绍下,包括如何从配置文件指定调度算法【书8.3.1 + 自由发挥】)
  2. 事件管理器EventBroadcaster的介绍和在scheduler中的作用和注册过程分【书8.3.4】
  3. HTTP和HTTPS部分代码,重点可以强调下怎么给scheduler加API接口【书8.3.5】

新增:
事件管理器EventBroadcaster的介绍和在scheduler中的作用和注册过程分【书8.3.4】
HTTP和HTTPS部分代码,重点可以强调下怎么给scheduler加API接口【书8.3.5】

优先级降低
scheduler参数介绍【书8.1】#60
命令行参数解析源码分享 【书8.3.2】#61

【提问】pkg/controller/deployment/deployment_controller.go 中,updateDeployment 和 updateReplicaSet 两个函数,为啥 updateReplicaSet 中有对比 ResourceVersion 的步骤而 updateDeployment 中没有?

问题描述

最近在学习 informer 机制,但是还是不太了解 resync 的实际用途,现在遇到如下问题:

在 pkg/controller/deployment/deployment_controller.go 101行 中的 NewDeploymentController 会传入 DeploymentInformer 和 ReplicaSetInformer,为什么这两个 Informer 的 AddEventHandler 中 注册的 UpdateFunc “dc.updateDeployment dc.updateReplicaSet” updateReplicaSet 中有对比 ResourceVersion 的步骤而 updateDeployment 中没有?

相关代码

pkg/controller/deployment/deployment_controller.go:
func NewDeploymentController(dInformer appsinformers.DeploymentInformer, rsInformer appsinformers.ReplicaSetInformer, podInformer coreinformers.PodInformer, client clientset.Interface) (*DeploymentController, error) {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartStructuredLogging(0)
	eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")})

	if client != nil && client.CoreV1().RESTClient().GetRateLimiter() != nil {
		if err := ratelimiter.RegisterMetricAndTrackRateLimiterUsage("deployment_controller", client.CoreV1().RESTClient().GetRateLimiter()); err != nil {
			return nil, err
		}
	}
	dc := &DeploymentController{
		client:        client,
		eventRecorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "deployment-controller"}),
		queue:         workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "deployment"),
	}
	dc.rsControl = controller.RealRSControl{
		KubeClient: client,
		Recorder:   dc.eventRecorder,
	}

	dInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    dc.addDeployment,
		UpdateFunc: dc.updateDeployment,
		// This will enter the sync loop and no-op, because the deployment has been deleted from the store.
		DeleteFunc: dc.deleteDeployment,
	})
	rsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    dc.addReplicaSet,
		UpdateFunc: dc.updateReplicaSet,
		DeleteFunc: dc.deleteReplicaSet,
	})
	podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		DeleteFunc: dc.deletePod,
	})

	dc.syncHandler = dc.syncDeployment
	dc.enqueueDeployment = dc.enqueue

	dc.dLister = dInformer.Lister()
	dc.rsLister = rsInformer.Lister()
	dc.podLister = podInformer.Lister()
	dc.dListerSynced = dInformer.Informer().HasSynced
	dc.rsListerSynced = rsInformer.Informer().HasSynced
	dc.podListerSynced = podInformer.Informer().HasSynced
	return dc, nil
}
func (dc *DeploymentController) updateDeployment(old, cur interface{}) {
	oldD := old.(*apps.Deployment)
	curD := cur.(*apps.Deployment)
	klog.V(4).Infof("Updating deployment %s", oldD.Name)
	dc.enqueueDeployment(curD)
}
func (dc *DeploymentController) updateReplicaSet(old, cur interface{}) {
	curRS := cur.(*apps.ReplicaSet)
	oldRS := old.(*apps.ReplicaSet)
	if curRS.ResourceVersion == oldRS.ResourceVersion {
		// Periodic resync will send update events for all known replica sets.
		// Two different versions of the same replica set will always have different RVs.
		return
	}

	curControllerRef := metav1.GetControllerOf(curRS)
	oldControllerRef := metav1.GetControllerOf(oldRS)
	controllerRefChanged := !reflect.DeepEqual(curControllerRef, oldControllerRef)
	if controllerRefChanged && oldControllerRef != nil {
		// The ControllerRef was changed. Sync the old controller, if any.
		if d := dc.resolveControllerRef(oldRS.Namespace, oldControllerRef); d != nil {
			dc.enqueueDeployment(d)
		}
	}

	// If it has a ControllerRef, that's all that matters.
	if curControllerRef != nil {
		d := dc.resolveControllerRef(curRS.Namespace, curControllerRef)
		if d == nil {
			return
		}
		klog.V(4).Infof("ReplicaSet %s updated.", curRS.Name)
		dc.enqueueDeployment(d)
		return
	}

	// Otherwise, it's an orphan. If anything changed, sync matching controllers
	// to see if anyone wants to adopt it now.
	labelChanged := !reflect.DeepEqual(curRS.Labels, oldRS.Labels)
	if labelChanged || controllerRefChanged {
		ds := dc.getDeploymentsForReplicaSet(curRS)
		if len(ds) == 0 {
			return
		}
		klog.V(4).Infof("Orphan ReplicaSet %s updated.", curRS.Name)
		for _, d := range ds {
			dc.enqueueDeployment(d)
		}
	}
}

【提问】apiserver 中 CRD 的 api group 是在哪里创建的呢?

问题描述

在 apiserver 启动过程中,会为不同的资源注册不同的 api。如:kubeApiServer负责内置资源的 api 创建,extensionServer 负责 crd 这种外部资源的 api 创建(只负责 apiextensions.k8s.io/v1 这个组的资源的 api 创建),aggregatorServer 负责 apiregistration 的api创建(只负责 apiextensions.k8s.io 这个组的资源的 api 创建)。
但在我们写 CRD 时,会自定义 CR 的 api 。比如 podlog 是我写的 CRD。执行 kubectl get podlog 命令的时候,会去调 apiServer对应的 api 去获取cr的相关信息:
pod-log crd

图中 log.pod.log.io/v1 是我自定义的 api group,那么这个 group 是什么时候创建的呢?它的创建逻辑在哪呢?

【分享】关于 Phase、State、Conditions 的理解

分享内容

在进行源码阅读的时候,会经常看到资源 Status 里面有 PhaseStateConditions 这几个字段,仔细看会发现它们都是描述资源的状态,那它们到底有什么区别呢,不仔细研究还真是会让人傻傻分清楚。

这里首先给出个人的理解 Conditions 是一组详细状态,描述的是资源当前以及过去的状态;Phase 和 State 描述的是资源的当前状态,这两个的用法是一样的,至于用哪个则根据个人喜好了,Phase、State 的取值来自于 Condtions。

Conditions 已经成为一个用于收集资源的详细状态的标准机制,Conditions 应该是可扩展的,常用一个数组、切片来实现,典型的 condition 的结构如下:

字段名称 描述
type 状况的名称
status 表明该状况是否适用,可能的取值有 "True", "False" 或 "Unknown"
lastProbeTime 上次探测资源状况时的时间戳
lastTransitionTime 上次从一种状态转换到另一种状态时的时间戳
reason 机器可读的、驼峰编码(UpperCamelCase)的文字,表述上次状况变化的原因。用于 kubectl get 显示如 NewReplicaSetAvailable
message 人类可读的消息,给出上次状态转换的详细信息。用于 kubectl describe 显示
  • type 状况名称尽量言简意骇,如使用 "Ready" 而不是 "MyResourceReady",同时应该有较好的可读性,例如 "Ready"、"Succeeded" 就比 "Failed" 更具有可读性,因为 "Failed=Unknown" 或者 "Failed=False" 这时候就不容易判断出状态到底是成功还是失败。
  • status 的值通常为 True、False、Unknown,分别代表状况适用、不适用、未知,当 status 缺失的时候应该当作 Unknown 处理。type 描述的应该是资源当前能观察到的状态,而不描述资源当前的状态转换,应该用形容词来作为描述词而不是用动词作为描述词,例如用 Ready、OutOfDisk、Succeeded、Failed 等而不是 Creating、Deloying。

每个 condition 包含资源调谐过程中某个阶段的详细状态,例如一个资源的协调过程中涉及到与组件 A 的交互,这时候可以用一个 type 为 Ready 的condition 描述与组件 A 的交互状态,在请求返回之前 condition 的 status 应该为 Unknown(常用 Unknown 描述未知状态,如结果还未返回) ,这样当有其他请求在同时间协调这个资源的时候,会根据 condition status 的Unknown 状态知道已经与组件 A 交互过了,就会跳过与组件 A 交互的逻辑。

参考:

https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/

https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties

分享收获

备注

个人拙见,欢迎大佬补充。

[活动] Kubernetes 源码研习社 第一期活动

Lead:厉辉/yousa,腾讯云工程师,Apache APISIX PMC(https://github.com/Miss-You)

IT 技术日新月异,想必每个 IT 人都会有类似的焦虑:我该学习什么?哪些知识学到就是赚到?怎样学习才能最有效提升编程能力?
阅读优秀的代码是提高编程能力万无一失的办法。诚然,提高编程能力的显著方法是写更多代码,但也需要静下心来品味优秀的代码,大侠行走江湖也需要武功秘籍,而当今优秀的开源项目代码便是程序员的武林秘籍。

对于云原生爱好者,阅读 Kubernetes 核心源码是一个非常好的选择。Kubernetes 源码研习社是云原生社区旗下的一个学习小组,由热爱学习、注重个人成长的一帮小伙伴们自由、自愿成立的小组。每个人都非常希望从 Kubernetes 上学到知识,帮助自己实现成长和进步。欢迎加入,一起坚持,一起克服,一起成长。

第一期计划将在本周三/本周六正式启动,在群中的人即可自行报名,将自己的信息登记到excel表格中即可参与。

第一期的主题

《Kubernetes 源码剖析》:第五章——Client Go章节

本章主要阐述 client-go 编程式交互哦工具的实现机制,本章节涉及 Kubernetes 开发者常用的多种 Client ,例如: RESTClient、ClientSet、DynamicClient、DiscoveryClient。详细剖析其内部运行机制,例如:Informer机制、DeltaFIFO队列、Indexer索引机制等等。开发者常使用 client-go 基于 Kubernetes 做二次开发。所以 client-go 是开发者熟练并掌握的必会技能。

活动周期

三周半,8月6日到8月30日

日常任务活动

  • 完成每日阅读目标
  • 完成每周学习总结
  • 微信群中交流、提问和总结(交流讨论问题、提问、分享面试题等)

每日阅读目标

image

规则说明

第一期活动共四周。前三周根据学习要求精读《Kubernetes 源码剖析》和Client-go源码

基本要求:

  • 前三周必须产出一篇200字以上的笔记或随想,最后1周为总结周。(笔记或随想必须包括至少一个日常任务)。大家可以将自己的笔记或随想帖链接直接回复在 研习社事项跟踪 上。
  • 有不懂的问题,欢迎大家在群里抛出,不论是golang入门还是kubernetes源码细节疑问,都可以;但需要将问题以及答案沉淀到知识星球;当然部分疑难问题也欢迎通过知识星球向本书作者郑东旭大佬提问,大佬会直接回答哈

第一期门槛:

  1. 已准备好<<Kubernetes源码剖析>>书籍
  2. 已准备好kubernetes环境
  3. 已准备好kubernetes源码、go语言环境及IDE
  4. 熟练使用kubernetes
  5. 熟练使用go语言
  6. 每周学习时间不少于5小时

行为准则

  1. 所有成员志愿参与,遵守内部规约
  2. 兴趣小组须遵守国家的章程

声明

  • 自愿加入,不强制
  • 若加入请努力坚持下来

Kickoff

回看地址

【分享】k8s面试题

面试题

根据大佬们在群里的讨论,将相关面试题汇总到这里

  • k8s基础组件有哪些,什么功能
  • 一个pod创建流程
  • 网络选型需要注意什么
  • etcd用的什么算法,简单解释一下
  • pod中penging状态,是什么原因产生的,pod出现问题,排查思路
  • kubernetes发布策略(4种)
  • 手写raft
  • 你们监控用的什么,怎么利用普罗米修斯监控pod信息,k8s状态,如果你来设计相关的监控如何落地
  • 如果利用k8s实现滚动更新,我说的配置文件机制
  • statefulset是怎么实现滚动更新的?
  • 基本就是继续k8s架构问,,遇到的问题,怎么处理
  • kubectl exec实现的原理?
  • 如何实现schedule水平扩展?
  • 为什么k8s要用申明式?
  • 了解过 endpointslice吗? 怎么实现的?
  • 容器的驱逐时间是?
  • 节点notready是什么导致的? notready会发生什么?
  • api-server到etcd怎么保证事件不丢失?
  • sidecar要保证顺序启动怎么保证?几种方式可以做到?
  • 有了解过qos吗? 怎么实现的?
  • 详述kube-proxy原理
  • k8s的pause容器有什么用。是否可以去掉
  • k8s的service和ep是如何关联和相互影响的
  • StatefulSets和operator区别

【分享】golang channel 详解

分享内容

大家在看 k8s 源码的时候不免会看到不少 channel 相关的操作。在GopherCon 2017中,Golang专家Kavya深入介绍了 Go Channels 的内部机制,以及运行时调度器和内存管理系统是如何支持Channel的,图文并茂,简单直观,有时间的小伙伴可以看看。

文档链接

使用容器环境构建k8s:提前导入需要的镜像,make quick-release的时候还是要去拉镜像

环境:centos 7 k8s:1.14 go:1.12

提前导入镜像:
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kube-build build-fbd5cbc23f-5-v1.12.12-1 6933b19a02f5 12 minutes ago 2.52GB
k8s.gcr.io/kube-cross v1.12.12-1 adda3dc86c33 9 months ago 1.87GB
k8s.gcr.io/debian-iptables-amd64 v11.0.2 01a746008995 16 months ago 45.4MB
k8s.gcr.io/debian-base-amd64 v1.0.0 204e96332c91 16 months ago 42.3MB
image

执行make quick-release :
[root@localhost kubernetes]# make quick-release
+++ [0728 23:10:02] Verifying Prerequisites....
+++ [0728 23:10:02] Building Docker image kube-build:build-fbd5cbc23f-5-v1.12.12-1
+++ [0728 23:10:46] Creating data container kube-build-data-fbd5cbc23f-5-v1.12.12-1
+++ [0728 23:11:10] Syncing sources to container
+++ [0728 23:11:28] Running build command...
+++ [0728 23:11:36] Building go targets for linux/amd64:
./vendor/k8s.io/code-generator/cmd/deepcopy-gen
+++ [0728 23:11:44] Building go targets for linux/amd64:
./vendor/k8s.io/code-generator/cmd/defaulter-gen
+++ [0728 23:11:50] Building go targets for linux/amd64:
./vendor/k8s.io/code-generator/cmd/conversion-gen
+++ [0728 23:11:59] Building go targets for linux/amd64:
./vendor/k8s.io/kube-openapi/cmd/openapi-gen
+++ [0728 23:12:08] Building go targets for linux/amd64:
./vendor/github.com/jteeuwen/go-bindata/go-bindata
+++ [0728 23:12:10] Building go targets for linux/amd64:
cmd/kube-proxy
cmd/kube-apiserver
cmd/kube-controller-manager
cmd/cloud-controller-manager
cmd/kubelet
cmd/kubeadm
cmd/hyperkube
cmd/kube-scheduler
vendor/k8s.io/apiextensions-apiserver
cluster/gce/gci/mounter
+++ [0728 23:16:20] Building go targets for linux/amd64:
cmd/kube-proxy
cmd/kubeadm
cmd/kubelet
+++ [0728 23:16:48] Building go targets for linux/amd64:
cmd/kubectl
+++ [0728 23:17:03] Building go targets for linux/amd64:
cmd/gendocs
cmd/genkubedocs
cmd/genman
cmd/genyaml
cmd/genswaggertypedocs
cmd/linkcheck
vendor/github.com/onsi/ginkgo/ginkgo
test/e2e/e2e.test
+++ [0728 23:19:34] Building go targets for linux/amd64:
cmd/kubemark
vendor/github.com/onsi/ginkgo/ginkgo
test/e2e_node/e2e_node.test
+++ [0728 23:20:33] Syncing out of container
+++ [0728 23:20:44] Building tarball: src
+++ [0728 23:20:44] Building tarball: manifests
+++ [0728 23:20:44] Starting tarball: client linux-amd64
+++ [0728 23:20:44] Waiting on tarballs
+++ [0728 23:20:53] Building images: linux-amd64
+++ [0728 23:20:53] Building tarball: node linux-amd64
+++ [0728 23:21:01] Starting docker build for image: cloud-controller-manager-amd64
+++ [0728 23:21:01] Starting docker build for image: kube-apiserver-amd64
+++ [0728 23:21:01] Starting docker build for image: kube-controller-manager-amd64
+++ [0728 23:21:01] Starting docker build for image: kube-scheduler-amd64
+++ [0728 23:21:01] Starting docker build for image: kube-proxy-amd64
+++ [0728 23:21:01] Building hyperkube image for arch: amd64
+++ [0728 23:21:01] Building conformance image for arch: amd64
Sending build context to Docker daemon 36.64MB
Step 1/2 : FROM k8s.gcr.io/debian-iptables-amd64:v11.0.2
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
!!! [0728 23:21:24] Call tree:
!!! [0728 23:21:24] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:21:24] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:21:24] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:21:24] 4: build/release.sh:45 kube::release::package_tarballs(...)
Sending build context to Docker daemon 39.2MB
Step 1/2 : FROM k8s.gcr.io/debian-base-amd64:v1.0.0
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
!!! [0728 23:21:34] Call tree:
!!! [0728 23:21:34] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:21:34] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:21:34] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:21:34] 4: build/release.sh:45 kube::release::package_tarballs(...)
Sending build context to Docker daemon 115MB
Step 1/2 : FROM k8s.gcr.io/debian-base-amd64:v1.0.0
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
!!! [0728 23:21:40] Call tree:
!!! [0728 23:21:40] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:21:40] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:21:40] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:21:40] 4: build/release.sh:45 kube::release::package_tarballs(...)
Sending build context to Docker daemon 167MB
Step 1/2 : FROM k8s.gcr.io/debian-base-amd64:v1.0.0
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
!!! [0728 23:21:50] Call tree:
!!! [0728 23:21:50] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:21:50] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:21:50] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:21:50] 4: build/release.sh:45 kube::release::package_tarballs(...)
Sending build context to Docker daemon 99.91MB
Step 1/2 : FROM k8s.gcr.io/debian-base-amd64:v1.0.0
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
!!! [0728 23:22:00] Call tree:
!!! [0728 23:22:00] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:22:00] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:22:00] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:22:00] 4: build/release.sh:45 kube::release::package_tarballs(...)
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
make[1]: *** [build] Error 1
!!! [0728 23:22:00] Call tree:
!!! [0728 23:22:00] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:420 kube::release::build_hyperkube_image(...)
!!! [0728 23:22:00] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:22:00] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:22:00] 4: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:22:00] 5: build/release.sh:45 kube::release::package_tarballs(...)
Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
make[1]: *** [build] Error 1
!!! [0728 23:22:10] Call tree:
!!! [0728 23:22:10] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:424 kube::release::build_conformance_image(...)
!!! [0728 23:22:10] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:22:10] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:22:10] 4: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:22:10] 5: build/release.sh:45 kube::release::package_tarballs(...)
!!! [0728 23:22:10] previous Docker build failed
!!! [0728 23:22:10] Call tree:
!!! [0728 23:22:10] 1: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:231 kube::release::create_docker_images_for_server(...)
!!! [0728 23:22:10] 2: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:237 kube::release::build_server_images(...)
!!! [0728 23:22:10] 3: /root/go/src/k8s.io/kubernetes/build/lib/release.sh:93 kube::release::package_server_tarballs(...)
!!! [0728 23:22:10] 4: build/release.sh:45 kube::release::package_tarballs(...)
!!! [0728 23:22:10] previous tarball phase failed
make: *** [quick-release] Error 1
[root@localhost kubernetes]#

期望结果:期望make quick-release 成功执行完成。

学习前提条件

Must have:

  1. 准备一套环境已经安装好go软件
  2. 准备《Kubernetes源码剖析》书籍, 书籍是1.14版本。
  3. 准备一套K8S环境,要求版本在1.4或以上
  4. 阅读go 代码软件一个,推荐 IDEA

【提问】DeltaFIFO 消费者方法中 process 回调函数的实现

问题描述

在书本 156 页 DeltaFIFO 消费者方法 Pop 的实现中,会将 Delta 中存的 item 对象传入 process 回调函数,由上层消费者进行处理,一个 process 回调函数实现中,它对整个 item 进行了遍历,并通过 distribute 函数将资源对象分发到 SharedInformer,我没法理解这里为什么使用遍历的方法对整个 Obj 的资源对象进行检查,并且通知对应的事件处理函数

相关代码

一个回调函数的实现:

func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error {
	s.blockDeltas.Lock()
	defer s.blockDeltas.Unlock()

	// from oldest to newest
	for _, d := range obj.(Deltas) {
		switch d.Type {
		case Sync, Added, Updated:
			isSync := d.Type == Sync
			s.cacheMutationDetector.AddObject(d.Object)
			if old, exists, err := s.indexer.Get(d.Object); err == nil && exists {
				if err := s.indexer.Update(d.Object); err != nil {
					return err
				}
				s.processor.distribute(updateNotification{oldObj: old, newObj: d.Object}, isSync)
			} else {
				if err := s.indexer.Add(d.Object); err != nil {
					return err
				}
				s.processor.distribute(addNotification{newObj: d.Object}, isSync)
			}
		case Deleted:
			if err := s.indexer.Delete(d.Object); err != nil {
				return err
			}
			s.processor.distribute(deleteNotification{oldObj: d.Object}, false)
		}
	}
	return nil
}

备注

我的理解是这个 item 存储了这个 Obj 目前的整个进度,每当发生一个 DeltaType 就会触发一次,而 HandleDeltas 这个事件又有上锁,我的理解是这个分发操作应该只需要做最新的事件分发即可,为什么要每次都要进行遍历呢?如果这样的话,SharedInformer 又是怎么去 handle 重复事件的?

【分享】kubernetes 领域书籍推荐

分享内容

(简要介绍你分享的资料内容)

分享收获

(描述为什么分享这个资料?学习之后会有什么收获?)

备注

任何你想要表达的话语。

[提问] client-go中的request函数会在请求结束的时候丢弃resp.Body,这是出于什么考虑呢?

https://github.com/kubernetes/client-go/blob/6c5935290e3335b200f75d874a19be3093e9c36b/rest/request.go#L785

			defer func() {
				const maxBodySlurpSize = 2 << 10
				if resp.ContentLength <= maxBodySlurpSize {
					io.Copy(ioutil.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize})
				}
				resp.Body.Close()
			}()

不明白 io.Copy(ioutil.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize}) 有什么作用。

[活动] Kubernetes 源码剖析研习活动优秀总结汇总

hi,大家好,第一周的活动结束了,以下同学笔记经过Maintainer审核,其总结的内容很优秀,特此鼓励:

Github ID Wechat ID 笔记链接
AbyssViper @AbyssViper LTX-AbyssViper https://app.yinxiang.com/fx/86f469c5-e395-4c05-b33b-0a3baf1acf13
lianghao208 @lianghao208 roy-lianghao208 https://blog.csdn.net/qq_17305249/article/details/107802411
janeliul @JaneLiuL 娟-JaneLiuL https://github.com/JaneLiuL/study-client-go
strive-after @strive-after 帅波-strive-after https://blog.csdn.net/weixin_45413603/article/details/107349333
liangyuanpeng @liangyuanpeng 梁远鹏-liangyuanpeng https://zhuanlan.zhihu.com/p/173809214
Luffy110 @luffy110 姚沈结-Luffy110 https://luffyao.com/2020/08/clientgo/
YouEclipse @YouEclipse 吴垂优-YouEclipse https://blog.golang.im/source-k8s-client-go/
liuyangc3 @liuyangc3 web https://github.com/liuyangc3/note-book/blob/master/applications/k8s/client-go-week-1.md
herbguo @herbguo bin-herbguo https://herbguo.gitbook.io/client-go/client_obj

下面这些童鞋的笔记,虽然有些总结的并非上周的阅读内容,但总结得也非常好,请再接再厉:

Github ID Wechat ID 笔记链接
zForrest @zforrest 看山是山 https://zwforrest.github.io/post/informer/
shenhonglei @shenhonglei Honglei https://blog.csdn.net/shenhonglei1234/article/details/107898527
glyasai @GLYASAI 润豪-glyasai https://blog.abewang.io/2020/08/09/client-go-indexer/

client-go源码issue

例子理解

我们从一段最简单的代码入手,这段代码块主要是监听了namespace,对于有新的namespace添加,就打印namespace的名字,我们看看K8S源码,看看这个黑盒子实际在在K8S发生了什么动作?

func main() {
	// in cluster get config
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	// cilentset
	clientset, err := kubernetes.NewForConfig(config)
...

	// listen namespace informer for AddFunc
	stopCh := make(chan struct{})
	defer close(stopCh)

	shareInformers := informers.NewSharedInformerFactory(clientset, time.Second)
	informer := shareInformers.Core().V1().Namespaces().Informer()

	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func (obj interface{})  {
			nObj := obj.(v1.Object)
			log.Printf("New namespaces add %s", nObj.GetName())
		},
	})
	informer.Run(stopCh)
	
}

NewSharedInformerFactory

我们先看看这段代码首先使用了rest去获取kubeconfig, 然后初始化clientset对象, 接下来我们使用NewSharedInformerFactory工厂来获得shareInformers, 然后再使用调用Informer来创建真正的Informer

我们可以先来看看namespace资源实现了Informer机制的代码块, 实现了Informer和Lister方法(K8S内部的每一个资源都实现了Informer机制)

代码块staging/src/k8s.io/client-go/informers/core/v1/namespace.go

type NamespaceInformer interface {
	Informer() cache.SharedIndexInformer
	Lister() v1.NamespaceLister
}

我们看看NewSharedInformerFactory 工厂, NewFilteredSharedInformerFactory 工厂来获得factory 对象,这个对象中的 informers是一个map 类型的数据结构, key 为资源类型,而 value 便是关注该资源类型的 Informer。

QA

其实我看不出来同一类资源Informer是共享一个Reflector的...

代码块staging/src/k8s.io/client-go/informers/factory.go

func NewFilteredSharedInformerFactory(client kubernetes.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
	return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
}

// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
func NewSharedInformerFactoryWithOptions(client kubernetes.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
	factory := &sharedInformerFactory{
		client:           client,
		namespace:        v1.NamespaceAll,
		defaultResync:    defaultResync,
		informers:        make(map[reflect.Type]cache.SharedIndexInformer),
		startedInformers: make(map[reflect.Type]bool),
		customResync:     make(map[reflect.Type]time.Duration),
	}

	// Apply all options
	for _, opt := range options {
		factory = opt(factory)
	}

	return factory
}

AddEventHandler

继续,我们的代码现在走到了AddEventHandler, 可以看到这个接口实现了三个方法,我们的代码块只使用了其中的OnAddd去接受添加事件的触发

代码块staging/src/k8s.io/client-go/tools/cache/controller.go

type ResourceEventHandler interface {
	OnAdd(obj interface{})
	OnUpdate(oldObj, newObj interface{})
	OnDelete(obj interface{})
}

Informer.Run

最后我们通过informer.Run(stopCh)来执行,这段代码比较重要

代码块staging/src/k8s.io/client-go/tools/cache/shared_informer.go

func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()

    // 首先初始化了一个fifo的队列,我们可以看看下面DeltaFIFO的说明
	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, s.indexer)

    // 我们把我们的deltafifo的队列以及listerWatcher等去初始化一个Controller所需要的配置:cfg对象,可以看下方的controller章节
	cfg := &Config{
		Queue:            fifo,
		ListerWatcher:    s.listerWatcher,
		ObjectType:       s.objectType,
		FullResyncPeriod: s.resyncCheckPeriod,
		RetryOnError:     false,
		ShouldResync:     s.processor.shouldResync,

		Process: s.HandleDeltas,
	}

	func() {
		s.startedLock.Lock()
		defer s.startedLock.Unlock()

        // 创建了一个controller 实例
		s.controller = New(cfg)
		s.controller.(*controller).clock = s.clock
		s.started = true
	}()

    // 启动cacheMutationDetector
    // 启动了processor	
	processorStopCh := make(chan struct{})
	var wg wait.Group
	defer wg.Wait()              // Wait for Processor to stop
	defer close(processorStopCh) // Tell Processor to stop
    
	wg.StartWithChannel(processorStopCh, s.cacheMutationDetector.Run)
	wg.StartWithChannel(processorStopCh, s.processor.run)

	defer func() {
		s.startedLock.Lock()
		defer s.startedLock.Unlock()
		s.stopped = true // Don't want any new listeners
	}()
	s.controller.Run(stopCh)
}

DeltaFIFO

拆开理解,FIFO是一个先进先出的队列,然后Delta是一个资源对象的存储,可以保存资源对象的操作类型。

我们来看看DeltaFIFO的数据结构:

queue字段存储资源对象的key, 该key可以通过下面的KeyOf方法来得到,而items字段通过map数据结构的方式存储

type DeltaFIFO struct {
	lock sync.RWMutex
	cond sync.Cond
	items map[string]Deltas    
	queue []string
	populated bool
	initialPopulationCount int
	keyFunc KeyFunc	
	knownObjects KeyListerGetter
	closed     bool
	closedLock sync.Mutex
}

func (f *DeltaFIFO) KeyOf(obj interface{}) (string, error) {
	if d, ok := obj.(Deltas); ok {
		if len(d) == 0 {
			return "", KeyError{obj, ErrZeroLengthDeltasObject}
		}
		obj = d.Newest().Object
	}
	if d, ok := obj.(DeletedFinalStateUnknown); ok {
		return d.Key, nil
	}
	return f.keyFunc(obj)
}

从代码块staging/src/k8s.io/client-go/tools/cache/delta_fifo.go可以看出这个DeltaFIFO具有队列操作的级别方法,例如Added(添加)/Deleted等操作如下面代码块所示,理解一下,就是提供了生产者/消费者模式,,可以看看下面生产者消费者的小节。

QA

生产者是Reflector调用的Add方法,消费者是Controller调用的Pop方法 这里我没看出来这个顺序???咋看出来的

func (f *DeltaFIFO) Add(obj interface{}) error {
...
}

// Update is just like Add, but makes an Updated Delta.
func (f *DeltaFIFO) Update(obj interface{}) error {
...
}

func (f *DeltaFIFO) Delete(obj interface{}) error {	
...
}
DeltaFIFO生产者消费者方法
func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) {
	f.lock.Lock()
	defer f.lock.Unlock()
	for {
        // 队列是空得时候,就阻塞
		for len(f.queue) == 0 {
...
			f.cond.Wait()
		}
        // 从头部取第一个数据
		id := f.queue[0]
		f.queue = f.queue[1:]
		if f.initialPopulationCount > 0 {
			f.initialPopulationCount--
		}
		item, ok := f.items[id]
		if !ok {
			// Item may have been deleted subsequently.
			continue
		}
		delete(f.items, id)
        // process 提供了 EventHandler 注册和事件分发的功能,由上层消费者进行处理
		err := process(item)
		if e, ok := err.(ErrRequeue); ok {
			f.addIfNotPresent(id, item)
			err = e.Err
		}
		return item, err
	}
}

Controller

上方informer.Run的时候,初始化了Controller所需要配置Config的对象,我们看看Controller配置的

代码块staging/src/k8s.io/client-go/tools/cache/controller.go

type Config struct {
	// The queue for your objects - has to be a DeltaFIFO due to
	// assumptions in the implementation. Your Process() function
	// should accept the output of this Queue's Pop() method.
	Queue
	// Something that can list and watch your objects.
	ListerWatcher
	Process ProcessFunc
	ObjectType runtime.Object
	FullResyncPeriod time.Duration
	ShouldResync ShouldResyncFunc
	RetryOnError bool
}
QA

我从最简单的代码块,仍然是没办法像下图1 里面的顺序这样走下去,我的顺序更多是API Server-->ClientSet-->ShareInformerFactory-->AddEvent...

image

第一周的笔记比较惨,真的我没法按书里的顺序看下去。。按书的顺序我没法形成一个flow chart

[募集] 为Kubernetes 源码研习社募集三位 maintainer

2020年08月04日晚,Kubernetes 源码研习社第一期 kickoff 完成,确定了活动基于本仓库来提问、整理和归档问题,通过云原生社区官网、公众号和知识星球来分享讨论成功和经验的基本流程。

为了更好的组织本次活动,需要三位活动参加者作为本仓库的maintainer。

Maintainer 的权利

  1. 拥有本仓库的 maintainer 权限(push、管理 issue)
  2. 名誉学习委员,展示在本仓库 README 首页
  3. 召集学员对某个话题的讨论
  4. 分享将优先发表在云原生社区的官网、公众号及其他渠道

Maintainer 的义务

  1. 对一期学员在本仓库的 issue 进行格式化,包括 issue 标题的格式化、内容的模板的制定、内容的格式化、设置 label、做好分类、及时的关闭和处理 issue
  2. 对微信群中讨论的有价值的问题创建 issue 并持续跟踪
  3. 其他有助于成员之间交流和学习的事情

时限

  • Maintainer 可以长期担任
  • 每次新的一期 Kubernetes 源码研习社可以产生新的 Maintainer

成为 Maintainer 的条件

  1. Maintainer 从参加Kubernetes 源码剖析的一期学员中产生
  2. Maintainer 必须保证有充足的时间参与本次活动
  3. Maintainer 需要多 Kubernetes 及一期活动讨论的话题有一定认识
  4. Maintainer 通过自荐和一期研习会会长 @Miss-you@rootsongjc 确认产生

请大家自告奋勇自荐成为一期的学习委员。

【提问】pkg/kubelet/kubelet.go 中 syncpod函数内apiPodStatus和existingStatus

// Generate final API pod status with pod and status manager status
apiPodStatus := kl.generateAPIPodStatus(pod, podStatus)
...
// Record the time it takes for the pod to become running.
existingStatus, ok := kl.statusManager.GetPodStatus(pod.UID)

看了注释仍然不清晰这两个podStatus有什么作用
初步查看了代码有一下发现
1、apiPodStatus依赖于cache中的数据,而cache中的数据来自于PLEG周期获取宿主机上容器的状态
2、existingStatus通过statusManager获取,而statusManager的数据主要来自上面两段代码的之后一段代码

// Update status in the status manager
kl.statusManager.SetPodStatus(pod, apiPodStatus)

[提问]Informer 中为什么需要引入 Resync 机制?

Resync 机制会将 Indexer 的本地缓存重新同步到 DeltaFIFO 队列中。一般我们会设置一个时间周期,让 Indexer 周期性地将缓存同步到队列中。直接 list/watch apiserver 就已经能拿到集群中资源对象变化的 event 了,这里引入 Resync 的作用是什么呢?去掉会有什么影响呢?

【提问】pv_controller中的resync协程是什么作用?为什么这么设计?

问题描述

kube-controller中的pv_controller有三个协程:

  • resync:定期使用List方法,同步所有pvc到claimQueue,同步所有pv到volumeQueue;
  • volumeWorker:处理volumeQueue中的PV对象;
  • claimWorker:处理claimQueue中的PVC对象。

1)使用resync定期同步pvc和pv的必要性是什么?为什么这么设计?

2)在volumeWorker和claimWorker中,处理PV和PVC是异步的。因此,一些PV对象的处理失败,可能是对应PVC处理未完成导致的。所以,PV在处理失败的obj时,并未直接将其重新Add进volumeWorker队列,而是等待resync协程将其重新放入队列。此时,使用queue.AddRateLimited()这类方法,是不是也可以达到响应的目的?resync协程是否还有其他的作用?

环境信息

相关代码

pkg/controller/volume/persistentvolume

备注

Kubernetes源码研习社一期总结

大家好,源码研习社一期活动已经结束,感谢大家的积极参与。这一期我们以《Kubernetes源码剖析》第5章作为参考,深入的学习 Kubernetes 客户端库 client-go 的源码。

回想本次活动的初衷,我们做到了。

搭建一个由热爱学习、注重个人成长的一帮小伙伴们组成的小组,以Kubernetes为主题,每个人都能从中学到知识,帮助自己成长与进步。

学习过程

第一期活动共五周,根据学习要求精读《Kubernetes 源码剖析》和Client-go源码。

计划原定一周一小节内容,后由于源码难度以及学习深度,改为二周一小节。要求每一节都产出一篇200字以上的笔记或随想,最后1周为总结周。将自己的笔记或随想帖链接直接回复在 研习社事项跟踪 上。

20200909192504

每一节都有很多同学提交自己精读研究源码后的思考总结文章。

20200909193110

本期收获

  1. 我们组建了微信群,陆陆续续有382名同学加入,群内气氛活跃,每日都有同学交流讨论Kubernetes源码相关知识,互相学习帮助。
  2. 有30多位同学提交了学习笔记,很多笔记深入分析源码细节,每一篇读起来都收获颇丰。其中还有几篇投稿到社区,已发布在官网和公众号上。
  3. 很多同学不仅用文字记录下了自己的思考,还画了构思精巧有细节有全局的剖析图,搭配着图看源码很容易理解。

这些成果都是因为大家积极的学习热情。

  • 感谢所有参与活动,在群里活跃交流讨论的382名同学。
  • 感谢认真学习,提交笔记的同学。
  • 感谢打磨文章,投稿给社区的同学。

展望下一期

先对本次活动做个反思,相比较群内活跃的气氛,沉浸下来提交笔记的同学还是少了些,可能是由于主题难度与流程没有照顾到不同需求的同学。另外我们志愿者本应起到更大的推动作用,但应参与精力有限,没有达到较好的效果,在合作形式与安排上可以有更多的优化。

下一期我们可以分小组学习,安排任务从不同的角度去学习源码,不同需求的同学加入不同的小组,都可以参与到活动中并有所收获。

我们也需要招募更多的志愿者,详细计划落实下一期活动,欢迎大家加入志愿者小组。

下一期的主题暂定为 kube-scheduler

创建一个调查问卷

创建一个调查问卷,旨在了解研习会中成员对 Go 语言、Kubernetes 的了解程度。每个人的基础、可用时间都不一样,不可能保证所有人进度都一致,该问卷可以帮助研习会管理员控制研习进度。

问卷细节

  • 使用腾讯问卷
  • 在研习会微信群中发放,仅限研习会成员填写
  • 问卷内容待定

【提问】Reflector.lastSyncResourceVersion 是哪个资源的 resourceVersion

问题描述

Reflector.ListAndWatch 方法中 resourceVersion 相关代码如下:

// List 部分代码
var resourceVersion string
options := metav1.ListOptions{ResourceVersion: "0"}
list, err = r.listerWatcher.List(options)
listMetaInterface, err := meta.ListAccessor(list)
resourceVersion = listMetaInterface.GetResourceVersion()
r.setLastSyncResourceVersion(resourceVersion)
// Watch 部分代码
		options = metav1.ListOptions{
			ResourceVersion: resourceVersion,
			// We want to avoid situations of hanging watchers. Stop any wachers that do not
			// receive any events within the timeout window.
			TimeoutSeconds: &timeoutSeconds,
		}
                w, err := r.listerWatcher.Watch(options)
                err := r.watchHandler(w, &resourceVersion, resyncerrc, stopCh)

watch 处理的逻辑在 watchHandler 函数,如下:

event, ok := <-w.ResultChan()
meta, err := meta.Accessor(event.Object)
newResourceVersion := meta.GetResourceVersion()
*resourceVersion = newResourceVersion
r.setLastSyncResourceVersion(newResourceVersion)

开始 List 后 resourceVersion 获取的是列表的资源版本 resourceVersion = listMetaInterface.GetResourceVersion()
列表的资源版本是空值(这里有疑问,懂的同学解释一下)
Watch 的时候 resourceVersion 传入空值表示 Get State and Start at Most Recent
在 watchHandler 里根据最新资源的版本更新 resourceVersion,这样后续的 Watch 就会根据最新的资源版本监听。 Watch resourceVersion="{value other than 0}" 表示 Start at Exact

这里理解的核心在于 Watch 调用对 resourceVersion 的处理。

一般觉得每个资源都会有 resourceVersion,那 Reflector 里一个 resourceVersion 怎么会够用,而且这个值一会儿是 List 的,一会儿是具体资源的。。。

[提问]client-go restclient 封装http

func (r *Request) Do(ctx context.Context) Result {
	var result Result
	err := r.request(ctx, func(req *http.Request, resp *http.Response) {
		result = r.transformResponse(resp, req)
	})
	if err != nil {
		return Result{err: err}
	}
	return result
}


func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Response)) error {
...
    resp, err := client.Do(req)
...
    fn(req, resp)
    return true
}

问题: 为什么Do要用传入 fn 的形式 去转换 Response, 直接按照下面的写不行么, 例如

func (r *Request) Do(ctx context.Context) Result {
	result, err := r.request(ctx)
        if err != nil {
		return Result{err: err}
	}
	return result
}

func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Response)) (Result, error) {
...
    resp, err := client.Do(req)
...
   result = r.transformResponse(resp, req)
   return result, nil
}

【提问】kube-proxy 在 IPVS 模式 获取来源IP问题;

问题描述

Node01 上面的Pod01 通过 service 去访问 Node02 上面Pod02 提供的服务,在Pod02 上面查看来源 IP 时,显示的是 Node01 的IP,而不是Pod01 的IP,如何获取 Pod01 的 IP?

环境信息

CNI:Calico BGP
kube-proxy: IPVS
ServiceType: ClusterIP

注意:经测试 ClusterIP 不支持 Service的externalTrafficPolicy: Local ,无法通过这个方式实现;

【提问】Operator开发中,如何解决资源对象太大,导致数据过多的时候拉胯ETCD

问题描述

请教一下,Operator开发中,资源对象过大,如果历史数据较多的情况下,势必会拉胯 etcd。

按照平均每个资源对象3000个字符,假设都是中文字符且在UTF-8编码中,那么每个字符占3个字节的,这样一换算,每个资源大约 9KB,如果这个资源是个被周期性任务创建产生的,每个小时创建 1000 个,那么一天数据量为:9KB * 1000 * 24 = 211MB, 这么大的数据量,如果又要将这些历史数据保存 1 个月以上,etcd 势必会被压垮。

我先抛砖引玉,分享一下我想到的解决方案:

  1. 起一个 watchService,将资源对象从 etcd 中卸载到 db 上(如 mysql)

不知道各位大佬有没有其他更好的实践分享一下,感谢!

Kubernetes 源码研习社 Kickoff

2020年07月23 日,Kubernetes 源码研习社正式成立,并公告成员招募。

一夜之间,成员规模就达到了 200 余人,为了更高效的组织该活动,云原生社区计划举行一次研习社 Kickoff,使用 zoom 会议(限额 100 人)+ B 站直播的形式,和大家共同探讨活动的组织形式,征集下大家对该活动的诉求和建议。

更多信息请持续关注该 issue。

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.