通用技术 一文带你看透 kubernetes 容器编排系统

匿名 · 2019年01月07日 · 1053 次阅读

本文由云 + 社区发表

作者:turboxu

Kubernetes 作为容器编排生态圈中重要一员,是 Google 大规模容器管理系统 borg 的开源版本实现,吸收借鉴了 google 过去十年间在生产环境上所学到的经验与教训。 Kubernetes 提供应用部署、维护、 扩展机制等功能,利用 Kubernetes 能方便地管理跨机器运行容器化的应用。当前 Kubernetes 支持 GCE、vShpere、CoreOS、OpenShift、Azure 等平台,除此之外,也可以直接运行在物理机上.kubernetes 是一个开放的容器调度管理平台,不限定任何一种言语,支持 java/C++/go/python 等各类应用程序 。

kubernetes 是一个完备的分布式系统支持平台,支持多层安全防护、准入机制、多租户应用支撑、透明的服务注册、服务发现、内建负载均衡、强大的故障发现和自我修复机制、服务滚动升级和在线扩容、可扩展的资源自动调度机制、多粒度的资源配额管理能力,完善的管理工具,包括开发、测试、部署、运维监控,一站式的完备的分布式系统开发和支撑平台。

一. 系统架构

kubernetes 系统按节点功能由 master 和 node 组成。

img

Master

Master 作为控制节点,调度管理整个系统,包含以下组件:

API Server 作为 kubernetes 系统的入口,封装了核心对象的增删改查操作,以 RESTful 接口方式提供给外部客户和内部组件调用。它维护的 REST 对象将持久化到 etcd。

Scheduler:负责集群的资源调度,为新建的 pod 分配机器。这部分工作分出来变成一个组件,意味着可以很方便地替换成其他的调度器。

Controller Manager:负责执行各种控制器,目前有两类:

  1. Endpoint Controller:定期关联 service 和 pod(关联信息由 endpoint 对象维护),保证 service 到 pod 的映射总是最新的。
  2. Replication Controller:定期关联 replicationController 和 pod,保证 replicationController 定义的复制数量与实际运行 pod 的数量总是一致的。

Node

Node 是运行节点,运行业务容器,包含以下组件:

Kubelet:责管控 docker 容器,如启动/停止、监控运行状态等。它会定期从 etcd 获取分配到本机的 pod,并根据 pod 信息启动或停止相应的容器。同时,它也会接收 apiserver 的 HTTP 请求,汇报 pod 的运行状态。

Kube Proxy:负责为 pod 提供代理。它会定期从 etcd 获取所有的 service,并根据 service 信息创建代理。当某个客户 pod 要访问其他 pod 时,访问请求会经过本机 proxy 做转发。

借用一张网图,表达功能组件之间关系:

img

二.基本概念

Node

node 是 kubernetes 集群中相对于 master 而言的工作主机,在较早版本中也被称为 minion。Node 可以是一台物理主机,也可以是一台虚拟机(VM)。在每个 node 上运行用于启动和管理 pod 的服务——kubelet,并能够被 master 管理。在 node 上运行的服务进程包括 kubelet、kube-proxy 和 docker daemon。

Node 的信息如下:

node 地址:主机的 IP 地址或者 nodeid。

node 的运行状态: pending,running,terminated。

node condition: 描述 running 状态 node 的运行条件,目前只有一种条件 Ready,表示 node 处于健康状态,可以接收 master 发来的创建 pod 的指令。

node 系统容量:描述 node 可用的系统资源,包括 CPU、内存、最大可调度 pod 数量等。

Pod

pod 是 kubernetes 的最基本操作单元,包括一个或多个紧密相关的容器,一个 pod 可以被一个容器化的环境看作应用层的 “逻辑宿主机”( Logical host )。一个 pod 中的多个容器应用通常是紧耦合的。Pod 在 node 上被创建、启动或者销毁。

