Kubernetes投入生产的3年,我们得到的一些经验教训

我们从2017年开始基于1.9.4版本构建第一个Kubernetes 集群。 我们有两个集群,一个集群在裸金属的RHEL 虚拟机上运行,另一个集群在AWS EC2上运行。

现在,我们的Kubernetes 基础设施平台由分布在多个数据中心的400多台虚拟机组成。 该平台托管了高可用的关键任务软件应用程序和系统,以管理具有近四百万个活动设备的大型实时网络。

Kubernetes 最终使我们变得更轻松,但是这个过程很艰难,是一种思维上的转变。 不仅让我们的技能和工具有了彻底的转变,还让我们的设计和思维也得到了彻底的转变。 我们不得不采用多种新技术,并进行大量投资以扩展和提高我们的团队和基础架构的技能。

回顾Kubernetes 在生产环境中运行的这三年,我们记下了一些很重要的经验教训。

  1. Java应用程序的奇怪案例

在微服务和容器化方面,工程师倾向于避免使用Java,这主要是由于Java 臭名昭著的内存管理。 但是,现在情况发生了改变,过去几年来Java 的容器兼容性得到了改善。 毕竟,大量的系统(例如Apache KafkaElasticsearch)在Java上运行。

回顾2017-18年度,我们有一些应用程序在Java 8上运行。这些应用程序通常很难理解像Docker 这样的容器环境,并因堆内存问题和异常的垃圾回收趋势而崩溃。 我们了解到,这是由于JVM 无法使用Linuxcgroupnamespace造成的,而它们是容器化技术的核心。

但是,从那时起,Oracle 一直在不断提高Java 在容器领域的兼容性。 甚至Java 8的后续补丁都引入了实验性的JVM 标志来解决这些问题,XX:+UnlockExperimentalVMOptionsXX:+UseCGroupMemoryLimitForHeap

但是,尽管做了所有的这些改进,不可否认的是,Java 在内存占用方面仍然声誉不佳,与Python 或Go 等同行相比启动速度慢。 这主要是由JVM 的内存管理和类加载器引起的。

现在,如果我们必须选择Java,请确保版本为11或更高。 并且Kubernetes 的内存限制要在JVM 最大堆内存(-Xmx)的基础上增加1GB,以留有余量。 也就是说,如果JVM 使用8GB的堆内存,则我们对该应用程序的Kubernetes 资源限制为9GB。

  1. Kubernetes 生命周期管理:升级

Kubernetes 生命周期管理(例如升级或增强)非常繁琐,尤其是如果已经在裸金属或虚拟机上构建了自己的集群。 对于升级,我们已经意识到,最简单的方法是使用最新版本构建新集群,并将工作负载从旧版本过渡到新版本。 节点原地升级所做的努力和计划是不值得的。

Kubernetes 具有多个活动组件,需要升级保持一致。 从Docker 到Calico 或Flannel 之类的CNI 插件,你需要仔细地将它们组合在一起才能正常工作。 虽然像Kubespray、Kubeone、Kops和Kubeaws 这样的项目使它变得更容易,但它们都有缺点。

我们在RHEL 虚拟机上使用Kubespray 构建了自己的集群。 Kubespray 非常棒,它具有用于构建、添加和删除新节点、升级版本的playbook,以及我们在生产环境中操作Kubernetes 所需的几乎所有内容。 但是,用于升级的playbook附带了免责声明,以避免我们跳过子版本。 因此,必须经过所有中间版本才能到达目标版本。

关键是,如果你打算使用Kubernetes 或已经在使用Kubernetes,请考虑生命周期活动以及解决这一问题的方案。 构建和运行集群相对容易一些,但是生命周期维护是一个全新的体验,具有多个活动组件。

  1. 构建和部署

在准备重新设计整个构建和部署流水线之前, 我们的构建过程和部署必须经历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 的values 文件,则只更改chart 的补丁版本(例如,从1.1.01.1.1)。所有这些版本均由每个存储库中的RELEASE.txt中的发行说明规定。
  • 对于我们未构建或修改代码的系统应用程序,例如Apache Kafka 或Redis ,工作方式有所不同。 也就是说,我们没有两个git 存储库,因为Docker 标签只是Helm chart 版本控制的一部分。 如果我们更改了docker 标签以进行升级,则会升级chart 标签的主要版本。
  1. 存活和就绪探针(双刃剑)

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 版本。

启动探针会禁用就绪和存活检查,直到容器启动为止,以确保应用程序的启动不会中断。

  1. 公开外部IP

我们了解到,使用静态外部IP 公开服务会对内核的连接跟踪机制造成巨大代价。除非进行完整的计划,否则它很轻易就破坏了扩展性。

我们的集群运行在Calico for CNI上,在Kubernetes 内部采用BGP作为路由协议,并与边缘路由器对等。对于Kubeproxy,我们使用IP Tables模式。我们在Kubernetes 中托管着大量的服务,通过外部IP 公开,每天处理数百万个连接。由于来自软件定义网络的所有SNAT 和伪装,Kubernetes 需要一种机制来跟踪所有这些逻辑流。为此,它使用内核的Conntrack and 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 可以极大地提高你的生产力。

记住,为了技术而技术是没有意义的。

原文链接:
3 Years of Kubernetes in Production–Here’s What We Learned

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章