使用 AWS CodePipeline 部署 Docker 容器实现 DevOps

阅读数:11570 2019 年 8 月 28 日 08:00

使用AWS CodePipeline部署Docker容器实现DevOps

本文要点

  • 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 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 1:CodePipeline 的不同阶段

在源码方面,我们会使用 Github 仓库。在源码构建方面,我们会使用 AWS CodeBuild 项目。在部署方面,我们会使用 ECS 服务的 Fargate 启动形式。

创建和部署 CodePipeline 应用到 ECS Fargate 涉及到如下的步骤:

  1. 为服务创建 ECS Fargate Task Definition
  2. 配置 Task Subnet/s 的连接
  3. 创建或配置 S3 Bucket,用于存放 CodePipeline Build 阶段的输出制件(Output Artifact)。
  4. 创建 CodePipeline,用来在 ECS Fargate 上部署 Docker 容器应用(镜像)。
  5. 为 Stage 制件修改输入 / 输出设置。
  6. 运行 CodePipeline
  7. 修改代码并重新运行 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中需要的一个映射。versionbuildspec.yml需要的另外一个映射。 buildspec.yml 文件位于 GitHub 的仓库中。

添加镜像定义文件

要部署基于容器的应用,比如部署到 ECS 上的应用,AWS CodePipeline 需要有一个 JSON 格式的镜像定义文件。镜像定义文件默认名为imagedefinitions.json,但是可以使用其他的名称。镜像定义文件描述了容器应用,它包含两个属性:nameimageURIname指明了 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 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 2:任务定义

在任务子网中配置连接性

在创建服务之前,我们需要在 Subnets 中配置到 Internet 的连接性,在配置服务的时候,我们需要用的这个子网。Route Table列出了路由,我们使用默认的 Internet gateway 添加一个新的路由。添加路由时,将Destination设置为 0.0.0.0/0,这是一个 IP 地址。将Target选择为internet gateway.

创建和测试容器服务

接下来,在默认集群中创建一个 ECS 容器,如图 3 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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 所示的一条信息。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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

使用AWS CodePipeline部署Docker容器实现DevOps

图 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。

使用AWS CodePipeline部署Docker容器实现DevOps

图 9 配置 Service Role 和 Service Role 的设置

Review页面,我们可以检查SourceBuild环境。向下滑动并点击 Create 就可以创建 CodeBuild 项目了。CodeBuild 项目创建成功后会列到 Build projects 中,如图 10 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 10 CodeBuild 项目

CodeBuild 默认创建的 service role 并不包含一些所需的权限。我们需要修改 service role 以便于包含内联的策略,添加s3:GetObjects3:PutObject权限。要添加的内联策略如下所示:

复制代码
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":["s3:PutObject","s3:GetObject"],
"Resource":"arn:aws:s3:::*"
}
]
}

测试 CodeBuild 项目

在 CodePipeline 中创建和配置 CodeBuild 之前,我们可以测试一下 CodeBuild 项目,这样如果有错误的话,我们可以立即修正。如图 11 所示,点击 Start build 将会开始构建。

使用AWS CodePipeline部署Docker容器实现DevOps

图 11 开始构建

此时将会启动Start new build向导,点击Start build。CodeBuild 项目将会启动,代码会开始构建。当 CodeBuild 项目完成时,Phase details将会与图 12 保持一致。

使用AWS CodePipeline部署Docker容器实现DevOps

图 12 Phase details 和 Build logs 表明 CodeBuild 已经完成

CodeBuild 项目会生成 dvohra/node-server Docker 镜像并将其上传至Docker Hub,如图 13 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 13 CodeBuild 生成 Docker 镜像并将其上传至 Docker Hub

创建 CodePipeline

我们已经为 CodePipeline 的每个阶段都创建了项目:源码所需的 GitHub 代码仓库、用于构建的 CodeBuild 以及用于 Staging 的 ECS Fargate 服务,接下来,我们就可以创建 CodePipeline 了。打开该地址的 CodePipeline 控制台,并点击图 14 中所示的 Get started。

使用AWS CodePipeline部署Docker容器实现DevOps

图 14 CodePipeline>Get started

这样将会启动 Create pipeline 向导,如图 15 所示。首先,我们指定Pipeline name(node-server-fargate),然后点击 Next step。

使用AWS CodePipeline部署Docker容器实现DevOps

图 15 设置 Pipeline Name

接下来,配置 Source location。在Source provider中,选择GitHub,如图 16 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 16 选择 Source provider

接下来,通过Connect to GitHub连接至 GitHub,如图 17 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 17 Connect to GitHub

选择 GitHub Repository,如图 18 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 18 选择 GitHub 仓库

选择仓库Branch,如图 19 所示,然后点击 Next step。