为什么 kubernetes 使用 pod 在容器之上再封装一层呢?一个很重要的原因是,docker 容器之间通信受到 docker 网络机制的限制。在 docker 的,世界中,一个容器需要通过 link 方式才能访问另一个容器提供的服务(端口)。大量容器之间的 link 将是一个非常繁重的工作。通过 pod 的概念将多个容器组合在一个虚拟的 “主机” 内,可以实现容器之间仅需通过 localhost 就能相互通信了。

一个 pod 中的应用容器共享一组资源,如:

pid 命名空间:pod 中的不同应用程序可以看到其他的进程 PID

网络命名空间:pod 中的多个容器能够访问同一个 IP 和端口范围

IPC 命名空间:pod 中的多个容器能够使用 systemV ipc 或 POSIX 消息队列进行通信。

UTS 命名空间:pod 中的多个容器共享一个主机名。

Volumes(共享存储卷):pod 中的各个容器可以访问在 pod 级别定义的 volumes。

Label

label 是 kubernetes 系统中的一个核心概念。Label 以 key/value 键值对的形式附加到各种对象上,如 pod、service、RC、Node 等。Label 定义了这些对象的可识别属性,用来对它们进行管理和选择。Label 可以在创建对象时附加到对象上,也可以在对象创建后通过 API 进行管理。

在为对象定义好 label 后,其他对象就可以使用 label selector 来定义其他作用的对象了。

label selector 的定义由多个逗号分隔的条件组成: “label”: { “key1”: ” value1”, “key2”: ” value2” }

Resource controller(RC)

Resource controller(RC) 是 kubernetes 系统中的核心概念,用于定义 pod 副本的数量。在 master 的 Controller manager 进程通过 RC 的定义来完成 pod 的创建、监控、启停等操作。

根据 replication controller 的定义,kubernetes 能够确保在任意时刻都能运行用户指定的 pod“副本”(replica)数量。如果有过多的的 pod 副本在运行,系统会停掉一些 pod;如果运行的 pod 副本数量太少,系统就会再启动一些 pod,总之,通过 RC 的定义,kubernetes 总是保证集群中运行着用户期望副本数量。

Service(服务)

在 kubernetes 的世界里,虽然每个 pod 都会被分配一个单独的 IP 地址,但这个 IP 地址会随着 pod 的销毁而消失。这就引出一个问题:如果有一组 pod 组成一个集群来提供服务,那么如何来访问它们呢?

kubernetes 的 service 就是用来解决这个问题的核心概念。一个 service 可以看作一组提供相同服务的 pod 的对外访问接口。Service 作用于哪些 pod 是通过 label selector 来定义的。

pod 的 IP 地址是 docker daemon 根据 docker0 网桥的 IP 地址段进行分配的,但 service 的 Cluster IP 地址是 kubernetes 系统中的虚拟 IP 地址,由系统动态分配。 Service 的 ClusterIP 地址相对于 pod 的 IP 地址来说相对稳定,service 被创建时即被分配 IP 地址,在销毁该 service 之前,这个 IP 地址都不会再变化。

由于 service 对象在 Cluster IP Range 池中分配到的 IP 只能在内部访问,所以其他 pod 都可以无障碍地访问到它。但如果这个 service 作为前端服务,准备为集群外的客户端提供服务,我们就需要给这个服务提供公共 IP 了。

kubernetes 支持两种对外提供服务的 service 的 type 定义:nodeport 和 loadbalancer。

Volume(存储卷)

volume 是 pod 中能够被多个容器访问的共享目录。Kubernetes 的 volume 概念与 docker 的 volume 比较类似,但并不完全相同。Kubernetes 中的 volume 与 pod 生命周期相同,但与容器的生命周期不相关。当容器终止或重启时,volume 中的数据也不会丢失。另外,kubernetes 支持多种类型的 volume,并且一个 pod 可以同时使用任意多个 volume。

