GitHub网站究竟可不可靠?谷歌工程师教你用BigQuery一探究竟

  1. 云栖社区>
  2. 博客>
  3. 正文

GitHub网站究竟可不可靠?谷歌工程师教你用BigQuery一探究竟

【方向】 2017-02-11 21:40:35 浏览4628
展开阅读全文

更多深度文章,请关注: https://yq.aliyun.com/cloud

作者简介:Felipe Hoffa,谷歌开发顾问,来自智利,现工作于旧金山以及全球各地,谷歌Cloud Platform社区编辑。


以独特方式组织的可视化数据往往能够为我们提出一些有趣的观点。Felipe Hoffa最近使用BigQuery结合Reddit近八年来的网站活动以及上线时间数据进行了可视化分析,我们从中看到了一些非常有趣的信息。作为一名刚入门的负责任务控制的网站可靠性工程师,我总是会这样问自己,"如果我是负责这项服务的可靠性工程师,我会采用什么方法解决这个问题?"

这次,Felipe将从可靠性工程师的角度出发,对GitHub的一些历史数据展开分析。首先,我们需要先确定,使用BigQuery对GitHub Archive上关于GitHub的部分事件数据进行分析是否足以推断出GitHub网站的健康状况。GitHub为开发者定义了很多种不同的活动事件类型,但在本篇文章的分析中,我们仅关注成功向GitHub发出请求的事件。

我们可以使用这个查询语句:

#StandardSQL 
SELECT TIMESTAMP_TRUNC(created_at, MINUTE) minute, COUNT(*) 
FROM `githubarchive.month.201607` 
GROUP BY 1 
ORDER BY 1

我们可以找到在2016年7月中GitHub在每一分钟发生的事件数量。created_at字段记录了一个以微秒为单位的时间戳,查询语句将其按分钟进行截取。这使得我们将查询结果按时间戳分组时,可以使用COUNT聚合函数来统计每分钟对应的事件数量。将查询结果简单可视化即可得到下图:


9a26e9f745bea2c293acd60f823df35236e30f64

在上图中我们可以发现一些很有趣的数据点,这些数据点对应的事件数量格外得低,但是,仅通过上图我们很难准确判断每一分钟究竟是"正常的"还是"异常的"。因此,我们可以根据查询结果创建事件数据直方图,使判断的过程变得更加清晰。


b4346e1a391eedfaee31ff66c2d9bc79172d69be

这幅图很明显地表明,对于GitHub,当每分钟内处理的事件总数低于200时,网站处于异常的状态,这一结论至少在2016年7月份是成立的。我们假设每分钟极少数的事件与异常少的终端用户请求无关,而是由于网站自身服务器问题所导致的。在这个前提下,有两种可能的解释:用户请求未达到服务器,或者服务器无法成功响应用户请求。这为我们提供了一个标志来近似地判断GitHub当前的状态是"健康"还是"不健康"。

在这里我们定义了一个"服务水平指标",即一个SLI(Service Level Indicator)。根据网站可靠性工程相关文献中的介绍,服务水平指标是"一个针对所提供服务水平中的某些方面谨慎定义的定量标准"。在我们这个例子中,服务水平指标是指"每分钟成功处理的事件数量"。我们要做的是,对这个服务水平指标进行转换,帮助我们判断网站服务在某一时刻是否上线或宕机。最简单的方法是为"每分钟成功处理事件"设置一个阈值,低于这个阈值时,我们将认为网站服务"宕机"。那么,我们应该怎么选择这个阈值?

总中断会导致在特定的时间内没有请求事件记录。因此,我们可以将"宕机"阈值设置为0来检测总中断的情况。然而,大多数的中断都不是总中断。网站服务可能会进入某种不健康的状态,进而在提供服务的过程中开始出现异常高的错误率,这一现象会在日志数据中格外低的(成功请求)事件比例上得以体现。

考虑到总中断情况下不会记录请求事件,我们在前面使用的简单查询在这种情况下可能会出现一些问题。没有事件记录的分钟不会显示在查询结果中!这样的结果完全忽略了总中断的情况。下面的查询语句展示了解决此类问题的方法。它创建了一个包含查询月份所有分钟的表格,并将其与前面的查询结果连接,随后将所有为NULL的结果(意味着不存在对应的行)映射为0事件数量:

#StandardSQL
SELECT b.minute m, IFNULL(c, 0) c
FROM (
  # first day of month
  SELECT i*60 + UNIX_SECONDS(TIMESTAMP('2016-07-01')) minute 
  FROM `fh-bigquery.geocode.numbers_65536`
  WHERE i < 60*24*
    # number of days in month
    EXTRACT(DAY FROM DATE_SUB(DATE_ADD(DATE('2016-07-01'), INTERVAL 1 MONTH), INTERVAL 1 DAY))
) b 
LEFT JOIN (
  SELECT UNIX_SECONDS(TIMESTAMP_TRUNC(created_at, MINUTE)) minute, COUNT(*) c
  FROM `githubarchive.month.201607`
  GROUP BY 1
) a
ON a.minute=b.minute
ORDER BY b.minute

