Kubernetes1.1源代码分析(三)

4、Kubelet模块

Kubelet中主要使用的结构体是KubeletServer,kubelet启动时会初始化KubeletServer数据结构,在这个数据结构中变量CAdvisorPort用来表示CAdvisor端口,默认是4194;变量ContainerRuntime用来表示使用容器类型;一种是Docker,一种是RKT,默认是Docker容器;变量CPUCFSQuota用来表示是否启动CPU CFS quota控制;变量PodInfraContainerImage表示POD系统镜像;在kubernetes1.1版本中默认使用的是GCE镜像仓库里面的gcr.io/google_containers/pause:0.8.0,在国内可能会无法访问,所以需要在kubelet启动时将这个镜像重新指向到docker公共仓库里面的kubernetes/puase:0.8.0;变量RegisterNode表示是否向api-server注册NODE节点,默认是true,也就是默认向api-server注册;变量LowDiskSpaceThresholdMB表示最小剩余磁盘空间,当NODE节点上剩余磁盘空间小于最小剩余空间时,无法在这个NODE上面创建POD,默认是256MByte;变量ImageGCHighThresholdPercent表示磁盘使用率上限,如果NODE节点上磁盘使用率超过这个上限,那么就会启动镜像空间回收功能,对不使用的镜像文件进行回收处理;变量ImageGCLowThresholdPercent表示磁盘使用率下限,如果NODE节点上磁盘使用率低于这个下限,那么就不会启动镜像空间回收功能;变量DockerExecHandlerName表示采用什么方法进入容器并执行命令,kubernetes提供两种进入容器的方法,一种是使用容器原生方法,一种是使用nsenter工具,默认是使用容器原生方法;KubeletServer结构体中还有很多变量,这里不一一介绍,我们来看看如何使用这个结构体的。
Kubelet整体上是非常复杂的,我们简单的进行概要性的描述,Kubelet启动后首先初始化KubeletServer结构体,然后初始化cadvisor,cAdvisor 是谷歌公司用来分析运行中的 Docker 容器的资源占用以及性能特性的工具。cAdvisor 是一个运行中的守护进程用来收集、聚合、处理和导出运行容器相关的信息,每个容器保持独立的参数、历史资源使用情况和完整的资源使用数据,接着启动容器回收GO程序和镜像回收GO程序。
容器回收GO程序查找不运行的容器和创建时间已经超过指定时间(默认是1分钟)的容器,第一个回收策略是每个POD里面最多允许2个满足回收条件的容器,如果超过了2个,那么按照创建时间顺序,依次删除容器;第二个回收策略是Node节点上最多允许100个满足回收条件的容器,如果超过了100个,那么按照创建时间顺序,依次删除容器。
镜像回收GO程序查找磁盘使用率,如果磁盘使用率大于变量ImageGCHighThresholdPercent表示的磁盘使用率上限,那么启动镜像回收过程,按照镜像上一次使用时间排序,依次删除镜像,直到磁盘使用率小于变量ImageGCLowThresholdPercent表示磁盘使用率下限。
kubelet可以作为普通进程运行一次,也可以作为Daemon进程后台运行。
如果kubelet作为普通进程运行一次,那么使用runOnce函数,这个函数创建一系列POD,然后返回这些POD的状态。canAdmitPod函数用来判断在NODE上面是否可以创建新的POD,如果不能创建新的POD,要返回失败原因,比如NODE端口冲突、NODE selector不匹配、没有足够的CPU资源、没有足够的内存资源、没有足够的磁盘资源,对于磁盘资源还区分docker镜像所需磁盘资源和root文件系统所需磁盘资源。如果有足够的资源可以创建POD,那么会运行一个GO程序,用来循环处理,每次循环创建一个POD,并且等待这个POD中所有的容器都启动,然后才会进入下一次循环处理。
这个GO程序首先会判断将要启动的POD是否已经存在,如果已经存在,那么判断这个POD中所有的容器是否都已经启动,如果都已经启动,那么不对这个POD进行任何操作,否则会先检查NODE是否满足创建POD所需要的资源,这些资源包括主机网络、主机IP和主机IPC,如果NODE上的资源不能创建POD,那么创建失败,如果NODE上的资源可以创建POD,那么继续往下执行。
接下来就是创建POD的过程,首先在NODE上面创建POD存放数据的目录,这些目录包括POD目录、POD使用到的卷目录和POD使用到的插件目录,接着将外部卷挂载到POD上,获取POD的pull secret,然后kubelet开始创建并启动kubernetes infra容器,kubelet支持两种容器,一种是Docker,一种是Rocket,kubelet默认支持的是Docker容器,接着设置infra容器网络,在infra容器创建并启动后才会创建POD中的用户容器,用户容器同infra容器使用相同的网络命名空间和IPC命名空间。
如果kubelet作为Daemon进程运行,那么kubelet会作为后台进程启动,同时会判断是否启动kubelet服务,用来接收外部访问请求,默认是启动kubelet服务的,服务请求端口是10250,接着判断是否启动只读端口,默认只读端口是10255,只读端口的作用在于从kubelet中暴露基本的只读服务信息,这样heapster就可以通过只读端口采集kubelet上的监控信息。Kubelet作为后台进程启动后,会启动镜像管理GO程序,启动cadvisor监控采集GO程序,启动容器管理GO程序,启动内存监控管理GO程序,启动POD删除GO程序,启动POD状态管理GO程序。
镜像管理GO程序周期性的(默认是5分钟)来检查Node节点上的镜像文件,同时检查NODE节点上所有容器,这些容器对应的镜像文件会被标识出来,没有被标识出来的镜像文件会被删除掉。
容器管理GO程序首先会检查Node节点上操作系统是否已经挂载了cgroup虚拟文件系统,这里只检查四个挂载点cpu、cpuacct、cpuset和memory,只要这四个挂载点存在,那么就认为已经挂载了需要的cgroup虚拟文件系统。四个挂载点其实就是四个子系统,每个子系统实际上就是cgroups的资源控制系统,每种子系统独立地控制一种资源:

  • cpu: 这个子系统使用调度程序控制task对CPU的使用。
  • cpuacct:
  • cpuset: 这个子系统可以为cgroup中的task分配独立的CPU(此处针对多处理器系统)和内存。
  • memory:这个子系统可以设定cgroup中task对内存使用量的限定,并且自动生成这些task对内存资源使用情况的报告。

