Analyse du code source du mécanisme d'expulsion de Kubelet

1. Vue d'ensemble:

1.1 Environnement de code source

Les informations de version sont les suivantes:
a. Cluster kubernetes: v1.15.0

1.2 Deux lignes de défense pour maintenir la stabilité des nœuds

Pour le système d'exploitation Linux, la tranche de temps CPU, la mémoire, la capacité du disque, l'inode, le PID, etc. sont toutes des ressources système.

Une classification des ressources système:
a, ressources compressibles (CPU)
b, ressources incompressibles (mémoire, capacité disque et inode, PID)

En tant qu'agent de nœud, kubelet a naturellement besoin d'un certain mécanisme pour garantir que les ressources du serveur ne seront pas épuisées. Lorsque les ressources compressibles ne sont pas suffisantes, le Pod (conteneur ou processus) sera affamé, mais il ne sera pas forcé de quitter le système d'exploitation et le kubelet. Lorsque les ressources incompressibles (telles que la mémoire) ne sont pas suffisantes, la couche kubelet peut récupérer certaines ressources du serveur à l'avance (supprimer les images inutilisées, nettoyer les conteneurs sortis et les pods défaillants, tuer les pods en cours d'exécution) et le MOO du système d'exploitation Linux. dernière ligne de défense, KILLER a également la possibilité de tuer directement le processus en mode utilisateur.

Le kubelet tue le pod en cours d'exécution. Ce processus est appelé expulsion. Kubelet supprime les images inutilisées, nettoie les conteneurs retirés et les pods en panne. Ce processus s'appelle le recyclage des ressources au niveau du nœud.

Par conséquent, il existe deux lignes de défense pour maintenir la stabilité du nœud: la première ligne de défense est le processus utilisateur kubelet, et la deuxième ligne de défense est Linux OOM KILLER.


1.3 Mécanisme de kubelet pour maintenir la stabilité des nœuds

Lorsque les ressources incompressibles (mémoire, capacité du disque et inode, PID) ne sont pas suffisamment utilisées, cela affectera sérieusement la stabilité du nœud. Pour cette raison, kubelet, en tant qu'agent de nœud du processus en mode utilisateur Linux et du cluster k8s, devrait avoir un certain mécanisme pour maintenir la stabilité du nœud. L'essentiel est d'atteindre l'objectif en supprimant l'image et en arrêtant le processus du conteneur.


1.3.1 Seuil de ressources, les utilisateurs ont le dernier mot

Ce kubelet a besoin d'obtenir régulièrement les informations globales des ressources des nœuds, ce qui est inévitable.
Combien de ressources restent sur le serveur pour être considérées comme des ressources incompressibles "ne suffit pas"? Cette base "pas assez" est spécifiée par l'utilisateur dans le fichier de configuration de kubelet.
Tant que kubelet obtient la capacité des ressources du nœud, l'utilisation réelle (taux) et le seuil configuré par l'utilisateur, il estime que les "conditions sont remplies" peuvent commencer à supprimer l'image et arrêter le processus du conteneur.


1.3.2 Expulsion douce et dure

"Les conditions sont remplies", attendez zéro seconde pour commencer à supprimer l'image et arrêter le processus de conteneur, c'est une expulsion difficile.
"Les conditions sont remplies", attendez N (N> 0) secondes pour démarrer l'opération de suppression de l'image et d'arrêt du processus de conteneur, qui est une expulsion douce. Le nombre N peut être défini par le paramètre -eviction-soft-grace-period de kubelet. De plus, pour l'expulsion logicielle, le paramètre --eviction-max-pod-grace-period affecte le temps pendant lequel le gestionnaire d'expulsion attend que kubelet nettoie le pod (le temps d'attente réel est eviction-max-pod-grace-period + ( expulsion-max-pod-grace-period / 2)).


1.3.3 Création d'un bloc de pod

Lorsque les ressources incompressibles du nœud ne sont "pas suffisantes", kubelet doit également disposer d'un mécanisme pour bloquer la création de Pod, qui est lié à la méthode Admit (...) du gestionnaire d'expulsion.

1.3.4 Affecter Linux OOM KILLER

Kubelet est la première ligne de défense pour maintenir la stabilité du serveur.Si vous ne pouvez pas le comprendre, alors la deuxième ligne de défense, Linux OOM KILLER, viendra inévitablement au fond. La corrélation entre la première ligne de défense et la deuxième ligne de défense est oom_score_adj.
Le Qos du pod est différent, kubelet configure différents scores pour oom_score_adj, et le oom_score_adj du processus est un facteur qui affecte la destruction par Linux OOM KILLER des processus en mode utilisateur.

