我们如何转型微服务?

微服务在这个时代是一个常常被提及的话题。

我在 SoundCloud时, 曾经负责把一个巨石架构的 Ruby on Rails 应用迁移到微服务。这个故事的技术层面, 我做过多次演讲并且在 SoundCloud 的技术博客上发表过系列文章。这些工程的知识是人们最感兴趣的, 但最近我意识到我从来没有向大众解释我们是如何开启这段微服务之旅的。

抱歉让技术粉失望了, 我们迁移到微服务的原因更多是从产能考虑而非纯技术原因。下面我会做出解释。

注意:这篇文章有很多修正主义的成分, 为了使它更容易理解, 我们把一个相当混乱的事件链简化到一个线性时间线。以此描绘出一幅我的头几年在 SoundCloud 的全貌。

一、Next项目

我刚加入这家公司时,最重要的项目就是我们内部代号为v2的项目,它把我们的网站进行重构并以“The Next SoundCloud”品牌名进行发布。

我先加入了后端团队,App团队,负责巨石架构 Ruby on Rails 应用。当时我们没有把它称为遗留, 而是称它为母舰。App 团队拥有 Rails 应用程序中的所有内容, 包括旧的用户界面。Next是一个single-page JavaScript web 应用程序, 那时我们遵循标准实践, 并将其作为常规客户端构建到我们的公共API中, 这是在 Rails 巨石架构中实现的。

这两个团队, App和web, 真的是孤立的-我们甚至住在不同的楼里,横跨柏林。我们只能在全员工大会上才能见面, 主要沟通工具是issue trackers 和 IRC。不过, 如果您向任何团队中的任何人询问我们的开发过程是如何工作的, 他们会描述如下的场景:

1、有人关于一个功能有些想法, 写一些描述,画一些原型。然后我们作为一个团队讨论它。

2、设计师形成UX。

3、我们写代码。

4、经过一些测试, 完成部署。

但不知何故, 很多挫折感。工程师和设计师抱怨他们工作过度, 但与此同时, 产品经理和合作伙伴抱怨说, 他们无法按时完成任何事情。

作为一个小的消费品类的业务, 我们确实需要确保我们能有更多的合作伙伴(你知道, 那些合作伙伴苹果和 Google 在他们发布新产品的时候就在幻灯片上展示), 就像我们所能做的那样, 这意味着自由的发布和增长。

我们也真的需要在圣诞节前在Beta版中发布Next , 否则节假日将把我们所有的计划推到新的一年的第二季度, 因为我们不希望在新网站还在用时发布任何新功能。为了尽快确定文案的基调, 并确保我们不会浪费整个季度的功能推出, 我们必需要在deadline之前完成这些更新。

这时我们决定去尝试理解我们的成长过程。

二、过程解构

加入 SoundCloud 之前, 我已经做了很多年顾问, 我从这黑暗的过去带来的最有价值的工具之一就是创建价值流地图的概念。对于此技术, 我不会谈Why 和How的部分, 但如果下面描述的过程听起来很有趣, 至少现在您知道去google 什么。

通过与不同工程师的非正式访谈和从我们的多个自动化系统收集数据, 我们能够绘制出我们的实际进程的地图, 而不是我们认为的过程。我不能给你看实际的文件, 但是跟下图所示差不多:

过程如下:

1、有人想到了一个功能。然后, 他们编写了一个相当轻量级的需求, 原型, 并存储在google drive document。

2、需求留在这份文件中, 直到有人有时间处理它。

3、小设计团队将接收需求和UX。这将成为 Web 团队拥有的 Trello 板中的一张任务卡。

4、该卡任务将在 Trello 板上一段时间, 至少2周的迭代, 直到有工程师处理它。

5、工程师将开始处理它。在使用假的/静态数据将设计转换成适当的基于浏览器的体验后, 工程师将记下他们需要的 Rails API 在工作中的变化。然后进入Pivotal Tracker, 这是App团队的工具。

