在生产环境使用一年Kubernetes的经验教训


【编者的话】Luminis Technologies使用Kubernetes的经验总结,供大家学习。

2015年初,在Amazon EC2上执行部署多年后,我在Luminis Technologies的团队接受了一个新任务,该任务是为所有我们的开发团队建立一个新的部署平台。基于AWS的配置环境多年来一直为我们服务的很好,但这套使用自定义脚本和工具自动化部的配置环境对于运营团队以外的团队并不是很容易使用的,尤其是那些小团队,他们没有资源来学习所有这些脚本和工具的详细信息。主要问题是没有“部署单元”,一没有个,导致开发和运营之间的存在一个鸿沟。容器化趋势显然会改变这种情况。

如果你仍然还没有准备在生产环境引入Docker和Kubernetes,可以了解下我的团队是如何成为早期“尝鲜者”的。我们现在已经在生产环境中运行Kubernetes有一年多了。

开始用容器和容器编制工具

我现在相信容器是未来部署应用的事实标准,容器让你更容易打包应用程序所需的基础设施。而像Docker这样的工具可以提供真正的容器,我们也需要工具来管理容器的复制和故障切换,以及也需要API来自动化部署到多台机器。

Kubernetes和Docker Swarm等编排工具在2015年初的状态还是很不成熟的,只有早期的alpha版本。我们还试着使用它们,开始使用的是Docker Swarm。

起初,我们自己用它来处理网络,通过Ambassador模式和一系列脚本来自动化部署。这能有多难?这是我们学到的第一课:容器集群、网络和自动化部署实际上是很难解决的问题。

我们很快意识到这一点,决定押注在另一个可用的工具上。Kubernetes似乎是最好的选择,因为它是由谷歌、Red Hat、CoreOS和其他团体支持的,这些团体显然知道如何运行大规模部署。

使用Kubernetes负载平衡

在使用Kubernetes时,你必须熟悉节点服务和和 复制控制器等这些概念。如果你不熟悉这些概念,有一些优秀的资源可以让你跟上节奏。Kubernetes文档 是一个很好的开始,因为它有好几个初学者指南。

一旦有一个Kubernetes集群可以启动并运行,我们可以使用kubectl(Kubernetes CLI)部署一个应用程序,但我们很快发现当我们想自动化部署时,kubectl并不足够。但首先,我们有另一个问题要解决:如何从互联网上访问部署的应用程序?

在部署前面的服务有一个IP地址,但是这个地址只在Kubernetes集群中。这意味着这个服务在互联网上根本不可用!当运行在谷歌云引擎(Google Cloud Engine,GCE)时,Kubernetes可以自动配置一个负载均衡器来访问应用程序。如果你不是在GCE上(像我们这样),你需要做一些额外的工作来使负载平衡工作。

直接在主机端口上提供服务是有可能的,这是很多人开始会的,但是我们发现它错过了很多Kubernetes的提供便利。如果我们依赖主机的端口,我们在部署多个应用程序时将会遇到端口冲突。这也使得它更难规模化集群或更换主机。

两步设置负载均衡器

我们发现一个更好的方法是配置一个负载均衡器例如HAProxy或NGINX,在Kubernetes集群前面。我们开始在AWS上的VPN内运行Kubernetes集群,使用AWS弹性负载均衡将外部网页流量路由到一个内部HAProxy集群。HAProxy为为每个Kubernetes服务配置一个“后端”,它将流量代理到每个节点。

这两步配置负载均衡的方法主要是为了应对AWS ELB很有限的配置选项。其中限制之一是,它不能处理多个虚拟主机 。这正是我们使用HAProxy的原因。只用HAProxy(没有ELB)也可以工作,但是你将不得不在DNS上解决AWS的动态IP地址问题。
1.jpeg

图1:两步负载平衡

在任何情况下,当新的Kubernetes服务创建时,我们需要一种机制来动态地重新配置负载均衡(HAProxy,在我们的例子中)。

Kubernetes社区目前正在开发一个特性Ingress 。 它能够直接从Kubernetes配置一个外部负载均衡。目前这个特性还没有真正可用,因为它没有完成。去年,我们使用API和一个小开源工具来配置负载均衡。

配置负载均衡

首先,我们需要一个地方来存储负载均衡的配置。他们可以存储在任何地方,但由于我们已经有etcd可用,我们决定在那里存储负载均衡的配置。我们使用的工具confd监控etcd的配置更改,并基于模板生成一个新的HAProxy配置文件。当一个新的服务添加到Kubernetes时,我们添加一个新的配置到etcd,这会产生一个新的配置文件给HAProxy。