Insérez la description de l'image ici

Autres instructions:
1) Modèle de commande pour afficher le score oom_score_adj du processus du conteneur dans le Pod:
kubectl exec demo-qos-pod cat / proc / 1 / oom_score_adj


1.4 Présentation de Linux OOM KILLER

1) Linux répond «oui» à la plupart des demandes d'application de mémoire, de sorte que de plus en plus de programmes plus volumineux puissent être exécutés. Parce qu'après avoir demandé la mémoire, la mémoire ne sera pas utilisée immédiatement. Cette technique s'appelle Overcommit.

2) Lorsque Linux trouve que la mémoire est insuffisante, un tueur OOM (OOM = out-of-memory) se produit. À ce stade, il choisit de tuer certains processus (processus en mode utilisateur, pas les threads du noyau) afin de libérer de la mémoire .

3) Quand oom-killer se produit, quels processus Linux choisira-t-il de tuer? Linux calculera le nombre de points (0 ~ 1000) pour chaque processus. Plus le nombre de points est élevé, plus ce processus est susceptible d'être tué.
La formule des points oom:

进程OOM点数 = oom_score + oom_score_adj 

#oom_score est lié à la mémoire consommée par le processus.
#oom_score_adj est configurable et la plage de valeurs est -1000 est la plus basse et 1000 est la plus élevée.

4) Le journal oom se trouve dans le fichier / var / log / messages, qui appartient au journal du noyau Vous pouvez afficher le journal du noyau via la commande grep kernel / var / log / messages.
Insérez la description de l'image ici


Analyse du code source de l'expulsion de 2 kubelet:

2.1 Méthodes clés

																				|-> kl.cadvisor.Start()  // 启动cadvisor,cadvisor能获取节点信息和容器信息
																				|
		  //启动							//初始化额外模块,只会运行一次		        |
Kubelet -> Run() -> updateRuntimeUp()  ->  initializeRuntimeDependentModules -->|-> kl.containerManager.Start()  //containerManager需要cAdvisor提供的文件系统信息
																				|
																				|
																				|	//evictionManager需要通过cadvisor得知容器运行时是否具备一个独立专用的imagefs
																				|
																				|-> kl.evictionManager.Start() -> 定时任务 ->| -> m.synchronize() -> | -> m.reclaimNodeLevelResources(...)  //清理节点级别的资源(镜像、已退出的容器)
																															|					    |
																															| 	                    | -> m.evictPod(...)  //清理pod对象
																															|
																															| -> m.waitForPodsCleanup(...)   //如果m.synchronize()驱逐了pod,执行本方法进行等待pod被清理.

2.2 Signaux pris en charge pouvant déclencher l'expulsion de Pod

Lorsque la mémoire, la capacité du disque, le numéro d'inœud du disque et le nombre de pid sont insuffisants, kubelet peut être déclenché pour nettoyer les images et les conteneurs afin d'atteindre l'objectif de récupération des ressources du nœud.

// Signal defines a signal that can trigger eviction of pods on a node.
type Signal string

/*
分类:
1)内存
2)磁盘容量
3)磁盘inode数量
4)pid数量
*/
const (
	// SignalMemoryAvailable is memory available (i.e. capacity - workingSet), in bytes.
	SignalMemoryAvailable Signal = "memory.available"
	
	// SignalNodeFsAvailable is amount of storage available on filesystem that kubelet uses for volumes, daemon logs, etc.
	// nodefs是kubelet使用的卷、进程日志所在的文件系统
	SignalNodeFsAvailable Signal = "nodefs.available"
	
	// SignalNodeFsInodesFree is amount of inodes available on filesystem that kubelet uses for volumes, daemon logs, etc.
	SignalNodeFsInodesFree Signal = "nodefs.inodesFree"
	
	// SignalImageFsAvailable is amount of storage available on filesystem that container runtime uses for storing images and container writable layers.
	// imagefs是容器运行时(例如docker)的镜像层、读写层所在的文件系统
	SignalImageFsAvailable Signal = "imagefs.available"
	
	// SignalImageFsInodesFree is amount of inodes available on filesystem that container runtime uses for storing images and container writable layers.
	SignalImageFsInodesFree Signal = "imagefs.inodesFree"
	
	// SignalAllocatableMemoryAvailable is amount of memory available for pod allocation (i.e. allocatable - workingSet (of pods), in bytes.
	SignalAllocatableMemoryAvailable Signal = "allocatableMemory.available"
	
	// SignalPIDAvailable is amount of PID available for pod allocation
	SignalPIDAvailable Signal = "pid.available"
)

