比特移动如何使用Kubernetes在云端和本地进行多阶段Canary部署

编者按:今天的帖子由Bitmovin的基础架构架构师Daniel Hoelbling-Inzko撰写,该公司提供将数字视频和音频转码为流格式的服务,分享有关使用Kubernetes的见解。

在多个公共云上运行大规模视频编码基础架构非常困难。在 比特移动,过去几年来我们一直在成功地做到这一点,但是从工程学的角度来看,它既不愉快也不有趣。

显然,使用Kubernetes真正使我们成功的主要因素之一是,它来自不同受支持的云提供商的通用抽象以及它提供的经过深思熟虑的编程接口。更重要的是,Kubernetes项目并没有采用最低公分母方法。相反,他们添加了在云中运行容器化工作负载所必需和有用的必要抽象概念,然后进行了所有艰苦的工作,以将这些概念映射到不同的云提供商及其产品。

我们在2016年中期的早期测试中看到了出色的稳定性,速度和操作可靠性,这使向Kubernetes的迁移变得轻而易举。

And, it didn’t hurt that the vision for scale the Kubernetes project has been pursuing is closely aligned with our own goals as a company. Aiming for >1,000 node clusters might be a lofty goal, but for a fast growing video company like ours, having your infrastructure aim to support future growth is essential. Also, after initial brainstorming for our new infrastructure, we immediately knew that we would be running a huge number of containers and having a system, with the expressed goal of working at global scale, was the perfect fit for us. Now with the recent Kubernetes 1.6 发布及其 支持5,000个节点集群,我们在选择容器编排系统时感到更加认可。

在使我们的基础架构在Kubernetes上运行的测试和迁移阶段,我们非常熟悉Kubernetes API及其周围的整个生态系统。因此,当我们在考虑扩展云视频编码产品以供客户在自己的数据中心或云环境中使用时,我们很快决定利用Kubernetes作为我们无处不在的云操作系统,以此作为该解决方案的基础。

仅仅几个月后,这种努力就成为了我们最新的服务产品: 比特移动托管本地编码。由于所有Kubernetes集群都共享相同的API,因此使我们的云编码服务也可以在Kubernetes上运行,使我们能够部署到客户的数据中心中,而无需考虑其下面的硬件基础架构如何。借助社区中的出色工具,例如kube-up和交钥匙解决方案(例如Google Container Engine),任何人都可以在自己的基础架构中或在自己的云帐户中轻松配置新的Kubernetes集群。

为了为部署到裸机且可能尚未为Kubernetes进行任何自定义云集成的客户提供最大的灵活性,我们决定将解决方案仅基于任何Kubernetes安装中都可用的设施,并且不需要任何与Kubernetes安装的集成。周围的基础设施(甚至可以在内部运行 迷你库!)。我们不依赖于LoadBalancer类型的服务,这主要是因为企业IT部门通常不愿意为开放的Internet开放端口-并非所有安装Kubernetes的裸机都支持开箱即用的外部负载均衡器。为了避免这些问题,我们部署了一个在群集内运行的BitmovinAgent,并在不进行任何网络设置的情况下轮询API进行新的编码作业。然后,该代理使用本地可用的Kubernetes凭据启动新的部署,这些部署通过Kubernetes API在可用的硬件上运行编码器。

即使没有可用的完整云集成,使用Kubernetes API所获得的一致的调度,运行状况检查和监控也确实使我们能够专注于使编码器在容器内工作,而不是花费宝贵的工程资源来集成大量不同的虚拟机管理程序,机器预配器和监视系统。

多阶段金丝雀部署

我们与Kubernetes API的首次接触不是针对本地编码产品。在看到在我们的Bitmovin API基础架构的开发和推出过程中Kubernetes平台被证明多么简单和强大之后,我们在Kubernetes上构建容器化编码工作流是一个决定。我们大约在四个月前迁移到Kubernetes,这使我们能够为我们的服务提供快速的开发迭代,同时满足我们的无停机部署和对生产管道稳定开发的要求。为了实现这一目标,我们提出了一种架构,该架构可以运行近一千个容器,并满足我们在第一天提出的以下要求:

  1. 1.为客户零停机时间部署
  2. 2.在每次git主线推送中持续部署到生产
  3. 3,客户部署服务的高稳定性

显然,如果将每个合并的功能立即部署到生产中,则#2和#3彼此矛盾-我们如何确保这些版本没有错误,并且不会对客户产生不利的副作用?

为了克服这种矛盾,我们为每个微服务提出了一个四阶段的Canary管道,我们在将其同时部署到生产环境并保持更改远离客户,直到证明新版本在生产环境中可靠且正确地工作为止。

推送新版本后,我们将其部署到内部阶段,该阶段只能用于内部测试和集成测试套件。内部测试套件通过后,质量检查不会报告任何问题,而且我们也没有发现任何异常行为,因此将新版本推向了免费阶段。这意味着我们有5%的免费用户会被随机分配到这个新版本中。在此阶段的一段时间后,该版本将升级到下一阶段,该阶段会将5%的付费用户路由到该阶段。仅当构建成功通过所有这三个障碍后,它才会部署到生产层,在该层将接收来自其余用户以及企业客户的所有流量,这些流量不属于付费用户,并且永远不会他们的流量被路由到金丝雀轨道。

默认情况下,此设置使我们成为一个很大的Kubernetes安装,因为我们所有的canary层都可用最小复制2来提供。由于我们目前正在集群中部署约30个微服务(并且正在不断增长),因此加起来最少每个服务10个Pod(8个应用程序Pod +至少2个执行Canary路由的HAProxy Pod)。尽管实际上,我们首选的标准配置通常运行2个内部,4个免费,4个其他和10个生产吊舱以及4个HAProxy吊舱-总共约700个吊舱。这也意味着我们正在运行至少150个服务,这些服务为其基础的微服务canary层提供静态ClusterIP。

