kubelet源码分析

kubelet的启动

1.kubeConfig、管理Poddocker的参数、定期同步Poddocker信息的参数导入到cfg

2.根据cfg的属性创建多种Restclient对象访问kube-apiserver,如EventClient\discovery\kubeclient

3.根据cfg的属性创建cloudprovider,获取云供应商Cloud的资源。

4.根据cfg的属性创建cAdvisor来监控本地的Docker容器

5.根据cfg的属性创建ContainerManager来管理Docker容器

6.设置服务,防止发生OOM

7.开启垃圾回收协程以清理无用的容器和镜像,释放磁盘空间。

8.启动kubelet,创建PodSource对象拉取pod数据并汇总输出到同一个Podchannel中,kubelet启动协程监听podchannel中的数据并处理。

当前支持三种PodSource类型:

1.configfile:本地配置文件作为pod数据源

2.httpURLpod数据源的内容通过一个HTTPUTL方式获取

3.kubernetesAPI server:默认方式,从APIserver中获取pod数据源。

9.启动kubelet服务的心跳机制,监听kubelet的启动状况。





//PodConfig is a configuration mux that merges many sources of podconfiguration into a single

//consistent structure, and then delivers incremental changenotifications to listeners

//in order.

typePodConfig struct {

pods*podStorage

mux *config.Mux



//the channel of denormalized changes passed to listeners

updateschan kubetypes.PodUpdate



//contains the list of all configured sources

sourcesLocksync.Mutex

sources sets.String #包括当前加载的所有PodSource类型

}

mux:pod发生变动时(创建、删除、更新),相关的podsource会产生对应的podupdate事件并推送到channel上。mux收集来自多个podsourcechannel的事件,并交给merger来处理。merger将多路channel发来的事件合并写入updateschannel中。等待kubelet处理





kubelet创建和同步Pod的代码流程

1.run()启动kubelet,调用CreateAPIServerClientConfig创建kube-apiserver的连接

调用cadvisor.New()创建CAdvisorInterface接口监视容器的状态

调用NewContainerManager()创建ContainerManager对象管理容器

2.fun()调用CreateAndInitKubelet()创建和初始化kubelet服务

3.CreateAndInitKubelet()调用makePodSourceConfig()创建podconfig对象和调用NewMainKubelet()创建和初始化kubelet服务

3.1NewMainKubelet()创建imagemanagercAdvisorContainerManagerOOMwatcherstatusmanagermemorywatcherstatusManager(获取pod状态信息)、probeManagerprobe的控制对象)、evictionManagerpodWorkers(管理pod的工作者)等对象

4.makePodSourceConfig()调用NewPodConfig()创建podconfig对象,

podConfig:= &PodConfig{

pods: storage,

mux: config.NewMux(storage), #聚合器,聚合多个podsourceupdate事件

#storage :=newPodStorage()

updates:updates, #监听channel加载所有podsourceupdate事件

sources:sets.String{}, #加载所有podsource

}

config.NewMux()实质是创建Mux对象

typeMux struct {

//Invoked when an update is sent to a source.

mergerMerger #将聚合的update事件合并写入updateschannel

//Sources and their lock.

sourceLocksync.RWMutex #排它锁

//Maps source names to channels

sourcesmap[string]chan interface{} #pod sourceupdatechannel组成的map

}

4.1.makePodSourceConfig()调用podconfig对象的Channel()加载所有podsource

c.sources.Insert(source)

returnc.mux.Channel(source)

4.1.1c.mux.Channel()开启协程监听各个podsourceupdatechannelupdate事件,将监听到的update事件交给merger处理

4.1.2mergerconfig.podStorage对象,由newPodStorage()创建。

typepodStorage struct {

podLocksync.RWMutex #排它锁

podsmap[string]map[string]*api.Pod #存放每个podsource上获取的pod数据

modePodConfigNotificationMode #podStoragepod事件通知模式

updateLocksync.Mutex

updates chan<- kubetypes.PodUpdate #podconfigupdates的一个引用

sourcesSeenLocksync.Mutex

sourcesSeen sets.String

recorderrecord.EventRecorder

}

4.1.3mergerpodupdate事件分解为对应的(ADDUPDATE REMOVE SET)新增、更新及删除设置等类型podupdate事件

注:SET是一种snapshotupdate操作,kubernetes目前不支持

4.1.4相见处理update事件的模式(mode):

mode1PodConfigNotificationIncremental(默认)

将多类podupdate事件 写入 updateschannel

mode2PodConfigNotificationSnapshotAndUpdates:

将更新类型的podupdate事件写入updateschannel中,封装podupdate事件成PodUpdate类型的事件再写入updateschannel

kubetypes.PodUpdate{Pods:s.MergedState().([]*api.Pod), Op: kubetypes.SET, Source: source}

mode3PodConfigNotificationSnapshot:

封装PodUpdate事件成PodUpdate类型的事件再写入updateschannel

