ECS Fargate を用いて自分のアプリをデプロイ

March 17, 2019

LogChance

今回は LogChance というアプリケーションを Fargate を使ってデプロイしようかと思います。
普通のブラウザアプリなんですが、自分のアプリをデプロイすることに意味があると思ってるんですよね、モチベーション的に。
実際にデプロイに移る前に LogChance って何?って話を軽くしておきます。

私はもともと大学で物理学科に所属していたのですが、物理学科では成績評価におけるテストの比重が非常に高かったんです。
で、テスト前日とかはサイゼ○アに皆集まってテスト勉強をしていたわけです。(サイ◯オール部という団体を築き上げました)
でも、テスト勉強とはいえ、人が集まれば会話は生まれるんですよね。そして会話をしていればいずれ誰かがスベることになる、と。

ある日、誰かがスベった時の対処をどうしようか、という議題が上がったんです。
飲みの場であればお酒を飲めば良いだけなのですが、テスト勉強中なのでそれは難しい…。

「そうだ、ストック制にしよう!」

皆の背中に稲妻が走った瞬間でした。
そうか、誰かがスベったらその行いをストックとして記録しておき、テスト期間終了後の打ち上げでストックを消費すれば良いんだ。

その制度が生まれてからは皆どんどんストックを貯めてゆきました。

「はい今のストック〜w」
「うーん、今のはひどすぎるし、流石に 3 ストックものでしょ」

みたいな会話が繰り広げられ、深夜テンションも相まってテスト勉強会はより盛り上がりを見せてきました。

ところがある問題が生まれたんです。
そう、一回で付与されるストックの数がインフレを始めたんです。

「1億ストック」などという無謀な数がストックとして付与されるようになってきました。
でもお酒を1億杯飲むのは現実的じゃないですよね。

そこで LogChance という制度が生まれました。
LogChance は 1/2 の確率で今の自分のストック数に対して底が 10 の対数を取れる、という素晴らしい制度です。(少数繰り上げです)

例えば 100000000 ストックある状態で LogChance に成功すると自分のストックは 8 になります。
皆の目に希望の光が戻りました。

「これに成功すれば打ち上げで1億杯飲まなくて済む…!」

しかしもちろん無条件に対数が取れるわけではありません。1/2 の確率で失敗すると、ストックは据え置きとなり、且つその場でグラスワインを1杯飲まなければいけないというルールがあります。
自分の単位がかかっているテスト勉強中にグラスワインを飲むのはかなりリスクが高い行為です。それでも1億杯という途方も無いストックを抱えるよりは..と考え皆この LogChance に全てをかけました。

上記の制度をシステム化したものが LogChance アプリです。
構成としては、フロントが js、バックエンドが php -> DynamoDB みたいな簡単且つ典型的な Web アプリです。

ちなみに「そもそも勉強中にお酒が飲めないからストック制度が生まれたのでは…?」とかいうツッコミは一切受け付けません。
また、その後皆の単位がどうなったのかについては、触れないでおきます。

ECR にイメージを Push

まずは、ECR にイメージを Push します。
Dockerfile はこんな感じ。

$ cat Dockerfile   
FROM centos:7  
MAINTAINER "yuki"  
  
# install apache2.4  
RUN yum -y install httpd  
  
# install php5.6  
RUN yum -y install epel-release  
RUN rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm  
RUN yum remove php-*  
RUN yum -y install --enablerepo=remi,remi-php56 php php-devel php-mbstring php-mysql php-pdo php-gd  
RUN yum -y install git  
  
