写点什么

Etsy 工程师 Sam Haskins 谈代码部署,监控与故障处理

  • 2013-03-20
  • 本文字数:6889 字

    阅读完需:约 23 分钟

在本次访谈中,Sam Haskins 分享了他在 Etsy 做 DevOps 的经验。Etsy 使用 Git 做代码管理,平均每天提交的更新数量在 30 次左右,有时甚至达到 70 个。在如此高频率提交代码的情况下,他们如何进行代码审查?如何在每次更新之后监控系统性能?如何处理故障?下面,Sam 将一一解答。

Sam Haskins 目前属于 Etsy 的核心平台团队,该团队一共有 10~15 人,整体负责服务器及系统层与应用层的接口,为上层开发特性的工程师们提供服务。Sam 于 2010 年中加入 Etsy,他现在主要负责数据库接入层,也在 web 框架上做一些东西。核心平台团队的其他成员还负责开发图片存储系统、异步任务管理等功能。Sam 毕业于卡内基梅隆大学的数学系。

代码部署和审计

InfoQ:第一个问题是关于部署系统的。你们使用哪些工具?

Sam:我们使用 Git 做代码版本管理,使用 Deployinator(我们为自己开发的一个工具)从 Git 中拿东西出来,并把它放到所有的服务器上。基本上就是文件复制工作,没有什么特别的。另外,我们使用 Chef 做配置管理。就这些了。实际上我们用 GitHub。我们有一个内部的 GitHub 来管理代码。

关于代码审计流程,实际上我们没有很多正式的检查。只要你的代码能编译,能运行,你就可以放到代码库上,不需要审计。但是,我们鼓励大家做代码复查,而且大家经常这么做。代码复查通常是这样的——你只要把你的补丁发给别人看,他们看过后给你一些点评。大多数人在推送代码到代码库前都会这么做。

InfoQ:有 QA 吗?

Sam: 没有。我们没有传统意义上的 QA。因为我们每次的提交都很小,所以发现哪些代码破坏了系统通常是相当容易的。当然,也有一些人的工作有点儿像 QA,他们的工作就是试着破坏我们的系统,然后让我们知道哪些东西坏了。我们部署做得很多,一天部署 50 次挺平常的,有时候会更多。在部署前后做全面的测试是不可能的,根本没有足够的时间。但是我们有自动化测试,和代码审查等等,而且我们每次的变更都很小,所以,我们认为我们并不需要有一个大规模的正式的 QA 过程。

InfoQ:那么,当你做更新时,你如何做环境控制,保证代码安全?

Sam:对于我们的工具 Deployinator 来说,只要你点一下按钮,它所做的就是从 Git 中拿出东西,并把它分发下去,仅此而已。

InfoQ:你们部署的是源代码,而不是打包后的二进制文件?

Sam:是的,我们不打包。它就是把它们放到 web 服务器的某个目录下之类的,没有什么太复杂的。我们不做太复杂的事儿。

非常简单,只要几分钟就运行完了。 正是因为很容易,所以我们才能部署这么多次。

InfoQ:那么一天 50 次是比较典型的部署频率吗?

Sam:差不多。今年的平均值可能是 30 或 33 次,算上星期六和星期日。但通常星期六和星期日我们什么也不做。我想,最多的时候,我们可能在一天里我们最多部署 60 或者 70 次,少的时候也有二、三十次。的确很多,但这也是我们改变了写代码的方式,让它变成合理的事情了。假如我们每次都做很大的变更,这么做可能就不行啦,一定会变成一种灾难。正是因为我们每次只做很小的改更,所以这种方式才能行。

部署的流程与健康监控

InfoQ:由于你们每天部署这么多次,那么你们通常在一天中的什么时间部署?你是部署后,观察几分钟,然后再部署另一次吗?

Sam:是的,我们有很多度量项,可能成千上万吧。所以 每次部署后,除了要监控你的变更是否起作用了,我们还有一个标准的部署指示仪表盘,你需要关注。那上面有一堆图表,你要看着它,并确保仪表盘上的指标没有异常。

我们还有一个工具,用来读实时日志,你也要看看那个东西。假如有些东西不正常,那你就需要修复它。但只需要几分钟就行,通常是一两分钟。但我要看更多一些的图表数据。 只要没有什么问题出现,并且你认为你的修改正常工作了,那就轮到下一组啦。

InfoQ:当部署时,你是先把它部署到测试环境上吗?