Kubernetes:成熟的正确方法

Kubernetes仍然存在许多尚未解决的问题,就像他们经常在负载均衡中表现的那样。这些问题正在被社区所识别,也有一些文档来讨论解决其中的一些问题。但想出适合每个人的解决方案是需要时间的,这就意味着在他们发布版本解决其中一些问题之前需要花费很长一段时间。这是一件好事,因为在设计新功能时走捷径长期来看是非常不利的。

这并不意味着现在Kubernetes是不易使用的。使用API,可以让Kubernetes做几乎所有你需要的事,如果你现在开始使用它。一旦更多的功能集成到Kubernetes版本中,我们可以用标准方案替换我们自己的解决方案。

我们开发完自己的负载均衡解决方案后,我们的下一个挑战是实现一个基本的部署方案:蓝绿部署。

Kubernetes中的蓝绿部署

蓝绿部署是一个没有任何停机时的方案。相比滚动更新,蓝绿部署是先启动一个新版本的副本集群,此时所有的旧副本仍然为实时请求提供服务。只有当新的副本完全启动并运行,负载均衡配置改变,将负载切换到新版本。这种方法的一个好处是,总是只有一个版本的应用程序在运行,减少处理多个并发版本的复杂性。当副本的数量是相当小时,蓝绿部署可以更好地工作。
2.jpeg

图2:Kubernetes蓝绿部署

图2显示一个组件“部署者”,来协调部署。该组件可以很容易由你自己的团队创建,因为我们基于Apache许可开源了我们的实现,作为Amdatu umbrella 项目一部分,它还提供了一个Web界面来配置部署。

这种机制的一个重要方面是健康检查,在重新配置负载均衡前,它在节点上执行之。我们希望部署的每个组件都能够进行健康检查。现在我们通常通过HTTP在每个应用程序组件上添加一个健康检查。

自动部署

有了“部署者” ,我们可以把部署挂到构建流上。构建成功后,构建服务器可以提交一个新的容器到注册中心如Docker Hub,然后构建服务器唤起“部署者”自动将新版本部署到测试环境。通过触发生产环境的部署者,可以将相同的镜像提交到生产环境。
3.jpeg

图3:我们自动化容器部署流水线

知道你的资源约束

当我们开始使用Kubernetes时,知道我们的资源约束 是至关重要。你可以配置资源请求和CPU/内存限制在每个节点上,你还可以控制资源保障和峰值限制。

对于有效地运行多个容器在一起,这些设置是非常重要的。如果我们不正确地设置这些值,容器会经常崩溃,因为他们无法分配足够的内存。

早开始设置和测试约束。没有限制,一切仍将运行很好,但当你把任何重负载放到其中一个容器时,你会得到一个巨大的,令人不快的意外。

我们如何监控Kubernetes

当我们使用Kubernetes配置完毕,我们很快意识到在这个新的动态环境中监控和日志至关重要。登录到一个服务器查看日志不再好使了,当你处理大量的副本和节点时。一旦你开始使用Kubernetes,你也应该有一个计划建立集中化的日志记录和监控。

日志记录

有很多开源工具可用于日志记录。我们决定使用 Graylog(一个优秀的日志记录工具)和Apache Kafka,一个消息系统从我们的容器收集和记录日志。容器发送日志给Kafka,Kafka处理日志给Graylog索引。我们选择使应用程序组件发送日志给Kafka,这样我们就可以易于索引的方式流化日志记录。其他替代方案,有工具可以从容器外部获得日志,并转发给一个日志记录方案。

监控

当节点有错误时,Kubernetes做了出色的工作来恢复节点。当节点由于任何原因崩溃时,Kubernetes将重新启动它们。当Kubernetes正在运行复制时,终端用户可能甚至都不会察觉有错误发生。Kubernetes的恢复工作如此完美,以至于我们的容器因为内存泄露一天崩溃多次,而任何人(包括我们)都没有发现。

尽管从Kubernetes的角度来看,这是很棒的,你可能仍然想知道何时发生了问题。我们使用一个定制的健康检查面板来监测Kubernetes节点,单独节点(使用程序特制的健康检查)和其他服务如数据存储。实现一个这样的仪表面板,Kubernetes API再次被证明可以提供非常宝贵的信息。

