kubelet liveness probe源码简析

1 概述:

1.1 环境

版本信息如下:
a、操作系统: centos 7.6,amd64
b、kubernetes版本:v1.15.4
c、服务器docker版本:v18.09.2


2 liveness探针功能:

存活性探针用于探测目标容器是否正常运行,手段有http get请求、tcp请求以及exec命令。探测失败,则kubelet会调用停止容器的API(docker stop)来结束目标容器。


3 源码简析:

探针的实现是定时器,定时器会定期执行预先设置好的探测动作,探测结果存放在kubelet的成员livenessManager中。在kubelet主循环中,从livenessManager获取探测结果,如果探测结果是失败的,则调用停止容器的API来杀死容器。

3.1 kubelet初始化probeManager对象

func NewMainKubelet(...) {

	/*
	其他代码
	*/

	klet.probeManager = prober.NewManager(
		klet.statusManager,
		klet.livenessManager,
		klet.runner,
		containerRefManager,
		kubeDeps.Recorder)

	/*
	其他代码
	*/

}

3.2 probeManager的AddPod(…)方法

func (kl *Kubelet) HandlePodAdditions(pods []*v1.Pod) {
	start := kl.clock.Now()
	sort.Sort(sliceutils.PodsByCreationTime(pods))
	for _, pod := range pods {
	
		/*
		其他代码
		*/		
		
		// 同步目标pod
		kl.dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)
		// 对目标pod进行探测
		kl.probeManager.AddPod(pod)
	}
}
func (m *manager) AddPod(pod *v1.Pod) {
	m.workerLock.Lock()
	defer m.workerLock.Unlock()

	key := probeKey{podUID: pod.UID}
	for _, c := range pod.Spec.Containers {
		key.containerName = c.Name

		/*
		   目标容器设置了就绪性探针
		*/	

		// 目标容器设置了存活性探针
		if c.LivenessProbe != nil {
			key.probeType = liveness
			if _, ok := m.workers[key]; ok {
				klog.Errorf("Liveness probe already exists! %v - %v",
					format.Pod(pod), c.Name)
				return
			}
			// 1)创建一个worker对象,并放入一个map对象中
			w := newWorker(m, liveness, pod, c)
			m.workers[key] = w
			// 2)调用worker对象的run()方法开启定时器,定时执行探测动作
			go w.run()
		}
	}
}

3.3 func (w *worker) run()

定时执行预先设定的探测操作,探测操作是w.doProbe() 方法。

func (w *worker) run() {
	probeTickerPeriod := time.Duration(w.spec.PeriodSeconds) * time.Second
	time.Sleep(time.Duration(rand.Float64() * float64(probeTickerPeriod)))

	probeTicker := time.NewTicker(probeTickerPeriod)

	// run()方法结束,进行清理工作
	defer func() {
		// 1)关闭定时器
		probeTicker.Stop()
		// 2)删除探针结果
		if !w.containerID.IsEmpty() {
			w.resultsManager.Remove(w.containerID)
		}

		// 3)将worker对象从map中删除
		w.probeManager.removeWorker(w.pod.UID, w.container.Name, w.probeType)
	}()

probeLoop:
	for w.doProbe() {
		// 定时器返回数据,则进入下一个循环
		select {
		case <-w.stopCh:
			break probeLoop
		case <-probeTicker.C:
		}
	}
}

3.4 func (w *worker) doProbe()

执行探测动作,探测结果保存在worker对象的resultsManager中。

func (w *worker) doProbe() (keepGoing bool) {
	/*
		其他代码
	*/

	status, ok := w.probeManager.statusManager.GetPodStatus(w.pod.UID)
	
	/*
		其他代码
	*/

	result, err := w.probeManager.prober.probe(w.probeType, w.pod, status, w.container, w.containerID)
	if err != nil {
		// 探测失败,直接返回,不需要记录探测结果
		return true
	}
	
	/*
		其他代码
	*/

	// worker对象的成员resultsManager,其实是kubelet的成员livenessManager对象。
	// 将本次探测结果存放到worker对象的resultsManager中,即kubelet的livenessManager对象中。
	w.resultsManager.Set(w.containerID, result, w.pod)

	return true
}

kubelet的成员livenessManager对象,worker对象的成员resultsManager、manager对象(管理worker对象)的成员livenessManager,其实是同一个值。因此,探针的结果其实都保存在kubelet的成员livenessManager对象中。

// kubelet创建manager对象(管理worker对象),第二个入参livenessManager的值其实是klet.livenessManager
func NewManager(
	statusManager status.Manager,
	livenessManager results.Manager,
	runner kubecontainer.ContainerCommandRunner,
	refManager *kubecontainer.RefManager,
	recorder record.EventRecorder) Manager {
	
	/*
		其他代码
	*/
	
	return &manager{
		/*
			其他代码
		*/
		livenessManager:  livenessManager,
	}
}