Sam:有一个前提,在你部署之前,你已经自行测试过它了,所以我们是直接部署的。不过,的确也可以说我们已经在测试环境上部署过了,因为我们的试运行环境也是我们的生产环境,是同一个环境,只是版本高一点儿,而且用户不可能访问到这些服务器,只有我们才能访问。

因此,当部署时,首先会放到试运行环境,然后 CI(持续集成服务器)会运行测试,是单元测试。顺便说一下,我们使用的是 Jenkins。同时,你可以访问试运行环境,Jenkins 运行一系列的测试。之后,一旦推送代码的人认为试运行环境上的功能没有问题,他们已经测试过他们的变更,并认为这些变更运行正确的话,那么只是 Jenkins 运行完(通常总共需要 5 分钟),就可以推送到生产环境了。

InfoQ:你在这里提到的生产环境,是指所有的机器吗?

Sam:是的,是所有的机器。

这个工具只有两个按钮,一个指向试运行环境,另一个就指向生产环境。

InfoQ:那是否根据服务的不同,分成了不同的集群呢?比如一种服务,使用一类集群?

Sam:算是吧。所有的部署都是同样的软件栈。 同样的 web 软件栈。但我们的确有搜索专用的软件栈和图片存储专用的软件栈,但那些软件栈也与它相关。我们并没有太多的 Service。我们只有一个代码库,所以它也非常大,大约 2GB 吧。但绝大部分都用同一个软件栈。

因些,当做部署时,先是 web 服务器,然后是 API 服务器。我们有内部的支持系统,异步任务,Gearman,以及类似于运行后台任务 (Cron) 的机器和相关设施,所有这些都是一次完成的。

InfoQ:那么你会根据它们的重要程度做分别推送吗?你要知道,有些部署可能会对用户产生很大的影响。

Sam:我们非常喜欢现在这种工作方式的原因,就是因为这种方式决定了你说的这种情况不会经常发生。如果真有什么事情非常重要的话,那么你可以自己做部署,并确保更顺利。但是,通常来说,我们更喜欢鼓励大家以一种好的方式工作,以避免这种事情的发生。

InfoQ:也就是说,避免一次性做很大的变更,是吗?

Sam:是这样的。

InfoQ:所以,你们总是提交很小的变更?

Sam:当我们发布新功能时,我们会使用配置文件对功能进行控制,比如关闭这个功能,打开那个功能,因为很难一点儿一点儿的发布一个新功能。当需要发布某个功能时,我们再把开关打开。这种方法使得发布并不需要太多的精力,并且比较安全,我们可以做到只在公司内部发布,我们也可以做到只发布给属于 beta 群组中的用户,也能做到每次向百分之多少的用户发布。

所以当做了一点儿改动时,我们常常只发布给 1% 的用户,以验证没有破坏其它功能,然后再逐步发布给所有的用户,而不是一次性发布。如果发布的是一个新特性,我们更多的时候只是把开关打开。但有时我们只会将特性发布给一部分用户,以便观察用户在接下来的几天里是如何使用它的。我们会持续关注。

InfoQ:是不是说,beta 用户会被导向某个特定的集群?

Sam:节点都是一样的。我们只是在运行时检测,比如通过用户的 user ID。

InfoQ:你们是使用自己构建的系统做这件事呢?还是用其它的什么工具?

Sam:我们自己写的代码。它原来的版本是开源的,而这个新版本要比原来那个版本好得太多了,但是还没有发布,不过即将发布了。

这个项目的名字不怎么好,好像是叫做 Feature Flags 之类的。它在我们 Github 的主页里。

这就引出了另一个话题,也是我们开发中使用的方法。我们并不使用 Git 中的分支。大家并不会在一个分支上工作很长时间。通常一个分支的生命周期很短,很快就会把代码部署了。

有关故障

InfoQ:你们是如何处理故障的?

Sam:因为大家都被培训过如何看图表,如何查日志。通常我们发现故障是非常非常快的,因为一直有人在查看我们的那些度量项,这些度量项的确非常多。大家都非常了解这些度量项正常是应该是什么样子的。

InfoQ:那是一分钟查看一次,还是一秒钟?

Sam:我们使用 Nagios 做自动化的检查。每个检查的时间周期不同。有一些非常非常频繁,而有一些则不是。我们不但有很多这种 Nagios 做的自动化检查,还有一些人来看图表,以及对应的模式。这种图表的更新通常是一秒钟。