2.3 Code général

//kubelet的启动入口
func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) {
	/*
		其他代码
	*/
	
	go wait.Until(kl.updateRuntimeUp, 5*time.Second, wait.NeverStop)
	
}


// 当容器运行时第一次运行,初始化一些它依赖的模块
func (kl *Kubelet) updateRuntimeUp() {
	/*
		其他代码
	*/
	
	kl.oneTimeInitializer.Do(kl.initializeRuntimeDependentModules)

}

//启动cadvisor、containerManager和本文的核心研究对象evictionManager
func (kl *Kubelet) initializeRuntimeDependentModules() {

	// 启动cadvisor
	if err := kl.cadvisor.Start(); err != nil {		
	}
	
	/*
		其他代码
	*/	
	
	// containerManager必须在cAdvisor之后启动,因为它需要cAdvisor提供的文件系统信息
	if err := kl.containerManager.Start(node, kl.GetActivePods, kl.sourcesReady, kl.statusManager, kl.runtimeService); err != nil {		
	}
	
	//evictionManager是本文的核心
	// evictionManager必须在cAdvisor之后启动,因为它需要知道容器运行时是否具备一个独立专用的imagefs
	kl.evictionManager.Start(kl.StatsProvider, kl.GetActivePods, kl.podResourcesAreReclaimed, evictionMonitoringPeriod)
	
	/*
		其他代码
	*/	
}

Les principaux attributs de la structure du gestionnaire des expulsions

// managerImpl implements Manager
type managerImpl struct {
	
	//kubelet的配置文件中和驱逐相关的字段会赋值到本属性
	config Config
	
	//杀死Pod的方法
	//--eviction-max-pod-grace-period参数和此方法相关
	killPodFunc KillPodFunc
	
	
	// the interface that knows how to do image gc
	imageGC ImageGC
	// the interface that knows how to do container gc
	containerGC ContainerGC
	// protects access to internal state
	
	// 达到阈值的不可压缩资源,就会出现在本属性
	thresholdsMet []evictionapi.Threshold
	
	// 对Pod进行排序的方法保存在此属性
	signalToRankFunc map[evictionapi.Signal]rankFunc
	// signalToNodeReclaimFuncs maps a resource to an ordered list of functions that know how to reclaim that resource.
	
	thresholdNotifiers []ThresholdNotifier

}	

2.4 Comment démarrer le gestionnaire d'expulsions

func (m *managerImpl) Start(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc, podCleanedUpFunc PodCleanedUpFunc, monitoringInterval time.Duration) {
	/*
	其他代码
	*/
	
	//通过for循环和睡眠来定时执行m.synchronize()方法
	go func() {
		for {
		    //m.synchronize()方法内部有清理 节点资源 和 Pod 的逻辑
			if evictedPods := m.synchronize(diskInfoProvider, podFunc); evictedPods != nil {
				m.waitForPodsCleanup(podCleanedUpFunc, evictedPods)
			} else {
			    //睡眠
				time.Sleep(monitoringInterval)
			}
		}
	}()
}

2.4 La logique principale du gestionnaire d'expulsion pour récupérer les ressources


