Amazon EC2 Container Service (ECS) 是 Amazon 用于运行和编排 Docker 容器的解决方案。它提供了一个接口,用于定义和部署 Docker 容器以在 EC2 实例集群上运行。
ECS 集群的初始设置和配置并非微不足道,但一旦配置好,它就会运行良好,并使运行和扩展基于容器的应用程序变得相对容易。 ECS 还支持内置的蓝绿部署,但首先我们将介绍一些有关使用 ECS 进行设置的基础知识。
在 ECS 中,您创建 任务定义 ,这与 docker-compose.yml 文件非常相似。任务定义是 容器定义 的集合,每个容器定义都有一个名称、要运行的 Docker 映像以及覆盖映像 入口点 和 命令 的选项。容器定义也是您定义环境变量、端口映射、要装载的卷、内存和 CPU 分配以及特定容器是否应被视为必不可少的地方,ECS 以此了解任务是否健康或需要重新启动.
您可以在多容器应用程序的任务定义中设置多个容器定义。默认情况下,ECS 知道如何从 官方 Docker Hub 中提取数据,也可以配置为从私有注册表中提取数据。但是,私有注册表需要对安装在 EC2 主机实例上的 Docker 客户端进行额外配置。
一旦有了任务定义,就可以从中创建 服务 。服务允许您定义要运行的任务数并与弹性负载均衡器 (ELB) 关联。当一项任务映射到特定端口(如 443)时,ECS 集群中的每个 EC2 实例只能运行一个任务实例。因此,您运行的任务不能超过 EC2 实例。事实上,您需要确保运行的任务至少少于 EC2 实例的数量,以便利用蓝绿部署。任务定义是版本化的,服务被配置为使用特定版本的任务定义。
什么是蓝绿部署?
蓝绿色、黑红色、紫红色-长春花色,用什么颜色真的无所谓。关键是有两个独立但相等的环境。
在任何给定时刻,您的应用程序都在其中一个环境中运行,而另一个环境要么被破坏以节省资源,要么闲置等待下一次更新。第二个环境允许您在不中断当前活动环境的情况下部署更新。部署准备就绪后,可以更新负载均衡器/路由器以将流量路由到新环境。
这个概念并不新鲜,但由于第二环境的要求,还没有被广泛采用。根据应用程序架构的大小和复杂性,第二个环境可能成本很高且难以管理。使用 Docker 容器和微服务架构可以帮助缓解这一挑战。使用 ECS 管理 EC2 上的容器可以进一步减轻这种负担。
Amazon ECS 如何处理蓝绿部署
当更新服务以使用更新版本的任务定义时,ECS 有助于蓝绿部署。当您定义服务并设置应运行的任务数时,ECS 将确保许多任务正在运行,前提是您有足够的容量来运行它们。当服务更新为使用新版本的任务定义时,只要集群中有空闲容量,它就会从新定义开始新任务。随着新任务的启动,它会耗尽旧任务的连接并杀死它们。
看一个最基本的例子,考虑在一个 ECS 集群中有两个 EC2 实例。您定义了一个服务来运行单个任务实例。该任务将仅在一个 EC2 实例上运行。当任务定义更新并且服务更新为使用新任务定义时,ECS 将在第二个 EC2 实例上启动一个新任务,将其注册到 ELB,从第一个实例中删除连接,然后终止旧任务。
正如我之前提到的,您需要确保集群中至少有一个额外的 EC2 实例可用,而不是您在服务中设置的任务数。如果在这个基本示例中我们有两个任务在运行,那么每个 EC2 实例上都会有一个,ECS 将没有备用容量来启动一个新任务,因此蓝绿部署就不会发生。您必须手动终止至少一个任务才能启动该过程。
另外值得注意的是,ECS每次启动任务时,都会拉取定义中指定的Docker镜像。因此,当您构建新版本的映像并将它们推送到注册表时,ECS 中启动的下一个任务将拉取该版本。因此,从 持续集成 和交付的角度来看,您只需构建镜像、推送到注册中心并在 ECS 上触发蓝绿部署,即可让更新后的应用程序上线。
下面的一系列图表说明了 ECS 上的简化蓝绿部署过程。
-
首先,我们有一个运行两个任务的服务。这两个任务在 EC2 实例 1 和 EC2 实例 2 之间分配。
- 已创建更新的任务定义,服务已更新为使用新的任务定义。 ECS 在 EC2 实例 3 上启动一个新任务,并开始耗尽之前任务的连接。
- 当连接从现有任务中耗尽时,ECS 将一次终止一个并启动其他任务,直到达到所需的计数。
- 当 ECS 达到所需的运行任务数时,它会终止仍在运行该任务的先前版本的所有剩余任务。
就是这样。应用程序的更新版本正在新的“绿色”环境中运行。对于 ECS,单独的蓝色和绿色环境的概念有点虚拟和流动,但由于容器是隔离的,所以这真的无关紧要。
ECS 部署:触发蓝绿部署的简单而优雅的方式
在 ECS 上触发蓝绿部署非常简单:创建任务定义的新版本(无需更改)并更新服务以使用新定义。每次您要部署时手动执行此操作有点麻烦,尤其是在任务定义不需要更改的情况下。
作为一个开发团队,我们喜欢运行一个持续的集成和交付过程,使我们能够通过将代码合并到 git 存储库中的适当分支来轻松触发部署。针对 develop 的合并意味着它应该部署到我们的暂存环境,而针对 master 的合并意味着它应该部署到生产环境。除了合并和推送到 git 之外,我们不需要任何进一步的手动处理。
我们的持续集成过程克隆我们的存储库,构建我们的 Docker 镜像,对镜像执行单元测试,将镜像推送到我们的私有注册表(在 ECS 上运行),最后在 ECS 上触发蓝绿部署。当我们寻找触发 ECS 上的更新/部署的解决方案时,选项很复杂。我们可以使用 Amazon 的 CodeDeploy 或 Elastic Beanstalk,但它们需要不同的构建过程,这与我们在 CI 中运行的内容不匹配。
由于触发蓝绿部署所需的全部是任务定义和服务的更新,我们编写了一个带有一些参数的 shell 脚本,然后使用 AWS 命令行工具获取当前任务定义,创建一个从中获取新版本,并更新服务以使用它。它工作得很好并且非常简单。触发更新后,它会监视服务以确保它在退出前运行更新的版本。如果它看到新版本正在运行,它将以正常的零状态码退出;否则它会出错退出。这样,我们的 CI/CD 流程就知道部署是否成功,并且我们可以收到部署失败的通知。
顺便说一下, 我们的脚本 是开源的,有 MIT 许可证。
ecs-deploy
可作为 shell 脚本和 Docker 映像使用。该脚本使用 sed 等 Linux 实用程序,它们在 Linux 和 Mac 上的行为不同,更不用说 Windows 了。使用 Docker 映像可能会给您带来更高的一致性。
ecs-deploy 的要求
ecs-deploy
使用其他软件来执行其工作。值得注意的是,它使用 AWS CLI 工具,通常通过运行
pip install awscli
通过 pip 安装。它还使用
jq
,这是一个命令行 JSON 解析器。
虽然该脚本不要求您设置任何环境变量,但强烈建议以这种方式设置 AWS API 凭证,以免它们出现在您的 shell 历史记录和进程列表中。还可以通过环境变量设置 AWS 区域,以便将命令行选项保持在最低限度。
使用 Shell 脚本
如果您已克隆存储库或将
ecs-deploy
脚本下载到您的路径中,则只需运行它即可获得完整的使用选项。这是一个例子:
$ ecs-deploy -c clusterName -n serviceName -i repo/name:tag
该示例假设您已经为
AWS_ACCESS_KEY_ID
、
AWS_SECRET_ACCESS_KEY
和
AWS_DEFAULT_REGION
配置了环境变量。
使用 Docker 镜像
如果您不想安装
jq
和
AWS CLI
(或依赖的 Python 工具,如
easy_install
),您可以只运行 Docker 镜像。
使用 Docker 镜像的最佳方式是克隆
ecs-deploy
项目存储库并使用提供的 docker-compose.yml 配置。通过使用 Docker Compose 运行图像,您可以通过文件提供与 AWS 相关的环境变量,使它们远离命令行参数。当您克隆存储库时,将 local.env.dist 文件复制到 local.env 并将您的凭据添加到该文件中。然后你可以使用
docker-compose run ecsdeploy
来运行镜像。
Docker 映像使用
ecs-deploy
脚本的入口点,因此您只需按照与 shell 脚本相同的方式提供参数。这是一个例子:
$ ecs-deploy -c clusterName -n serviceName -i repo/name:tag
如果你想将
ecs-deploy
合并到你自己的 docker-compose 项目中,你可以只添加另一个服务:
$ ecs-deploy -c clusterName -n serviceName -i repo/name:tag
请务必使用您的 AWS 凭证配置 env_file 以确保安全操作。
综上所述
蓝绿部署提供了一种在发布期间最大限度地减少生产影响的好方法,而亚马逊的 EC2 容器服务简化了所涉及的许多复杂性。我认识到我们的用例相对简单,以这种方式部署更大、更复杂的应用程序可能不那么容易,但这绝对值得研究。我们在代码更改触发的自动化部署中所获得的舒适感确实改变了我们的行为和开发过程,使其变得更好。它使我们更加敏捷,并且我们的开发人员更高兴没有大量的构建和发布过程。
我们发现我们的
ecs-deploy
脚本对部署非常有帮助、易于使用且可靠,我希望您也能从中受益。我们非常感谢您对改进它的意见,并欢迎对新功能的拉取请求。在下方发表您的评论和问题,让对话继续进行。
本文最初出现在 Phillip Shipley 的 Codeship 博客 上。