典型的部署如下所示:

|服务(ClusterIP)|部署| #Pods | |客户服务|帐户服务代理| 4 | |内部帐户服务|帐户服务内部v1.18.0 | 2 | |帐户服务金丝雀| account-service-canary-v1.17.0 | 4 | |帐户服务付费|帐户服务付费v1.15.0 | 4 | |客户服务生产|帐户服务生产-v1.15.0 | 10 |

生产跟踪的示例服务定义将具有以下标签选择器:

apiVersion: v1

kind: Service

metadata:

 name: account-service-production

 labels:

 app: account-service-production

 tier: service

 lb: private

spec:

 ports:

 - port: 8080

 name: http

 targetPort: 8080

 protocol: TCP

 selector:

 app: account-service

 tier: service

 track: production

在Kubernetes服务的前面,负载均衡了该服务的不同金丝雀版本,并存在一个小的HAProxy Pod集群,该集群从Kubernetes获取其haproxy.conf。 ConfigMaps 看起来像这样:

frontend http-in

 bind \*:80

 log 127.0.0.1 local2 debug


 acl traffic\_internal hdr(X-Traffic-Group) -m str -i INTERNAL

 acl traffic\_free  hdr(X-Traffic-Group) -m str -i FREE

 acl traffic\_enterprise hdr(X-Traffic-Group) -m str -i ENTERPRISE


 use\_backend internal if traffic\_internal

 use\_backend canary if traffic\_free

 use\_backend enterprise if traffic\_enterprise


 default\_backend paid


backend internal

 balance roundrobin

 server internal-lb  user-resource-service-internal:8080 resolvers dns check inter 2000

backend canary

 balance roundrobin

 server canary-lb    user-resource-service-canary:8080 resolvers dns check inter 2000 weight 5

 server production-lb user-resource-service-production:8080 resolvers dns check inter 2000 weight 95

backend paid

 balance roundrobin

 server canary-paid-lb user-resource-service-paid:8080 resolvers dns check inter 2000 weight 5

 server production-lb user-resource-service-production:8080 resolvers dns check inter 2000 weight 95

backend enterprise

 balance roundrobin

 server production-lb user-resource-service-production:8080 resolvers dns check inter 2000 weight 100

每个HAProxy将检查由我们的API网关分配的标头,该标头称为X-Traffic-Group,该标头确定此请求属于哪个客户群。基于此,决定选择金丝雀部署还是生产部署。

显然,在这样的规模下,kubectl(虽然仍然是我们在集群上运行的主要日常工具)并不能真正让我们很好地了解一切是否按照预期运行,以及是否超出预期范围。复制。

由于我们进行的是蓝色/绿色部署,因此有时有时会忘记在新版本启动后关闭旧版本,因此某些服务可能正在复制中运行,而在kubectl中列出的25个部署中发现这些问题并非易事,至少可以说。

因此,拥有像Kubernetes这样的容器协调器,这是非常受API驱动的,对于我们来说确实是一个天赐之礼,因为它使我们能够编写解决这个问题的工具。

我们构建了可以直接在kubectl上运行的工具(例如bash脚本),或者可以直接与API交互,并且了解我们的特殊架构,从而可以快速概览系统。这些工具大多是使用 客户去 图书馆。

这些工具之一值得重点介绍,因为它基本上是我们一目了然地真正了解服务运行状况的唯一方法。它遍历我们所有具有tier:服务选择器的Kubernetes服务,并检查随附的HAProxy部署是否可用以及所有Pod是否都使用4个副本运行。它还检查HAProxy背后的4个服务(内部,免费,其他和生产)是否至少有2个端点正在运行。如果不满足这些条件中的任何一个,我们会立即通过Slack和电子邮件发送通知。

事实证明,使用我们以前的协调器管理这么多吊舱非常不可靠,并且覆盖网络经常会引起问题。 Kubernetes并非如此-即使将目前的工作量加倍以进行测试也可以正常工作,并且总的来说,自从我们安装集群以来,集群就一直像发条一样工作。

切换到Kubernetes的另一个优势是,除了API(我们用来编写一些内部部署工具)之外,还提供了kubernetes资源规范。这使我们能够拥有所有Kubernetes规范的Git存储库,其中的每个轨道都是通过通用模板生成的,并且仅包含诸如金丝雀轨道和名称之类的可变内容的占位符。

对集群的所有更改都必须通过工具来修改这些资源规范,并自动将其检入git,因此,每当我们发现问题时,我们都可以调试基础架构随着时间的变化!

总结一下这篇文章-通过将我们的基础架构迁移到Kubernetes,Bitmovin能够拥有:

  • 零停机时间部署,使我们的客户能够不间断地进行24/7编码
  • 快速开发到生产周期,使我们能够更快地发布新功能
  • 多层次的质量保证和对生产部署的高度信心
  • 跨云架构和本地部署的无处不在抽象
  • 稳定可靠的健康检查和服务调度
  • 围绕我们的基础架构的自定义工具,用于检查和验证系统
  • 部署的历史记录(git +自定义工具中的资源规范)

我们要感谢Kubernetes社区在该项目中所做的出色工作。项目移动的速度令人叹为观止!在如此多样化的环境中保持如此高的质量和耐用性确实令人惊讶。

--Daniel Hoelbling-Inzko,Bitmovin基础架构架构师