创建worker对象的构造方法,第一个入参就是manager对象(管理worker对象)。

func newWorker(
	m *manager,
	probeType probeType,
	pod *v1.Pod,
	container v1.Container) *worker {

	w := &worker{
		stopCh:       make(chan struct{}, 1), 
		pod:          pod,
		container:    container,
		probeType:    probeType,
		probeManager: m,
	}

	switch probeType {
	case readiness:
		/*
			其他代码
		*/
	case liveness:
		// 入参manager对象的成员livenessManager赋予给worker对象的成员resultsManager 
		w.resultsManager = m.livenessManager
	}

	/*
		其他代码
	*/

	return w
}

3.5 func (m *kubeGenericRuntimeManager) SyncPod(…)

SyncPod(…)是用来同步一个pod,pod中待杀死的容器放在podContainerChanges.ContainersToKill中。
在本方法的Step 3中,遍历podContainerChanges.ContainersToKill,调用停止容器的API来杀死容器,存活性探针失败的情形就属于这个情景。

func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
   
     // Step 1: 计算sandbox和业务container的变化情况,返回值podContainerChanges有一个成员叫ContainersToKill
    podContainerChanges := m.computePodActions(pod, podStatus)
   
    // Step 2: Kill the pod if the sandbox has changed.
    if podContainerChanges.KillPod {
       
    } else {
        // Step 3: 杀死pod中的container,如果这些container理应不该运行
        // m.computePodActions(  )方法有机会填充 ContainersToKill,例如在liveness探针失败的情景。
        // 遍历 ContainersToKill,调用m.killContainer(...)方法杀死容器
        for containerID, containerInfo := range podContainerChanges.ContainersToKill {
            if err := m.killContainer(pod, containerID, containerInfo.name, containerInfo.message, nil); err != nil {
                return
            }
        }
    }

    // Step 4: Create a sandbox for the pod if necessary.

    // Step 5: start the init container.

    // Step 6: start containers in podContainerChanges.ContainersToStart.
    
    return
}

在这里插入图片描述


3.6 func (m *kubeGenericRuntimeManager) computePodActions(…)

computePodActions(…)方法用于计算容器的变化情况,其中包含了待杀死的容器。
代码逻辑是:从kubelet的成员livenessManager中获取存活性探测的结果,如果探测结果是失败的,则将目标容器放入changes.ContainersToKill中,最后返回。

func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions {

    changes := podActions{
         /*
        	其他代码
    	*/
        ContainersToKill:  make(map[kubecontainer.ContainerID]containerToKillInfo),
    }

    /*
        其他代码
    */

    // 遍历pod中的每个容器
    for idx, container := range pod.Spec.Containers {
        containerStatus := podStatus.FindContainerStatusByName(container.Name)

         /*
       		 其他代码
   		 */

        
        var message string
        restart := shouldRestartOnFailure(pod)
        if _, _, changed := containerChanged(&container, containerStatus); changed {
         /*
       		 其他代码
   		 */

		// 从livenessManager中获取探测结果,livenessManager其实也是kubelet的成员livenessManager。
        } else if liveness, found := m.livenessManager.Get(containerStatus.ID); found && liveness == proberesults.Failure {
            // 遇见失败的探测结果则进来,不会执行下面的continue指令,则后续一定会将容器放入ContainersToKill
            message = fmt.Sprintf("Container %s failed liveness probe", container.Name)
        } else {
            // 代码来到此处,说明保留当前的container
            keepCount++
            continue
        }
        
         /*
       		 其他代码
   		 */

        // 把容器放入ContainersToKill
        changes.ContainersToKill[containerStatus.ID] = containerToKillInfo{
            name:      containerStatus.Name,
            container: &pod.Spec.Containers[idx],
            message:   message,
        }
    }

    return changes
}

4 总结:

liveness探针的实现是定时器,探测结果存放在kubelet的成员livenessManager中,kubelet在同步一个pod时,会从成员livenessManager中获取探测结果,如果探测结果是失败的,则调用停止容器的API来杀死目标pod中的目标容器。

