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

标签: kubernetes 生产 经验 | 发表时间:2020-09-23 17:26 | 作者:Komal Venkatesh Ganesan
出处:https://www.infoq.cn

我们从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 无法使用Linux cgroupnamespace造成的,而它们是容器化技术的核心。

但是,从那时起,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

相关 [kubernetes 生产 经验] 推荐:

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

- - InfoQ推荐
我们从2017年开始基于1.9.4版本构建第一个Kubernetes 集群. 我们有两个集群,一个集群在裸金属的RHEL 虚拟机上运行,另一个集群在AWS EC2上运行. 现在,我们的Kubernetes 基础设施平台由分布在多个数据中心的400多台虚拟机组成. 该平台托管了高可用的关键任务软件应用程序和系统,以管理具有近四百万个活动设备的大型实时网络.

构建生产就绪的Kubernetes集群的16点清单

- - DockOne.io
Kubernetes是用于构建高度可扩展系统的强大工具. 结果,许多公司已经开始或正在计划使用它来协调生产服务. 不幸的是,像大多数强大的技术一样,Kubernetes也很复杂. 我们整理了以下清单,以帮助你生产环境最佳实践Kubernetes. Kubernetes提供了一种编排容器化服务的方法,因此,如果您没有按顺序实践你的容器,那么集群一开始就不会处于良好状态.

生产环境中的Kubernetes最佳实践

- - DockOne.io
2020年,12月1日 Pavan Belagatti. DevOps从提出到现在,已经走过了一段很长的路. 包括Docker和Kubernetes在内的多种平台也已经帮助企业用前所未有的速度实现了软件应用的交付. 同时,随着应用的容器化构建和发布比率不断上升,作为事实上的容器编排工具,Kubernetes在企业用户中备受欢迎和广泛认可.

Kubernetes & Microservice

- - 午夜咖啡
这是前一段时间在一个微服务的 meetup 上的分享,整理成文章发布出来. 谈微服务之前,先澄清一下概念. 微服务这个词的准确定义很难,不同的人有不同的人的看法. 比如一个朋友是『微服务原教旨主义者』,坚持微服务一定是无状态的 http API 服务,其他的都是『邪魔歪道』,它和 SOA,RPC,分布式系统之间有明显的分界.

kubernetes移除Docker?

- -
两周前,Kubernetes在其最新的Changelog中宣布1.20之后将要弃用dockershime,也就说Kubernetes将不再使用Docker做为其容器运行时. 这一消息持续发酵,掀起了不小的波澜,毕竟Kubernetes+Docker的经典组合是被市场所认可的,大量企业都在使用. 看上去这个“弃用”的决定有点无厘头,那么为什么Kubernetes会做出这样的决定.

Kubernetes 完全教程

- - 午夜咖啡
经过一个阶段的准备,视频版本的 《Kubernetes 完全教程》出炉了. 课程一共分为七节,另外有一节 Docker 预备课,每节课大约一个多小时. 目标是让从没接触过 Kubernetes 的同学也能通过这个课程掌握 Kubernetes. 为什么要学习 Kubernetes. 在介绍课程之前,先说说为什么要学习 Kubernetes 以及什么人需要学习 Kubernetes.

Kubernetes 监控详解

- - DockOne.io
【编者的话】监控 Kubernetes 并不是件容易的事. 本文介绍了监控 Kubernetes 的难点、用例以及有关工具,希望可以帮助大家进一步了解监控 Kubernetes. 如果想要监控 Kubernetes,包括基础架构平台和正在运行的工作负载,传统的监控工具和流程可能还不够用. 就目前而言,监控 Kubernetes 并不是件容易的事.

喜大普奔:Spark on kubernetes

- - Zlatan Eevee
两个星期前(08/15/2017),spark社区提了一个新的SPIP(Spark Project Improvement Proposals): Spark on Kubernetes: Kubernetes as A Native Cluster Manager,即用k8s管理spark集群. 经过社区2个星期的投票,看上去很快要能合入了.

Kubernetes 日志收集方案

- - IT瘾-dev
Kubernetes 中的基本日志. Kubernetes 日志收集. 以 sidecar 容器收集日志. 用 sidecar 容器重新输出日志. 使用 sidecar 运行日志采集 agent. 前面的课程中和大家一起学习了 Kubernetes 集群中监控系统的搭建,除了对集群的监控报警之外,还有一项运维工作是非常重要的,那就是日志的收集.

Kubernetes 会不会“杀死” DevOps?

- - InfoQ推荐
DevOps 这个概念最早是在 2007 年提出的,那时云计算基础设施的概念也才刚刚提出没多久,而随着互联网的逐渐普及,应用软件的需求爆发式增长,软件开发的理念也逐渐从瀑布模型(waterfall)转向敏捷开发(agile). 传统的软件交付模式(应用开发人员专注于软件开发、IT 运维人员负责将软件部署到服务器运行),再也无法满足互联网软件快速迭代的需求.