我们在Kubernetes上使用GitLab.com一年后学到了什么

2020-09-17 06:28:12

大约一年来,基础设施部门一直致力于将GitLab.com上运行的所有服务迁移到Kubernetes。这项工作并不是没有挑战,不仅是将服务转移到Kubernetes,而且在过渡期间管理混合部署。在这个过程中,我们学到了一些教训,我们将在这篇文章中探讨这些教训。

从GitLab.com一开始,网站的服务器就运行在虚拟机上的云中。这些虚拟机由Chef管理,并使用我们的官方Linux软件包进行安装。当需要更新应用程序时,我们的部署策略是使用CI流水线以协调滚动的方式简单地升级服务器群。这种方法虽然缓慢且有点乏味,确保GitLab.com与我们使用Linux软件包的自我管理客户使用相同的安装方法和配置。我们使用此方法是因为GitLab.com也能感受到社区在安装或配置自我管理GitLab时感受到的任何痛苦或喜悦,这一点尤为重要。这种方法一度对我们很有效,但随着GitLab.com已发展到托管1000多万个项目,我们意识到它不再能满足我们的扩展和部署需求。

我们在2017年创建了GitLab图表项目,为部署在云中的GitLab做好准备,并支持自我管理的用户将GitLab安装到Kubernetes集群中。那时我们就知道,在Kubernetes上运行GitLab.com将使SaaS平台在扩展、部署和高效使用计算资源方面受益。不过,当时仍有许多依赖于NFS挂载的应用程序功能延迟了我们从VM的迁移。

对云原生和Kubernetes的推动让工程人员有机会规划逐步过渡,在继续开发新功能的同时消除一些对应用程序的网络存储依赖。自从我们在2019年夏天开始规划迁移以来,这些限制中的大部分都已经解决了,在Kubernetes上运行所有GitLab.com的旅程现在正在顺利进行!

对于GitLab.com,我们使用单个地区性GKE集群来服务所有应用程序流量。为了最大限度地降低迁移(已经很复杂)的复杂性,我们将重点放在不依赖本地存储或NFS的服务上。虽然GitLab.com主要运行在单一的Rails代码库上,但是我们根据工作负载特征将流量路由到不同的端点,这些端点被隔离到它们自己的节点池中。

在前端,这些类型分为Web、API、GIT SSH/HTTPS请求和注册表。在后端,我们根据预定义的资源边界将排队的作业划分为不同的特征,这些资源边界允许我们为一系列不同的工作负载设置服务级别目标(SLO)目标。

所有这些GitLab.com服务都使用未经修改的GitLab Helm图表进行配置,该图表在子图表中配置它们,当我们逐渐将服务迁移到群集时,可以有选择地启用这些子图表。虽然我们选择不包括Redis、Postgres、GitLab Pages和Gitaly等一些有状态服务,但当迁移到Kubernetes完成时,它将极大地减少我们当前使用Chef管理的VM数量。

所有配置都使用Terraform和Helm在GitLab本身的三个配置项目中进行管理。当我们尽可能使用GitLab运行GitLab时,我们维护单独的GitLab安装以进行操作。这样做是为了确保我们不会依赖GitLab.com的可用性来部署和升级GitLab.com。

尽管我们针对Kubernetes集群执行的管道运行在这个单独的GitLab部署上,但是代码存储库是镜像的,并且可以在以下位置公开查看:

K8s-工作负载/GitLab-helmfiles:包含与GitLab应用程序没有直接关系的服务的配置。这包括集群日志和监控的配置,以及PlantUML等集成。

GitLab-COM-Infrastructure:Kubernetes和遗留VM基础设施的TerraForm配置。在此配置运行群集所需的所有资源,包括群集、节点池、服务帐户和IP地址保留。

每当建议更改时,都会显示一个公共简短摘要,其中包含一个指向详细差异的链接,SRE在将更改应用到群集之前会查看该差异。

对于SRE,我们链接到具有有限访问权限的运营GitLab实例的详细差异。这允许员工和社区查看建议的配置更改,这些员工和社区无权访问仅限于SRE的运营项目。通过拥有代码的公共GitLab实例和CI管道的私有实例,我们能够保留单个工作流,同时确保我们不会依赖GitLab.com进行配置更新。

在这一过程中,我们学到了一些东西,我们正在应用于未来的迁移和到Kubernetes的新部署。

Google将其网络划分为区域,并将区域划分为可用区(AZ),由于Git托管需要大量的带宽,因此了解网络出口是非常重要的。对于内部网络流量,只有保留在单个AZ中的出口才是免费的。在撰写本博客文章时,我们仅为Git存储库在一个典型工作日提供大约100TB的容量。在旧式VM拓扑上,以前并置在相同VM上的服务现在在Kubernetes Pod中运行。这意味着以前在VM本地的一些网络流量现在可能会穿越可用区。

