Kubernetes已经成为企业新一代云IT架构的重要基础设施,但是在企业部署和运维Kubernetes集群的过程中,依然充满了复杂性和困扰。阿里云容器服务自从2015年上线后,一路伴随客户和社区的成长,目前托管着上万的K8s集群来支撑全球各地的客户。我们对客户在规划集群过程中经常会遇见的问题,进行一些分析解答。试图缓解大家的“选择恐惧症”。
如何选择Worker节点实例规格?
裸金属还是虚拟机?
在Dimanti 2019年的容器调查报告中,对专有云用户选择裸金属服务器来运行容器的主要原因进行了分析。
- 选择裸金属服务器的最主要原因(超过55%)是:传统虚拟化技术I/O损耗较大;对于I/O密集型应用,裸金属相比传统虚拟机有更好的性能表现
- 此外近36%的客户认为:裸金属服务器可以降低成本。大多数企业在初始阶段采用将容器运行在虚拟机的方案,但是当大规模生产部署的时候,客户希望直接运行在裸金属服务器上来减少虚拟化技术的license成本(这也常被戏称为“VMWare税”)。
- 还有近30%的客户因为在物理机上部署有更少的额外资源开销(如虚拟化管理、虚拟机操作系统等);还有近24%的客户选择的原因是:可以有更高的部署密度,从而降低基础设施成本。
- 超过28%的客户认为,在物理机上可以更加灵活地选择网络、存储等设备和软件应用生态。
在公共云上,我们应该如何选择呢?
2017年10月,阿里云“神龙架构”横空出世。弹性裸金属服务器(ECS Bare Metal Instance)是一款同时兼具虚拟机弹性和物理机性能及特性的新型计算类产品,实现超强超稳的计算能力,无任何虚拟化开销。阿里云2019年8月重磅发布了弹性计算第六代企业级实例,基于神龙架构对虚拟化能力进行了全面升级
基于阿里自研神龙芯片和全新的轻量化Hypervisor - 极大减少虚拟化性能开销
- 基于阿里云智能神龙芯片和全新的轻量化VMM,将大量传统虚拟化功能卸载到专用硬件上,大大降低了虚拟化的性能开销,同时用户几乎可以获得所有的宿主机CPU和内存资源,提高整机和大规格实例的各项能力,尤其是I/O性能有了大幅度提升。
使用最新第二代英特尔至强可扩展处理器 - E2E性能提升
- 使用最新一代Intel Cascade Lake CPU, 突发主频提升至3.2GHz, 各场景E2E性能大幅提升,并在深度学习的多种场景有数倍的提升。
给企业级场景带来稳定和可预期的表现 - 全球最高水准SLA
- 针对软硬件优化以及更加实施更细致的QoS手段,给企业级客户的负载提供更稳定可预期的性能。
ECS实例类型 | 第六代裸金属实例 | 六代虚拟机实例 |
---|---|---|
CPU | 目前包含80/104两种规格 | 2~104 |
CPU与内存配比 | 1:1.8 ~ 1:7.4 | 1:2 ~ 1:8 |
虚拟化资源开销 | 没有 | 极小 |
GPU支持 | 是 (部分型号) | 是 (部分型号) |
支持Intel SGX | 是 (部分型号) | 否 |
支持安全沙箱容器(如KataContainer, 阿里云安全沙箱容器等) | 是 | 否 |
支持License绑定硬件 | 是 | 是 (DDH支持) |
支持热迁移 | 否 (支持宕机迁移) | 是 |
一般而言建议:
- 对性能极其敏感的应用,如高性能计算,裸金属实例是较好的选择。
- 如果需要Intel SGX,或者安全沙箱等技术,裸金属实例是不二选择。
- 六代虚拟机实例基于神龙架构,I/O性能有了显著提升,同时有更加丰富的规格配置,可以针对自身应用需求灵活选择,降低资源成本。
- 虚拟机实例支持热迁移,可以有效降低运维成本。
阿里云ACK K8s集群支持多个节点伸缩组(AutoScalingGroup),不同弹性伸缩组支持不同的实例规格。在工作实践,我们会为K8s集群划分静态资源池和弹性资源池。通常而言,固定资源池可以根据需要选择裸金属或者虚拟机实例。弹性资源池建议根据应用负载使用合适规格的虚拟机实例来优化成本、避免浪费,提升弹性供给保障。
此外由于裸金属实例一般CPU核数非常多,大规格实例在使用中的挑战请参见下文。
较少的大规格实例还是较多的小规格实例?
一个引申的问题是,如何选择实例规格?我们列表对比一下
较少的大规格实例 | 较多的小规格实例 | 备注 | |
---|---|---|---|
节点管理开销 | 较低 | 较高 | |
操作系统额外开销 | 较低 | 较高 | |
节点部署密度 | 较高 | 较低 | |
节点调度复杂性(如NUMA) | 较高 | 较低 | 部署密度增加之后,需要更加合理的资源调度来保障应用SLA |
节点稳定性 | 较低 | 较高 | 随着部署密度增加,节点自身的稳定性也会随之下降 |
节点失效爆炸半径 | 较大 | 较小 | 一个大规格实例失效,会影响更多的应用容器。也需要预留更多的资源进行宕机迁移。 |
Master组件压力 | 较小 | 较大 | Worker节点数量是影响Master节点容量规划和稳定性的因素之一。K8s 1.13版本引入的NodeLease功能让节点数量对master组件的压力降低很多。 |
默认情况下,kubelet 使用 CFS 配额 来执行 pod 的 CPU 约束。当节点上运行了很多 CPU 密集的应用时,工作负载可能会迁移到不同的 CPU 核,工作负载的会受到 CPU 缓存亲和性以及调度延迟的影响。当使用大规格实例类型时,节点的CPU数量较多,现有的Java,Golang等应用在多CPU共享的场景,性能会出现明显下降。所有对于大规格实例,需要对CPU管理策略进行配置,利用CPU set进行资源分配。
此外一个重要的考虑因素就是NUMA支持。在NUMA开启的裸金属实例或者大规格实例上,如果处理不当,内存访问吞吐可能会比优化方式降低了30%。Topology管理器可以开启NUMA感知 https://kubernetes.io/docs/tasks/administer-cluster/topology-manager/ 。但是目前K8s对NUMA的支持比较简单,还无法充分发挥NUMA的性能。
阿里云容器服务提供了 CGroup Controller 可以更加灵活地对NUMA架构进行调度和重调度。
如何容器运行时?
Docker容器还是安全沙箱?
在 Sysdig 发布的 2019 容器使用报告中,我们可以看到Docker容器占据市场规模最大的容器运行时 (79%),containerd 是 Docker贡献给CNCF社区的开源容器运行时,现在也占据了一席之地,并且得到了厂商的广泛支持;cri-o 是红帽公司推出的支持OCI规范的面向K8s的轻量容器运行时,目前还处在初级阶段。
很多同学都关心containerd与Docker的关系,以及是否containerd可以取代Docker?Docker Engine底层的容器生命周期管理也是基于containerd实现。但是Docker Engine包含了更多的开发者工具链,比如镜像构建。也包含了Docker自己的日志、存储、网络、Swarm编排等能力。此外,绝大多数容器生态厂商,如安全、监控、日志、开发等对Docker Engine的支持比较完善,对containerd的支持也在逐渐补齐。所以在Kubernetes运行时环境,对安全和效率和定制化更加关注的用户可以选择containerd作为容器运行时环境。对于大多数开发者,继续使用Docker Engine作为容器运行时也是一个不错的选择。
此外,传统的Docker RunC容器与宿主机Linux共享内核,通过CGroup和namespace实现资源隔离。但是由于操作系统内核的攻击面比较大,一旦恶意容器利用内核漏洞,可以影响整个宿主机上所有的容器。
越来越多企业客户关注容器的安全性,为了提升安全隔离,阿里云和蚂蚁金服团队合作,引入安全沙箱容器技术。19年9月份我们发布了基于轻量虚拟化技术的RunV安全沙箱。相比于RunC容器,每个RunV容器具有独立内核,即使容器所属内核被攻破,也不会影响其他容器,非常适合运行来自第三方不可信应用或者在多租户场景下进行更好的安全隔离。
阿里云安全沙箱容器有大量性能优化,可以达到90%的原生RunC性能
- 利用Terway CNI网络插件,网络性能无损。
- 利用DeviceMapper构建了⾼速、稳定的容器 Graph Driver
- 优化 FlexVolume 和 CSI Plugin,把 mount bind 的动作下沉到沙箱容器内,从而避开了 9pfs 带来的性能损耗。
而且,ACK为安全沙箱容器和和RunC容器提供了完全一致的用户体验,包括日志、监控、弹性等。同时,ACK可以在一台神龙裸金属实例上同时混布RunC和RunV容器,用户可以根据自己的业务特性自主选择。
同时,我们也要看到安全沙箱容器还有一些局限性,现有很多日志、监控、安全等工具对独立内核的安全沙箱支持不好,需要作为sidecar部署在安全沙箱内部。
对于用户而言,如果需要多租户隔离的场景,可以采用安全沙箱配合network policy来完成,当然也可以让不同租户的应用运行在不同的虚拟机或者弹性容器实例上,利用虚拟化技术来进行隔离。
注意:安全沙箱目前只能运行在裸金属实例上,当容器应用需要资源较少时成本比较高。可以参考下文的Serverless K8s有关内容。
如何规划容器集群?
一个大集群还是一组小集群?
在生产实践中,大家经常问到的一个问题是我们公司应该选择一个还是多个Kubernetes集群。
Rob Hirschfeld在Twitter上做了一个调查,
- 一个大一统的的平台,支持多种应用负载、环境和多租户隔离
- 或者,一组以应用为中心的小集群,支持不同应用和环境的生命周期管理。
https://thenewstack.io/the-optimal-kubernetes-cluster-size-lets-look-at-the-data/
大多数的用户选择是后者,典型的场景是
- 开发、测试环境使用不同的集群
- 不同的部门使用不同的集群进行隔离
- 不同的应用使用不同的集群
- 不同版本的K8s集群
在用户反馈中,采用以多个小集群的主要原因在于爆炸半径比较小,可以有效提升系统的可用性。同时通过集群也可以比较好地进行资源隔离。管理、运维复杂性的增加是采用多个小集群的一个不足之处,但是在公共云上利用托管的K8s服务(比如阿里云的ACK)创建和管理K8s集群生命周期非常简单,可以有效解决这个问题。
我们可以比较一下这两种选择
单一大集群 | 多个应用为中心集群 | |
---|---|---|
爆炸半径 | 大 | 小 |
硬多租(安全、资源强隔离) | 复杂 | 简单 |
混合调度多种类型资源 (GPU等 ) | 复杂 | 简单 |
集群管理复杂性 | 较低 | 较高(自建)/较低(采用托管K8s服务) |
集群生命周期的灵活性 | 困难 | 简单(不同集群可以有不同的版本和伸缩策略) |
规模化引入的复杂性 | 复杂 | 简单 |
源自Google Borg的理念,Kubernetes的愿景是成为Data Center Operating System,而且Kubernetes也提供了RBAC、namespace等管理能力,支持多用户共享一个集群,并实现资源限制。 但是这些更多是 “软多租” 能力,不能实现不同租户之间的强隔离。在多租最佳实践中,我们可以有如下的一些建议
- 数据平面:可以通过PSP (PodSecurityPolicy) 或者安全沙箱容器,提升容器的隔离性;利用 Network Policy 提升应用之间网络隔离性;可以通过将nodes和namespace绑定在一起,来提升namespace之间资源的隔离。
- 控制平面:Kubernetes的控制平面包括master组件API Server, Scheduler, etcd等,系统addon如CoreDNS, Ingress Controller等,以及用户的扩展,如3方的CRD (Customer Resource Definition) controller。这些组件大多不具备良好的租户之间的安全、资源和故障隔离能力。一个错误的CRD contoller实现有可能打挂一个集群的API Server。
关于Kubernetes多租户实践的具体信息可以参考下文。目前而言,Kubernetes对硬隔离的支持存在很多局限性,同时社区也在积极探索一些方向,如阿里容器团队的Virtual Cluster Proposal 可以提升隔离的支持,但是这些技术还未成熟。
另一个需要考虑的方案是Kubernetes自身的可扩展性,我们知道一个Kubernetes集群的规模在保障稳定性的前提下受限于多个维度,一般而言Kubernetes集群小于5000节点。当然,运行在阿里云上还受限于云产品的quota限制。阿里经济体在Kubernetes规模化上有丰富的经验,但是对于绝大多数客户而言,是无法解决超大集群的运维和定制化复杂性的。
对于公共云客户我们一般建议,针对业务场景建议选择合适的集群规模
- 对于跨地域(region)场景,采用多集群策略,K8s集群不应跨地域。我们可以利用CEN将不同地域的VPC打通。
- 对于强隔离场景,采用多集群策略,不同安全域的应用部署在不同的集群上。
- 对于应用隔离场景,比如SaaS化应用,可以采用单集群方式支持多租,并加强安全隔离。
- 对于多个大规模应用,可以采用多集群策略,比如,在线应用、AI训练、实时计算等可以运行在不同的K8s集群之上,一方面可以控制集群规模,一方面可以针对应用负载选择合适的节点规格和调度策略。
-
- 由于有VPC中节点、网络资源的限制,我们可以甚至将不同的K8s集群分别部署在不同的VPC,利用CEN实现网络打通,这部分需要对网络进行前期规划。
如果需要对多个集群的应用进行统一管理,有如下几个考虑
- 利用Kubefed 构建集群联邦,ACK对集群联邦的支持可以参考 https://help.aliyun.com/document_detail/121653.html
- 利用统一的配置管理中心,比如GitOps方式来管理和运维应用 https://github.com/fluxcd/flux
另外利用托管服务网格服务ASM,可以利用Istio服务网格轻松实现对多个K8s集群的应用的统一路由管理。
如何选择K8s或者Serverless K8s集群?
在所有的调查中,K8s的复杂性和缺乏相应的技能是阻碍K8s企业所采用的主要问题,在IDC中运维一个Kubernetes生产集群还是非常具有挑战的任务。阿里云的Kubernetes服务ACK简化了K8s集群的生命周期管理,托管了集群的master节点被,但是用户依然要维护worker节点,比如进行升级安全补丁等,并根据自己的使用情况进行容量规划。
针对K8s的运维复杂性挑战,阿里云推出了Serverless Kubernetes容器服务 ASK。
完全兼容现有K8s容器应用,但是所有容器基础设施被阿里云托管,用户可以专注于自己的应用。它具备几个特点
- 首先它按照容器应用实际消耗的资源付费,而不是按照预留的节点资源付费
- 对用户而言没有节点的概念,零维护
- 所有资源按需创建,无需任何容量规划。
在ASK中,应用运行在弹性容器实例 - ECI (Elastic Container Instance)中,ECI基于轻量虚拟机提供的沙箱环境实现应用安全隔离,并且完全兼容Kubernetes Pod语义。在ASK中我们通过对Kubernetes做减法,将复杂性下沉到基础设施,极大降低了运维管理负担,提升用户体验,让Kubernetes更简单,让开发者更加专注于应用自身。除了无需管理节点和Master外,我们将DNS, Ingress等能力通过阿里云产品的原生能力来实现,提供了极简但功能完备的Kubernetes应用运行环境。
Serverless Kubernetes极大降低了管理复杂性,而且其自身设计非常适合突发类应用负载,如CI/CD,批量计算等等。比如一个典型的在线教育客户,根据教学需要按需部署教学应用,课程结束自动释放资源,整体计算成本只有使用包月节点的 1/3。
在编排调度层,我们借助了CNCF社区的Virtual-Kubelet,并对其进行了深度扩展。Virtual-Kubelet提供了一个抽象的控制器模型来模拟一个虚拟Kubernetes节点。当一个Pod被调度到虚拟节点上,控制器会利用ECI服务来创建一个ECI实例来运行Pod。
我们还可以将虚拟节点加入ACK K8s集群,允许用户灵活控制应用部署在普通节点上,还是虚拟节点上。
值得注意的是 ASK/ECI 是 nodeless 形态的pod,在K8s中有很多能力和node相关,比如NodePort等概念不支持,此外类似日志、监控组件经常以DaemonSet的方式在K8s节点上部署,在ASK/ECI中需要将其转化为Sidecar。
用户该如何选择ACK和ASK呢?ACK主要面向的是基础设施运维团队,具备和K8s高度的兼容性和灵活性控制性。而ASK则是面向业务技术团队或者开发者,用户完全不需具备K8s的管理运维能力,即可管理和部署K8s应用。而ACK on ECI,则同时支持用户负载运行在ECS实例或者ECI实例上,可以允许用户进行灵活控制。
ACK on ECI/ASK则可以将弹性的负载通过ECI的方式运行,有几个非常典型的场景:
- 应对突发流量:ECI基于面向容器的轻量安全沙箱,可以实现30s 500Pod的弹性伸缩能力,可以轻松应对突发的业务流量,在面对不确定的业务流量时,可以简化弹性配置。
- 批量数据处理:我们可以实现Serverless Spark/Presto这样的计算任务, 按需为计算任务分配计算资源。
- 安全隔离:有些业务应用需要运行3方不可信应用,比如一个AI算法模型,ECI本身利用安全沙箱进行隔离,我们可以利用ECI隔离运行相关应用。
ACK on ECI还可以和Knative这样的Serverless应用框架配合,开发领域相关的Serverless应用。
总结
合理规划K8s集群可以有效规避后续运维实践中的稳定性问题,降低使用成本。期待与大家一起交流阿里云上使用Kubernetes的实践经验。