Kubernetes 1.3 的现在和未来

本文主要讨论Kubernetes 1.3 版本的一些新功能以及对未来功能的展望。阅读本文需要读者对kubernetes的基本结构有所了解。

支持更多类型的应用

Init container

Init container是1.3中的alpha feature,目的是支持一类需要在启动Pod“普通容器”前,先进行Pod初始化的应用。执行该初始化任务的容器被成为“初始化容器”(init container)。例如,在启动应用之前,初始化数据库,或等待数据库启动等。下图是一个包含init container的Pod:

图片描述

对于此类Pod,kubernetes的运行策略如下:

  • 初始化容器按顺序依次执行,即图中容器1->2
  • 若其中某一个初始化容器运行失败,则整个Pod失败
  • 当所有初始化容器运行成功,启动普通容器,即图中容器A和B
    在alpha版本中使用init container需要用annotation,下图是来自k8s的一个例子(略有裁剪):

图片描述

可以看到,我们在启动nginx普通容器之前,先用init container来获取index.html,之后访问nginx就会直接返回该文件。当init container功能稳定后,k8s会直接在pod.spec内加上initContainers字段,如下所示:

图片描述

init container看起来是一个小功能,但是在实现上还是需要考虑不少问题,比如几个比较重要的点:

资源问题:当调度存在init container的Pod时,应该怎样计算所需要的资源?两个极端情况:如果对init container和regular container所需要的资源求和,那么当init container成功初始化Pod之后,就不会再使用所请求的资源,而系统认为在使用,会造成浪费;反之,不计算init container的资源又会导致系统不稳定(init container所使用的资源未被算入调度资源内)。目前的方法是取折中:由于初始化容器和普通容器不会同时运行,因此Pod的资源请求是两者中的最大值。对于初始化容器,由于他们是依次运行,因此选择其中的最大值;对于普通容器,由于是同时运行,选择容器资源的和。

Pod Status: 目前,Pod有Pending, Running, Terminating等状态。对于有初始化容器的Pod,如果仍然使用Pending状态,则很难区分Pod当前在运行初始化容器还是普通容器。因此,理想情况下,我们需要增加一个类似于Initializing的状态。在alpha版本中暂时还未添加。

健康检查及可用性检查:有了init container之后,我们该如何检查容器的健康状态?alpha版本将两个检查都关闭了,但init container是会在node上实实在在运行的容器,理论上是需要进行检查的。对于可用性检查,关闭掉是一个可行的办法,因为init container的可用性其实就是当它运行结束的时候。对于健康检查,node需要知道某个Pod是否处在初始化阶段;如果处在初始化阶段,那么node就可以对init container进行健康检查。因此,kubernetes很有可能在添加类似Initializing的Pod状态之后,开启init container的健康检查。

围绕init container的问题还有很多,比如QoS,Pod的更新等等,其中不少都是有待解决的问题,这里就不一一展开。

PetSet

PetSet应该算是社区期待已久的功能,其目的是支持有状态和集群化的应用,目前也是alpha阶段。PetSet的应用场景很多,包括类似zookeeper、etcd之类的quorum leader election应用,类似Cassandra的Decentralized quorum等。PetSet中,每个Pod都有唯一的身份,分别包括:名字,网络和存储;并由新的组件PetSet Controller负责创建和维护。下面依次看一看kubernetes是如何维护Pod的唯一身份。

名字比较容易理解,当我们创建一个RC之后,kubernetes会创建指定副本数量的Pod,当使用kubectl获取Pod信息时,我们会得到如下信息:

图片描述

其中,5个字符的后缀为kubernetes自动生成。当Pod重启,我们会得到不同的名字。对于PetSet来讲,Pod重启必须保证名字不变。因此,PetSet控制器会维护一个identityMap,每一个PetSet中的每个Pod都会有一个唯一名字,当Pod重启,PetSet控制器可以感知到是哪个Pod,然后通知API server创建新的同名Pod。目前的感知方法很简单,PetSet控制器维护的identityMap将Pod从0开始进行编号,然后同步的过程就像报数一样,哪个编号不在就重启哪个编号。
图片描述

此外,该编号还有另外一个作用,PetSet控制器通过编号来确保Pod启动顺序,只有0号Pod启动之后,才能启动1号Pod。

网络身份的维护主要通过稳定的hostname和domain name来维护,他们通过PetSet的配置文件指定。例如,下图是一个PetSet的Yaml文件(有裁剪),其中metadata.name指定了Pod的hostname前缀(后缀即前面提到的从0开始的索引),spec.ServiceName指定了domain name。

图片描述

通过上面的Yaml文件创建出来两个Pod:web-0和web-1。其完整的域名为web-0.nginx.default.svc.cluster.local,其中web-0为hostname,nginx为Yaml中指定的domain name,剩下的部分与普通service无异。当创建请求被下发到节点上时,kubelet会通过container runtime设置UTS namespace,如下图所示(省略了部分组件如apiserver)。

图片描述

