Tinder 过渡到 Kubernetes

笔记。 翻译。:世界著名的 Tinder 服务的员工最近分享了一些将其基础设施迁移到 Kubernetes 的技术细节。 这个过程花了近两年时间,最终在 K8s 上推出了一个非常大规模的平台,其中包含托管在 200 个容器上的 48 项服务。 Tinder 工程师遇到了哪些有趣的困难以及他们取得了哪些成果?请阅读此翻译。

Tinder 过渡到 Kubernetes

Зачем?

大约两年前,Tinder 决定将其平台迁移到 Kubernetes。 Kubernetes 将允许 Tinder 团队通过不可变部署以最小的努力进行容器化并转移到生产环境 (不可变部署)。 在这种情况下,应用程序的组装、它们的部署以及基础设施本身将由代码唯一定义。

我们也在寻找可扩展性和稳定性问题的解决方案。 当扩展变得至关重要时,我们通常不得不等待几分钟才能让新的 EC2 实例启动。 启动容器并在几秒钟而不是几分钟内开始提供流量的想法对我们非常有吸引力。

结果证明这个过程很困难。 在 2019 年初的迁移过程中,Kubernetes 集群达到了临界量,我们开始遇到由于流量、集群大小和 DNS 等方面的各种问题。 在此过程中,我们解决了许多有趣的问题,涉及迁移 200 个服务和维护由 1000 个节点、15000 个 Pod 和 48000 个正在运行的容器组成的 Kubernetes 集群。

怎么样?

自2018年XNUMX月以来,我们经历了迁移的各个阶段。 我们首先将所有服务容器化并将其部署到 Kubernetes 测试云环境。 从 XNUMX 月份开始,我们开始有条不紊地将所有现有服务迁移到 Kubernetes。 到第二年 XNUMX 月,我们完成了迁移,现在 Tinder 平台仅在 Kubernetes 上运行。

为 Kubernetes 构建镜像

我们有超过 30 个用于在 Kubernetes 集群上运行的微服务的源代码存储库。 这些存储库中的代码是用不同的语言(例如 Node.js、Java、Scala、Go)编写的,具有同一语言的多个运行时环境。

构建系统旨在为每个微服务提供完全可定制的“构建上下文”。 它通常由 Dockerfile 和 shell 命令列表组成。 它们的内容是完全可定制的,同时,所有这些构建上下文都是按照标准化格式编写的。 标准化构建上下文允许一个构建系统处理所有微服务。

Tinder 过渡到 Kubernetes
图 1-1。 通过 Builder 容器标准化构建流程

实现运行时之间的最大一致性 (运行时环境) 开发和测试期间使用相同的构建过程。 我们面临一个非常有趣的挑战:我们必须开发一种方法来确保整个平台上构建环境的一致性。 为了实现这一目标,所有组装过程都在特殊容器内进行。 生成器.

他的容器实现需要先进的 Docker 技术。 Builder 继承访问私有 Tinder 存储库所需的本地用户 ID 和机密(例如 SSH 密钥、AWS 凭证等)。 它挂载包含源的本地目录以自然地存储构建工件。 这种方法提高了性能,因为它消除了在 Builder 容器和主机之间复制构建工件的需要。 存储的构建工件可以重复使用,无需额外配置。

对于某些服务,我们必须创建另一个容器来将编译环境映射到运行时环境(例如,Node.js bcrypt 库在安装过程中生成特定于平台的二进制工件)。 在编译过程中,不同服务的要求可能有所不同,最终的 Dockerfile 是动态编译的。

Kubernetes集群架构及迁移

集群规模管理

我们决定使用 kube-aws 用于 Amazon EC2 实例上的自动化集群部署。 一开始,一切都在一个公共节点池中运行。 我们很快意识到需要按大小和实例类型分离工作负载,以更有效地利用资源。 其逻辑是,运行多个已加载的多线程 Pod 在性能方面比与大量单线程 Pod 共存更具可预测性。

最终我们决定:

  • m5.4x大 — 用于监控(普罗米修斯);
  • c5.4xlarge - 用于 Node.js 工作负载(单线程工作负载);
  • c5.2xlarge - 适用于 Java 和 Go(多线程工作负载);
  • c5.4xlarge — 用于控制面板(3 个节点)。

迁移

从旧基础设施迁移到 Kubernetes 的准备步骤之一是将服务之间现有的直接通信重定向到新的负载均衡器(弹性负载均衡器(ELB))。 它们是在 Virtual Private Cloud (VPC) 的特定子网上创建的。 该子网已连接到 Kubernetes VPC。 这使我们能够逐步迁移模块,而无需考虑服务依赖关系的具体顺序。

这些端点是使用 DNS 记录的加权集创建的,这些记录的 CNAME 指向每个新的 ELB。 为了进行切换,我们添加了一个新条目,指向 Kubernetes 服务的新 ELB,权重为 0。然后,我们将条目的生存时间 (TTL) 设置为 0。之后,新旧权重分别为:慢慢调整,最终100%的负载被发送到新的服务器上。 切换完成后,TTL值恢复到比较合适的水平。

