首页 >> 大全

如何成功构建大规模 Web 搜索引擎架构

2022-08-13 大全 126 作者:考证青年

制作 | CSDN (ID:)

以下为译文:

在本文中,我们将系统地介绍我们经过多年迭代的私人搜索产品,以满足外部和内部用户的需求。

我们结合使用经过严格测试的知名开源和云原生技术。对于我们没有从开源或商业系统中找到解决方案的领域,我们不得不从头开始自己挖掘和编写系统。这种方式非常适合我们目前的规模。

免责声明:本文仅描述系统的当前状态。当然,原始系统并非如此。多年来,我们采用了多种架构,并不断考虑成本、流量和数据大小等限制因素。但这篇文章并不是构建搜索引擎的指南,只是我们目前正在使用的系统, 曾经说过:

“过早的优化是万恶之源。”

我们完全同意这种说法。真心劝大家不要一次把所有的食材都扔进锅里。但不一定要一件一件的,而是一步一个脚印,逐渐增加复杂度。

搜索引擎体验 - 下拉菜单和 SERP

Cliqz 的搜索引擎有两种不同需求的客户。

搜索提示

如何成功构建大规模 Web 搜索引擎架构?

浏览器中的 Cliqz 下拉菜单

在浏览器的地址栏中可以进行搜索,搜索结果显示在下拉菜单中。这种搜索需要的结果很少(一般是3个),但是对延迟的要求非常严格(一般在150毫秒以内),否则会影响用户体验。

搜索 SERP

如何成功构建大规模 Web 搜索引擎架构?

Cliqz 搜索引擎的结果页面

在网络上进行搜索并显示一个包含知名搜索结果的页面。在这里,搜索的深度是无限的,但与下拉菜单相比,它的延迟要求更低(略低于 1000 毫秒)。

自动和近乎实时的搜索

考虑一个查询,例如“拜仁慕尼黑”。这个查询看起来很常见,但它使用了我们系统中的几个服务。考虑到此查询的意图,用户可能希望:

研究拜仁慕尼黑足球俱乐部(在这种情况下,显示维基百科的小窗口可能有用)

您可能会注意到,这些意图远非“相关页面”摘要。这些信息不仅在语义上相关,而且在时间上也是相关的。搜索的时间敏感性对于用户体验非常重要。

为提供合理的用户体验,这些信息必须由不同的信息源提供,并近乎实时地转换为可搜索的索引。我们希望使所有模型、索引和相关文件保持最新(例如,加载的图像必须反映当前事件,并且标题和内容必须始终根据不断变化的事件进行更新)。在规模上,尽管这看起来很难,但我们坚持我们应该始终让我们的用户保持最新状态。这一理念贯穿于我们整个系统架构的基础。

Cliqz 的数据处理和服务平台采用多层架构。该架构根据内容索引的即时性分为三层,分别是:

近乎实时的索引

基于每周或滑动窗口的批次索引

全批次索引

值得指出的是,近实时索引和每周索引占 SERP 上搜索相关内容的很大一部分。其他搜索引擎也采用了类似的方法,将更多的权重放在某个主题的最新内容上,而不是历史内容上。批处理索引处理与时间无关的查询、长尾查询以及对稀有、历史或上下文要求高的内容的查询。这三者的结合给了我们足够的结果,Cliqz 搜索就是今天的样子。所有系统都能够回答所有查询,但最终结果是所有索引上的混合结果。

部署 - 历史背景

“只有当你明白什么时候不应该使用工具时,你才能真正掌握它。” ——

从一开始,我们就专注于使用云服务提供商来提供搜索服务,而不是构建我们自己的基础架构。在过去的十年里,云服务已经成为行业标准。相比自建数据中心,云服务在复杂度和资源需求方面有着巨大的优势,而且使用起来非常方便。现收现付。 AWS 对我们来说非常方便,我们不需要管理自己的机器和基础设施。如果没有 AWS,我们需要付出很多努力才能达到现在的水平。 (不过,AWS虽然方便,但也很贵。本文将介绍一些降低成本的方法,但我们建议您谨慎大规模使用云服务。)

