本文要点
AWS CodePipeline 是一项 DevOps 服务,用来对各种 AWS 平台上托管的应用进行持续集成、持续交付和持续部署。
Amazon Elastic Container Service(ECS)是 AWS 托管的一项服务,它能够使用 Docker 容器实现应用的容器化。
Amazon Fargate 是 Amazon Elastic Container Service(ECS)的一种无服务器(serverless)启动形式。
对于部署到 ECS 的示例应用来说,AWS CodePipeline 由一个源码仓库(如 GitHub repo)、用于进行构建的 AWS CodeBuild 以及用于 Staging 环境的 AWS ECS(Fargate)服务组成。
将 AWS CodePipeline 用于 AWS ECS 服务的好处在于在新 Docker 镜像构建和部署的过程中,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 涉及到如下的步骤:
为服务创建 ECS Fargate Task Definition
配置 Task Subnet/s 的连接
创建或配置 S3 Bucket,用于存放 CodePipeline Build 阶段的输出制件(Output Artifact)。
创建 CodePipeline,用来在 ECS Fargate 上部署 Docker 容器应用(镜像)。
为 Stage 制件修改输入/输出设置。
运行 CodePipeline
修改代码并重新运行 CodePipeline。
搭建环境
唯一的先决条件是要有一个 AWS 帐户。CodePipeline 部署在 ECS Fargate 上的应用程序是一个 Docker 应用。任何具有源码仓库的 Docker 镜像都可以使用,我们所使用的是dvohra/node-server
Docker 镜像。Docker 镜像dvohra/node-server
的源码存放在GitHub仓库中。
创建 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 请求/响应。
为 CodeBuild 项目添加构建规范
构建规范(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 服务器的响应
创建和配置 S3 Bucket
CodePipeline 在构建 Node 服务器应用的源码并将其以 Docker 镜像的形式部署为 ECS 服务时,需要 CodeBuild 项目生成“输出制件(Output Artifacts)”。输出制件会保存在 S3 bucket 中,在创建 CodePipeline 的时候要选择这个 bucket。在 S3 控制台中创建一个新的 S3 Bucket,或者选择之前运行 CodePipeline 时所创建的 S3 bucket。
创建 CodeBuild 项目
接下来,我们要创建一个 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
权限。要添加的内联策略如下所示:
测试 CodeBuild 项目
在 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
我们已经为 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
要在修改之后运行 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 服务器应用的响应
修改源码,重新运行 CodePipeline
如果 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
评论