标签 Github 下的文章

我不能为我的人生想出一个引子来,所以……

1 在 GitHub.com 上编辑代码

我想我要开始介绍的第一件事是多数人都已经知道的(尽管我一周之前还不知道)。

当你登录到 GitHub ,查看一个文件时(任何文本文件,任何版本库),右上方会有一只小铅笔。点击它,你就可以编辑文件了。 当你编辑完成后,GitHub 会给出文件变更的建议,然后为你 复刻 fork 该仓库并创建一个 拉取请求 pull request (PR)。

是不是很疯狂?它为你创建了一个复刻!

你不需要自己去复刻、拉取,然后本地修改,再推送,然后创建一个 PR。

不是一个真正的 PR

这对于修改错误拼写以及编辑代码时的一些糟糕的想法是很有用的。

2 粘贴图像

在评论和 工单 issue 的描述中并不仅限于使用文字。你知道你可以直接从剪切板粘贴图像吗? 在你粘贴的时候,你会看到图片被上传 (到云端,这毫无疑问),并转换成 markdown 显示的图片格式。

棒极了。

3 格式化代码

如果你想写一个代码块的话,你可以用三个反引号(`)作为开始 —— 就像你在浏览 精通 Markdown 时所学到的一样 —— 而且 GitHub 会尝试去推测你所写下的编程语言。

但如果你粘贴的像是 Vue、Typescript 或 JSX 这样的代码,你就需要明确指出才能获得高亮显示。

在首行注明 ``jsx`:

…这意味着代码段已经正确的呈现:

(顺便说一下,这些用法也可以用到 gist。 如果你给一个 gist 用上 .jsx 扩展名,你的 JSX 语法就会高亮显示。)

这里是所有被支持的语法的清单。

4 用 PR 中的魔法词来关闭工单

比方说你已经创建了一个用来修复 #234 工单的拉取请求。那么你就可以把 fixes #234 这段文字放在你的 PR 的描述中(或者是在 PR 的评论的任何位置)。

接下来,在合并 PR 时会自动关闭与之对应的工单。这是不是很酷?

这里是更详细的学习帮助

5 链接到评论

是否你曾经想要链接到一个特定的评论但却无从着手?这是因为你不知道如何去做到这些。不过那都过去了,我的朋友,我告诉你啊,点击紧挨着名字的日期或时间,这就是如何链接到一个评论的方法。

嘿,这里有 gaearon 的照片!

6 链接到代码

那么你想要链接到代码的特定行么。我了解了。

试试这个:在查看文件的时候,点击挨着代码的行号。

哇哦,你看到了么?URL 更新了,加上了行号!如果你按下 Shift 键并点击其他的行号,格里格里巴巴变!URL 再一次更新并且现在出现了行范围的高亮。

分享这个 URL 将会链接到这个文件的那些行。但等一下,链接所指向的是当前分支。如果文件发生变更了怎么办?也许一个文件当前状态的 永久链接 permalink 就是你以后需要的。

我比较懒,所以我已经在一张截图中做完了上面所有的步骤:

说起 URL…

7 像命令行一样使用 GitHub URL

使用 UI 来浏览 GitHub 有着很好的体验。但有些时候最快到达你想去的地方的方法就是在地址栏输入。举个例子,如果我想要跳转到一个我正在工作的分支,然后查看与 master 分支的差异,我就可以在我的仓库名称的后边输入 /compare/branch-name

这样就会访问到指定分支的 diff 页面。

然而这就是与 master 分支的 diff,如果我要与 develoment 分支比较,我可以输入 /compare/development...my-branch

对于你这种键盘快枪手来说,ctrl+Lcmd+L 将会向上跳转光标进入 URL 那里(至少在 Chrome 中是这样)。这(再加上你的浏览器会自动补全)能够成为一种在分支间跳转的便捷方式。

专家技巧:使用方向键在 Chrome 的自动完成建议中移动同时按 shift+delete 来删除历史条目(例如,一旦分支被合并后)。

(我真的好奇如果我把快捷键写成 shift + delete 这样的话,是不是读起来会更加容易。但严格来说 ‘+’ 并不是快捷键的一部分,所以我并不觉得这很舒服。这一点纠结让 整晚难以入睡,Rhonda。)

8 在工单中创建列表

你想要在你的 工单 issue 中看到一个复选框列表吗?

你想要在工单列表中显示为一个漂亮的 “2 of 5” 进度条吗?

很好!你可以使用这些的语法创建交互式的复选框:

 - [ ] Screen width (integer)
 - [x] Service worker support
 - [x] Fetch support
 - [ ] CSS flexbox support
 - [ ] Custom elements

它的表示方法是空格、破折号、再空格、左括号、填入空格(或者一个 x ),然后封闭括号,接着空格,最后是一些话。

然后你可以实际选中或取消选中这些框!出于一些原因这些对我来说看上去就像是技术魔法。你可以选中这些框! 同时底层的文本会进行更新。

他们接下来会想到什么魔法?

噢,如果你在一个 项目面板 project board 上有这些工单的话,它也会在这里显示进度:

如果在我提到“在一个项目面板上”时你不知道我在说些什么,那么你会在本页下面进一步了解。

比如,在本页面下 2 厘米的地方。

9 GitHub 上的项目面板

我常常在大项目中使用 Jira 。而对于个人项目我总是会使用 Trello 。我很喜欢它们两个。

当我学会 GitHub 的几周后,它也有了自己的项目产品,就在我的仓库上的 Project 标签,我想我会照搬一套我已经在 Trello 上进行的任务。

没有一个是有趣的任务

这里是在 GitHub 项目上相同的内容:

你的眼睛最终会适应这种没有对比的显示

出于速度的缘故,我把上面所有的都添加为 “ 备注 note ” —— 意思是它们不是真正的 GitHub 工单。

但在 GitHub 上,管理任务的能力被集成在版本库的其他地方 —— 所以你可能想要从仓库添加已有的工单到面板上。

你可以点击右上角的 添加卡片 Add Cards ,然后找你想要添加的东西。在这里,特殊的搜索语法就派上用场了,举个例子,输入 is:pr is:open 然后现在你可以拖动任何开启的 PR 到项目面板上,或者要是你想清理一些 bug 的话就输入 label:bug

亦或者你可以将现有的备注转换为工单。

再或者,从一个现有工单的屏幕上,把它添加到右边面板的项目上。

它们将会进入那个项目面板的分类列表,这样你就能决定放到哪一类。

在实现那些任务的同一个仓库下放置任务的内容有一个巨大(超大)的好处。这意味着今后的几年你能够在一行代码上做一个 git blame,可以让你找出最初在这个任务背后写下那些代码的根据,而不需要在 Jira、Trello 或其它地方寻找蛛丝马迹。

缺点

在过去的三周我已经对所有的任务使用 GitHub 取代 Jira 进行了测试(在有点看板风格的较小规模的项目上) ,到目前为止我都很喜欢。

但是我无法想象在 scrum(LCTT 译注:迭代式增量软件开发过程)项目上使用它,我想要在那里完成正确的工期估算、开发速度的测算以及所有的好东西怕是不行。

好消息是,GitHub 项目只有很少一些“功能”,并不会让你花很长时间去评估它是否值得让你去切换。因此要不要试试,你自己看着办。

无论如何,我听说过 ZenHub 并且在 10 分钟前第一次打开了它。它是对 GitHub 高效的延伸,可以让你估计你的工单并创建 epic 和 dependency。它也有 velocity 和 燃尽图 burndown chart 功能;这看起来可能是世界上最棒的东西了。

延伸阅读: GitHub help on Projects

10 GitHub 维基

对于一堆非结构化页面(就像维基百科一样), GitHub 维基 wiki 提供的(下文我会称之为 Gwiki)就很优秀。

结构化的页面集合并没那么多,比如说你的文档。这里没办法说“这个页面是那个页面的子页”,或者有像‘下一节’和‘上一节’这样的按钮。Hansel 和 Gretel 将会完蛋,因为这里没有面包屑导航(LCTT 译注:引自童话故事《糖果屋》)。

(边注,你有读过那个故事吗? 这是个残酷的故事。两个混蛋小子将饥肠辘辘的老巫婆烧死在她自己的火炉里。毫无疑问她是留下来收拾残局的。我想这就是为什么如今的年轻人是如此的敏感 —— 今天的睡前故事太不暴力了。)

继续 —— 把 Gwiki 拿出来接着讲,我输入一些 NodeJS 文档中的内容作为维基页面,然后创建一个侧边栏以模拟一些真实结构。这个侧边栏会一直存在,尽管它无法高亮显示你当前所在的页面。

其中的链接必须手动维护,但总的来说,我认为这已经很好了。如果你觉得有需要的话可以看一下

它将不会与像 GitBook(它使用了 Redux 文档)或定制的网站这样的东西相比较。但它八成够用了,而且它就在你的仓库里。

我是它的一个粉丝。

我的建议:如果你已经拥有不止一个 README.md 文件,并且想要一些不同的页面作为用户指南或是更详细的文档,那么下一步你就需要停止使用 Gwiki 了。

如果你开始觉得缺少的结构或导航非常有必要的话,去切换到其他的产品吧。

11 GitHub 页面(带有 Jekyll)

你可能已经知道了可以使用 GitHub 页面 Pages 来托管静态站点。如果你不知道的话现在就可以去试试。不过这一节确切的说是关于使用 Jekyll 来构建一个站点。

最简单的来说, GitHub 页面 + Jekyll 会将你的 README.md 呈现在一个漂亮的主题中。举个例子,看看我的 关于 github 中的 readme 页面:

点击 GitHub 上我的站点的 设置 settings 标签,开启 GitHub 页面功能,然后挑选一个 Jekyll 主题……

我就会得到一个 Jekyll 主题的页面

由此我可以构建一个主要基于易于编辑的 markdown 文件的静态站点,其本质上是把 GitHub 变成一个 CMS(LCTT 译注:内容管理系统)。

我还没有真正的使用过它,但这就是 React 和 Bootstrap 网站构建的过程,所以并不可怕。

注意,在本地运行它需要 Ruby (Windows 用户会彼此交换一下眼神,然后转头看向其它的方向。macOS 用户会发出这样这样的声音 “出什么问题了,你要去哪里?Ruby 可是一个通用平台!GEMS 万岁!”)。

(这里也有必要加上,“暴力或威胁的内容或活动” 在 GitHub 页面上是不允许的,因此你不能去部署你的 Hansel 和 Gretel 重启之旅了。)

我的意见

为了这篇文章,我对 GitHub 页面 + Jekyll 研究越多,就越觉得这件事情有点奇怪。

“拥有你自己的网站,让所有的复杂性远离”这样的想法是很棒的。但是你仍然需要在本地生成配置。而且可怕的是需要为这样“简单”的东西使用很多 CLI(LCTT 译注:命令行界面)命令。

我只是略读了入门部分的七页,给我的感觉像是我才是那个小白。此前我甚至从来没有学习过所谓简单的 “Front Matter” 的语法或者所谓简单的 “Liquid 模板引擎” 的来龙去脉。

我宁愿去手工编写一个网站。

老实说我有点惊讶 Facebook 使用它来写 React 文档,因为他们能够用 React 来构建他们的帮助文档,并且在一天之内预渲染到静态的 HTML 文件

他们所需要做的就是利用已有的 Markdown 文件,就像跟使用 CMS 一样。

我想是这样……

12 使用 GitHub 作为 CMS

比如说你有一个带有一些文本的网站,但是你并不想在 HTML 的标记中储存那些文本。

取而代之,你想要把这堆文本存放到某个地方,以便非开发者也可以很容易地编辑。也许要使用某种形式的版本控制。甚至还可能需要一个审查过程。

这里是我的建议:在你的版本库中使用 markdown 文件存储文本。然后在你的前端使用插件来获取这些文本块并在页面呈现。

我是 React 的支持者,因此这里有一个 <Markdown> 插件的示例,给出一些 markdown 的路径,它就会被获取、解析,并以 HTML 的形式呈现。

(我正在使用 marked npm 包来将 markdown 解析为 HTML。)

这里是我的示例仓库 /text-snippets,里边有一些 markdown 文件 。

(你也可以使用 GitHub API 来获取内容 —— 但我不确定你是否能搞定。)

你可以像这样使用插件:

如此,GitHub 就是你的 CMS 了,可以说,不管有多少文本块都可以放进去。

上边的示例只是在浏览器上安装好插件后获取 markdown 。如果你想要一个静态站点那么你需要服务器端渲染。

有个好消息!没有什么能阻止你从服务器中获取所有的 markdown 文件 (并配上各种为你服务的缓存策略)。如果你沿着这条路继续走下去的话,你可能会想要去试试使用 GitHub API 去获取目录中的所有 markdown 文件的列表。

奖励环节 —— GitHub 工具!

我曾经使用过一段时间的 Chrome 的扩展 Octotree,而且现在我推荐它。虽然不是吐血推荐,但不管怎样我还是推荐它。

它会在左侧提供一个带有树状视图的面板以显示当前你所查看的仓库。

这个视频中我了解到了 octobox ,到目前为止看起来还不错。它是一个 GitHub 工单的收件箱。这一句介绍就够了。

说到颜色,在上面所有的截图中我都使用了亮色主题,所以希望不要闪瞎你的双眼。不过说真的,我看到的其他东西都是黑色的主题,为什么我非要忍受 GitHub 这个苍白的主题呐?

这是由 Chrome 扩展 Stylish(它可以在任何网站使用主题)和 GitHub Dark 风格的一个组合。要完全黑化,那黑色主题的 Chrome 开发者工具(这是内建的,在设置中打开) 以及 Atom One Dark for Chrome 主题你肯定也需要。

Bitbucket

这些内容不适合放在这篇文章的任何地方,但是如果我不称赞 Bitbucket 的话,那就不对了。

两年前我开始了一个项目并花了大半天时间评估哪一个 git 托管服务更适合,最终 Bitbucket 赢得了相当不错的成绩。他们的代码审查流程遥遥领先(这甚至比 GitHub 拥有的指派审阅者的概念要早很长时间)。

GitHub 后来在这次审查竞赛中追了上来,干的不错。不幸的是在过去的一年里我没有机会再使用 Bitbucket —— 也许他们依然在某些方面领先。所以,我会力劝每一个选择 git 托管服务的人考虑一下 Bitbucket 。

结尾

就是这样!我希望这里至少有三件事是你此前并不知道的,祝好。

修订:在评论中有更多的技巧;请尽管留下你自己喜欢的技巧。真的,真心祝好。

(题图:orig08.deviantart.net)


via: https://hackernoon.com/12-cool-things-you-can-do-with-github-f3e0424cf2f0

作者:David Gilbertson 译者:softpaopao 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我们 MySQL 数据库基础架构是 Github 关键组件。 MySQL 提供 Github.com、 GitHub 的 API 和验证等等的服务。每一次的 git 请求都以某种方式触及 MySQL。我们的任务是保持数据的可用性,并保持其完整性。即使我们 MySQL 集群是按流量分配的,但是我们还是需要执行深度清理、即时更新、在线 模式 schema 迁移、集群拓扑重构、 连接池化 pooling 和负载平衡等任务。 我们建有基础架构来自动化测试这些操作,在这篇文章中,我们将分享几个例子,来说明我们是如何通过持续测试打造我们的基础架构的。这是让我们一梦到天亮的根本保障。

备份

没有比备份数据更重要的了,如果您没有备份数据库,在它出事前这可能并不是什么问题。Percona 的 Xtrabackup 是我们一直用来完整备份 MySQL 数据库的工具。如果有专门需要备份的数据,我们就会备份到另一个专门备份数据的服务器上。

除了完整的二进制备份外,我们每天还会多次运行逻辑备份。这些备份数据可以让我们的工程师获取到最新的数据副本。有时候,他们希望从表中获取一整套数据,以便他们可以在一个生产级规模的表上测试索引的修改,或查看特定时间以来的数据。Hubot 可以让我们恢复备份的表,并且当表准备好使用时会通知我们。

tomkrouper

.mysql backup-list locations

Hubot

+-----------+------------+---------------+---------------------+---------------------+----------------------------------------------+
| Backup ID | Table Name | Donor Host    | Backup Start        | Backup End          | File Name                                    |
+-----------+------------+---------------+---------------------+---------------------+----------------------------------------------+
|   1699494 | locations  | db-mysql-0903 | 2017-07-01 22:09:17 | 2017-07-01 22:09:17 | backup-mycluster-locations-1498593122.sql.gz |
|   1699133 | locations  | db-mysql-0903 | 2017-07-01 16:11:37 | 2017-07-01 16:11:39 | backup-mycluster-locations-1498571521.sql.gz |
|   1698772 | locations  | db-mysql-0903 | 2017-07-01 10:09:21 | 2017-07-01 10:09:22 | backup-mycluster-locations-1498549921.sql.gz |
|   1698411 | locations  | db-mysql-0903 | 2017-07-01 04:12:32 | 2017-07-01 04:12:32 | backup-mycluster-locations-1498528321.sql.gz |
|   1698050 | locations  | db-mysql-0903 | 2017-06-30 22:18:23 | 2017-06-30 22:18:23 | backup-mycluster-locations-1498506721.sql.gz |
| ...
|   1262253 | locations  | db-mysql-0088 | 2016-08-01 01:58:51 | 2016-08-01 01:58:54 | backup-mycluster-locations-1470034801.sql.gz |
|   1064984 | locations  | db-mysql-0088 | 2016-04-04 13:07:40 | 2016-04-04 13:07:43 | backup-mycluster-locations-1459494001.sql.gz |
+-----------+------------+---------------+---------------------+---------------------+----------------------------------------------+

tomkrouper

.mysql restore 1699133

Hubot

A restore job has been created for the backup job 1699133. You will be notified in #database-ops when the restore is complete.

Hubot

@tomkrouper: the locations table has been restored as locations_2017_07_01_16_11 in the restores database on db-mysql-0482

数据被加载到非生产环境的数据库,该数据库可供请求该次恢复的工程师访问。

我们保留数据的“备份”的最后一个方法是使用 延迟副本 delayed replica 。这与其说是备份,不如说是保护。对于每个生产集群,我们有一个延迟 4 个小时复制的主机。如果运行了一个不该运行的请求,我们可以在 chatops 中运行 mysql panic 。这将导致我们所有的延迟副本立即停止复制。这也将给值班 DBA 发送消息。从而我们可以使用延迟副本来验证是否有问题,并快速前进到二进制日志的错误发生之前的位置。然后,我们可以将此数据恢复到主服务器,从而恢复数据到该时间点。

备份固然好,但如果发生了一些未知或未捕获的错误破坏它们,它们就没有价值了。让脚本恢复备份的好处是它允许我们通过 cron 自动执行备份验证。我们为每个集群设置了一个专用的主机,用于运行最新备份的恢复。这样可以确保备份运行正常,并且我们能够从备份中检索数据。

根据数据集大小,我们每天运行几次恢复。恢复的服务器被加入到复制工作流,并通过复制保持数据更新。这测试不仅让我们得到了可恢复的备份,而且也让我们得以正确地确定备份的时间点,并且可以从该时间点进一步应用更改。如果恢复过程中出现问题,我们会收到通知。

我们还追踪恢复所需的时间,所以我们知道在紧急情况下建立新的副本或还原需要多长时间。

以下是由 Hubot 在我们的机器人聊天室中输出的自动恢复过程。

Hubot

gh-mysql-backup-restore: db-mysql-0752: restore_log.id = 4447 
gh-mysql-backup-restore: db-mysql-0752: Determining backup to restore for cluster 'prodcluster'. 
gh-mysql-backup-restore: db-mysql-0752: Enabling maintenance mode 
gh-mysql-backup-restore: db-mysql-0752: Setting orchestrator downtime 
gh-mysql-backup-restore: db-mysql-0752: Disabling Puppet 
gh-mysql-backup-restore: db-mysql-0752: Stopping MySQL 
gh-mysql-backup-restore: db-mysql-0752: Removing MySQL files 
gh-mysql-backup-restore: db-mysql-0752: Running gh-xtrabackup-restore 
gh-mysql-backup-restore: db-mysql-0752: Restore file: xtrabackup-notify-2017-07-02_0000.xbstream 
gh-mysql-backup-restore: db-mysql-0752: Running gh-xtrabackup-prepare 
gh-mysql-backup-restore: db-mysql-0752: Starting MySQL 
gh-mysql-backup-restore: db-mysql-0752: Update file ownership 
gh-mysql-backup-restore: db-mysql-0752: Upgrade MySQL 
gh-mysql-backup-restore: db-mysql-0752: Stopping MySQL 
gh-mysql-backup-restore: db-mysql-0752: Starting MySQL 
gh-mysql-backup-restore: db-mysql-0752: Backup Host: db-mysql-0034 
gh-mysql-backup-restore: db-mysql-0752: Setting up replication 
gh-mysql-backup-restore: db-mysql-0752: Starting replication 
gh-mysql-backup-restore: db-mysql-0752: Replication catch-up 
gh-mysql-backup-restore: db-mysql-0752: Restore complete (replication running) 
gh-mysql-backup-restore: db-mysql-0752: Enabling Puppet 
gh-mysql-backup-restore: db-mysql-0752: Disabling maintenance mode 
gh-mysql-backup-restore: db-mysql-0752: Setting orchestrator downtime 
gh-mysql-backup-restore: db-mysql-0752: Restore process complete. 

还有一件我们可以使用备份做的事情是向一组现有的 MySQL 服务器添加一个新的副本。我们初始化一个新的服务器,一旦我们得知它准备就绪,我们就可以开始恢复该特定集群的最新备份。我们有一个脚本,可以运行所有需要手动执行的恢复命令。我们的自动恢复系统基本上使用的是相同的脚本。这简化了系统构建过程,并允许我们在一个启动运行的主机上使用几个对话命令(chatops)来替代一堆手动执行命令。下面显示的是在该对话(chatops)中手动执行的恢复:

jessbreckenridge

.mysql backup-restore -H db-mysql-0007 -o -r magic_word=daily_rotating_word

Hubot

@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Determining backup to restore for cluster 'mycluster'. 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: restore_log.id = 4449 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Enabling maintenance mode
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Setting orchestrator downtime 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Disabling Puppet 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Stopping MySQL 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Removing MySQL files 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Running gh-xtrabackup-restore 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Restore file: xtrabackup-mycluster-2017-07-02_0015.xbstream 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Running gh-xtrabackup-prepare 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Update file ownership 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Starting MySQL 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Upgrade MySQL 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Stopping MySQL 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Starting MySQL 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Setting up replication 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Starting replication 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Backup Host: db-mysql-0201 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Replication catch-up 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Replication behind by 4589 seconds, waiting 1800 seconds before next check. 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Restore complete (replication running) 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Enabling puppet 
@jessbreckenridge gh-mysql-backup-restore: db-mysql-0007: Disabling maintenance mode 

故障转移

我们使用协调器 来为 主服务器 master 中间服务器 intermediate master 执行自动化故障切换。我们期望 协调器 orchestrator 能够正确检测主服务器故障,指定一个副本进行晋升,在所指定的副本下修复拓扑,完成晋升。我们预期 VIP(虚拟 IP)、连接池可以相应地进行变化、客户端进行重连、puppet 在晋升后的主服务器上运行基本组件等等。故障转移是一项复杂的任务,涉及到我们基础架构的许多方面。

为了建立对我们的故障转移的信赖,我们建立了一个类生产环境的测试集群,并且我们不断地崩溃它来观察故障转移情况。

这个类生产环境的测试集群是一套复制环境,与我们的生产集群的各个方面都相同:硬件类型、操作系统、MySQL 版本、网络环境、VIP、puppet 配置、haproxy 设置 等。与生产集群唯一不同的是它不发送/接收生产流量。

我们在测试集群上模拟写入负载,同时避免复制滞后。写入负载不会太大,但是有一些有意地写入相同数据集的竞争请求。这在正常情况下并不是很有用,但是事实证明这在故障转移中是有用的,我们将会稍后简要描述它。

我们的测试集群有来自三个数据中心的典型的服务器。我们希望故障转移能够从同一个数据中心内晋升替代副本。我们希望在这样的限制下尽可能多地恢复副本。我们要求尽可能地实现这两者。协调器对拓扑结构没有 先验假定 prior assumption ;它必须依据崩溃时的状态作出反应。

然而,我们有兴趣创建各种复杂而多变的故障恢复场景。我们的故障转移测试脚本为故障转移提供了基础:

  • 它能够识别现有的主服务器
  • 它能够重构拓扑结构,来代表主服务器下的所有的三个数据中心。不同的数据中心具有不同的网络延迟,并且预期会在不同的时间对主机崩溃做出反应。
  • 能够选择崩溃方式。可以选择干掉主服务器(kill -9)或网络隔离(比较好的方式: iptables -j REJECT 或无响应的方式: iptables -j DROP)方式。

脚本通过选择的方法使主机崩溃,并等待协调器可靠地检测到崩溃然后执行故障转移。虽然我们期望检测和晋升在 30 秒钟内完成,但脚本会稍微放宽这一期望,并在查找故障转移结果之前休眠一段指定的时间。然后它将检查:

  • 一个新的(不同的)主服务器是否到位
  • 集群中有足够的副本
  • 主服务器是可写的
  • 对主服务器的写入在副本上可见
  • 内部服务发现项已更新(如预期般识别到新的主服务器;移除旧的主服务器)
  • 其他内部检查

这些测试可以证实故障转移是成功的,不仅是 MySQL 级别的,而是在更大的基础设施范围内成功的。VIP 被赋予;特定的服务已经启动;信息到达了应该去的地方。

该脚本进一步继续恢复那个失败的服务器:

  • 从备份恢复它,从而隐含地测试了我们的备份/恢复过程
  • 验证服务器配置是否符合预期(该服务器不再认为其是主服务器)
  • 将其加入到复制集群,期望找到在主服务器上写入的数据

看一下以下可视化的计划的故障转移测试:从运行良好的群集,到在某些副本上发现问题,诊断主服务器(7136)是否死机,选择一个服务器(a79d)来晋升,重构该服务器下的拓扑,晋升它(故障切换成功),恢复失败的(原)主服务器并将其放回群集。

automated master failover

测试失败怎么样?

我们的测试脚本使用了一种“停止世界”的方法。任何故障切换​​组件中的单个故障都将导致整个测试失败,因此在有人解决该问题之前,无法进行任何进一步的自动化测试。我们会得到警报,并检查状态和日志进行处理。

脚本将各种情况下失败,如不可接受的检测或故障转移时间;备份/还原出现问题;失去太多服务器;在故障切换后的意外配置等等。

我们需要确保协调器正确地连接服务器。这是竞争性写入负载有用的地方:如果设置不正确,复制很容易中断。我们会得到 DUPLICATE KEY 或其他错误提示出错。

这是特别重要的,因此我们改进协调器并引入新的行为,以允许我们在安全的环境中测试这些变化。

出现:混乱测试

上面所示的测试程序将捕获(并已经捕获)我们基础设施许多部分的问题。这些够了吗?

在生产环境中总是有其他的东西。有些特定测试方法不适用于我们的生产集群。它们不具有相同的流量和流量方式,也不具有完全相同的服务器集。故障类型可能有所不同。

我们正在为我们的生产集群设计混乱测试。 混乱测试将会在我们的生产中,但是按照预期的时间表和充分控制的方式来逐个破坏我们的部分生产环境。 混乱测试在恢复机制中引入更高层次的信赖,并影响(因此测试)我们的基础设施和应用程序的更大部分。

这是微妙的工作:当我们承认需要混乱测试时,我们也希望可以避免对我们的服务造成不必要的影响。不同的测试将在风险级别和影响方面有所不同,我们将努力确保我们的服务的可用性。

模式迁移

我们使用 gh-ost来运行实时 模式迁移 schema migration 。gh-ost 是稳定的,但也处于活跃开发中,重大新功能正在不断开发和计划中。

gh-ost 通过将数据复制到 ghost 表来迁移,将由二进制日志拦截的进一步更改应用到 ghost 表中,就如其正在写入原始表。然后它将 ghost 表交换代替原始表。迁移完成时,GitHub 继续使用由 gh-ost 生成和填充的表。

在这个时候,几乎所有的 GitHub 的 MySQL 数据都被 gh-ost 重新创建,其中大部分重新创建多次。我们必须高度信赖 gh-ost,让它一遍遍地操弄我们的数据,即使它还处于活跃开发中。下面是我们如何获得这种信赖的。

gh-ost 提供生产环境测试能力。它支持在副本上运行迁移,其方式与在主服务器上运行的方式大致相同: gh-ost 将连接到副本,并将其视为主服务器。它将采用与实际主机迁移相同的方式解析其二进制日志。但是,它将复制行并将二进制日志事件应用于副本,并避免对主服务器进行写入。

我们在生产环境中给 gh-ost 提供专用的副本。这些副本并不为生产环境提供服务。每个这样的副本将检索生产表的当前列表,并以随机顺序对其进行迭代。一个接一个地选择一个表并在该表上执行复制迁移。迁移实际上并不修改表结构,而是运行一个微不足道的 ENGINE=InnoDB 更改。该测试运行迁移时,如果表正在生产环境中使用,会复制实际的生产数据,并接受二进制日志之外的真实的生产环境流量。

这些迁移可以被审计。以下是我们如何从对话中(chatops)检查运行中的测试状态:

ggunson

.migration test-status

Hubot

# Migrating `prod`.`pull_requests`; Ghost table is `prod`.`_pull_requests_gho`
# Migrating ghost-db-mysql-0007:3306; inspecting ghost-db-mysql-0007:3306; executing on ghost-db-mysql-0007
# Migration started at Mon Jan 30 02:13:39 -0800 2017
# chunk-size: 2500; max-lag-millis: 1500ms; max-load: Threads_running=30; critical-load: Threads_running=1000; nice-ratio: 0.000000
# throttle-additional-flag-file: /tmp/gh-ost.throttle
# panic-flag-file: /tmp/ghost-test-panic.flag
# Serving on unix socket: /tmp/gh-ost.test.sock
Copy: 57992500/86684838 66.9%; Applied: 57708; Backlog: 1/100; Time: 3h28m38s(total), 3h28m36s(copy); streamer: mysql-bin.000576:142993938; State: migrating; ETA: 1h43m12s

当测试迁移完成表数据的复制时,它将停止复制并执行切换,使用 ghost 表替换原始表,然后交换回来。我们对实际替换数据并不感兴趣。相反,我们将留下原始的表和 ghost 表,它们应该是相同的。我们通过校验两个表的整个表数据来验证。

测试能以下列方式完成:

  • 成功 :一切顺利,校验和相同。我们期待看到这一结果。
  • 失败 :执行问题。这可能偶尔发生,因为迁移进程被杀死、复制问题等,并且通常与 gh-ost 自身无关。
  • 校验失败 :表数据不一致。对于被测试的分支,这个需要修复。对于正在进行的 master 分支测试,这意味着立即阻止生产迁移。我们不会遇到后者。

测试结果经过审核,发送到机器人聊天室,作为事件发送到我们的度量系统。下图中的每条垂直线代表成功的迁移测试:

automated master failover

这些测试不断运行。如果发生故障,我们会收到通知。当然,我们可以随时访问机器人聊天室(chatops),了解发生了什么。

测试新版本

我们不断改进 gh-ost。我们的开发流程基于 git 分支,然后我们通过拉取请求(PR)来提供合并。

提交的 gh-ost 拉取请求(PR)通过持续集成(CI)进行基本的编译和单元测试。一旦通过,该 PR 在技术上就有资格合并,但更好的是它有资格通过 Heaven 进行部署。作为我们基础架构中的敏感组件,在其进入 master 分支前,我们会小心部署分支进行密集测试。

shlomi-noach

.deploy gh-ost/fix-reappearing-throttled-reasons to prod/ghost-db-mysql-0007

Hubot

@shlomi-noach is deploying gh-ost/fix-reappearing-throttled-reasons (baee4f6) to production (ghost-db-mysql-0007). 
@shlomi-noach's production deployment of gh-ost/fix-reappearing-throttled-reasons (baee4f6) is done! (2s) 
@shlomi-noach, make sure you watch for exceptions in haystack

jonahberquist

.deploy gh-ost/interactive-command-question to prod/ghost-db-mysql-0012

Hubot

@jonahberquist is deploying gh-ost/interactive-command-question (be1ab17) to production (ghost-db-mysql-0012). 
@jonahberquist's production deployment of gh-ost/interactive-command-question (be1ab17) is done! (2s) 
@jonahberquist, make sure you watch for exceptions in haystack

shlomi-noach

.wcid gh-ost

Hubot

shlomi-noach testing fix-reappearing-throttled-reasons 41 seconds ago: ghost-db-mysql-0007 
jonahberquist testing interactive-command-question 7 seconds ago: ghost-db-mysql-0012 

Nobody is in the queue.

一些 PR 很小,不影响数据本身。对状态消息,交互式命令等的更改对 gh-ost 应用程序的影响较小。而其他的 PR 对迁移逻辑和操作会造成重大变化,我们将严格测试这些,通过我们的生产表车队运行这些,直到其满足了这些改变不会造成数据损坏威胁的程度。

总结

在整个测试过程中,我们建立对我们的系统的信赖。通过自动化这些测试,在生产环境中,我们得到了一切都按预期工作的反复确认。随着我们继续发展我们的基础设施,我们还通过调整测试来覆盖最新的变化。

产品总会有令你意想不到的未被测试覆盖的场景。我们对生产环境的测试越多,我们对应用程序的期望越多,基础设施的能力就越强。


via: https://githubengineering.com/mysql-testing-automation-at-github/

作者:tomkrouperShlomi Noach 译者:MonkeyDEcho 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

构建一个健壮的系统需要为故障而设计。作为 GitHub 的网站可靠性工程师(SRE),我们一直在寻求通过冗余来帮助缓解问题,今天将讨论最近我们所做的工作,以便支持你通过 DNS 来查找我们的服务器。

大型 DNS 提供商在其服务中构建了多级冗余,出现导致中断的问题时,可以采取措施来减轻其影响。最佳选择之一是把你的 区域 zone 的权威服务分割到多个服务提供商中。启用 分割权威 split authority 很简单,你只需在域名注册商配置两套或多套你区域的名称服务器,然后 DNS 请求将分割到整个列表中。但是,你必须在多个提供商之间对这些区域的记录保持同步,并且,根据具体情况这可能要么设置复杂,要么是完全手动的过程。

$ dig NS github.com. @a.gtld-servers.net.

...

;; QUESTION SECTION:
;github.com.            IN  NS

;; AUTHORITY SECTION:
github.com.     172800  IN  NS  ns4.p16.dynect.net.
github.com.     172800  IN  NS  ns-520.awsdns-01.net.
github.com.     172800  IN  NS  ns1.p16.dynect.net.
github.com.     172800  IN  NS  ns3.p16.dynect.net.
github.com.     172800  IN  NS  ns-421.awsdns-52.com.
github.com.     172800  IN  NS  ns-1283.awsdns-32.org.
github.com.     172800  IN  NS  ns2.p16.dynect.net.
github.com.     172800  IN  NS  ns-1707.awsdns-21.co.uk.

...

上面的查询是向 TLD 名称服务器 询问 github.com.NS 记录。它返回了在我们在域名注册商中配置的值,在本例中,一共有两个 DNS 服务提供商,每个四条记录。如果其中一个提供商发生中断,那么其它的仍有希望可以服务请求。我们在各个地方同步记录,并且可以安全地修改它们,而不必担心数据陈旧或状态不正确。

完整地配置分割权威的最后一部分是在两个 DNS 服务提供商中将所有名称服务器作为顶层 NS 记录添加到区域的根中。

$ dig NS github.com. @ns1.p16.dynect.net.

...

;; QUESTION SECTION:
;github.com.            IN  NS

;; ANSWER SECTION:
github.com.     551 IN  NS  ns1.p16.dynect.net.
github.com.     551 IN  NS  ns2.p16.dynect.net.
github.com.     551 IN  NS  ns-520.awsdns-01.net.
github.com.     551 IN  NS  ns3.p16.dynect.net.
github.com.     551 IN  NS  ns-421.awsdns-52.com.
github.com.     551 IN  NS  ns4.p16.dynect.net.
github.com.     551 IN  NS  ns-1283.awsdns-32.org.
github.com.     551 IN  NS  ns-1707.awsdns-21.co.uk.

在 GitHub,我们有几十个区域和数千条记录,而大多数这些区域并没有关键到需要冗余,因此我们只需要处理一部分。我们希望有能够在多个 DNS 服务提供商中保持这些记录同步的方案,并且更一般地管理内部和外部的所有 DNS 记录。所以今天我们宣布了 OctoDNS

octoDNS logo

配置

OctoDNS 能够让我们重新打造我们的 DNS 工作流程。我们的区域和记录存储在 Git 仓库的配置文件中。对它们的变更使用 GitHub 流,并像个站点一样用分支部署。我们甚至可以做个 “空” 部署来预览哪些记录将在变更中修改。配置文件是 yaml 字典,每个区域一个,它的顶层的键名是记录名称,键值是 ttl、类型和类型特定的数据。例如,当包含在区域文件 github.com.yaml 中时,以下配置将创建 octodns.github.com.A 记录。

octodns:
  type: A
  values:
    - 1.2.3.4
    - 1.2.3.5

配置的第二部分将记录数据的源映射到 DNS 服务提供商。下面的代码片段告诉 OctoDNS 从 config 提供程序加载区域 github.com,并将其结果同步到 dynroute53

zones:
  github.com.:
    sources:
      - config
    targets:
      - dyn
      - route53

同步

一旦我们的配置完成,OctoDNS 就可以评估当前的状态,并建立一个计划,其中列出将需要将目标状态与源相匹配的一组更改。在下面的例子中,octodns.github.com 是一个新的记录,所以所需的操作是在两者中创建记录。

$ octodns-sync --config-file=./config/production.yaml
...
********************************************************************************
* github.com.
********************************************************************************
* route53 (Route53Provider)
*   Create <ARecord A 60, octodns.github.com., [u'1.2.3.4', '1.2.3.5']>
*   Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0
* dyn (DynProvider)
*   Create <ARecord A 60, octodns.github.com., [u'1.2.3.4', '1.2.3.5']>
*   Summary: Creates=1, Updates=0, Deletes=0, Existing Records=0
********************************************************************************
...

默认情况下 octodns-sync 处于模拟运行模式,因此不会采取任何行动。一旦我们审阅了变更,并对它们感到满意,我们可以添加 `--doit' 标志并再次运行命令。OctoDNS 将继续它的处理流程,这一次将在 Route53 和 Dynect 中进行必要的更改,以便创建新的记录。

$ octodns-sync --config-file=./config/production.yaml --doit
...

此刻,在两个 DNS 服务提供商里我们有了相同的数据记录,并可以轻松地分割我们的 DNS 请求给它们,并知道它们将提供准确的结果。当我们直接运行上面的 OctoDNS 命令时,我们的内部工作流程依赖于部署脚本和 chatops。你可以在 README 的工作流程部分中找到更多信息。

总结

我们认为大多数网站可以从分割权威中受益,并且希望用 OctoDNS,其中最大的障碍已被扫除。即使对分割权威不感兴趣,OctoDNS 仍然值得一看,因为它将基础设施即代码的好处带给了 DNS。

想帮助 GitHub SRE 团队解决有趣的问题吗?我们很乐意加入我们。在这里申请


via: https://githubengineering.com/enabling-split-authority-dns-with-octodns/

作者:Ross McFarland 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过使用 GitHub 的 拉取请求 Pull Request 正确地进行代码审核,把时间更多的花在构建上,而在修复上少用点时间。

Measure

如果你不是每天编写代码,你可能不知道软件开发人员日常面临的一些问题。

  • 代码中的安全漏洞
  • 导致应用程序崩溃的代码
  • 被称作 “技术债务” 和之后需要重写的代码
  • 在某处你所不知道地方的代码已经被重写

代码审查 Code review 可以允许其他的人或工具来检查代码,帮助我们改善所编写的软件。这种审查(也称为 同行评审 peer review )能够通过自动化代码分析或者测试覆盖工具来进行,是软件开发过程中两个重要的部分,它能够节省数小时的手工工作。同行评审是开发人员审查彼此工作的一个过程。在软件开发的过程中,速度和紧迫性是两个经常提及的问题。如果你没有尽快的发布,你的竞争对手可能会率先发布新功能。如果你不能够经常发布新的版本,你的用户可能会怀疑您是否仍然关心改进你的应用程序。

权衡时间:代码审查与缺陷修复

如果有人能够以最小争议的方式汇集多种类型的代码审查,那么随着时间的推移,该软件的质量将会得到改善。如果认为引入新的工具或流程最先导致的不是延迟,那未免太天真了。但是代价更高昂的是:修复生产环境中的错误花费的时间,或者在放到生产环境之前改进软件所花费的时间。即使新工具延迟了新功能的发布和得到客户欣赏的时间,但随着软件开发人员提高自己的技能,该延迟会缩短,软件开发周期将会回升到以前的水平,而同时缺陷将会减少。

通过代码审查实现提升代码质量目标的关键之一就是使用一个足够灵活的平台,允许软件开发人员快速编写代码,置入他们熟悉的工具,并对彼此进行同行评审。 GitHub 就是这样的平台的一个很好的例子。然而,只是把你的代码放在 GitHub 上并不会魔术般地使代码审查发生;你必须使用 拉取请求 Pull Request 来开始这个美妙的旅程。

拉取请求:关于代码的现场讨论

拉取请求 Pull Request 是 Github 上的一个工具,允许软件开发人员讨论并提出对项目的主要代码库的更改,这些更改稍后可以部署给所有用户看到。这个功能创建于 2008 年 2 月,其目的是在接受(合并)之前,对某人的建议进行更改,然后在部署到生产环境中,供最终用户看到这种变化。

拉取请求开始是以一种松散的方式让你为某人的项目提供更改,但是它们已经演变成:

  • 关于你想要合并的代码的现场讨论
  • 提升了所更改内容的可视功能
  • 整合了你最喜爱的工具
  • 作为受保护的分支工作流程的一部分可能需要显式的拉取请求评审

对于代码:URL 是永久的

看看上述的前两个点,拉取请求促成了一个正在进行的代码讨论,使代码变更可以更醒目,并且使您很容易在审查的过程中找到所需的代码。无论是对于新人还是有经验的开发人员,能够回顾以前的讨论,了解一个功能为什么以这种方式开发出来,或者与另一个相关功能的讨论相联系起来是无价的。当跨多个项目协调,并使每个人尽可能接近代码时,前后讨论的内容也非常重要。如果这些功能仍在开发中,重要的是能够看到上次审查以来更改了哪些内容。毕竟,审查小的更改要比大的容易得多,但不可能全都是小功能。因此,重要的是能够找到你上次审查,并只看到从那时以来的变化。

集成工具:软件开发人员的偏执

再看下上述第三点,GitHub 上的拉取请求有很多功能,但开发人员总是偏好第三方工具。代码质量是个完整的代码审查领域,它涉及到其它组件的代码评审,而这些评审不一定是由人完成的。检测“低效”或缓慢的代码、具有潜在安全漏洞或不符合公司标准的代码是留给自动化工具的任务。类似 SonarQubeCode Climatecan 这样工具可以分析你的代码,而像 CodecovCoveralls 的这样工具可以告诉你刚刚写的新代码还没有得到很好的测试。这些工具最令人称奇的是,它们可以集成到 GitHub 中,并把它们的发现汇报到拉取请求当中!这意味着该过程中不仅是人们在审查代码,而且工具也在会在那里报告情况。这样每个人都可以完全了解一个功能是如何开发的。

最后,根据您的团队的偏好,您可以利用受保护的分支工作流 必需状态 required status 功能来要求进行工具审查和同行评审。

虽然您可能只是刚刚开始您的软件开发之旅,或者是一位希望知道项目正在做什么的业务利益相关者,抑或是一位想要确保项目的及时性和质量的项目经理,你都可以通过设置批准流程来参与到拉取请求中,并考虑集成更多的工具以确保质量,这在任何级别的软件开发中都很重要。

无论是为您的个人网站,贵公司的在线商店,还是想用最新的组合以获得最大的收获,编写好的软件都需要进行良好的代码审查。良好的代码审查涉及到正确的工具和平台。要了解有关 GitHub 和软件开发过程的更多信息,请参阅 O'Reilly 的 《GitHub 简介》 一书, 您可以在其中了解创建项目、启动拉取请求以及概要了解团队的软件开发流程。


作者简介:

Brent Beer

Brent Beer 通过大学的课程、对开源项目的贡献,以及担任专业网站开发人员使用 Git 和 GitHub 已经超过五年了。在担任 GitHub 上的培训师时,他也成为 O’Reilly 的 《GitHub 简介》的出版作者。他在阿姆斯特丹担任 GitHub 的解决方案工程师,帮助 Git 和 GitHub 向世界各地的开发人员提供服务。

Peter Bell

Peter Bell 是 Ronin 实验室的创始人以及 CTO。培训是存在问题的,我们通过技术提升培训来改进它!他是一位有经验的企业家、技术专家、敏捷教练和 CTO,专门从事 EdTech 项目。他为 O'Reilly 撰写了 《GitHub 简介》,为代码学校创建了“精通 GitHub ”课程,为 Pearson 创建了“ Git 和 GitHub 现场课”课程。他经常在国际和国际会议上发表 ruby、 nodejs、NoSQL(尤其是 MongoDB 和 neo4j )、云计算、软件工艺、java、groovy、j 等的演讲。


via: https://www.oreilly.com/ideas/how-to-use-pull-requests-to-improve-your-code-reviews

作者:Brent Beer, Peter Bell 译者:MonkeyDEcho 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在 GitHub,我们最近从头改进了 DNS。这包括了我们如何与外部 DNS 提供商交互以及我们如何在内部向我们的主机提供记录。为此,我们必须设计和构建一个新的 DNS 基础设施,它可以随着 GitHub 的增长扩展并跨越多个数据中心。

以前,GitHub 的 DNS 基础设施相当简单直接。它包括每台服务器上本地的、只具备转发功能的 DNS 缓存服务器,以及一对被所有这些主机使用的缓存服务器和权威服务器主机。这些主机在内部网络以及公共互联网上都可用。我们在缓存守护程序中配置了 区域 zone 存根 stub ,以在本地进行查询,而不是在互联网上进行递归。我们还在我们的 DNS 提供商处设置了 NS 记录,它们将特定的内部 domain 指向这对主机的公共 IP,以便我们网络外部的查询。

这个配置使用了很多年,但它并非没有缺点。许多程序对于解析 DNS 查询非常敏感,我们遇到的任何性能或可用性问题在最好的情况下也会导致服务排队和性能降级,而最坏情况下客户会遭遇服务中断。配置和代码的更改可能会导致查询率发生大幅度的意外变化。因此超出这两台主机的扩展成为了一个问题。由于这些主机的网络配置,如果我们只是继续添加 IP 和主机的话存在一些本身的问题。在试图解决和补救这些问题的同时,由于缺乏测量指标和可见性,老旧的系统难以识别问题的原因。在许多情况下,我们使用 tcpdump 来识别有问题的流量和查询。另一个问题是在公共 DNS 服务器上运行,我们处于泄露内部网络信息的风险之下。因此,我们决定建立更好的东西,并开始确定我们对新系统的要求。

我们着手设计一个新的 DNS 基础设施,以改善上述包括扩展和可见性在内的运维问题,并引入了一些额外的需求。我们希望通过外部 DNS 提供商继续运行我们的公共 DNS 域,因此我们构建的系统需要与供应商无关。此外,我们希望该系统能够服务于我们的内部和外部域,这意味着内部域仅在我们的内部网络上可用,除非另有特别配置,而外部域也不用离开我们的内部网络就可解析。我们希望新的 DNS 架构不但可以基于部署的工作流进行更改,并可以通过我们的仓库和配置系统使用 API 自动更改 DNS 记录。新系统不能有任何外部依赖,太依赖于 DNS 功能将会陷入级联故障,这包括连接到其他数据中心和其中可能有的 DNS 服务。我们的旧系统将缓存服务器和权威服务器在同一台主机上混合使用。我们想转到具有独立角色的分层设计。最后,我们希望系统能够支持多数据中心环境,无论是 EC2 还是裸机。

实现

为了构建这个系统,我们确定了三类主机: 缓存主机 cache 边缘主机 edge 权威主机 authority 。缓存主机作为 递归解析器 recursive resolver 和 DNS “路由器” 缓存来自边缘层的响应。边缘层运行 DNS 权威守护程序,用于响应缓存层对 DNS 区域 zone 的请求,其被配置为来自权威层的 区域传输 zone transfer 。权威层作为隐藏的 DNS 主服务器 master ,作为 DNS 数据的规范来源,为来自边缘主机的 区域传输 zone transfer 提供服务,并提供用于创建、修改或删除记录的 HTTP API。

在我们的新配置中,缓存主机存在于每个数据中心中,这意味着应用主机不需要穿过数据中心边界来检索记录。缓存主机被配置为将 区域 zone 映射到其 地域 region 内的边缘主机,以便将我们的内部 区域 zone 路由到我们自己的主机。未明确配置的任何 区域 zone 将通过互联网递归解析。

边缘主机是地域性的主机,存在我们的网络边缘 PoP( 存在点 Point of Presence )内。我们的 PoP 有一个或多个依赖于它们进行外部连接的数据中心,没有 PoP 数据中心将无法访问互联网,互联网也无法访问它们。边缘主机对所有的权威主机执行 区域传输 zone transfer ,无论它们存在什么 地域 region 位置 location ,并将这些区域存在本地的磁盘上。

我们的权威主机也是地域性的主机,只包含适用于其所在 地域 region 区域 zone 。我们的仓库和配置系统决定一个 区域 zone 存放在哪个 地域性权威主机 regional authority ,并通过 HTTP API 服务来创建和删除记录。 OctoDNS 将区域映射到地域性权威主机,并使用相同的 API 创建静态记录,以及确保动态源处于同步状态。对于外部域 (如 github.com),我们有另外一个单独的权威主机,以允许我们可以在连接中断期间查询我们的外部域。所有记录都存储在 MySQL 中。

可运维性

迁移到更现代的 DNS 基础设施的巨大好处是可观察性。我们的旧 DNS 系统几乎没有指标,只有有限的日志。决定使用哪些 DNS 服务器的一个重要因素是它们所产生的指标的广度和深度。我们最终用 Unbound 作为缓存主机,NSD 作为边缘主机,PowerDNS 作为权威主机,所有这些都已在比 GitHub 大得多的 DNS 基础架构中得到了证实。

当在我们的裸机数据中心运行时,缓存通过私有的 任播 anycast IP 访问,从而使之可以到达最近的可用缓存主机。缓存主机已经以机架感知的方式部署,在它们之间提供了一定程度的平衡负载,并且与一些电源和网络故障模式相隔离。当缓存主机出现故障时,通常将用其进行 DNS 查询的服务器现在将自动路由到下一个最接近的缓存主机,以保持低延迟并提供对某些故障模式的容错。任播允许我们扩展单个 IP 地址后面的缓存数量,这与先前的配置不同,使得我们能够按 DNS 需求量运行尽可能多的缓存主机。

无论地域或位置如何,边缘主机使用权威层进行区域传输。我们的 区域 zone 并没有大到在每个 地域 region 保留所有 区域 zone 的副本成为问题。(LCTT 译注:此处原文“Our zones are not large enough that keeping a copy of all of them in every region is a problem.”,根据上下文理解而翻译。)这意味着对于每个区域,即使某个地域处于脱机状态,或者上游服务提供商存在连接问题,所有缓存服务器都可以访问具备所有区域的本地副本的本地边缘服务器。这种变化在面对连接问题方面已被证明是相当有弹性的,并且在不久前本来会导致客户面临停止服务的故障期间帮助保持 GitHub 可用。

那些区域传输包括了内部和外部域从它们相应的权威服务器进行的传输。正如你可能会猜想像 github.com 这样的区域是外部的,像 github.net 这样的区域通常是内部的。它们之间的区别仅在于我们使用的类型和存储在其中的数据。了解哪些区域是内部和外部的,为我们在配置中提供了一些灵活性。

$ dig +short github.com
192.30.253.112
192.30.253.113

公共 区域 zone 同步到外部 DNS 提供商,并且是 GitHub 用户每天使用的 DNS 记录。另外,公共区域在我们的网络中是完全可解析的,而不需要与我们的外部提供商进行通信。这意味着需要查询 api.github.com 的任何服务都可以这样做,而无需依赖外部网络连接。我们还使用了 Unbound 的 stub-first 配置选项,它给了我们第二次查询的机会,如果我们的内部 DNS 服务由于某些原因在外部查询失败,则可以进行第二次查找。

$ dig +short time.github.net
10.127.6.10

大部分的 github.net 区域是完全私有的,无法从互联网访问,它只包含 RFC 1918 中规定的 IP 地址。每个地域和站点都划分了私有区域。每个地域和/或站点都具有适用于该位置的一组子区域,子区域用于管理网络、服务发现、特定的服务记录,并且还包括在我们仓库中的配置主机。私有区域还包括 PTR 反向查找区域。

总结

用一个新系统替换可以为数百万客户提供服务的旧系统并不容易。使用实用的、基于需求的方法来设计和实施我们的新 DNS 系统,才能打造出一个能够迅速有效地运行、并有望与 GitHub 一起成长的 DNS 基础设施。

想帮助 GitHub SRE 团队解决有趣的问题吗?我们很乐意你加入我们。在这申请


via: https://githubengineering.com/dns-infrastructure-at-github/

作者:Joe Williams 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

像大多数新手一样,我一开始是在 StackOverflow 上搜索 Git 命令,然后把答案复制粘贴,并没有真正理解它们究竟做了什么。

Image credit: XKCD

我曾经想过:“如果有一个最常见的 Git 命令的列表,以及它们的功能是什么,这不是极好的吗?”

多年之后,我编制了这样一个列表,并且给出了一些最佳实践,让新手们甚至中高级开发人员都能从中发现有用的东西。

为了保持实用性,我将这个列表与我过去一周实际使用的 Git 命令进行了比较。

几乎每个开发人员都在使用 Git,当然很可能是 GitHub。但大多数开发者大概有 99% 的时间只是使用这三个命令:

git add --all
git commit -am "<message>"
git push origin master

如果你只是单枪匹马,或者参加一场黑客马拉松或开发一次性的应用时,它工作得很好,但是当稳定性和可维护性开始成为一个优先考虑的事情后,清理提交、坚持分支策略和提交信息的规范性就变得很重要。

我将从常用命令的列表开始,使新手更容易了解 Git 能做什么,然后进入更高级的功能和最佳实践。

经常使用的命令

要想在 仓库 repo 中初始化 Git,你只需输入以下命令即可。如果你没有初始化 Git,则不能在该仓库内运行任何其他的 Git 命令。

git init

如果你在使用 GitHub,而且正在将代码推送到在线存储的 GitHub 仓库中,那么你正在使用的就是 远程 remote 仓库。该远程仓库的默认名称(也称为别名)为 origin。如果你已经从 Github 复制了一个项目,它就有了一个 origin。你可以使用命令 git remote -v 查看该 origin,该命令将列出远程仓库的 URL。

如果你初始化了自己的 Git 仓库,并希望将其与 GitHub 仓库相关联,则必须在 GitHub 上创建一个,复制新仓库提供的 URL,并使用 git remote add origin <URL> 命令,这里使用 GitHub 提供的 URL 替换 <URL>。这样,你就可以添加、提交和推送更改到你的远程仓库了。

最后一条命令用在当你需要更改远程仓库时。如果你从其他人那里复制了一个仓库,并希望将远程仓库从原始所有者更改为你自己的 GitHub 帐户。除了改用 set-url 来更改远程仓库外,流程与 git remote add origin 相同。

git remote -v
git remote add origin <url>
git remote set-url origin <url>

复制仓库最常见的方式是使用 git clone,后跟仓库的 URL。

请记住,远程仓库将连接到克隆仓库原属于的帐户。所以,如果你克隆了一个属于别人的仓库,你将无法推送到 GitHub,除非你使用上面的命令改变了 origin

git clone <url>

你很快就会发现自己正在使用分支。如果你还不理解什么是分支,有许多其他更深入的教程,你应该先阅读它们,再继续下面的操作。(这里是一个教程

命令 git branch 列出了本地机器上的所有分支。如果要创建一个新的分支,可以使用命令 git branch <name>,其中 <name> 表示分支的名字,比如说 master

git checkout <name> 命令可以切换到现有的分支。你也可以使用 git checkout -b 命令创建一个新的分支并立即切换到它。大多数人都使用此命令而不是单独的 branchcheckout 命令。

git branch
git branch <name>
git checkout <name>
git checkout -b <name>

如果你对一个分支进行了一系列的更改,假如说此分支名为 develop,如果想要将该分支合并回主分支(master)上,则使用 git merge <branch> 命令。你需要先检出(checkout)主分支,然后运行 git merge developdevelop 合并到主分支中。

git merge <branch>

如果你正在与多个人进行协作,你会发现有时 GitHub 的仓库上已经更新了,但你的本地却没有做相应的更改。如果是这样,你可以使用 git pull origin <branch> 命令从远程分支中拉取最新的更改。

git pull origin <branch>

如果您好奇地想看到哪些文件已被更改以及哪些内存正在被跟踪,可以使用 git status 命令。如果要查看每个文件的更改,可以使用 git diff 来查看每个文件中更改的行。

git status
git diff --stat

高级命令和最佳实践

很快你会到达一个阶段,这时你希望你的提交看起来整洁一致。你可能还需要调整你的提交记录,使得提交更容易理解或者能还原一个意外的有破坏性的更改。

git log 命令可以输出提交的历史记录。你将使用它来查看提交的历史记录。

你的提交会附带消息和一个哈希值,哈希值是一串包含数字和字母的随机序列。一个哈希值示例如下:c3d882aa1aa4e3d5f18b3890132670fbeac912f7

git log

假设你推送了一些可能破坏了你应用程序的东西。你最好回退一个提交然后再提交一次正确的,而不是修复它和推送新的东西。

如果你希望及时回退并从之前的提交中检出(checkout)你的应用程序,则可以使用该哈希作为分支名直接执行此操作。这将使你的应用程序与当前版本分离(因为你正在编辑历史记录的版本,而不是当前版本)。

git checkout c3d88eaa1aa4e4d5f

然后,如果你在那个历史分支中做了更改,并且想要再次推送,你必须使用强制推送。

注意:强制推送是危险的,只有在绝对必要的时候才能执行它。它将覆盖你的应用程序的历史记录,你将失去之后版本的任何信息。

git push -f origin master

在其他时候,将所有内容保留在一个提交中是不现实的。也行你想在尝试有潜在风险的操作之前保存当前进度,或者也许你犯了一个错误,但希望在你的版本历史中避免尴尬地留着这个错误。对此,我们有 git rebase

假设你在本地历史记录上有 4 个提交(没有推送到 GitHub),你要回退这是个提交。你的提交记录看起来很乱很拖拉。这时你可以使用 rebase 将所有这些提交合并到一个简单的提交中。

git rebase -i HEAD~4

上面的命令会打开你计算机的默认编辑器(默认为 Vim,除非你将默认修改为其他的),提供了几个你准备如何修改你的提交的选项。它看起来就像下面的代码:

pick 130deo9 oldest commit message
pick 4209fei second oldest commit message
pick 4390gne third oldest commit message
pick bmo0dne newest commit message

为了合并这些提交,我们需要将 pick 选项修改为 fixup(如代码下面的文档所示),以将该提交合并并丢弃该提交消息。请注意,在 Vim 中,你需要按下 ai 才能编辑文本,要保存退出,你需要按下 Esc 键,然后按 shift + z + z。不要问我为什么,它就是这样。

pick 130deo9 oldest commit message
fixup 4209fei second oldest commit message
fixup 4390gne third oldest commit message
fixup bmo0dne newest commit message

这将把你的所有提交合并到一个提交中,提交消息为 oldest commit message

下一步是重命名你的提交消息。这完全是一个建议的操作,但只要你一直遵循一致的模式,都可以做得很好。这里我建议使用 Google 为 Angular.js 提供的提交指南

为了更改提交消息,请使用 amend 标志。

git commit --amend

这也会打开 Vim,文本编辑和保存规则如上所示。为了给出一个良好的提交消息的例子,下面是遵循该指南中规则的提交消息:

feat: add stripe checkout button to payments page

- add stripe checkout button
- write tests for checkout

保持指南中列出的 类型 type 的一个优点是它使编写更改日志更加容易。你还可以在 页脚 footer (再次,在指南中规定的)中包含信息来引用 问题 issue

注意:如果你正在协作一个项目,并将代码推送到了 GitHub,你应该避免重新引用(rebase)并压缩(squash)你的提交。如果你开始在人们的眼皮子底下更改版本历史,那么你可能会遇到难以追踪的错误,从而给每个人都带来麻烦。

Git 有无数的命令,但这里介绍的命令可能是您最初几年编程所需要知道的所有。


Sam Corcos 是 Sightline Maps 的首席开发工程师和联合创始人,Sightline Maps 是最直观的 3D 打印地形图的平台,以及用于构建 Phoenix 和 React 的可扩展生产应用程序的中级高级教程网站 LearnPhoenix.io。使用优惠码:freecodecamp 取得 LearnPhoenix 的20美元。

(题图:GitHub Octodex


via: https://medium.freecodecamp.org/git-cheat-sheet-and-best-practices-c6ce5321f52

作者:Sam Corcos 译者:firmianay 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出