因为一直在不停地部署代码,所以不得不时刻查看那些图表。既然经常看着那些图表,那么对我们来说,快速地发现问题也就不是什么难事啦,因为一直不停地在做这件事,所以也就做得好啦。

InfoQ:听上去感觉好像很依赖人肉啊?

Sam:是这样的。但我们也有像 Nagios 这样的工具来检查那些可能出错的地方。不能及时捕获故障的情况很少,几乎总是能马上发现问题。那些无法用 Nagios 无法检查的东西在半夜也不会出问题,因为没人会在大家都睡觉时去部署代码。那些出错的地方通常都是在系统级别上,而通常都是由 Nagios 来监控的。在白天我们工作时,因为有新的部署,这些地方才有可能出错。但这时会有人看着,因为他们正在部署代码。所以那些很难用计算机去监控的东西在晚上不会出错。

InfoQ:那么,当出现了一个问题时,你们会采取什么样的措施?

Sam:如果某次部署严重地影响了网站,我们所做的就是要求大家别再推送代码,并让整个生产线停下来,然后无论是谁发现的问题,他都要马上开始分析,尝试找出是哪里出的错。我们都可以访问那些日志和图表数据。当我们确定哪个特性出了问题时,我们会联系那些做这个特性的人或可能知道这个特性的人。通常情况下,如果与部署有关的话,相关的部署人员都已经就位了,因为我们时刻使用 IRC Chat。

大家都在这个聊天室中,所以如果是部署问题,正在做部署和观察监控的人就知道哪里出了错,很可以也已经知道如何修复它了,因为每次部署只做了很小的改动,所以他们可以将代码回滚,部署原来的那个版本。但对于比较小的缺陷,我们更倾向于推送新的修复代码,而不是回滚。就是直接修复好缺陷,再次部署。

InfoQ:那会花的时间较长吧。

Sam:是的。所以如果只是小问题,能很快修复,那就提交并部署新的代码。因为即使回滚的话,你也要再一次推送代码,总共也需要大约十分钟的时间。因为先要上试运行环境,然后才是生产环境。所以无论你是提交修复,还是回滚,花的时间差不多。如果你多花五分钟 能够正确地修复问题,其实并不赖,甚至可能更好。

所以,通常我们会这么做。如果问题很严重,显然我们会先回滚,然后再找问题的根本原因。然而经常遇到的情况是你并不能完全确定回滚就是你想要的结果,所以与盲目回滚相比,我们更多的是试图找到真正的问题所在。

InfoQ:向前修复通常是以什么样的方式进行的呢?

Sam:大多数在部署时遇到的问题并不会影响整个网站,它们只是会影响其中的一小部分。如果是向前修复的话,当你在修复这个问题时,其他人还可以继续推送他们的代码,继续做他们原来做的事情。但是,显然我们做这种决定要基于故障的严重程度。

InfoQ:修复不好,怎么办?

Sam:如果不立即修复的话,就要开始做回滚操作。

InfoQ:比如说,花了十分钟以后,你也不能修复故障怎么办呢?

Sam:我们并没有那种严格的用于快速决策的规则。但是如果大家不得不在那里等待你修复你的问题,那通常说明什么地方出了问题,其他人就不会部署代码。如果你耽误其他人部署代码太长时间的话,那么我们的做法是采取最快的方式,无论哪种方式都行,让大家可以部署代码。所以十分钟过去了你还没有找到问题,但你认为回滚会让工作回到正轨上的话,当然你就要先回滚,然后在你自己的机器上继续找问题。

InfoQ:能讲一下最近的一次故障吗?

Sam:好。比较典型的情况是,我们只会遇到一些很小的问题。比如,有人正在修改一个页面,而此时我们网站上的一个卖家正在修改他有多少东西可以卖。我并不能说这是一个现在正在发生的案例,但是,它的确与我们看到的小故障类似。这个开发人员修改的恰好是这个页面,那么卖家可能就会看到一个错误,但这并不会耽误大家在网站上卖东西。这时我们会认为,问题并不大。这个开发人员去查看一下,然后修复它就行了。通常由于变更很小,所以直接修复,然后推送部署就行了。但要强调的是,如果问题比较严重,那就会回滚。