此时,hostname已经在容器层面设置完成,剩下还需要为hostname增加集群层面的解析,以及添加domain name的解析,这部分工作理所当然就交给了kube dns。了解Kubernetes的读者应该知道,要添加解析,我们需要创建service;同理,这里也需要为PetSet创建service。不同的是,普通的service默认后端的Pod是可替换的,并采用诸如roundrobin,client ip的方式选择后端的Pod,这里,由于每个Pod都是一个Pet,我们需要定位每一个Pod,因此,我们创建的service必须要能满足这个要求。在PetSet中,利用了kubernetes headless service。Headless service不会分配cluster IP来load balance后端的Pod,但会在集群DNS服务器中添加记录:创建者需要自己去利用这些记录。下图是我们需要创建的headless service,注意其中的clusterIP被设置为None,表明这是一个headless service。

图片描述

Kube dns进行一番处理之后,会生成如下的记录:

图片描述

可以看到,访问web-0.nginx.default.svc.cluster.local会返回pod IP,访问nginx.default.svc.cluster.local会返回所有Pet中的pods IP。一个常见的方式是通过访问domain的方式来获取所有的peers,然后依次和单独的Pod通信。

存储身份这块采用的是PV/PVC实现,当我们创建PetSet时,需要指定分配给Pet的数据卷,如下图:

图片描述

这里,volumeClaimTemplates指定每个Pet需要的存储资源。注意目前所有Pet都得到相同大小和类型的数据卷。当PetSet控制器拿到请求时,会为每一个Pet创建PVC,然后将每个Pet和对应的PVC联系起来:

图片描述

之后的PetSet只需要确保每个Pet都与相对应的PVC绑定在一起即可,其他工作,类似于创建数据卷,挂载等工作,都交给其他组件。

通过名字,网络,存储,PetSet能够cover大多数的案例。但是,目前还存在很多需要完善的地方,感兴趣的读者可以参考:https://github.com/kubernetes/kubernetes/issues/28718

Scheduled Job

Scheduled Job本质上是集群cron,类似mesos chronos,采用标准的cron语法。遗憾的是在1.3中还并未达到发布的标准。Scheduled Job其实在很早就提出来过,但当时kubernetes的重点还在API层面,并且即使有很大需求,也计划在Job(1.2GA)之后实现。当scheduled job在之后的版本发布之后,用户可以用一条简单的命令在kubernetes上运行Job,

例如:

kubectl run cleanup -image=cleanup –runAt=”0 1 0 0 *” – /scripts/cleanup.sh

一些关于scheduled job的更新可以参考:https://github.com/kubernetes/kubernetes/pull/25595

Disruption Budget

Disruption Budget的提出是为了向Pod提供一个反馈机制,确保应用不会被集群自身的变动而受影响。例如,当集群需要进行重调度时,应用可以通过Disruption Budget来说明Pod能不能被迁移。Disruption Budget只负责集群自身发起的变动,不负责突发事件比如节点突然掉线,或者应用本身的问题比如不断重启的变动。Disruption Budget同样没有在1.3中发布。
与kubernetes大多数资源类似,我们需要通过Yaml文件创建一个PodDisruptionBudget资源,例如,下图所示的Disruption Budget选中了所有带有app:nginx标签的pod,并且要求至少有3个Pod在同时运行。

图片描述

Controller manager内有一个新的组件DisruptionBudget Controller,来负责维护所有Budget的状态,例如上图中的status表明当前共有4个健康的Pod(currentHealthy),应用要求至少有3个(desiredHealthy),总共有5个Pod(expectedPods)。为了维护这个状态,DisruptionBudget Controller会遍历所有的Budget和所有的Pod。有了Budget的状态,需要改变Pod状态的组件都要先查询之。若其操作导致最小可用数低于应用要求,则操作会被拒绝。

Disruption Budget与QoS联系很紧密。例如,如果一个QoS level很低的应用有着非常严格的Disruption Budget,系统应该如何处理?目前,kubernetes还没有严格的处理这个问题,一个可行的办法是对Disruption Budget做优先级处理,确保高优先级的应用拥有高优先级的Disruption Budget;此外,Disruption Budget可以加入Quota系统,高优先级的应用可以获得更多Disruption Budget Quota。关于Disruption Budget的讨论可以参考:https://github.com/kubernetes/kubernetes/issues/12611

支持更好的集群管理

Cascading Deletion

在kubernetes 1.2之前,删除控制单元都不会删除底层的资源。例如,通过API删除RC之后,其管理的Pod不会被删除(使用kubectl可以删除,但kubectl里面有reaper逻辑,会依次删除底层的所有Pod,本质上是客户端逻辑)。另外一个例子,当删除下图中的Deployment时,ReplicaSet不会被自动删除,当然,Pod也不会被回收。

图片描述

Cascading deletion指的就是在删除控制单元后,将被管理单元也同时回收。但是,kubernetes 1.3中的cascading deletion并不是简单地讲kubectl中的逻辑复制到server端,而是做了更高层次的工作:垃圾回收。简单来讲,garbagecollector controller维护了几乎所有集群资源的列表,并接收资源修改的事件。controller根据事件类型更新资源关系图,并将受影响的资源放入Dirty Queue或者Orphan Queue中。具体实现可以参考官方文档和garbagecollector controller实现:https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/garbage-collection.md
图片描述