(1)EmptyDir:一个 EmptyDir volume 是在 pod 分配到 Node 时创建的。从它的名称就可以看出,它的初始内容为空。在同一个 pod 中所有容器可以读和写 EmptyDir 中的相同文件。当 pod 从 node 上移除时,EmptyDir 中的数据也会永久删除。

(2)hostPath:在 pod 上挂载宿主机上的文件或目录。通常用于: 容器应用程序生成的日志文件需要永久保存,可以使用宿主机的高速文件系统进行存储; 需要访问宿主机上的 docker 引擎内部数据结构的容器应用,可以通过定义 hostPath 为宿主机/var/lib/docker 目录,使容器内部应用可以直接访问 docker 的文件系统。

(3)gcePersistentDick:使用这种类型的 volume 表示使用谷歌计算引擎(Google Compute Engine, GCE)上永久磁盘(persistent disk,PD)上的文件。与 EmptyDir 不同,PD 上的内容会永久保存,当 pod 被删除时,PD 只是被卸载(unmount),但不会被删除。需要注意的是,你需要先创建一个永久磁盘(PD)才能使用 gcePersistentDisk。

(4)awsElasticBlockStore:与 GCE 类似,该类型的 volume 使用 Amazon 提供的 Amazon Web Service(AWS)的 EBS Volume,并可以挂载到 pod 中去。需要注意的是,需要先创建一个 EBS Volume 才能使用 awsElasticBlockStore。

(5)nfs:使用 NFS(网络文件系统)提供的共享目录挂载到 Pod 中。在系统中需要一个支行中的 NFS 系统。

(6)iscsi:使用 iSCSI 存储设备上的目录挂载到 pod 中。

(7)glusterfs:使用开源 BlusterFS 网络文件系统的目录挂载到 pod 中。

(8)rbd:使用 Linux 块设备共享存储(Rados Block Device)挂载到 pod 中。

(9)gitRepo:通过挂载一个空目录,并从 GIT 库 clone 一个 git repository 以供 pod 使用。

(10)secret:一个 secret volume 用于为 pod 提供加密的信息,你可以将定义在 kubernetes 中的 secret 直接挂载为文件让 pod 访问。Secret volume 是通过 tmfs(内存文件系统)实现的,所以这种类型的 volume 总是不会持久化的。

(11)persistentVolumeClaim:从 PV(persistentVolume)中申请所需的空间,PV 通常是种网络存储,如 GCEPersistentDisk、AWSElasticBlockStore、NFS、iSCSI 等。

Namespace(命名空间)

namespace(命名空间)是 kubernetes 系统中另一个非常重要的概念,通过将系统内部的对象 “分配” 到不同的 namespace 中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能分别管理。

kubernetes 集群在启动后,会创建一个名为 “default” 的 namespace。接下来,如果不特别指明 namespace,则用户创建的 pod、RC、Service 都将被系统创建到名为 “default” 的 namespace 中。

使用 namespace 来组织 kubernetes 的各种对象,可以实现对用户的分组,即 “多租户” 管理。对不同的租房还可以进行单独的资源配额设备和管理,使得整个集群配置非常灵活、方便。

Annotation(注解)

annotation 与 label 类似,也使用 key/value 键值对的形式进行定义。Label 具有严格的全名规则,它定义的是 kubernetes 对象的元数据(metadata),并且用于 label selector。Annotation 则是用户任意定义的 “附加” 信息,以便于外部工具进行查找。

用 annotation 来记录的信息包括: build 信息、release 信息、docker 镜像信息等,如时间戳、release id 号、PR 号、镜像 hash 值、docker Controller 地址等。

典型流程

以创建一个 Pod 为例,kubernetes 典型的流程如下图所示:

img

三.组件

Replication Controller

为了区分 Controller Manager 中的 Replication Controller(副本控制器) 和资源对象 Replication Controller,我们将资源对象简写为 RC,而 Replication Controller 特指 “副本控制器”。

