Amazon Elastic Container Service (Amazon ECS) 是Amazon专有的、高度可扩展的高性能容器管理服务,可轻松运行、停止和管理集群上的 Docker容器。

使用ECS,不再需要安装、运维和扩展自己的集群管理基础设施。只需简单的 API 调用便可以启动和停止Docker应用程序,查询集群状态等。可以根据资源需求、隔离策略和可用性要求来安排容器在集群中的位置。ECS集成了Amazon Elastic Container Registry、Elastic Load Balancing、Elastic Block Store、Elastic Network Interfaces、Virtual Private Cloud、IAM 和 CloudTrail,可提供运行各种容器化应用程序或服务的完整解决方案。

ECS是一个久经考验的解决方案,已应用于许多其他 AWS 服务,比如Amazon SageMaker 和 Amazon Lex。这足以证明ECS的安全性、可靠性、可用性,可用于生产环境。

启动类型

ECS支持两种启动类型:Fargate和EC2。

Fargate 启动类型

Fargate 启动类型无需配置和管理底层基础设施,只需定义任务(相当于Kubernetes的Pod),指定CPU、内存、网络、IAM策略等,即可运行容器化的应用程序。
【AWS征文】ECS让容器编排更轻松

EC2 启动类型

EC2 启动类型允许在自己管理的 EC2 实例集群上运行容器化的应用程序。
【AWS征文】ECS让容器编排更轻松

您可以使用 Fargate 启动类型启动服务或任务,运行在ECS 管理的无服务器基础设施上,不必管理 Amazon EC2 实例服务器或集群。若要进行更多控制,可以使用 EC2 启动类型。

下面讲解利用Fargate 启动类型部署Angular 9集成Spring Boot 2详解中的Spring Boot和Angular应用。

创建Docker Image

Spring Boot项目的Dockerfile文件:

Dockerfile.spring

FROM openjdk:8-jdk-slim

WORKDIR app
ARG APPJAR=target/heroes-api-1.0.0.jar
COPY ${APPJAR} app.jar

ENTRYPOINT ["java","-jar","app.jar"]

Angular项目的Dockerfile文件:

Dockerfile.angular

FROM httpd:2.4

ARG DISTPATH=./dist/
ARG CONFFILE=./heroes-httpd.conf
COPY ${DISTPATH} /usr/local/apache2/htdocs/
COPY ${CONFFILE} /usr/local/apache2/conf/httpd.conf

以部署到Apache为例,运行以下命令获取httpd.conf:

docker run --rm httpd:2.4 cat /usr/local/apache2/conf/httpd.conf > heroes-httpd.conf

修改配置文件,启用proxy_module、proxy_http_module、rewrite_module,然后添加如下内容:

ProxyPreserveHost on
ProxyPass "/api" "http://127.0.0.1:8080/api"
ProxyPa***everse "/api" "http://127.0.0.1:8080/api"

RewriteEngine  on
RewriteRule ^/$ /en/index.html

# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

# If the requested resource doesn't exist, use index.html
RewriteRule ^/zh /zh/index.html
RewriteRule ^/en /en/index.html

我们将在一个任务中包含这两个image,使用apache代理后台地址。

构建Image
运行如下命令构建Image:

docker build --build-arg APPJAR=heroes-api-1.0.0.jar -f Dockerfile.spring -t heroes-api .
docker build -f Dockerfile.angular -t heroes-web .

推送Image到ECR

Amazon Elastic Container Registry (Amazon ECR) 是一项Docker Container Registry服务,可让开发人员轻松存储、管理和部署 Docker 容器映像。

  1. 创建ECR存储库

首先创建两个ECR存储库,可以从控制台创建,也可以运行如下AWS CLI命令:

aws ecr create-repository --repository-name heroes/heroes-api
aws ecr create-repository --repository-name heroes/heroes-web
  1. 登录ECR存储库

执行如下命令:

`aws ecr get-login --no-include-email`

或

aws ecr get-login-password | docker login --username AWS --password-stdin 888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn
  1. 标记image
docker tag heroes-api:latest 888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn/heroes/heroes-api:latest
docker tag heroes-web:latest 888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn/heroes/heroes-web:latest
  1. 推送Image
docker push 888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn/heroes/heroes-api:latest
docker push 888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn/heroes/heroes-web:latest

创建集群