为了使接下来的查询操作更加简便,我们将本次查询的结果以视图形式保存,命名为events_per_minute_201607,并在接下来的查询中基于该视图检索数据。

既然我们现在有一个视图,其中记录了所有零事件对应的分钟,那么我们可以使用一个分位数函数来探索在每分钟事件数量较低情况下的数据分布。

#StandardSQL 
SELECT APPROX_QUANTILES(c, 10001) as q 
FROM `fh-bigquery.public_dump.events_per_minute_201607`

APPROX_QUANTILE函数的"10001"参数表明数据将被划分至10000个桶中,每个桶具有相同数量的数据点。非统计学专业的读者可能对具有100个桶的分位数函数的特殊情况比较熟悉。例如,如果你在测试中处于95分位数,这意味着在测试中有5%的人成绩比你好,有94%的人成绩不如你,同时还有1%的人的成绩与你相同。

下图为其中20条结果对应的条形图:


70658b7e4fcd720187a15cc4eb49c23fbd10aedb

由于我们认为中断属于异常情况,因此在这里我们寻找的是超出"正常"范围的数据点。我们想要选择一个合适的阈值,这样的阈值足够低,不会误将事件处理数较少的分钟判断为宕机时间,同时阈值也应足够高,以至于我们可以检测到任何明显导致请求事件响应数降低的操作。通常情况下,每分钟响应的用户请求数在20到77之间时,可能会出现"状态转换"的情况,这可能意味着当每分钟响应请求数低于20时,网站的服务几乎完全"宕机"。

尽管上面的分析还未得出任何结论,但是我们可以先将阈值设为每分钟20个事件,并以此为服务是否"宕机"的判断依据,看看我们能够发现什么。使用下面的查询语句我们可以看到GitHub在2016年7月总共宕机了多少分钟:

#StandardSQL
SELECT m, c
FROM `fh-bigquery.public_dump.events_per_minute_201607`
WHERE c <= 20

查询语句返回了54行记录,因此,根据我们的定义,GitHub的服务在七月份总共"宕机"了54分钟。但是54分钟究竟是好还是不好呢?

为了回答这个问题,我们需要确定可接受的宕机时间是多少。正常运行时间目标通常以你希望达到多少个9的形式表示,例如,99%的正常运行时间,99.9%的正常运行时间等等。99.9%的正常运行时间意味着你只能在0.1%的时间内宕机。但是,为了让这样的表述有实际意义,我们必须先选择一个时间段。一个月是一个不错的选择。此时,正常运行时间目标的确切表述可能是"网站服务在一个公历月内宕机时间不得超过总共运行时间的0.1%"。

为服务水平指标添加这样一个条件可将其转换为服务水平目标(SLO,Service Level Objective)。2016年7月共有44640分钟。这意味着在未达到服务水平目标前,我们总共有44.6分钟的宕机时间。这通常被称为"错误预算"。

因此我们可以说在2016年7月份,GitHub的网站服务消耗了44分钟错误预算中的54分钟,因此,它以10分钟的差距未达到99.9%的服务水平目标。

将服务水平目标可视化的典型方法是在给定时间段内累积错误时间,在我们的例子中,累计计算的时间段是七月的31天时间。接下来的查询语句统计了七月份的累积宕机时间。

#StandardSQL
SELECT m, cumulative_down, next_cum
FROM (
  SELECT m, cumulative_down, LEAD(cumulative_down) OVER(ORDER BY m) next_cum, MIN(m) OVER() min_m, MAX(m) OVER() max_m
  FROM (
    SELECT m, SUM(IF(c <= 20, 1, 0)) OVER(ORDER BY m ROWS BETWEEN 43200 PRECEDING AND CURRENT ROW) cumulative_down
    FROM (
      SELECT m, c, 1 as down
      FROM `fh-bigquery.public_dump.events_per_minute_201606` UNION ALL
      SELECT m, c, 1 as down
      FROM `fh-bigquery.public_dump.events_per_minute_201607` UNION ALL
      SELECT m, c, 1 as down
      FROM `fh-bigquery.public_dump.events_per_minute_201608`)
    )
  )
WHERE cumulative_down!=next_cum 
OR m IN (min_m, max_m)
ORDER BY m

根据查询结果我们绘制了下图。图中折线的阶跃对应于"中断"的情况。中断时间越长,阶跃的幅度越大。当折线越过44分钟时,我们的宕机时间已经超过了错误预算,称该服务在30天的时间窗口内"未达到服务水平协议的要求"。


07f0747764d1a0670d32beb22413c3806f1d58b5

我们的分析表明,两次中断消耗了大部分的错误预算,一次是在月中,而另一次则发生在月末。事实上,如果我们查看GitHub的状态日志,我们可以看到GitHub在7月12日的报告中记录了"异常率激增"的情况,并在7月27日的报告中记录了"主要服务中断"。