Replication Controller 的核心作用是确保在任何时间集群中一个 RC 所关联的 pod 都保持一定数量的 pod 副本处于正常运行状态。如果该类 pod 的 pod 副本数量太多,则 Replication Controller 会销毁一些 pod 副本;反之 Replication Controller 会添加 pod 副本,直到该类 pod 的 pod 副本数量达到预设的副本数量。最好不要超过 RC 直接创建 pod,因为 Replication Controller 会通过 RC 管理 pod 副本,实现自动创建、补足、替换、删除 pod 副本,这样就能提高系统的容灾能力,减少由于节点崩溃等意外状况造成的损失。即使应用程序只用到一个 pod 副本,也强烈建设使用 RC 来定义 pod。

Replication Controller 管理的对象是 pod,因此其操作与 pod 的状态及重启策略息息相关。

副本控制器的常用使用模式:

(1)重新调度:不管想运行 1 个副本还是 1000 副本,副本控制器能够确保指定 pod 数量的副本存在于集群中,如果节点故障或副本被终止运行等意外情况,将会重新调度直到达到预期的副本正常运行。

(2)弹性伸缩:手动或通过自动扩容代理修改副本控制器的 spec.replicas 属性值,非常容易实现扩大或缩小副本的数量。

(3)滚动更新:副本控制器被设计成通过逐个替换 pod 的方式来辅助服务的滚动更新。推荐的方式是创建一个新的只有一个副本的 RC,若新的 RC 副本数量加 1,则旧的 RC 的副本数量减 1,直到这个旧的 RC 副本数量为零,然后删除该旧的 RC。

在滚动更新的讨论中,我们发现一个应用在滚动更新时,可能存在多个版本的 release。事实上,在生产环境中一个已经发布的应用程序存在多个 release 版本是很正常的现象。通过 RC 的标签选择器,我们能很方便地实现对一个应用的多版本 release 的跟踪。

node controller

Node Controller 负责发现、管理和监控集群中的各个 node 节点。Kubelet 在启动时通过 API Server 注册节点信息,并定时向 API Server 发送节点信息。API Server 接收到这些信息后,将这些信息写入 etcd。存入 etcd 的节点信息包括节点健康状况、节点资源、节点名称、节点地址信息、操作系统版本、docker 版本、kubelet 版本等。节点健康状况包含 “就绪(true)”、“未就绪 (false)” 和 “未知(unknown)” 三种。

img

(1)Controller Manager 在启动时如果设置了—cluster-cidr 参数,那么为每个没有设置 spec.podCIDR 的 node 生成一个 CIDR 地址,并用该 CIDR 设置节点的 spec.PodCIDR 属性,这样的目的是防止不同节点的 CIDR 地址发生冲突。

(2)逐个读取节点的信息,多次尝试修改 nodeStatusMap 中的节点状态信息,将该节点信息和 node controller 的 nodeStatusMap 中保存的节点信息比较。如果判断中没有收到 kubelet 发送的节点信息、第一次收到节点 kubelet 发送的节点信息,或在该处理过程中节点状态变成非 “健康” 状态,则在 nodeStatusMap 中保存该节点的状态信息,并用 node controller 所在节点的系统时间作为探测时间和节点状态变化时间。 如果判断出在某一段时间内没有收到节点的状态信息,则设置节点状态为 “未知(unknown)”,并且通过 api server 保存节点状态。

(3)逐个读取节点信息,如果节点状态变为非 “就绪” 状态,则将节点加入待删除队列,否则将节点从该队列中删除。如果节点状态为非 “就绪” 状态,且系统指定了 Cloud Provider,则 node controller 调用 Cloud Provider 查看节点,若发现并节点故障,则删除 etcd 中的节点信息,并删除和该节点相关的 pod 等资源的信息。

ResourceQuota controller

