@levinzhang
2019-08-20T22:25:10.000000Z
字数 9457
阅读 868
by
reviewed by
在本教程中,我们将会看到如何使用AWS CodePipeline和Amazon Elastic Container Service(ECS)实现无停机的持续集成和持续部署。
Docker容器可以使用多种云平台进行部署,而Amazon Elastic Container Service(ECS)就是其中之一。ECS提供了Fargate运行类型,这是一个serverless平台,借助它容器服务能够运行在Docker容器中,而不是运行在EC2实例中。
由于Docker镜像源码或代码构建的变更,Docker容器的部署可能需要更新或修改。Docker镜像中任何的代码修改都需要重新构建Docker镜像,Docker服务也需要重新部署。当某个AWS ECS正在运行时,如果没有集成、构建、交付和部署源码的机制,那么就会涉及到停止ECS服务任务,这样的后果就是ECS服务的停机。ECS服务的高可用性具有很高的优先级,所以停止服务重新进行部署并不是合适的可选方案。
AWS CodePipeline是一个用于持续集成、持续交付和持续部署的DevOps服务,它适用于各种AWS平台上的应用,其中也包括Amazon ECS和Fargate平台。ECS在更新和修改时,可以不用停止ECS服务任务。AWS CodePipeline在一个动态环境中提供了ECS服务的高可用性,在这种环境中,Docker镜像的源码变更是非常常见的。CodePipeline由三个阶段组成:源代码集成、源代码构建和部署,如图1所示。
图1:CodePipeline的不同阶段
在源码方面,我们会使用Github仓库。在源码构建方面,我们会使用AWS CodeBuild项目。在部署方面,我们会使用ECS服务的Fargate启动形式。
创建和部署CodePipeline应用到ECS Fargate涉及到如下的步骤:
唯一的先决条件是要有一个AWS帐户。CodePipeline部署在ECS Fargate上的应用程序是一个Docker应用。任何具有源码仓库的Docker镜像都可以使用,我们所使用的是dvohra/node-server
Docker镜像。Docker镜像dvohra/node-server
的源码存放在GitHub仓库中。
如果要使用新的GitHub源码仓库的话,它必须要包含一个Dockerfile文件,据此来构建Docker镜像。dvohra/node-server
镜像的Dockerfile是基于node:4.6
Docker镜像的。Dockerfile声明要复制server.js
文件到当前目录下(这个文件用来创建一个Node服务器)、暴露Node服务器所监听的8080端口并为server.js脚本运行一个node命令。server.js文件会创建一个Node服务器并处理HTTP请求/响应。
构建规范(build spec)是具有build命令和设置的YAML语法的文件,CodeBuild项目会使用它来运行构建过程。构建规范文件必须叫做“buildspec.yml”并且必须要复制到源码仓库的根路径下。buildspec.yml文件包含了描述各个构建阶段的键/值对。构建文件中的phases
表述了阶段序列,这是buildspec.yml
中需要的一个映射。version
是buildspec.yml
需要的另外一个映射。buildspec.yml文件位于GitHub的仓库中。
要部署基于容器的应用,比如部署到ECS上的应用,AWS CodePipeline需要有一个JSON格式的镜像定义文件。镜像定义文件默认名为imagedefinitions.json
,但是可以使用其他的名称。镜像定义文件描述了容器应用,它包含两个属性:name
和imageURI
。name
指明了Docker容器的名称,容器必须要在运行CodePipeline之前运行。imageURI
指定了要在Docker容器中运行的Docker镜像。Docker镜像一般情况下会与ECS容器中已经运行的镜像相同。镜像可以是不同的,变种形式一般会通过镜像标签来设置。Node服务器应用所用的imagedefinitions.json文件可以在GitHub上看到,该应用将会部署到ECS Fargate上。
ECS应用中的任务定义(task definition)描述了ECS部署环境中的容器。在本节中,我们会为Node服务器容器创建任务定义,以便于将其部署到ECS Fargate中。如果你没有登录的话,请访问该地址并登录。点击Get started访问ECS控制台。然后,点击导航区域的Task Definitions。在Task Definitions中点击Create new Task Definition。在Create new Task Definition中选择Fargate启动类型。点击Next step,我们要配置任务和容器定义。在Add container对话框中,指定Container name(node-server)并指定Image为dvohra/node-server。任务定义如图2所示。
图2:任务定义
在创建服务之前,我们需要在Subnets中配置到Internet的连接性,在配置服务的时候,我们需要用的这个子网。Route Table列出了路由,我们使用默认的Internet gateway添加一个新的路由。添加路由时,将Destination设置为0.0.0.0/0,这是一个IP地址。将Target选择为internet gateway.。
接下来,在默认集群中创建一个ECS容器,如图3所示。
图3:具有一个服务的集群
按照Fargate启动类型,每个任务都会有一个Elastic Network Interface(ENI)。复制该任务的Public IP,它与Elastic Network Interface的Public IP相同,我们可以从任务Details页的Network区域,也可以从ENI控制台找到它。在浏览器中打开<Public IP of Task>:8080 这个URL地址并调用Node服务应用。Node服务器会返回如图4所示的一条信息。
图4:Node服务器的响应
CodePipeline在构建Node服务器应用的源码并将其以Docker镜像的形式部署为ECS服务时,需要CodeBuild项目生成“输出制件(Output Artifacts)”。输出制件会保存在S3 bucket中,在创建CodePipeline的时候要选择这个bucket。在S3控制台中创建一个新的S3 Bucket,或者选择之前运行CodePipeline时所创建的S3 bucket。
接下来,我们要创建一个CodeBuild项目,它用来将源码构建到Docker镜像中。关于Docker镜像dvohra/node-server
的源码以及用于将源码构建到Docke镜像中的buildspec.yml
文件,我们在前文中已经讨论过了。在Docker镜像构建完成之后,CodeBuild会将其上传至Docker hub。要创建CodeBuild项目,我们需要在Web浏览器中打开这个URL。选择Build projects并点击Create project,如图5所示。
图5:Build projects>Create project
这样会启动Create project向导。在Configure project中指定项目名称(node-server),在Source>Source Provider中选择GitHub。对于仓库,使用Use a repository in my account并选择dvohra/docker-node-server 仓库。Git clone depth使用默认的设置,即值为1。
选中Webhook、Insecure SSL和Build Badge选项。选择Webhook能够让GitHub仓库在每次代码变更的时候都会重新构建代码。Insecure SSL选项能够在连接项目源时,生成源码构建的SSL告警。Build Badge能够让项目的状态可见并且可嵌入。在Environment: How to build中的Environment image部分,选择Use an image managed by AWS CodeBuild。将Operating System选择为Ubuntu。对于Runtime,选择Docker,如图6所示。
图6:将运行时选择为Docker
对于Runtime version,请选择aws:codebuild/docker:17.09.0,它代表了Docker的17.9版本。如果有新的可用版本的话,请选择更新的版本。对于Docker运行时环境,Privileged选项会自动选中,因为它是构建Docker镜像所需要的。对于Build specification,请选择Use the buildspec.yml in the source code root directory,如图7所示。默认情况下,Buildspec name的值为buildspec.yml。
图7 配置环境:如何构建
对于Certificate,选择Do not install any certificate。CodePipeline并不需要证书,但是如果要实现更安全CodeBuild,我们可以安装来自S3的自签名证书。接下来,在Artifacts中配置输出制件。CodePipeline需要输出制件。这里,我们将Type选择为Amazon S3,并指明要使用的S3 bucket文件夹名。将Path设置为“/”,这样它将会在bucket根路径下创建文件夹。将Namespace type选择为Node。将Bucket name选择为我们之前配置的Bucket,如图8所示,同时,将Cache设置为No cache。
图8 为制件配置S3 Bucket
如果是第一次创建CodeBuild项目的话,在Service role中选择Create a service role in your account选项。如果之前创建过CodeBuild项目,那么选择Choose an existing service role from your account。选中Allow AWS CodeBuild to modify this service role,这样它才能够和本构建项目一起使用,如图9所示。对于VPC,请选择No VPC,然后点击Continue。
图9 配置Service Role和Service Role的设置
在Review页面,我们可以检查Source和Build环境。向下滑动并点击Create就可以创建CodeBuild项目了。CodeBuild项目创建成功后会列到Build projects中,如图10所示。
图10 CodeBuild项目
CodeBuild默认创建的service role并不包含一些所需的权限。我们需要修改service role以便于包含内联的策略,添加s3:GetObject
和s3:PutObject
权限。要添加的内联策略如下所示:
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["s3:PutObject","s3:GetObject"],
"Resource":"arn:aws:s3:::*"
}
]
}
在CodePipeline中创建和配置CodeBuild之前,我们可以测试一下CodeBuild项目,这样如果有错误的话,我们可以立即修正。如图11所示,点击Start build将会开始构建。
图11 开始构建
此时将会启动Start new build向导,点击Start build。CodeBuild项目将会启动,代码会开始构建。当CodeBuild项目完成时,Phase details将会与图12保持一致。
图12 Phase details和Build logs表明CodeBuild已经完成
CodeBuild项目会生成dvohra/node-server Docker镜像并将其上传至Docker Hub,如图13所示。
图13 CodeBuild生成Docker镜像并将其上传至Docker Hub
我们已经为CodePipeline的每个阶段都创建了项目:源码所需的GitHub代码仓库、用于构建的CodeBuild以及用于Staging的ECS Fargate服务,接下来,我们就可以创建CodePipeline了。打开该地址的CodePipeline控制台,并点击图14中所示的Get started。
图14 CodePipeline>Get started
这样将会启动Create pipeline向导,如图15所示。首先,我们指定Pipeline name(node-server-fargate),然后点击Next step。
图15 设置Pipeline Name
接下来,配置Source location。在Source provider中,选择GitHub,如图16所示。
图16 选择Source provider
接下来,通过Connect to GitHub连接至GitHub,如图17所示。
图17 Connect to GitHub
选择GitHub Repository,如图18所示。
图18 选择GitHub仓库
选择仓库Branch,如图19所示,然后点击Next step。
图19 Source location>Next step
接下来,我们要配置Build。将Build provider选择为AWS CodeBuild,如图20所示。
图20 将Build provider选择为AWS CodeBuild
后续展现的内容会基于所选择的Build provider有所差异。对于AWS CodeBuild来说,将会展现一个CodeBuild详情的区域。在Configure your project区域中,选择Select an existing project,如图21所示。在Project name中,选择之前创建的名为node-server的项目。
图21 选择CodeBuild项目
点击Next step,接下来,我们需要配置CodePipeline的Deploy阶段,如图22所示。将Deployment provider选择为Amazon ECS。
图22 将Deployment provider选择为Amazon ECS
后续展现的内容会基于所选择的Deployment provider有所差异,图23展示了Amazon ECS的配置区域。在Amazon ECS区域中,选择Cluster name,这将是ECS服务要部署到的集群,在这里选择default。
图23 选择ECS集群
Service name中要选择ECS要部署的服务,也就是node-server-service,如图24所示。
图24 选择ECS服务
将Image filename设置为imagedefinitions.json
,如图25所示。如果忽略该值的话,默认的Image filename是imagedefinitions.json
,并且这个文件应该位于GitHub仓库的源码中。然后点击Next step。
图25 指定Image filename
接下来,选择Service Role name,如图26所示。在CodePipeline第一次创建的时候,将会在IAM中自动创建一个service role。在以后的操作中,将会列出Service role供选择。
图26 选择Service role
点击Next step,检查CodePipeline之后点击Create pipeline,如图27所示。
图27 Create pipeline
如图28所示,CodePipeline将会创建完成。CodePipeline创建之后将会自动运行,如图28,它会处于Source阶段的In Progress状态。
图28 CodePipeline Source阶段处于in Progress状态
Source阶段完成之后,它的状态将会变成Succeeded,如图29所示。接下来,Build阶段将会运行,它会处于In Progress状态。
图29 Source阶段完成,Build阶段处于In Progress状态
Build完成时也会成为Succeeded状态,如图30所示。此时,Staging阶段开始运行。
图30 Build阶段完成,Staging阶段处于In Progress状态
我们已经配置了CodePiepline的所有阶段,那么为什么还需要修改输入/输出制件的设置呢?这是因为对于由CodePipeline部署的示例ECS应用程序来说,默认的输入/输出制件设置并不是运行CodePipeline所需要的。默认情况下,Staging阶段的输入制件是Build阶段的输出制件。CodeBuild只是构建Docker映像并将Docker映像上载到Docker Hub或Amazon ECR中,CodeBuild阶段不会生成任何输出制件。Staging阶段的输入制件需要设置为Source阶段的输出制件。如果不修改输入/输出制件的设置的话,Staging阶段将会失败。创建后自动启动的CodePipeline将会失败。为了修改输入/输出制件的设置,点击Edit,如图31所示。
图31 CodePipeline>Edit
在修改完输入/输出制件后,点击Save pipeline changes,如图32所示。
图32 Save pipeline changes
要在修改之后运行CodePipeline,我们需要点击Release change,如图33所示。
图33 Release change
在Release change确认对话框中,点击Release。修改将会应用到CodePipeline上,CodePipeline会从头开始运行,Source阶段会处于In Progress状态,如图34所示。 Build和Staging阶段所显示的状态是之前运行的结果,并不代表这些阶段的当前状态。
图34 Source阶段处于In Progress状态
CodePipeline的所有阶段都会完成,并处于Succeeded状态,如图35所示。
图35 CodePipeline的所有阶段都成功完成
前文所述的服务任务会停止运行,基于修改后的任务定义所生成的任务将会开始运行,如图36所示的RUNNING任务状态。
图36 新任务正在运行
要调用新的任务,我们可以像前面那样在Elastic Network interface找到该任务的公开IP,并在Web浏览器中打开URL <Public IP>:8080,这样就能调用该任务了。Node服务器应用的响应如图37所示。
图37 Node服务器应用的响应
如果ECS服务的响应和没有CodePipeline时直接调用服务完全一样,那么运行CodePipeline的优势在什么地方呢?一般来讲,部署在生产环境的ECS的源码会定期更新,这意味着Docker镜像需要重新构建。在ECS服务任务中部署的Docker镜像也需要进行更新。在更新Docker镜像的时候,不能中断基于ECS的服务。借助CodePipeline,每次修改源代码时都会自动重新运行CodePipeline。在没有任何用户干预的情况下,每当源代码发生更改时,ECS部署将会自动更新。
为了阐述该功能,我们对GitHub仓库中的代码稍微修改一下,比如让server.js
中的Hello消息略有差异。在仓库中点击Commit changes。CodePipeline将会自动重新运行,如图38所示,图中Source阶段已经成功完成,而Build阶段正处于in progress状态。
图38 CodePipeline自动重启
在Build阶段成功完成之后,Staging阶段会开始运行,其状态信息如图39所示。
图39 Build阶段完成,Staging阶段启动
Staging阶段也会成功完成。新的任务将会启动,使用新任务的公开IP,在Web浏览器中打开<Public IP>:8080 URL。新任务将会被调用,Node服务器的响应如图40所示,Node服务器的响应反映了server.js的变化。
图40 新任务中已修改的Node服务器响应
在本文中,我们讨论了如何将Docker容器应用部署到ECS Fargate中。我们演示了如何更新Docker镜像的源代码,并在不停止容器服务的情况下将新的Docker镜像部署到正在运行的容器服务中,所有这些都使用AWS CodePipeline完成的。
Deepak Vohra是Sun认证的Java程序员和Web组件开发人员。Vohra在WebLogic Developer Journal、XML Journal、ONJava、Java.net、IBM developerWorks、Java Developer Journal、Oracle杂志和devx上都发表过Java和Java EE相关的技术文章。Vohra已经出版了五本关于Docker的书籍,他是一位Docker导师。
查看英文原文:Deploying Docker Containers using an AWS CodePipeline for DevOps