文档章节

Kubernetes PodGC Controller源码分析

WaltonWang
 WaltonWang
发布于 2017/08/13 11:50
字数 1473
阅读 204
收藏 2
点赞 0
评论 0

Author: xidianwangtao@gmail.com

PodGC Controller配置

关于PodGC Controller的相关配置(kube-controller-manager配置),一共只有两个:

flagdefault valuecomments
--controllers stringSlice*这里配置需要enable的controlllers列表,podgc当然也可以在这里设置是都要enable or disable,默认podgc是在enable列表中的。
--terminated-pod-gc-threshold int3212500Number of terminated pods that can exist before the terminated pod garbage collector starts deleting terminated pods. If <= 0, the terminated pod garbage collector is disabled. (default 12500)

PodGC Controller入口

PodGC Controller是在kube-controller-manager Run的时候启动的。CMServer Run时会invoke StartControllers将预先注册的enabled Controllers遍历并逐个启动。

cmd/kube-controller-manager/app/controllermanager.go:180

func Run(s *options.CMServer) error {
   ...
	err := StartControllers(newControllerInitializers(), s, rootClientBuilder, clientBuilder, stop)
	...
}

在newControllerInitializers注册了所有一些常规Controllers及其对应的start方法,为什么说这些是常规的Controllers呢,因为还有一部分Controllers没在这里进行注册,比如非常重要的service Controller,node Controller等,我把这些称为非常规Controllers

func newControllerInitializers() map[string]InitFunc {
	controllers := map[string]InitFunc{}
	controllers["endpoint"] = startEndpointController
	...
	controllers["podgc"] = startPodGCController
	...

	return controllers
}

因此CMServer最终是invoke startPodGCController来启动PodGC Controller的。

cmd/kube-controller-manager/app/core.go:66

func startPodGCController(ctx ControllerContext) (bool, error) {
	go podgc.NewPodGC(
		ctx.ClientBuilder.ClientOrDie("pod-garbage-collector"),
		ctx.InformerFactory.Core().V1().Pods(),
		int(ctx.Options.TerminatedPodGCThreshold),
	).Run(ctx.Stop)
	return true, nil
}

startPodGCController内容很简单,启动一个goruntine协程,创建PodGC并启动执行。

PodGC Controller的创建

我们先来看看PodGCController的定义。

pkg/controller/podgc/gc_controller.go:44

type PodGCController struct {
	kubeClient clientset.Interface

	podLister       corelisters.PodLister
	podListerSynced cache.InformerSynced

	deletePod              func(namespace, name string) error
	terminatedPodThreshold int
}
  • kubeClient: 用来跟APIServer通信的client。
  • PodLister: PodLister helps list Pods.
  • podListerSynced: 用来判断PodLister是否Has Synced。
  • deletePod: 调用apiserver删除对应pod的接口。
  • terminatedPodThreshold: 对应--terminated-pod-gc-threshold的配置,默认为12500。
pkg/controller/podgc/gc_controller.go:54

func NewPodGC(kubeClient clientset.Interface, podInformer coreinformers.PodInformer, terminatedPodThreshold int) *PodGCController {
	if kubeClient != nil && kubeClient.Core().RESTClient().GetRateLimiter() != nil {
		metrics.RegisterMetricAndTrackRateLimiterUsage("gc_controller", kubeClient.Core().RESTClient().GetRateLimiter())
	}
	gcc := &PodGCController{
		kubeClient:             kubeClient,
		terminatedPodThreshold: terminatedPodThreshold,
		deletePod: func(namespace, name string) error {
			glog.Infof("PodGC is force deleting Pod: %v:%v", namespace, name)
			return kubeClient.Core().Pods(namespace).Delete(name, metav1.NewDeleteOptions(0))
		},
	}

	gcc.podLister = podInformer.Lister()
	gcc.podListerSynced = podInformer.Informer().HasSynced

	return gcc
}

创建PodGC Controller时其实只是把相关的PodGCController元素进行赋值。注意deletePod方法定义时的参数metav1.NewDeleteOptions(0),表示立即删除pod,没有grace period。

PodGC Controller的运行

创建完PodGC Controller后,接下来就是执行Run方法启动执行了。

pkg/controller/podgc/gc_controller.go:73

func (gcc *PodGCController) Run(stop <-chan struct{}) {
	if !cache.WaitForCacheSync(stop, gcc.podListerSynced) {
		utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
		return
	}

	go wait.Until(gcc.gc, gcCheckPeriod, stop)
	<-stop
}
  • 每100ms都会去检查对应的PodLister是否Has Synced,直到Has Synced。
  • 启动goruntine协程,每执行完一次gcc.gc进行Pod回收后,等待20s,再次执行gcc.gc,直到收到stop信号。