作为容器集群的管理平台, kubernetes 也提供了资源配额管理这一高级功能,资源配额管理确保指定的对象在任何时候都不会超量占用系统资源,避免了由于某些业务进程的设计或实现的缺陷导致整个系统运行紊乱甚至意外宕机,对整个集群的平稳运行和稳定性有非常重要的作用。

目前 kubernetes 支持三个层次的资源配额管理:

(1)容器级别,可以对 CPU 和内存的资源配额管理。

(2)pod 级别,可以对 pod 内所有容器的可用资源进行限制。

(3)namespace 级别,为 namespace(可以用于多租户)级别的资源限制,包括:pod 数量、replication Controller 数量、service 数量、ResourceQuota 数量、secret 数量、可持有的 PV(persistent volume)数量。 kubernetes 的配额管理是通过准入机制(admission control)来实现的,与配额相关的两种准入控制器是 LimitRanger 和 ResoureQuota,其中 LimitRanger 作用于 pod 和 container 上,ResourceQuota 则作用于 namespace 上。此外,如果定义了资源配额,则 scheduler 在 pod 调度过程中也会考虑这一因素,确保 pod 调度不会超出配额限制。

典型的资源控制流程如下图所示:

img

namaspace controller

img

用户通过 API Server 可以创建新的 namespace 并保存在 etcd 中,namespace controller 定时通过 api server 读取这些 namespace 信息。如果 namespace 被 API 标识为优雅删除(设置删除期限,deletionTimestamp 属性被设置),则将该 namespace 的状态设置为 “terminating” 并保存到 etcd 中。同时 namespace controller 删除该 namespace 下的 serviceAccount、RC、Pod、Secret、PersistentVolume、ListRange、SesourceQuota 和 event 等资源对象。 当 namespace 的状态被设置为 “terminating” 后,由 Adminssion Controller 的 NamespaceLifecycle 插件来阻止为该 namespace 创建新的资源。同时,在 namespace controller 删除完该 namespace 中的所有资源对象后,Namespace Controller 对该 namespace 执行 finalize 操作,删除 namespace 的 spec.finalizers 域中的信息。

如果 Namespace Controller 观察到 namespace 设置了删除期限(即 DeletionTimestamp 属性被设置),同时 namespacer 的 spec.finalizers 域值是空的,那么 namespace controller 将通过 API Server 删除该 namespace 资源。

kubernetes 安全控制

ServiceAccount Controller 和 token Controller 是与安全相关的两个控制器。ServiceAccount Controller 在 Controller Manager 启动时被创建。它监听 Service Account 的删除事件和 Namespace 的创建、修改事件。如果在该 Service Account 的 namespace 中没有 default Service Account,那么 ServiceAccount Controller 为该 Service Account 的 namespace 创建一个 default ServiceAccount。

在 API Server 的启动中添加 “—admission_control=ServiceAccount” 后,API Server 在启动时会自己创建一个 key 和 crt(/var/run/kubernetes/apiserver.crt 和 apiserver.key),然后在启动./kube-controller-manager 时添加参数 service_account_privatge_key_file=/var/run/kubernetes/apiserver.key,这样启动 kubernetes master 后,就会发现在创建 Service Account 时系统会自动为其创建一个 secret。

如果 Controller Manager 在启动时指定参数为 service-account-private-key-file,而且该参数所指定的文件包含一个 PEM-encoded 的编码的 RSA 算法的私钥,那么,Controler Manager 会创建 token controller 对象。

Token controller

token controller 对象监听 Service Account 的创建、修改和删除事件,并根据事件的不同做不同的处理。如果监听到的事件是创建和修改 Service Account 事件,则读取该 Service Account 的信息;如果该 Service Account 没有 Service Account Secret(即用于访问 Api server 的 secret),则用前面提及的私钥为该 Service Account 创建一个 JWT Token,将该 Token 和 ROOT CA(如果启动时参数指定了该 ROOT CA)放入新建的 secret 中,将该新建的 secret 放入该 Service Account 中,同时修改 etcd 中 Service Account 的内容。如果监听到的事件是删除 Service Account 事件,则删除与该 Service Account 相关的 secret。