6、该任务卡将停留在Pivatal, 直到App 团队的人有空看它, 通常花费另外两个星期。

7、App团队成员将完成写代码,测试及让API能使用的相关工作。然后他们将更新 Trello, 让 Web 团队知道他们的部分已经完成。

8、更新后的 Trello 任务卡将在积压中多待一段时间, 等待 Web 团队中的工程师完成他们在等待后端工作时开始做的事情。

9、Web 团队开发人员将使他们的客户端代码与后端实现中的任何缺陷相匹配, 并通过部署,标绿。

10、由于部署存在风险、缓慢而痛苦, App 团队将等待几个功能部署到master, 然后再将其部署到生产中。这意味着该功能将在源代码管理中待上几天, 而且由于在完全无关的部分中出现问题, 您的功能将会被退回。

11、在某种程度上, 该功能最终会部署到生产中。

当人们需要澄清或想出更好的点子时, 这些步骤之间会有大量的反复, 但现在让我们先忽略这些因素。

总体而言, 一个功能实现大约需要花两个月。更糟糕的是: 这段时间的一半以上都将用于等待, 即等待工程师处理的某项工作。

利用上面的图可以很容易地发现一个人的过程中有明显的怪异步骤。显而易见我们应该为巨石架构定制发布链。与其等待有足够的资源发布,我们尝试每天发布,有等待就发布,不管有多少功能挂到master。这还不是持续发布, 但可以把我们的周期减少一点点:

让事情唾手可得是伟大的驱动力, 我们的情况是,大象在房间主要指前后端的交互。

我们划分 Web 和应用程序团队之间的工作方式完全脱离了后端开发人员和实际产品。他们会感到沮丧, 觉得自己对产品没有发言权。他们觉得自己像操作工,只是被要求要做什么。在这个市场上, 对有经验的开发者远远供不应求, 这样对待你的团队并不明智。

但是我们今天要关注的问题是, 在项目周期的47天里, 只有11天是在做实际工作。其余的是浪费在队列和一般等待时间。

对于等待新的迭代等待的时间是多少, 还有很多话要说, 但即使是迁移到一个无迭代的过程, 如看板的变化, 也没有太多帮助。

然后, 我们决定做一些有争议的事情: 对前后开发人员进行结对, 专注一个功能开发, 直到完成。我们只有8个后端和 11个前端工程师, 所以围绕这一战略的争论是由于我们需要让前端开发者尽可能多的前置时间, 这样后端的人会尽可能少每个功能的开发时间。这种设置是直观的, 但过程地图表明, 它实际上是非常适得其反。即使前后端交互时间降低, 我们在实际发布前仍然有太长的等待时间!

我们决定先做一个小组实验,在继续推广到其他团队,新的流程是这样的:

对个体而言, 每个人最终都花更多的时间做每个功能的工作。然而, 这不重要, 因为他们并行做, 实现在更少的时间内端到端的开发。需要注意的是, 鉴于后端开发人员并不像以前那样接近App团队的其他成员, 所以在更新发布到Rail app的代码主干上之前必须有一个强制性的代码审查 (也就是 Pull request)。

时间的降低是非常显著的, 我们决定在其他团队和过程中推广这个尝试。我们让设计师、产品经理和前端开发人员在一个功能上彼此紧密地工作, 时间周期也减少了很多:

图上可以看出,时间非常显著的降低。使用更短的工作流, 在deadline之前我们可以轻松地发布Next的第一版。我们从不同的方向迭代尝试结对编程,从而形成了SoundCloud 的功能导向团队。但这是另一个话题, 现在我们需要关注的是, 这个长的Pull Request队列是怎么回事。

三、从母舰到遗留的问题

让我最兴奋的一件事就是 SoundCloud 的工程文化。它的大部分听起来和我在ThoughtWorks的项目很类似, 但有一个方面对我是全新的:强制性代码审查。

