AWS: ECS – EC2 Container Service: Docker в Amazon

By | 01/09/2017
 

Amazon EC2 Container Service (Amazon ECS) – масштабируемый сервис для запуска, остановки и управления Docker-контейнерами в кластере Amazon Elastic Compute Cloud  (EC2) инстансов.

В этом посте – обзор ECS, его основных компонентов, и пример создания кластера и запуска задачи в нём с помощью AWS CLI.

ECS CLI

Установка

При желании – установим ECS CLI. Дальше в посте использоваться не будет, но в будущем всё-равно пригодится.

Для Linux – выполняем:

$ sudo curl -o /usr/local/bin/ecs-cli https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-latest
$ sudo chmod +x /usr/local/bin/ecs-cli

Проверяем:

$ ecs-cli --version
ecs-cli version 0.4.4 (7e1376e)

Настройка

Файл конфигурации ECS CLI~/.ecs/config.

При первом запуске – CLI требуется настроить параметры доступа к AWS аккаунту с помощью опции configure. Для его вызова – требуется задать параметры доступа, что можно сделать несколькими способами:

  1. С помощью переменных AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY.
  2. При помощи опции --profile, указав имя профиля из файла ~/.aws/credentials (см. AWS: именованные профили доступа).
  3. Испольуя опции --access-key и --secret-key самого ESC CLI.

Выполняем:

$ ecs-cli configure --profile default --cluster rtfm-example
INFO[0000] Saved ECS CLI configuration for cluster (rtfm-example)

Проверяем:

$ cat ~/.ecs/config 
[ecs]
cluster                     = rtfm-example
aws_profile                 = default
region                      = 
aws_access_key_id           = 
aws_secret_access_key       = 
compose-project-name-prefix = ecscompose-
compose-service-name-prefix = ecscompose-service-
cfn-stack-name-prefix       = amazon-ecs-cli-setup-

ESC

Описание

Amazon ECS – сервис по управлению контейнерами в кластере из нескольких Availability Zones в регионе.

Кластер ECS должен работать в AWS VPC, а после запуска кластера – вы должны сконфигурировать EC2 инстансы (ECS Container Instacnes) с Docker, которые будут работать в этом кластере.

Образы Docker хранятся в Docker registry, которые могут быть развернуты в вашем AWS – либо вы можете использовать такие сервисы, как Docker Hub или AWS ECR (Amazon EC2 Container Registry).

Общая схема ECS выглядит так:

Task Definitions

Для запуска вашего приложения в AWS ECS – вы должны создать task definition (“описание задачи”), который представляет собой JSON-файл с описанием одного или более контейнеров. В нём описываются образ, репозиторий, порты, диски, которые будут использоваться контейнерами и т.д.

Пример task definition для запуска одного контейнера с NGINX сервером:

{
      "family": "webserver",
      "containerDefinitions": [
      {
              "name": "web",
              "image": "nginx",
              "cpu": 99,
              "memory": 100,
              "portMappings": [{
                      "containerPort": 80,
                      "hostPort": 80
              }]
      }]
}

Больше примеров можно найти тут>>>, а описание параметров, используемых в task defenitions – тут>>>.

Вы можете думать о файлах с описанием task defnitions как о docker-compose или даже Dockerfile-файлах для ваших сервисов, суть практически та же – описание контейнеров.

Tasks и Scheduling

Task (“задача”) – это выполнение task definition в вашем кластере. Вы можете менять количество задач, запущенных для каждой имеющейся task definition.

Amazon ECS task scheduler – планировщик, отвечающий за размещение задач на ваших инстансах, который имеет несколько опций.

Например – вы можете указать service, который будет запускать и обслуживать несколько задач одновременно.

Подробнее – см тут>>>.

Task можно представлять себе как уже запущенный контейнер, который использует task defenition (“Dockerfile“) для запуска сервиса в нём.

Cluster

Когда вы запускаете выполнение задач в вашем AWS ECS – они выполняются в кластере, который представляет собой логическую группу EC2 инстансов. AWS ECS загружает ваши Docker образы, и запускает соответствующие контейнеры в в кластере.

