kubelet的启动
1.将kubeConfig、管理Pod和docker的参数、定期同步Pod和docker信息的参数导入到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.httpURL:pod数据源的内容通过一个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收集来自多个podsource的channel的事件,并交给merger来处理。merger将多路channel发来的事件合并写入updates的channel中。等待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()创建imagemanager、cAdvisor、ContainerManager、OOMwatcher、statusmanager、memorywatcher、statusManager(获取pod状态信息)、probeManager(probe的控制对象)、evictionManager、podWorkers(管理pod的工作者)等对象
4.makePodSourceConfig()调用NewPodConfig()创建podconfig对象,
podConfig:= &PodConfig{
pods: storage,
mux: config.NewMux(storage), #聚合器,聚合多个podsource的update事件
#storage :=newPodStorage()
updates:updates, #监听channel加载所有podsource的update事件
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 source和updatechannel组成的map
}
4.1.makePodSourceConfig()调用podconfig对象的Channel()加载所有podsource
c.sources.Insert(source)
returnc.mux.Channel(source)
4.1.1c.mux.Channel()开启协程监听各个podsource的updatechannel的update事件,将监听到的update事件交给merger处理
4.1.2merger是config.podStorage对象,由newPodStorage()创建。
typepodStorage struct {
podLocksync.RWMutex #排它锁
podsmap[string]map[string]*api.Pod #存放每个podsource上获取的pod数据
modePodConfigNotificationMode #podStorage的pod事件通知模式
updateLocksync.Mutex
updates chan<- kubetypes.PodUpdate #podconfig的updates的一个引用
sourcesSeenLocksync.Mutex
sourcesSeen sets.String
recorderrecord.EventRecorder
}
4.1.3merger将podupdate事件分解为对应的(ADDUPDATE REMOVE SET)新增、更新及删除设置等类型podupdate事件
注:SET是一种snapshotupdate操作,kubernetes目前不支持
4.1.4相见处理update事件的模式(mode):
mode1:PodConfigNotificationIncremental(默认)
将多类podupdate事件 写入 updateschannel中
mode2:PodConfigNotificationSnapshotAndUpdates:
将更新类型的podupdate事件写入updateschannel中,封装podupdate事件成PodUpdate类型的事件再写入updateschannel中
kubetypes.PodUpdate{Pods:s.MergedState().([]*api.Pod), Op: kubetypes.SET, Source: source}
mode3:PodConfigNotificationSnapshot:
封装PodUpdate事件成PodUpdate类型的事件再写入updateschannel中
5.启动kubelet的startKubelet()创建协程来处理updateschannel的各种事件
gowait.Until(func() { k.Run(podCfg.Updates()) }, 0, wait.NeverStop)
5.1调用KubeletBootstrap的func(kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate)
5.2启动一个HTTPFile Server来远程获取本节点的系统日志
5.3.启动imagemanager、cAdvisor、ContainerManager、OOMwatcher、statusmanager、memorywatcher、statusManager(获取pod状态信息)、probeManager(probe的控制对象)、evictionManager等主件
5.3.1其中statusManager创建协程通过kubeapi server同步pod的status
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.podManager将podupdate事件转化为mirrorPod类型的事件,并让dispatchWork处理
5.7过滤状态为Failed"or "Succeeded“的pod
5.8调用kl.podWorkers.UpdatePod处理每个pod的update事件,它将pod的update事件发布到pod对应的podUpdateschannel中,在调用syncPodFn()处理podupdates,
创建podWorkers时,syncPodFn()就是klet.syncPod()
5.9syncPod()的主要流程:
5.9.1如果updateType是SyncPodKill,就调用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如果pod有PV定义,则调用kl.volumeManager.WaitForAttachAndMount()挂载
5.9.5如果pod有imagePullSecrets属性,则在APIserver上获取image对应的secret
5.9.6调用kl.containerRuntime.SyncPod()处理podupdate事件
kl.containerRuntime含有rkt和docker两种,默认是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()判断pod的infraContainer是否发生变化。
调用pod中dockerclient获取infraContainer的信息比较podupdate事件中infraContainer的信息,判断 网络模式 、 网络插件是否发生变化
pod中infraContainer的端口是否发生变化
6.1.2调用podStatus.FindContainerStatusByName()获取不同pod的containerStatus。
6.1.3调用ShouldContainerBeRestarted()判断container是否需要重启
容器状态是否异常
6.1.4判断podupdate中containerStatus和实际的containerStatus是否相等,不等则containerChanged为True,并将其加入containerChanges
6.2如果infraContainer需要发生变化,则先killpod然后启动pod的infraContainer并设定好网络,最后启动pod里的所有容器,否则就先kill那些需要重启的container,然后重启启动它们
6.3如果需要创建infraContainer,则调用dm.createPodInfraContainer创建infraContainer
7创建infraContainer的流程
7.1通过IsHostNetworkPod()判断pod的网络是不是HostNetwork模式,不是的话,则搜集pod中所有container的pod,加入ports列表
7.2通过dm.imagePuller.PullImage()拉取infraContainer的镜像
7.3创建infraContainer对象,并通过dm.runContainerInPod()启动
注:runContainerInPod是DockerManager的核心方法之一,不管是创建pod的infraContainer还是pod里的其它容器,都会通过此方法创建和运行容器
8runContainerInPod()的流程
8.1调用GenerateContainerRef()和GenerateContainerRef()生成container必要的环境变量和参数,比如:ENV环境变量、volumemounts信息、端口映射信息、DNS服务器信息、容器的日志目录、parentcgroup等
8.2调用dm.calculateOomScoreAdj()计算出OOM数值
8.3.调用runcontainer()创建和启动dockercontainer
8.4如果容器定义了Lifecycle和PostStart,就会在容器中执行PostStart()
8.5创建软连接文件指向容器的日志文件,此链接文件名包口pod的名称,容器名称和容器ID
8.6.设置OOM参数低于标准值
8.7.修改resolv.config文件,增加ndots参数,默认为5
9runcontainer()创建和启动container的流程
实质就是dockercreate container和dockerstart container
9.1获取container名字
dockerName:= KubeletContainerName{
PodFullName: kubecontainer.GetPodFullName(pod),
PodUID: pod.UID,
ContainerName:container.Name,
}
9.2调用getSecurityOpt()获取权限的权限的参数
9.3调用newLabels()创建pod和container的labels
9.4从podupdate事件的参数中获取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如果该container是infra-container,则调用setInfraContainerNetworkConfig()配置网络,其它的container共享pod中infra-container的网络资源
9.11.1调用makePortsAndBindings()创建和绑定port
a.设置port的协议,port的协议有UDP和TCP两种,默认使用tcp
b.调用dockernat.Port()创建port
c.调用dockernat.PortBinding()将HostPort和HostIP绑定成hostBinding结构体
9.11.2将hostBinding保存到dockerOpts.HostConfig.PortBindings中
9.12调用setEntrypointAndCommand()设置container的Command和Args,并存入dockerOpts.Config
9.13调用dm.client.CreateContainer()根据dockerOpts参数创建container
9.14调用dm.client.StartContainer()启动container
10.makePodSourceConfig()获取所有podupdate事件(创建协程调用获取方法)
10.1当PodSource类型为configfile时,makePodSourceConfig()调用NewSourceFile()获取所有podupdate事件
NewSourceApiserver()调用sourceFile的run()获取文件中的update事件
将pods的update事件写入updateschannel中
s.updates<- kubetypes.PodUpdate{Pods: pods, Op: kubetypes.SET, Source:kubetypes.FileSource}
10.2当PodSource类型为URL时,makePodSourceConfig()调用NewSourceURL()获取所有podupdate事件
NewSourceURL()调用sourceURL的run()获取文件中的update事件
将pods的update事件写入updateschannel中
10.3当PodSource类型为kubeclient时,makePodSourceConfig()调用NewSourceApiserver()获取所有podupdate事件
NewSourceApiserver()调用NewListWatchFromClient()从kube-apiserver中获取podupdate事件
将pods的update事件写入updateschannel中
kubelet创建和同步Pod的流程总结:
0.监听
启动kubelet服务时,创建协程监听podsource上的podupdate消息
1.获取
podsource获取podupdate事件
2.汇总
将多个podsource获取的podupdate事件汇聚到一个总的channel上
3.初审
过滤不符合本节点的podupdate事件,对每个满足条件的podupdate生成一个workupdate事件,并交给podworkers处理
4.接待
podworks对每个pod的workupdate事件排队,并且负责更新缓存中的pod状态,而把具体的任务转给kubelet去处理(syncPod())
5.终审
Kubelet对符合条件的pod进一步作出审查,如检查pod是否有权限再本节点上运行,对符合审查的pod开始着手准备工作,包括目录创建\PV创建、image获取、处理mirrorpod问题等。
6.完成
dockerManager分析对比每个pod,决定这个pod究竟是新建、完全重启还是部分更新的。分析后,调用dockerClient创建或启动docker