容器中窥世界
容器技术已经风靡全球,我们欢迎容器化领域的新霸主们。
然而,他们也给Riot的流水线工程团队(Pipeline Engineering team)的同事们带来了新挑战。我叫Maxfield Stewart,是Riot的工程师,我们组主要负责构建流水线(Pipeline)——从代码签入(check in)到部署的一切工作,甚至更多。如果说持续集成(continuous delivery)是一首主题曲的话,那么我们就是用清唱的方式演唱它。我们运转的是一个类似云的环境,管理着Riot最大的一个服务器和虚拟机集群。其 中的一个庞然大物是构建集群(Build Farm),由大量的物理机和虚拟机组成。它是从数年前的一个小集群发展而来的,当时只负责构建英雄联盟(League of Legends)的游戏客户端。
最近,我们已经融入了Docker容器技术。我们是如何将容器与传统的构建集群集成,并使其越来越像一个自我服务的云工作引擎的呢(self- service cloud-base work engine)?我们能否使用Dockerfiles定义构建环境,并与我们常用的开源架构相结合呢?我们又能不能抛弃传统的基于虚拟机的云,转身拥抱容 器云呢?
上述问题已经持续了相当长的一段时间,就像寒冰射手(Ashe)的箭一样。接下来,我将通过一个系列博客介绍我们的团队是如何尝试回答上述问题的。本文是 其中第一篇博客,主要是介绍我们的团队背景,以及我们为什么要整合容器技术。在后续博客中,我将具体地分享如何整合Jenkins和Docker。第一篇 教程是一个 基础介绍 。如果你对于使用容器创建构建集群、持续集成、帮助工程师快速交付,那么这个系列就是你要的。请准备好:我将从基本介绍,逐步深入,最终介绍如何使用Docker承载真正的业务(Serious Business)。
一年之前,我们将持续集成引入到了英雄联盟。在那之前,我们拼命地尝试以一个常规节奏来发布英雄联盟,但是我们步履维艰。因此我们打算尽可能自动 化这一切,从构建流水线到创建测试环境,获得了大量的成果,包括提高交付一致性、减少构建时间、改善总体完成度。英雄联盟从一天几次的构建,增长到了每天 30次构建。
构建英雄联盟可不是开玩笑的,其中包括了超过150个任务,我们构建每个重要的版本。每次构建有各种形式,从传统的debug构建到新版本,以及 专门为了包括腾讯和Garena在内的全球合作伙伴准备的变种版本。我们可以追踪到每次构建、什么测试环境、什么测试内容、PBE以及快速部署成产品。我 们可以一键创建测试环境,并且可以在几个星期内从20个测试环境增加到70多个,包括450多个虚拟机。构建英雄联盟只是构建集群的一部分工作,构建集群 本身支持了Riot各个工程团队的3300多个构建任务。然而,这一构建流程并不是完美的,这些陈旧的工具有时需要连接起来才能工作。在持续集成中,我们 秉持4项原则:
- 我们认为工程师团队必须能完全掌控他们的技术栈,包括对于构建环境的管理员权限。
- 我们认为配置即代码(Configuration as Code)。团队应当尽可能使用源码控制来维护他们自己的构建流水线和环境。
- 我们认为每当工程师执行一次构建,都需要针对所有可部署的配置构建一个可交付的版本。一个“构建”并不只是编译代码而已,而是所有可部署的组件的集合。
- 我们认为一次交付就是一个产品决策(shipping is a product decision)。只需按一下按钮,产品团队就能够部署并查看最新版本。
我们需要世界一流的技术栈才能达到这些目标。通常有三种选择:完全重头编写、购买别人的工具或者定制化开源项目。
我们选择了第三种。在这篇博客中,我不想比较各种CI工具。不过,通过修改开源工具来符合我们的需求是一个最好的折中方案:不需要重头编写;可以与开源世界合作;如果有必要的话,可以轻易脱离它。
因此,我们的技术栈非常简单:
- 开源版本的Jenkins
- Jenkins的任务DSL插件(Job DSL Plugin)
- Jenkins的构建流插件(Build Flow plugin)
- 工程师们连接各个组件的独创性
我们继续使用Jenkins,是因为它是灵活的、开源的、易于处理我们的基本构建操作。总体来说,Jenkins是易于创建一个构建流水线的,符 合我们持续集成的核心需求(如上所述)。作为一款广泛应用的开源工具,有一个有活力的社区可以与我们合作。与重头编写自定义工具相比,工程师团队可以利用 开源标准的实现,这是很有帮助的,也是具有风险的。开源标准经常变化,昨天的一个好主意明天就可能变成一个坏主意。然而,利用合适的插件和技术诀窍,我们 只用了少量的代码、配置和开销,就完成了一个全自动的持续集成链。
那么,Docker发挥了什么作用呢?让我们回想一下我提到的持续集成的核心原则。最近,我们团队遇到的一个挑战是构建环境的所有权 (ownership)。之前,工程师们通过Packer.io定义自己的虚拟机镜像,然后给产品团队集群的root权限。本质上,我们需要通过 Jenkins这一个工作流引擎(workflow engine)定义一个内部的云环境。我们探索了几个通用的配置管理工具,如Puppet和Chef,来实现虚拟云环境,并使工程师们能控制这些机器。
然后,Docker出现了。
这件事情就变得简单了:Dockerfile比其他工具更易于维护。在Docker的帮助下,我们意识到容器更容易管理了。如果我们把Docker中Dockerfile的概念和构建环境的所有权结合起来,我们就进入了工程天堂(engineering heaven)。
Docker很善于解决部署中的挑战。我主要关注Docker是如何帮助工作流引擎、构建系统和流水线,同时也熟悉了如何将其作为一个部署工具和 方法论。Riot管理着大量的微服务,而容器和微服务的组合就像花生酱和巧克力。因为Docker成为了一个标志(Thing(tm)),我们也会使用它 来解决其他的一些问题。
流水线工程团队的梦想变得更真实了:我们想要一个流水线构建工具,它能动态地加速持续集成流水线,使用框架代码来按需地一键构建环境。为了创建一个完整的构建流水线,我们之前是通过自动化配置虚拟机来实现的,现在我们认为使用Docker容器来完成。
需要说明的是,Docker并不是一个完美的整体解决方案(turnkey solution)。它不能解决Windows和OSX的构建环境中的问题,也不能和我们使用的每个工具结合。但是,Docker确实解决了Linux平 台中我们遇到的很多困难。在Riot,我们在平台和后端上进行了大量的工程工作。包括核心的后台服务在内,几乎所有的特性都是通过跑在Linux上的微服 务来提供的。因此,如何优化解决方案空间是值得我们投入时间和精力的。
我们已经开始将Docker与现有的构建栈结合,并获得了一些早期的成就。我们创建了Jenkins的一键部署环境,在容器中部署,加速了测试和 调试过程。我们从一个小型集群(大概500个任务)开始,使用容器作为构建环境,在所有权和迭代速度上团队也提供了积极的反馈,包括:
- 基于Linux构建微服务和网站的工程师们能够以编程的方式(programmatically)定义他们的构建环境了
- 本地的构建环境和构建集群中的构建环境是完全一致的(在后续博客中,我将介绍如何做到这一点)
- 动态资源分配意味着降低整体计算成本
- * 一台虚拟机可以处理4个不同的组合300多个构建任务,这原本是通过8台虚拟机完成的
这篇博客仅仅是一个系列话题的介绍,这个系列将覆盖多个领域,以教程的形式发布,提供实例和源码。首先,系列博客将介绍如何使用Docker来部 署Jenkins,包括各种最佳实践,并通过一个真实应用引入Docker的基础知识;然后,系列博客将探索容器化构建环境的各种方案,并介绍Riot是 如何将Docker融入Jenkins的生态环境;最后,将介绍流水线工程团队是如何完成最终目标的。
本质上来说,这一系列博客就是记录了这个旅程。就像之前宣称的,我们知道我们想要的就是Docker容器定义的构建环境,开发团队可以通过 Dockerfiles来维护它。在已知技术栈的情况下,我们希望能预生成构建流水线,这样一来,团队从创建代码库的第一天就能维护持续集成的构建流水 线。
我们希望通过系列博客来能分享我们的发现以及遭遇的挫折。这些或许不是什么大秘密,但可能是不容易发现的。我希望我们的系列博客能回报社区,并通过交流和对话学习到更多。
原文链接: THINKING INSIDE THE CONTAINER (翻译:夏彬 校对:)