<think>好的,我现在需要帮助用户解决Kubernetes中的几个问题:Startup ProbeLiveness Probe失败,以及FailedMount错误,特别是提到的metallb-memberlist Secret找不到的问题。首先,我得理清这些组件之间的关系以及可能的原因。 首先,用户提到FailedMount错误,具体是mountvolume setup metallb-memberlist secret not found。这说明在挂载卷时,Kubernetes无法找到名为metallb-memberlist的Secret。这个Secret应该是MetalLB组件的一部分,用于成员之间的通信。可能的情况是Secret未被正确创建,或者Pod的配置引用了错误的名称或命名空间。需要检查MetalLB的安装步骤,确认Secret是否存在,以及Pod的volumeMounts部分是否正确引用了该Secret。 接下来是Startup ProbeLiveness Probe失败的问题。这两个探针的失败可能与应用程序启动时间过长或健康检查路径配置不当有关。Startup Probe用于处理启动时间较长的应用,如果启动时间超过设定的阈值,就会失败。而Liveness Probe失败则意味着应用在运行后无法通过健康检查,可能需要调整探测参数或检查应用本身的健康状况。 可能的解决步骤包括: 1. 检查metallb-memberlist Secret是否存在,确认其名称和命名空间是否与Pod配置中的Volume引用一致。如果不存在,可能需要重新部署MetalLB或手动创建该Secret。 2. 验证MetalLB的配置是否正确,尤其是Secret的生成部分。MetalLB可能需要通过生成器来创建Secret,比如使用kubectl apply时指定生成器,或者使用helm chart时是否正确配置了相关参数。 3. 调整Startup Probe的initialDelaySeconds和periodSeconds,给予应用足够的启动时间。如果应用启动较慢,增加这些值可以避免探针过早失败。 4. 检查Liveness Probe的路径和端口是否正确,确保应用的健康检查接口可用,并且返回正确的状态码。可能需要进入Pod内部手动测试接口是否可达。 5. 查看kubelet的日志,获取更详细的错误信息。使用kubectl describe pod命令查看Pod的事件,以及kubectl logs来查看具体容器的日志,有助于定位问题根源。 另外,用户可能混淆了不同组件的配置,例如Secret所在的命名空间是否与Pod部署的命名空间一致。如果MetalLB部署在metallb-system命名空间,而Pod在其他命名空间,可能需要跨命名空间引用Secret,或者调整部署位置。 同时,需要考虑权限问题,确保ServiceAccount有权限访问该Secret。RBAC配置错误可能导致无法读取Secret,从而挂载失败。 最后,综合这些可能性,逐步排查,从Secret的存在性开始,到探针配置,再到应用本身的状态,应该能解决用户的问题。</think>### Kubernetes故障排查:Startup ProbeLiveness Probe失败与FailedMount问题 #### 一、FailedMount: metallb-memberlist Secret未找到 该错误表明Kubernetes无法挂载名为`metallb-memberlist`的Secret到Pod中。常见原因包括: 1. **Secret未创建** MetalLB需要成员列表Secret实现节点间通信,若未正确生成会导致挂载失败。检查Secret是否存在: ```bash kubectl get secret metallb-memberlist -n metallb-system ``` 如果不存在,需重新部署MetalLB。使用官方manifest部署时,注意需要先创建Secret生成器[^1]: ```yaml # metallb-secret.yaml apiVersion: v1 kind: Secret metadata: name: metallb-memberlist namespace: metallb-system type: Opaque stringData: secretkey: $(openssl rand -base64 128) ``` 2. **命名空间不匹配** MetalLB默认安装在`metallb-system`命名空间,若Pod部署在其他命名空间,需显式指定: ```yaml volumes: - name: memberlist secret: secretName: metallb-memberlist namespace: metallb-system # 关键配置 ``` #### 二、Startup Probe失败 该探针用于检测应用是否完成启动。失败常见原因: 1. **初始延迟不足** 如果应用启动时间超过`initialDelaySeconds`,需增加等待时间: ```yaml startupProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 # 根据实际启动时间调整 periodSeconds: 5 failureThreshold: 10 ``` 2. **资源限制过紧** CPU/内存限制过低会导致启动卡顿,可通过`kubectl describe pod`查看`OOMKilled`事件。 #### 三、Liveness Probe失败 该探针用于检测运行中的应用是否健康。排查方向: 1. **检测路径配置错误** 验证`httpGet.path`是否对应应用的真实健康检查接口: ```bash kubectl exec -it <pod-name> -- curl http://localhost:8080/health ``` 2. **应用性能问题** 响应超时(默认1秒)可能导致误判,调整`timeoutSeconds`: ```yaml livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 timeoutSeconds: 3 # 适当放宽超时时间 ``` #### 四、关联问题排查 1. **检查kubelet日志** 查看节点上的kubelet日志,过滤NFS相关错误: ```bash journalctl -u kubelet | grep -iE 'mount|nfs|secret' ``` 2. **验证StorageClass配置** 如果使用动态存储,检查StorageClass是否适配当前集群环境: ```bash kubectl get storageclass kubectl describe storageclass <name> ``` #### 五、MetalLB特定问题 1. **Secret加密方式验证** MetalLB要求Secret必须包含`secretkey`字段,且值需为base64编码的128字节随机数: ```bash kubectl get secret metallb-memberlist -n metallb-system -o jsonpath='{.data.secretkey}' | base64 -d | wc -c # 正确应输出128 ``` 2. **Speaker Pod状态检查** 确认MetalLB的speaker pod正常运行: ```bash kubectl get pods -n metallb-system -l app=metallb,component=speaker ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值