AWS ECS on EC2 な環境をDocker公式イメージでサクッと構築 | アントレプログラマー

AWS ECS on EC2 な環境をDocker公式イメージでサクッと構築

AWS

こんにちは、ニキです。

今回の記事では、ECSタスクをEC2上で実行します。

ECSを使えばコンテナ化されたアプリケーションを簡単にデプロイできます。また、Fargateと比較すると請求額を削減できます。

以下の流れで進めます。

  • クラスターの作成
  • Amazon ECS AMI を使用してインスタンスを起動
  • コンテナインスタンスをクラスターに登録
  • タスク定義の作成
  • タスクの実行

簡単のためにECRではなく、Docker公式イメージを使います。

では、解説していきます。

Takaharu Niki

・Webエンジニア6年目。
・バックエンドを中心に、フロントエンドやDevOps業務も経験。
・現在は、自社サービス企業のテックリードとして従事。

Takaharu Nikiをフォローする

ECSクラスターの作成

クラスターを作成します。

ここでは、MyClusterというクラスター名にしています。

aws ecs create-cluster --cluster-name MyCluster

以下のように出力されます。

{
    "cluster": {
        "clusterArn": "arn:aws:ecs:ap-northeast-1:825046422689:cluster/MyCluster",
        "clusterName": "MyCluster",
        "status": "ACTIVE",
        "registeredContainerInstancesCount": 0,
        "runningTasksCount": 0,
        "pendingTasksCount": 0,
        "activeServicesCount": 0,
        "statistics": [],
        "tags": [],
        "settings": [
            {
                "name": "containerInsights",
                "value": "disabled"
            }
        ],
        "capacityProviders": [],
        "defaultCapacityProviderStrategy": []
    }
}

ECSコンソール上で、以下のように作成したクラスターを確認できます。

ECSコンテナインスタンスをクラスターに登録

Amazon ECS AMI を使用してインスタンスを起動します。

タスクを実行するには、ECS用に最適化したコンテナインスタンスを起動する必要があります。

その他には高度な詳細で以下のように設定する以外は、通常の起動方法と同じなのでさらっと解説します。

  • IAMインスタンスプロフィール: ecsInstaceRoleを選択
  • ユーザーデータ: 指定のクラスターでコンテナインスタンスが起動される、スクリプトを記述

EC2起動方法の詳細は以下の記事で解説しています。

アプリケーションと OS イメージ (Amazon マシンイメージ)

以下から、Amazon ECS AMIを選択します。

Amazon ECS に最適化された AMI - Amazon Elastic Container Service
AmazonECSは、AmazonECSLinuxインスタンスにコンテナワークロードを実行する要件と推奨事項で事前設定したAmazonECSに最適化されたAMIを提供します。アプリケーションが特定のオペレーティングシステムやそのAMIでまだ使用できないDockerバージョンを必要とする場合を除き、AmazonEC2イン...

インスタンスタイプとキーペアを選択します。

ネットワーク設定とストレージを設定します。

高度な詳細で以下のように設定します。

  • IAMインスタンスプロフィール: ecsInstaceRole(自動で作成されます)
  • ユーザーデータ:
#!/bin/bash
echo ECS_CLUSTER=MyCluster >> /etc/ecs/ecs.config

概要を確認します。

指定のクラスター内にECSコンテナインスタンスが起動しているかを確認します。

aws ecs list-container-instances --cluster MyCluster

以下のようであれば、正常に起動しています。

{
    "containerInstanceArns": [
        "arn:aws:ecs:ap-northeast-1:aws_account_id:container-instance/MyCluster/container_instance_ID"
    ]
}

指定のクラスターに、コンテナインスタンスを登録します。

この作業で、クラスター内でコンテナインスタンスを使用できるようになります。

aws ecs describe-container-instances --cluster MyCluster --container-instances container_instance_ID

以下のように出力されます。

