kubelet源码分析(五)之 syncPod
以下代码分析基于
kubernetes v1.12.0
版本。
本文主要分析kubelet
中syncPod
的部分。
1. managePodLoop
managePodLoop
通过读取podUpdates
channel的信息,执行syncPodFn
函数,而syncPodFn
函数在newPodWorkers
的时候赋值了,即kubelet.syncPod
。
managePodLoop完整代码如下:
此部分代码位于pkg/kubelet/pod_workers.go
1 | func (p *podWorkers) managePodLoop(podUpdates <-chan UpdatePodOptions) { |
以下分析syncPod
相关逻辑。
2. syncPod
syncPod
可以理解为是一个单个pod进行同步任务的事务脚本。其中入参是syncPodOptions
,syncPodOptions
记录了需要同步的pod的相关信息。具体定义如下:
1 | // syncPodOptions provides the arguments to a SyncPod operation. |
syncPod
主要执行以下的工作流:
- 如果是正在创建的pod,则记录pod worker的启动
latency
。 - 调用
generateAPIPodStatus
为pod提供v1.PodStatus
信息。 - 如果pod是第一次运行,记录pod的启动
latency
。 - 更新
status manager
中的pod状态。 - 如果pod不应该被运行则杀死pod。
- 如果pod是一个
static pod
,并且没有对应的mirror pod
,则创建一个mirror pod
。 - 如果没有pod的数据目录则给pod创建对应的数据目录。
- 等待volume被attach或mount。
- 获取pod的secret数据。
- 调用
container runtime
的SyncPod
函数,执行相关pod操作。 - 更新pod的
ingress
和egress
的traffic limit
。
当以上任务流中有任何的error,则return error。在下一次执行syncPod
的任务流会被再次执行。对于错误信息会被记录到event中,方便debug。
以下对syncPod
的执行过程进行分析。
syncPod的代码位于pkg/kubelet/kubelet.go
2.1. SyncPodKill
首先,获取syncPodOptions
的pod信息。
1 | func (kl *Kubelet) syncPod(o syncPodOptions) error { |
如果pod是需要被杀死的,则执行killPod
,会在指定的宽限期内杀死pod。
1 | // if we want to kill a pod, do it now! |
2.2. SyncPodCreate
如果pod是需要被创建的,则记录pod的启动latency
,latency
与pod在apiserver中第一次被记录相关。
1 | // Latency measurements for the main workflow are relative to the |
通过pod和pod status生成最终的api pod status并设置pod的IP。
1 | // Generate final API pod status with pod and status manager status |
记录pod到running状态的时间。
1 | // Record the time it takes for the pod to become running. |
如果pod是不可运行的,则更新pod和container的状态和相应的原因。
1 | runnable := kl.canRunPod(pod) |
并更新status manager中的状态信息,杀死不可运行的pod。
1 | // Update status in the status manager |
如果网络插件还没到Ready
状态,则只有在使用host
网络模式的情况下才启动pod。
1 | // If the network plugin is not ready, only start the pod if it uses the host network |
2.3. Cgroups
给pod创建Cgroups
,如果cgroups-per-qos
参数开启,则申请相应的资源。对于terminated
的pod不需要创建或更新pod的Cgroups
。
当重新启动kubelet
并且启用cgroups-per-qos
时,应该间歇性地终止所有pod的运行容器并在qos cgroup hierarchy
下重新启动。
如果pod的cgroup已经存在或者pod第一次运行,不杀死pod中容器。
1 | // Create Cgroups for the pod and apply resource parameters |
如果pod被杀死并且重启策略是Never
,则不创建或更新对应的Cgroups
,否则创建和更新pod的Cgroups
。
1 | // Create and Update pod's Cgroups |
其中创建Cgroups
是通过containerManager
的UpdateQOSCgroups
来执行。
1 | if err := kl.containerManager.UpdateQOSCgroups(); err != nil { |
2.4. Mirror Pod
如果pod是一个static pod
,没有对应的mirror pod
,则创建一个mirror pod
;如果存在mirror pod
则删除再重建一个mirror pod
。
1 | // Create Mirror Pod for Static Pod if it doesn't already exist |
2.5. makePodDataDirs
给pod创建数据目录。
1 | // Make data directories for the pod |
其中数据目录包括
PodDir
:{kubelet.rootDirectory}/pods/podUIDPodVolumesDir
:{PodDir}/volumesPodPluginsDir
:{PodDir}/plugins
1 | // makePodDataDirs creates the dirs for the pod datas. |
2.6. mount volumes
对非terminated
状态的pod挂载volume
。
1 | // Volume manager will not mount volumes for terminated pods |
2.7. PullSecretsForPod
获取pod的secret数据。
1 | // Fetch the pull secrets for the pod |
getPullSecretsForPod
具体实现函数如下:
1 | // getPullSecretsForPod inspects the Pod and retrieves the referenced pull |
2.8. containerRuntime.SyncPod
调用container runtime
的SyncPod
函数,执行相关pod操作,由此kubelet.syncPod
的操作逻辑转入containerRuntime.SyncPod
函数中。
1 | // Call the container runtime's SyncPod callback |
3. Runtime.SyncPod
SyncPod
主要执行sync操作使得运行的pod达到期望状态的pod。主要执行以下操作:
- 计算
sandbox
和container
的变化。 - 必要的时候杀死pod。
- 杀死所有不需要运行的
container
。 - 必要时创建
sandbox
。 - 创建
init container
。 - 创建正常的
container
。
Runtime.SyncPod部分代码位于pkg/kubelet/kuberuntime/kuberuntime_manager.go
3.1. computePodActions
计算sandbox
和container
的变化。
1 | // Step 1: Compute sandbox and container changes. |
3.2. killPodWithSyncResult
必要的时候杀死pod。
1 | // Step 2: Kill the pod if the sandbox has changed. |
3.3. killContainer
杀死所有不需要运行的container
。
1 | // Step 3: kill any running containers in this pod which are not to keep. |
3.4. createPodSandbox
必要时创建sandbox
。
1 | // Step 4: Create a sandbox for the pod if necessary. |
3.5. start init container
创建init container
。
1 | // Step 5: start the init container. |
3.6. start containers
创建正常的container
。
1 | // Step 6: start containers in podContainerChanges.ContainersToStart. |
4. startContainer
startContainer
启动一个容器并返回是否成功。
主要包括以下几个步骤:
- 拉取镜像
- 创建容器
- 启动容器
- 运行post start lifecycle hooks(如果有设置此项)
startContainer
完整代码如下:
startContainer部分代码位于pkg/kubelet/kuberuntime/kuberuntime_container.go
1 | // startContainer starts a container and returns a message indicates why it is failed on error. |
以下对startContainer
分段分析:
4.1. pull image
通过EnsureImageExists
方法拉取拉取指定pod容器的镜像,并返回镜像信息和错误。
1 | // Step 1: pull the image. |
4.2. CreateContainer
首先生成container的*v1.ObjectReference
对象,该对象包括container的相关信息。
1 | // Step 2: create the container. |
统计container的重启次数,新的容器默认重启次数为0。
1 | // For a new container, the RestartCount should be 0 |
生成container的配置。
1 | containerConfig, cleanupAction, err := m.generateContainerConfig(container, pod, restartCount, podIP, imageRef, containerType) |
调用runtimeService
,执行CreateContainer
的操作。
1 | containerID, err := m.runtimeService.CreateContainer(podSandboxID, containerConfig, podSandboxConfig) |
4.3. StartContainer
执行runtimeService
的StartContainer
方法,来启动容器。
1 | // Step 3: start the container. |
4.4. execute post start hook
如果有指定Lifecycle.PostStart
,则执行PostStart
操作,PostStart
如果执行失败,则容器会根据重启的规则进行重启。
1 | // Step 4: execute the post start hook. |
5. 总结
kubelet的工作是管理pod在Node上的生命周期(包括增删改查),kubelet通过各种类型的manager异步工作各自执行各自的任务,其中使用到了多种的channel来控制状态信号变化的传递,例如比较重要的channel有podUpdates <-chan UpdatePodOptions
,来传递pod的变化情况。
创建pod的调用逻辑
syncLoopIteration-->kubetypes.ADD-->HandlePodAdditions(u.Pods)-->dispatchWork(pod, kubetypes.SyncPodCreate, mirrorPod, start)-->podWorkers.UpdatePod-->managePodLoop(podUpdates)-->syncPod(o syncPodOptions)-->containerRuntime.SyncPod-->startContainer
参考:
赞赏一下