我们拥有的 Java 模块可以处理低 TTL DNS,但 Node 应用程序不能。 一位工程师重写了部分连接池代码,并将其包装在一个管理器中,该管理器每 60 秒更新一次池。 所选择的方法效果非常好,并且没有任何明显的性能下降。

经验教训

网络结构的局限性

8年2019月XNUMX日凌晨,Tinder平台意外崩溃。 为了响应当天早上早些时候平台延迟的无关增加,集群中的 Pod 和节点数量增加了。 这导致我们所有节点上的 ARP 缓存耗尽。

Linux 有 XNUMX 个与 ARP 缓存相关的选项:

Tinder 过渡到 Kubernetes
()

GC_thresh3 - 这是一个硬限制。 日志中出现“邻居表溢出”条目意味着即使在同步垃圾收集(GC)之后,ARP 缓存中也没有足够的空间来存储邻居条目。 在这种情况下,内核只是完全丢弃该数据包。

我们用 绒布 作为 Kubernetes 中的网络结构。 报文通过VXLAN传输。 VXLAN 是建立在 L2 网络之上的 L3 隧道。 该技术采用MAC-in-UDP(MAC地址在用户数据报协议)封装,并允许扩展第2层网段。 物理数据中心网络的传输协议是IP+UDP。

Tinder 过渡到 Kubernetes
图 2-1。 法兰绒图()

Tinder 过渡到 Kubernetes
图 2-2。 VXLAN 包()

每个 Kubernetes 工作节点从较大的 /24 块中分配一个具有 /9 掩码的虚拟地址空间。 对于每个节点,这是 手段 路由表中的一项、ARP 表中的一项(在 flannel.1 接口上)以及交换表 (FDB) 中的一项。 它们在第一次启动工作节点或每次发现新节点时添加。

此外,节点-pod(或pod-pod)通信最终通过接口 eth0 (如上面的Flannel图所示)。 这会在 ARP 表中为每个相应的源主机和目标主机生成一个附加条目。

在我们的环境中,这种类型的通信非常常见。 对于 Kubernetes 中的服务对象,会创建一个 ELB,并且 Kubernetes 将每个节点注册到 ELB。 ELB 对 Pod 一无所知,所选节点可能不是数据包的最终目的地。 要点是,当节点从 ELB 收到数据包时,它会考虑规则 iptables的 对于特定服务并随机选择另一个节点上的 Pod。

故障发生时,集群中有 605 个节点。 由于上述原因,这足以克服其重要性 GC_thresh3,这是默认值。 当这种情况发生时,不仅数据包开始被丢弃,而且带有 /24 掩码的整个 Flannel 虚拟地址空间也会从 ARP 表中消失。 节点-pod 通信和 DNS 查询中断(DNS 托管在集群中;请阅读本文后面的内容了解详细信息)。

要解决这个问题,需要增加值 GC_thresh1, GC_thresh2 и GC_thresh3 并重新启动 Flannel 以重新注册丢失的网络。

意外的 DNS 扩展

在迁移过程中,我们积极使用DNS来管理流量,并逐步将服务从旧基础设施转移到Kubernetes。 我们在Route53中为关联的RecordSet设置了相对较低的TTL值。 当旧基础设施在 EC2 实例上运行时,我们的解析器配置指向 Amazon DNS。 我们认为这是理所当然的,低 TTL 对我们的服务和 Amazon 服务(例如 DynamoDB)的影响基本上没有被注意到。

当我们将服务迁移到 Kubernetes 时,我们发现 DNS 每秒处理 250 万个请求。 结果,应用程序开始遇到持续且严重的 DNS 查询超时问题。 尽管付出了令人难以置信的努力来优化 DNS 提供商并将其切换为 CoreDNS(在峰值负载下达到了在 1000 个内核上运行的 120 个 Pod),但这种情况还是发生了。

在研究其他可能的原因和解决方案时,我们发现 文章,描述影响数据包过滤框架的竞争条件 网络过滤器 在Linux中。 我们观察到的超时以及不断增加的计数器 插入失败 Flannel 界面中的内容与文章的研究结果一致。

问题发生在源和目标网络地址转换(SNAT 和 DNAT)以及随后进入表的阶段 连接跟踪。 内部讨论和社区建议的解决方法之一是将 DNS 移至工作节点本身。 在这种情况下:

  • 不需要 SNAT,因为流量保留在节点内部。 不需要通过接口路由 eth0.
  • 不需要 DNAT,因为目标 IP 是节点本地的,而不是根据规则随机选择的 pod iptables的.

