关于云计算:Cloudpods容器化经验分享

Cloudpods是一个开源的多云混合云治理平台。Cloudpods首先是一个公有云云平台,具备将计算节点应用开源QEMU/KVM虚拟化技术虚构出虚拟机,实现公有云的性能。其次,Cloudpods可能纳管其余的云平台,包含支流公有云和私有云,实现云管的性能。Cloudpods的指标是帮忙用户基于本地根底设置以及已有云根底设置,构建一个对立交融的云上之云,达到升高复杂度,进步管理效率的成果。Cloudpods从3.0开始全面拥抱Kubernetes,基于Kubernetes部署运行云平台的服务组件,采纳Kubernetes Operator,基于Kubernetes集群自动化部署服务,实现了云平台的服务的容器化分布式部署。本文总结了Cloudpods在过来3年云平台底层容器化革新的教训。目前,将Kubernetes作为IAAS平台的底层服务治理平台是一个趋势,例如OpenStack的Kolla我的项目,VMware的Tanzu,以及基于Kubernetes的虚拟化计划KubeVirt。Cloudpods适应此趋势,早在2019年下半年开始基于Kubernetes构建Cloudpods的服务组件基础设施。实践上,Cloudpods站在了伟人的肩膀上。有了Kubernetes的加持,咱们基于Operator治理CRD(Custom Resource Definition)机制做到了更优雅的服务自动化部署,合乎IaC(Infrastructure as code)实际的服务降级和回滚,服务的主动高可用部署等等。但在实际效果上,咱们基于Kubernetes,取得了一些便当,但也遇到了不少未曾预料到的问题。本文介绍自从2019年3.0容器化革新以来,因为引入Kubernetes遇到的问题,咱们的一些解决方案,以及未来的布局。

1、容器化带来了哪些益处

1)不便对散布于多个节点上的服务的治理

管理员能够在管制节点对立地查看运行在各个节点的服务状态,查看日志,启停和公布回滚服务,甚至exec进入服务容器排查问题。同时咱们引入Loki收集所有容器的日志,能够对立地查看各个服务的日志。对分布式集群的运维和排障都变得绝对简略。采纳Kubernetes之后,间接登录各个节点排障的机会大大降低了。

2)集群配置变更更不便及可控

整个集群的状态能够保留为一个OnecloudCluster yaml文件。能够不便地变更集群的配置,包含集群的版本,实现版本的降级和回退,以及集群服务的开启和敞开,镜像版本等要害参数的变更等。更进一步地,能够通过git进行配置yaml的版本控制,做到变更的历史记录审计,并且能够随时复原到任意指定的配置。

3)便于适配不同的CPU架构和操作系统

Kubernetes作为一层中间层,从肯定水平上屏蔽了底层的差别。采纳Kubernetes后,对CPU和操作系统的适配大略分为三局部工作:

  1. Kubernetes对CPU和操作系统的适配;
  2. 不同CPU架构下服务容器镜像的构建;
  3. Kubernetes之外的组件的适配,例如平台依赖的rpm包等。基于Kubernetes本身弱小的生态,根本都有现成的解决方案,只须要做相应的集成工作。只需通过docker buildx工具生成异构CPU架构的镜像。因而,整个适配工作复杂度大大降低了。

4)部署的便利性减少

引入Kubernetes之后,整个部署流程分为几个阶段:

Kubernetes的部署,这个步骤通过基于kubeadm革新的ocadm实现。
Cloudpods服务容器的部署,这个步骤通过ocadm在容器内部署operator,通过operator实现相应configmaps,deployments和daemonsets等资源的创立,进而主动创立服务集群。
Kubernetes之外依赖组件的装置部署。这个步骤通过ocboot,集成ansible实现。每个阶段都是基于成熟的开源计划扩大实现,可靠性高。同时,各个组件分工明确,模块化清晰,易于保护和扩大。

5)可复用Kubernetes自身自带的弱小性能

如coredns能够自定义域名,甚至能够做泛域名解析。Ingress自带反向代理的性能。service+deployment提供的多正本冗余机制。daemonset提供的在新增加节点主动拉起服务的能力。对服务的资源限度(CPU,内存,过程号等)。这些都使得云平台服务性能个性的实现变得更加容易。

2、容器化遇到了哪些问题,如何解决

上面总结一些遇到的问题。这些问题是咱们在采纳Kubernetes治理和运行云平台组件中陆续发现的。有些曾经彻底解决,但很大一部分还只是局部解决,彻底解决的计划还在继续摸索中。

