资讯专栏INFORMATION COLUMN

kubernetes 1.3 current and future

entner / 3228人阅读

摘要:执行该初始化任务的容器被成为初始化容器。目前,有等状态。网络身份的维护主要通过稳定的和来维护,他们通过的配置文件指定。若其操作导致最小可用数低于应用要求,则操作会被拒绝。

本文讨论 K8S 1.3 的一些新功能,以及正在进行中的功能。读者应该对 kubernetes 的基本结构已经有所了解。

支持更多类型的应用

1、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 内加上 init Containers 字段,如下所示:

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 的更新等等,其中不少都是有待解决的问题,这里就不一一展开了。

2、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 大多数的案例。但是,目前还存在很多需要完善的地方,感兴趣的读者可以参考:链接

3、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 的更新可以参考:链接

4、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 内有一个新的组件 Disruption Budget Controller,来负责维护所有 Budget 的状态,例如上图中的 status 表明当前共有 4 个健康的 Pod(currentHealthy),应用要求至少有 3 个(desiredHealthy),总共有 5 个Pod(expectedPods)。为了维护这个状态,Disruption Budget 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 的讨论可以参考:链接

支持更好的集群管理

1、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 中。具体实现可以参考官方文档和 garbage collector controller 实现:链接

2、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 优先考虑。具体细节可以参考:链接

3、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 来实现隔离,可以参考:链接。关于 Network Policy 的细节,可以参考:链接

4、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,即一个应用跨多个集群的访问,具体细节可以参考:链接以及链接

结束语

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/32482.html

相关文章

  • kubernetes1.1.1中的自动扩容特性

    摘要:它实现了中的横向自动扩容。目前只支持针对使用度进行动态扩容。这个就是真正控制扩容的,其结构如下其中的是一个的引用,定义了自动扩容的配置允许的最大实例数,最小实例数,以及使用配额。这也是为了避免出现频繁的扩容缩容。 最新一版的kubernetes release中我们看到了一个令人欣喜的特性:Autoscaling。它实现了replicationcontroller中pod的横向自动扩容...

    Drummor 评论0 收藏0
  • tornado 源码阅读-初步认识

    摘要:序言最近闲暇无事阅读了一下的源码对整体的结构有了初步认识与大家分享不知道为什么右边的目录一直出不来非常不舒服不如移步到吧是的核心模块也是个调度模块各种异步事件都是由他调度的所以必须弄清他的执行逻辑源码分析而的核心部分则是这个循环内部的逻辑贴 序言 最近闲暇无事,阅读了一下tornado的源码,对整体的结构有了初步认识,与大家分享 不知道为什么右边的目录一直出不来,非常不舒服. 不如移...

    2450184176 评论0 收藏0
  • 跟我学 K8S--代码: Kubernetes StatefulSet 代码分析与Unknown 状

    摘要:节点对不会有影响,查询处于状态并一直保持。根据上一节描述,此时已经有正确的在其他节点,此时故障节点恢复后,执行优雅删除,删除旧的。会从状态变为状态,执行优雅删除,,然后执行重新调度与重建操作。会从状态直接变成状态,不涉及重建。 节点离线后的 pod 状态 在 kubernetes 使用过程中,根据集群的配置不同,往往会因为如下情况的一种或几种导致节点 NotReady: kubele...

    tolerious 评论0 收藏0
  • tornado 源码分析 之 异步io的实现方式

    摘要:前言本文将尝试详细的带大家一步步走完一个异步操作从而了解是如何实现异步的其实本文是对上一篇文的实践和复习主旨在于关注异步的实现所以会忽略掉代码中的一些异常处理文字较多凑合下吧接下来只会贴出部分源码帮助理解希望有耐心的同学打开源码一起跟踪一遍 前言 本文将尝试详细的带大家一步步走完一个异步操作,从而了解tornado是如何实现异步io的. 其实本文是对[上一篇文][1]的实践和复习 主...

    xiangzhihong 评论0 收藏0
  • 多线程中的局部变量

    摘要:在实现多线程业务时,如果没有涉及到共享数据处理的业务,还是使用局部变量,必将,在处理共享数据时,还是需要加锁线程与线程间的局部变量相互独立,变量的处理互补干扰。 在实现多线程业务时,如果没有涉及到共享数据处理的业务,还是使用局部变量,必将,在处理共享数据时,还是需要加锁;线程与线程间的局部变量相互独立,变量的处理互补干扰。 在多线程的场景下,针对线程中的局部变量,如果需要让其他业务操作...

    bitkylin 评论0 收藏0

发表评论

0条评论

entner

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<