func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc) []*v1.Pod {
		
	/*
		填充m.signalToRankFunc的值,也就是pod排序的排序函数,整个过程只执行一次;
	*/
	
	// 获取节点上运行中的pod
	activePods := podFunc()
	// 从cadvisor获取节点的详细信息,并进行统计
	summary, err := m.summaryProvider.Get(true)
	//再从统计数据中获得节点资源的使用情况observations
	observations, statsFunc := makeSignalObservations(summary)
	
	// 将资源实际使用量和资源容量进行比较,最终得到阈值结构体对象的列表
	thresholds = thresholdsMet(thresholds, observations, false)	
	thresholdsFirstObservedAt := thresholdsFirstObservedAt(thresholds, m.thresholdsFirstObservedAt, now)
	//此时的thresholds是即将触发回收资源的thresholds,因为这些thresholds已经过了平滑时间
	thresholds = thresholdsMetGracePeriod(thresholdsFirstObservedAt, now)

		
	/*
		获取nodeConditions;
		nodeConditions是一个字符串切片;
		打印一些和nodeConditions相关的日志信息;	
	*/
	
	/*
		更新驱逐管理器(即本方法的接收者)的一些成员变量的值
		
		m.thresholdsMet = thresholds
		
		//m.nodeConditions过了--eviction-pressure-transition-period指定的时间,也会更新到kubelet本地的node对象中,而node对象最终也会被kubelet同步到kube-apiserver中
		//m.nodeConditions也和m.Admit(...)方法相关,m.Admit(...)方法决定是否允许在当前节点上创建或运行目标pod
		m.nodeConditions = nodeConditions
		
	*/
	
	// 本地临时存储导致的驱逐(一定是驱逐pod),如果发生,后续的回收资源(清理节点资源和运行中的pod)的操作不会执行,直接返回
	if utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
		if evictedPods := m.localStorageEviction(summary, activePods); len(evictedPods) > 0 {
			return evictedPods
		}
	}
	
	// 节点资源使用量没有到达用户配置的阈值,不需要回收节点资源,因此直接返回
	if len(thresholds) == 0 {
		klog.V(3).Infof("eviction manager: no resources are starved")
		return nil
	}
	
	// 回收节点级的资源,如果回收的资源足够的话,直接返回,不需要驱逐正在运行中的pod
	if m.reclaimNodeLevelResources(thresholdToReclaim.Signal, resourceToReclaim) {
		klog.Infof("eviction manager: able to reduce %v pressure without evicting pods.", resourceToReclaim)
		return nil
	}

	// 根据资源类型,从一个map中拿到一个排序方法,使用该排序方法对运行中的pod进行排序
	rank, ok := m.signalToRankFunc[thresholdToReclaim.Signal]	
	rank(activePods, statsFunc)
	// 此时activePods已经按照一定规则进行排序
	klog.Infof("eviction manager: pods ranked for eviction: %s", format.Pods(activePods))
	
	// 开始对运行中的pod实施驱逐操作
	for i := range activePods {	
		//如果驱逐了一个pod,则立马返回,因为一个周期最多驱逐一个pod
		if m.evictPod(pod, gracePeriodOverride, message, annotations) {
			return []*v1.Pod{pod}
		}
	}
	
	// 到达此处,说明本周期试图驱逐pod,但连一个pod都没驱逐成功
	klog.Infof("eviction manager: unable to evict any pods from the node")
	return nil
}

2.4 La propriété signalToRankFunc du gestionnaire d'expulsions

Il contient un ensemble de méthodes pour trier les pods.

func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc) []*v1.Pod {
	/*
	其他代码
	*/
	
	if m.dedicatedImageFs == nil {
		hasImageFs, ok := diskInfoProvider.HasDedicatedImageFs()
		if ok != nil {
			return nil
		}
		//dedicatedImageFs被赋值后,不会再进入当前整个if语句
		m.dedicatedImageFs = &hasImageFs
		//把pod的排序方法作为map传给驱逐管理器的signalToRankFunc属性
		m.signalToRankFunc = buildSignalToRankFunc(hasImageFs)   
	}
	
	/*
	其他代码
	*/
}
func buildSignalToRankFunc(withImageFs bool) map[evictionapi.Signal]rankFunc {

	//不管入参是true还是false,内存、pid这两种资源的排序方法是不变的
	signalToRankFunc := map[evictionapi.Signal]rankFunc{
		evictionapi.SignalMemoryAvailable:            rankMemoryPressure,
		evictionapi.SignalAllocatableMemoryAvailable: rankMemoryPressure,
		evictionapi.SignalPIDAvailable:               rankPIDPressure,
	}
	
	//虽然都是rankDiskPressureFunc,但方法的入参不同。
	if withImageFs {
		
		// 使用独立的imagefs,那么nodefs的排序方法只包含日志和本地卷
		signalToRankFunc[evictionapi.SignalNodeFsAvailable] = rankDiskPressureFunc([]fsStatsType{fsStatsLogs, fsStatsLocalVolumeSource}, v1.ResourceEphemeralStorage)
		signalToRankFunc[evictionapi.SignalNodeFsInodesFree] = rankDiskPressureFunc([]fsStatsType{fsStatsLogs, fsStatsLocalVolumeSource}, resourceInodes)
			
		// 使用独立的imagefs,那么imagefs的排序方法只包含rootfs
		signalToRankFunc[evictionapi.SignalImageFsAvailable] = rankDiskPressureFunc([]fsStatsType{fsStatsRoot}, v1.ResourceEphemeralStorage)
		signalToRankFunc[evictionapi.SignalImageFsInodesFree] = rankDiskPressureFunc([]fsStatsType{fsStatsRoot}, resourceInodes)
	} else {	
		// 不使用独立的imagefs,nodefs的排序方法是包含所有文件系统,即fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource
		// 此时imagefs和nodefs在使用同一个块设备,因此它们的排序方法都是一样的。
		signalToRankFunc[evictionapi.SignalNodeFsAvailable] = rankDiskPressureFunc([]fsStatsType{fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource}, v1.ResourceEphemeralStorage)
		signalToRankFunc[evictionapi.SignalNodeFsInodesFree] = rankDiskPressureFunc([]fsStatsType{fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource}, resourceInodes)
		signalToRankFunc[evictionapi.SignalImageFsAvailable] = rankDiskPressureFunc([]fsStatsType{fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource}, v1.ResourceEphemeralStorage)
		signalToRankFunc[evictionapi.SignalImageFsInodesFree] = rankDiskPressureFunc([]fsStatsType{fsStatsRoot, fsStatsLogs, fsStatsLocalVolumeSource}, resourceInodes)
	}
	return signalToRankFunc
}