1)容器内运行零碎级服务

Cloudpods在计算节点运行的服务都是零碎级的服务,如计算节点的外围服务hostagent,需具备几个特权:

需启动零碎的daemon服务过程,如qemu虚拟机过程,vswitchd等零碎过程,这些过程由hostagent启动,但需独立于hostagent运行;
需拜访计算节点的任意目录文件。
在容器化之前,这些服务由systemd治理,以root身份运行。这些特权都天然具备。容器化后,服务需运行在容器内。尽管能够通过配置给与容器零碎级的root权限,然而一些特权操作在容器内仍然无奈执行。

首先,容器内无奈启动零碎级daemon服务过程。如果通过容器内的程序启动过程,则该过程只能运行在容器内的PID空间(pid namespace),只能追随容器的生命周期启停。为了解决这个问题,咱们将零碎服务的二进制程序安装在计算节点的底层操作系统,并且开发了一个命令执行代理executor-server。该代理装置在底层操作系统,并作为一个零碎服务运行。容器内的hostagent通过该代理执行零碎级命令,例如启动这些daemon服务,设置内核参数等,从而取得了执行零碎级命令的特权。

其次,每个容器具备本人独立的文件系统命名空间(mount namespace)。为了容许容器内服务拜访计算节点底层零碎的特定门路文件,须要将该门路显式地挂载到容器的文件系统命名空间。例如,虚拟机的配置文件和本地磁盘文件都存储在/opt/cloud/workspace目录下。容器内的hostagent在虚拟机筹备和配置阶段须要可能拜访这个目录的文件,同时,启动虚拟机后,在底层操作系统运行的虚拟机qemu过程也须要可能拜访对应的文件。并且,因为上述命令执行代理的机制,为了简化和放弃向后兼容的目标,须要确保尽量以统一的门路在容器内和容器外拜访这些文件。为此,咱们将一些特定的系统目录以同样的门路挂载到hostagent的容器内,例如零碎设施文件门路/dev,云平台的配置文件门路/etc/yunion,虚拟机系统文件门路/opt/cloud/workspace等。然而,这个机制还无奈解决容器内服务拜访底层零碎任意门路的问题。例如,用户能够将底层零碎的任意目录设置为虚拟机磁盘的存储目录,然而该目录其实并未通过容器的spec挂载到hostagent容器内,从而导致hostagent在容器内无法访问该目录。为了解决这个问题,咱们对hostagent进行了革新。当hostagent检测到用户增加了新的本地目录作为虚构磁盘文件的存储门路,会主动地执行底层系统命令,将该门路挂载到底层操作系统的/opt/cloud/workspace目录下。因该目录曾经挂载到hostagent容器内,这样hostagent就能够在容器内拜访这个目录下的文件。

总之,相比将一个一般应用程序容器化,将零碎级的服务程序从systemd托管变为在Kubernetes容器中运行,不是仅仅简略地打一个容器镜像,其实还须要做一系列比较复杂和繁冗的革新工作。

2)日志长久化

容器化之前,服务日志会记录到journald中,并被长久化到/var/log/messages。依照CentOS的默认策略,保留最近一段时间的日志。遇到问题的时候,能够到对应服务器查找到对应的日志,排查谬误起因。然而,不不便的中央是须要登录到服务运行的节点查看日志。在一个事变波及多个节点的时候,就须要同时登录多个节点进行日志排查。

容器化之后,能够不便地在一个中央,通过kubectl log命令查看指定容器的日志,不须要登录到服务运行的节点。

然而,如果没有做非凡设置,K8s里的容器的日志都是不长久保留的,并且只保留以后正在运行的容器的最近一段时间的日志。而容器往往十分动静,很容易删除。这就导致遇到问题须要排查曾经被删除的容器时候,容易遇到找不到对应的日志。这就使得追溯问题变得比拟艰难。

咱们的解决方案是从3.7开始,会默认在k8s集群里部署Loki套件来收集容器的日志,日志最初存在minio的S3 Bucket 外面。这样做可能长久化容器的日志。解决上述问题。然而,保留Loki日志有肯定的零碎负载,并且须要较大容量的存储空间。在集群容量缓和的状况下成为平台的额外负担,可能造成平台的不稳固。

3)节点Eviction机制

