目次
はじめに
こんにちは、クラウド事業部の梅本です。
今回は再入門ということで Amazon ECS を使ってコンテナを立てたいと思います。
普段は CloudFormation や Terraform 等の IaC で構築したり、Web コンソールからポチポチで作成するのですが、今回はステップバイステップで改めて関連サービスを意識しながら構築していこうと思います。
環境情報
- クライアント:AWS CloudShell
- AWS CLI:2.16.8
Role作成
最初に Role を作成します。作成する Role の種類は以下の通りです。
- タスクロール
- コンテナ内のアプリがAWSリソースにアクセスするためのロール
- タスク実行ロール
- ECSエージェントがタスクを実行する際に必要なリソースにアクセスするためのロール
- 例)
- ECRからコンテナイメージをプルする
- CloudWatch Logsにログを送信する
- Secret Manager、System Manager Parameter Storeからシークレットを取得
それでは早速作っていきます。
# 作成するリソース名に付与するプレフィックス prefix=ume # Role の URL で利用するためにアカウント ID を取得しておく account_id=$(aws sts get-caller-identity --query 'Account' --output text) # タスクロール作成 aws iam create-role \ --role-name "$prefix-ecs-task-role" \ --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' # タスクロール作成確認 aws iam get-role \ --role-name "$prefix-ecs-task-role" \ --query 'Role.{RoleName:RoleName, CreateDate:CreateDate}' \ --output table ---------------------------------------------------- | GetRole | +----------------------------+---------------------+ | CreateDate | RoleName | +----------------------------+---------------------+ | 2024-06-17T15:38:16+00:00 | ume-ecs-task-role | +----------------------------+---------------------+ # タスクロールに付与するポリシーの作成 # 今回は作成したコンテナに shell でアクセスするためにコマンド実行許可のポリシーを追加 aws iam create-policy \ --policy-name "$prefix-ecs-task-policy" \ --policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ssmmessages:CreateControlChannel", "ssmmessages:CreateDataChannel", "ssmmessages:OpenControlChannel", "ssmmessages:OpenDataChannel" ], "Resource": "*" } ] }' # タスクロールに作成したポリシーをアタッチ aws iam attach-role-policy \ --role-name "$prefix-ecs-task-role" \ --policy-arn "arn:aws:iam::$account_id:policy/$prefix-ecs-task-policy" # タスクロールのポリシーを確認 aws iam list-attached-role-policies \ --role-name "$prefix-ecs-task-role" \ --output table ----------------------------------------------------------------------------------- | ListAttachedRolePolicies | +---------------------------------------------------------------------------------+ || AttachedPolicies || |+-------------------------------------------------------+-----------------------+| || PolicyArn | PolicyName || |+-------------------------------------------------------+-----------------------+| || arn:aws:iam::XXXXXXXXXXXX:policy/ume-ecs-task-policy | ume-ecs-task-policy || |+-------------------------------------------------------+-----------------------+| # タスク実行ロールの作成 aws iam create-role \ --role-name "$prefix-ecs-task-execution-role" \ --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }' # タスク実行ロール作成確認 aws iam get-role \ --role-name "$prefix-ecs-task-execution-role" \ --query 'Role.{RoleName:RoleName, CreateDate:CreateDate}' \ --output table -------------------------------------------------------------- | GetRole | +----------------------------+-------------------------------+ | CreateDate | RoleName | +----------------------------+-------------------------------+ | 2024-06-17T15:41:48+00:00 | ume-ecs-task-execution-role | +----------------------------+-------------------------------+ # タスク実行ロールに AWS 側で用意しているポリシーをアタッチ aws iam attach-role-policy \ --role-name "$prefix-ecs-task-execution-role" \ --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy # タスク実行ロールのポリシーを確認 aws iam list-attached-role-policies \ --role-name "$prefix-ecs-task-execution-role" \ --output table ----------------------------------------------------------------------------------------------------------------- | ListAttachedRolePolicies | +---------------------------------------------------------------------------------------------------------------+ || AttachedPolicies || |+------------------------------------------------------------------------+------------------------------------+| || PolicyArn | PolicyName || |+------------------------------------------------------------------------+------------------------------------+| || arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy | AmazonECSTaskExecutionRolePolicy || |+------------------------------------------------------------------------+------------------------------------+|
ECSタスク定義作成
次に ECS で実行するコンテナの定義である タスク定義
を作成します。
今回は Web ブラウザで動作する VSCode のコンテナイメージ code-server
を利用していきます。
設定は以下の通りです。
- コンテナは
Fargate
で起動する- Fargate を指定した場合、NetworkMode は awsvpc で固定
- CPU/Memory は少し余裕をもって設定
- 作成したタスクロール、タスク実行ロールを指定
- コンテナの設定は
--container-definitions
で設定- code-server のイメージを指定
- essential で必須なコンテナかどうかを指定
- 外部からコンテナにアクセスする際に開放するポートを指定
- 今回は
http
プロトコルの8080
番
- 今回は
# タスク定義作成 aws ecs register-task-definition \ --family "$prefix-task-def" \ --network-mode awsvpc \ --requires-compatibilities FARGATE \ --cpu "1024" \ --memory "2048" \ --task-role-arn "arn:aws:iam::$account_id:role/$prefix-ecs-task-role" \ --execution-role-arn "arn:aws:iam::$account_id:role/$prefix-ecs-task-execution-role" \ --container-definitions '[ { "name": "code-server", "image": "codercom/code-server:4.89.1", "essential": true, "portMappings": [ { "containerPort": 8080, "hostPort": 8080, "protocol": "tcp", "appProtocol": "http" } ] } ]' # タスク定義作成確認 aws ecs describe-task-definition \ --task-definition "$prefix-task-def" \ --query 'taskDefinition.{Family:family, RegisterDate:registeredAt}' \ --output table ------------------------------------------------------ | DescribeTaskDefinition | +---------------+------------------------------------+ | Family | RegisterDate | +---------------+------------------------------------+ | ume-task-def | 2024-06-17T15:44:06.623000+00:00 | +---------------+------------------------------------+
VPC、SecurityGroup 作成
続いて起動するコンテナのネットワーク準備を行います。
今回は簡易な構成にするためパブリックのセグメントのみで構成しています。
# VPC の作成 vpc_id=$(aws ec2 create-vpc \ --cidr-block 10.0.0.0/16 \ --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=$prefix-vpc}]" \ --query 'Vpc.VpcId' \ --output text) # Subnet の作成 subnet_id=$(aws ec2 create-subnet \ --vpc-id "$vpc_id" \ --cidr-block 10.0.1.0/24 \ --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=$prefix-subnet}]" \ --query 'Subnet.SubnetId' \ --output text) # InternetGateway の作成 igw_id=$(aws ec2 create-internet-gateway \ --tag-specifications "ResourceType=internet-gateway,Tags=[{Key=Name,Value=$prefix-igw}]" \ --query 'InternetGateway.InternetGatewayId' \ --output text) # InternetGateway を VPC にアタッチ aws ec2 attach-internet-gateway \ --vpc-id "$vpc_id" \ --internet-gateway-id "$igw_id" # RouteTable の作成 route_table_id=$(aws ec2 create-route-table \ --vpc-id "$vpc_id" \ --tag-specifications "ResourceType=route-table,Tags=[{Key=Name,Value=$prefix-route-table}]" \ --query 'RouteTable.RouteTableId' \ --output text) # RouteTable にデフォルトルートを追加 aws ec2 create-route \ --route-table-id "$route_table_id" \ --destination-cidr-block 0.0.0.0/0 \ --gateway-id "$igw_id" # Subnet に RouteTable に関連付ける aws ec2 associate-route-table \ --subnet-id "$subnet_id" \ --route-table-id "$route_table_id" # SecurityGroup の作成 security_group_id=$(aws ec2 create-security-group \ --group-name "$prefix"-sg \ --description "Allow access for code-server" \ --vpc-id "$vpc_id" \ --output text \ --query 'GroupId') # SecurityGroup にルールを追加 # 今回は code-server の 8080 ポートを許可 aws ec2 authorize-security-group-ingress \ --group-id "$security_group_id" \ --protocol tcp \ --port 8080 \ --cidr "0.0.0.0/0"
NW関連は今回の本題ではないのでサクッと作成します。
ECS クラスター、サービス作成
準備が整ったのでコンテナを起動していきます。
まずは起動のための ECS クラスターを作成します。
# ECS クラスター作成 aws ecs create-cluster \ --cluster-name "$prefix-cluster" # ECS クラスター作成確認 aws ecs describe-clusters \ --clusters "${prefix}-cluster" \ --output table -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | DescribeClusters | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ || clusters || |+---------------------+---------------------------------------------------------------+--------------+--------------------+------------------------------------+--------------------+----------+| || activeServicesCount | clusterArn | clusterName | pendingTasksCount | registeredContainerInstancesCount | runningTasksCount | status || |+---------------------+---------------------------------------------------------------+--------------+--------------------+------------------------------------+--------------------+----------+| || 0 | arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:cluster/ume-cluster | ume-cluster | 0 | 0 | 0 | ACTIVE || |+---------------------+---------------------------------------------------------------+--------------+--------------------+------------------------------------+--------------------+----------+|
クラスタの作成ができたので、コンテナを起動します。ECS でコンテナを起動するためにサービスを作成します。
設定は以下の通りです。
- 作成したクラスター、タスク定義、VPC/SecurityGroup を指定
- Fargate で起動を指定
--desired-count
で起動するコンテナの数を指定assignPublicIp
で Public IP 付与を指定
# サービス作成 aws ecs create-service \ --cluster "$prefix-cluster" \ --service-name "$prefix-service" \ --task-definition "$prefix-task-def" \ --desired-count 1 \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={ subnets=[$subnet_id], securityGroups=[$security_group_id], assignPublicIp=ENABLED }" # サービス作成確認 aws ecs describe-services \ --cluster "$prefix-cluster" \ --services "$prefix-service" \ --query "services[*].{Name:serviceName,Status:status}" \ --output table --------------------------- | DescribeServices | +--------------+----------+ | Name | Status | +--------------+----------+ | ume-service | ACTIVE | +--------------+----------+
動作確認
サービスを作成したことで、コンテナが起動しました。 起動したコンテナに shell 及び Web ブラウザでアクセスしてみます。
shell でアクセスするためにはもう1つ設定とコンテナの再起動が必要です。
# 作成したサービスでコマンドが実行できるように設定を更新 aws ecs update-service \ --cluster "$prefix-cluster" \ --service "$prefix-service" \ --enable-execute-command # 更新した設定を確認 aws ecs describe-services \ --cluster "$prefix-cluster" \ --services "$prefix-service" \ --query "services[*].{Name:serviceName,ExecuteCommand:enableExecuteCommand}" \ --output table ----------------------------------- | DescribeServices | +-----------------+---------------+ | ExecuteCommand | Name | +-----------------+---------------+ | True | ume-service | +-----------------+---------------+ # 設定した内容でコンテナを起動させるため、新しいコンテナを起動 # 新しいコンテナを起動させるため、既存コンテナの Task ID を取得 task_id=$(aws ecs list-tasks --cluster "$prefix-cluster" --service-name ${prefix}-service --query "taskArns[0]" --output text) # 起動しているコンテナを停止し、新しいコンテナを起動する aws ecs stop-task --cluster "$prefix-cluster" --task "$task_id"
Shell でアクセスできる準備ができたので、アクセス確認してみます。
# アクセスするために Task ID を取得 task_id=$(aws ecs list-tasks --cluster ${prefix}-cluster --service-name ${prefix}-service --query "taskArns[0]" --output text) # bash をコンテナ内で実行して、コンテナ内にアクセスする aws ecs execute-command \ --cluster "$prefix-cluster" \ --task "$task_id" \ --container code-server \ --interactive \ --command "/bin/bash" # ここからコンテナ内 The Session Manager plugin was installed successfully. Use the AWS CLI to start a session. Starting session with SessionId: ecs-execute-command-hp565njqk7cd4obq3sdko2hhlm # コンテナの OS 情報を出力 root@b7a3b13147df47dc9c9e2efc5757e547-119361880:/home/coder# cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)" VERSION_CODENAME=bookworm ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" # code-server のバージョンを出力 root@b7a3b13147df47dc9c9e2efc5757e547-119361880:/home/coder# code-server --version [2024-06-17T16:07:46.385Z] info Wrote default config file to /root/.config/code-server/config.yaml 4.89.1 effc6e95b4ad1c5ac5f9083ec06663ba4a2e005c with Code 1.89.1 # 確認できたので、コンテナ内から離れる root@b7a3b13147df47dc9c9e2efc5757e547-119361880:/home/coder# exit # ここまでコンテナ内
最後に Web UI にアクセスします。アクセスのための Public IP を取得します。
aws ec2 describe-network-interfaces \ --network-interface-ids $(aws ecs describe-tasks --cluster "$prefix-cluster" --tasks "$task_id" --query "tasks[0].attachments[0].details[?name=='networkInterfaceId'].value" --output text) \ --query "NetworkInterfaces[0].Association.PublicIp" --output text
アクセスする URL は http://<取得した Public IP>:8080
です。
まとめ
AWS CLI を使って、ECS でコンテナを起動してみました。
ステップバイステップでコマンドを実行することで何を作成し、指定したオプションでどんな設定がなされているのかの理解にもなり私にとって復習にちょうど良かったです。
引き続き ECS と他サービスの連携について投稿できればと思ってます。
お知らせ
APCはAWS Advanced Tier Services(アドバンストティアサービスパートナー)認定を受けております。
その中で私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。
また、AWSの運用自動化ツールも展開しております。
一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。