本文主要使用的AWS Devops工具有AWS Codecommit、AWS Codebuild以及AWS Pipeline。
- AWS Codecommit是一项安全的、可高度扩展的托管型源代码控制服务,可用于托管私有Git存储库。Codecommit可以存储从代码到二进制文件的一切内容,可以与现有的基于Git的工具无缝协作。
- AWS Codebuild是一项完全托管的生成服务,可编译源代码、运行测试以及生成可供部署的软件包。Codebuild可以持续扩展并同时处理多项构建任务,因此构建任务不会在队列中等待。
- AWS Codepipeline是一种持续集成与持续交付服务,可以实现快速而可靠的应用程序和基础设施更新。根据自定义的发布流程模型,只要满足触发条件,Codepipeline便会根据流程来构建、测试以及部署代码。
而Docker容器化部署,则主要是利用Amazon Elastic Container Service(ECS)以及Amazon Elastic Container Registry(ECR)。
- ECS是一项高度可扩展的高性能容器管理服务,支持Docker容器,并且能够在托管的Amazon EC2实例集群上轻松运行应用程序。ECS还提供了FARGATE运行类型,这是一种Serverless平台,借助这个平台,容器服务能够运行在Docker容器中,而不是EC2实例中。
- ECR是完全托管的Docker容器注册表,可以使开发人员能够轻松存储、管理和部署Docker容器映像。并且ECR与ECS集成,从而简化生产工作流程的开发。
问题
作为一个只会写代码的懒人,我的想做的只有生产Bug,至于写代码生产Bug之外的,如编译、打包、部署等等,都太复杂了,所以就想,能不能我每次写完代码,提交合并之后,剩下的都由机器自动帮我完成呢?
加上最近容器化部署又是一大热门,用最少的资源,干最多的活儿,这就绕不开Docker了。但是由于Docker镜像源码或代码构建的变更,Docker容器的部署可能需要更新或修改。Docker镜像中任何代码修改都需要重新构建Docker镜像,Docker服务也需要重新部署。那么问题来了,重新部署可能会导致原来的服务出现停止服务的情况。也就是说,直到新的服务运行起来,这中间是不能提供任何服务的,你的服务是有一个空档期的,这肯定不合适。
解决方案
为了解决上面提出的几个问题,我们需要去引入Devops自动化流程,这时候就考虑到了使用AWS的Devops工具来解决我们的问题。
首先,AWS Codepipeline是一个用于持续集成、持续交付和持续部署的Devops服务,它适用于各种AWS平台上的应用,其中包括Amazon ECS和Fargate平台。其次,通过Codepipeline控制的ECS服务在更新和修改时,可以不用停止ECS服务任务。Pipeline在一个动态的环境中提供了ECS服务的高可用性,所以即便Docker镜像的源码变更需要重新部署ECS服务,也不会出现ECS服务宕机的情况。
AWS Codepipeline简单来说,由三个阶段组成:源代码集成、源代码构建和部署,如下图所示:
在源码方面,使用AWS的Codecommit仓库,在源码构建阶段,采用AWS Codebuild项目。在部署方面,选择ECS服务的Fargate启动形式。
搭建环境
要使用AWS上的服务,前提就是需要一个AWS的账号。
第一步,创建一个Codecommit存储库,来存放源代码;
第二步,创建一个Amazon ECR存储库。本文最终目的是需要在ECS Fargate上部署Docker应用,这里选择Amazon ECR作为Docker镜像存储的位置,当然,其余的Docker镜像存储也可以。
第三步,创建一个Amazon ECS集群,用来管理和部署推进ECR仓库重的镜像。
创建Codecommit仓库
进入Codecommit管理界面,点击Create Repository,创建Codecommit存储库。在Repository Name输入存储库名称,接着在Description中写上合适的描述,最后点击Create,即可完成存储库的创建。
然后根据Connection Steps的步骤,通过Git工具连接上Codecommit存储库,将代码推送进去。
需要注意的是,在源代码中,我们需要编写一个Dockerfile文件,它是将代码打包成Docker镜像的配置文件,到后面会用到。
创建ECR存储库
这一过程就比较简单了,进入ECR管理界面的Repositories,点击右上角的Create Repository创建存储库,进入创建存储库配置页面。存储库的配置分为三个部分,第一部分General Settings,该部分主要是配置基础设置,存储库名称、类型(私有、公有)、标签是否唯一;第二部分,Image Scan Settings,开启之后,则会对每一个推送进来的镜像进行扫描;第三部分,Encryption Settings,这一部分则是KMS加密设置,开启之后会利用KMS(AWS Key Management Service)对存储库中的Images镜像进行加密处理。设置完成之后,点击Create Repository即可完成存储库的创建。
创建和配置S3 Bucket
Codepipeline在构建Node 服务器应用的源码并将其以 Docker 镜像的形式部署为 ECS服务时,需要Codebuild项目生成“输出制件(Output Artifacts)”。输出制件会保存在S3 Bucket中,在创建Codepipeline的时候要选择这个Bucket。在 S3 控制台中创建一个新的S3 Bucket,或者选择之前运行Codepipeline时所创建的S3 Bucket。
进入S3管理控制台,点击侧边栏的Buckets,进入Buckets管理界面,点击右上角的Create Bucket进入创建Bucket配置界面,如下图所示:
在Bucket Name一栏写上Bucket名称,其余保持默认就行。创建完成之后,在Bucket界面就会出现一条记录,如下图所示:
创建Codebuild 中的Build Project
进入Codebuild 管理控制台,点击侧边栏的Build Projects,进入Build Projects管理界面。点击右上角的Create Build Project橙色按钮,进入创建Create Build Project配置界面。在这个界面,可以很清楚的看到,一个完整的Build Project分为7个部分:Project Configuration、Source、Environment、Buildspec、Batch Configuration、Artifacts、Logs。
Project Configuration
这里主要是配置Build Project的基本信息,例如Project Name、Description等。
Source
在Source一栏中主要选择是源代码Git存储库以及想要编译的代码。在Source Provider中选择Codecommit,Repository中选择前面创建的源代码仓库,Reference Type选择Branch,然后在下面的Branch选择Master,Commit ID选择最近的一次提交。
Environment
环境根据官方文档的说法来,因为我们Build Project的最终产物是一个Docker镜像,那么环境配置和官方文档上说的保持一致就可以了,也就是我下图所展示的。
这里需要注意的有两点,第一个是Privileged,如果期望Build的产物是Docker镜像,那么就需要将Privileged这一栏勾上,而且其后面解释也很清楚了。第二个是Service Role。在我的配置图中,选择的是New Service Role,意味着它自动的在IAM中去创建一个规则,默认的规则权限是很小的,如果要实现我们的期望——将源码编译打包成Docker镜像并推送到ECR中,后面还需要对这个规则进行修改。
Buildspec
这一步涉及到一个叫做Build Spec,即构建规范的文件,它使用的是YAML语法编写的一系列Build命令,Codebuild会根据这个文件来运行构建步骤。有两种方式存放该文件的内容,第一种,存储在源代码仓库中,在此种方式下,该文件就必须被命名为“Buildspec.yml”并且存在源码仓库的根路径下;第二种就是直接在Build创建的过程中,将Build Commands插进Build过程中。
Build Spec中主要包括Build的步骤,包括连接ECR存储库,运行Docker打包步骤,推送Docker镜像等。当然,Build还能干更多的事,可以查看适用于 CodeBuild 的构建规范参考
本次示例选择第二种方式,Buildspec文件示例:
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxx.dkr.ecr.ap-northeast-1.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- cd ./service
- docker build -t xxx/backend:latest .
- docker tag xxx/backend:latest xxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxx/service:latest
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxx/service:latest
- cd ..
- printf '[{"name":"service","imageUri":"xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxx/service:latest"}]' > imagedefinitions.json
artifacts:
files:
- ./service/imagedefinitions.json
- '**/*'
name: BuildService
在示例中有一个比较重要的东西就是,Imagedefinitions.json这个文件,这是ECS部署的镜像配置文件,它会根据这个文件去寻找镜像。
Batch Configuration
这个配置提供了一个可以将一组构建作为一次执行来运行的选项。当开始构建时,批处理配置在高级选项中也可用。本案例中没有使用,就默认不选。
Artifacts
配置Artifacts,主要是配置将Build产物输出到S3中进行存储,在Codepipeline中作为Deploy的输入,这一步是十分重要的。配置参考我下图就可以了。
关于其详细信息,参阅What is AWS Codeartifact? - Codeartifact (Amazon.com)。
Logs
这是一个收集Build日志的服务,它会将Build过程中的日志信息存储到Cloudwatch中。
测试Codebuild
如上图所示,进入Build Projects管理界面,选中想要运行的Project,点击上方的Start Build按钮,即可开始Build项目。点击Project,进入Build Project内部,可以查看该项Project的Build历史,点击每一条构建历史,还能看到该条构建的日志信息。
创建Task Definition
ECS集群中的任务定义(Task Definition)描述了ECS部署环境中的容器。每一个服务都是根据任务定义中的配置来运行的。接下来,为前面Codebuild的产物Docker镜像创建一个任务定义,以便将其部署到ECS Fargate中。
首先进入ECS控制台,然后点击左侧导航栏中的Task Definitions,进入Task Definitions管理界面。点击Create New Task Definition蓝色按钮进入Task Definition配置界面。选择FARGATE,然后点击Next Step,接下来就是配置任务和容器定义。
如图,在Task Definition Name中写上合适的名字,然后在Task Role这一栏中选择ecsTaskExecutionRole,接下来,在Taks Size根据容器服务实际需要的资源选择配置,这里我内存选的0.5GB,CPU选的0.25 vcpu。
配置完任务之后,找到Container Definitions,点击Add Container蓝色按钮,添加容器定义。在Container Name中填上合适的名字,如:Web;在Image一栏中填上镜像URI,这里使用的是ECR存储库,那么URI就类似于http://80xxxxxxxx2174.dkr.ecr.ap-northeast-1.amazonaws.com/repository/web:latest,这里具体的根据实际情况来确定。最后将容器运行服务的端口映射出来,剩下的,根据实际情况配置,这里都选择默认。定义完成之后,点击Add,完成容器定义的添加。
最后点击Create即可完成创建Task Definition。如果是和我的配置一样来进行操作的,创建Task Definition的同时还会创建一个Cloudwatch服务。Cloudwatch可以用来监控容器运行时,使用资源的情况。
根据Task Definition创建ECS Service
进入ECS Cluster管理界面,选择进入到某个集群中,如下图所示,点击蓝色的Create按钮,创建Services。
进入到Create Service配置界面,Service的配置比较简单,这里就不做过多阐述,直接贴图好了。
需要特别注意的是在Deployment Type这儿,需要选择第一个,Rolling Update,剩下的步骤可以直接参考官方文档,官方文档链接:创建服务 - Amazon Elastic Container Service。
创建完成之后,可以对刚刚创建的Service进行简单的测试,返回最开始创建Service的位置,点击刚刚创建的Service链接,进入其详细界面,有一个Tasks选项栏,点击,如下所示:
在这一栏中会展示运行该项Service的所有Tasks,在最开始本实例配置的Task实例是1,所以这里只会有1个Task,以此类推。
点击Task的链接,进入Task详情页,这里会有两个IP地址,如下所示:
Private IP就是属于VPC网络中的IP地址,只有处于VPC网络中才能访问,Public IP就是属于公网IP了,访问这个IP来验证一下是否创建成功:
访问API,会返回结果。
创建Codepipeline
将上面的所有准备工作做完之后,就可以创建Codepipeline了。创建Pipeline的过程会十分简单,只要前面的步骤没有出现错误。
进入Codepipeline管理界面,点击侧边栏的Pipelines进入Pipelines管理页面,然后点击右上角的Create Pipeline创建Pipeline。
Step1,Choose Pipeline Settings,对Pipeline进行一个简单的全局配置。
在Pipeline Name中输入名称,我这里其余保持默认配置。如果有自己创建S3 Bucket,需要去更改Advanced Settings中的Artifact Store,需要更改为Custom Location,并选择自己创建的S3 Bucket。然后点击Next。
Step2,Add Source Stage,添加源。
本实例中使用的是存放在Codecommit中的存储库,其余保持默认配置。
Step3,Add Build Stage,增加Build步骤。
选择在Codebuild中创建的Buildproject,其余保持默认。
Step4,Add Deploy Stage,增加Deploy步骤。
Deploy方式选择ECS部署,选择之前在ECS中创建配置的Service,唯一需要注意的,就是Image Definitions File这一栏,默认就是我图中展示的,在根路径下,这个文件根据实际情况更改。
Step5,Review,检查一下,确认无误后点击Create Pipeline就行。
创建完成之后,它会自动触发第一次运行,同时,这也可以检查你Pipeline是否会出现问题。
没有问题,那当然是一路绿灯啦。
测试Pipeline
接下来,对刚刚创建的Codepipeline进行简单的测试。
Pipeline当前的状态,是Succeeded,最近一次运行是两个小时之前,接下来,我们随便更改某个文件,对这个Pipeline进行一个简单的测试。修改代码,提交之后,稍等一会,发现Pipeline的状态已经发生了改变,变成了In Progress状态,并且Source也发生了变化,变成了我刚刚的提交。
等待一会儿,它会自己触发配置的步骤,重新编译和更新代码,知道状态变为Succeeded,访问方式在前文创建ECS 服务中已经说过了,这里就不重复了。
小结
在本文中,我们主要是讨论了如何将源代码利用Devops工具,实现源代码的自动化打包、编译、部署,以及如何将Docker容器应用部署到ECS FARGATE中,并在不停止容器服务的情况下将新的 Docker 镜像部署到正在运行的容器服务中,所有这些都使用 AWS Codepipeline 完成的。用好Devops工具可以很好地利用机器去完成流水线式的工作,极大地节省人力,解放人手,让开发者更专注于开发。