我们通常会避免可能有用的服务,因为在我们的规模下,成本可能高得无法接受。为了便于理解,我举一个 2014 年的例子,当时我们遇到的一个日益严重的问题是如何在 AWS 上可靠地分配资源和部署应用程序。

刚开始时,我们尝试在 AWS 上构建自己的基础设施和配置管理系统。我们的做法是实现一套解决方案,让开发者更容易上手。该解决方案基于项目并与 Boto 集成,只需几行代码即可设置新服务器和配置应用程序。当时我们刚刚起步,采用传统的直接发布包或者纯文本文件的方式,依赖管理难度很大。尽管该项目受到了很多关注,并且许多产品中都使用 Cliqz 来管理服务,但基于库的基础架构和配置管理方法一直存在一些缺点。全局状态管理、基础设施变更集中锁定、无法集中查看项目或开发者使用的云资源、依赖外部工具清理孤儿资源、配置管理功能受限、难以查看开发者资源使用情况,这些问题带来的不便,比如泄露用户的环境,逐渐让操作变得越来越复杂。

因此我们决定寻找一种新的外部管理解决方案,因为我们没有资源自行开发它。我们最终确定的解决方案是使用来自 和 的解决方案组合,以及 和 Salt 等配置管理工具。

使用良好的声明性方法来定义基础架构管理,这是云原生领域的许多最新技术所采用的概念。因此,经过仔细评估,我们决定放弃我们所基于的部署库,转而采用它。除了技术上的利弊,还要考虑人的因素。一些团队接受变革的速度很慢,要么是因为缺乏资源,要么是因为团队之间的变革成本不统一。我们花了整整一年的时间才完成迁移。

我们以前没有的一些开箱即用的功能,例如:

同时,我们在使用过程中也面临一些挑战:

Cliqz 肯定在其中占有一席之地,我们今天仍然使用它来部署我们的大部分基础架构。

搜索系统的复杂性

如何成功构建大规模 Web 搜索引擎架构?

搜索系统概览

多年来,我们由数十台服务器组成的分布式架构已经迁移到单体架构,最终迁移到微服务架构。

在当时的资源条件下,我们认为每一项服务都是最方便的。例如,使用单体架构是因为绝大多数延迟是由于集群中服务器之间的网络 IO 造成的。当时 AWS 发布了具有 2TB 内存的 X1 实例。改变架构可以有效减少延迟,但成本当然会攀升。下一个架构迭代侧重于成本。我们一点一点地改变每个变量,而不影响其他因素。虽然这种方法看起来不那么漂亮,但它对我们来说非常有效。

“微服务架构风格将一个应用程序分解为一组小服务,每个服务都运行在自己的进程上,通过轻量级机制(通常是 HTTP 资源 API)与其他进程通信。” ——

p>

理论上,给出的微服务定义是正确的,但过于抽象。对我们来说,这个定义并没有说明微服务应该如何构建和分割,这就是重点。采用微服务给我们带来以下好处:

从整体架构和微服务结构来看,每当向后端发送查询请求时,请求路径上都会触发多个服务。每个服务都可以被视为微服务,因为它们具有关注点分离、使用轻量级协议 (REST/GRPC) 并且可以水平扩展。每个服务都由一系列独立的微服务组成,这些微服务可以有一个持久层。请求路径通常包括:

所有服务都编排到一个通用 API 网关,该网关处理搜索结果的大小并提供其他功能,例如防止流量激增、根据请求量/CPU/内存/自定义基准自动扩展、边缘缓存、流量模拟和细分、A/B 测试、蓝绿部署、金丝雀发布等等。

容器和容器编排系统