2011, 所有初创公司都试图复制 Github 模型, 来自对How GitHub Uses GitHub to Build GitHub 的How GitHub Uses GitHub to Build GitHub 谈话总结。经过这么多年提倡和使用trunk-based-development, 我迫不及待地想知道像 SoundCloud 和 Github 这样的成功公司是如何使用这种非常不同的方法的。

那时, App团队中的所有工程师都会围坐在一张桌子旁, 共享相同的任务积压, 通常非常近。巨石架构中的代码库已经陈旧, 成熟, 乏味。我们都遵循相同的原则和模式, 整个代码库, 提交将只是基于现有的设计, 并没有带来任何惊喜。这使得Pull request这一过程大多流于形式, 人们会花不到一小时的时间来审查提交。

随着越来越多的人离开了紧密的小微团队, 与来自 Web 团队的人一起开发Next功能, 非正式的沟通渠道就中断了。由于误解了什么是正在部署或如何设计的一些功能, 部署开始出问题,变得频繁。因为它通常发生在人类身上, 在其中的一个太多之后, 我们决定, 解决方案将是在合并更改时执行更严格的过程。从现在起, 在将其交给主分支并最终部署之前, 所有更改都必须由第二位工程师 “正式” 批准。

正如上面的地图所示, 这最终造成了一个生产前的漫长的等待请求。在试图解决这个问题时, 我们迈出的第一步是使每个人每天至少花一小时的时间来检查来自团队外部的请求, 即来自Next工作的人员。这并没有大大减少队列, 最终我们意识到一些较小的请求被许多人审查, 而最大的请求 (和来自Next的拉请求通常很大) 没有被任何人审阅, 直到产品经理对队列大吼大叫。更大的变化需要大量的时间来审查, 由于我们的 Rail代码像意面一样, 这是非常危险的。人们避免了像瘟疫这样的大请求。

我们聚在一起, 并决定在Next功能上工作的开发人员将其工作分成更小、更易于管理的Pull request。好的方面是, 每一个Pull request可以被快速被审查和合并, 但缺点是每一个功能被模拟成Pull request,使审查的人只见树木不见森林。有时好的评论会隐藏一个结构性错误。我们确定需要更好的用户故事, 但员工培训需要时间, 为了业务存活,我们需要一个短期的解决方案。

结论是是应用最古老的把戏:结对。我们的要求是, 代码应该由另一个开发人员来审查。对编程,在任何时候我们做到实时审查, 这意味着每个提交获取和自动+1。大多数人对结对很满意, 而那些不高兴的人则可以选择继续自己工作, 但只考虑与Next项目无关的任务。

我们开始尝试几组结对,一些有趣的事情让故事又回到原点。原来, 我们的巨石架构代码基础是如此庞大, 如此丰富, 没有人知道所有细节。围绕子应用人们开发了他们自己擅长和负责的领域。一个结对小组当他们没有足够的领域知识时要么等领域专家有时间,要么被换到做另一个优先级低的功能,两种方式都不好。

这个公司的基因就是,在没有碰到巨石架构之前,每件事情都是好玩的,像游戏一样轻松。

四、巨石架构无法降低的复杂性

要减掉这8天的提前量, 我们需要退后一步看, 为什么我们要始于Pull request这个动作。我们研究整个过程之后, 思考如下:

1、为什么需要Pull request?我们知道, 从多年的经验来看, 很多人都会犯愚蠢的错误,在线修改一个需求,导致整个平台中断几个小时。

2、为什么人们经常犯错误?因为代码库太复杂。很难记住所有的事。

3、为什么代码库如此复杂?因为 SoundCloud 开始是一个非常简单的网站, 但随着时间的推移, 它成长为一个大的平台。我们有很多功能, 不同的客户端应用程序, 不同类型的用户, 同步和异步工作流, 巨大的规模。代码库实现并反映了现在复杂平台的许多组件。