token controller 对象同时监听 secret 的创建、修改和删除事件,并根据事件的不同做不同的处理。如果监听到的事件是创建和修改 secret 事件,那么读取该 secret 中 annotation 所指定的 Service Account 信息,并根据需要为该 secret 创建一个和其 Service Account 相关的 token;如果监听到的事件是删除 secret 事件,则删除 secret 和相关的 Service Account 的引用关系。

service controller&endpoint controller

img

Kubernetes service 是一个定义 pod 集合的抽象,或者被访问都看作一个访问策略,有时也被称为微服务。 kubernetes 中的 service 是种资源对象,各所有其他资源对象一样,可以通过 API Server 的 POST 接口创建一个新的实例。在下面的例子代码中创建了一个名为 “MyServer” 的 Service,它包含一个标签选择器,通过该标签选择器选择所有包含标签为 “app=MyApp” 的 pod 作为该 service 的 pod 集合。Pod 集合中的每个 pod 的 80 端口被映射到节点本地的 9376 端口,同时 kubernetes 指派一个集群 IP(即虚拟 IP)给该 service。

{
    kind: service,
    apiVersion: v1,
    metadata: {
        name: MyService
    },
    spec: {
        selector: {
            app: MyApp
        },
        ports: [
            {
                protocol: TCP,
                port: 80,
                targetPort: 9376
            }
        ]
    },

四.功能特性

Service 集群访问流程(服务发现)

img

在 kubernetes 集群中的每个 node 上都运行着一个叫 “kube-proxy” 的进程,该进程会观察 master 节点添加和删除 “service” 和 “endpoint” 的行为,如图中第 1 步所示。

kube-proxy 为每个 service 在本地主机上开一个端口(随机选择)。任何访问该端口的连接都被代理到相应的一个后端 pod 上。Kube-proxy 根据 round robin 算法及 service 的 session 粘连(SessionAffinity)决定哪个后台 pod 被选中,如第 2 步所示。

最后,如第 3 步所示,kube-proxy 在本机的 iptables 中安装相应的规则,这些规则使得 iptables 将捕获的流量重定向到前面提及的随机端口。通过该端口流量再被 kube-proxy 转到相应的后端 pod 上。

在创建了服务后,服务 endpoint 模型会创建后端 pod 的 IP 和端口列表(包含中 endpoint 对象中),kube-proxy 就是从这个 endpoint 列表中选择服务后端的。集群内的节点通过虚拟 IP 和端口能够访问 service 后台的 pod。

在默认情况下,kubernetes 会为 server 指定一个集群 IP(或虚拟 IP、cluster IP),但在某些情况下,希望能够自己指定该集群 IP。为了给 service 指定集群 IP,用户只需要在定义 service 时,在 service 的 spec.clusterIP 域中设置所需要的 IP 地址即可。

Scheduler(调度)

scheduler 在整个 kubernetes 系统中承担了 “承上启下” 的重要功能,“承上” 是指它负责接收 Controller Manager 创建的新 pod,为其安排一个落脚的 “家”——目标 node;“启下” 是指安置工作完成后,目标 node 上的 kubelet 服务进程接管后继工作,负责 pod 生命周期中的 “下半生”。

具体来说,scheduler 的作用是将待调度的 pod(API 新创建的 Pod、Controller Manager 为补足副本而创建的 pod 等)按照特定的调度算法和调度策略绑定(binding)到集群中的某个合适的 node 上,并将绑定信息写入 etcd 中。在整个调度过程中涉及三个对象,分别是:待调度的 pod 列表、可用 node 列表、以及调度算法和策略。简单地说,就是通过调度算法调度,为待调度 pod 列表中的每个 pod 从 node 列表中选择一个最适合的 node。

随后,目标 node 上的 kublet 通过 API Server 监听到 scheduler 产生的 pod 绑定事件,然后获对应的取 pod,下载 image 镜像,并启动容器。

img

Scheduler(调度策略)

scheduler 当前提供的默认调度流程分为两步:

(1)预选调度过程,即遍历所有目标 node,筛选出符合要求的候选节点。为此 kubernetes 内置了多种预先策略(xxx predicates)供用户选择。

(2)确定最优节点,在第一步的基础上,采用优先策略(xxx priority)计算出每个候选节点的积分,积分最高都胜出。 scheduler 的调度流程是通过插件方式加载的 “调度算法提供者(AlgorithmProvider)” 具体实现的。一个 AlgorithmProvider 其实就是包括了一组预选策略与一组优选策略的结构体,注册 AlgorithmProvider 的函数如下: func RegisterAlgorithmProvider(name string, predicateKeys, priorityKeys util.StringSet) 它包含 3 个参数,name string 参数为算法名;

predicateKeys 参数为算法集合用到的预选策略集合 priorityKeys 参数为算法用到的优选策略集合 scheduler 中可用的预选策略包含 7 个,每个节点只有通过 PodFitsPorts、PodFitsResources、NoDiskConflict、PodSelectorMatches、PodFitsHost 5 个默认预先策略后,才能初步被选中,进入下一个流程。

每个节点通过优选策略时都会算出一个得分,计算各项得分,最终选出得分值最大的节点作为优选的结果(也是调度算法的结果)。LeastRequestedPriority,选择资源消耗最小节点:

(1) 计算出所有备选节点上运行的 pod 和备选 pod 的 CPU 占用量 totalMilliCPU

(2) 计算出所有备选节点上运行的 pod 和备选 pod 的内存占用量 totalMomory

(3) 计算每个节点的得分,计算规则大致如下: score=int(((nodeCpuCapacity-totalMilliCPU)*10)/nodeCpuCapacity+((nodeMemoryCapacity-totalMemory)*10/nodeMemoryCapacity)/2) CalculateNodeLabelPriority,根据 CheckNodeLabelPresence 策略打分 BalancedResourceAllocation,选择资源使用最均衡节点

(1) 计算出所有备选节点上运行的 pod 和备选 pod 的 CPU 占用量 totalMilliCPU

(2) 计算出所有备选节点上运行的 pod 和备选 pod 的内存占用量 totalMomory

(3) 计算每个节点的得分,计算规则大致如下: score=int(10-math.abs(totalMilliCPU/nodeCpuCapacity-totalMemory/nodeMemoryCapacity) * 10)

节点管理

节点管理包含节点的注册、状态上报、Pod 管理、容器健康检查、资源监控等部分。

img

节点注册

在 kubernetes 集群中,在每个 node 节点上都会启动一个 kubelet 服务进程。该进程用于处理 master 节点下发到本节点的任务,管理 pod 及 pod 中的容器。每个 kubelet 进程会在 API Server 上注册节点自身信息,定期向 master 节点汇报节点资源使用情况,并通过 cAdvisor 监控容器和节点资源。

节点通过设置 kubelet 的启动参数 “—register-node”,来决定是否向 API Server 注册自己。如果该参数为 true,那么 kubelet 将试着通过 API Server 注册自己。作为自注册,kubelet 启动还包含下列参数:

--api-servers,告诉 kubelet API Server 的位置; --kubeconfig,告诉 kubelet 在哪儿可以找到用于访问 API Server 的证书; --cloud-provider,告诉 kubelet 如何从云服务商(IAAS)那里读取到和自己相关的元数据。

状态上报

kubelet 在启动时通过 API Server 注册节点,并定时向 API Server 发送节点新消息,API Server 在接收到这些信息后,将这些信息写入 etcd。通过 kubelet 的启动参数 “—node-status-update-frequency” 设置 kubelet 每隔多少时间向 API Server 报告节点状态,默认为 10 秒。

Pod 管理

kubelet 通过以下几种方式获取自身 node 上所要运行的 pod 清单:(1)文件:kubelet 启动参数 “--config” 指定的配置文件目录下的文件。通过—file-check-frequency 设置检查该文件目录的时间间隔,默认为 20 秒。

(2)HTTP 端点(URL):通过 “—manifest-url” 参数设置。通过—http-check-frequency 设置检查该 HTTP 端点的数据时间间隔,默认为 20 秒。

(3)API Server:kubelet 通过 API Server 监听 etcd 目录,同步 pod 清单。

所有以非 API Server 方式创建的 pod 都叫作 static pod。Kubelet 将 static pod 的状态汇报给 API Server,API Server 为 static pod 创建一个 mirror pod 和其相匹配。Mirror pod 的状态将真实反映 static pod 的状态。当 static pod 被删除时,与之相对应的 mirror pod 也会被删除。Kubelet 通过 API Server client 使用 watch+list 的方式监听 “/registry/node/<当前 node 名称>” 和 “/registry/pods” 目录,将获取的信息同步到本地缓存中。

kubelet 监听 etcd,所有针对 pod 的操作将会被 kubelet 监听到。如果发现有新的绑定到本节点的 pod,则按照 pod 清单的要求创建该 pod。如果发现本地的 pod 被修改,则 kubelet 会做出相应的修改,如删除 pod 中的某个容器时,则通过 docker client 删除该容器。如果发现删除本节点的 pod,则删除相应的 pod,并通过 docker client 删除 pod 中的容器。

kubelet 读取监听到的信息,如果是创建和修改 pod 任务,则做如下处理:

(1)为该 pod 创建一个数据目录。

(2)从 API Server 读取该 pod 清单。

(3)为该 pod 挂载外部卷(Extenal Volume)。

(4)下载 pod 用到的 secret。

(5)检查已经运行在节点中的 pod,如果该 pod 没有容器或 pause 容器没有启动,则先停止 pod 里所有容器进程。如果在 pod 中有需要删除的容器,则删除这些容器。

(6)用 “kubernetes/pause” 镜像为每个 pod 创建一个容器,该 pause 容器用于接管 pod 中所有其他容器的网络。

(7)为 pod 中的每个容器做如下处理:

为容器计算一个 hash 值,然后用容器的名字去 docker 查询对应容器的 hash 值。若查到容器,且两者 hash 值不同,则停止 docker 中容器进程,并停止与之关联的 pause 容器进程;若两者相同不做任何处理。

如果容器被中止了,且容器没有指定的 restartPolicy(重启策略),则不做任何处理。

调用 docker client 下载容器镜像,调用 docker client 运行容器。

容器健康检查

pod 通过两类探针来检查容器的健康状态。一个是 LivenessProbe 探针,用于判断容器是否健康,告诉 kubelet 一个容器什么时候处于不健康的状态。如果 LivenessProbe 探针探测到容器不健康,则 kubelet 将删除容器,并根据容器的重启策略做相应的处理。如果一个容器不包含 LivenessProbe 探针,那么 kubelet 认为该容器的 LivenessProbe 探针返回的值永远是 “success”.另一类是 ReadinessProbe 探针,用于判断容器是否启动完成,且准备接收请求。如果 ReadinessProbe 探针检测到失败,则 pod 的状态将被修改。Endpoint controller 将从 service 的 endpoint 中删除包含该容器所在 pod 的 IP 地址的 endpoint 条目。

参考

《kubernetes 权威指南》

此文已由作者授权腾讯云 + 社区在各渠道发布

获取更多新鲜技术干货,可以关注我们腾讯云技术社区 - 云加社区官方号及知乎机构号

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册