在生产环境使用Kubernetes三年后我学到的东西
我们于2017年开始构建第一个Kubernetes集群1.9.4版本。 我们有两个集群,一个集群在裸机RHEL VM上运行,另一个集群在AWS EC2上运行。
今天,我们的Kubernetes基础架构团队由分布在多个数据中心的400多个虚拟机组成。 该平台托管高度可用的关键任务软件应用程序和系统,以管理具有近四百万个活动设备的大型实时网络。
Kubernetes最终使我们的生活变得更轻松,但是这一旅程是艰难的,是一种范式转变。 不仅我们的技能和工具有了彻底的转变,而且我们的设计和思维也得到了彻底的转变。 我们必须采用多种新技术并进行大量投资,以提高和提高我们的团队和基础架构的技能。
回顾三年来,Kubernetes在生产中运行了三年,这是我们日记中的重要课程。
1. Java应用程序的奇怪案例
在微服务和容器化方面,工程师倾向于避免使用Java,这主要是由于Java臭名昭著的内存管理。 但是,现在情况发生了变化,多年来Java的容器兼容性得到了改善。 毕竟,无所不在的系统(例如Apache Kafka和Elasticsearch)在Java上运行。
回顾2017-18年度,我们有一些应用程序在Java版本8上运行。这些应用程序通常很难理解Docker等容器环境,并因堆内存问题和异常的垃圾收集趋势而崩溃。 我们了解到,这是由于JVM无法使用Linux cgroup和命名空间(它们是容器化技术的核心)造成的。
但是,从那时起,Oracle一直在不断提高Java在容器领域的兼容性。 甚至Java 8的后续补丁都引入了实验性JVM标志来解决这些问题,XX:+ UnlockExperimentalVMOptions和XX:+ UseCGroupMemoryLimitForHeap
但是,尽管有了所有的改进,但无可否认的是,与Python或Go等同行相比,Java在占用内存和启动速度慢方面仍然享有不良声誉。 这主要是由JVM的内存管理和类加载器引起的。
今天,如果我们必须选择Java,请确保它的版本为11或更高。 并且我们的Kubernetes内存限制在JVM最大堆内存(-Xmx)之上设置为1GB,以留有余量。 也就是说,如果JVM使用8GB的堆内存,则我们对该应用程序的Kubernetes资源限制为9GB。 这样,生活会更好。
2. Kubernetes生命周期升级
Kubernetes生命周期管理(例如升级或增强功能)非常繁琐,尤其是如果您已在裸机或VM上构建了自己的集群。 对于升级,我们已经意识到,最简单的方法是使用最新版本构建新集群,并将工作负载从旧版本过渡到新版本。 就地节点升级所做的努力和计划是不值得的。
Kubernetes具有多个移动部件,需要与升级保持一致。 从Docker到Calico或Flannel之类的CNI插件,您都需要仔细地将它们拼凑在一起才能正常工作。 尽管像Kubespray,Kubeone,Kops和Kubeaws这样的项目使它变得更容易,但它们都有缺点。
我们在RHEL VM上使用Kubespray构建了集群。 Kubespray非常棒,它具有用于构建,添加和删除新节点,升级版本的手册,以及我们在生产环境中操作Kubernetes所需的几乎所有内容。 但是,升级手册附带了免责声明,可防止我们跳过次要版本。 因此,必须经过所有中间版本才能达到目标版本。
要点是,如果您打算使用Kubernetes或已经在使用Kubernetes,请考虑生命周期活动以及您的解决方案如何解决这一问题。 构建和运行集群相对容易一些,但是生命周期维护是一个全新的游戏,具有多个活动部分。
3.构建和部署
准备重新设计整个构建和部署管道。 我们的构建过程和部署必须经历Kubernetes世界的完整转型。 不仅在Jenkins管道中进行了大量的重组,而且还使用了诸如Helm之类的新工具,对新的git流和构建进行了策略化,标记了docker映像,并对helm部署chart进行了版本控制。
您不仅需要维护代码,还需要维护Kubernetes部署文件,Docker文件,Docker映像,Helm chart的策略,并设计一种将所有这些链接在一起的方法。
经过几次迭代,我们决定采用以下设计。
· 应用程序代码及其Helm Chart位于单独的git存储库中。 这使我们可以分别对它们进行版本控制。 (语义版本控制)
· 然后,我们将Chart版本的映射与应用程序版本一起保存,并使用它来跟踪发布。 因此,例如,app-1.2.0部署有Charts-1.1.0。 如果仅更改Helm值文件,则仅更改Chart的补丁程序版本。 (例如,从1.1.0到1.1.1)。 所有这些版本均由每个存储库RELEASE.txt中的发行说明规定。
· 我们未构建或修改其代码的Apache Kafka或Redis等系统应用程序的工作方式有所不同。 也就是说,我们没有两个git存储库,因为Docker标签只是Helm chart版本控制的一部分。 如果我们更改了docker标签以进行升级,则会在图表标签中增加主要版本。
4.Liveness和Readiness就绪探针(双刃剑)
Kubernetes的活跃性和就绪性探查是自动解决系统问题的出色功能。 他们可以在发生故障时重新启动容器,并从不正常的实例转移流量。 但是,在某些故障情况下,这些探测可能会变成一把双刃剑,并会影响应用程序的启动和恢复,尤其是有消息的应用程序,例如消息平台或数据库。
我们的Kafka系统就是这个受害者。 我们运行了一个3 Broker 3 Zookeeper状态集,该状态集的ReplicationFactor为3,而minInSyncReplica为2。当Kafka在意外的系统故障或崩溃后启动时,就会发生此问题。 这导致它在启动期间运行其他脚本来修复损坏的索引,根据严重性,此过程可能需要10到30分钟。 由于增加了时间,生动性探针将不断失败,从而向Kafka发出终止信号以重新启动。 这阻止了Kafka修改索引并完全启动。
唯一的解决方案是在实时探针设置中配置initialDelaySeconds,以在容器启动后延迟探针评估。 但是,当然,问题在于很难对此加以说明。 有些恢复甚至需要一个小时,因此我们需要提供足够的空间来解决这一问题。 但是,增加initialDelaySeconds的次数越多,弹性的速度就越慢,因为在启动失败期间Kubernetes需要更长的时间来重新启动容器。
因此,中间的目的是评估initialDelaySeconds字段的值,以使其在您在Kubernetes中寻求的弹性与应用程序在所有故障情况(磁盘故障,网络故障,系统崩溃等)下成功启动所花费的时间之间取得更好的平衡 )
更新:如果您使用的是最新的最新版本,Kubernetes引入了第三种探针类型,称为"启动探针",以解决此问题。 从1.16版开始提供alpha版本,从1.18版开始提供beta版本。
启动探针会禁用就绪性和活动性检查,直到容器启动为止,以确保应用程序的启动不会中断。
5.公开外部IP
我们了解到,使用静态外部IP公开服务会对内核的连接跟踪机制造成巨大损失。 除非进行彻底计划,否则它只会按比例分解。
我们的集群在Kubernetes内的路由协议上运行在Calico for CNI和BGP上,并与边缘路由器对等。 对于Kubeproxy,我们使用IP Tablesmode。 我们在Kubernetes中托管着庞大的服务,该服务通过每天处理数百万个连接的外部IP公开。 由于来自软件定义网络的所有SNAT和伪装,Kubernetes需要一种机制来跟踪所有这些逻辑流。 为此,它使用内核的Conntrack和netfilter工具管理与静态IP的这些外部连接,然后将其转换为内部服务IP,然后转换为您的pod IP。 这全部通过conntrack表和IP表完成。
但是,此conntrack表有其局限性。 一旦达到限制,您的Kubernetes集群(位于下面的OS内核)将不再能够接受新连接。 在RHEL上,您可以通过这种方式进行检查。
$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count = 167012
net.netfilter.nf_conntrack_max = 262144
解决此问题的一些方法是使用边缘路由器对等多个节点,以使到您的静态IP的传入连接遍及整个群集。 因此,如果您的集群中有大量的计算机,则累积起来,您可以拥有一个大的conntrack表来处理大量的传入连接。
早在2017年成立之初,这一切就让我们望而却步,但最近,Calico在2019年对此进行了详细研究,标题为"为什么conntrack不再是您的朋友"。
您绝对需要Kubernetes吗?
三年过去了,我们仍然每天继续发现和学习新知识。 它是一个复杂的平台,具有自己的一系列挑战,尤其是在构建和维护环境方面的开销。 它将改变您的设计,思维,架构,并需要提高技能和扩大团队规模以适应转型。
但是,如果您在云上并且能够将Kubernetes用作"服务",则可以减轻平台维护带来的大部分开销,例如"如何扩展内部网络CIDR?"。 或"如何升级我的Kubernetes版本?"
今天,我们意识到,您需要问自己的第一个问题是"您是否绝对需要Kubernetes?" 这可以帮助您评估您所遇到的问题以及Kubernetes解决该问题的重要性。
Kubernetes转型并不便宜。 您为此支付的价格必须确实证明"您的"用例及其如何利用该平台。 如果可以,那么Kubernetes可以极大地提高您的生产力。
记住,为了技术而技术是没有意义的。
本文翻译自Komal Venkatesh Ganesan的文章《3 Years of Kubernetes in Production–Here's What We Learned》