4、为什么需要一个单一的代码库来实现许多组件?由于范围的经济性。母舰已经有了一个很好的部署过程和工具, 有一个针对峰值性能和 DDOS 的测试体系架构, 很容易扩展。如果我们建立新的系统, 我们将不得不为每一个新的系统建立所有这些。

5、为什么我们不能为多个、较小的系统提供规模经济?Hmmm…

第五个问题花了一点时间回答。我们的集体经验和对同行的调查显示, 有两种可能的选择:

  • (A)为什么我们不能为多个、较小的系统提供规模经济?不是不能, 但它不会像我们把所有的东西都放在一个代码库中那样有效。相反, 我们应该在巨石架构和开发人员的可用性方面构建更好的工具和测试。这就是 Facebook 和 Etsy 的模式。
  • (B)为什么我们不能为多个、较小的系统提供规模经济?我们可以。我们将需要做一些实验来找出我们需要的工具和支持。此外, 根据我们构建的独立系统的数量, 我们还需要考虑规模经济, 这是 Netflix、Twitter 和其他公司建立系统的方式。

每种方法都有他们的拥护者, 没有对错。最大的问题是每个方法需要多少成本。金钱和资源不是问题, 但我们没有足够的人力和时间来投资任何大的变革。我们需要一个可以逐步实施的战略, 但从一开始就开始提供价值。

我们又看了一眼我们手上的东西。我们把我们的后端系统简化成如下模型:

这种思维方式自然的使得我们把这个大盒子作为一个巨石架构来实施。正如我们在我们的自我质询中发现的, 虽然, 事情并不像上面的图片让你相信的那么简单。实际上, 如果你打开那个黑盒子, 你会发现我们的系统更像下面的 (非常简化的) 图:

我们没有一个单一的网站, 我们有一个平台与多个组件。每个组件都有自己的所有者和利益干系人, 以及独立的生命周期。

例如,订阅模块是一次性生成的, 只有在付款网关要求我们更改流程中的某些内容时才会修改。另一方面, 与增长和保留相关的通知和其他模块将每天发生变化, 因为我们的年轻的初创公司试图获取更多的用户和内容。

它们也有非常不同的服务级别期望。如果我们没有一个小时的通知, 没有人会死, 但是回放模块中的五分钟停机时间已经足以让我们的指标难以实现。

在探索选项 (A)的同时, 我们得出的结论是, 使这个整体的唯一方法是使这些组件在我们的代码和部署体系结构中显式地工作。

在代码级别, 我们需要确保对单个功能所做的更改可以在相对隔离的状态下开发, 而不需要我们从其他组件中进行接触。我们需要合理地确定, 该更改不会在系统的相关部分引入 bug 或更改运行时行为。这是行业中的一个老问题, 我们知道我们必须做的是使我们的隐式组件显式有界上下文, 并确保我们注意到哪个模块可以依赖于哪一个。

我们讨论了使用 Rails引擎和其他各种工具来实现这一点, 它看起来有点像这样:

在部署方面, 我们需要确保可以单独部署某个功能。将对模块的更改推送到生产不应要求新部署不相关的模块, 如果此类部署坏了, 并且生产中断, 则唯一受影响的功能应该是被更改的一个。

为了实现这一点, 我们考虑仍然将相同的工件部署到所有服务器上, 但使用负载均衡器来确保一组服务器只负责单个功能, 并将此功能的任何问题与其他服务器隔离开来:

完成这些工作并不简单。即使上述不需要跟任何技术栈和我们一直在使用的工具隔离开, 这些变化也带来了问题和风险。

但是, 即使一切进展顺利, 我们知道, 目前的巨石架构代码无论如何必须重构。这段代码在过去的几年里受到了很多的影响, 技术债务无处不在。除了我们自己制造的混乱, 我们还必须从 Rails 2.x 升级到 3, 这本身就是一个巨大的迁移。

这些注意事项导致我们重新考虑选项 (B)。我们认为它看起来不会有很大不同:

