大変ご無沙汰しております。
最近Locust
という負荷試験ツールを触る機会があり、せっかくならCDK(Python)
を利用して構築してみたいと思い立ったので、ECS(Fargate)
上で動く環境を構築していきたいと思います。
さらに今回は、『AWS re:Invent 2022』で発表されたECSの新機能Service Connect
も取り入れていきます。
用語解説(粗)
-
CDK
AWS Cloud Development Kit
のこと。特定のプログラミング言語でAWSリソースをコード化し、そのコードを実行することでAWS環境ができあがる優れものです。
CDK
の導入編としてこちらの記事を書いておりますので、CDK
をまだ試したことのない方はそちらを見ていただくことをお勧めいたします。 -
ECS
DockerコンテナアプリケーションをAWS上で簡単に実行できるサービス。docker-compose
で行うような管理(複数のコンテナ群をひとまとめにして管理するなど)をAWS上で利用できるイメージ。 -
Fargate
ECS
やEKS
(AWSのk8s
サービス)で定義したコンテナをAWSが提供しているホスト環境で実行することができるサービス。いわゆるサーバーレス。
なお、ECS
はEC2
にも展開できますが、EC2
の場合は自分で環境管理が必要になり運用の手間が発生するため、その手間を省きたい場合はFargate
がおすすめです。 -
Service Connect
ECS
内のService
間の通信を容易に設定できるようなサービス。
今まではCloud Map
やApp Mesh
といったサービスを駆使する必要があったのですが、ECS
のサービス内で完結して設定できるようになったようです。(裏ではCloud Map
が動いているようですが、それらをわざわざ自前で準備、または意識しなくても良くなった、という感じでしょうか。) -
Locust
OSSの負荷試験ツール。日本語に訳すと『イナゴ』。Python
コードでテストシナリオを定義できるため、テストシナリオをPython
でごりごり書きたいという方にはおすすめのツールです。
また、Master/Slave
構成で起動することも可能で、Slave
を複数起動させて大規模な負荷をかけることも容易にできます。
以降の記事では、Master
=Master
機、Slave
=Worker
機として解説します。
構成図
今回構築するAWS環境イメージは以下となります。(ECS
周辺サービスのみ掲載)
メインはLocust
側の環境構築となりますが、Locust
の動作確認用におまけでNginx
環境も添えています。
構築手順
実施環境
本記事で構築を実施した環境は以下となります。特別な環境は用意していませんが、Service Connect
を利用できるのはCDK
のバージョンが2.52.0
以上となっていますので、Service Connect
を利用したい場合にはご自身のCDK
のバージョンをお確かめください。
- OS : Ubuntu 20.04 LTS (GNU/Linux 5.10.16.3-microsoft-standard-WSL2 x86_64)
- Python : 3.8.10
- AWS CDK : 2.65.0 (build 5862f7a)
- Nodejs : v19.6.0
- AWS CLI : 1.18.69 (Credentials Configured)
手順
1. CDKプロジェクト作成
まずはCDKプロジェクトを作成します。
cdk init app --language python
のコマンドにて必要なファイルを生成し、その後各環境のスタック別のフォルダやLocust
のコンテナイメージ作成用のフォルダを手動でカスタマイズしまして、以下のようなファイル群を作りました。
本記事では一部の主要なファイルのみ説明していきます。
.
├── README.md
├── app.py # 4.にて説明
├── cdk.context.json
├── cdk.json
├── cdk.out
├── locust_docker # Locustのコンテナイメージ向けフォルダ
│ ├── Dockerfile # 5.にて説明
│ ├── docker-compose.yml # ローカルでのみ利用したため割愛
│ └── locustfile.py # 5.にて説明
├── locust_on_ecs # Locust環境向けスタック向けフォルダ
│ ├── __init__.py
│ └── locust_on_ecs_stack.py # 2.にて説明
├── nginx_on_ecs # Nginx環境向けスタック向けフォルダ
│ ├── __init__.py
│ └── nginx_on_ecs_stack.py # 3.にて説明
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
2. Locust
環境向けスタック作成
今回はLocust
用とNginx
用の2つスタックを作成します。まずはLocust
用のスタックのソースコードを作っていきます。
from aws_cdk import (
RemovalPolicy,
Stack,
aws_ec2 as ec2,
aws_ecs as ecs,
aws_ecr as ecr,
aws_logs as logs,
)
from constructs import Construct
vpc_cidr = "10.0.0.0/16"
repository_name = "locustio/locust/custom"
class LocustOnEcsStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# [1] ECR Repository
repository = ecr.Repository(self, "Repository",
repository_name=repository_name
)
repository.apply_removal_policy(RemovalPolicy.DESTROY)
# [2] Create VPC (Public Only)
vpc = ec2.Vpc(self, "VPC",
cidr=vpc_cidr,
max_azs=2,
subnet_configuration=[
ec2.SubnetConfiguration(
cidr_mask=24,
name='public',
subnet_type=ec2.SubnetType.PUBLIC,
)
],
)
# [3] Create ECS Cluster
cluster = ecs.Cluster(self, "Cluster",
vpc=vpc
)
# [4] Create Cloud Map NameSpace and ECS Cluster Setting (for ECS Service Connect)
namespace = cluster.add_default_cloud_map_namespace(
name="local"
)
# [5] Create Log Group
log_group = logs.LogGroup(self, "LogGroup",
log_group_name="locust-logs"
)
log_group.apply_removal_policy(RemovalPolicy.DESTROY)
# [6] Create ECS Task Definition for Locust Master
task_definition_master = ecs.FargateTaskDefinition(self, "TaskDefinitionForLocustMaster",
memory_limit_mib =512,
cpu=256,
)
task_definition_master.add_container("locust-master",
image=ecs.ContainerImage.from_ecr_repository(repository), # Custom image(from ECR)
port_mappings=[
ecs.PortMapping(
container_port = 8089,
# container_port = 80,
),
ecs.PortMapping(
name="master-and-worker-connect-1",
container_port = 5557,
),
ecs.PortMapping(
name="master-and-worker-connect-2",
container_port = 5558,
)
],
command=[
"-f",
"/mnt/locust/locustfile.py",
"--master",
# "-P",
# "80",
],
logging=ecs.LogDrivers.aws_logs(
log_group=log_group,
stream_prefix="locust-master"
),
)
# [7] Create ECS Task Definition for Locust Worker
task_definition_worker = ecs.FargateTaskDefinition(self, "TaskDefinitionForLocustWorker",
memory_limit_mib=512,
cpu=256,
)
task_definition_worker.add_container("web",
image=ecs.ContainerImage.from_ecr_repository(repository), # Custom image(from ECR)
command=[
"-f",
"/mnt/locust/locustfile.py",
"--worker",
"--master-host",
"locust-master",
],
logging=ecs.LogDrivers.aws_logs(
log_group=log_group,
stream_prefix="locust-worker"
),
)
# [8] Create ECS Service for Locust Master
service_master = ecs.FargateService(self, "ServiceMaster",
cluster=cluster,
task_definition=task_definition_master,
assign_public_ip=True,
desired_count=0,
service_connect_configuration=ecs.ServiceConnectProps(
services=[
ecs.ServiceConnectService(
port_mapping_name="master-and-worker-connect-1",
dns_name="locust-master",
port=5557,
),
ecs.ServiceConnectService(
port_mapping_name="master-and-worker-connect-2",
dns_name="locust-master",
port=5558,
),
],
log_driver=ecs.LogDrivers.aws_logs(
log_group=log_group,
stream_prefix="service-connect-traffic-master"
),
)
)
service_master.node.add_dependency(namespace)
# [9] Create ECS Service for Locust Worker
service_worker = ecs.FargateService(self, "ServiceWorker",
cluster=cluster,
task_definition=task_definition_worker,
assign_public_ip=True,
desired_count=0,
)
service_worker.enable_service_connect()
service_worker.node.add_dependency(namespace)
# [10] Update ECS Service SecurityGroups
service_master.connections.allow_from_any_ipv4(ec2.Port.tcp(8089))
service_master.connections.allow_from(service_worker.connections, ec2.Port.tcp(5557))
service_master.connections.allow_from(service_worker.connections, ec2.Port.tcp(5558))
以下にソースコードの内容を説明します。細かく説明しておりますので少し長くなりますが、ご容赦ください。
-
[1]
ECR
にコンテナリポジトリ作成
Locust
のコンテナイメージを自前で利用するため、それを管理するコンテナリポジトリをECR
上に作成します。後ほどこちらにイメージをPush
します。 -
[2]
VPC
作成
ECS Cluster
内にコンテナを起動させるためには、まずネットワーク環境が必要になりますので、VPC
を作成します。
ECS Cluster
のコンストラクタで自動生成もできますが、NAT Gateway
を利用したリッチな構成が爆誕してしまいますので、今回は節約構成(Public Subnetのみ)を自前で作成します。 -
[3]
ECS Cluster
作成
[2]で構築したVPC
を割り当て作成します。それだけです。 -
[4]
Cloud Map
にName Space
作成+ECS Cluster
に紐づけ
Service Connect
を使用するために、Cloud Map
にName Space
を作成します。
そして作成したName Space
は、[3]で作成したECS Cluster
にて利用でできるように設定します。 -
[5]
CloudWatch Logs
にLog Group
作成
ECS
内で実行するコンテナ群から出力されるログの送信先となるCloud Watch Logs
のLog Group
を作成します。
こちらは事前に作成せずともECS
のタスク定義側で自動生成することもできるのですが、自動生成されたものはCDK
で環境削除した際に残ってしまうため、ゴミが残らないように自前で作成しています。 -
[6]
Fargate
向けTaskDefinition
作成(Locust Master
用)
Locust
のMaster
機が稼働するタスク定義(TaskDefinition
)を作成します。
Master
機はLocustのWebUIが稼働するポート(8089
)とWorkerとのやり取りを行うポート5557
5558
を利用しますので、それらをポートマッピングに定義します。なお、Service Connect
で利用するECS内部のポート(今回は5557
5558
)はECS Service
での設定でname
が必要になりますので、各ポートマッピングにname
をそれぞれ定義してください。(重複NG)
image
には、[1]で作成したECR
のコンテナリポジトリを利用するよう定義をしています。この定義をすると、タスク定義のタスク実行ロールが作成され、ECR
からイメージをPull
する権限が自動的に付与されます。
command
には、このコンテナがMaster
機として稼働するおまじないを実行しています。
logging
には、[5]で作成したLog Group
のログを出力するような定義をしています。この定義をすると、タスク定義のタスク実行ロールが作成され、CloudWatch Logs
へログを送信する権限が自動的に付与されます。 -
[7]
Fargate
向けTaskDefinition
作成(Locust Worker
用)
Locust
のWorker
機が稼働するタスク定義(TaskDefinition
)を作成します。
command
には、このコンテナがWorker
機として稼働するおまじないを実行しています。
ここで重要なのが、Master
機に対する接続先の指定です。今回はlocust-master
というDNS Name
を利用しますので、そちらを指定しています。(後述で説明します。)
その他は[6]と同じ設定ですが、Worker
機はMaster
機に対して接続しに行き、そのコネクション内で通信をする仕様のため、Master
機が起点となる接続はありません(maybe...もし誤ってたらご指摘ください)。そのため、Worker
機のポートマッピング設定は不要となります。 -
[8]
Fargate
向けECS Service
作成(Locust Master
用)
Locust
のMaster
機を動かすためのECS Service
を作成します。
外部からのアクセスしたいため、assign_public_ip
を有効化しています。
現段階では[1]に利用するコンテナイメージをPush
していないので、desired_count
はいったん0
にしています。(デフォルトは1
で自動起動してしまいますのでご注意を。)
service_connect_configuration
ではECS Cluster
内でECS Service
間でマッピングできるよう定義していきます。定義の仕方としては、[6]で定義したポートマッピングのname
に関連付けさせるよう、ポートやDiscovery Name
、DNS Name
を指定します。なお、Discovery Name
はポート単位で名前を分けなければならず、今回のように2つ以上のポートを同一名称で利用した場合にはDNS Name
を利用するのがよさそう(maybe...)なので、5557
5558
どちらも同じDNS Name
(locust-master
)を指定しています。
また、Service Connect
のログを出力することができますので、今回は出力するように定義しています。 -
[9]
Fargate
向けECS Service
作成(Locust Worker
用)
Locust
のWorker
機を動かすためのECS Service
を作成します。
基本的には[8]と同じですが、[7]で説明した通り、Master
機が起点となる接続はありませんので、[8]のようなマッピング定義は不要です。ただし、クライアントとして利用するだけでもService Connect
の有効化を行わないとMaster
機に接続できないので、必ずservice_worker.enable_service_connect()
を定義してください。(ここが今回のドはまりポイントでした…) -
[10]各
ECS Service
に適用されたSecurity Group
修正
最後に各ECS Service
のSecurity Group
を設定します。
ECS Service
では自動的にSecurity Group
を構築してくれますが、デフォルトでは、アウトバウンドトラフィックがすべて許可されているものが作成されるようです。今回は、以下のアクセスが必要となるため、各Security Group
にルールを追加していきます。-
Master
機へブラウザアクセスできるよう8089
ポートをフル開放 -
Worker
機がMaster
機へ5557
5558
ポートで接続するためにMaster
機側のインバウンドルール追加
-
3. Nginx
環境向けスタック作成
次にNginx
用のスタックのソースコードを作っていきます。
こちらは特に説明するところはないため、詳細は割愛しますが、NginxのコンテナイメージはAWSが提供しているDockerリポジトリAmazon ECR Public GalleryにあるNginxの公式イメージpublic.ecr.aws/nginx/nginx:1-alpine-perl
を利用しています。Docker Hub
のイメージを利用するのもありですが、Docker Hub
はPull制限がありますので、制限が気になる方はAmazon ECR Public Gallery
を利用することをお勧めします。
from aws_cdk import (
RemovalPolicy,
Stack,
aws_ec2 as ec2,
aws_ecs as ecs,
aws_logs as logs,
)
from constructs import Construct
vpc_cidr = "10.0.0.0/16"
class NginxOnEcsStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# [1] Create VPC
vpc = ec2.Vpc(self, "VPC",
cidr=vpc_cidr,
max_azs=2,
subnet_configuration=[
ec2.SubnetConfiguration(
cidr_mask=24,
name='public',
subnet_type=ec2.SubnetType.PUBLIC,
)
],
)
# [2] Create ECS Cluster
cluster = ecs.Cluster(self, "Cluster",
vpc=vpc
)
# [3] Create Log Group
log_group = logs.LogGroup(self, "LogGroup",
log_group_name="nginx-logs"
)
log_group.apply_removal_policy(RemovalPolicy.DESTROY)
# [4] Create ECS Task Definition for Nginx
task_definition = ecs.FargateTaskDefinition(self, "TaskDefinition",
memory_limit_mib =512,
cpu=256,
)
task_definition.add_container("nginx",
image=ecs.ContainerImage.from_registry('public.ecr.aws/nginx/nginx:1-alpine-perl'), # from Amazon ECR Public Gallery
logging=ecs.LogDrivers.aws_logs(
log_group=log_group,
stream_prefix="nginx"
),
)
# [5] Create ECS Service for Nginx
service = ecs.FargateService(self, "Service",
cluster=cluster,
task_definition=task_definition,
assign_public_ip=True,
desired_count=1,
)
# [6] Update ECS Service SecurityGroups
service.connections.allow_from_any_ipv4(ec2.Port.tcp(80))
4. 環境デプロイ
AWSに環境をデプロイしていきます。デプロイする前に、今回利用するapp.py
の内容を説明します。
#!/usr/bin/env python3
import os
import aws_cdk as cdk
from locust_on_ecs.locust_on_ecs_stack import LocustOnEcsStack
from nginx_on_ecs.nginx_on_ecs_stack import NginxOnEcsStack
app = cdk.App()
LocustOnEcsStack(app, "LocustOnEcsStack",
env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'),
region=os.getenv('CDK_DEFAULT_REGION')),
)
NginxOnEcsStack(app, "NginxOnEcsStack",
env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'),
region=os.getenv('CDK_DEFAULT_REGION')),
)
cdk.Tags.of(app).add("User", "MatsuMikan")
app.synth()
今回はLocust
用とNginx
用でスタックを分けています。そのため、それぞれ二つのスタックを定義し、デプロイ先のAWSアカウントIDやリージョンを指定するようにしています。また、おまけとしてすべてのリソースに自分の名前をタグ付けするようにもしています。
では、デプロイしていきます。一度もCDK
を実行したことがない場合はcdk bootstrap
のコマンドを行う必要がありますが、私はすでに実施済みなので省略し、今回利用するAWSアカウントIDとリージョンを環境変数に設定したうえで、すべてのスタックをデプロイするコマンドを実行します。
$ export CDK_DEFAULT_ACCOUNT=0123456789AB # Deploy to AWS Account ID
$ export CDK_DEFAULT_REGION=ap-northeast-1 # Deploy to Region
$ cdk deploy --all # --all オプション付与でapp.pyに定義したスタックを一括デプロイ
問題なくデプロイしたことは、cdk
コマンドの結果もしくはコンソール上で確認できます。
5. Locust
のDockerイメージ作成+ECRにPush
ここからはLocust
のコンテナイメージ作成していきます。
今回はLocust公式が提供しているイメージlocustio/locust
を利用し、自前で作成したテストコードlocustfile.py
をイメージの中に内包します。
FROM locustio/locust
WORKDIR /mnt/locust
COPY *.py /mnt/locust/
以下はLocust
上で実行するテストコードです。Locust
の中身についてはあまり深くは触れませんが、以下の内容が含まれています。
- WebUIで指定した
Host
にGet
メソッドでアクセスする - 2~5秒の間でランダム秒数でタスク(
Host
へのアクセス)を繰り返し実行する - Locust側の標準ログに各ユーザが何回
Host
へのアクセスしたかをわかるように出力する(ユーザはWebUIのユーザ数により生成)
from locust import HttpUser, task, events, between
from locust.runners import MasterRunner, WorkerRunner
usernames = []
def setup_test_users(environment, msg, **kwargs):
usernames.extend(map(lambda u: u["name"], msg.data))
@events.init.add_listener
def on_locust_init(environment, **_kwargs):
if not isinstance(environment.runner, MasterRunner):
environment.runner.register_message("test_users", setup_test_users)
@events.test_start.add_listener
def on_test_start(environment, **_kwargs):
if not isinstance(environment.runner, WorkerRunner):
users = []
for i in range(environment.runner.target_user_count):
users.append({"name": f"User{i+1}"})
worker_count = environment.runner.worker_count
chunk_size = int(len(users) / worker_count)
for i, worker in enumerate(environment.runner.clients):
start_index = i * chunk_size
if i + 1 < worker_count:
end_index = start_index + chunk_size
else:
end_index = len(users)
data = users[start_index:end_index]
environment.runner.send_message("test_users", data, worker)
class WebAccessUser(HttpUser):
seq = 0
wait_time = between(2, 5)
def __init__(self, parent):
self.username = usernames.pop()
super().__init__(parent)
@task
def web_access(self):
self.seq += 1
self.client.get("/")
print({
"Username": self.username,
"Sequence": str(self.seq)
})
上記ファイル群をビルドしてイメージを作成し、Locust
環境向けスタック内で準備したECR
のリポジトリにPush
します。
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 0123456789AB.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker build -t locustio/locust/custom .
$ docker tag locustio/locust/custom:latest 0123456789AB.dkr.ecr.ap-northeast-1.amazonaws.com/locustio/locust/custom:latest
$ docker push 0123456789AB.dkr.ecr.ap-northeast-1.amazonaws.com/locustio/locust/custom:latest
6. ECS Service
にてタスク起動
いよいよ準備が整ったので、Locust
のECS Service
にてタスクを起動していきます。
今回はCDK
でタスク数を変更し、再度deploy
していきます。
# {前略}
# [8] Create ECS Service for Locust Master
service_master = ecs.FargateService(self, "ServiceMaster",
cluster=cluster,
task_definition=task_definition_master,
assign_public_ip=True,
- desired_count=0,
+ desired_count=1,
# {中略}
# [9] Create ECS Service for Locust Worker
service_worker = ecs.FargateService(self, "ServiceWorker",
cluster=cluster,
task_definition=task_definition_worker,
assign_public_ip=True,
- desired_count=0,
+ desired_count=2,
# {後略}
$ cdk deploy LocustOnEcsStack
デプロイ結果はこちら。
Locust
側は合計3
つのタスク(Master
:1
,Worker
:2
)が無事に起動していることが確認できます。
7. ブラウザ経由でLocust
へアクセス+Nginx
へのアクセス実施
まずはNginxにブラウザ経由でアクセスしてみます。
- http://{Nginxのタスクのパブリック IP}/
無事アクセスできました。このURLはメモしておきます。
続きましてLocust
へアクセスします。 - http://{LocustのMaster機のタスクのパブリック IP}:8089/
こちらもアクセスできました。(初回アクセス時のスクショを取り忘れたので若干残像が残っていますが)
右上にひょうじされているWORKERS
の数字が2
と表示されていますが、これはECS Service
でデプロイしたタスク数と一致しており、Master
機とWorker
機が無事に接続できていることが確認できます。
それでは実際に動かしてみましょう。
Locust
のWebUIでは、アクセスするユーザ数やホスト、実行時間などを指定することができますので、今回は上記画面の通りに設定して動かしてみます。
そして、動かしてみた結果(チャート)がこちら。
Spawn rate
で指定した時間に合わせてじわじわアクセスユーザを増やし、指定したユーザ数10
のアクセスが維持された状態で定期的にリクエストが飛んでいたようです。
次はLocust
のWorker
機のログを見てみましょう。
ログ出力の仕方が雑だったのでわかりづらいですが、User1~10が、Nginx
に1分間にアクセスしていたであろう形跡が確認できました。
Worker
機が2台いたのですが、2台それぞれにUserが分散されて、各Worker
機からアクセスしていたようです。(Logstream
がタスク単位で払い出されるため、そちらとUserの番号を合わせて見ていくとそんな動きをしているようでした。)
Nginx
側も同時刻にアクセスログが動いており、Locust
からのアクセスを受信できていることも確認できました。
という感じで、すべての環境をCDK
を利用して構築(デプロイ)し、想定通りに動くところまで確認することができました。
お片付け
やりたいことは終わりましたので、環境を削除します。
早速cdk destroy --all
で全スタック一括デストロイしたい気持ちはやまやまですが、今回ECR
にイメージをPush
しているため、まずはこちらを削除するところから始まります。
ECR
はS3
と一緒で、データを空にしないとリポジトリを削除できない制限がかかっておりますので、まずはイメージを削除します。
$ aws ecr batch-delete-image --repository-name locustio/locust/custom --image-ids imageTag=latest
そして環境をすべてデストロイします。
$ cdk destroy --all
Are you sure you want to delete: NginxOnEcsStack, LocustOnEcsStack (y/n)? y
NginxOnEcsStack: destroying... [1/2]
✅ NginxOnEcsStack: destroyed
LocustOnEcsStack: destroying... [2/2]
✅ LocustOnEcsStack: destroyed
きれいになりました。
【補足】Service Connect
について
ECS
のタスクを確認したところ、タスク定義で指定したLocust
のコンテナ(以下の画像ではweb
と表記)以外に、ecs-service-connect-xxx
なるコンテナが動いていました。
おそらくこちらがService Connect
としての役目を果たす者(?)です。
Service Connect
ログを見てみると…なにやらEnvoy
が動いている模様でした。
また、Master
機のService Connect
をコンソール上で確認したところ、以下のようになっていました。
Service Connect
でECS内の名前解決する方法は二つあり、検出(Discovery Name
)とDNS Name
のいずれかを利用することができそうです。
Discovery Name
はデフォルトではポートマッピング名を使用していますが、変更できます。
ただし、既に説明した通り、Discovery Name
ポート単位で一意になるため、今回のような複数のポートで同一の名称を利用したいケースの場合はDNS Name
を利用するのがよさそうです。
ちなみに、Discovery Name
を利用する場合は、Discovery Name
.Name Space
という形式で利用することになるのでご注意ください。
【余談】その他にはまったこと
実は今回Locost
のMaster
機のWebUIのポートをデフォルトの8089
ではなく80
に変更したかったのですが、うまくいきませんでした。ローカル上ではうまくいったのですが、Fargate
では権限的な問題で指定できないようです。
そのため、80
ポートなど他のポートでWebUIにアクセスしたい場合は、Master
機の前にALB
を利用してフォワーディングさせるしかなさそうです。(もし別の手段がありましたらご教示くださいませ)
ちなみに80
ポートを利用しようとしてダメだったログはこちら。
[2023-02-19 11:35:08,755] ip-10-0-1-58.ap-northeast-1.compute.internal/INFO/locust.main: Starting web interface at http://0.0.0.0:80 (accepting connections from all network interfaces)
[2023-02-19 11:35:08,840] ip-10-0-1-58.ap-northeast-1.compute.internal/INFO/locust.main: Starting Locust 2.14.2
Traceback (most recent call last):
File "src/gevent/greenlet.py", line 908, in gevent._gevent_cgreenlet.Greenlet.run
File "/opt/venv/lib/python3.10/site-packages/locust/web.py", line 473, in start_server
self.server.serve_forever()
File "/opt/venv/lib/python3.10/site-packages/gevent/baseserver.py", line 398, in serve_forever
self.start()
File "/opt/venv/lib/python3.10/site-packages/gevent/baseserver.py", line 336, in start
self.init_socket()
File "/opt/venv/lib/python3.10/site-packages/gevent/pywsgi.py", line 1545, in init_socket
StreamServer.init_socket(self)
File "/opt/venv/lib/python3.10/site-packages/gevent/server.py", line 180, in init_socket
self.socket = self.get_listener(self.address, self.backlog, self.family)
File "/opt/venv/lib/python3.10/site-packages/gevent/server.py", line 192, in get_listener
return _tcp_listener(address, backlog=backlog, reuse_addr=cls.reuse_addr, family=family)
File "/opt/venv/lib/python3.10/site-packages/gevent/server.py", line 288, in _tcp_listener
sock.bind(address)
File "/opt/venv/lib/python3.10/site-packages/gevent/_socketcommon.py", line 563, in bind
return self._sock.bind(address)
PermissionError: [Errno 13] Permission denied: ('', 80)
2023-02-19T11:35:08Z <Greenlet at 0x7ff58e92bf60: <bound method WebUI.start_server of <locust.web.WebUI object at 0x7ff58e8b7130>>> failed with PermissionError
[2023-02-19 11:35:08,843] ip-10-0-1-58.ap-northeast-1.compute.internal/CRITICAL/locust.web: Unhandled exception in greenlet: <Greenlet at 0x7ff58e92bf60: <bound method WebUI.start_server of <locust.web.WebUI object at 0x7ff58e8b7130>>>
Traceback (most recent call last):
File "src/gevent/greenlet.py", line 908, in gevent._gevent_cgreenlet.Greenlet.run
File "/opt/venv/lib/python3.10/site-packages/locust/web.py", line 473, in start_server
self.server.serve_forever()
File "/opt/venv/lib/python3.10/site-packages/gevent/baseserver.py", line 398, in serve_forever
self.start()
File "/opt/venv/lib/python3.10/site-packages/gevent/baseserver.py", line 336, in start
self.init_socket()
File "/opt/venv/lib/python3.10/site-packages/gevent/pywsgi.py", line 1545, in init_socket
StreamServer.init_socket(self)
File "/opt/venv/lib/python3.10/site-packages/gevent/server.py", line 180, in init_socket
self.socket = self.get_listener(self.address, self.backlog, self.family)
File "/opt/venv/lib/python3.10/site-packages/gevent/server.py", line 192, in get_listener
return _tcp_listener(address, backlog=backlog, reuse_addr=cls.reuse_addr, family=family)
File "/opt/venv/lib/python3.10/site-packages/gevent/server.py", line 288, in _tcp_listener
sock.bind(address)
File "/opt/venv/lib/python3.10/site-packages/gevent/_socketcommon.py", line 563, in bind
return self._sock.bind(address)
PermissionError: [Errno 13] Permission denied: ('', 80)
[2023-02-19 11:35:09,345] ip-10-0-1-58.ap-northeast-1.compute.internal/INFO/locust.main: Shutting down (exit code 2)
さいごに/感想
久しぶりにCDK
を触ったのですが、やはり最低限のコード記述だけでいい感じに環境構築できるのは大変素晴らしい!と改めて実感しました。(CloudFormationは細かく定義しなければならないので、それに比べると本当に楽ちんでした…)
そしてSevice Connect
の登場で、ECS Service
間の通信設定がかなり簡単にできることもわかり、ECS
ユーザにはかなり良いアップデートだったのではないでしょうか。
ちなみにService Connect
はリリースされてから3か月程度しかたっていないと思うのですが、早い段階でCDK
経由で構築できるようになっていたので、AWSの中の皆様のお仕事の速さにも驚きです。
こんなに便利なツールCDK
をもっと積極的に勉強して使っていかねばと思いつつ、その前に自身のPython
言語の理解の浅さを痛感した(ドキュメントの解読に苦労していた)ため、Python
言語力(それともコーディング力?)をまずは養う必要があるのでは…と感じた次第でした。
こちらからは、以上です。
参考URL