我们决定坚持这种方法。 CoreDNS 在 Kubernetes 中部署为 DaemonSet,我们在 配置文件 每个 pod 通过设置一个标志 --集群 DNS 命令 库贝莱 。 事实证明,该解决方案对于 DNS 超时非常有效。

然而,我们仍然看到数据包丢失和计数器增加 插入失败 在法兰绒界面中。 在实施解决方法后,这种情况仍在继续,因为我们能够仅消除 DNS 流量的 SNAT 和/或 DNAT。 为其他类型的流量保留了竞争条件。 幸运的是,我们的大多数数据包都是 TCP,如果出现问题,它们只需重新传输即可。 我们仍在努力寻找适合所有类型流量的解决方案。

使用 Envoy 实现更好的负载平衡

当我们将后端服务迁移到 Kubernetes 时,我们开始遇到 Pod 之间负载不平衡的问题。 我们发现 HTTP Keepalive 导致 ELB 连接在每个部署的第一个就绪 Pod 上挂起。 因此,大部分流量通过一小部分可用 Pod。 我们测试的第一个解决方案是将新部署上的 MaxSurge 设置为 100%,以应对最坏的情况。 事实证明,对于更大规模的部署来说,效果是微不足道的,而且没有希望。

我们使用的另一个解决方案是人为地增加关键服务的资源请求。 在这种情况下,与其他重型吊舱相比,放置在附近的吊舱将有更多的操作空间。 从长远来看,这也是行不通的,因为这会浪费资源。 此外,我们的 Node 应用程序是单线程的,因此只能使用一个核心。 唯一真正的解决方案是使用更好的负载平衡。

我们很早就想充分欣赏 使者。 目前的情况允许我们以非常有限的方式部署它并立即得到结果。 Envoy 是一款高性能、开源、第 XNUMX 层代理,专为大型 SOA 应用程序而设计。 它可以实现先进的负载平衡技术,包括自动重试、断路器和全局速率限制。 (笔记。 翻译。:您可以阅读更多相关内容 本文 关于 Istio,它基于 Envoy。)

我们提出了以下配置:为每个 pod 提供一个 Envoy sidecar 和一条路由,并通过端口将集群本地连接到容器。 为了最大限度地减少潜在的级联并保持较小的命中半径,我们使用了一组 Envoy 前端代理 Pod,每个服务的每个可用区 (AZ) 一个。 他们依赖于我们的一位工程师编写的简单服务发现引擎,该引擎只是返回给定服务的每个可用区中的 Pod 列表。

然后,服务前端特使将这种服务发现机制与一个上游集群和路由结合使用。 我们设置了足够的超时,增加了所有断路器设置,并添加了最小重试配置,以帮助解决单一故障并确保顺利部署。 我们在每个服务前端 Envoy 前面放置了一个 TCP ELB。 即使我们主代理层的 keepalive 被困在某些 Envoy Pod 上,它们仍然能够更好地处理负载,并配置为通过后端的 less_request 进行平衡。

对于部署,我们在应用程序 pod 和 sidecar pod 上使用了 preStop 挂钩。 该钩子在检查位于 sidecar 容器上的管理端点的状态时触发了错误,并进入休眠状态一段时间以允许终止活动连接。

我们能够如此迅速地采取行动的原因之一是我们能够轻松地将详细指标集成到典型的 Prometheus 安装中。 这使我们能够准确地看到在调整配置参数和重新分配流量时发生的情况。

结果立竿见影、显而易见。 我们从最不平衡的服务开始,目前它运行在集群中 12 个最重要的服务之前。 今年,我们计划过渡到具有更先进的服务发现、熔断、异常检测、速率限制和跟踪的完整服务网格。

Tinder 过渡到 Kubernetes
图 3-1。 过渡到 Envoy 期间一项服务的 CPU 融合

Tinder 过渡到 Kubernetes

Tinder 过渡到 Kubernetes

最终结果

通过这些经验和额外的研究,我们建立了一支强大的基础设施团队,在设计、部署和运营大型 Kubernetes 集群方面拥有强大的技能。 所有 Tinder 工程师现在都具备打包容器并将应用程序部署到 Kubernetes 的知识和经验。

当旧基础设施需要额外容量时,我们必须等待几分钟才能启动新的 EC2 实例。 现在,容器开始运行并在几秒钟(而不是几分钟)内开始处理流量。 在单个 EC2 实例上调度多个容器还可以提高水平集中度。 因此,我们预计 2019 年 EC2 成本将比去年大幅下降。

迁移花了近两年的时间,但我们在 2019 年 200 月完成了。 目前,Tinder 平台仅在 Kubernetes 集群上运行,该集群由 1000 个服务、15 个节点、000 个 Pod 和 48 个正在运行的容器组成。 基础设施不再是运营团队的唯一领域。 我们所有的工程师都共同承担这一责任,并仅使用代码来控制构建和部署应用程序的过程。

译者PS

另请阅读我们博客上的一系列文章:

来源: habr.com

添加评论