Kubernetes有驱赶机制(Evict)。当节点的资源余量有余时,例如磁盘残余空间低于阈值或残余内存低于阈值(默认根分区磁盘空间低于85%,闲暇内存低于500M)等,会触发Kubernetes的节点驱赶机制,将该节点设置为不可调度,下面的所有容器都设置为Evict状态,进行运行。

该机制对于无状态利用能够动静地躲避有问题的节点,是一个好的个性。然而,在云平台的场景中,甚至对于广泛的有状态服务场景中,Eviction机制导致节点可用性变得十分动静,进而升高了整体的稳定性。例如,因为用户上传一个大的镜像,导致管制节点根分区利用率超过Eviction的阈值85%,云平台的所有管制服务就会被立刻驱除,导致云平台管制立体齐全不可用。用户在虚拟机磁盘写入大量数据导致宿主机磁盘空间利用率超过阈值,也会引起计算节点上所有服务被驱赶,进而导致这台计算节点上所有的虚拟机失联,无法控制。能够看到,尽管触发Eviction机制的问题存在造成服务问题的可能,然而这些问题对服务的影响是延后的,逐渐失效的。Eviction机制则使得这些潜在危险对服务的影响提前了,并立刻产生,起到了放大的作用。

为了防止Eviction机制失效,云平台在计算节点的agent启动的时候,会自动检测该节点的Eviction阈值,并设置为计算节点的资源申请下限。云平台在调度主机的时候,会思考到Eviction的阈值,防止资源分配触发Eviction。这个机制能从肯定水平躲避Eviction的呈现,但云平台只能治理由云平台调配的资源,还是存不在云平台治理范畴内的存储和内存调配导致Eviction的状况。因而须要计算节点肯定水平的内存和存储的over-provisioning。

目前,Eviction的存在也有肯定的踊跃作用,那就是让节点资源的不足以云平台罢工的形式提出警示。因为云平台的冗余设计,云平台的临时罢工并不会影响虚拟机的运行,因而影响水平还比拟可控。无论如何,以云平台可用性的就义来达到资源有余的警示,代价还是有点大。这样的警示能够其余更柔和的形式来实现。随着云平台本身治理资源容量能力的欠缺,Eviction机制应该能够去除。

4)容器内过程泄露

Cloudpods服务次要为go开发的应用程序,容器镜像采纳alpine根底镜像最小化构建,仅蕴含服务的二进制和alpine根底镜像,服务过程作为容器的启动过程(1号过程)运行。咱们的服务程序没有为作为1号过程做专门的优化,因而不具备systemd/init等失常操作系统1号过程具备的过程治理能力,例如解决孤儿过程,回收zombie过程等。然而,一些服务存在fork子过程的场景,例如kubeserver调用服务的时候会fork ssh执行近程命令,cloudmon则会执行采集监控数据的子过程。当这些子过程遇到异样退出时,因为咱们的服务过程不具备被动回收子过程的性能,导致系统里积压了了大量退出异样未回收的子过程,导致过程泄露。这些子过程占用操作系统过程号,当达到零碎最大过程数时,会呈现零碎CPU和内存十分闲暇,然而无奈进一步fork新的过程的状况,导致系统服务异样。

为了防止容器内过程泄露问题,咱们在Cloudpods服务框架里退出了回收子过程的逻辑,并且增加到每个服务过程中,这样在子过程异样退出后,咱们的服务过程会回收子过程资源,从而防止了这个问题。同时也配置了kubelet的 最大过程数的限度参数,限度一个pod外面最多能有1024个过程。

5)高可用不肯定高可用

咱们基于Kubernetes实现了管制节点的3节点高可用,基本思路是应用3个节点部署高可用的Kubernetes的管制服务,包含apiserver, scheduler, controller, etcd等。Kubernetes服务通过VIP拜访。采纳keepalived实现VIP在三个管制节点上的主动漂移。这此高可用Kubernetes集群之上,部署云平台管制服务,实现云平台管制立体的高可用。预期成果是将3个管制节点中的任意节点宕机后,次要服务不受影响,如果有影响,需可能在短时间内主动复原。

然而,初期测试发现采纳默认参数部署的 Kubernetes 高可用主动复原的工夫高达15分钟,不合乎预期。通过调研发现,能够通过给各个组件设置相干的参数来缩小复原工夫(https://github.com/yunionio/o…)。通过参数调整,能够让Kubernetes集群高可用切换工夫缩短到1分钟以内。

6)服务的启动程序