到目前为止,我们已经介绍了一些产品要求和一些细节。我们描述了如何部署它,以及每个选项的缺点。有了这些经验教训,我们最终选择了作为所有服务的基本组成部分。我们开始使用容器来分发代码,而不是虚拟机 + 代码 + 依赖项。这样,代码和依赖项就可以作为镜像发送到容器存储库 (ECR)。

但随着服务的不断发展,我们需要管理这些容器,尤其是在我们需要扩大生产规模的情况下。困难包括(1)浪费大量计算资源(2)基础设施复杂性(3)配置管理。

人与算力一直是稀缺资源,是很多资源有限的创业公司面临的困境。当然,为了提高效率,我们必须专注于解决现有工具无法解决的问题。但是,我们不想重新发明轮子(除非这样做有效地改变了情况)。我们非常愿意使用开源软件,它可以解决许多关键的业务问题。

1.0版发布后,我们立即开始尝试。到了1.4版本的时候,已经比较稳定,工具也比较成熟,所以我们开始在上面跑生产环境的工作负载。同时,我们还在 .最后,我们决定对所有事情都使用编排,因为有足够的证据表明,有非常有吸引力的措施可以解决编排和配置管理问题,而其他解决方案则没有。更不用说强大的社区支持了。

- Cliqz 的技术栈

如何成功构建大规模 Web 搜索引擎架构?

Cliqz 使用的开源软件

“开源软件赢得世界!”

Cliqz 依赖于许多开源软件项目,尤其是 Cloud (Cloud) 下的项目,以提供整体的云原生体验。我们通过贡献代码、博客文章和 Slack 等其他渠道,尽最大努力回馈开源社区。以下是我们技术堆栈中使用的关键开源项目:

KOPS——组织

在容器编排方面,我们使用 KOPS 和一些自研的工具来管理跨多个区域的集群,管理集群生命周期和插件等。感谢 Santa 和 kops 维护者的出色工作,k8s 控制平面和节点可以很好的结合起来。目前我们不依赖任何提供商托管服务,因为 KOPS 非常灵活,而 AWS 提供的 k8s 控制平面服务 EKS 非常不成熟。

使用 KOPS 和自我管理集群意味着我们可以按照自己的节奏进行游戏,我们可以深入研究问题,并且我们可以启用应用程序真正需要但仅存在于特定版本中的功能。如果我们依赖云服务,则需要更长的时间才能达到现状。

织网——网络覆盖