尽管服务水平目标有时按照公历月定义,但是这样的定义方式有时会给工程师带来困扰,主要有两个原因。首先,"公历月"并不是一个固定的时间跨度。一个公历月可能有28天也可能有31天,这取决于当前时间碰巧属于哪个月份。其次,这样的定义使个别几天变得非常特殊,错误预算在这几天会被神奇地重置。例如,对于一个服务水平目标为公历月99.9%正常运行时间的服务,可能在7月31日发生44分钟的中断,并且在8月1日又遭受另一次中断,这种情况下只要该服务在这两个月未发生其他中断,我们仍称其达到了服务水平目标,尽管该服务在48小时内总共发生了88分钟的中断。

为了解决这些不一致的现象,服务水平目标通常采用30天滑动窗口的形式定义 ,而非公历月的形式。在不考虑闰秒的情况下,30天相比于公历月,是一个固定的时间跨度。而且,使用滑动窗口的方式也可以解决前面讨论的第二个问题。假设在7月31日和8月1日发生的两次44分钟中断将使该服务在8月1日中断发生后,便"超过服务水平目标的要求",直到7月31日的中断脱离30天滑动窗口的范围。

这一点很重要,因为"超过服务水平目标的要求"应该引起负责团队的注意,并采取更为保守有效的决策。例如,如果你提供的服务"未达到服务水平目标",那么现在可能不是进行关键软件升级的最佳时机。48小时内88分钟的宕机时间足以令一个需达到99.9%服务水平目标的团队采取更保守的措施。第一个基于公历月的服务水平目标定义无法做到这一点,而第二个基于30天滑动窗口的定义则可以。

BigQuery可以轻松地跨多个日期分片表进行查询,因此,我们可以为2016年6月,7月和8月的数据创建类似的视图,同时,我们可以使用OVER子句来计算滑动窗口内的累积中断时间(30天,共43200分钟):

#StandardSQL SELECT m, cumulative_down, next_cum FROM ( SELECT m, cumulative_down, LEAD(cumulative_down) OVER(ORDER BY m) next_cum, MIN(m) OVER() min_m, MAX(m) OVER() max_m FROM ( SELECT m, SUM(IF(c

当我们为这份新的滑动窗口数据绘制图表时,我们可以清楚地看到GitHub的网站服务在七月下旬至八月上旬期间未到达服务水平目标(即图中折线超过43)的时间段。我们也可以清楚地看到服务在八月份逐渐回到了服务水平目标要求的水平,在这个时候,关于服务的相关决策与措施可"一切照旧"。


5ea6b1ea5c344bd9beafdd926d62aa9092b5851d

截止到目前,我们还未讨论过服务水平协议(SLA,Service Level Agreement)。我们刻意这样做。服务水平指标代表着一项服务的健康与否。而服务水平目标旨在为确定何时适合执行风险较高的操作,如主要服务升级,提供保障。服务水平协议是服务提供商与其客户之间的法律协议,通常对违反服务水平协议的情况指定罚金,例如服务信用等。一个服务水平协议一般应对应多个服务水平目标,因为服务水平目标指导我们如何操作服务,但服务水平协议在不同情况下通常指定不同的细节与阈值。例如,尽管我们已经观察到30天滑动窗口更有利于指导服务相关的操作,但是为了简单期间,法律合同通常还是会按照公历月的形式编写。此外,服务水平目标的阈值通常比服务水平协议的阈值更加保守。例如,服务水平目标阈值可定为99.9%,但在服务水平协议中,有可能会产生罚金的阈值可能会设置为99.5%。

在GitHub的具体示例中,它们的服务条款并没有提到"服务水平协议"。实际上,GitHub声明该网站服务基于"原生"与"可用"的原则提供的。缺少服务水平协议是否就意味着GitHub不关心网站的可靠性?当然不是!实际上Github上线了记录网站状态的网页,这表明他们不仅测量并跟踪其网站的可靠性,同时也对其用户保持透明公开。

在这一点上,我们应该注意到,为了在图表中展示一些有趣的东西,我们预先使用特定的查询对数据进行了略微的处理,以便于找到一个日期相对较近且发生了一两次中断的月份,我们通过使用GitHub的状态存档来确认中断是否是真实发生的。如果我们在前面的例子中选择了八月份,那么这将是一篇非常无聊的文章,因为GitHub在八月的可靠性是100%。干得不错,GitHub!


数十款阿里云产品限时折扣中,赶紧点击领劵开始云上实践吧!

以上为译文

本文由北邮@爱可可-爱生活 老师推荐,阿里云云栖社区组织翻译。

文章原标题《A Google SRE explores GitHub reliability with BigQuery》,作者:Felipe Hoffa,译者:6816816151

文章为简译,更为详细的内容,请查看原文

网友评论

登录后评论
0/500
评论
【方向】
+ 关注