当你利用 CloudFormation 在 YAML 模板中定义你的基础设施时,将 Docker 容器部署到 AWS弹性容器服务(ECS)是直接和自动化的。在这里,我们将通过一个简单的例子,我们将设置在 AWS 中运行一个 NGINX 容器并通过互联网访问它所需的一切。
AWS ECS 概述
我们选择运行 NGINX 官方的 Docker 镜像,因为它将允许我们浏览到 80 端口并查看响应,以证明容器正在运行。为了让这个部署到 ECS 中,我们需要以下构件。
- ECS 任务定义: 你的容器的规范,包括使用什么 Docker 镜像,暴露什么端口,以及分配什么硬件资源
- ECS 任务: ECS 任务定义的一个运行实例。相当于一个正在运行的 Docker 容器
- ECS 服务: 负责运行任务定义的实例,包括部署的数量、网络和安全
- ECS 群组: ECS 服务和任务的分组
- ECS 任务执行角色: 任务将承担的 IAM 角色,在我们的案例中,允许将日志事件写入CloudWatch
- 安全组: 可以将安全组附加到 ECS 服务上,我们将用它来定义允许进入 80 端口的容器的规则。我们将用它来定义允许在 80 端口进入容器的规则
- ECS 任务执行角色: 任务将承担的 IAM 角色,在我们的案例中,允许将日志事件写入CloudWatch
AWS ECS 发射类型
ECS 任务可以根据您的要求,以 2 种模式运行。
- EC2:您负责为您的任务运行的 EC2 实例进行配置。
- Fargate:AWS 会提供硬件,让你的任务在上面运行。你需要做的就是指定内存和CPU 需求。请注意,Fargate 目前只支持非持久性存储卷。
在这个例子中,我们将使用 Fargate 发射类型,因为它是最快速的入门方式。✔
先决条件
为了使这个例子尽可能的简单,我们将假设你已经有了下面的设置。
- 设置了 AWS CLI 访问权限的 AWS 账户。
- 一个默认的 VPC(AWS 在您创建 AWS 帐户时默认创建这个)
使用 CloudForm 构建 ECS
我们将使用 YAML 风味的 CloudFormation,并逐个建立一个堆栈,直到我们有一个 NGINX容器运行,我们可以通过互联网访问。
我推荐 IntelliJ IDEA 来编辑 CloudFormation 模板,因为它有一个插件,可以提供语法验证。
创建 ECS 群集、日志组、执行角色和安全组
首先创建一个文件 ecs.yml,并添加以下定义。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
SubnetID:
Type: String
Resources:
Cluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: deployment-example-cluster
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: deployment-example-log-group
ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: deployment-example-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: ContainerSecurityGroup
GroupDescription: Security group for NGINX container
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
参数
我们的模板只需要一个参数,SubnetID,来指定将 ECS 任务部署到哪个子网。在正常的生产设置中,你会希望部署到多个子网的可用性区,以实现高可用性。
集群
AWS::ECS::Cluster
资源除了名称外,不需要其他配置。
日志组
ECS 任务将把应用日志记录到这个日志组。
执行作用
这是 ECS 任务在执行过程中要承担的角色。因此,它需要所提供的承担角色策略文档,允许ECS 任务承担这个角色。
它还附加了 AmazonECSTaskExecutionRolePolicy,其中包含了 logs:CreateLogStream 和 logs:PutLogEvents 等动作。
安全组
安全组定义了哪些网络流量将被允许访问 ECS 任务。在我们的例子中,我们只需要访问 80 端口,即默认的 NGINX 端口。
让我们使用以下 AWS CLI 命令应用此模板,创建一个 CloudFormation 堆栈来供应上述资源。记住要用自己的子网来代替 <subnet-id>。
$ aws cloudformation create-stack --stack-name example-deployment --template-body file://./ecs.yml --capabilities CAPABILITY_NAMED_IAM --parameters 'ParameterKey=SubnetID,ParameterValue=<subnet-id>'
最终,如果您在 AWS 控制台中导航到 CloudFormation > Stacks > example-deployment > Resources,您会看到以下资源已经创建。
创建任务定义和服务
将以下定义添加到 ecs.yml CloudFormation 模板的末尾。
//previous template code
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: deployment-example-task
Cpu: 256
Memory: 512
NetworkMode: awsvpc
ExecutionRoleArn: !Ref ExecutionRole
ContainerDefinitions:
- Name: deployment-example-container
Image: nginx:1.17.7
PortMappings:
- ContainerPort: 80
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Ref AWS::Region
awslogs-group: !Ref LogGroup
awslogs-stream-prefix: ecs
RequiresCompatibilities:
- EC2
- FARGATE
Service:
Type: AWS::ECS::Service
Properties:
ServiceName: deployment-example-service
Cluster: !Ref Cluster
TaskDefinition: !Ref TaskDefinition
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- !Ref SubnetID
SecurityGroups:
- !GetAtt ContainerSecurityGroup.GroupId
任务定义
我们正在定义一个 AWS::ECS::TaskDefinition
,其重要属性如下。
- _族_是一种将同一任务定义的不同版本进行分组的方式。
- 我们指定了多少硬件资源用于这个任务。
- 我们使用的是网络模式
awsvpc
,这是 Fargate 启动类型所需要的。这个网络模式意味着我们的任务将拥有和 EC2 实例一样的网络能力,比如它自己的 IP 地址。 - 我们之前定义的 执行角色
- 容器定义,指定映像、容器端口和日志配置,以告诉它使用 awslogs 日志驱动程序进行日志记录(即到 CloudWatch)。
- 我们指定该任务定义与 EC2 和 Fargate 发射类型兼容(尽管我们将使用 Fargate)。
服务项目
我们正在定义一个AWS::ECS::
服务,其属性如下。
- 该服务将部署任务的 ECS集群
- 要部署的 任务定义
- 要运行的实例数量。对于这个简单的例子,我们将运行 1 个,但对于高可用性,你会希望至少运行 2 个。
- 的_发射类型_,这样我们就不用担心硬件的配置问题了。
- 网络配置,其中指定了我们想要的公共 IP 地址、服务要使用的子网以及要应用的安全组。
现在让我们用 update-stack 命令更新 CloudFormation 栈。
$ aws cloudformation update-stack --stack-name example-deployment --template-body file://./ecs.yml --capabilities CAPABILITY_NAMED_IAM --parameters 'ParameterKey=SubnetID,ParameterValue=<subnet-id>'
稍等片刻,然后您可以看到我们的 CloudFormation 堆栈中又创建了一些资源。
掌握我们的ecs资源
前往 ECS > 服务,我们来看看有什么创造。
你会看到 deployment-example-cluster,它重要有 1 个服务和 1 个正在运行的任务。
单击群集,然后单击_“任务”_选项卡。
这里可以看到我们使用的是我们在 CloudFormation 中定义的任务定义,任务状态为运行,启动类型为 Fargate。
点击任务 id 查看更多详情。这里是详情页的网络部分。
你可以看到这里我们已经得到了任务的公共 IP 地址。去试试在浏览器中打这个 IP。
看来我们得到了一个 NGINX!
写在最后
要清理的话,只需运行 delete-stack 命令即可。
$ aws cloudformation delete-stack --stack-name example-deployment
希望你已经看到,在 ECS 中运行 Docker 容器是很直接的,而且 AWS 提供了大量的配置选项,可以让事情完全按照你的意愿工作。
使用 CloudFormation,可以直接进行增量更改,是管理 ECS 集群的好选择。