5.启动kubeletstartKubelet()创建协程来处理updateschannel的各种事件

gowait.Until(func() { k.Run(podCfg.Updates()) }, 0, wait.NeverStop)

5.1调用KubeletBootstrapfunc(kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate)

5.2启动一个HTTPFile Server来远程获取本节点的系统日志

5.3.启动imagemanagercAdvisorContainerManagerOOMwatcherstatusmanagermemorywatcherstatusManager(获取pod状态信息)、probeManagerprobe的控制对象)、evictionManager等主件

5.3.1其中statusManager创建协程通过kubeapi server同步podstatus

5.4.循环调用syncLoopIteration方法处理事件

5.5syncLoopIteration根据podupdate的事件类型分配给不同handler处理

ADD kl.podManager.AddPod(pod)

UPDATE kl.podManager.UpdatePod(pod)

DELETE kl.podManager.DeletePod(pod)

RECONDILE kl.podManager.UpdatePod(pod)

SYNC kl.podManager.GetMirrorPodByPod(pod)

CLEANUP kl.probeManager.CleanupPods(activePods)

5.6kl.podManagerpodupdate事件转化为mirrorPod类型的事件,并让dispatchWork处理

5.7过滤状态为Failed"or "Succeeded“pod

5.8调用kl.podWorkers.UpdatePod处理每个podupdate事件,它将podupdate事件发布到pod对应的podUpdateschannel,在调用syncPodFn()处理podupdates,

创建podWorkers时,syncPodFn()就是klet.syncPod()

5.9syncPod()的主要流程:

5.9.1如果updateTypeSyncPodKill,就调用kl.statusManager.SetPodStatus()发送更改pod状态的请求给kubeapi server。再调用kl.containerRuntime.KillPod()删除该pod

5.9.2调用kl.statusManager.SetPodStatus()设置pod的状态

a.调用copyStatus()获取缓存中的pod状态,

b.调用updateStatusInternal()比较pod的当前状态和缓存中pod状态,如果发生了变化,生成podStatusSyncRequest并放到syncRequest队列中等待上报

c.调用syncPod()处理syncRequest队列中的podStatusSyncRequest,调用m.kubeClient更新pod的状态

5.9.3调用kl.makePodDataDirs()创建pod相关的工作目录、PV存放目录、plugin插件目录

5.9.4如果podPV定义,则调用kl.volumeManager.WaitForAttachAndMount()挂载

5.9.5如果podimagePullSecrets属性,则在APIserver上获取image对应的secret

5.9.6调用kl.containerRuntime.SyncPod()处理podupdate事件

kl.containerRuntime含有rktdocker两种,默认是docker,目前也只支持docker

6.docker处理podupdate事件的流程SyncPod()

// The workflow is:
// * If the pod is being created, record pod worker start latency
// * Call generateAPIPodStatus to prepare an v1.PodStatus for the pod
// * If the pod is being seen as running for the first time, record pod
//   start latency
// * Update the status of the pod in the status manager
// * Kill the pod if it should not be running
// * Create a mirror pod if the pod is a static pod, and does not
//   already have a mirror pod
// * Create the data directories for the pod if they do not exist
// * Wait for volumes to attach/mount
// * Fetch the pull secrets for the pod
// * Call the container runtime's SyncPod callback
// * Update the traffic shaping for the pod's ingress and egress limits

// SyncPod syncs the running pod into the desired pod by executing following steps:
//
//  1. Compute sandbox and container changes.
//  2. Kill pod sandbox if necessary.
//  3. Kill any containers that should not be running.
//  4. Create sandbox if necessary.
//  5. Create init containers.
//  6. Create normal containers.


6.1调用computePodContainerChanges()获取container需要发生变化的部分

6.1.1调用podInfraContainerChanged()判断podinfraContainer是否发生变化。

调用poddockerclient获取infraContainer的信息比较podupdate事件中infraContainer的信息,判断 网络模式 、 网络插件是否发生变化

podinfraContainer的端口是否发生变化

6.1.2调用podStatus.FindContainerStatusByName()获取不同podcontainerStatus

6.1.3调用ShouldContainerBeRestarted()判断container是否需要重启

容器状态是否异常

6.1.4判断podupdatecontainerStatus和实际的containerStatus是否相等,不等则containerChangedTrue,并将其加入containerChanges

6.2如果infraContainer需要发生变化,则先killpod然后启动podinfraContainer并设定好网络,最后启动pod里的所有容器,否则就先kill那些需要重启的container,然后重启启动它们

6.3如果需要创建infraContainer,则调用dm.createPodInfraContainer创建infraContainer



7创建infraContainer的流程

7.1通过IsHostNetworkPod()判断pod的网络是不是HostNetwork模式,不是的话,则搜集pod中所有containerpod,加入ports列表

7.2通过dm.imagePuller.PullImage()拉取infraContainer的镜像