2.4 Méthode de tri du Pod

Lorsque le gestionnaire d'expulsion expulse le pod, il trie les pods en cours d'exécution et le pod en haut de la file d'attente est nettoyé en premier.

Le pod dont le Qos est garanti est le dernier de la file d'attente, car en fonctionnement normal, la différence entre l'utilisation et la demande doit être un nombre négatif, et bien sûr, il se trouve au fond de la file d'attente. Lorsque l'utilisation des ressources de ce type de pod atteint la demande, c'est-à-dire lorsque la limite est atteinte, linux détecte et compare la valeur définie par la limite (dans le fichier cgroup), et envoie un signal d'arrêt au processus dans le pod Il ne s’agit pas d’un pod en cours d’exécution pour le moment.

Et le pod avec Qos comme meilleur effort est le top, car la demande n'est pas définie, alors la différence entre l'utilisation et la demande doit être un nombre positif, elle doit donc être la partie supérieure de la file d'attente.


2.4.1 Comment trier les pods lorsque la mémoire est insuffisante

/*
当内存使用量达到阈值时,对入参pods进行排序。
首先看使用量是否超过pod的request字段
接着看pod的QOS类型
最后看使用量和request之间的差值
*/

func rankMemoryPressure(pods []*v1.Pod, stats statsFunc) {

	orderedBy(
		exceedMemoryRequests(stats),  //使用量是否超过pod的request字段
		priority,  //pod的QOS类型
		memory(stats)  //使用量和request之间的差值
	).Sort(pods) 
	
}

2.4.2 Comment trier les pods lorsque Pid est nerveux

/*
当pid用量达到阈值时,根据pod的QOS类型,对入参pods进行排序。
*/
func rankPIDPressure(pods []*v1.Pod, stats statsFunc) {
	orderedBy(priority).Sort(pods)
}

2.4.3 Comment trier les pods lorsque le disque est serré

/*
当磁盘使用量达到阈值时,对入参pods进行排序。
首先看使用量是否超过pod的request字段
接着看pod的QOS类型
最后看使用量和request之间的差值
*/
func rankDiskPressureFunc(fsStatsToMeasure []fsStatsType, diskResource v1.ResourceName) rankFunc {
	return func(pods []*v1.Pod, stats statsFunc) {
	
		orderedBy(
			exceedDiskRequests(stats, fsStatsToMeasure, diskResource),	//使用量是否超过pod的request字段
			priority,  //pod的QOS类型
			disk(stats, fsStatsToMeasure, diskResource)   //使用量和request之间的差值
		).Sort(pods) 
		
	}
}

2.4 Autres méthodes

2.4.1 func (m * managerImpl) Admettre (…)

La méthode Admit (...) du gestionnaire d'expulsion affectera si le pod est autorisé à être créé.