pkg/controller/podgc/gc_controller.go:83

func (gcc *PodGCController) gc() {
	pods, err := gcc.podLister.List(labels.Everything())
	if err != nil {
		glog.Errorf("Error while listing all Pods: %v", err)
		return
	}
	if gcc.terminatedPodThreshold > 0 {
		gcc.gcTerminated(pods)
	}
	gcc.gcOrphaned(pods)
	gcc.gcUnscheduledTerminating(pods)
}

gcc.gc是最终的pod回收逻辑:

  • 调从PodLister中去除所有的pods(不设置过滤)
  • 如果terminatedPodThreshold大于0,则调用gcc.gcTerminated(pods)回收那些超出Threshold的Pods。
  • 调用gcc.gcOrphaned(pods)回收Orphaned pods。
  • 调用gcc.gcUnscheduledTerminating(pods)回收UnscheduledTerminating pods。

注意:

  1. gcTerminated和gcOrphaned,gcUnscheduledTerminating这三个gc都是串行执行的。
  2. gcTerminated删除超出阈值的pods的删除动作是并行的,通过sync.WaitGroup等待所有对应的pods删除完成后,gcTerminated才会结束返回,才能开始后面的gcOrphaned.
  3. gcOrphaned,gcUnscheduledTerminatin,gcUnscheduledTerminatin内部都是串行gc pods的。

回收那些Terminated的pods

