10.4 构建和部署管道实战
从10.3节中介绍的通用架构中可以看到,在构建/部署管道背后有许多活动部件。由于本书的目的是“在实战中”向读者介绍知识,我们将详细介绍为EagleEye服务实现构建/部署管道的细节。图10-16列出了要用来实现这一管道的不同技术。
图10-16 EagleEye构建中使用的技术
(1)GitHub——GitHub是我们的源代码控制库。本书的所有应用程序代码都在GitHub中。选择GitHub作为源代码控制库出于两个原因:首先,我不想管理和维护自己的Git源代码管理服务器;其次,GitHub提供了各种各样的Web钩子和强大的基于REST的API,用于将GitHub集成到构建过程中。
(2)Travis CI——Travis CI是我用于构建和部署EagleEye微服务,并提供Docker镜像的持续集成引擎。Travis CI是一个基于云的、基于文件的CI引擎,它易于建立,并且与GitHub和Docker有着很强的集成能力。虽然Travis CI不像Jenkins这样的CI引擎功能那么全面,但对我们的使用来说已经足够了。10.5节和10.6节将介绍如何使用GitHub和Travis CI。
(3)Maven/Spotify Docke插件——虽然我们使用vanilla Maven编译、测试和打包Java代码,但我们使用的一个关键Maven插件是Spotify的Docker插件,这个插件允许我们从Maven内部启动Docker构建的创建。
(4)Docker——我选择Docker作为容器平台出于两个原因。首先,Docker在多个云服务提供商之间是可移植的。我可以采用相同的Docker容器,并以最少的工作将其部署到AWS、Azure或Cloud Foundry。其次,Docker是轻量级的。在本书结束时,读者将会构建并部署大约10个Docker容器(包括数据库服务器、消息传递平台和搜索引擎)。在本地桌面上部署相同数量的虚拟机将是很困难的,因为每个镜像的规模大,并且需要的运行速度高。Docker、Maven和Spotify的创建和配置将不在本章中讨论,而是留在附录A中介绍。
(5)Docker Hub——构建完服务并创建了Docker镜像之后,需要使用唯一的标识符对Docker镜像进行标记,并将它推送到中央存储库。对于Docker镜像存储库,我选择使用Docker Hub,即Docker公司的公共镜像存储库。
(6)Python——为了编写在部署Docker镜像之前执行的平台测试,我选择了Python作为编写平台测试的工具。我坚信在工作中应使用合适的工具。坦率地说,我认为Python是一种非常棒的编程语言,特别是对于编写基于REST的测试用例。
(7)Amazon的EC2容器服务(ECS)——我们的微服务的最终目标是将Docker实例部署到亚马逊的Docker平台。我选择亚马逊作为我的云平台,因为它是迄今为止最成熟的云提供商,它能让Docker服务的部署变得十分简单。
等等,你说Python什么
读者可能会觉得奇怪,我用Python编写平台测试,而不是用Java。我是故意这么做的。
Python(就像Groovy一样)是编写基于REST的测试用例的绝妙脚本语言。我坚信在工作中应使用合适的工具。对采用微服务的组织来说,我所见过的最大的思想转变之一是,选择语言的职责应该在开发团队中。我在太多的组织中目睹过对标准的教条式拥护(“我们的企业标准是Java……,所有的代码都必须用Java编写”)。因此,当10行的Groovy或Python脚本就可以完成这个工作时,我目睹过开发团队跳过这一选择,转而编写了一大堆Java代码。
我选择Python的第二个原因是,与单元测试和集成测试不同,平台测试是真正的“黑盒”测试,开发人员的行为就像在真实环境中运行的实际API的消费者。单元测试运行最低级别的代码,运行时不应该有任何外部依赖。集成测试上升了一个级别,它负责测试API,但是需要stub或mock关键的外部依赖项(如对其他服务的调用、数据库调用等)。平台测试应该是真正独立于底层基础设施的测试。
10.5 开始构建和部署管道:GitHub和Travis CI
有数十种源代码管理引擎和构建部署引擎(包括内部部署和基于云的)可以实现构建和部署管道。对于本书中的示例,我特意选择了GitHub作为源代码控制库,并使用Travis CI作为构建引擎。Git源代码控制库是非常流行的代码库,GitHub是当今最大的基于云的源代码控制库之一。
Travis CI是一个与GitHub紧密集成的构建引擎(它也支持Subversion和Mercurial)。它非常容易使用,并完全由项目的根目录中的单个配置文件
(.travis.yml)驱动。Travis CI的简单性使得建立一个简单的构建管道变得非常容易。
到目前为止,本书中的所有代码示例都可以从桌面单独运行(除了连接到GitHub之外)。在本章中,如果读者想完全遵循代码示例,则需要创建自己的GitHub、Travis CI和Docker Hub账户。本章不会介绍如何创建这些账户,但个人Travis CI账户和GitHub账户的建立都可以从Travis CI网页完成。
开始前的一个简单提示
出于本书的目的(和我的理智),我为本书的每一章建立了一个单独的GitHub存储库。本章的所有源代码都可以作为一个单独的单元来进行构建和部署。然而,在本书之外,我强烈建议读者在自己的环境中使用微服务自己的存储库和独立构建过程去建立每个微服务。这样,每个服务都可以独立地部署。在构建过程中,我将所有的服务部署为单个单元,仅仅是因为我想用一个构建脚本将整个环境推送到Amazon云中,而不是为每个单独的服务管理构建脚本。
10.6 使服务能够在Travis CI中构建
在本书中构建的每个服务的核心都是一个Maven pom.xml文件,它用于构建Spring Boot服务、将服务打包到可执行JAR中,然后构建可用于启动服务的Docker镜像。到目前为止,服务的编译和启动都是通过以下步骤来完成。
(1)在本地机器上打开一个命令行窗口。
(2)运行对应章的Maven脚本。这将构建这一章的所有服务,然后将它们打包成一个Docker镜像,并将该镜像推送到本地运行的Docker存储库。
(3)从本地Docker存储库启动新创建的Docker镜像,方法是使用docker-compose和docker- machine启动对应章的所有服务。
问题是,如何在Travis CI中重复这个过程?这一切都是从一个名为.travis.yml的文件开始。.travis.yml是一个基于YAML的文件,它描述了当Travis CI执行构建时开发人员想要采取的行动。这个文件存储在微服务的GitHub存储库的根目录下。对于第10章,这个文件可以在spmia- chapter10-code/.travis.yml中找到。
当一个提交发生在Travis CI正在监视的GitHub存储库上时,Travis CI将查找.travis.yml文件,然后启动构建过程。图10-17展示了当一个提交发生在用于保存本章代码的GitHub存储库时,.travis.yml文件将执行的步骤。
图10-17 .travis.yml文件构建和部署软件的具体步骤
(1)开发人员对第10章的GitHub存储库中的一个微服务进行了更改。
(2)GitHub通知Travis CI发生了一个提交。当我们注册了Travis并提供了自己的GitHub账户通知时,这个通知配置就会无缝地进行。Travis CI将启动一个虚拟机,用于执行构建。然后,Travis CI将从GitHub中签出源代码,然后使用.travis.yml文件开始整个构建和部署过程。
(3)Travis CI在构建中创建基本配置并安装依赖项。基本配置包括将在构建中使用哪种编程语言(Java)、是否需要Sudo执行软件安装和访问Docker(用于创建和标记Docker容器)、设置在构建中所需的secure环境变量,以及定义如何通知构建的成功或失败。
(4)在实际构建执行之前,作为构建过程的一部分,可以指示Travis CI安装可能需要的任何第三方库或命令行工具。我们使用travis和亚马逊的ecs-cli(EC2容器服务客户端)这两个命令行工具。
(5)对于构建过程,总是先在源代码库中标记代码,以便在将来的任何时候都可以根据构建的标签提取源代码的完整版本。
(6)构建过程接下来将执行服务的Maven脚本。Maven脚本将编译Spring微服务、运行单元测试和集成测试,然后基于该构建来构建一个Docker镜像。
(7)一旦构建的Docker镜像完成,构建过程会使用与标记源代码存储库相同的标签名称,将镜像推送到Docker Hub。
(8)然后构建过程将使用项目的docker-compose文件和亚马逊的ecs-cli将构建的所有服务部署到亚马逊的Docker服务——ECS。
(9)一旦服务部署完成,构建过程将启动一个完全独立的Travis CI项目,该项目将针对开发环境运行平台测试。
我们现在已经看完.travis.yml文件中涉及的一般步骤,让我们来看看.travis.yml文件的细节。代码清单10-1展示了.travis.yml文件的不同部分。
代码清单10-1 解剖.travis.yml构建
language: java ⇽--- 3.为构建创建核心运行时配置
jdk:
- oraclejdk8
cache:
directories:
- "$HOME/.m2"
sudo: required
services:
- docker
notifications:
email:
on_success: always
on_failure: always
branches:
only:
- master
env:
global:
# 为了简洁,省略了部分内容
before_install: 4.执行所需的命令行工具的预构建安装
- gem install travis -v 1.8.5 --no-rdoc --no-ri
- sudo curl -k -o /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-latest
- sudo chmod +x /usr/local/bin/ecs-cli
- export BUILD_NAME=chapter10-$TRAVIS_BRANCH-$(date -u "+%Y%m%d%H%M%S")-$TRAVIS_BUILD_NUMBER
- export CONTAINER_IP=54.215.193.139
- export PLATFORM_TEST_NAME="chapter10-platform-tests"
script:
- sh travis_scripts/tag_build.sh ⇽--- 5.执行一个shell脚本,它将使用构建名标记源代码
- sh travis_scripts/build_services.sh ⇽--- 6.使用Maven构建服务器和本地Docker镜像
- sh travis_scripts/deploy_to_docker_hub.sh ⇽--- 7.将Docker镜像推送到Docker Hub
- sh travis_scripts/deploy_to_amazon_ecs.sh ⇽--- 8.在Amazon ECS容器中启动服务
# sh travis_scripts/trigger_platform_tests.sh ⇽--- 9.触发一个Travis构建,为构建服务执行平台测试
注意代码清单10-1中注释的编号与图10-17中的数字一一对应。
现在,我们将详细介绍构建过程中涉及的每一个步骤。
10.6.1 构建的核心运行时配置
.travis.yml文件的第一部分处理Travis构建的核心运行时配置。通常.travis.yml文件的这部分将包含特定Travis的功能,如:
(1)告诉Travis开发工作使用的编程语言;
(2)定义构建过程是否需要Sudo访问权限;
(3)定义在构建过程中是否要使用Docker;
(4)声明将要使用的secure环境变量。
代码清单10-2展示了构建文件的这部分配置。
代码清单10-2 为构建配置核心运行时
language: java
jdk:
- oraclejdk8 ⇽--- ❶ 告诉Travis在主要运行时环境中使用Java和JDK 8 jdk
cache: ⇽--- ❷ 告诉Travis在构建之间缓存和复用Maven目录
directories:
- "$HOME/.m2"
sudo: required ⇽--- ❸ 允许构建在正在运行的虚拟机上使用Sudo访问
services:
- docker
notifications:
email: ⇽--- ❹ 配置用于通知构建成功或失败的电子邮件地址
on_success: always
on_failure: always
branches: ⇽--- ❺ 指示Travis,它应该只在主分支有提交情况下进行构建
only:
- master
env: ⇽--- ❻ 在脚本中创建secure环境变量
global:
-secure: IAs5WrQIYjH0rpO6W37wbLAixjMB7kr7DBAeWhjeZFwOkUMJbfuHNC=z…
# 为了简洁,省略了其他代码
Travis构建脚本的第一件事就是告诉Travis使用哪种主要语言来完成构建。通过将language指定为java和将jdk属性指定为oraclejdk8❶,Travis将确保为项目安装和配置JDK。
.travis.yml文件的下一部分,即cache.directories属性❷,告诉Travis,在执行构建时缓存此目录的结果,并在多个构建中复用它。在处理像Maven这样的包管理器时是非常有用的,因为每次构建启动都需要花费大量时间来下载jar依赖项的新副本。如果没有设置cache.directories属性,则本章的构建可能需要花费10 min的时间来下载所有相关的jar文件。
代码清单10-2中接下来的两个属性是sudo属性和services属性❸。sudo属性用于告诉Travis,构建过程需要使用sudo作为构建的一部分。UNIX sudo命令用于临时提升用户权限到root权限。通常来说,在需要安装第三方工具时使用sudo。当需要安装Amazon ECS工具时,确实需要在构建中使用sudo。
services属性用于告诉Travis,在执行构建时是否要使用某些关键服务。例如,如果集成测试需要本地数据库供其运行,则Travis允许开发人员在构建中直接启动MySQL或PostgreSQL数据库。在这个例子中,需要运行Docker为每个EagleEye服务构建Docker镜像,并将镜像推送到Docker Hub。我们已经将services属性设置为在构建启动时启动Docker。
下一个属性notifications❹定义了构建成功或失败时使用的通信通道。现在,我们始终通过将构建的通知通道设置为电子邮件来传达构建结果。Travis会通过电子邮件通知构建的成功与失败。此外,Travis CI可以通过除电子邮件以外的多种通道进行通知,包括Slack、IRC、HipChat或自定义Web钩子。
branches.only属性❺告诉Travis,应该针对什么分支进行构建。对于本章中的示例,只需完成Git的master分支的构建。这样可以防止每次在GitHub中标记存储库或提交代码到分支时都启动构建。这一点很重要,因为每次标记存储库或创建发布时,GitHub都会对Travis进行回调。branch.only属性设置为master以防止Travis陷入无休止的构建。
构建配置的最后一部分是设置敏感的环境变量❻。在构建过程中,可能会与第三方供应商(如Docker、GitHub和Amazon)进行通信。有时通过它们的命令行工具进行通信,而其他时候则是使用API。无论如何,我们经常需要出示敏感的凭据。Travis CI能够让开发人员添加加密的环境变量来保护这些凭据。
要添加一个加密的环境变量,必须在包含源代码的项目目录中使用桌面上的travis命令行工具对环境变量进行加密。要在本地安装Travis命令行工具,可查阅该工具的官方文档。对于本章中使用的.travis.yml,我创建并加密了以下环境变量。
DOCKER_USERNAME——Docker Hub用户名。
DOCKER_PASSWORD——Docker Hub密码。
AWS_ACCESS_KEY——亚马逊的ecs-cli命令行客户端使用的AWS访问密钥。
AWS_SECRET_KEY——亚马逊的ecs-cli命令行客户端使用的AWS私密密钥。
GITHUB_TOKEN——GitHub生成的令牌,用于指示允许调入的应用程序对服务器执行的访问级别。这个令牌必须先用GitHub应用程序生成。
一旦安装了travis工具,以下命令就会将加密的环境变量DOCKER_USERNAME添加到.travis.yml文件的env.global部分:
travis encrypt DOCKER_USERNAME=somerandomname --add env.global
运行此命令后,现在应在.travis.yml文件的env.global部分中看到一个secure属性标签,后面是一长串文本。图10-18展示了一个加密的环境变量是什么样子。
图10-18 加密的Travis环境变量直接放在.travis.yml文件中
但是,Travis不会在.travis.yml文件中标记加密环境变量的名字。
注意
加密的变量只适用于它们加密所在的单个GitHub存储库,并且Travis是针对这个GItHub存储库进行构建的。不能采用剪切加密环境变量并在多个.travis.yml文件中进行粘贴的这种方式。如果读者这么做,构建将无法运行,因为加密的环境变量不能正确解密。
不管构建工具是什么,要始终加密凭据尽管我们所有的例子都使用Travis CI作为构建工具,但所有现代构建引擎都允许开发人员加密凭据和令牌。请务必确保加密凭据。在源代码存储库中嵌入的凭据是一个常见的安全漏洞。不要因为相信源代码控制库是安全的,就相信它里面的凭据是安全的。
10.6.2 安装预构建工具
预构建的配置居然有那么多,而下一部分的配置却很少。构建引擎通常包含大量“胶水代码”脚本,用于将构建过程中使用的不同工具联系在一起。使用上述Travis脚本,需要安装以下两个命令行工具。
travis——这个命令行工具用于与Travis构建进行交互。本章稍后将使用它来检索GitHub令牌,以编程方式触发另一个Travis构建。
ecs-cli——这是用于与Amazon ECS交互的命令行工具。
.travis.yml文件的before_install部分中列出的每一项都是一个UNIX命令,这些命令将在构建启动之前执行。代码清单10-3展示了before_install属性以及需要运行的命令。
代码清单10-3 预构建安装步骤
before_install:
- gem install travis -v 1.8.5 --no-rdoc --no-ri ⇽--- 安装Travis命令行工具
- sudo curl -o /usr/local/bin/ecs-cli
https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-latest ⇽--- 安装亚马逊的ECS客户端
- sudo chmod +x /usr/local/bin/ecs-cli ⇽--- 在ECS客户端将权限更改为可执行
- export BUILD_NAME=chapter10-$TRAVIS_BRANCH- ⇽--- 设置在构建过程中使用的环境变量
$(date -u "+%Y%m%d%H%M%S")-$TRAVIS_BUILD_NUMBER
- export CONTAINER_IP=52.53.169.60
- export PLATFORM_TEST_NAME="chapter10-platform-tests"
在构建过程中要做的第一件事,是在远程构建服务器上安装travis命令行工具:
gem install travis -v 1.8.5 --no-rdoc --no-ri
在稍后的构建过程中,我们将通过Travis REST API启动另一个Travis作业。我们需要使用travis命令行工具来获取用于调用此REST调用的令牌。
安装完travis工具之后,我们将安装亚马逊的ecs-cli工具。这个命令行工具用于部署、启动和停止在亚马逊云内部运行的Docker容器。我们首先下载二进制文件,然后将下载的二进制文件的权限更改为可执行文件来安装ecs-cli:
- sudo curl -o /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-latest
- sudo chmod +x /usr/local/bin/ecs-cli
在.travis.yml的before_install部分完成的最后一件事是在构建中设置3个环境变量。这3个环境变量将有助于驱动构建的行为。这些环境变量如下:
BUILD_NAME;
CONTAINER_IP;
PLATFORM_TEST_NAME。
在这些环境变量中设置的实际值如下:
- export BUILD_NAME=chapter10-$TRAVIS_BRANCH-
$(date -u "+%Y%m%d%H%M%S")-$TRAVIS_BUILD_NUMBER
- export CONTAINER_IP=52.53.169.60
- export PLATFORM_TEST_NAME="chapter10-platform-tests"
第一个环境变量BUILD_NAME生成一个唯一的构建名称,该名称包含构建的名称,后面是日期和时间(直到秒字段),然后是Travis中的构建编号。这个BUILD_NAME将用于在Docker镜像被推送到Docker Hub存储库时,对Docker镜像以及GitHub中的源代码进行标记。
第二个环境变量CONTAINER_IP
包含Amazon ECS虚拟机的IP地址,Docker容器将运行在该Amazon ECS虚拟机上。这个CONTAINER_IP稍后将被传递到另一个Travis CI作业,它将执行平台测试。
注意
我并没有将静态IP地址分配给Amazon ECS服务器。如果我彻底拆除容器,会得到一个新的 IP。在实际生产环境中,ECS集群中的服务器可能会被分配静态(不变)IP,并且集群将具有Amazon企业负载均衡器(Enterprise Load Balancer,ELB)
和Amazon Route 53 DNS名称,以便ECS服务器的实际IP地址对服务是透明的。但是,建立这么多的基础设施超出了本章演示的示例的范围。
第三个环境变量PLATFORM_TEST_NAME包含正在执行的构建作业的名称。我们将在本章稍后探讨它的用法。
关于审查与可追溯性
许多金融服务和医疗保健公司有一个共同需求,那就是它们必须要证明在生产中所部署的软件的可追溯性——一直追溯到所有较低的环境,接着追溯到构建软件的构建作业,然后追溯到代码何时被签入到源代码存储库中。在帮助组织满足这个需求时,不可变的服务器模式确实很有亮点。正如在构建示例中所看到的那样,我们将使用相同的构建名称标记源代码管理存储库以及将要部署的容器镜像。这个构建的名字是独一无二的,并且与一个Travis构建编号联系起来。因为我们只是在通过每个环境时提升容器镜像,并且每个容器镜像都使用构建名称进行标记,所以我们已经建立了该容器镜像的可追溯性,并将其追溯至与之相关的源代码。因为容器一旦被标记就永远不会被更改,所以我们就拥有了强大的审查功能,以展示已部署的代码与底层的源代码存储库相匹配。现在,如果读者想要更加安全,那么在为项目源代码添加标签时,还可以使用这个为构建生成的相同标签来标记驻留在Spring Cloud Config存储库中的应用程序配置。

浙公网安备 33010602011771号