/*
1)入参attrs对象中包含一个pod对象
2)m.nodeConditions字段通过本方法和kubelet的其他流程有关联:
  2.1)HandlePodAdditions(...)方法中会间接调用本方法,从而达到不创建目标pod的效果。
  2.2)syncPod(...)方法中会间接调用本方法,从而到达目标pod(如果存在的话)会被删除的效果。
*/

func (m *managerImpl) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult {
	m.RLock()
	defer m.RUnlock()
	
	//节点无资源压力,则允许创建任意pod
	if len(m.nodeConditions) == 0 {
		return lifecycle.PodAdmitResult{Admit: true}
	}
	
	//Critical pods也能被允许创建
	if kubelettypes.IsCriticalPod(attrs.Pod) {
		return lifecycle.PodAdmitResult{Admit: true}
	}
	
	if hasNodeCondition(m.nodeConditions, v1.NodeMemoryPressure) {
		notBestEffort := v1.PodQOSBestEffort != v1qos.GetPodQOS(attrs.Pod)
		
		// 节点处于内存压力状态,best-effort类型的pod不能被允许创建,其余类型的pod可以
		if notBestEffort {
			return lifecycle.PodAdmitResult{Admit: true}
		}

		// 节点处于内存压力状态并且开启TaintNodesByCondition特性,pod能容忍内存压力污点的话,它也能被允许创建
		if utilfeature.DefaultFeatureGate.Enabled(features.TaintNodesByCondition) &&
			v1helper.TolerationsTolerateTaint(attrs.Pod.Spec.Tolerations, &v1.Taint{
				Key:    schedulerapi.TaintNodeMemoryPressure,
				Effect: v1.TaintEffectNoSchedule,
			}) {
			return lifecycle.PodAdmitResult{Admit: true}
		}
	}

	// 来到此处,说明节点是处于磁盘压力状态,或者处于内存压力状态但入参的pod是bestEffort类型,此时不允许创建pod	
	klog.Warningf("Failed to admit pod %s - node has conditions: %v", format.Pod(attrs.Pod), m.nodeConditions)
	return lifecycle.PodAdmitResult{
		Admit:   false,
		Reason:  Reason,
		Message: fmt.Sprintf(nodeConditionMessageFmt, m.nodeConditions),
	}
}

3 L'expérience de l'expulsion de Pod

3.1 Une fois les pods des contrôleurs ds et sts expulsés, le contrôleur ne créera pas de nouveaux pods.

Le pod expulsé occupe toujours un enregistrement et la logique de boucle principale du contrôleur sts et du contrôleur ds ne créera pas de nouveau pod.
Par conséquent, il est préférable d'exécuter une tâche crontab (kubectl delete pod --all-namespaces --field-selector = 'status.phase == Failed') sur le nœud maître, et de supprimer périodiquement les pods en échec (y compris les pods expulsés ).

Une fois que les deux pods ds de la capture d'écran ont été expulsés, car le contrôleur n'a pas créé de nouveau pod, le problème de communication du conteneur du nœud a été causé, de sorte que les services ds et sts expulsés ne se guérissent pas.
Le pod coredns de la capture d'écran est un pod géré par le contrôleur de déploiement. Une fois l'ancien pod expulsé, le contrôleur créera un nouveau pod, de sorte que le service DNS à l'intérieur du cluster s'auto-réparera.
Insérez la description de l'image ici


4 résumé

Il est reconnu que les ressources des nœuds peuvent être divisées en ressources compressibles et ressources incompressibles. Lorsque les ressources incompressibles sont dans un état de rareté, ce type de rareté entraînera une instabilité du nœud.
En tant qu'agent de nœud, le processus utilisateur kubelet est la première ligne de défense pour maintenir la stabilité du nœud. Il vérifie périodiquement l'état de la ressource du nœud, l'état de la ressource réelle utilisée par le pod et le seuil de ressource configuré par le utilisateur, et enfin effectuer l'opération de nettoyage de l'image et du conteneur pour maintenir La stabilité du nœud, après tout, la stabilité du nœud est supérieure à la stabilité du conteneur.
La deuxième ligne de défense est Linux OOM KILLER, qui est également la ligne de défense inférieure.
Il y aura une sorte de collaboration entre la première ligne de défense et la deuxième ligne de défense, qui est oom_score_adj. Lorsque kubelet crée un conteneur, oom_score_adj peut être défini pour affecter le processus de destruction de Linux OOM KILLER.

Je suppose que tu aimes

Origine blog.csdn.net/nangonghen/article/details/109696462
conseillé
Classement