在过去一年内,Descomplica 计划往核心组件服务化的方向发展,我们一开始使用 Elastic Beanstalk 将这些服务编排到 AWS。
那时候来说,这个决定是明智的。总体来说,Elastic Beanstalk 挺好用的,而且比较容易学习;所有小组为他们各自的项目使用,也不会花费太多时间。
早在几个月之前,一切都还运行得好好的。但是在一些旧问题解决之后,新的问题接踵而至。
成本问题
在 Elastic Beanstalk,每个 EC2 实例只运行一个应用程序容器。这也就意味着,如果你遵循可靠性最佳实践,一个应用程序就会有两个或者多个实例(跨越部署在多个可用域)。除了生产环境,如果你还有其他环境的话(比如预生产环境),那么你可能就需要更多实例。
无论如何,你都要为每个服务根据不同的工作负载部署多个专用实例,并且大部分时间这些实例都很闲。
我们需要找到一个方法更加明智地使用我们的可用计算资源。
赢家
后来我们才发现,Kubernetes 就是我们一直在寻找的 ECS 的替代品。
Kubernetes 是一个容器编排工具,建立在谷歌 15 年运行产品的基础之上,与最佳想法、社区实践相结合。
虽然 Kubernetes 是一个功能丰富的项目,但是只有其中的几个关键功能引起了我们的注意:命名空间,自动滚动升级和回滚,通过 DNS 的服务发现,基于资源使用的容器自动扩容,以及,允诺的自我修复系统。
Kubernetes 围绕“容器应该如何被组织,如何通过网络联络”的理念,但是如果你的服务是遵从 Twelve-Factor 实践的,那么这个理念对你来说也不是什么问题。
我们的生产化之路
为了确保 Kubernetes 是个可行的选择,我们做的第一件事就是做些可靠性测试来确保它能够处理“不可用节点,Kubelet/Proxy/Docker daemons 终止,和可用区域运行中断”这类的故障模式。
预测所有的故障,这是不太可能的,但是最后,我们还是被 Kubernetes 解决这些故障的能力深深折服。
那时候,我们用 kube-up 来创建我们的测试集群。虽然这个工具按照它的目标来服务,但不是每一次都符合预期;它暴露出了非常多的问题,比如说不合理的默认设置,随机超时导致没有完全创建成功的程序栈,以及在删除集群时不确定的行为导致一些资源的遗留。
那时候我们一致同意要选择用 Kubernetes,所以我们需要一个更加可靠的方法来创建或者删除 Kubernetes 集群。
使用“kube-aws”
Kube-aws 这个工具是由 CoreOS 的几个人创建的。它最厉害的地方在于,在 hoods 下,使用 CloudFormation,这对于我们来说有利无弊。
最显著的优点就是,用这个工具创建或者删除集群十分容易,而且删除后还不会留下任何的遗留。
另一个功能在于,与 kube-up 不同,你可以在已经存在的 VPC 上创建集群,这样所有运行在 Kubernetes 上的服务都可以立即访问你的 AWS 资源(比如关系型数据库)。
事实上,你可以同时在同一个 VPC 上运行多个集群。这样就会带来一个良性的副作用,就是你可以把每个集群都看成是不变的基础设施,你不需要修改正在运行的集群——冒着破坏其中一些什么的风险,你只需要创建一个新的集群,然后用影响最小的方法慢慢地把流量从旧集群转移到新集群。
最后一个功能(也可能是最有用的一个),你可以通过配置轻松定制集群的任何方面来满足你的需求。在我们的这个案例中,我们添加集群层面的日志记录,摄取应用程序日志到 Sumologic,用 InfluxDB 和 Grafana 进行集群监控,基于 ABAC 的授权认证,以及一些其它的事情。
第一个环境
在解决了集群创建,集群删除问题之后,我们有信心开始着手迁移我们的预生产环境到 Kubernetes了。
为第一个 Deployment 手动创建 yaml 配置十分简单,但是我们需要自动化地在应用镜像被持续集成系统构建完毕之后立即进行部署。
为了证明这个概念,我们很快在 AWS Lambda(基于这篇文章)开辟了一个小的功能,这个功能可以在它收到一个测试通过了的合并通知,自动升级相应的部署单。
这个小 Lambda 功能现在涉及到我们的交付管道,也编排 deployments 到其它环境,包括生产环境。
有了这个,将预生产环境服务从 Beanstalk 迁移到 Kubernetes 就相当容易了。首先,我们为每个服务(一开始指的是在 Elastic Beanstalk 上遗留的部署)都创建了 DNS 记录,确保所有的服务都是通过这个 DNS 互相访问的。然后,就只要修改这些 DNS 记录来指向相应的 Kubernetes 管理的负载均衡器。
为了确保管道的每个部分都按照预期的来运行,我们监控所有的预生产环境部署,寻找 bug,然后尽我们所能地修复它,完善功能。
测试得越多,学习到得越多
在部署我们的第一个产品服务到 Kubernetes 之前,我们做了一些压力测试,来找到每个服务最佳的资源需求配置,也为了找出我们需要多少 pods 才可以支撑住目前的流量。
观察你的 services 在负载下是如何工作的,以及他们需要多少计算量。
同样,花些时间来理解 Kubernetes 的服务质量等级,这样你就可以更好地控制哪一个 pod 在内存压力下会被终止掉。如果你跟我们一样,跟所有环境都分享了同一个集群,那么这一点对你来说至关重要。
提示:启动 Cross-Zone Load Balabcing(AWS)
这一点在 Kubernetes1.4 中已经修改,但是现在,如果你通过 LoadBalancer type 来暴露你的服务,不要忘记为相应的 ELB 手动启动 cross-zone load balancing;如果你没有这么做,你应该会发现分布在不同的可用域当中的应用 pod 的负载不均衡。
提示: 多了解 kube-system
如果你曾经尝试用过 Kubernetes,你可能就会注意到 kube-system namespace 上有非常多的东西;花点时间来理解它们的意义。
比如,拿 DNS add-on 来说;经常能看到人们遇到 DNS 问题,因为他们忘记了随着负载增长而要增加更多的 DNS Pod。
上线
不要立即转换所有的流量,就如同我们在预生产环境中所做的,我们想的是,需要采取一个更加仔细的方法,用加权路由规则,慢慢转换流量到 Kubernetes 集群。
一旦我们注意到已经没有请求可以到达遗留的 Beanstalk 环境了,我们就继续向前,并且终止他们。
2016,9月21日更新内容:
所有主要的服务都已经被迁移到我们新的平台啦!
以下是最终数字:
每月减少约53-63%开销
实例的数量减少72-82%
生产环境之外
Kubernetes 可以用毫不费力的方式塑造交付管道,这是我们想都不敢想的体验。
我们的开发环境就是这样的一个例子。
无论什么时候有人打开了 Pull Request 给我们一个项目,我之前提到的 AWS Lambda 功能就会创建一个临时环境运行 PR 当中修改后的代码。
同样,无论什么时候 push 新的代码,一旦他们通过测试,这个环境就会自动更新。最后,当 PR 被合并(或者关闭),环境也就被删除了。
这个功能使得我们的代码审查更加彻底,因为开发人员能够看到修改的东西在运行。这对于前端服务的 UX 来说就更有价值了;设计师和产品经理有机会看到修改生效,也可以在 PR 合并之前验证修改和分享想法。为了要发送 Github Status 通知,可以看到这些图片中,我们用 Go 实现了一个守护进程,来监控到开发环境的部署并不断更新各个版本的部署状态。
结论
Kubernetes 是一款很复杂的软件,旨在解决复杂的问题,所以在把它用到你的项目之前要先花点时间了解一下它的那些功能是如何有条理地运行在一起的。
Kubernetes 是为生产准备的,所以要避免受到诱惑,不要尝试在它上面运行所有的东西。在我们的经验里,Kubernetes 并没有给可能遇到的一系列问题提供简单有效的统一方法,比如说有状态应用。
其实文档也不怎么样,但是比如 Kubernetes Bootcamp 以及 Kelsey Hightower 的《Kubernetes The HardWay》给了我希望,相信在不远的将来这些都将不是问题。
如果没有 Kubernetes,我不知道我们这个小团队要怎么在这么短的时间内解决这么多的问题。我们希望继续在 Kubernetes 上工作,确保交付平台更加有活力,更赞!
原文链接
转载请联系我们 -3-
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。