我们也认为测量负载、吞吐量、应用程序错误和其他统计数据是重要的。开源社区再一次提供了很多选择。我们的应用程序组件在时间序列存储InfluxDB中提供了测量指标。我们还使用了Heapster收集Kubernetes指标。在InfluxDB的指标可以通过一个开源的仪表板工具Grafana来可视化。有很多替代品InfluxDB/Grafana的组合,他们中任何一个都可以提供很多有价值的东西来跟踪容器如何运行的。

数据存储和Kubernetes

许多Kubernetes新用户都会问的一个问题是:“我该如何使用Kubernetes存储我的数据?”

当运行一个数据库如MongoDB或MySQL,你最有可能想要将数据持久化。在容器之外,容器重新启动时会丢失数据。这对于无状态的组件是极好的,但对于一个持久化数据库并不是。Kubernetes中有卷的概念来持久化数据。

卷可以支持多种实现,包括在主机机器上的文件、AWS弹性块存储(EBS)和nfs。当我们在研究持久化数据问题时,卷提供了一个好的答案,但对我们运行数据库来说,它不是一个解决方案。

复制问题

在大多数部署,数据库也是以副本运行的。Mongo通常运行在一个复制集合中,而MySQL可以以主/从模式运行。这就带来了一些问题。首先,重要的是数据库集群中的每个节点是由一个不同的卷实现的。往同一个卷写将会导致数据损坏。另一个问题是,大多数数据存储需要精确配置,来使集群启动并运行;自动发现和配置的节点是不常见的。

同时,运行数据库的机器通常是为这种类型工作负载专门进行过调优。更高的IOPS便是其中一个例子。 扩展(添加/移除节点)是一项昂贵的操作对于大多数数据库。这些东西与Kubernetes部署的动态特性很不匹配。

决定生产环境运行数据库不使用Kubernetes

这使给我们遇到一种情况,我们发现Kubernetes内运行一个数据库的好处是有限的。Kubernetes给我们的动态性并不能使用,配置也比大多数Kubernetes部署复杂得多。

由于这个原因,我们没有在Kubernetes内运行生产环境数据库。相反,我们手动设置这些集群在其他主机上,并进行所有必要的调优来优化数据库存储。我们Kubernetes内的应用程序运行就像正常连接到数据存储集群那样。最重要的教训是:一旦有了Kubernetes,你不需要将一切运行在Kubernetes内。不过,除了数据库和HAProxy服务器,其他一切确实都在Kubernetes内运行,包括我们的监控和日志记录方案。

拥抱Kubernetes,为什么我们对明年很兴奋

看看我们现在的部署,Kubernetes绝对是了不起的。Kubernetes API是一个很好的工具用于自动化部署流水线。部署不仅更可靠,而且速度快得多,因为我们不再和虚拟机打交道。我们的构建和部署更加可靠,因为它在容器中更容易测试和分发。

我们现在看到的这个新的部署方式是必要的,这样可以和行业内其他开发团队更频繁地进行部署,并降低他们的工作量。

成本计算

看成本,这个故事有两个方面。为了运行Kubernetes,需要一个etcd集群,以及主节点。虽然这些不一定是昂贵的组件,这个开销对一个很小的部署可以说是相对昂贵的。对于这些类型的部署,最好的方式是使用托管解决方案如谷歌容器服务。

对于更大的部署,很容易在服务器上节省很多。运行etcd和主节的开销在这些部署中并不是很明显的。Kubernetes使得在同一主机上运行很容器很容易,最大限度的利用可用的资源。这减少了所需的服务器数量,直接节省你的钱。运行Kubernetes听起来很不错,但运行这种集群的运营工作似乎不那么有吸引力,有许多托管服务可以考虑,包括Cloud RTI,这就是我的团队所使用的。

Kubernetes的光明未来

运行预发布版本的Kubernetes是非常有挑战性的,跟上(超越)新版本几乎是不可能的。Kubernetes的开发在过去的一年里一直在光速进行,社区已经成长为一个有很多开发人才的正规强大组织。很难相信仅仅在一年多的时间里取得了这么大的进步。

原文链接:One year using Kubernetes in production: Lessons learned (翻译:姜俊厚)

3 个评论

蓝绿部署中的get /health是健康检查吗?
我理解是调用http中get方法,路径是/health,来检查pod的运行状态
明白,谢谢

要回复文章请先登录注册