Kubernetes 并非灵丹妙药...
以下为译文:
我经常听到有人问这样一个问题:“我们应该将自家的技术栈托管到Kubernetes上吗?”不论是新建立的团队还是成熟的团队,都有这样的疑问。鉴于Kubernetes在科技圈内的火热,相信许多人的答案都是肯定的。
我使用K8s已经很多年了,经常与功能非常强大及复杂的平台打交道,而我认为这个问题的答案有点微妙。
在本文中,我将尽力为大家解开谜团。我不仅会考虑创业小公司的情况,而且也会考虑在更广泛的组织中负责托管自家产品的自给自足的团队,最后还希望为大型组织中更传统的IT部门人员提供参考价值。
Kubernetes有什么帮助性?
Kubernetes不仅仅是2018年的一个热门词汇。这是一个功能强大且高度可扩展的系统,可以让你利用一系列成熟的技术原语(Pod、服务、Ingress等)构建应用程序部署,然后还可以尽其所能让实际状态与你的所需状态相匹配。当应用程序崩溃时,Kubernetes会重启它们。当大量底层计算机消失时,Kubernetes会替换它们。如果你正在运行大量服务(可能是作为微服务架构开发的),而且你正在寻找高效、具有弹性以及良好部署的解决方案,则Kubernetes值得考虑。
你希望自己的服务具有基本的弹性?(答案必然是肯定的),那么可以在部署中运行多个副本,然后在这些副本之间实现流量均衡。
如果你的工作负载经常有突发流量(例如大量的API访问),则可以通过自动扩展来根据需要增加容量。这可以为你节省很多钱,因为你无需长期承担峰值容量的费用,同时还可以提供基本的负载量,保持平台正常运行,并在有需要时增加副本。此外,如果你可以将队列导入到系统中,则自动缩放也可用于基于队列的工作负载。
担心代码遭到破坏?你可以配置存活探针和就绪探针,如果这两个探针出现问题,则Kubernetes会自动重启你的服务。同样,对于硬件故障,我曾见过集群丧失了一半的节点,却仍在正常运行,就好像什么都没发生。配置以及维护良好的Kubernetes集群非常强大。如果有足够的资源,你甚至可以尝试运行“混乱的猴子”等混乱测试工具,以确保你的技术栈可以承受常规故障。
此外,Kubernetes还可以与CI工作流程完美地集成。最常见的模式(几乎是所有项目的标准化规范)是,将镜像推送到Docker容器仓库,然后通过K8s加载镜像。当然你也可以根据个人喜好,通过修改部署拉取新标签或让标签指向新镜像,并触发K8s重新加载所有的Pod。在大多数情况下,部署可以完全自动化:如果你相信自己的测试和功能标记,则可以实现百分百的自动化(即连续交付);如果你没有那么大勇气,则可以退一步,在构建后手动批准。无论采用哪种方式,开发人员都可以将大多数构建版本发布到集群,而无需Kubernetes专家的帮助。
与CI类似,Kubernetes可以标准化应用程序的日志记录以及监控。这种做法不是Kubernetes特有的,但是能够在整个集群范围内将有关所有服务的数据统一收集到一个地方,可以极大地减轻调试的负担。我曾见过一个非常优秀的例子:有人使用Fluentd,将应用程序中JSON结构的日志输出通过管道传输到AWS CloudWatch,并通过Insights进行查询。
最后,Kubernetes还可以极大地提高效率,不仅在托管的层面(即将尽可能多的应用程序塞入昂贵的EC2实例中),而且还可以减少开发人员花在部署软件上的时间。人力成本高于计算机,因此对于大多数组织而言,后者才是最大的胜利。但Kubernetes并不是万能的,我见过一些非常漂亮和高效的集群,但结果这些投资只是打了水漂。只有正确使用Kubernetes,才能节省资金。
Kubernetes适用于哪些情况?
首先考虑一下你的工作负载。你需要运行哪种应用程序?它们如何与彼此以及外界对话?根据经验,我认为以下属性表明你的技术栈非常适合Kubernetes:
从广义上讲,你是否遵循微服务架构?如果你只有一个应用程序,那么使用Kubernetes就大材小用了。你需要通过Docker打包应用程序,然后将其部署到K8s。不论什么项目,从第1天开始就这样做,可以帮你思考各个服务之间的界限。
你的服务是否通过HTTP(或HTTPs)相互公开以及对外公开了(应该公开了吧,毕竟都已经2020年了)?这非常适合K8s的模型,你可以使用常规的Ingress控制器作为这些服务的入口。
你的应用程序适合负载均衡吗?即没有本地状态(即使用PostgreSQL、Redis等产品),通过已知端点通信,能够快速启动和关闭。这并不是说集群中不能有Redis缓存等拥有短暂状态的应用,但在许多情况下,最好还是使用云提供商提供的服务。
Kubernetes非常适合无头的应用程序,例如批处理(通过Job控制器)以及长期的队列消费。
内存(以及扩展性更为有限的CPU)的使用是否可以预测?Kubernetes会设法将多个应用程序托管在同一台物理计算机上,因此,如果其中一个应用程序遇到麻烦,并消耗了所有RAM,则其他工作负载可能会被随机干掉。以我的经验,这是Kubernetes集群中最大的不稳定因素。如果你了解应用程序的资源使用情况,则可以声明resources.requests(资源请求)和resources.limits(资源约束),确保各个应用程序只能获得所需的内存,而且不会打扰到其他应用程序。
相反,我认为以下这些工作负载不适合使用Kubernetes:
静态网站。通常,使用Kubernetes的话,你需要将内容嵌入一个由Nginx派生而来的容器镜像中,并通过集群的Ingress控制器公开网站。但这是一种糟糕的托管静态内容的方式:所有Nginx的小副本都需要维护,效率低下,而且网络层面的弹性/性能也非常低。当然,你可以将内容放在CDN后面,但如果要这样做的话,为什么不直接让云服务托管内容呢?
托管不信任的代码。这可能意味着你的应用程序由客户或其他第三方提供,有安全方面的隐患,例如NPM的Wordpress或其他有问题的库。默认情况下,Kubernetes隔离工作负载的功能不是很好。虽然你可以添加诸如Calico之类的产品来控制网络访问,但很容易搞砸,而且你的安全模型将完全依赖于容器运行时。Kubernetes的默认设置(采用基于Linux cgroup的Docker)会导致有安全隐患的应用程序暴露大量的被攻击点:如果集群运行的代码库遭到黑客攻击,那么攻击者很轻易就能进一步攻击集群的其余部分。目前人们正在努力开发cgroup的替代方案(例如Kata Containers),但目前还不能向普通用户推荐。
即便工作负载不合适,你仍然可以选择将其添加到Kubernetes中(例如,可以通过卷保存长期状态),但是需要花费大量的工程时间来解决各种问题。而且很多做法都不推荐,因此,最好还是将资源和时间花在设计更好的技术栈上。强推Kubernetes只会让你的损失更惨重。
Kubernetes并非灵丹妙药
Kubernetes并非灵丹妙药。它可以帮助你将托管应用程序的复杂性转移到精心设计的Kubernetes架构中,但这些复杂性本身并不会消失。你始终需要保护和维护平台。
为了让普通的工作负载也能使用集群,我们需要管理很多附加组件。有些附件几乎每个人都在使用,则有些则比较小众。其中包括Nginx(Ingress控制器)、cert-manager和cluster-autoscaler(在没有足够的容量时添加额外的节点)等。还有一些软件专门为环境定制的关键功能非常独特,而且也同样需要管理。此外,这些组件也需要定期更新,有时可能还有质量问题。Helm或Terraform等配置管理工具几乎是必须的:手动调整集群非常危险,而且没有声明式设置你根本没办法启动另外一个一模一样的集群。我亲身经历过在运维过程或替换成更成熟的集群时引发了无穷无尽的问题。
在Kubernetes上运行任何重要的技术栈,都需要缜密的策划。放任员工随意部署他们喜欢的软件,只会让你的集群陷入混乱。最终,你只能得到一堆层次不齐的应用程序散布在几十个(甚至可能是一个)命名空间中,却没有人知道它们是如何组织在一起的。虽然旧的意大利面条式的基础设施非常混乱,但如今你只是换成了一种新型的意大利面条式的基础设施,最后还用一个更加冰凉的盘子端了上来。
我所见过的最成功的Kubernetes实现方式是:基础设施专家与开发人员合作,确保工作负载的配置合理、标准化、相互保护,而且还有明确的通信模式。基础设施专家负责在基础设施上处理应用程序的初始设置,并将其连接到构建/发布系统,如此一来,开发团队无需他们的帮助即可发布新版本的代码。其实,这个过程反应的是组织广泛的文化,如果你们的工程非常混乱,沟通不顺畅,而且职责不明确,那么托管的环境都将逐一反映出来。即便使用Kubernetes,你们的系统也会非常不可靠;而且在最糟糕的情况下,不可靠之余,还会引发不可维护、成本昂贵等各种问题。
使用Kubernetes的成本
如果你需要运行大规模的应用程序,那么Kubernetes可以为你节省很多钱。你可以研究一下自动伸缩(集群和副本集的自动伸缩)、竞价式实例池(EC2)或可抢占式虚拟机(Google)等功能。在大型环境中,仅此一项就足以让你做出决定。
此外,Kubernetes还有一个优秀的工具生态系统,可以帮助任何工程师创建各种测试集群来测试应用程序。在这些工具的帮助下,Kubernetes的入门学习曲线非常平缓,因此很容易将其投入到生产环境中并成为业务的关键部分,却没有意识到这样做所需的投资规模。Kubernetes的故障模式非常复杂,只有拥有大量专业技术才能充分利用。放任经验不足的开发团队急急忙忙地将集群搭建起来(使用Kops是常见的反模式),通常都会成为一场灾难。虽然开头的几个月可以正常工作,但如果你需要进行重大更改,那么往往会在紧急关头遭遇重新配置群集或故障排除某个已知的问题。
从零构建K8s集群的难度不亚于编译自己的内核,这是一项艰巨的工作,需要学习底层的工作原理,以及怎样运行生产应用程序的点点滴滴。因此,你应该使用现成的解决方案,例如AWS EKS或Google的GKE。很多精英人才投入了大量时间来解决这些问题,即使每个月你需要支付几百美元也是值得的。
即使使用现成的Kubernetes发行版,你也需要专业技能。控制平面由亚马逊负责,但是总有一天你会在节点上触发一些晦涩的bug,而且往往是在业务最繁忙的时候。因此,你必须随时准备好投入大量资源,而且可以接受相应的费用,才能运行系统。Kubernetes的发布周期很短,因此每年至少需要升级一次集群,还有API的定期改动,所以可千万不要小觑这项工作。无论你运行什么插件,都需要维护。如果你的系统规模非常小,要求非常低,那么可以雇佣兼职人员,但请相信我,如果某天凌晨4点,你所有的容器都抛出了某个莫名的线程错误,那么你可能需要其他人的帮助。
所有这些都令我相信,规模/复杂性超过某个阈值,才能高效地使用Kubernetes。如果你运行的服务很少(比如<5个)、很简单、需求不高,那么就不必再纠结了。只有当需要管理大量部署、工作负载变化繁多或标准化工具可以节省大量复杂性/成本的情况下,Kubernetes才能大展拳脚。
所以,我应该使用Kubernetes吗?
虽然我罗嗦了一大堆,但最终的答案仍然是:“视情况而定”。或许你可以马上绘制一张系统架构图。
如果你只有寥寥几个服务,而且短期内不会增加,那么简单又廉价的方式就是托管技术栈。你可以看看AWS ECS(尤其是与Fargate结合使用),将你的API或批处理作业重写为Lambdas / Cloud Functions,甚至还可以将你的应用程序托管给PaaS提供商(比如Heroku)。这种方式听起来虽然老套,但不要忽视在几台维护良好的Linux机器上运行简单的低流量应用程序所带来的价值和稳定性。
另外,安全性和合规性要求可能会影响你的决定。如果必须将工作负载托管到内部硬件上,则你可能需要承担大量的运维开销,尽管你也可以使用Kubernetes,但传统的解决方案可能更适合你。根据合规性的要求,审核所有使用的插件可能不太现实。
我见过很多创业公司,他们以为自己需要Kubernetes,最终却白白投入了大量资源。所以,请认真思考你是否需要所有的功能,以及是否有能力承担良好的实现。如果你的需求证明这些投资很合理,则当然应该下手。否则,你可以将Kubernetes作为备选,考虑将来引入Kubernetes的渠道,以及如何将其纳入你的技术决策中。如果决定使用Kubernetes,那么从第1天开始就应该在Docker中运行应用程序(使用docker-compose,对于开发人员和生产人员都非常有价值),而且需要考虑清楚是否让应用程序存储本地状态。
另一方面,预测未来的增长幅度也非常重要。如果现今你只有几个简单的服务,则可能不需要K8s。但是,它们是否会突飞猛进地发展成几十种呢?如果是,那么你现在就应该开始学习掌握这种复杂性的技术。虽然在双翼飞机的年代,没有人想着建造波音747;但另一方面,如果你需要承载300名乘客,则索普威思骆驼飞机绝对解决不了问题。
综上所述,基础设施的决策通常取决于软件体系结构方面的抉择。在选择基础设施时,你不能后知后觉,但也不要忘记基础设施越多成本就越高。如果有需要,你可以投资复杂的系统,但一定要三思而后行。
原文:https://mbird.biz/writing/do-i-need-kubernetes.html
本文为 CSDN 翻译,转载请注明来源出处。
更多阅读推荐