7.3创建infraContainer对象,并通过dm.runContainerInPod()启动

注:runContainerInPodDockerManager的核心方法之一,不管是创建podinfraContainer还是pod里的其它容器,都会通过此方法创建和运行容器

8runContainerInPod()的流程

8.1调用GenerateContainerRef()和GenerateContainerRef()生成container必要的环境变量和参数,比如:ENV环境变量、volumemounts信息、端口映射信息、DNS服务器信息、容器的日志目录、parentcgroup

8.2调用dm.calculateOomScoreAdj()计算出OOM数值

8.3.调用runcontainer()创建和启动dockercontainer

8.4如果容器定义了LifecyclePostStart,就会在容器中执行PostStart()

8.5创建软连接文件指向容器的日志文件,此链接文件名包口pod的名称,容器名称和容器ID

8.6.设置OOM参数低于标准值

8.7.修改resolv.config文件,增加ndots参数,默认为5



9runcontainer()创建和启动container的流程

实质就是dockercreate containerdockerstart container

9.1获取container名字

dockerName:= KubeletContainerName{

PodFullName: kubecontainer.GetPodFullName(pod),

PodUID: pod.UID,

ContainerName:container.Name,

}

9.2调用getSecurityOpt()获取权限的权限的参数

9.3调用newLabels()创建podcontainerlabels

9.4podupdate事件的参数中获取momerycpu GPU

9.5如果GPU不为0,则调用dockercontainer.DeviceMapping()创建devices,目前只支持/dev/nvidia0

9.6调用BuildDockerName()创建一个唯一ID的名字

9.7创建哎log文件,绑定到container

9.8调用dockercontainer.HostConfig()创建HostConfig对象,主要参数有:目录的映射、端口的映射、cgroup的设定等

9.9调用milliCPUToQuota()判断设定的cpu个数是否超过quota

9.10构造创建container的参数的对象dockerOpts

dockerOpts:= dockertypes.ContainerCreateConfig{

Name:containerName,

Config:&dockercontainer.Config{

Env: makeEnvList(opts.Envs),

Image: container.Image,

WorkingDir:container.WorkingDir,

Labels: labels,

OpenStdin:container.Stdin,

StdinOnce:container.StdinOnce,

Tty: container.TTY,

},

HostConfig:hc,

}

9.11如果该containerinfra-container,则调用setInfraContainerNetworkConfig()配置网络,其它的container共享podinfra-container的网络资源

9.11.1调用makePortsAndBindings()创建和绑定port

a.设置port的协议,port的协议有UDPTCP两种,默认使用tcp

b.调用dockernat.Port()创建port

c.调用dockernat.PortBinding()HostPortHostIP绑定成hostBinding结构体

9.11.2hostBinding保存到dockerOpts.HostConfig.PortBindings

9.12调用setEntrypointAndCommand()设置containerCommandArgs,并存入dockerOpts.Config

9.13调用dm.client.CreateContainer()根据dockerOpts参数创建container

9.14调用dm.client.StartContainer()启动container



10.makePodSourceConfig()获取所有podupdate事件(创建协程调用获取方法)

10.1PodSource类型为configfile时,makePodSourceConfig()调用NewSourceFile()获取所有podupdate事件

NewSourceApiserver()调用sourceFilerun()获取文件中的update事件

podsupdate事件写入updateschannel

s.updates<- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.SET, Source:kubetypes.FileSource}

10.2PodSource类型为URL时,makePodSourceConfig()调用NewSourceURL()获取所有podupdate事件

NewSourceURL()调用sourceURLrun()获取文件中的update事件

podsupdate事件写入updateschannel

10.3PodSource类型为kubeclient时,makePodSourceConfig()调用NewSourceApiserver()获取所有podupdate事件

NewSourceApiserver()调用NewListWatchFromClient()kube-apiserver中获取podupdate事件

podsupdate事件写入updateschannel





kubelet创建和同步Pod的流程总结:

0.监听

启动kubelet服务时,创建协程监听podsource上的podupdate消息

1.获取

podsource获取podupdate事件

2.汇总

将多个podsource获取的podupdate事件汇聚到一个总的channel

3.初审

过滤不符合本节点的podupdate事件,对每个满足条件的podupdate生成一个workupdate事件,并交给podworkers处理

4.接待

podworks对每个podworkupdate事件排队,并且负责更新缓存中的pod状态,而把具体的任务转给kubelet去处理(syncPod()

5.终审

Kubelet对符合条件的pod进一步作出审查,如检查pod是否有权限再本节点上运行,对符合审查的pod开始着手准备工作,包括目录创建\PV创建、image获取、处理mirrorpod问题等。

6.完成

dockerManager分析对比每个pod,决定这个pod究竟是新建、完全重启还是部分更新的。分析后,调用dockerClient创建或启动docker

猜你喜欢

转载自blog.csdn.net/panfengyun12345/article/details/72852387