ECS(Fargate)でJMeterを構築するっていうタスクに従事中
- タスクを振られたのでなんとか対応せねばなりません
- しかも、以下のような縛りがあります
- JMeterでシナリオ監視をせよ
- ECS、しかもFargateで構築せよ
- ちなみに、ECSやったことない、、、一応AWSのコンテナサービスだってことくらいは知っているくらい
- あと、JMeterもあのWEBサイトに試験的に負荷をかけるアレでしょ、程度の知識しかない、、、
tkaaad97.hatenablog.com
- ドンピシャかと思いきやFargateではなくEC2でクラスタを構築するとのこと、、、
- でも、上記の記事はとても参考になりました。50回くらいは読み込みました!
必死にググることで得たこと
- ECSでは
Fargate
とEC2
を選ぶことができる
Fargate
はEC2
でクラスタを構築してコンテナを動かすといった管理は不要でコンテナを実行できる
- 上記のような基礎的なことはこちらに完璧にまとまっています
dev.classmethod.jp
- ちょうどこのタスクを始めて1週間たった時期の記事で、タイムリーでとても助かりました!
- コンテナでJMeterを動かす、という視点が重要だってこと
- Dockefileを用意しないといけない
- Dockerfileの読み書きくらいはできるようにならないといけない
ではコンテナでJMeterを動かさなくては
- DockerfileはDockerHubから拝借します
hub.docker.com
- とくに
ENTRYPOINT ["/entrypoint.sh"]
理解できるまでとても難しくて、この参考例の場合はあくまで負荷検証ツールとしてリソースをどこまで割り当ててJMeterを起動させるかを考えた上でのENTRYPONT
になっていた
- 自分がやりたいのは、単純にシナリオが1回動けばいいだけ。
- これは、スケジュールが来たらシナリオ監視が1回起動して、シナリオ監視を終えたら停止するコンテナを作ればいいんだという発想になれたことで、先に進めました。バッチ処理みたいにコンテナの利用方法をすれば良いんだなということ。
JMeterでのシナリオ監視結果をCloudWatchに連携しないといけない
- そしてJMeterでの監視結果をCloudWatchに連携しないといけない
- これは
Fargateのログは標準出力に吐き出されたものをCloudWatchと連携できる
ということなので、シナリオ監視の結果を書き出すことで対応
- 参考は以下。めちゃくちゃ参考になりました!(もちろんGoogle翻訳した)
www.concurrencylabs.com
ということで参考までに以下の成果物を
FROM alpine:3.9
ARG JMETER_VERSION="5.1.1"
ARG PLUGIN_VERSION="1.4.0"
ENV JMETER_HOME /opt/apache-jmeter-${JMETER_VERSION}
ENV JMETER_BIN ${JMETER_HOME}/bin
ENV JMETER_DOWNLOAD_URL https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz
ENV PLUGIN_DOWNLOAD_URL https://jmeter-plugins.org/downloads/file/JMeterPlugins-Standard-${PLUGIN_VERSION}.zip
ENV SCENARIO_DOWNLOAD_URL [S3に配置したJMeterのシナリオファイル.jmx]
# Install extra packages
ARG TZ="Asia/Tokyo"
RUN apk update \
&& apk upgrade \
&& apk add ca-certificates \
&& update-ca-certificates \
&& apk add --update openjdk8-jre tzdata curl unzip bash \
&& apk add --no-cache nss \
&& rm -rf /var/cache/apk/* \
&& mkdir -p /tmp/dependencies \
&& curl -L --silent ${JMETER_DOWNLOAD_URL} > /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz \
&& curl -L --silent ${PLUGIN_DOWNLOAD_URL} > /tmp/dependencies/JMeterPlugins-${PLUGIN_VERSION}.zip \
&& curl -L --silent ${SCENARIO_DOWNLOAD_URL} > /tmp/test-plan.jmx \
&& mkdir -p /opt \
&& tar -xzf /tmp/dependencies/apache-jmeter-${JMETER_VERSION}.tgz -C /opt \
&& unzip -oq "/tmp/dependencies/JMeterPlugins-${PLUGIN_VERSION}.zip" -d $JMETER_HOME \
&& rm -rf /tmp/dependencies
ENV PATH $PATH:$JMETER_BIN
WORKDIR ${JMETER_HOME}
ENTRYPOINT [ "jmeter.sh" ]
CMD [ "-n" , "-t" , "/tmp/test-plan.jmx" ]
AWSTemplateFormatVersion: "2010-09-09"
Description:
jmeter-fargate-yml
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
Metadata:
"AWS::CloudFormation::Interface":
ParameterGroups:
- Label:
default: "Project Name Prefix"
Parameters:
- ProjectName
- Label:
default: "Fargate for ECS Configuration"
Parameters:
- ECSClusterName
- ECSTaskName
- ECSTaskCPUUnit
- ECSTaskMemory
- ECSContainerName
- ECSImageName
- ECSServiceName
- ECSTaskScheduleEventsName
- ECSTaskDesiredCount
- Label:
default: "Netowork Configuration"
Parameters:
- VpcId
- ECSSecurityGroupId
- ECSSubnetId
ParameterLabels:
ECSClusterName:
default: "ECSClusterName"
ECSTaskName:
default: "ECSTaskName"
ECSTaskCPUUnit:
default: "ECSTaskCPUUnit"
ECSTaskMemory:
default: "ECSTaskMemory"
ECSContainerName:
default: "ECSContainerName"
ECSImageName:
default: "ECSImageName"
ECSServiceName:
default: "ECSServiceName"
ECSTaskScheduleEventsName:
default: "ECSTaskScheduleEventsName"
ECSTaskDesiredCount:
default: "ECSTaskDesiredCount"
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
ProjectName:
Default: jmeter-test
Type: String
#VPCID
VpcId:
Description : "VPC ID"
Type: AWS::EC2::VPC::Id
#ECSSecurity Group
ECSSecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
#ECSSubnet
ECSSubnetId:
Description : "ECS Subnet"
Type : AWS::EC2::Subnet::Id
#ECSClusterName
ECSClusterName:
Type: String
Default: "cluster"
#ECSTaskName
ECSTaskName:
Type: String
Default: "task"
#ECSTaskCPUUnit
ECSTaskCPUUnit:
AllowedValues: [ 256, 512, 1024, 2048, 4096 ]
Type: String
Default: "256"
#ECSTaskMemory
ECSTaskMemory:
AllowedValues: [ 512, 1024, 2048, 4096 ]
Type: String
Default: "512"
#ECSContainerName
ECSContainerName:
Type: String
Default: "container"
#ECSImageName
ECSImageName:
Type: String
Default: "ECRのURI"
#ECSServiceName
ECSServiceName:
Type: String
Default: "service"
#ECSTaskScheduleEventsName
ECSTaskScheduleEventsName:
Type: String
Default: "TaskScheduleEvents"
#ECSTaskSchedule-Expression
ScheduleExpression:
Type: String
Description: Cron settings
Default: 'cron(0 * * * ? *)' #rate(1 hour)
#ECSTaskDesiredCount
ECSTaskDesiredCount:
Type: Number
Default: 0
Resources:
# ------------------------------------------------------------#
# ECS Cluster
# ------------------------------------------------------------#
ECSCluster:
Type: "AWS::ECS::Cluster"
Properties:
ClusterName: !Sub "${ProjectName}-${ECSClusterName}"
# ------------------------------------------------------------#
# ECS LogGroup
# ------------------------------------------------------------#
ECSLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub "/ecs/logs/${ProjectName}-ecs-group"
# ------------------------------------------------------------#
# ECS Task Execution Role
# ------------------------------------------------------------#
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${ProjectName}-ECSTaskExecutionRolePolicy"
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
# ------------------------------------------------------------#
# ECS TaskDefinition
# ------------------------------------------------------------#
ECSTaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
Cpu: !Ref ECSTaskCPUUnit
ExecutionRoleArn: !Ref ECSTaskExecutionRole
Family: !Sub "${ProjectName}-${ECSTaskName}"
Memory: !Ref ECSTaskMemory
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
# ------------------------------------------------------------#
#ContainerDefinitions
# ------------------------------------------------------------#
ContainerDefinitions:
- Name: !Sub "${ProjectName}-${ECSContainerName}"
Image: !Ref ECSImageName
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref ECSLogGroup
awslogs-region: !Ref "AWS::Region"
awslogs-stream-prefix: !Ref ProjectName
MemoryReservation: 128
PortMappings:
- HostPort: 4445
Protocol: tcp
ContainerPort: 4445
# ------------------------------------------------------------#
# ECS Service
# ------------------------------------------------------------#
ECSService:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref ECSCluster
DesiredCount: !Ref ECSTaskDesiredCount
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref ECSSecurityGroupId
Subnets:
- !Ref ECSSubnetId
ServiceName: !Sub "${ProjectName}-${ECSServiceName}"
TaskDefinition: !Ref ECSTaskDefinition
# ------------------------------------------------------------#
# ECS TaskSchedule # ここ以下をCFnで指定するとエラーになる
# ------------------------------------------------------------#
# TaskScheduleEvents:
# Type: AWS::Events::Rule
# Properties:
# Name: !Ref ECSTaskScheduleEventsName
# ScheduleExpression: !Ref ScheduleExpression
# State: ENABLED
# Targets:
# - Id: 1
# Arn: !GetAtt ECSCluster.Arn
# RoleArn: !GetAtt ECSTaskExecutionRole.Arn
# EcsParameters:
# TaskDefinitionArn: !Ref ECSTaskDefinition
# TaskCount: 1
# LaunchType: FARGATE
# NetworkConfiguration:
# AwsvpcConfiguration:
# AssignPublicIp: ENABLED #本番はdisable
# SecurityGroups:
# - !Ref ECSSecurityGroupId
# Subnets:
# - !Ref ECSSubnetId
# TaskSchedulerRole:
# Type: AWS::IAM::Role
# Properties:
# AssumeRolePolicyDocument:
# Version: "2012-10-17"
# Statement:
# -
# Effect: "Allow"
# Principal:
# Service:
# - "events.amazonaws.com"
# Action:
# - "sts:AssumeRole"
# Path: /
# Policies:
# - PolicyDocument:
# Statement:
# - Effect: "Allow"
# Condition:
# ArnEquals:
# ecs:cluster: !Ref ECSClusterArn
# Action: "ecs:RunTask"
# Resource: "*"
# - Effect: "Allow"
# Condition:
# ArnEquals:
# ecs:cluster: !Ref ECSClusterArn
# Action:
# - "iam:ListInstanceProfiles"
# - "iam:ListRoles"
# - "iam:PassRole"
# Resource: "*"
# PolicyName: "TaskSchedulerPolicy"
注意点
- で、上記のファイルを利用するにあたっての注意点が2つ
- Fargateでスケジュール起動のタスクを作成しようよしても失敗する
qiita.com
- ECRにDockerfileを登録しておく必要がある
把握しないといけないこと多い、、、
- FargateでJMeter構築ってだけかと思ったら、色々と付随して把握しないといけないことがでてきて大変。でも今はこうして1つ1つクリアしていくことが楽しいです!
- あとFargateは元になるDockerfileがポイントだなって感じました。ここでセンスの良し悪しが決まると思いました。