{
    "containerInstances": [
        {
            "containerInstanceArn": "arn:aws:ecs:ap-northeast-1:account_id:container-instance/MyCluster/container_instance_ID",
            "ec2InstanceId": "*************",
            "version": 3,
            "versionInfo": {
                "agentVersion": "1.76.0",
                "agentHash": "7ab9dd56",
                "dockerVersion": "DockerVersion: 20.10.23"
            },
            "remainingResources": [
                {
                    "name": "CPU",
                    "type": "INTEGER",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 2048
                },
                {
                    "name": "MEMORY",
                    "type": "INTEGER",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 1855
                },
                {
                    "name": "PORTS",
                    "type": "STRINGSET",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 0,
                    "stringSetValue": [
                        "22",
                        "2376",
                        "2375",
                        "51678",
                        "51679"
                    ]
                },
                {
                    "name": "PORTS_UDP",
                    "type": "STRINGSET",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 0,
                    "stringSetValue": []
                }
            ],
            "registeredResources": [
                {
                    "name": "CPU",
                    "type": "INTEGER",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 2048
                },
                {
                    "name": "MEMORY",
                    "type": "INTEGER",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 1855
                },
                {
                    "name": "PORTS",
                    "type": "STRINGSET",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 0,
                    "stringSetValue": [
                        "22",
                        "2376",
                        "2375",
                        "51678",
                        "51679"
                    ]
                },
                {
                    "name": "PORTS_UDP",
                    "type": "STRINGSET",
                    "doubleValue": 0.0,
                    "longValue": 0,
                    "integerValue": 0,
                    "stringSetValue": []
                }
            ],
            "status": "ACTIVE",
            "agentConnected": true,
            "runningTasksCount": 0,
            "pendingTasksCount": 0,
            "attributes": [
                {
                    "name": "ecs.capability.secrets.asm.environment-variables"
                },
                {
                    "name": "ecs.capability.branch-cni-plugin-version",
                    "value": "unknown-"
                },
                {
                    "name": "ecs.ami-id",
                    "value": "ami-0c9def8def64a90c8"
                },
                {
                    "name": "ecs.capability.secrets.asm.bootstrap.log-driver"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.none"
                },
                {
                    "name": "ecs.capability.ecr-endpoint"
                },
                {
                    "name": "ecs.capability.docker-plugin.local"
                },
                {
                    "name": "ecs.capability.task-cpu-mem-limit"
                },
                {
                    "name": "ecs.capability.secrets.ssm.bootstrap.log-driver"
                },
                {
                    "name": "ecs.capability.efsAuth"
                },
                {
                    "name": "ecs.capability.full-sync"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.30"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.31"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.32"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.fluentd"
                },
                {
                    "name": "ecs.capability.firelens.options.config.file"
                },
                {
                    "name": "ecs.capability.logging-driver.awsfirelens.log-driver-buffer-limit"
                },
                {
                    "name": "ecs.availability-zone",
                    "value": "ap-northeast-1a"
                },
                {
                    "name": "ecs.capability.aws-appmesh"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.24"
                },
                {
                    "name": "ecs.capability.task-eni-trunking"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.25"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.26"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.27"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.28"
                },
                {
                    "name": "com.amazonaws.ecs.capability.privileged-container"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29"
                },
                {
                    "name": "ecs.cpu-architecture",
                    "value": "arm64"
                },
                {
                    "name": "ecs.capability.firelens.fluentbit"
                },
                {
                    "name": "com.amazonaws.ecs.capability.ecr-auth"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.20"
                },
                {
                    "name": "ecs.capability.service-connect-v1"
                },
                {
                    "name": "ecs.os-type",
                    "value": "linux"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.21"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.22"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.23"
                },
                {
                    "name": "ecs.capability.task-eia"
                },
                {
                    "name": "ecs.capability.private-registry-authentication.secretsmanager"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.syslog"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.awsfirelens"
                },
                {
                    "name": "ecs.capability.firelens.options.config.s3"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.json-file"
                },
                {
                    "name": "ecs.capability.execution-role-awslogs"
                },
                {
                    "name": "ecs.vpc-id",
                    "value": "vpc-096810d0157e0326f"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
                },
                {
                    "name": "ecs.capability.docker-plugin.amazon-ecs-volume-plugin"
                },
                {
                    "name": "ecs.capability.task-eni"
                },
                {
                    "name": "ecs.capability.firelens.fluentd"
                },
                {
                    "name": "ecs.capability.efs"
                },
                {
                    "name": "ecs.capability.execution-role-ecr-pull"
                },
                {
                    "name": "ecs.capability.task-eni.ipv6"
                },
                {
                    "name": "ecs.capability.container-health-check"
                },
                {
                    "name": "ecs.os-family",
                    "value": "LINUX"
                },
                {
                    "name": "ecs.capability.execute-command"
                },
                {
                    "name": "ecs.subnet-id",
                    "value": "subnet-07d67e95d14649319"
                },
                {
                    "name": "ecs.instance-type",
                    "value": "t4g.small"
                },
                {
                    "name": "com.amazonaws.ecs.capability.task-iam-role-network-host"
                },
                {
                    "name": "ecs.capability.increased-task-cpu-limit"
                },
                {
                    "name": "ecs.capability.network.container-port-range"
                },
                {
                    "name": "ecs.capability.container-ordering"
                },
                {
                    "name": "ecs.capability.cni-plugin-version",
                    "value": "unknown-"
                },
                {
                    "name": "ecs.capability.env-files.s3"
                },
                {
                    "name": "ecs.capability.pid-ipc-namespace-sharing"
                },
                {
                    "name": "ecs.capability.secrets.ssm.environment-variables"
                },
                {
                    "name": "com.amazonaws.ecs.capability.task-iam-role"
                }
            ],
            "registeredAt": "2023-10-08T18:59:31.079000+09:00",
            "attachments": [],
            "tags": []
        }
    ],
    "failures": []
}

ECSコンソール上で、コンテナインスタンスの登録を確認できます。

ECSタスクの実行

タスク定義ファイルを作成します。

touch task-definition.json

今回はNginxを例に以下のように作成します。

動作可能な最小構成にしています。他にもタスク定義パラメーターはあり、以下のリンクから確認できます。