使用AWS CodePipeline部署Docker容器实现DevOps

图 19 Source location>Next step

接下来,我们要配置 Build。将Build provider选择为AWS CodeBuild,如图 20 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 20 将 Build provider 选择为 AWS CodeBuild

后续展现的内容会基于所选择的 Build provider 有所差异。对于AWS CodeBuild来说,将会展现一个 CodeBuild 详情的区域。在 Configure your project 区域中,选择Select an existing project,如图 21 所示。在Project name中,选择之前创建的名为node-server的项目。

使用AWS CodePipeline部署Docker容器实现DevOps

图 21 选择 CodeBuild 项目

点击 Next step,接下来,我们需要配置 CodePipeline 的Deploy阶段,如图 22 所示。将Deployment provider选择为Amazon ECS

使用AWS CodePipeline部署Docker容器实现DevOps

图 22 将 Deployment provider 选择为 Amazon ECS

后续展现的内容会基于所选择的 Deployment provider 有所差异,图 23 展示了 Amazon ECS 的配置区域。在Amazon ECS区域中,选择Cluster name,这将是 ECS 服务要部署到的集群,在这里选择default

使用AWS CodePipeline部署Docker容器实现DevOps

图 23 选择 ECS 集群

Service name中要选择 ECS 要部署的服务,也就是node-server-service,如图 24 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 24 选择 ECS 服务

Image filename设置为imagedefinitions.json,如图 25 所示。如果忽略该值的话,默认的 Image filename 是imagedefinitions.json,并且这个文件应该位于 GitHub 仓库的源码中。然后点击 Next step。

使用AWS CodePipeline部署Docker容器实现DevOps

图 25 指定 Image filename

接下来,选择Service Role name,如图 26 所示。在 CodePipeline 第一次创建的时候,将会在 IAM 中自动创建一个 service role。在以后的操作中,将会列出 Service role 供选择。

使用AWS CodePipeline部署Docker容器实现DevOps

图 26 选择 Service role

点击 Next step,检查 CodePipeline 之后点击Create pipeline,如图 27 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 27 Create pipeline

如图 28 所示,CodePipeline 将会创建完成。CodePipeline 创建之后将会自动运行,如图 28,它会处于Source阶段的In Progress状态。

使用AWS CodePipeline部署Docker容器实现DevOps

图 28 CodePipeline Source 阶段处于 in Progress 状态

Source 阶段完成之后,它的状态将会变成Succeeded,如图 29 所示。接下来,Build阶段将会运行,它会处于In Progress状态。

使用AWS CodePipeline部署Docker容器实现DevOps

图 29 Source 阶段完成,Build 阶段处于 In Progress 状态

Build完成时也会成为Succeeded状态,如图 30 所示。此时,Staging阶段开始运行。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 31 CodePipeline>Edit

在修改完输入 / 输出制件后,点击Save pipeline changes,如图 32 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 32 Save pipeline changes

运行 CodePipeline

要在修改之后运行 CodePipeline,我们需要点击Release change,如图 33 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 33 Release change

Release change确认对话框中,点击Release。修改将会应用到 CodePipeline 上,CodePipeline 会从头开始运行,Source阶段会处于In Progress状态,如图 34 所示。 BuildStaging阶段所显示的状态是之前运行的结果,并不代表这些阶段的当前状态。

使用AWS CodePipeline部署Docker容器实现DevOps

图 34 Source 阶段处于 In Progress 状态

CodePipeline 的所有阶段都会完成,并处于Succeeded状态,如图 35 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 35 CodePipeline 的所有阶段都成功完成

前文所述的服务任务会停止运行,基于修改后的任务定义所生成的任务将会开始运行,如图 36 所示的RUNNING任务状态。

使用AWS CodePipeline部署Docker容器实现DevOps

图 36 新任务正在运行

要调用新的任务,我们可以像前面那样在 Elastic Network interface 找到该任务的公开 IP,并在 Web 浏览器中打开 URL <Public IP>:8080,这样就能调用该任务了。Node 服务器应用的响应如图 37 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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 状态。

使用AWS CodePipeline部署Docker容器实现DevOps

图 38 CodePipeline 自动重启

在 Build 阶段成功完成之后,Staging 阶段会开始运行,其状态信息如图 39 所示。

使用AWS CodePipeline部署Docker容器实现DevOps

图 39 Build 阶段完成,Staging 阶段启动

Staging 阶段也会成功完成。新的任务将会启动,使用新任务的公开 IP,在 Web 浏览器中打开 <Public IP>:8080 URL。新任务将会被调用,Node 服务器的响应如图 40 所示,Node 服务器的响应反映了 server.js 的变化。

使用AWS CodePipeline部署Docker容器实现DevOps

图 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

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

发布