Amazon ECS 集群是任务或服务的逻辑分组,可以在一个账户中创建多个集群以保持资源独立。

创建Fargate集群非常简单,进入ECS集群控制台,点击“创建集群”,选择“仅限联网”集群模板,点击“下一步”,然后输入集群名称"heroes",点击“创建”即可。

创建任务定义

任务定义类似于应用程序的蓝图,需要任务定义才能在Amazon ECS 中运行 Docker 容器。

可在Fargate任务定义中指定的一些参数:

  • 用于任务的Docker Image
  • 任务的CPU和内存数量
  • 任务的日志记录配置
  • 任务使用的 IAM 角色
  • 任务中用于容器的数据卷
  • 容器在启动时应运行的命令

执行以下步骤创建任务定义:

  1. 进入ECS 任务定义控制台,点击创建任务定义
  2. 选择FARGATE启动类型,点击下一步
  3. 在配置任务和容器定义页面中,输入任务定义名称(heroes),选择任务角色,配置任务内存、CPU

内存、CPU的有效的组合

CPU 值 内存值 (MiB)
256 (.25 vCPU) 512 MB、1 GB、2 GB
512 (.5 vCPU) 1 GB、2 GB、3 GB、4 GB
1024 (1 vCPU) 2 GB、3 GB、4 GB、5 GB、6 GB、7 GB、8 GB
2048 (2 vCPU) 4GB 到 16GB(以1GB为增量)
4096 (4 vCPU) 8GB 到 30GB(以1GB为增量)

容器存储与共享卷
Fargate 启动类型,任务的最大容器存储为10GB,多个容器使用的共享卷的最大大小为4GB。若要添加共享卷,在卷配置部分点击“添加卷”,输入卷名称后点击“添加”即可。注意,任务存储是短暂存储。Fargate 任务停止后,该存储将被删除。

添加容器
接下来,在容器定义部分点击“添加容器”按钮,添加heroes-api和heroes-web容器:
【AWS征文】ECS让容器编排更轻松
在高级容器配置下部的“存储和日志记录”部分可以添加挂载点和配置日志:
【AWS征文】ECS让容器编排更轻松
默认,Fargate任务记录日志到CloudWatch Log,可以设置每个容器的日志组名称、日志流前缀等。

最后,点击“创建”,完成任务定义。

创建Service

服务可在 Amazon ECS 集群中同时运行和管理指定数量的任务定义实例。如果任务出于任何原因失败或停止,服务计划程序将启动另一个任务定义实例来替换它并根据所用的计划策略在服务中保留预期数量的任务。

除了在服务中保留预期数量的任务之外,还可选择借助负载均衡器运行服务。负载均衡器将在与服务关联的各个任务间分配流量。

在ECS控制台,从集群和任务定义均可创建服务。以任务定义为例,步骤如下:

配置基本参数
选中heroes任务定义,然后点击操作 -> 创建服务,在配置服务页面填充以下参数:

  • 启动类型:FARGATE
  • 集群:heroes
  • 服务名称:heroes
  • 任务数:1
  • 部署类型:滚动更新

点击下一步。

网络配置
在VPC 和安全组部分,选择集群 VPC、子网。默认会自动生成一个安全组名称,您可以点击编辑,修改安全组名称,或者选择一个现有安全组。若选择新建安全组,默认配置了80端口的入站规则。
我们使用ELB,不需要公有IP,将自动分配公有 IP选项设为DISABLED。

负载均衡
我们新创建一个ALB,先不必创建目标群组。

  • 负载均衡器类型: Application Load Balancer
  • 负载均衡器名称:选择前面新建的ALB
  • 选择一个容器: heroes-web:80:80,然后点击“添加到负载均衡器”,输入以下参数:
    生产侦听器端口:新建 80
    生产侦听器协议:HTTP
    目标组名称:新建 ecs-heroes
    目标组协议:HTTP

设置 Auto Scaling
服务 Auto Scaling:请勿调整服务的预期计数

审核
检查无误后点击创建服务。

返回到集群界面,可以看到heroes集群含有一个服务,一个正在运行的任务。

服务限制
在集群任务界面,我们若停止当前任务,稍后会自动重启新任务,同时会自动更新ELB的目标群组。

Amazon ECS 服务计划程序包含限制服务任务在反复启动失败后再启动的频率逻辑。

