我们于2017年开始构建第一个基于1.9.4版本的Kubernetes集群。至今,我们已经有两个集群,一个集群在裸机RHEL VM上运行,另一个集群在公有云AWS EC2上运行。
今天,我们的Kubernetes集群由分布在多个数据中心的400多个虚拟机组成。该平台承载着高度可用的核心应用程序和系统,管理拥有近400万个活跃用户的的大型实时系统。
Kubernetes最终使我们的运维变得更轻松,但是这一过程是艰辛的,是范式的转变。不仅仅是我们的技能和工具有了彻底的转变,而且我们的设计和思维也得到了彻底的转变。我们必须采用多种新技术,并进行大量的投入,以提高团队和基础设施的档次和技能。
回顾三年来,Kubernetes在我们的生产中运行了三年,这也是本文将会将的重点内容。
1. 基于Java的APP容器化问题
当涉及微服务和容器化时,很多人都倾向于避免使用Java,这主要是由Java不太友好的内存管理造成的。但是,现在情况发生了变化,并且Java的容器兼容性在过去几年中得到了很大的改善。毕竟,当前大部分的系统都有使用Java程序的 Apache Kafka作为中间件,另外Elasticsearch通常也是在Java程序上运行。
回顾2017-2018年度,我们有一些应用程序在Java 8上运行。它们常常难以理解像Docker这样的容器环境,并且由于堆内存问题和异常的垃圾收集而崩溃。我们了解到,这是由于JVM无法支持Linux cgroups和namespaces,后者是容器化技术的核心。
但是,从那时起,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生命周期管理(如升级或补丁修复)非常麻烦,尤其是在裸机或虚拟机上构建了自己的集群时。对于升级,我们已经意识到最简单的方法是用最新版本重新构建一个集群,并将工作负载从旧的集群迁移到新的集群。进行节点的升级相对而言会更复杂,且结果无法预料。Kubernetes有多个与其一起协调工作的组件需要与Kubernetes升级保持一致。从Docker到像Calico或Flannel这样的CNI插件,你需要小心地把它们拼凑在一起才能工作。尽管像Kubespray、Kubeone、Kops和Kubeaws这样的项目使它更容易维护,但它们都有各自的缺点,且不太通用。
我们在RHEL VM上使用Kubespray构建了集群。Kubespray非常不错,它有基于ansible的构建、添加和删除新节点、升级版本的playbooks,以及我们在生产中操作Kubernetes所需的几乎所有内容。但是,升级的playbook带了一个