值得一提的是,系统的某些部分是可以抽象的。不仅是计算和存储,还有网络。我们的集群可能会增长到数百个节点,因此我们使用覆盖网络(我们采用 Wea​​ve Net 作为覆盖网络,因为它易于管理。随着规模的扩大,我们可能会切换到 AWS VPC CNI 和 CNI,因为它们更成熟,提供更少的网络跃点,以及更一致的路由和流量。到目前为止,Weave Net 在我们的延迟和吞吐量环境中表现良好,因此还没有理由切换。

Helm / -- 包管理和发布

我们最初依赖 helm(v2) 进行包管理和发布。尽管它有很多痛点,但我们认为它是一个出色的发布管理和模板工具。我们采用单一存储库进行存储所有服务,使用项目打包和分发。环境相关的值保存在另一个代码仓库中,用于关注点分离。这些都是通过提供的 来实现的,它提供了声明性以实现多个 helm chart 的发布管理,以及关联 diff 等重要插件,并使用 SOPS 进行秘密管理。对代码库所做的更改将通过 CI/CD 管道进行验证和部署。

Tilk/K9s - 无压力的本地开发

我们面临的一个问题是如何在开发者的开发周期中引入它。一些需求是显而易见的,那就是如何构建代码并将其同步到容器中,如何快速和良好地完成它。最初我们使用一个简单的自制解决方案,使用文件系统事件来监控源代码更改,然后 rsync 到容器。我们也尝试了一些项目,比如微软的 Draft 和微软的 Draft,试图解决同样的问题。最适合我们的是 Tilt(谢谢),产品非常好,工作流程是驱动的,文件是用语言编写的。它可以监控文件编辑、自动应用修改、自动实时构建容器镜像、使用集群构建、跳过容器存储库等来加快构建速度,并拥有一个漂亮的 UI,可以在一个面板中查看所有服务的信息。如果您想深入挖掘,我们将这些 k8s 知识开源到一个名为 k9s() 的命令行工具中,该工具可以交互地执行 k9s 命令并简化开发人员的工作流程。如今,在 k8s 上运行的所有工作负载都在集群中开发,并提供统一的一、快速体验,每个新进入者只需几个命令即可开始工作,这一切都归功于 helm/tilt/k9s。

,,, 和 Loki - 可观察性

我们所依赖的监控解决方案使用时间序列数据库 (tsdb) 来收集、统计和转发从各种服务收集的指标数据。提供了非常好的查询语言和报警服务Alert。它构成了跟踪统计程序的支柱。我们最近从 Loki 迁移了日志记录后端,以提供类似的体验。这一切都是为了为所有可观察性需求提供单一平面,我们打算通过图表解决方案发布这些数据。为了协调这些服务,我们利用项目来管理多租户部署的生命周期。在任何时候,我们都会收到数十万条时序数据来了解基础设施的运行情况,并在出现问题时确定从哪个服务开始解决问题。

未来我们打算通过集成或项目来解决可扩展性问题,并提供全局查询视图、更高的可用性以及历史分析的数据备份能力。

Luigi 和 -- 自动化数据管道

我们使用 Luigi 来编排和自动化数据管道。批处理作业提交给 EMR,Luigi 负责构建非常复杂的批处理工作流。然后用来触发一系列的ETL操作,这样我们就可以控制每个任务的自动化和资源的使用了。

我们将批处理作业的代码打包并加上版本号,放在一个有版本号的容器中,保证在开发和生产环境中的体验一样。

插件项目

我们还使用了许多其他社区开发的项目,这些项目作为插件发布,作为集群生命周期的一部分,并为部署在生产和开发中的服务提供额外的价值。这里简单介绍一下:

凭借 k8s 经验加上广泛的社区支持,我们不仅能够发布核心的无状态服务提供搜索能力,还可以在多个区域和集群中运行大型有状态工作负载,例如 Kafka 等,提供高可用性和复制。我们还开发了其他工具来管理和安全地执行这些工作负载。

使用 Tilt 进行本地开发 - 端到端用例

以上描述了我们使用的许多工具。这里我想结合一个具体的例子来介绍这些工具的使用方法,更重要的是这些工具是如何影响开发者的日常工作的。

对于负责开发搜索结果排名的工程师来说,之前的工作流程是:

可见,开发者需要重复一系列的操作,而团队中的每一个新工程师都要重复这一切,完全是对开发者生产力的浪费。如果实例丢失,它将重复。此外,生产和本地开发的工作流程略有不同,有时会导致不一致。有人认为开发排名应用程序时不需要设置其他服务(例如前端),但这里的示例是为了概括,设置完整的产品并没有什么坏处。另外,随着团队的壮大,需要创建更多的云资源,资源的利用率越来越低。工程师保持实例运行,因为他们不想每天重复这一系列操作。如果工程师离开并且他的实例没有被足够的标记,则很难判断关闭实例并删除云资源是否安全。

理想的情况是为工程师提供一个基本模板,用于设置可以设置完整 SERP 以及排名应用程序所需的其他服务的本地开发环境。这个模板是通用的,并且唯一地标记了用户创建的其他资源,帮助他们控制应用程序的生命周期。因为 k8s 已经抽象了创建和管理实例的需求(我们通过 KOPS 集中管理),所以我们利用模板设置默认值(在非工作时间自动缩减),从而大大降低了成本。

现在,用户只关心他写的内容,而我们的工具(由 、Helm 和 Tilt 组成)神奇地在幕后完成了这一系列工作流。

以下示例描述了设置 SERP 的最小版本和其他相关服务所需的服务。要在开发模式下启动这些服务,用户只需要执行向上倾斜:

说明:

“只要技术够先进,就和魔法没什么区别。” - 亚瑟克拉克

但是这个魔法有问题。它通过更有效的资源共享来提高生产力、提高可靠性并降低成本。但是当出现问题时,人们很难看到问题出在哪里,很难找到问题的根源,而这种错误在人们不方便解决时尤其容易发生。因此,在为这些努力感到自豪的同时,我们仍然保持谦虚。

优化成本

您不可能拥有廉价的基础设施和互联网规模的搜索引擎。话虽如此,总有省钱的方法。下面介绍一下我们如何使用基于 k8s 的架构来优化成本。

1.现货

我们严重依赖 AWS 现场,并且通过这项服务,我们必须在构建系统时考虑可能出现的故障。但这是值得的,因为这些实例比按需实例便宜得多。但要小心,不要像我们那样在脚上开枪。我们太习惯了,有时会高估自己的优势,导致不应该发生的失败。并且不要从高性能服务器中榨取所有的力量,否则你最终会陷入与其他公司的竞购战。最后,切勿在大型 NLP/ML 会议之前使用现场 GPU。

使用 Spot 的混合实例池:我们不仅将 Spot 用于一次性作业,还用于运行服务工作负载。我们想出了一个绝妙的策略。使用多种实例类型(但均配置类似),我们为分布在多个可用区的资源创建了一个节点池。与 Spot 结合使用,我们可以将无状态工作负载转移到新创建或空闲的 Spot 节点,避免可能的长时间停机。

2. 共享 CPU 内存

由于我们完全依赖于工作负载,因此在谈论工作负载时,我们指的是每个服务需要多少 CPU、多少内存以及多少副本。因此,如果 和 相等,则性能得到保证。但是,如果它较低但 Limit 较高(这种情况在零星工作负载上很有用),我们可以预留更多资源,最大限度地提高实例的资源使用率(减少实例上的空闲资源量)。

3. 集群自动扩缩器,用于 pod 的垂直和水平

我们使用集群自动扩缩器来自动创建和扩展 pod,仅在需求增加时创建实例。这样可以在没有工作负载时仅启动最少的实例,并且不需要人工干预。

4.部署在开发环境中

对于开发设置中的所有服务,我们使用 down- 来缩减 pod 在特定时间的副本数,以 0.在中添加注释以指定启动时间表:

annotations: downscaler/uptime: Mon-Fri 08:00-19:30 Europe/Berlin

也就是说,在非工作时间,部署的大小被缩减为 0,副本的数量被集群的自动扩缩器缩减,因为实例上没有活动的工作负载。

5.成本评估和示例建议 - 长期成本降低

在生产环境中,一旦我们确定了资源使用情况,我们就可以选择那些负载会很高的实例。这些实例不使用按需模型,而是使用预留实例 ( ) 定价模型,这需要一年的预付款。但是,成本远低于按需实例。

其中有一些解决方案,例如监控长期使用成本,然后相应地建议额外的成本节约。它还提供特定工作负载的价格估算,以便计算部署系统的总体成本。通过同一个界面,用户还可以知道哪些资源可能不再使用,比如ebs卷。

所有这些措施每年可为我们节省数万至数千欧元。对于基础设施费用高昂的大公司而言,这些措施每年可以轻松节省数百万美元。

机器学习系统

如何成功构建大规模 Web 搜索引擎架构?

机器学习系统中隐藏的技术债务 - 等

有趣的是,我们的旅程以一种没有人预料的方式开始。我们想建立一个基础设施,以便我们可以运行分布式深度学习。这个想法在当时是新的。尽管分布式培训已经存在了一段时间,但除了少数财力雄厚的公司之外,很少有人知道如何从头到尾大规模运行分布式培训。当时也没有任何云解决方案可以解决这个问题。

我们从分布式架构开始,但很快意识到该解决方案在可扩展性方面存在局限性。同时,我们找到一些社区贡献的代码,使用jinja模板引擎生成,然后创建分布式部署的深度学习训练应用(包括参数服务器和工作模式)。这是我们第一次接触。此外,我们构建了自己的近乎实时的搜索引擎,同时尝试通过新颖性进行排名。就在那时,光明来到了我们身边,所以我们决定顺其自然。

作为机器学习系统之旅的一部分(与上述所有基础设施一样),我们的目标是向整个公司开放系统,以便开发人员可以轻松地在其上部署应用程序。我们希望开发人员将精力花在解决问题上,而不是解决服务带来的基础设施问题。

但是,虽然每个人都解决了机器学习方面的问题,但我们很快意识到维护机器学习系统可能是一件非常痛苦的事情。它不仅仅是编写机器学习代码或训练模型。即使对于我们这样规模的公司,也有一些问题需要解决。它在论文“Debt in”中有详细描述。任何希望在生产环境中大规模依赖和运行机器学习系统的人都应该仔细阅读本文。我们讨论了几种不同的解决方案,例如:

在所有这些服务中,我们发现了功能最丰富、最具成本效益和可定制的服务。

前段时间,我们也在官方博客上写了一些原因。除了能够为我们提供 TfJob 等自定义资源并运行训练代码之外,它的一大优势是它自身的支持。

Cliqz 用例

很多

的功能用于我们近乎实时的排名。工程师可以在集群中打开一个,直接进入数据基础设施(批处理和实时流)。共享使多人可以轻松地分别处理代码的不同部分。工程师可以轻松进行各种实验,因为他们不需要设置任何服务器,也不需要访问数据基础设施,更不用说部署的细节,只需使用简单的 Web 界面即可从所需资源中进行选择( CPU、内存,甚至 GPU),分配一个 EBS 卷,并启动一个服务器。有趣的是,一些实验是在 0.5 个 CPU 和 1GB 内存上进行的。通常这种大小的资源随时都存在于我们的集群中,而且很容易生成,甚至不需要创建新的实例。如果不这样做,当来自不同团队的两名工程师想要一起工作时,他们可能会启动自己的实例,从而导致成本增加和资源利用率低。

您还可以提交可用于在 .这方面的一个有趣的项目被称为。

这本身就是一个非常成熟的项目,而我们只触及了冰山一角。最近我们也开始学习其他项目,例如 Katib(机器学习模型的超参数调优)、(机器学习模型的无服务器推理)和 TFX(在生产中创建和管理 ML 管道)。我们已经使用这些项目创建了一些原型,并希望尽快将它们投入生产。

凭借所有这些好处,我们衷心感谢这个出色项目背后的团队。

随着我们的成长,并且随着我们越来越依赖机器学习,我们希望围绕机器学习的处理能够流水线化并且具有更高的可重复性。因此,模型跟踪、模型管理、数据版本管理等就变得极为重要。

为了能够在这种规模下稳定地运行模型,并定期更新和评估,我们需要一个数据管理解决方案来在生产中运行模型,从而实现模型和索引的自动热替换。为了解决这个问题,我们构建了一个解决方案“Hydra”,它可以为下游服务提供数据集订阅服务。它可以为集群中的服务提供卷管理。

结束语

“成功之后,下一个目标就是帮助别人成功。” --

Cliqz 的架构既困难又有趣。我们相信我们还有很长的路要走。随着开发的进展,我们有多种选择可供选择。

尽管 Cliqz 拥有 120 多名员工,但代码实际上是由成千上万的开源开发人员编写和发布的,他们尽可能编写高质量的代码,并尽一切努力确保其安全。没有他们,我们就不会是今天的样子。我们衷心感谢开源社区提供的代码,并在遇到问题时帮助我们解决问题。希望通过这篇文章,分享我们的困惑、经验和解决方法,希望对遇到类似问题的人有所帮助。怀着开放的心态,我们也希望分享我们的资源 ( ) 以回馈开源社区。

原文:

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了