如果一个 ECS 服务的任务总是无法进入 RUNNING 状态(直接从 PENDING 跳到 STOPPED),则后续重启尝试间隔的时间会逐渐拉长,最多达到 15 分钟

访问服务
进入heroes集群 -> 任务,点击任务,进入任务详细信息页面,可以看到任务的私有 IP 地址。在内部网络中,可以使用私有IP访问每个任务部署的应用。公网则通过ELB访问服务。

日志配置

awslogs

默认,Fargate任务使用awslogs日志驱动程序,记录日志到CloudWatch Log。

awslogs 日志驱动程序选项

选项 必须 说明
awslogs-create-group 自动创建日志组,IAM 策略必须包含 logs:CreateLogGroup 权限。默认值false
awslogs-region 日志驱动程序应将Docker 日志发送到的区域
awslogs-group 日志驱动程序将日志流发送到的日志组
awslogs-stream-prefix 日志流前缀,日志流格式:prefix-name/container-name/ecs-task-id
awslogs-datetime-format 以 Python strftime 格式定义多行开始位置模式
awslogs-multiline-pattern 使用正则表达式定义多行开始位置模式

说明:如果同时配置了 awslogs-datetime-format 和 awslogs-multiline-pattern, awslogs-datetime-format选项优先。多行日志记录对所有日志消息执行正则表达式解析和匹配,这可能会对日志记录性能产生负面影响。

FireLens集成

FireLens for Amazon ECS 可将日志路由到 AWS 服务或 AWS 合作伙伴网络(APN)目标位置,以进行日志存储和分析。FireLens 可与 Fluentd 和 Fluent Bit 结合使用。AWS 提供了 Fluent Bit 映像以及用于 CloudWatch Logs 和 Kinesis Data Firehose 的插件,也可以使用自己的 Fluentd 或 Fluent Bit 映像。

Fluent Bit Fluentd是两个日志归集工具,Fluent Bit 插件更节省资源、效率更高,建议使用 Fluent Bit 作为日志路由器。

选中我们前面创建的任务,点击“创建新修订”,启用FireLens 集成:

【AWS征文】ECS让容器编排更轻松

如上图,选中fluentbit后会自动填充映像地址,点击应用将自动添加log_router容器。

注意,使用CloudWatch插件,任务角色必须拥有CreateLogGroup、CreateLogStream、DescribeLogStreams、PutLogEvents权限;使用AmazonKinesisFirehose插件必须拥有 "firehose:PutRecordBatch"权限。

配置log_router

{
    "essential": true,
    "image": "128054284489.dkr.ecr.cn-north-1.amazonaws.com.cn/aws-for-fluent-bit:latest",
    "name": "log_router",
    "firelensConfiguration": {
        "type": "fluentbit"
    },
    "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
            "awslogs-group": "/ecs/firelens",
            "awslogs-region": "cn-north-1",
            "awslogs-create-group": "true",
            "awslogs-stream-prefix": "firelens"
        }
    },
    "memoryReservation": 50
}

Log Router日志使用awslogs驱动,记录到CloudWatch:
【AWS征文】ECS让容器编排更轻松

下面我们利用Fluent Bit将日志分别转发到CloudWatch Logs和Kinesis Data Firehose。

将日志转发到 CloudWatch Logs

修改容器heroes-api的日志配置,驱动程序选择"awsfirelens",日志选项 "Name"值设为"cloudwatch",启用CloudWatch Logs插件,其它配置如下:

{
    "essential": true,
    "image": "888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn/heroes/heroes-api:latest",
    "name": "heroes-api",
    "logConfiguration": {
       "logDriver": "awsfirelens",
       "options": {
         "Name": "cloudwatch",
         "region": "cn-north-1",
         "log_group_name": "/ecs/heroes-api",
         "log_stream_prefix": "from-fluent-bit",
         "auto_create_group": "true"
       }
    }
}

【AWS征文】ECS让容器编排更轻松

任务修改完毕后,点击“创建”保存任务定义。

返回到集群控制台,选中我们创建的服务,点击“更新”,然后选择最新的任务定义,选中“强制实施新部署”,一步步保存服务。

服务更新成功后将创建新的任务,进入Cloud Watch控制台查看日志,日志格式如下:

{
    "container_id": "cda4f603d8e485d48fd7e1a77b3737026221c165b8f6d582ed78bd947a12b911",
    "container_name": "/ecs-heroes-2-heroes-api-c2f3d4a8bdbcf3f9e601",
    "ecs_cluster": "arn:aws-cn:ecs:cn-north-1:888888888888:cluster/isd",
    "ecs_task_arn": "arn:aws-cn:ecs:cn-north-1:888888888888:task/078dd364-28b6-4650-b294-5eac6b39d08f",
    "ecs_task_definition": "heroes:2",
    "log": " /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\",
    "source": "stdout"
}

可以看到,日志添加了新的标识字段:container_id、container_name、ecs_cluster、ecs_task_arn、ecs_task_definition和source。

在任务定义时,点击“通过JSON配置”,设置fluentbit的选项enable-ecs-log-metadata为false,可以禁用上面三个ecs元数据:

"firelensConfiguration": {
    "type": "fluentbit",
    "options": {
        "enable-ecs-log-metadata": "false"
    }
}

在容器的日志配置中,增加log_key选项,可以仅转发log项:

"log_key": "log"

CloudWatch Logs插件支持的选项:

  • region: AWS region
  • log_group_name: CloudWatch日志组名称
  • log_stream_name: CloudWatch日志流名称
  • log_stream_prefix: 日志流名称前缀,与log_stream_name选项不兼容
  • log_key: 默认,整个日志记录都发送到CloudWatch,如果指定key name,仅发送指定项到CloudWatch
  • log_format: 日志格式
  • auto_create_group: 自动创建日志组,默认值false

将日志转发到 Amazon Kinesis Data Firehose

Kinesis Data Firehose插件支持的选项:

  • region: AWS region
  • delivery_stream: Kinesis Data Firehose 传输流的名字
  • data_keys: 默认,整个日志记录都发送到Kinesis, 如果指定key name(s),仅发送指定项到Kinesis。 多个名字以逗号分隔。

Kinesis Data Firehose传输流可以将日志发送到Amazon S3、Amazon Redshift和Amazon Elasticsearch Service。

以S3为例,我们先创建到S3的传输流heroes-web-log,然后修改容器heroes-web的日志配置,驱动程序选择"awsfirelens",日志选项 "Name"值设为"firehose",启用Kinesis Data Firehose插件,其它配置如下:

{
    "essential": true,
    "image": "888888888888.dkr.ecr.cn-north-1.amazonaws.com.cn/heroes/heroes-web:latest",
    "name": "heroes-web",    
    "logConfiguration": {
      "logDriver":"awsfirelens",
      "options": {
        "Name": "firehose",
        "region": "cn-north-1",
        "delivery_stream": "heroes-web-log",
        "data_keys": "log"
      }
    },
    "memoryReservation": 100        
}

【AWS征文】ECS让容器编排更轻松

保存任务定义、更新服务后即可到S3查看日志了。

ECS vs EKS

Amazon Elastic Kubernetes Service (Amazon EKS)是全托管的Kubernetes服务,运行开源的 Kubernetes来部署、管理和扩展容器化应用程序。EKS无需使用Kubernetes控制平面,消除了运行 Kubernetes 的重大操作负担,使得可以专注于构建应用程序而不是管理 AWS 基础设施。EKS可跨多个 AWS 可用区运行,与 Kubernetes 兼容,可以使用 Kubernetes 社区提供的所有现有插件和工具,所有标准 Kubernetes 环境中运行的应用程序均可轻松迁移到EKS。

通用性
ECS是AWS专有技术,而 EKS 则运行开源的 Kubernetes。因此,如果您的部署环境不局限于AWS,比如可能部署到Google GKE(Google Kubernetes Engine)、Microsoft AKS(Azure Kubernetes Service)或标准Kubernetes,应选用EKS。

简易性
ECS是一个开箱即用的解决方案,可通过 AWS 控制台轻松部署。EKS有点复杂,需要更多的配置,需要更多的专业知识。

价格
ECS不收取任何额外费用,只需为容器使用的计算资源付费。EKS集群每小时需支付 0.688 CNY(可以利用 Kubernetes 命名空间和 IAM 安全策略,使用一个EKS 集群运行多个应用程序),用于运行 Kubernetes 工作节点的 AWS 资源需按实际使用量付费。

经过测试与对比ECS、EKS、Kubernetes三种容器编排解决方案,笔者工作中选用了ECS,ECS完全满足项目需求,让部署更轻松、运维更轻松、成本更轻松。

参考文档

Amazon Elastic Container Service 文档