func (gcc *PodGCController) gcTerminated(pods []*v1.Pod) {
	terminatedPods := []*v1.Pod{}
	for _, pod := range pods {
		if isPodTerminated(pod) {
			terminatedPods = append(terminatedPods, pod)
		}
	}

	terminatedPodCount := len(terminatedPods)
	sort.Sort(byCreationTimestamp(terminatedPods))

	deleteCount := terminatedPodCount - gcc.terminatedPodThreshold

	if deleteCount > terminatedPodCount {
		deleteCount = terminatedPodCount
	}
	if deleteCount > 0 {
		glog.Infof("garbage collecting %v pods", deleteCount)
	}

	var wait sync.WaitGroup
	for i := 0; i < deleteCount; i++ {
		wait.Add(1)
		go func(namespace string, name string) {
			defer wait.Done()
			if err := gcc.deletePod(namespace, name); err != nil {
				// ignore not founds
				defer utilruntime.HandleError(err)
			}
		}(terminatedPods[i].Namespace, terminatedPods[i].Name)
	}
	wait.Wait()
}
  • 遍历所有pods,过滤出所有Terminated Pods(Pod.Status.Phase不为Pending, Running, Unknow的Pods).
  • 计算terminated pods数与terminatedPodThreshold的(超出)差值deleteCount。
  • 启动deleteCount数量的goruntine协程,并行调用gcc.deletePod(invoke apiserver's api)方法立刻删除对应的pod。

回收那些Binded的Nodes已经不存在的pods

// gcOrphaned deletes pods that are bound to nodes that don't exist.
func (gcc *PodGCController) gcOrphaned(pods []*v1.Pod) {
	glog.V(4).Infof("GC'ing orphaned")
	// We want to get list of Nodes from the etcd, to make sure that it's as fresh as possible.
	nodes, err := gcc.kubeClient.Core().Nodes().List(metav1.ListOptions{})
	if err != nil {
		return
	}
	nodeNames := sets.NewString()
	for i := range nodes.Items {
		nodeNames.Insert(nodes.Items[i].Name)
	}

	for _, pod := range pods {
		if pod.Spec.NodeName == "" {
			continue
		}
		if nodeNames.Has(pod.Spec.NodeName) {
			continue
		}
		glog.V(2).Infof("Found orphaned Pod %v assigned to the Node %v. Deleting.", pod.Name, pod.Spec.NodeName)
		if err := gcc.deletePod(pod.Namespace, pod.Name); err != nil {
			utilruntime.HandleError(err)
		} else {
			glog.V(0).Infof("Forced deletion of orphaned Pod %s succeeded", pod.Name)
		}
	}
}

gcOrphaned用来删除那些bind的node已经不存在的pods。

  • 调用apiserver接口,获取所有的Nodes。
  • 遍历所有pods,如果pod bind的NodeName不为空且不包含在刚刚获取的所有Nodes中,则串行逐个调用gcc.deletePod删除对应的pod。

回收Unscheduled并且Terminating的pods

pkg/controller/podgc/gc_controller.go:167

// gcUnscheduledTerminating deletes pods that are terminating and haven't been scheduled to a particular node.
func (gcc *PodGCController) gcUnscheduledTerminating(pods []*v1.Pod) {
	glog.V(4).Infof("GC'ing unscheduled pods which are terminating.")

	for _, pod := range pods {
		if pod.DeletionTimestamp == nil || len(pod.Spec.NodeName) > 0 {
			continue
		}

		glog.V(2).Infof("Found unscheduled terminating Pod %v not assigned to any Node. Deleting.", pod.Name)
		if err := gcc.deletePod(pod.Namespace, pod.Name); err != nil {
			utilruntime.HandleError(err)
		} else {
			glog.V(0).Infof("Forced deletion of unscheduled terminating Pod %s succeeded", pod.Name)
		}
	}
}

gcUnscheduledTerminating删除那些terminating并且还没调度到某个node的pods。

  • 遍历所有pods,过滤那些terminating(pod.DeletionTimestamp != nil)并且未调度成功的(pod.Spec.NodeName为空)的pods。
  • 串行逐个调用gcc.deletePod删除对应的pod。

总结

PodGC Controller作为Kubernetes默认启动的Controllers之一,在Master后台每隔20s进行一次Pod GC。

  • 通过--controllers可以控制PodGC Controller的开关。
  • 通过--terminated-pod-gc-threshold设置gcTerminated的阈值。
  • PodGC Controller串行的执行以下三个gc子过程:
    • 回收超过阈值的Terminated Pods(Pod.Status.Phase不为Pending, Running, Unknow的Pods)。
    • 回收那些binded的node已经不存在(不在etcd中)的pods。
    • 回收那些terminating并且还没调度到某个node的pods。

© 著作权归作者所有

共有 人打赏支持
WaltonWang
粉丝 159
博文 88
码字总数 182269
作品 0
深圳
程序员
Kubernetes权威指南精彩段落

看到上述两段代码,你可能会有一种“开门复动竹, 疑是故人来”的感觉。的确,这段代码经笔者反复考证后认定:100%高仿kubernetes Controller Server中的代码。连传说中的谷歌大神也能copy代码...

mycat ⋅ 2015/07/31 ⋅ 0

Kubernetes系统架构简介[转]

1. 前言 Together we will ensure that Kubernetes is a strong and open container management framework for any application and in any environment, whether in a private, public or ......

长征2号 ⋅ 2017/07/04 ⋅ 0

【读书笔记】Cronjob原理及源码分析

原文链接:https://mp.weixin.qq.com/s?biz=MzI0NjI4MDg5MQ==&mid=2715291842&idx=1&sn=e605f9b4099d946f94ca6783a8e4a6f6 背景: CronJob是Kubernetes提供的定时任务功能,CronJob可以根据你......

猫猫maomao ⋅ 06/15 ⋅ 0

浙江大学软件工程实验室关于Docker和KUBERNETES的分析文章

DOCKER源码分析(一):DOCKER架构, 2014.12.02, http://www.sel.zju.edu.cn/?p=112 DOCKER源码分析(二):DOCKER CLIENT创建与命令执行, 2014.12.02, http://www.sel.zju.edu.cn/?p=147 DO......

一配 ⋅ 2015/08/30 ⋅ 1

全视角了解基于容器的编排工具Kubernetes

Kubernetes在希腊语中是“船长”或者“水手”的意思,Kubernetes诞生于谷歌,2014年开源给了CNCF。它由Go语言开发,目标是建造一个运行大量容器生产环境的强大平台。Kubernetes库可以在GitHu...

m2l0zgssvc7r69efdtj ⋅ 05/24 ⋅ 0

谷歌生产级别开源容器调度和管理工具Kubernetes学习

一 Kubernetes介绍 1.Kubernetes是什么 Kubernetes是一个用于在由多个主机组成的集群上对应用容器自动化部署,扩容和运维的开源平台,它提供一个以容器为中心的基础设施。 利用Kubernetes,你...

自由linux ⋅ 2016/08/19 ⋅ 0

Kubernetes 惊天地泣鬼神之大Bug

自从三月份生产、非生产全面上线 Kubernetes 后,本以为部署的问题可以舒一口气了,但是断断续续在生产、非生产环境发现一个诡异的问题,这礼拜又调试了三天,在快要放弃的时候居然找到原因了...

m2l0zgssvc7r69efdtj ⋅ 05/25 ⋅ 0

如何基于 Kubernetes 开发高可靠服务?

Kubernetes 是当前主流的容器编排服务,它主要解决「集群环境」下「容器化应用」的「管理问题」,主要包括如下几方面: 容器集群管理: 编排 调度 访问 基础设施管理: 计算资源 网络资源 存...

Docker ⋅ 05/01 ⋅ 0

一文读懂百度PaddlePaddle EDL技术

最近看到 Kubernetes 官方博客上发表特邀文章 http://blog.kubernetes.io/2017/12/paddle-paddle-fluid-elastic-learning.html,介绍了百度和 CoreOS 的联合开发的 PaddlePaddle Elastic Dee......

技术小能手 ⋅ 2017/12/18 ⋅ 0

Kubernetes之scheduler模块源码分析

传送门 哈哈,隔了太长时间,网上已经有对应的分析,而且我看了以后觉得写的还真的挺好的,基本想要写的他都写的。 Kubernetes Scheduler原理解析 Kubernetes Scheduler源码分析 如何对kuber...

weixin_38975685 ⋅ 2017/09/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Python爬虫,抓取淘宝商品评论内容

作为一个资深吃货,网购各种零食是很频繁的,但是能否在浩瀚的商品库中找到合适的东西,就只能参考评论了!今天给大家分享用python做个抓取淘宝商品评论的小爬虫! 思路 我们就拿“德州扒鸡”...

python玩家 ⋅ 7分钟前 ⋅ 0

MySQL 内核深度优化

MYSQL数据库适用场景广泛,相较于Oracle、DB2性价比更高,Web网站、日志系统、数据仓库等场景都有MYSQL用武之地,但是也存在对于事务性支持不太好(MySQL 5.5版本开始默认引擎才是InnoDB事务...

java高级架构牛人 ⋅ 30分钟前 ⋅ 0

用户登录信息-钉子效果(基于jquery2.0)

本js效果使用jquery2.0,清晰的分解用户登录信息的(钉子效果),该效果直接用在作者网站(www.phpkhbd.com)上。 里面的难点有:定时器,延时。 大致效果如下: 一开始: 鼠标放上去的时候:...

宁哥实战课堂 ⋅ 31分钟前 ⋅ 0

解决yum安装报错Protected multilib versions

使用yum安装报错Protected multilib versions原因是因为多个库不能共存,不过更新的话也并不行,但是可以在安装命令后面加上如下一段命令: --setopt=protected_multilib=false 案例: 比如需...

北岩 ⋅ 42分钟前 ⋅ 0

为什么要学习Typescript???

简单来说 目前的typescript就是未来的javascript 为什么?? 这要从ECMA-262标准的第4版说起 对了 我们说的ES5 其实是ECMAScript3.1这个替代性建议被扶正了而已... 那么 第4版标准是什么? 看看...

hang1989 ⋅ 47分钟前 ⋅ 0

linux安装ipfs

一、下载ipfs # cd /usr/local/ipfs/ # wget https://dist.ipfs.io/go-ipfs/v0.4.15/go-ipfs_v0.4.15_linux-amd64.tar.gz # tar -zxvf go-ipfs_v0.4.15_linux-amd64.tar.gz 二、安装ipfs # ......

八戒八戒八戒 ⋅ 52分钟前 ⋅ 0

jvm程序执行慢诊断手册

生产环境最多的几种事故之一就是程序执行慢,如果是web服务的话,表现就是响应时间长。本文分享,从业多年形成的排查守则。 诊断步骤 系统资源查看 首先是系统资源查看,而且必须是在第一步。...

xpbob ⋅ 53分钟前 ⋅ 0

YII2 advanced 高级版本项目搭建-添加API应用以及多应用

一、YII安裝 安裝yii可以用composer安裝,也可以在yii中文社区下载归档文件安装 composer安装就不介绍了,因为要安装composer,比较麻烦,当然安装了composer是最好的,以后安装yii的插件要用...

botkenni ⋅ 54分钟前 ⋅ 0

在jdk1.8的环境下模拟永久代内存溢出

相信不少小伙伴在看深入理解Java虚拟机的时候,作者给我们举例一个demo来发生PermGen space 1、通过List不断添加String.intern(); 2、通过设置对应的-XX:PermSize与-XX:MaxPermSize(更快看到...

虾几把写 ⋅ 今天 ⋅ 0

开发OpenDaylight组件的完整流程

在前面介绍学习了OpenDaylight的几个重要模块后,这里再来介绍下完整开发一个模块的过程。 OSGI的bundles提供被其他OSGI组件调用的服务。这个教程中展示的是Data Packet Service去解析数据包...

wangxuwei ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部