タスク定義パラメータ - Amazon Elastic Container Service
タスク定義は、タスクファミリ、IAMタスクロール、ネットワークモード、コンテナ定義、ボリューム、タスク配置の制約事項、起動タイプの各部分に分かれています。ファミリとコンテナの定義は、タスク定義の必須項目です。これに対して、タスクロール、ネットワークモード、ボリューム、タスク配置の制約、起動タイプは省略することができます...
{
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "nginx",
      "memory": 10,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp"
        }
      ]
    }
  ],
  "family": "nginx"
}

タスク定義を登録します。

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

以下のように出力されます。

{
    "taskDefinition": {
        "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:account_id:task-definition/nginx:2",
        "containerDefinitions": [
            {
                "name": "nginx",
                "image": "nginx",
                "cpu": 0,
                "memory": 10,
                "portMappings": [],
                "essential": true,
                "environment": [],
                "mountPoints": [],
                "volumesFrom": []
            }
        ],
        "family": "nginx",
        "revision": 1,
        "volumes": [],
        "status": "ACTIVE",
        "placementConstraints": [],
        "compatibilities": [
            "EXTERNAL",
            "EC2"
        ],
        "registeredAt": "2023-10-09T15:21:46.147000+09:00",
        "registeredBy": "arn:aws:sts::account_id:assumed-role/************"
    }
}

タスク定義の一覧を表示します。

 aws ecs list-task-definitions

以下のように、作成したタスク定義が表示されます。

{
    "taskDefinitionArns": [
        "arn:aws:ecs:ap-northeast-1:825046422689:task-definition/nginx:1"
    ]
}

ECSコンソールからも確認できます。

タスクを実行します。

aws ecs run-task --cluster MyCluster --task-definition nginx:1 --count 1

以下のように出力されます。

{
    "tasks": [
        {
            "attachments": [],
            "attributes": [
                {
                    "name": "ecs.cpu-architecture",
                    "value": "arm64"
                }
            ],
            "availabilityZone": "ap-northeast-1a",
            "clusterArn": "arn:aws:ecs:ap-northeast-1:account_id:cluster/MyCluster",
            "containerInstanceArn": "arn:aws:ecs:ap-northeast-1:account_id:container-instance/MyCluster/task_ID",
            "containers": [
                {
                    "containerArn": "arn:aws:ecs:ap-northeast-1:account_id:container/MyCluster/task_ID/***************",
                    "taskArn": "arn:aws:ecs:ap-northeast-1:account_id:task/MyCluster/task_ID",
                    "name": "nginx",
                    "image": "nginx",
                    "lastStatus": "PENDING",
                    "networkInterfaces": [],
                    "cpu": "0",
                    "memory": "10"
                }
            ],
            "cpu": "0",
            "createdAt": "2023-10-09T16:23:13.916000+09:00",
            "desiredStatus": "RUNNING",
            "enableExecuteCommand": false,
            "group": "family:nginx",
            "lastStatus": "PENDING",
            "launchType": "EC2",
            "memory": "10",
            "overrides": {
                "containerOverrides": [
                    {
                        "name": "nginx"
                    }
                ],
                "inferenceAcceleratorOverrides": []
            },
            "tags": [],
            "taskArn": "arn:aws:ecs:ap-northeast-1:account_id:task/MyCluster/task_ID",
            "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:account_id:task-definition/nginx:3",
            "version": 1
        }
    ],
    "failures": []
}

タスクの一覧を表示します。

aws ecs list-tasks --cluster MyCluster

実行中のタスクが表示されます。

{
    "taskArns": [
        "arn:aws:ecs:ap-northeast-1:account_id:task/MyCluster/task_ID"
    ]
}

ECSコンソールからも確認できます。

Nginxを例に使ったので、ついでにブラウザからアクセスできるか確認します。

以下のように表示されれば正常に動作しています。

EC2に接続して、コンテナの状態を確認してみます。

sudo docker ps

想定通り動作しているのが確認できました。

CONTAINER ID   IMAGE                            COMMAND                  CREATED             STATUS                      PORTS                               NAMES
4c32404e3165   nginx                            "/docker-entrypoint.…"   17 minutes ago      Up 17 minutes               0.0.0.0:80->80/tcp, :::80->80/tcp   ecs-nginx-1-nginx-acd5f9d58c8098865e00
b73c8bb3e22b   amazon/amazon-ecs-agent:latest   "/agent"                 35 minutes ago      Up 35 minutes (healthy)                                         ecs-agent

まとめ

今回の記事では、ECSタスクをEC2上で実行する方法を解説しました。

Docker/Kubernetes 実践コンテナ開発入門

コンテナ技術をこれから学びたい方、あるいはすでに基本的な知識を持っているが実践的なスキルを伸ばしたい方に最適な書籍です。この書籍では、Dockerの基本操作から始まり、Kubernetesでのクラスタ管理、サービスの自動化、スケーリング、ネットワークの設定といった高度な内容に至るまで、手厚くガイドしてくれます。

Takaharu Niki

・Webエンジニア6年目。
・バックエンドを中心に、フロントエンドやDevOps業務も経験。
・現在は、自社サービス企業のテックリードとして従事。

Takaharu Nikiをフォローする
AWS
Takaharu Nikiをフォローする
アントレプログラマー
タイトルとURLをコピーしました