Kubernetes无奈指定pod启动的程序,同时也要求部署在K8s里的服务不要对其余服务的启动先后顺序有依赖。云平台服务在采纳Kubernetes部署治理之前是采纳systemd治理,systemd能够明确定义服务之间的启动程序。这导致服务之间有比拟显著的先后秩序依赖。比方,keystone服务就要求最先启动,其余所有服务都依赖keystone服务提供初始化服务账号的认证。容器化革新后,因为这个依赖,导致在keystone容器启动之前的服务无奈失常运行。定位到该问题后,咱们将服务因为启动程序导致的谬误降级为致命谬误。这样,该服务程序遇到依赖服务未启动导致的问题就异样退出。进而,通过Kubernetes主动重启拉起服务过程。通过这样的革新打消了其余服务对keystone的启动程序依赖。然而,咱们无奈找到无效伎俩辨认出所有依赖启动程序而呈现的谬误,因而这样的服务无程序革新还在继续。

7) 证书生效问题

Kubernetes集群节点之间的互相认证和通信依赖PKI秘钥体系。如果节点的PKI证书过期,则该节点kubelet无奈失常和ApiServer通信,进而导致节点状态被设置为NotReady,进而呈现前述的容器驱赶导致节点不可用的重大问题。刚开始,咱们部署的k8s集群还是采纳kubeadm默认的1年有效期的证书,过后还未顾及到证书到期的问题。到2020年底,开始陆续呈现多个集群莫名服务不可用的状况,才留神到证书过期的问题。针对这个问题,咱们刚开始采纳cronjob装置自动更新证书脚本的计划,并且在客户巡检中,专门查看证书过期问题,以提前发现问题。起初到了2021年3.8版本,采纳了更糙快猛的办法,间接批改了kubeadm的证书签发代码,一次性签发99年证书,从而彻底解决了Kubernetes的证书过期问题。

8) iptables批改

Kubernetes部署后,kubelet、kube-proxy以及咱们采纳的calico等都依赖iptables,会接管节点的iptables规定,在kubelet启动之后,对iptables规定的批改会被重置,并且会刷新iptables规定。如何长久化对iptables规定的批改成为问题。目前,针对节点的防火墙规定能够采纳calico的网络策略来实现,可参考文章https://www.cloudpods.org/zh/…。但更简单iptables规定,还没找到无效方法。

3、将来布局

1)降级Kubernetes版本

目前,云平台底座kubernetes的版本是1.15.12,该版本曾经不再被Kubernetes官网反对。目前存在的比拟显著的问题是和较新的采纳cgroup v2的操作系统不兼容,导致无奈设置服务的资源limit。后续思考降级底层Kubernetes到更新版本,以期取得更新的性能个性反对。

2)采纳K3S等更轻量版本

目前云平台依赖底层Kubernetes的性能个性不多,同时Kubernetes自身也要耗费肯定的节点资源,前面也打算思考采纳k3s等更轻量的Kubernetes版本,进一步升高Kubernetes的应用老本。

3)移除计算节点对iptables的依赖

计算节点网络次要依赖openvswitch实现虚拟机的通信,iptables次要是给kubelet,kube-proxy和cailico-node等k8s服务组件应用,而计算节点上的服务组件次要是用来治理QEMU/KVM虚拟机的host-agent等服务,这些服务自身具备基于ovs的网络管理能,不依赖k8s的网络,齐全能够只依赖host网络即可失常工作。因而,其实能够去掉计算节点的kubeproxy,calico等组件,去除对iptables的批改,这样简化组件依赖,进一步提高零碎的可靠性。

4)齐全禁用Eviction机制

Eviction机制在虚拟化云平台或有状态服务场景中,会起到故障放大的作用。在充沛掌控对节点资源耗尽预警的前提下,应思考彻底禁用Eviction机制。

5) 多数据中心架构的反对

目前云平台所有节点都运行在一个Kubernetes集群内。而云平台自身是能够反对多数据中心部署的。然而跨数据中心部署单个kubernetes集群不是最佳实际。比拟现实的架构是单个kubernetes集群部署在一个数据中心内。因而,应该容许云平台跨多个K8s集群部署。例如每个数据中心一个Kubernetes集群,其中一个集群部署残缺的云平台,其余Kubernetes集群以从可用区的角色退出主集群。每个Kubernetes集群之上只运行治理一个数据中心所需的云平台组件。进而形成一个多数据中心的云平台架构。

GitHub:https://github.com/yunionio/c…

查看原文

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据