Node eviction

Node/kubelet eviction指的是在节点将要超负荷之前,提前将Pod剔除出去的过程,主要是为了内存和磁盘资源。在kubernetes 1.3之前,kubelet不会“提前”感知节点的负荷,只会对已知的问题进行处理。当内存吃紧时,kubernetes依靠内核OOM killer;磁盘方面则定期对image和container进行垃圾回收。但是,这种方式有局限性,OOM killer本身需要消耗一定资源,并且时间上有不确定性;回收容器和镜像不能处理容器写日志的问题:如果应用不断写日志,则会消耗掉所有磁盘,但不会被kubelet处理。

Node eviction通过配置kubelet解决了以上问题。当启动kubelet时,我们通过指定memory.available, nodefs.available, nodefs.inodesFree等参数来确保节点稳定工作。例如,memory.available < 200Mi 表示当内存少于200Mi时,kubelet需要开始移除Pod(可以配置为立即移除或者延迟移除,即hard vs soft)。kubernetes 1.3中,node eviction的特性是opt-in,默认关闭,可以通过配置kubelet打开相关功能。

尽管node eviction是kubelet层面采取的措施,我们也必须考虑与整个集群的交互关系。其中最重要的一点是如何将这个问题反馈给scheduler,不然被剔除的Pod很有可能会被重新调度回来。为此,kubernetes添加了新的node condition:MemoryPressure, DiskPressure。当节点的状态包含其中任意一种时,调度器会避免往该节点调度新的Pod。这里还有另外一个问题,即如果节点的资源使用刚好在阈值附近,那么节点的状态可能会在Pressure和Not Pressure之间抖动。防抖动的方法有很多种,例如平滑滤波,即将历史数据也考虑在内,加权求值。k8s目前采用较为简单的方法:即如果节点处于Pressure状态,为了转变成Not Pressure状态,资源使用情况必须低于阈值一段时间(默认5分钟)。这种方法会导致false alarm,比如,若一个应用每隔一段时间请求一块内存,之后很快释放掉,那么可能会导致节点一直处于Pressure状态。但大多数情况下,该方法能处理抖动的情况。

说到eviction pod,那么另外一个不得不考虑的问题就是找一个倒霉的Pod。这里kubernetes定义了不少规则,总结下来主要是两点:1. 根据QoS来判断,QoS低的应用先考虑;2. 根据使用量判断,使用量与总请求量比例大的Pod优先考虑。具体细节可以参考:https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/kubelet-eviction.md

Network Policy

Network policy的目的是提供Pod之间的隔离,用户可以定义任意Pod之间的通信规则,粒度为端口。例如,下图的规则可以解释成:拥有标签“db”的Pod,只能被拥有标签“frontend”的Pod访问,且只能访问tcp端口6379。

图片描述

Network policy目前处于beta版本,并且只是API。也就是说,kubernetes不会真正实现网络隔离:如果我们将上述Yaml文件提交到kubernetes,并不会有任何反馈,kubernetes只是保存了这个Policy内容。真正实现policy功能需要其他组件,比如calico实现了一个controller,会读取用户创建的Policy来实现隔离,可以参考:https://github.com/projectcalico/k8s-policy/。关于Network Policy的细节,可以参考:https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/network-policy.md

Federation

Federation cluster翻译成中文叫“联合集群”,即将多个kubernetes集群联合成一个整体,并且不改变原始kubernetes集群的工作方式。根据kubernetes官方设计文档,federation的设计目的主要是满足服务高可用,混合云等需求。在1.3版本之前,kubernetes实现了federation-lite,即一个集群中的机器可以来自于相同cloud的不同zone;1.3版本中,federation-full的支持已经是beta版本,即每个集群来自不同的cloud(或相同)。

Federation的核心组件主要是federation-apiserver和federation-controller-manager,以Pod形式运行在其中一个集群中。如下图所示,外部请求直接与Federation Control Panel通信,由Federation分析请求并发送至kubernetes集群。
图片描述

在应用层面,Federation目前支持federated services,即一个应用跨多个集群的访问,具体细节可以参考:http://blog.kubernetes.io/2016/07/cross-cluster-services.html 以及http://kubernetes.io/docs/admin/federation/

结束语

kubernetes 1.3带来了非常多的特性,这里只cover了其中一部分。在安全方面,kubernetes已经支持RBAC,实现更好的权限控制;PodSecurityContext也进入beta版本,支持运行部分需要特权的Pod等。在性能方面,由于protocol buffere serialization的引入,是性能提高了几倍,并且正在酝酿中的etcd3会将性能提升更进一步。相信之后的版本会带给我们更多的惊喜。

作者简介:邓德源,才云 CTO,曾为美国谷歌(Google)集群管理组核心成员(Cluster Management Team)。在谷歌期间作为核心成员参加了开发基于容器集群的谷歌开源项目(Kubernetes)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值