但至少我们能够从零天的方式受益。任何新的我们要建立的将成为一个Green field project, 可以省掉Pull request 带来的延时。

我们决定给它一个尝试, 并最终建立了我们的第一个盈利项目所需的一切服务, 独立于巨石架构。该项目引入了几个大功能, 并对我们的订阅模型进行了全面修改, 并在截止日期之前交付了2个2人工程师的团队。

这一经验已经足够好了, 我们决定继续将这个架构应用到我们构建的任何新事物上。我们的第一个服务是使用 Clojure 和 JRuby, 最终移动到 Scala 和Finagle。

对康威定律的强制性引用

坦率地说,对2013年以来几乎所有新建立在 SoundCloud的服务,从某一刻开始,我们开始用微服务这个词, 但是当我们这个体系开始构建时, 我们还没有真正考虑到使用微服务这个词 (第一次在 SoundCloud 的电子邮件中使用 “微服务” 是2013年, 而第一批服务是2012年实施)。

有了新的体系结构框架, 我们就能够将新功能提前发布, 尽管它仍然离过去的好日子有些遥远, 但对于试图在竞争激烈的音乐行业中发挥作用的公司来说, 这些时间更是可以接受的:

针对全新的功能发布,到目前为止这个方式一切正常。但是当我们需要更新已有的功能, 那些在过去的巨石架构中实现的, 我们就回到了旧的模式。更糟糕的是, 很多人在这些新的微服务上花费了更多的时间, 审阅者数量在减少, 请求队列越来越大。

每当提出一些更大的更新时, 我们一定会确保预留足够的时间从大的整体中国做迁移。然而, 它从未发生过。人们仍然会在旧的代码库中实现更改, 或者创建一些怪异的混合, 导致这些更新即在微服务中实现,又在大的整体中实现。

在这个阶段, App “团队” 就像后端开发人员的池, 他们与来自 Web 团队、设计人员和产品经理结对, 在一段时间内处理某个功能。大家一直都在从一个功能跳到另一个功能, 我们意识到, 我们并没有对系统的任何部分形成责任制。如果他们不觉得对它负责,没有人愿意冒险抓取一些旧的代码。这是一个古老格言的实例:人人有责等于人人不用负责。

我们曾考虑将这个池分成小团队, 聚焦特定区域。我们花了很多时间来尝试组队的逻辑分组, 但每件事都不能达成一致。这是一次令人沮丧的练习, 在某种程度上, 我只是把他们分成3-4 人的团队, 以半随机的方式将模块责任分给他们。

这些团队被明确告知, 他们对自己的模块全权负责。这意味着, 任何事情都会直接找到他们, 但他们也自由的选择他们认为合理的策略。如果他们决定把东西放在旧的整理块里,也可以。无论如何, 他们才是代码的负责人。

你可能猜到了,我们看到了旧的巨石架构的瓦解。消息,统计, 以及大多数新 iOS 应用程序所需的更新功能都是从主代码库中提取的。

一切都很顺利, 但我的半随机分组的方式有一个很大的问题: 一个团队要负责的大部分真正的基本功能和对象的生态系统, 如轨道和用户元数据和用例图。这个团队一直处于救火模式, 他们缺乏动力把功能迁移到微服务, 因为这将带来更多的风险和潜在的中断。 这一问题最近才得以解决。我们仍然让一个单独的团队负责这些对象, 现在我们的架是更加稳定, 减少了救火所需的时间。这使得我们可以让这些人从旧巨石架构中提取模块作为它里面的一个项目。

截至今天, SoundCloud 仍然有巨石架构存在, 但它的重要性每天都在减少。它仍然是许多功能的关键路径, 好在stranglers系统解决了这个问题, 它甚至不是面向 Internet。我不确定它是否会永远消失, 它提供的一些功能是如此的小和稳定, 让它们永远在那里可能是更经济的做法, 但我给它一年, 直到巨石架构不再是任何事件的关键的路径。

