大约两年前,我们决定放弃基于Ansible的配置管理设置,以便在EC2上部署应用程序,并转向使用Kubernetes进行容器化和编排应用程序。我们已将大部分基础架构迁移到Kubernetes。这是一项艰巨的任务,也有其自身的挑战-从运行混合基础架构的技术挑战到完成大部分迁移,再到以全新的运营模式培训整个团队,仅举几例。
在这篇文章中,我们想反思我们的经验,并与您分享我们在这次旅程中的经验,以帮助您做出更好的决定并增加成功的机会。
明确说明您迁移到Kubernetes的原因
所有那些无服务器和容器的东西都很好。如果您要开始一项新业务并从头开始构建所有内容,请务必使用容器来部署应用程序,并在拥有带宽(或可能不具备)的情况下使用Kubernetes来编排应用程序,并且拥有配置和配置技术的技能。操作Kubernetes以及在Kubernetes上部署应用程序。
即使您将Kubernetes的操作卸载到诸如EKS,GKE或AKS之类的Kubernetes托管服务上,在Kubernetes上正确部署和操作应用程序也需要学习。您的开发团队应该应对挑战。只有您的团队遵循DevOps理念,才能实现很多好处。如果您有*sysadmin团队为其他团队开发的应用程序编写清单,那么从DevOps的角度来看,我们个人认为Kubernetes的好处较小。当然,您可以选择Kubernetes带来许多其他好处,例如成本,更快的实验,更快的自动缩放,弹性等。
如果您已经在云VM或其他PaaS上进行部署,那么为什么真正考虑从现有基础架构迁移到Kubernetes?您是否相信Kubernetes是解决问题的唯一方法?您必须清楚自己的动机,因为将现有基础架构迁移到Kubernetes是一项艰巨的任务。
我们在这方面犯了一些错误。我们迁移到Kubernetes的主要原因是建立一个持续集成的基础架构,该基础架构可以帮助我们快速重新构建微服务,而这些年来,这些微服务一直困扰着许多架构。大多数新功能都需要涉及多个代码库,因此,一起开发和测试所有这些功能会使我们慢下来。我们认为有必要为每个开发人员和每个变更提供一个集成的环境,以帮助加快开发和测试周期,而无需协调谁来获得“共享阶段环境”。
> One of our continuous integration pipelines that provisions a new integrated environment with all the microservices and runs automated tests
我们今天做得很好。我们今天在8分钟内在Kubernetes上的集成环境中提供了21种微服务。任何开发人员都可以使用我们自己开发的工具来执行此操作。对于为这21个微服务中的任何一个创建的每个拉取请求,我们还提供了该环境的子集。整个测试周期(提供环境和运行测试)需要不到12分钟的时间。可能感觉很长,但它阻止了我们在当前所处的体系结构混乱中进行重大更改。
> Continuous Integration pipeline execution report
赞!建立所有这些需要什么?我们花了将近1.5年的时间。那值得吗?
通过构建其他工具,遥测并重新部署每个应用程序的方式,我们花了将近1.5年的时间来稳定这种复杂的CI设置。为了实现开发/产品平价,我们也必须将所有这些微服务都部署到生产中,否则,基础架构和部署设置之间的偏差将使应用程序难以为开发人员辩护,并且本应为开发人员做出选择一个噩梦。
我们对这个话题有不同的看法。回顾过去,我们认为解决连续集成的问题变得更加糟糕,因为将所有微服务推向生产以实现开发/产品平价的复杂性使得实现更快的CI的挑战变得更加复杂和困难。在使用Kubernetes之前,我们将Ansible与Hashicorp Consul和Vault一起用于基础架构供应,配置管理和部署。慢吗?是的,一点没错。但是我们认为我们可以通过Consul引入服务发现并优化Ansible部署,从而在合理的较短时间内就足够接近我们的目标。
我们应该迁移到Kubernetes吗?是的,一点没错。使用Kubernetes有很多好处-服务发现,更好的成本管理,弹性,治理,对云基础架构基础架构的抽象等等。今天,我们也收获了所有这些好处。但这并不是我们开始时的主要目标,也许没有必要施加自己的压力和痛苦来实现自己的方式。
对我们来说,一大收获是,我们本可以采用另一种且抗性较小的方式来采用Kubernetes。我们只是被Kubernetes收购,这是我们甚至不在乎评估其他选项的唯一解决方案。
我们将在此博客文章中看到,在Kubernetes上进行迁移和操作与在云VM或裸机上进行部署不同。您的云工程和开发团队有一条学习曲线。对于您的团队来说,值得一试。但是,现在您需要做的是这个问题。您必须尝试清楚地回答。
开箱即用的Kubernetes几乎对任何人来说都是远远不够的
许多人对Kubernetes作为PaaS解决方案感到困惑,这不是PaaS解决方案。它是构建PaaS解决方案的平台。OpenShift就是这样一个例子。
开箱即用的Kubernetes对于几乎任何人来说都是远远不够的。这是一个学习和探索的绝佳游乐场。但是您很可能需要在顶部放置更多基础结构组件,并将它们很好地结合在一起,作为应用程序的解决方案,以使其对您的开发人员更有意义。通常,这种带有附加基础设施组件和策略的Kubernetes捆绑包称为内部Kubernetes平台。这是一个非常有用的范例,并且有几种扩展Kubernetes的方法。
度量标准,日志,服务发现,分布式跟踪,配置和秘密管理,CI / CD,本地开发经验,对自定义度量标准的自动扩展都是需要照顾和做出决定的事情。这些只是我们要呼吁的一些事情。肯定会有更多的决策和更多的基础架构要建立。一个重要的方面是您的开发人员将如何使用Kubernetes的资源和清单-在本博客文章的后面将对此进行更多介绍。
这是我们的一些决定和理由。
指标
我们最后确定了Prometheus。Prometheus几乎是事实上的度量标准基础结构。CNCF和Kubernetes非常喜欢它。它在Grafana生态系统中非常有效。而且我们喜欢Grafana!我们唯一的问题是我们正在使用InfluxDB。我们已决定从InfluxDB迁移并完全致力于Prometheus。
日志
日志一直是我们的大难题。我们一直在努力使用ELK创建一个稳定的日志记录平台。我们发现ELK具有我们团队无法实际使用的功能。这些功能需要付出一定的代价。另外,我们认为将Elasticsearch用于日志存在固有的挑战,使其成为昂贵的日志解决方案。我们由Grafana最终确定了Loki。这很简单。它具有满足我们团队需求的必要功能。极具成本效益。但最重要的是,由于它的查询语言与PromQL非常相似,因此它具有出色的UX。此外,它与Grafana搭配使用也很好。这样一来,就可以将整个指标监视和日志记录体验集中到一个用户界面中。
An example of a Grafana dashboard with visualization over metrics and corresponding logs side-by-sid
配置和密钥
您会发现大多数文章在Kubernetes中使用configmap和密钥对象。我们的学习是,它可以帮助您入门,但是对于我们的用例而言,我们发现它还远远不够。将configmap与现有服务一起使用需要一定的费用。Configmap可以通过某种方式安装到pod中-使用环境变量是最常见的方式。如果您有大量的旧式微服务从配置管理工具(如Puppet,Chef或Ansible)提供的文件中读取配置,则您将不得不在所有代码库中重做配置处理,以便现在从环境变量中进行读取。我们没有找到足够的理由在合理的情况下执行此操作。此外,配置或机密的更改意味着您将必须通过打补丁来重新部署您的部署。这将是kubectl命令的额外命令性编排。
> Design by Asif Jamal
为了避免所有这些情况,我们决定使用Consul,Vault和Consul模板进行配置管理。今天,我们将Consul模板作为初始化容器运行,并计划将其作为Pod中的Sidecar运行,以便它可以监视Consul中的配置更改并刷新Vault中即将过期的密钥并优雅地重新加载应用程序进程。
CI / CD
在迁移到Kubernetes之前,我们一直在使用Jenkins。迁移到Kubernetes之后,我们决定坚持使用詹金斯。到目前为止,我们的经验是,Jenkins并不是使用云原生基础架构的最佳解决方案。我们发现自己使用Python,Bash,Docker和脚本化/声明性Jenkins管道做了很多工作,以使其全部正常工作。建立和维护这些工具和管道开始变得昂贵。我们现在正在探索Tekton和Argo工作流作为我们的新CI / CD平台。您可以在CI / CD环境中探索更多选项,例如Jenkins X,Screwdriver,Keptn等。
开发经验
在开发工作流程中有多种使用Kubernetes的方法。我们主要将选择权归零到两个选项-Telepresence.io和Skaffold。Skaffold能够监视您的本地更改并将其不断部署到您的Kubernetes集群中。另一方面,网真允许您在与Kubernetes集群建立透明网络代理的同时在本地运行服务,以便您的本地服务可以与Kubernetes中的其他服务进行通信,就好像它已部署在集群中一样。这是个人意见和偏好的问题。很难决定一种工具。目前,我们主要在尝试网真,但我们并没有放弃Skaffold对我们来说是更好的工具的可能性。只有时间会告诉我们我们决定使用什么,或者也许我们会同时使用。还有其他解决方案,例如草稿值得一提。
分布式跟踪
我们暂时还没有进行分布式跟踪。但是,我们计划很快就对该领域进行投资。像日志记录一样,我们希望在Grafana旁边提供度量标准和日志记录旁边的分布式跟踪,以便为我们的开发团队提供更加集成的可观察性体验。
应用程序打包,部署和工具
Kubernetes的一个重要方面是考虑开发人员如何与集群进行交互并部署其工作负载。我们希望使事情简单易扩展。我们正在向Kustomize,Skaffold以及一些本地CRD汇聚,以作为开发人员部署和管理应用程序的方式。话虽如此,只要团队是开源的并且建立在开放标准之上,那么任何团队都可以*地使用他们想与集群进行交互的任何工具。
操作Kubernetes集群很困难
我们主要在AWS的新加坡地区以外运营。在我们开始使用Kubernetes的旅程时,在新加坡地区还没有提供EKS服务。因此,我们必须使用kops在EC2上建立自己的Kubernetes集群。
建立一个基本集群也许并不那么困难。我们能够在一周内建立起第一个集群。当您开始部署工作负载时,大多数问题都会发生。从调整群集自动缩放器到在正确的时间配置资源,再到正确配置网络以实现正确的性能,您必须自己研究和配置。在大多数情况下,默认设置在大多数情况下都不会起作用(或者至少在那时对我们不起作用)。
我们的学习是,操作Kubernetes是复杂的。有很多活动部件。而且,学习如何操作Kubernetes很可能不是您业务的核心。尽可能将负载卸载到云服务提供商(EKS,GKE,AKS)。自己进行此操作没有任何价值。
您仍然必须考虑升级
Kubernetes非常复杂,即使您使用的是托管服务,升级也不会一帆风顺。
即使使用托管的Kubernetes服务,也要尽早投资基础架构即代码的设置,以使灾难恢复和升级过程在未来的痛苦相对较小,并且能够在发生灾难时快速恢复。
如果愿意,您可以尝试推动GitOps。如果您无法做到这一点,那么将手动步骤减少到最低限度是一个很好的开始。我们结合使用eksctl,terraform和我们的群集配置清单(包括平台服务的清单)来建立所谓的“ Grofers Kubernetes平台”。为了使设置和部署过程更简单,可重复,我们建立了一个自动化管道来设置新集群并将更改部署到现有集群。
资源请求和限制
开始迁移后,由于配置错误,我们在群集中观察到许多性能和功能问题。其结果之一是在资源请求和限制中添加了许多缓冲区,以消除资源限制,从而降低性能。
最早的观察之一是由于节点上的内存限制而导致的逐出。原因是与资源请求相比,资源限制过高。随着流量的激增,内存消耗的增加可能导致节点上的内存饱和,从而进一步导致Pod逐出。
我们的学习是将资源请求保持在足够高的水平,但又不要过高,以便在低流量时间内浪费资源,并使资源限制相对接近资源请求,以便为尖峰流量留出一定的喘息空间,而不会由于节点上的内存压力而驱逐Pod。限制与请求之间的接近程度取决于您的流量模式。
这不适用于非生产环境(例如开发,登台和CI)。这些环境不会带来任何流量高峰。从理论上讲,如果将CPU请求设置为零并为容器设置足够高的CPU限制,则可以运行无限个容器。如果您的容器开始占用大量CPU,它们将受到限制。您也可以对内存请求和限制执行相同的操作。但是,达到内存限制的行为与CPU不同。如果您使用的内存超过了设置的内存限制,则容器将被杀死OOM,然后容器将重新启动。如果您的内存限制异常高(例如高于节点的容量),则可以继续使用内存,但是最终,当节点可用内存不足时,调度程序将开始逐出Pod。
在非生产环境中,我们通过保持极低的资源请求和极高的资源限制来尽可能安全地超额分配资源。在这种情况下,限制因素是内存,即无论内存请求有多低和内存限制有多高,pod逐出都是节点上调度的所有容器使用的内存总和的函数。
安全与治理
Kubernetes旨在为开发人员解锁云平台,使其更加独立,并推动DevOps文化。向开发人员开放平台,减少云工程团队(或系统管理员)的干预以及使开发团队独立应该是重要目标之一。
有时,这种独立性可能会带来严重的风险。例如,默认情况下,在EKS中使用LoadBalancer类型服务会配置面向ELB的公共网络。添加某些注释将确保提供内部ELB。我们在早期就犯了一些错误。
我们使用开放策略代理来减少各种安全风险,以及降低成本,安全和技术债务相关的风险。
部署Open Policy Agent以构建正确的控件有助于自动化整个变更管理过程,并为我们的开发人员构建正确的安全网。借助Open Policy Agent,我们可以限制如前所述的方案-除非存在正确的注释,否则可以限制创建服务对象,以免开发人员意外创建公共ELB。
成本
迁移后,我们看到了巨大的成本优势。但是,并非所有好处都会立即产生。
注意:我们正在整理有关我们最近的成本优化计划得更详细的文章。提防Lambda。
更好地利用资源容量
这是最明显的一个。今天,我们的基础架构所配置的计算,内存和存储远远少于以前。除了由于更好地包装容器/过程而提高了容量利用率之外,我们还能够比以前更好地利用我们的共享服务,例如过程的可观察性(指标,日志)。
但是,最初,我们在迁移时浪费了大量资源。由于我们无法正确调整自我管理的Kubernetes集群,从而导致大量性能问题,因此我们最终要求在Pod中使用大量资源作为缓冲区,更像是保险,以减少因出现故障或性能问题而导致的机会缺乏计算或内存。
由于存在较大的资源缓冲区,导致高昂的基础架构成本是一个大问题。由于我们应该拥有Kubernetes,我们并没有真正实现容量利用的任何好处。在迁移到EKS之后,观察到它带来的稳定性使我们变得更加自信,这帮助我们采取了必要的步骤来纠正资源需求并大幅度减少资源浪费。
实例
与Kubernetes一起使用竞价型实例比在原始VM上使用竞价型实例要容易得多。使用VM,您可以自己管理竞价型实例,这可能会有些复杂性,无法确保您的应用程序具有适当的正常运行时间,或者使用SpotInst等服务。Kubernetes同样适用,但是Kubernetes带来的资源效率可以为您留出足够的空间来保留一些缓冲区,以便即使集群中的几个实例被中断,在其上安排的容器也可以在其他地方快速重新安排。有一些选项可以有效地管理现场中断。
竞价型实例帮助我们节省了大量资金。今天,我们整个阶段的Kubernetes集群都在竞价型实例上运行,而我们的生产Kubernetes集群的99%都由预留实例,节省计划和竞价型实例覆盖。
优化的下一步是如何在竞价型实例上运行整个生产集群。在另一篇博客文章中,有关此主题的更多信息。
ELB合并
我们使用Ingress在舞台环境中整合了ELB,并大幅降低了ELB的固定成本。为了避免这种情况导致代码之间出现开发/产品差异,我们决定实现一个控制器,该控制器会将LoadBalancer类型服务与我们阶段集群中的入口对象一起更改为NodePort类型服务。
对于我们来说,向Nginx入口的迁移相对简单,并且由于采用了控制器方法,因此无需进行大量更改。如果我们也将ingress用于生产中,则可以节省更多的钱。这不是一个简单的变化。在以正确的方式配置生产的入口时,必须考虑几个方面,并且还必须从安全性和API管理的角度来考虑。这是我们打算在不久的将来工作的领域。
增加跨可用区AZ数据传输
尽管我们节省了很多基础架构支出,但仍有一部分基础架构成本增加了-跨可用区数据传输。
可以在任何节点上配置Pod。即使您控制Pod在群集中的分布方式,也没有一种简单的方法来控制服务如何以一种服务的Pod与同一AZ中的另一服务的Pod交谈的方式来发现彼此,以减少跨可用区的数据转移。
经过与其他公司的同行进行大量研究和交谈之后,我们了解到可以通过引入服务网格来控制如何将流量从Pod路由到目标Pod来实现。我们还没有准备好为了节省跨可用区数据传输的成本而自己承担操作服务网格的复杂性。
CRD,Operator和控制器–迈向简化运营的一步和更全面的体验
每个组织都有自己的工作流程和运营挑战。我们也有我们的。
在我们使用Kubernetes的两年旅程中,我们了解到Kubernetes很棒,但是当您使用控制器,Operator和CRD等功能来简化日常操作并为开发人员提供更集成的体验时,Kubernetes会更好。
我们已经开始投资大量的控制器和CRD。例如,LoadBalancer服务类型到入口的转换是控制器操作。同样,只要部署了新服务,我们就会使用控制器在DNS提供程序中自动创建CNAME记录。这些是几个例子。我们还有其他5个单独的用例,它们依靠我们的内部控制器来简化日常操作并减少工作量。
我们还建立了一些CRD。通过声明指定应使用哪些监视仪表板,其中之一已在当今广泛用于在Grafana上生成监视仪表板。这使开发人员可以在其应用程序代码库旁边嵌入其监视仪表板,并使用相同的工作流(kubectl apply -f)部署所有内容。。
我们正在大量看到控制者和CRD的好处。当我们与云供应商AWS紧密合作以简化集群基础架构操作时,我们就腾出了更多精力专注于构建“ Grofers Kubernetes平台”,该平台旨在以最佳方式支持我们的开发团队。
原文链接:https://lambda.grofers.com/learnings-from-two-years-of-kubernetes-in-production-b0ec21aa2814