容器管理GO程序接着会设置Node节点上操作系统内核参数,kubelet会将内核参数overcommit_memory和panic_on_oom分别设置成1和0。
内核参数overcommit_memory指定了内核针对内存分配的策略,设置成1表示内核允许分配所有的物理内存,而不管当前的内存状态如何,设置成2表示内核允许分配超过所有物理内存和交换空间总和的内存。内存参数panic_on_oom指定了当内核遇到内存不够时的执行策略,设置成0表示内核会选择进程,然后终止已经选择的进程用来释放内存,设置成2表示内核不进行其他处理,直接内存溢出,也就是我们常见的Out of Memory。
容器管理最后通过GO程序周期性的(默认是1分钟)来检查系统进程,并将系统进程放入指定的Cgroups中管理。
内存监控管理GO程序会监控内存溢出,并将内存溢出记录到系统事件中。
POD删除GO程序就是将POD中所有的容器进行删除,同时将infra容器也删除掉。
POD状态管理GO程序会从api-server中获取POD的状态信息,然后在NODE节点上进行同步。

5、Kube-proxy模块

kube-proxy中主要使用的结构体是ProxyServer,变量Client用来同api-server进行通讯,变量Config用来保存kube-proxy的配置,变量EndpointsConfig用来追踪Endpoints配置变化,接收”set”, “add”和”remove”操作,变量EndpointsHandler用来在Endpoint变化时进行相应处理操作,Endpoint变化比如新增service,service中新增容器或者删除容器,变量IptInterface用来操作NODE节点上的路由规则,变量ServiceConfig用来追踪services配置变化,接收”set”, “add”和”remove”操作。

type ProxyServer struct {
    Client           *kubeclient.Client
    Config           *ProxyServerConfig
    EndpointsConfig  *proxyconfig.EndpointsConfig
    EndpointsHandler proxyconfig.EndpointsConfigHandler
    IptInterface     utiliptables.Interface
    OomAdjuster      *oom.OomAdjuster
    Proxier          proxy.ProxyProvider
    Recorder         record.EventRecorder
    ServiceConfig    *proxyconfig.ServiceConfig
}

变量Config保存的kube-proxy的配置信息是一个结构体ProxyServerConfig,在这个结构体中变量BindAddress表示kube-proxy所在的IP地址,HealthzPort表示用来进行健康检查的端口,如果配置成0,那么表示不进行健康检查,变量OOMScoreAdj是kube-proxy进程的oom-score-adj值,变量PortRange表示NODE节点上可以被kube-proxy使用到的端口范围,如果不设置的话,那么kube-proxy会随机选择NODE节点上的端口。

type ProxyServerConfig struct {
    BindAddress        net.IP
    HealthzPort        int
    HealthzBindAddress net.IP
    OOMScoreAdj        int
    ResourceContainer  string
    Master             string
    Kubeconfig         string
    PortRange          util.PortRange
    HostnameOverride   string
    ProxyMode          string
    SyncPeriod         time.Duration
    nodeRef            *api.ObjectReference // Reference to this node.
    MasqueradeAll      bool
    CleanupAndExit     bool
    UDPIdleTimeout     time.Duration
}

Kube-proxy会读取配置文件,然后根据配置文件初始化ProxyServer结构体,接着创建一个Kubernetes客户端程序,这个客户端程序会监听Service和Endpoint的变化,如果发生变化,那么对NODE节点上的路由规则进行同步,Kube-proxy有两种操作路由规则的方式,一种是通过iptables,一种是通过userspace。如果配置了HealthzPort端口,那么Kube-proxy还会启动健康检查服务。

你可能感兴趣的:(容器)