# Vinted 体系结构：每天部署数百次，以保持繁忙的门户稳定

> 原文： [http://highscalability.com/blog/2015/2/9/vinted-architecture-keeping-a-busy-portal-stable-by-deployin.html](http://highscalability.com/blog/2015/2/9/vinted-architecture-keeping-a-busy-portal-stable-by-deployin.html)

![](img/94c1673242b5990d73da86cff661af23.png)

*这是 [Vinted](https://www.vinted.com/) 的 [NerijusBendžiūnas](https://www.linkedin.com/profile/view?id=48589106)和 [Tomas Varaneckas](https://twitter.com/spajus) 的来宾帖子。*

[Vinted](https://www.vinted.com/) 是一个对等市场，用于出售，购买和交换衣服。 它允许成员直接通信，并具有社交网络服务的功能。

它始于 2008 年，最初是一个立陶宛女孩的小社区，后来发展成为一个全球性项目，为 8 个不同国家/地区的 700 万用户提供服务，并且这一需求正在不断增长，每天处理超过 2 亿个请求。

## 统计资料

*   700 万用户，并且还在不断增长
*   每月 250 万活跃用户
*   每天 2 亿个请求（浏览量+ API 调用）
*   9.3 亿张照片
*   80 TB 原始空间用于图像存储
*   60 TB HDFS 原始空间用于分析数据
*   70％的流量来自移动应用（API 请求）
*   每位成员每周花费 3 个小时以上的时间
*   2500 万个上市项目
*   超过 220 台服务器：
    *   47 用于内部工具（vpn，厨师，监视，开发，制图，构建，备份等）
    *   Hadoop 生态系统 38
    *   34 用于图像处理和存储
    *   独角兽和微服务 30
    *   28 个用于 MySQL 数据库，包括副本
    *   19 用于狮身人面像搜索
    *   10 个用于背景工作
    *   10 用于使用 Nginx 进行负载平衡
    *   卡夫卡 6
    *   Redis 4
    *   4 用于电子邮件传递

## 技术栈

### 第三方服务

*   [GitHub](https://github.com/) ，用于代码，问题和讨论
*   [NewRelic](http://newrelic.com/) 用于监视应用程序响应时间
*   [CloudFlare](https://www.cloudflare.com/) 用于 DDoS 保护和 DNS
*   [Amazon Web Services](http://aws.amazon.com/) （SES，Glacier），用于通知和长期备份
*   [闲暇](https://slack.com/)用于工作对话和“聊天操作”
*   [Trello](https://trello.com/) 完成任务
*   [Pingdom](http://pingdom.com/) 用于正常运行时间监控

### 核心应用和服务

*   [Ruby](https://www.ruby-lang.org/) 用于服务，脚本和主应用
*   [主应用程序和内部应用程序的 Rails](http://rubyonrails.org/)
*   [独角兽](http://unicorn.bogomips.org/)服务于主应用
*   [Percona MySQL 服务器](http://www.percona.com/software/percona-server/ps-5.6)作为主数据库
*   [Sphinx 搜索](http://sphinxsearch.com/)用于全文搜索并减少 MySQL 的负载（测试 [ElasticSearch](http://www.elasticsearch.org/) ）
*   [Capistrano](http://capistranorb.com/) 用于部署
*   [Jenkins](http://jenkins-ci.org/) 用于运行测试，部署和其他各种任务
*   [Memcached](http://memcached.org/) 用于缓存
*   [请求](http://resquework.org/)进行后台作业
*   [Redis](http://redis.io/) 用于 Resque 和 Feed
*   [RabbitMQ](http://www.rabbitmq.com/) 用于将消息传递到服务
*   [HAproxy](http://www.haproxy.org/) 提供高可用性和负载平衡
*   [GlusterFS](http://www.gluster.org/) 用于分布式存储
*   [Nginx](http://nginx.org/) 投放 Web 请求
*   [Apache Zookeeper](http://zookeeper.apache.org/) 用于分布式锁定
*   [Flashcache](https://github.com/facebook/flashcache/) 可提高 I / O 吞吐量

### 数据仓库堆栈

*   [Clojure](http://clojure.org/) 用于数据提取服务
*   [Apache Kafka](http://kafka.apache.org/) ，用于存储飞行中的数据
*   [Camus](https://github.com/linkedin/camus) 用于将数据从 Kafka 卸载到 HDFS
*   [Apache Hive](https://hive.apache.org/) 作为 SQL-on-Hadoop 解决方案
*   [Cloudera CDH](http://www.cloudera.com/content/cloudera/en/products-and-services/cdh.html) Hadoop 发行版
*   [Cloudera Impala](http://www.cloudera.com/content/cloudera/en/products-and-services/cdh/impala.html) 作为低延迟 SQL-on-Hadoop 解决方案
*   [Apache Spark](https://spark.apache.org/) 处于实验阶段
*   [Apache Oozie](http://oozie.apache.org/) 作为工作流调度程序（研究替代方案）
*   [扩展](https://github.com/twitter/scalding)用于数据转换
*   [Avro](http://avro.apache.org/) 用于数据序列化
*   [Apache Parquet](http://parquet.incubator.apache.org/) 用于数据序列化

### 服务器和资源调配

*   多为裸金属
*   多个数据中心
*   [CentOS](http://www.centos.org/)
*   [厨师](https://www.chef.io/)用于配置几乎所有内容
*   [Berkshelf](http://berkshelf.com/) ，用于管理 Chef 依赖项
*   [测试厨房](http://kitchen.ci/)，用于在 VM 上运行基础结构测试
*   [Ansible](http://www.ansible.com/) 用于滚动升级

### 监控方式

*   [Nagios](http://www.nagios.org/) 用于常规操作监控
*   [仙人掌](http://www.cacti.net/)用于图形和容量规划
*   [Graylog2](https://www.graylog2.org/) 适用于应用程序日志
*   用于服务器日志的 [Logstash](http://logstash.net/)
*   [Kibana](http://www.elasticsearch.org/overview/kibana/) 用于查询 logstash
*   [统计信息](https://github.com/etsy/statsd)，用于从应用程序收集实时指标
*   [石墨](http://graphite.wikidot.com/)，用于存储应用程序中的实时指标
*   [Grafana](http://grafana.org/) 用于创建带有应用指标的精美仪表板
*   [集线器](https://hubot.github.com/)用于基于聊天的监视

## 架构概述

*   裸机服务器被用作云提供商的更便宜，更强大的替代产品。
*   在欧洲和美国的 3 个不同数据中心托管服务器。
*   Nginx 前端将 HTTP 请求路由到独角兽工作者并执行 SSL 终止。
*   [Pacemaker](http://clusterlabs.org/) 用于确保 Nginx 服务器的高可用性。
*   在不同的国家/地区，每个 Vinted 门户网站都有自己单独的部署和资源集。
*   为了提高机器利用率，属于多个门户的大多数服务可以在单个裸机服务器上彼此并行运行。 主厨配方负责唯一的端口分配和其他分离问题。
*   通过为每个门户网站使用单独的数据库来避免 MySQL 分片。
*   在我们最大的门户中，功能性分片已经在发生，距离单个最大表无法容纳在服务器中的点还差几个月。
*   Rails 应用程序中的缓存使用带有 L2 缓存的自定义机制，可以防止主缓存过期时出现峰值。 使用了几种缓存策略：
    *   远程（在 memcached 中）
    *   本地（在独角兽工作者的记忆中）
    *   半本地（在独角兽工作器内存中，当本地缓存到期时回退到 memcached）。
*   围绕核心 Rails 应用程序构建了几个微服务，它们都有明确的用途，例如发送 iOS 推送通知，存储和提供品牌名称，存储和提供标签。
*   微服务可以是按端口，按数据中心或全局的。
*   每个微服务的多个实例正在运行以实现高可用性。
*   Nginx 平衡了基于 HTTP 的微服务。
*   使用 Redis 实现每个成员的自定义 feed。
*   使用过滤器（自定义大小，颜色等）时，从 Sphinx 索引而不是 MySQL 加载目录页面。
*   图像由单独的 Rails 应用处理。
*   处理后的图像存储在 GlusterFS 中。
*   第 3 次匹配后会缓存图片，因为前几次匹配通常来自上传者，并且图片可能会旋转。
*   使用 [twemproxy](https://github.com/twitter/twemproxy) 分割 Memcached 实例。

## 球队

*   超过 150 名全职员工
*   30 位开发人员（后端，前端，移动）
*   5 名现场可靠性工程师
*   立陶宛维尔纽斯的总部
*   在美国，德国，法国，捷克共和国和波兰设有办事处

## 公司的运作方式

*   几乎所有信息对所有员工开放
*   使用 GitHub 进行几乎所有操作（包括讨论公司问题）
*   Slack 中的实时讨论
*   每个人都可以自由参加他们感兴趣的事情
*   自治队
*   没有资历等级
*   跨职能开发团队
*   没有强制执行的流程，团队可以决定如何管理自己
*   团队致力于解决高层问题，并决定如何解决它们
*   每个团队都决定如何，何时何地工作
*   团队在需要时自行雇用

## 开发周期

*   开发人员从主人处分支。
*   更改成为 GitHub 中的请求请求。
*   Jenkins 运行 [Pronto](https://github.com/mmozuras/pronto) 进行静态代码和样式检查，在 pull request 分支上运行所有测试，并使用状态更新 GitHub pull request。
*   其他开发人员查看更改，添加评论。
*   拉取请求可能会使用各种修复程序多次更新。
*   每次更新后，Jenkins 都会运行所有测试。
*   在与 master 合并之前清理 Git 历史，以保持 master 历史的简洁性。
*   接受的请求请求将合并到主服务器中。
*   Jenkins 使用所有测试运行主版本，并触发部署作业以推出新版本。
*   几分钟后，代码到达 master 分支后，将其应用于生产中。
*   拉取请求通常很小。
*   每天部署约 300 次。

## 避免失败

每天部署数百次并不意味着总是必须破坏所有东西，但是保持站点稳定需要一定的纪律。

*   如果测试失败，则不会进行部署。 没有方法可以覆盖它，母版必须为绿色才能进行部署。
*   每天晚上和周末自动部署锁定。
*   任何人都可以通过 Slack 聊天手动锁定部署。
*   部署锁定后，可以从 Slack 聊天中手动“强制”部署。
*   在审查代码时，团队非常挑剔。 质量标准设置得很高。 测试是强制性的。
*   在 Unicorn 重新加载代码之前，将在生产中进行预热。 它包括对门户网站各个关键部分的若干请求。
*   在预热期间，如果任何一个请求均未返回 200 OK，则旧代码将保留，并且部署将失败。
*   有时，测试/预热未发现问题，并且已损坏的代码发布到了生产环境中。
*   错误将流式传输到 Graylog 服务器。
*   如果错误率超过阈值，则会触发警报。
*   错误率警报和失败的构建通知会立即报告给 Slack 聊天。
*   所有错误都包含额外的元数据：Unicorn worker 主机和 pid，HTTP 请求详细信息，导致问题的代码的 git 修订版，错误回溯。
*   某些类型的“致命”错误也直接报告给 Slack 聊天。
*   每个部署日志都包含带有所有更改的 GitHub 差异 URL。
*   如果生产中出现问题，由于变更集和即时错误反馈的原因，很容易快速查明问题。
*   部署详细信息会报告给 NewRelic，从而可以轻松解决性能瓶颈的引入。

## 减少生产时间

快速发布和稳定发布都非常受关注，团队一直在努力使构建和部署时间尽可能短。

*   完整的测试套件包含> 7000 个测试，运行时间约为 3 分钟。
*   不断添加新的测试。
*   Jenkins 在具有 32 个 CPU，128G RAM 和 SSD 驱动器的裸机上运行。
*   Jenkins 将所有测试分成多个块，并并行运行它们，以汇总最终结果。
*   在没有并行化的情况下，测试将在一台普通计算机上运行 1 个小时以上。
*   在 Jenkins 中，对于 master 分支中的每次提交，Rails 资产都是预先构建的。
*   在部署期间，从 jenkins 下载预建资产，并将其上载到所有目标服务器。
*   将版本上载到目标服务器时使用 BitTorrent。

## 运行实时数据库迁移

在大多数情况下，即使在高峰时间，我们也可以在不停机的情况下更改生产 MySQL 数据库的结构。

*   在任何部署期间都可能发生数据库迁移
*   Percona 工具箱的 pt-online-schema-change 用于在表的副本上运行更改，同时使其与原始副本保持同步，并在更改完成后将副本与原始副本切换

# 运作方式

## 服务器配置

*   Chef 用于提供我们基础架构的几乎所有方面。
*   SRE 团队确实会提出对基础结构更改的请求，就像开发团队对代码更改所做的一样。
*   詹金斯（Jenkins）正在验证所有 Chef 拉取请求，运行交叉依赖关系检查，食品评论等。
*   团队在 GitHub 上审查了厨师代码的更改。
*   开发人员可以向 Chef 存储库发出基础结构更改请求请求。
*   合并拉取请求时，Jenkins 上载更改后的 Chef 食谱并将其应用于生产中。
*   计划产生 DigitalOcean 小滴以与 Jenkins 一起进行 Chef 厨房测试。
*   Jenkins 本身是使用 Chef 配置的，而不是使用 Web UI。

## 监控方式

*   仙人掌图服务器端指标
*   Nagios 检查可能出现的所有故障
*   Nagios 健康检查，用于我们的某些应用程序和服务
*   Statsd / Graphite，用于跟踪应用程序指标，例如成员登录，注册，数据库对象创建
*   Grafana 用于漂亮的仪表板
*   可以使用 Chef 动态描述和创建 Grafana 仪表板

## 聊天操作

*   Hubot 坐在 Slack 中。
*   松弛房间通过 [hubot-pubsub](https://github.com/spajus/hubot-pubsub) 订阅了各种事件通知。
*   #deploy 会议室显示有关部署的信息，并提供指向 GitHub diff，Jenkins 控制台输出等的链接。在这里，可以使用简单的聊天命令来触发，锁定和解锁部署。
*   ＃deploy-fail 会议室仅显示部署失败。
*   #failboat 会议室显示有关错误率和生产中个别错误的公告。
*   有多个＃failboat- *房间，其中提供有关 cron 故障，卡住的 resque 作业，nagios 警告，newrelic 警报的信息。
*   每天两次将 Graylog 错误与 GitHub 进行处理和交叉检查，生成报告并将其提交给开发团队。
*   如果有人在 GitHub 上提到您，Hubot 会在 Slack 上的私人消息中为您提供该问题的链接。
*   在 GitHub 中创建问题或请求请求时，提到的团队会在其适当的 Slack 会议室中收到 ping 命令。
*   可以通过 Hubot 在 Slack 聊天中查询并显示石墨图。
*   您可以向 Hubo​​t 询问有关基础设施的问题，即有关部署特定服务的机器的问题。 Hubot 查询 Chef 服务器以获取数据。
*   开发人员将 Hubot 通知用于我们应用中发生的某些事件。 例如，向客户支持聊天室自动通知可能的欺诈案件。
*   Hubot 与 Office [netatmo](https://www.netatmo.com/) 模块集成在一起，可以告诉所有人什么是 CO2，噪声，温度和湿度水平。

# 数据仓库堆栈

*   我们从两个来源提取数据：
    *   网站事件跟踪数据：印象数，点击数等
    *   数据库（MySQL）更改。
*   大多数事件跟踪数据都从 JavaScript / Android / iOS 前端应用程序发布到 HTTP 端点。 一些事件由我们的核心 Ruby on Rails 应用程序发布到本地 UDP 套接字。 我们选择 UDP 以避免阻塞请求。
*   有一种服务可以监听 UDP 套接字上的新事件，对它们进行缓冲，并定期在 Kafka 中发出原始事件的主题。
*   流处理服务使用原始事件主题，验证事件，将它们编码为 Avro 格式，然后发出特定于事件的主题。
*   我们将事件的 Avro 模式保存在单独的集中式模式注册表服务中。
*   无效事件被置于单独的 Kafka 主题中，以进行可能的更正。
*   我们使用 LinkedIn 的 Camus 作业将事件从特定于事件的主题逐步转移到 HDFS。 事件到 Hadoop 的时间通常最多为一个小时。
*   使用 Hive 和 Impala 进行临时查询和数据分析。
*   研究基于伸缩的数据处理解决方案。
*   报告使用我们内部开发的，用 Ruby 编写的类似 OLAP 的报告系统运行。

# 得到教训

## 产品开发

*   投资代码质量。
*   通过测试涵盖所有内容。
*   发布小更改。
*   使用功能开关将未完成的功能部署到生产中。
*   开发某些东西时，不要保留长时间运行的代码分支。
*   投资一个快速的发布周期。
*   首先构建移动产品。
*   设计 API 从一开始就很好，以后很难更改。
*   在 RabbitMQ 消息中包含发件人主机名和应用程序名称可以节省生命。
*   不要猜测或做假设，请进行 [AB](https://github.com/vinted/ab) 测试并检查数据。
*   如果您打算将 Redis 用于更大的事情，请从一开始就考虑分片。
*   如果您计划使 Rails 保持最新状态，则切勿使用叉状和稍作修饰的红宝石宝石。
*   通过社交网络尽可能轻松地共享您的内容。
*   搜索引擎优化是一项全职工作。

## 基础设施和运营

*   经常部署以提高系统稳定性。 起初听起来可能违反直觉。
*   自动化一切。
*   监视所有可能发生的故障。
*   网络交换缓冲区很重要。
*   容量规划很困难。
*   从一开始就考虑 HA。
*   RabbitMQ 集群不值得付出努力。
*   测量并绘制所有图形：HTTP 响应时间，Resque 队列大小，模型持久率，Redis 响应时间。 您无法改善无法衡量的东西。

## 数据仓库堆栈

*   Hadoop 生态系统中有许多工具。 很难选择合适的。
*   如果您正在为 Hive 编写用户定义函数（UDF），那么该考虑使用非 SQL 解决方案进行数据转换了。
*   “ Hadoop 应用程序”概念含糊不清。 通常，我们需要手动将应用程序组件粘合在一起。
*   每个人都编写自己的工作流管理器。 并且有充分的理由。
*   分布式系统监视非常困难。 异常检测和作图很有帮助。
*   核心 Hadoop 基础架构（HDFS，YARN）坚如磐石，但较新的工具通常会有细微差别。
*   卡夫卡很棒。

这个网站是关于高可扩展性还是如何浪费服务器？ 您是否真的需要 220 台服务器来服务〜2300req / sec？

他了解了其中一些服务器使用情况。 这是 Ruby on Rails，所以这就是原因。

您能否详细说明“ RabbitMQ 集群不值得付出”？ 您在运行时遇到问题吗？

@Peter-与其他高可用性站点一样，它们可以使用少于十个服务器来为流量提供服务。
从文章看来-其余内容涉及（a）收集和分析数据； （b）监督和监督系统； （c）确保高可用性。
在三个区域中只有 10 台边缘（缓存/ nginx）服务器（由于 HA，30 台独角兽是自然扩展）。 鉴于流量不统一（因此 200M req / day 不能转换为 2300 req / s），这似乎是适中的，因为在任何区域中，请求都只能由几个端点服务器之一来处理。 再次-这就是您在 HA 设置中所期望的-2 个以上的服务器用于任何潜在的传入（请求）请求。

我必须同意彼得的观点。 在最近阅读了此处的 Stack Exchange 文章之后，很难不认为此设置会浪费一些资源。

感谢分享。
您似乎正在使用大量的开源项目。 我对你的决定感到好奇。 团队成员一开始是否已经熟悉大多数技术？ 如果没有，那么在这么多方面加强工作是否有点势不可挡？

大家好，

Vaidotas，这里的 Vinted 工程师之一。

想对服务器数量发表评论。 如果您查看详细信息，将会看到很多服务器专用于分析数据收集。 而且我认为分析数据可能是单独的主题。

如果您查看运行 Vinted 应用程序所需的服务器及其所有内容，则会看到以下内容：

34 个用于图像处理和存储（9.3 亿张照片）
30 个用于 Unicorn 和微服务（每天 2 亿个请求）
28 个用于 MySQL 数据库，包括副本
19 个用于 Sphinx 搜索（2500 万个列出的项 索引）
10，用于可处理的后台作业
10，用于与 Nginx 进行负载平衡（充当代理和一些缓存，可能会更少，但在我们运营所在的不同国家/地区需要它们）。
Redis 4

因此，这就是 135 台运行应用程序基础结构的服务器。 现在请记住，所有内容都必须具有高可用性。 这意味着至少所有内容的 2 倍，即使您目前不需要/不使用它也是如此。

希望能解释一下服务器数量。

另外，您的行动小组的规模如何，才能维持这种运营/基础架构？ 仅列出了 SRE。5\. DevOps 工程师，系统管理员和/或 DBA 怎么样？

您的 Jenkins 并行化设置似乎很有趣。 你是如何做到的？ 也许不久之后还会有另一篇专门针对此的博客文章？

乔恩，我们有 5 位站点可靠性工程师来维护所有基础架构。 我们执行从服务器引导到数据库管理的所有工作。 我们严重依赖自动化:)

我真正无法理解的是-每天 200 M 的请求如何转换为实际的 Internet 流量？ StackExchange 的请求量为 1.5 亿，在 Alexa 评分中排名第 53，vinted.com 在 38763 上排名较高，但 RPS 较高。
与 http://highscalability.com/blog/2014/11/10/nifty-architecture-tricks-from-wix-building-a-publishing-pla.html（wix）相同-他们要求 700M 请求/ 一天，但肯定无法将 Alexa 评级与 StackExchange 相比。

谁能澄清？

@tao，一开始只有一个开发人员（CEO，联合创始人），而且技术堆栈要小得多。 您的问题的答案是否定的。

当项目开始发展时，出现了一些东西。 我们雇用了更多的开发人员，工程师等。我们选择雇用最聪明的工程师。 之所以使用某些技术是因为他们具有经验，而其他技术则是“试验和错误”（尤其是大数据的东西）。 我们在解决问题的过程中一直在解决它们。 我们仍然有需要解决的问题。 这就是新技术出现的方式。 我相信，一年后，我们可以写一篇关于我们如何做事的文章。

@lan：RabbitMQ 非常棒，我们正在使用它，但是在分析事件方面，我们遇到了一些限制，因此选择 kafka 作为事件发射。 RabbitMQ 集群的东西很容易破解。 正如文档所述-“ RabbitMQ 群集不能很好地容忍网络分区”，这并不像我们希望的那样罕见。

@Tim：我们使用 https://github.com/grosser/parallel_tests 的稍加修改版本

@安东：不确定“转换”的含义，但我们为女孩提供了超过 10 Gigs 的流量。 在谈论 Alexa 时，目前我们有 8 个不同的国家，例如，我们的一个国家（http://kleiderkreisel.de）的排名为 7,468，因此没有一个汇总排名。 另一件事是，70％的流量来自移动应用程序，我不确定 Alexa 是否会对此进行计数。

希望大家，Vinted 回答了您所有的问题:)