地域GKE集群提供了跨越多个可用区进行冗余的便利,我们正在考虑将地域GKE集群拆分成单个分区集群,用于使用大量带宽的服务,以避免网络出口费用,同时保持集群级别的冗余。

为registry.gitlab.com上的生产流量提供服务的副本数量,注册表流量在格林威治标准时间约15:00时达到峰值。

我们的迁移故事始于2019年8月,当时我们将GitLab Container Registry迁移到Kubernetes,这是第一个迁移的服务。虽然这是一个关键且高流量的服务,但它是第一次迁移的好选择,因为它是一个只有几个外部依赖的无状态应用程序。我们遇到的第一个挑战是,由于节点的内存限制,我们遇到了大量被驱逐的Pod。这需要对请求和限制进行多次更改。我们发现,对于随时间增加内存利用率的应用程序,低请求(为每个Pod保留内存)和慷慨的硬限制利用率是导致节点饱和和高清除率的原因。为此,我们最终决定使用更高的请求和更低的限制,这减轻了节点的压力,并允许回收Pod,而不会给节点带来太大的压力。在经历了一次这种情况后,我们使用接近相同值的慷慨请求和限制开始迁移,并根据需要向下调整。

基础设施部门重点关注延迟、错误率和饱和度,这些指标与我们的整体系统可用性相关的服务级别目标(SLO)。

在过去的一年中,基础架构部门的主要变化之一是改进了我们监视和管理SLO的方式。SLO使我们能够为迁移期间密切监视的单个服务设置目标。然而,即使提高了可观察性,我们也不能总是立即发现指标报告和警报方面的问题。例如,关注延迟和错误率可能不足以覆盖要迁移的服务的所有使用情况。我们很早就发现了这个问题,有些工作负载移到了群集中。当我们必须验证那些不会收到很多请求但具有非常具体的配置依赖项的功能时,这一挑战尤为严峻。关键的迁移经验之一是不仅要评估监控指标,还要评估日志和监控中的错误长尾。现在,对于每个迁移,我们都包括详细的日志查询列表,并计划清晰的回滚过程,以便在出现问题时可以从一个班次移交到下一个班次。

同时在旧式VM基础架构和Kubernetes上服务相同的请求带来了独特的挑战。与升降式迁移不同,同时在旧式VM和Kubernetes上运行要求我们的可观察性与两者兼容,并将指标合并到一个视图中。最重要的是,我们使用相同的控制面板和日志查询来确保过渡期间可观察性的一致性。

对于GitLab.com,我们维护一个名为金丝雀阶段的舰队细分。这个金丝雀舰队为我们的内部项目服务,或者可以由用户启用,首先部署用于基础架构和应用程序更改。我们迁移的第一个服务从内部接收有限的流量开始,我们将继续使用此方法来确保在将所有流量提交到群集之前满足SLO。这意味着迁移到内部项目的请求首先路由到Kubernetes,然后我们使用HAProxy后端加权慢慢将其他流量移动到群集。我们在从虚拟机迁移到Kubernetes的过程中了解到,在虚拟机和Kubernetes之间轻松移动流量对我们非常有益。

我们很早就发现了一个问题,虽然我们的注册表服务的Pod启动时间非常短,但Sidekiq的启动时间长达两分钟。当我们开始将工作负载移动到Kubernetes时,对于需要快速处理作业和快速扩展的员工来说,Sidekiq的启动时间很长,这给我们带来了挑战。这里的教训是,虽然水平Pod Autoscaler(HPA)在Kubernetes中能够很好地适应不断增加的流量,但评估工作负载特征并设置保留的Pod容量也很重要,特别是在需求不均匀的情况下。我们看到作业突然激增,导致大量扩展事件,导致在我们可以扩展节点池之前使CPU饱和。虽然我们很容易从群集中挤出尽可能多的空间,但在经历了一些初始性能问题之后,我们现在开始时会从慷慨的Pod预算开始,稍后会缩减规模,同时密切关注SLO。Sidekiq服务的Pod启动时间已显著改善,现在平均约为40秒。改进吊舱开始时间使GitLab.com和所有使用官方GitLab Helm图表的自我管理客户受益。

在转换每个服务之后,我们享受到了在生产中使用Kubernetes的许多好处,包括更快、更安全的部署

我们希望您喜欢阅读我们的库伯内斯移民之旅。随着我们继续将更多服务迁移到群集,您可以通过以下链接了解更多信息:

注册GitLab每月两次的时事通讯,探索即将到来的网络广播、操作博客,并随时了解每月发布的令人兴奋的新功能:

Git是软件自由保护协会的商标,我们对GitLab的使用正在获得许可