WORKDIR /root/  
RUN git clone [GitHub Repository]  
RUN cp -r /root/LogChance/* /var/www/html/  
  
ADD 10-php.conf /opt/docker/etc/httpd/conf.d/  
  
# enable service.  
RUN systemctl enable httpd  

で、docker build して、既に作成してあった ECR レポジトリにイメージを Push します。

$ docker build -t logchance LogChance  
$ $(aws ecr get-login --no-include-email --region us-west-2)  
WARNING! Using --password via the CLI is insecure. Use --password-stdin.  
WARNING! Your password will be stored unencrypted in /home/ec2-user/.docker/config.json.  
Configure a credential helper to remove this warning. See  
https://docs.docker.com/engine/reference/commandline/login/#credentials-store  
  
Login Succeeded  
$ docker tag logchance:latest xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/logchance:latest  
$ docker push xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/logchance:latest  
The push refers to repository [xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/logchance]  
7a7fbb2202e5: Pushed   
ca318149f993: Pushed   
ec3688a20c1e: Pushed   
888f1887b6da: Pushed   
218bb42aae1d: Pushed   
e36614699bec: Pushed   
e9e7a8f0e618: Pushed   
b0c1680ff46e: Pushed   
47ae0adf67db: Pushed   
40b463ba2ba2: Pushed   
d69483a6face: Pushed   
latest: digest: sha256:f91c26a73d4d15fc1788a4b00464231da97cab0a3694cd2b7edbf38aeef846f4 size: 2635  

完了!

タスク実行ロールの作成

Fargate を用いる場合、ECR からイメージを Pull してきたり、awslogs ログドライバーを使用したりするには、タスク実行ロールが必要となるので、それを作っておく。
まずは、以下のファイルを作成。

$ cat task-execution-assume-role.json   
{  
  "Version": "2012-10-17",  
  "Statement": [  
    {  
      "Sid": "",  
      "Effect": "Allow",  
      "Principal": {  
        "Service": "ecs-tasks.amazonaws.com"  
      },  
      "Action": "sts:AssumeRole"  
    }  
  ]  
}  

で、ロールを作ってポリシーをアタッチ。

$ aws iam --region us-east-1 create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://task-execution-assume-role.json  
$ aws iam --region us-east-1 attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy  

クラスター及び設定ファイルの作成

いよいよクラスターを作成。
まずは、ECS CLI を設定して、クラスターを作成し、ついでセキュリティグループも作っておく。

$ ecs-cli configure --cluster LogChance --region us-west-2 --default-launch-type FARGATE --config-name LogChance  
INFO[0000] Saved ECS CLI cluster configuration LogChance.  
$ ecs-cli configure profile --access-key XXXXXXXXXXXXXXXXXXXXXX --secret-key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx --profile-name LogChance  
INFO[0000] Saved ECS CLI profile configuration LogChance.   
$ ecs-cli up --cluster LogChance --vpc vpc-xxxxxxxxxxxxxxxxxxx --subnets subnet-xxxxxxxxxxxxxxxx,subnet-xxxxxxxxxxxxxxxx --security-group sg-xxxxxxxxxxxxxxxx --launch-type FARGATE --region us-west-2 --ecs-profile LogChance  
INFO[0000] Created cluster                               cluster=LogChance region=us-west-2  
INFO[0000] Waiting for your cluster resources to be created...   
INFO[0000] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS  
Cluster creation succeeded.  
$ aws ec2 create-security-group --group-name "logchance-sg" --description "Security Group for LogChance" --vpc-id vpc-03577906fb4ee5ae8 --region us-west-2  
{  
    "GroupId": "sg-xxxxxxxxxxxxxxxxxx"  
}  
$ aws ec2 authorize-security-group-ingress --group-id sg-xxxxxxxxxxxxxxxxxx --protocol tcp --port 80 --cidr 0.0.0.0/0 --region us-west-2  

次に、以下の docker-compose.yml 及び ecs-param.yml を用意する。
ecs-param.yml では VPC 周りの設定やタスクに割り当てる Computing resource 等の設定を行う。

$ cat docker-compose.yml   
version: "2"  
  
services:  
  frontend:  
    image: xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/logchance  
    ports:  
      - "80:80"  
    command: bash -c "/usr/sbin/httpd -DFOREGROUND"  
    logging:  
      driver: awslogs  
      options:  
        awslogs-group: LogChance  
        awslogs-region: us-west-2  
        awslogs-stream-prefix: frontend      
$ cat ecs-param.yml   
version: 1  
task_definition:  
  ecs_network_mode: awsvpc  
  task_execution_role: ecsTaskExecutionRole  
  task_size:  
    cpu_limit: 512  
    mem_limit: 2GB  
  services:  
    web:  
      essential: true  
   
run_params:  
  network_configuration:  
    awsvpc_configuration:  
      subnets:  
        - subnet-xxxxxxxxxxxxxxxxx  
        - subnet-xxxxxxxxxxxxxxxxx  
      security_groups:  
        - sg-xxxxxxxxxxxxxxxxxxx  
      assign_public_ip: ENABLED  

なお、今回は Frontend と Backend それぞれにサービスを作ることにしました。そのため docker-compose.yml の services の部分や awslogs-stream-prefix は frontend -> backend に変更している。
(本当はこの場合タスクで分けるべきなんだろうけど、あえてサービスを 2 つという構成に…)

サービスの開始

ここまで来たら、後はサービスを開始するだけ。
なお、今回は事前に作成しておいた ALB (Target Group) を使用している。また、CloudWatch Logs にログが出力されるように、—create-log-groups オプションもつけておく。

$ ecs-cli compose --project-name logchance-frontend --cluster LogChance --cluster-config LogChance service up --target-group-arn arn:aws:elasticloadbalancing:us-west-2:xxxxxxxxxxxxxxxx:targetgroup/logchance-frontend/xxxxxxxxxxxxxxxx --container-name frontend --container-port 80 --launch-type FARGATE --create-log-groups  
INFO[0000] Using ECS task definition                     TaskDefinition="logchance-frontend:2"  
WARN[0000] Failed to create log group LogChance in us-west-2: The specified log group already exists   
WARN[0000] No log groups to create; no containers use 'awslogs'   
INFO[0000] Auto-enabling ECS Managed Tags                 
INFO[0000] Created an ECS service                        service=logchance-frontend taskDefinition="logchance-frontend:2"  
INFO[0000] Updated ECS service successfully              desiredCount=1 force-deployment=false service=logchance-frontend  
INFO[0015] (service logchance-frontend) has started 1 tasks: (task 2937be539ebd4c6d8ea076f86da4a74c).  timestamp="2019-03-17 13:44:22 +0000 UTC"  
INFO[0060] Service status                                desiredCount=1 runningCount=1 serviceName=logchance-frontend  
INFO[0060] (service logchance-frontend) registered 1 targets in (target-group arn:aws:elasticloadbalancing:us-west-2:xxxxxxxxxxxxxxxx:targetgroup/logchance-frontend/xxxxxxxxxxxxxxxx)  timestamp="2019-03-17 13:45:06 +0000 UTC"  
INFO[0060] ECS Service has reached a stable state        desiredCount=1 runningCount=1 serviceName=logchance-frontend  

バックエンド側は DynamoDB にアクセスする必要があるので、タスク用の IAM ロールを渡してる。

$ ecs-cli compose --project-name logchance-backend --cluster LogChance --cluster-config LogChance --task-role-arn arn:aws:iam::xxxxxxxxxxxx:role/ECSTaskRole service up --target-group-arn arn:aws:elasticloadbalancing:us-west-2:xxxxxxxxxxxx:targetgroup/logchance-backend/xxxxxxxxxxxx --container-name backend --container-port 80 --launch-type FARGATE --create-log-groups   
INFO[0000] Using ECS task definition                     TaskDefinition="logchance-backend:2"  
WARN[0000] Failed to create log group LogChance in us-west-2: The specified log group already exists   
WARN[0000] No log groups to create; no containers use 'awslogs'   
INFO[0000] Auto-enabling ECS Managed Tags                 
INFO[0000] Created an ECS service                        service=logchance-backend taskDefinition="logchance-backend:2"  
INFO[0000] Updated ECS service successfully              desiredCount=1 force-deployment=false service=logchance-backend  
INFO[0015] (service logchance-backend) has started 1 tasks: (task ba3e8d51a75e46e392b69b6de47f10f8).  timestamp="2019-03-17 13:57:20 +0000 UTC"  
INFO[0061] Service status                                desiredCount=1 runningCount=1 serviceName=logchance-backend  
INFO[0061] ECS Service has reached a stable state        desiredCount=1 runningCount=1 serviceName=logchance-backend  

で、DNS 設定してデプロイ完了!
実際のアプリはこんな感じです。

f:id:shiro_kochi:2018××××××××:plain:w100:left

サービスのスケール

ECS CLI を使えばサービスのスケールも簡単に行えます。

$ ecs-cli compose --project-name logchance-frontend --cluster LogChance service scale 3  
INFO[0000] Updated ECS service successfully              desiredCount=3 force-deployment=false service=logchance-frontend  
INFO[0000] Service status                                desiredCount=3 runningCount=1 serviceName=logchance-frontend  
INFO[0015] (service logchance-frontend) has started 2 tasks: (task 52b3a2a98e2f4f41a26de543a6a568e0) (task 1e118bd8ca1f429da5b337b30aa23b7d).  timestamp="2019-03-17 14:11:53 +0000 UTC"  
INFO[0060] Service status                                desiredCount=3 runningCount=3 serviceName=logchance-frontend  
INFO[0060] (service logchance-frontend) registered 1 targets in (target-group arn:aws:elasticloadbalancing:us-west-2:xxxxxxxxxxxx:targetgroup/logchance-frontend/xxxxxxxxxxxxxxxx)  timestamp="2019-03-17 14:12:35 +0000 UTC"  
INFO[0060] ECS Service has reached a stable state        desiredCount=3 runningCount=3 serviceName=logchance-frontend  
$ ecs-cli compose --project-name logchance-backend --cluster LogChance service scale 3  
INFO[0000] Updated ECS service successfully              desiredCount=3 force-deployment=false service=logchance-backend  
INFO[0000] Service status                                desiredCount=3 runningCount=1 serviceName=logchance-backend  
INFO[0015] (service logchance-backend) has started 2 tasks: (task de34041681534ef7a1be71d5e4e5ea03) (task bccb252d8a0346e1a3d5a95fadb3d296).  timestamp="2019-03-17 14:17:16 +0000 UTC"  
INFO[0060] Service status                                desiredCount=3 runningCount=3 serviceName=logchance-backend  
INFO[0060] (service logchance-backend) registered 2 targets in (target-group arn:aws:elasticloadbalancing:us-west-2:xxxxxxxxxxxx:targetgroup/logchance-backend/xxxxxxxxxxxxxxxx)  timestamp="2019-03-17 14:18:11 +0000 UTC"  
INFO[0060] ECS Service has reached a stable state        desiredCount=3 runningCount=3 serviceName=logchance-backend  

 © 2023, Dealing with Ambiguity