未来:

就像在文章开篇提到的, 这就是我们微服务旅程的简化版本。

我在公司的最后12个月, 真正关注的是我们想开拓的范围和规模经济。正如我不断重复的那样, 微服务这一术语并不特指什么, 当有人用这个词来描述他们的体系架构时, 有一件事可以确定,就是会有很多服务。随着组织的发展, 他们需要注意每项服务的固定成本。

我的团队和我花了很多时间考虑如何开拓我们的限制, 并确保运营这种架构比运营一个大的整体的成本和复杂度都要低。希望一些工作将成为开源, 所以请确保您订阅他们的技术博客。我还会在以后的帖子中写更多这方面的文章。

在过去的几年里, 我们学到了很多, 当我离开 SoundCloud, 我相当肯定, 公司架构和团队组织 (他们是并行推进的) 将会使得他们的公司在未来几年实现moonshots的目标-也许那时unikernels和nanoservices 会成为一种流行?

原文发布于微信公众号 - DevOps时代(DevOpsTimes)

原文发表时间:2018-01-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从流域到海域

可视化微服务:设计微服务系统

原文地址:https://dzone.com/articles/visualizing-microservices-designing-a-microservi...

4617
来自专栏web前端教室

[2018/08/27期]学生们今天结课了,今天这篇文章是写给你们的

前端这个行业日渐火爆,以后还将继续火爆,许多新人、零基础、转行的人不断进入。他们都有一个共同的问题,那就是“如何提问?”

1053
来自专栏java工会

十个提高编码技能的诀窍,你掌握了几个?

你想成为一名程序员,并且正在为之奋斗,那么你努力的方式,比如做事方法、思维习惯都将会影响你会成为怎样的一名程序员。 那么,你需要成为一个天才才能学好编程吗?...

861
来自专栏钱塘大数据

【11大编程语言薪资排行榜】用空格缩进比用Tab挣得多?

作者:新智元 编程语言有很多,但并非每一种的需求或工资都相同。人工智能和机器学习走热,让 Python 从众多编程语言中脱颖而出。本文将综合各种信源,比较与不同...

3458
来自专栏即时通讯技术

子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践

自从2018年8月20日子弹短信在锤子发布会露面之后(详见《老罗最新发布了“子弹短信”这款IM,主打熟人社交能否对标微信?》),关于它的讨论不绝于耳,7 天融资...

4212
来自专栏数据星河

可能是讲分布式系统最到位的一篇文章

  如果现在让你阐述一下什么是“分布式系统”,你脑子里第一下跳出来的是什么?我想,此时可以用苏东坡先生的一句诗,来形象地描述大家对分布式系统的认识:

640
来自专栏IT技术精选文摘

基于JIRA的产品需求全生命周期管理实践

本文将以有赞零售产品为例,介绍需求全生命周期的管理实践,包括:商家的原始需求收集、产品设计与评审、研发的需求实现、上线后运营反馈、新一轮迭代优化,构成了需求全生...

7484
来自专栏后端技术探索

京东海量订单处理

2014年的618显得和以往任何店庆促销日都不同,不仅仅是因为电子商务本身在中国不断飞速发展对京东系统带来的挑战,更为重要的是2014年5月22日刚走入美国纳斯...

4133
来自专栏嵌入式程序猿

freeRTOS-&OpenRTOS-&SafeRTOS

在很早之前我们就曾在公众号里给大家介绍过freeRTOS,并且还介绍过在NXP kenitis KV46上的移植,相信很多猿友应该还有印象,freeRTOS因其...

3446
来自专栏云计算D1net

混合云管理平台与现代企业不可不说关系

混合云管理是现代IT运营领域中的一个热点话题。继续阅读本文以了解它是什么,它的工作原理以及它能够为用户的基础设施做些什么。 目前,最大的IT热点之一就是混合云。...

35711

扫码关注云+社区

领取腾讯云代金券