Подробнее о кластерах – тут>>>, а про ECS Container Instances – тут>>>.

Container Agent

Container Agent работает на каждом инстансе кластера, и отправляет информацию о запущенных на инстансе задачах и используемых ресурсах инстанса непосредственно AWS ECS, а так же выполняет запуск и остановку задач по запросу от ECS.

Сам агент запущен в контейнере, так же, как будут запущены сервисы на этом интансе:

# docker ps
CONTAINER ID        IMAGE                            COMMAND             CREATED             STATUS              PORTS               NAMES
c74d0ac426af        amazon/amazon-ecs-agent:latest   "/agent"            2 minutes ago       Up 2 minutes                            ecs-agent

А его настройки хранятся в файле /etc/ecs/ecs.config:

# cat /etc/ecs/ecs.config
ECS_CLUSTER=rtfm-example

Подробнее – см. тут>>>.

Пример создания ECS

Кластер

Далее с помощью AWS CLI мы создадим кластер, поднимем EC2 интанс с ECS-агентом, создадим task defention и запустим сервис с NGINX.

Вы можете использовать кластер по умолчанию (default), либо создать свой с помощью create-cluster:

$ aws ecs create-cluster --cluster-name rtfm-example
{
    "cluster": {
        "registeredContainerInstancesCount": 1,
        "pendingTasksCount": 0,
        "status": "ACTIVE",
        "clusterName": "rtfm-example",
        "clusterArn": "arn:aws:ecs:eu-west-1:264418146286:cluster/rtfm-example",
        "activeServicesCount": 0,
        "runningTasksCount": 3
    }
}

Используя list-clusters – проверяем:

$ aws ecs list-clusters
{
    "clusterArns": [
        "arn:aws:ecs:eu-west-1:264418146286:cluster/rtfm-example",
        "arn:aws:ecs:eu-west-1:264418146286:cluster/default"
    ]
}

ECS Container Instance

Далее – вы должны создать EC2 инстанс в кластере, на котором будут выполняться задачи.

Ниже представлен список AMI ID для использования в различных регионах:

Region AMI Name AMI ID
us-east-1 amzn-ami-2016.09.c-amazon-ecs-optimized ami-6df8fe7a
us-east-2 amzn-ami-2016.09.c-amazon-ecs-optimized ami-c6b5efa3
us-west-1 amzn-ami-2016.09.c-amazon-ecs-optimized ami-1eda8d7e
us-west-2 amzn-ami-2016.09.c-amazon-ecs-optimized ami-a2ca61c2
eu-west-1 amzn-ami-2016.09.c-amazon-ecs-optimized ami-ba346ec9
eu-central-1 amzn-ami-2016.09.c-amazon-ecs-optimized ami-e012d48f
ap-northeast-1 amzn-ami-2016.09.c-amazon-ecs-optimized ami-08f7956f
ap-southeast-1 amzn-ami-2016.09.c-amazon-ecs-optimized ami-f4832f97
ap-southeast-2 amzn-ami-2016.09.c-amazon-ecs-optimized ami-774b7314

Можно использовать любой другой AMI, в таком случае – потребуется ручная установка ECS агента.

Для того, что бы ECS агент мог выполнять API запросы к ECS сервису – ему требуется IAM роль ecsInstanceRole.

Проверяем её наличие:

$ aws iam list-roles | grep ecsInstanceRole
            "Arn": "arn:aws:iam::264418146286:role/ecsInstanceRole",
            "RoleName": "ecsInstanceRole"

Подробнее про IAM для ECSтут>>>, а про запуск EC2 для ECSтут>>>.

По умолчанию – EC2-инстансы, запущенные с этой ролью будут подключены к кластеру default.

Что бы изменить это – создаём файл для user-data:

$ cat rtfm-cluster.sh
#!/bin/bash
echo ECS_CLUSTER=rtfm-example >> /etc/ecs/ecs.config

Запускаем AMI ami-ba346ec9, передав ему IAM роль ecsInstanceRole и файл rtfm-cluster.sh для user-data:

$ aws ec2 run-instances --image-id ami-ba346ec9 --key-name my-cluster --instance-type t2.nano --iam-instance-profile Name=ecsInstanceRole --user-data file://rtfm-cluster.sh

При проблемах с подключением – смотрим лог агента на запущенном инстансе:

[ec2-user@ip-172-31-34-31 ~]$ cat /var/log/ecs/ecs-agent.log.2017-01-06-12 
2017-01-06T12:29:42Z [INFO] Starting Agent: Amazon ECS Agent - v1.13.1 (efe53c6)
2017-01-06T12:29:42Z [INFO] Loading configuration
...
2017-01-06T12:29:42Z [INFO] Registering Instance with ECS
2017-01-06T12:29:42Z [INFO] Registered! module="api client"
2017-01-06T12:29:42Z [INFO] Registration completed successfully. I am running as 'arn:aws:ecs:eu-west-1:264418146286:container-instance/94953bf4-d74b-479d-aad6-89790befe978' in cluster 'rtfm-example'
...

Проверяем:

$ aws ecs list-container-instances --cluster rtfm-example
{
    "containerInstanceArns": [
        "arn:aws:ecs:eu-west-1:264418146286:container-instance/94953bf4-d74b-479d-aad6-89790befe978"
    ]
}

Просмотреть детали самого инстанса можно с помощью describe-container-instances:

$ aws ecs describe-container-instances --cluster rtfm-example --container-instances 94953bf4-d74b-479d-aad6-89790befe978
{
    "containerInstances": [
        {
            "runningTasksCount": 0,
            "ec2InstanceId": "i-0350dce3645f6c77d",
            "version": 3,
            "pendingTasksCount": 0,
            "agentConnected": true,
            "attributes": [
                {
                    "name": "ecs.availability-zone",
                    "value": "eu-west-1b"
...

Task definition

Создаём task definition:

{
  "containerDefinitions": [
    {
      "name": "rtfm-nginx",
      "image": "nginx",
      "cpu": 10,
      "command": [],
      "memory": 10,
      "essential": true
    }
  ],
  "family": "nginx"
}

Подключаем его:

$ aws ecs register-task-definition --cli-input-json file://rtfm-task-def.json

Проверяем:

$ aws ecs list-task-definitions
{
    "taskDefinitionArns": [
        "arn:aws:ecs:eu-west-1:264418146286:task-definition/nginx:1"
    ]
}

Task

Теперь, когда есть кластер, инстанс и task defenition – с помощью run-task можно запустить задачу:

$ aws ecs run-task --cluster rtfm-example --task-definition nginx:1
{
    "tasks": [
        {
            "desiredStatus": "RUNNING",
            "group": "family:nginx",
            "taskDefinitionArn": "arn:aws:ecs:eu-west-1:264418146286:task-definition/nginx:1",
            "clusterArn": "arn:aws:ecs:eu-west-1:264418146286:cluster/rtfm-example",
            "createdAt": 1483706916.493,
            "containers": [
                {
                    "containerArn": "arn:aws:ecs:eu-west-1:264418146286:container/f54b4ffb-c3ff-412c-80ee-7d8a8de24b1b",
                    "name": "rtfm-nginx",
                    "lastStatus": "PENDING",
                    "taskArn": "arn:aws:ecs:eu-west-1:264418146286:task/0d66808f-6b8c-4ec2-9cd4-84fb0fd3a84d"
                }
            ],
            "version": 1,
            "overrides": {
                "containerOverrides": [
                    {
                        "name": "rtfm-nginx"
                    }
                ]
            },
            "containerInstanceArn": "arn:aws:ecs:eu-west-1:264418146286:container-instance/94953bf4-d74b-479d-aad6-89790befe978",
            "lastStatus": "PENDING",
            "taskArn": "arn:aws:ecs:eu-west-1:264418146286:task/0d66808f-6b8c-4ec2-9cd4-84fb0fd3a84d"
        }
    ],
    "failures": []
}

Проверяем:

$ aws ecs list-tasks --cluster rtfm-example
{
    "taskArns": [
        "arn:aws:ecs:eu-west-1:264418146286:task/0d66808f-6b8c-4ec2-9cd4-84fb0fd3a84d"
    ]
}

Просмотреть информацию о запущенной задаче можно с помощью describe-tasks:

$ aws ecs describe-tasks --cluster rtfm-example --task 0d66808f-6b8c-4ec2-9cd4-84fb0fd3a84d
{
    "tasks": [
        {
            "taskArn": "arn:aws:ecs:eu-west-1:264418146286:task/0d66808f-6b8c-4ec2-9cd4-84fb0fd3a84d",
            "createdAt": 1483706916.493,
            "desiredStatus": "RUNNING",
            "startedAt": 1483706928.403,
            "clusterArn": "arn:aws:ecs:eu-west-1:264418146286:cluster/rtfm-example",
            "containers": [
                {
                    "taskArn": "arn:aws:ecs:eu-west-1:264418146286:task/0d66808f-6b8c-4ec2-9cd4-84fb0fd3a84d",
                    "networkBindings": [],
                    "containerArn": "arn:aws:ecs:eu-west-1:264418146286:container/f54b4ffb-c3ff-412c-80ee-7d8a8de24b1b",
                    "name": "rtfm-nginx",
                    "lastStatus": "RUNNING"
                }
            ],
            "lastStatus": "RUNNING",
            "taskDefinitionArn": "arn:aws:ecs:eu-west-1:264418146286:task-definition/nginx:1",
            "overrides": {
                "containerOverrides": [
                    {
                        "name": "rtfm-nginx"
                    }
...

При проверке сейчас – NGINX не сможет ответить, т.к. не были указаны порты.

Обновляем шаблон task definition, добавляем порт 80 на хосте (EC2 инстансе) и самом контейнере:

{
  "containerDefinitions": [
    {
      "name": "rtfm-nginx",
      "image": "nginx",
      "cpu": 10,
      "command": [],
      "memory": 10,
      "essential": true,
      "portMappings":[
        {
          "containerPort":80,                                                                                                                                                                  
          "hostPort": 80  
        }
      ]
    }
  ],
  "family": "nginx"
}

Добавляем:

$ aws ecs register-task-definition --cli-input-json file://rtfm-task-def.json

Проверяем имеющиеся описания задач:

$ aws ecs list-task-definitions
{
    "taskDefinitionArns": [
        "arn:aws:ecs:eu-west-1:264418146286:task-definition/nginx:1",
        "arn:aws:ecs:eu-west-1:264418146286:task-definition/nginx:2"
    ]
}

Теперь есть задача nginx версии 2.

Находим запущенную задачу:

$ aws ecs list-tasks --cluster rtfm-example
{
    "taskArns": [
        "arn:aws:ecs:eu-west-1:264418146286:task/e04a14fc-2278-4dcf-8349-f46a81507e49"
    ]
}

Стопаем её:

$ aws ecs stop-task --cluster rtfm-example --task e04a14fc-2278-4dcf-8349-f46a81507e49
{
    "task": {
        "group": "family:nginx",
        "desiredStatus": "STOPPED",
...

Запускаем таску со второй ревизии:

$ aws ecs run-task --cluster rtfm-example --task-definition nginx:2

Проверяем:

$ curl 52.214.206.108      
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Контейнер с NGINX на EC2-интансе:

$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS              PORTS                         NAMES
7c541ec81758        nginx                            "nginx -g 'daemon off"   23 seconds ago      Up 22 seconds       0.0.0.0:80->80/tcp, 443/tcp   ecs-nginx-4-rtfm-nginx-8ab8b9ee9fb0a0bbc201
c74d0ac426af        amazon/amazon-ecs-agent:latest   "/agent"                 14 minutes ago      Up 14 minutes                                     ecs-agent

Выводы

Первый раз меня документация AWS приводит в такое уныние.

Например, в одном документе – portmappings, в другом – portMappings, в третьм – PortMappings.

Указание кластера через user-data – тоже выглядит… Как будто AWS не закончил разработку интерфейса в портале. Ну и привязка через IAM смотрится не слишком логичным решением.  Тем не менее – ECS показался сервисом весьма приятным, хотя по началу и немного “замороченным”.