最近就发生了一个非常严重的问题:我们在数据库中使用完整的 IDs,而这些 IDs 不是自增的。我们没有让数据库来做这种 ID 自增,因为我们所有的数据库都是双主方式 (two masters) 的,即 master-master。所以无法使用自增,因为你不知道哪个 master 是最近一次做自增操作的。所以我们有另外一个服务器,由它来专门提供这些自增数字。这个服务器也有两个。其中一个只产生奇数,而另一个只产生偶数。 这就是用户 ID 帐户的来源。

几个月前,这个服务器的数字超过了 2^31,所以溢出了 32 位的整数列,但这也不是太大的问题,因为你可以把它保存在一个 64 位的列中。然而,在代码中的一些地方并没有这么做。但是,大家并没有意识到为什么 ID 突然出现了 64 位。因此,当 ID 太大时,一些相关的特性就无法工作了。

这些地方的代码无法得到合理的 ID,但仍旧试图把它保存到数据库上,但数据库无法保存它。当这个问题发生时,我们立刻要求大家不要再推送代码了。我们只花了大约一分钟就找到了这个故障的原因,因为在日志里有很多错误信息,这些信息中能看到那些数字。如果之前你看到过这样的数字,你会就发现这个问题。你能想像得到当看到这样的数字时,那种不祥的感觉。

我们看到后,我们就立刻停止推送,是我们的团队(core platform team)发现它的。尽管我们知道这是一个严重的问题,但我们并不知道有多少数据库表使用了它,因为没有地方可以看到这样的信息。我们召集大家,包括一些运维工程师和我们团队的一些人,到会议里来讨论如何处理这个问题。最后决定相看所有的表,看看到底哪些数据库表的列宽不足。

在数据库里,代码中,以及写的 schemas 中都可能有相关的信息。所以我们只能查看所有的代码,来判断有多少 schemas 中存在这个问题。有些只是代码中有问题,有些只是在数据库中有问题。有些地方则是两方面都有问题。同时,其他人来判断哪些特性受到了牵连。我们从错误日志中有可能发现这些受牵连的特性,也可能从图表上发现。只要发现了,就可以找以相应的配置文件,把这些特性关掉。

一旦我们修改好所有的地方,我们就可以在数据库上运行这些变更,将那些字段改成 64 位,再把关掉的特性开关打开,然后马上监控,看哪些数据还会出异常。整个过程只持续了几个小时,而且只有几个比较小的特性受到了影响。但直到把全部问题都修复了,我们才能知道到底有多少特性受到了影响。因为很多影响不是显而易见的。

InfoQ:这是一个相当快的修复了。

Sam:是的,这是最糟糕的故障之一。我想,在过去的几年里我们并没有出现太多的问题。在过去的几年只,可能停机的时间不超过三小时,停机事件也很少发生。如果真发生了,那绝对是很糟糕的事情。

监控

InfoQ:你们如何做服务器监控?我是指网络性能监控和应用程序的监控。

Sam:对于网络性能,我们用 Cacti,以及我们自己写的一个工具,叫做 FITB,在 Github 上开源了。他会监控所有的 switch,并保持那些度量项数据比较少。它有点儿像 Cacti。但我不记得到底区别在哪里了,因为我很少处理网络度量数据。我们用这两个工具监控不同端口上 switch 的性能。我们在这方面很少遇到问题。网络几乎没有给我们带来任何麻烦。我们的容量目前是足够的。

InfoQ:那么,系统常见的瓶颈在哪里呢?

Sam:更多的是数据库和 memcache。最近,我们在 memcache 上遇到了问题,而且与网络相关。有很多特别关键的数据保存在 memcache 中,很多人都需要用。最近,在美国,我们的流量陡增。好象是在 Cyber Monday(译者注:感恩节假期之后的第一个上班日的网购促销活动),流量非常高,有一个关键数据被访问了很多次,其中的一个 memcache 服务器占满了网络连接。而对这个问题的修复方法只是不再把这个数据放在 memcache 中了。因为根本不需要放在那里,所以我们就把它从那里删除了。但这个修复并不合理,因为直到网卡饱和了,我们才发现。

我们用 Cacti,但我并不知道它的监控频度是多少,也不知道其他人隔多长时间来看一下数据。当然,我们用 Nagios 来监控这个数据,判断事情是否出了问题。

对于客户端数据,我们有自己的一套信号器,记录用户在网站上的操作,这些信息会保存到我们的大数据栈上。对于用户行为的监控,我们使用 Hadoop 集群,来处理分析这些信号量,比如我加载了主页,点击了一个物品,并买下了它。我们会观察分析这类的操作模式。我们也会将前端 JavaScript 的错误记录到后台。我们既有服务器端的性能测试,也有 web 页面的测试,并经常运行它们。我们有一个 web 页面测试集群,用它来判断性能是否达到许可水平。

我们也会将应用程序的度量数据与用户的行为做关联。所以我们会度量有多少人在网站上查询买品列表,以便来衡量用户是如何使用我们的网站的。如果这样的操作不再发生了,那么也许我们应该在用户体验方面要改点儿什么东西了。我们也会保存我们帮助论坛的那些图片。这样的话,如果越来越多的人在帮助论坛中请求帮助的话,那么很可能我们破坏了什么功能。当然,还有来自客服的信息,我们常常和他们沟通来尝试判断用户遇到的问题。

查看英文原文 Interview: Sam Haskins from Etsy on Code Deployment, Monitoring and Failure Procedures


感谢黄冬对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2013-03-20 09:205785
用户头像

发布了 100 篇内容, 共 21.7 次阅读, 收获喜欢 5 次。

关注

评论

发布
暂无评论
发现更多内容

MySQL事务浅析|由浅入深

MySQL 编程 架构

诊所数字化:搭建网络路径的信息铺设策略

boshi

数字化转型 医疗 七日更 28天写作

【STM32】CubeMX+HAL 点亮 LED

AXYZdong

硬件 stm32 2月春节不断更

电子产品中EMC隔离设计的方法

不脱发的程序猿

二月春节不断更 电路设计 EMC 电子产品

架构设计篇之微服务实战笔记(一)

小诚信驿站

架构师 刘晓成 小诚信驿站 28天写作 架构师成长笔记

APM 行业认知系列 - 二

东风微鸣

APM Trace 可观察性

构建万物可信互联的基石,带你深度剖析区块链跨链的关键技术,满满是干货!

华为云开发者联盟

区块链 智能合约 云原生 跨链技术 分布式账本技术

大小厂必问Java后端面试题(含答案)

yes

Java 面试 后端

如何 0 改造,让单体/微服务应用成为Serverless Application

阿里巴巴云原生

Docker Serverless 容器 微服务 云原生

面试官:Java性能调优你会多少?一个问题就把我问的哑口无言,哭了!

996小迁

架构 面试 Java性能调优

IDEA 敏捷开发技巧——后缀完成

程序员小航

Java 后端 IDEA

EMC设计中电缆屏蔽使用方法

不脱发的程序猿

二月春节不断更 电路设计 EMC 电子产品 电缆屏蔽

LoadRunner测试中遇见的不可思议的问题及其解决方法

陈磊@Criss

全网最新、最全面蚂蚁金服面经分享:简历模板/面试题库/Java核心技术笔记

比伯

Java 编程 程序员 面试 技术宅

Golang代码测试:一点到面用测试驱动开发

华为云开发者联盟

测试 TDD 代码 Go 语言

可能是Java 8 Optional最佳实践

ES_her0

28天写作

APM 行业认知系列 - 四

东风微鸣

APM Trace 可观察性

滚雪球学 Python 番外系列,自动化测试是个啥?

梦想橡皮擦

Python 28天写作 2月春节不断更

Elasticsearch踩坑记之深度分页

topsion

大数据 elasticsearch 深度分页

Elasticsearch filter vs. query 对比

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试 2月春节不断更

《经济学人》2021年2月20日刊精彩文章导读及资源下载

wbliu85

【LeetCode】数组的度Java题解

Albert

算法 LeetCode 28天写作 2月春节不断更

SpringBoot之自定义启动异常堆栈信息打印

false℃

产品训练营--第四期作业

曦语

产品训练营

Java实体映射利器---MapStruct

是小毛吖

Java MapStruct

你的面试专属!JVM G1GC的算法+实现,90张图+33段代码

Java架构追梦

Java 架构 JVM 调优 G1GC

APM(应用性能监控) 行业认知系列 - 一

东风微鸣

APM Trace 可观察性

APM 行业认知系列 - 三

东风微鸣

APM Trace 可观察性

著名的Java并发编程大师都这么说了,你还不知道伪共享么!

看点代码再上班

Java 后端

重大更新!一文了解京东通用目标重识别开源库FastReID V1.0

京东科技开发者

AI 监控

读书总结2020

IT民工大叔

#读书

Etsy工程师Sam Haskins谈代码部署,监控与故障处理_DevOps & 平台工程_Sai Yang_InfoQ精选文章