Edited at

ECSで運用していた社内ツールをFargate化したときに、ハマりやすかった3つのポイント

More than 1 year has passed since last update.

AWS Fargate Advent Calendar 2017の25日目の記事になります。

他の方が詳細な見解や調査など行われているので、大トリの記事がこれぐらいの内容で大丈夫なのかビビりながら書いています。

ひとまず、年末年始でFargateを試してみようかなあという方の参考になれば。


1.どんな環境をFargate化したか

fargate.png

上記のような構成でECSで運用していた社内ツールのprpr(※)をFargate化しました。

現在のFargateの制限としては、


  1. 東京リージョンがない

  2. SLAがない

  3. 知見が少ない

ということで、production環境にいきなり入れるというよりは、こうしたサービスレベルの低い社内ツールから移行するのがよいかと思います。

※1 SLA設定されてました Amazon Compute サービスレベルアグリーメントを Amazon ECS および AWS Fargate に拡張

※2 prprについては、下記のブログを参照

prprでGithubのPullRequestレビュー依頼をSlack通知する


2.ハマったところ


2-1.FargateがECRのコンテナイメージをpullできない

デプロイしたECSのステータスが、延々とSTOPPEDを繰り返して、ECSのログを見ると、下記のようなエラーが出力され続けているときがありました。残念ながら、Fargate化されても、デプロイ失敗したときなど、ECSが再起動しまくるのは、自前で何とか検知する仕組みを作らないといけなさそう...

ss 2017-12-24 11.51.14.png


解決例:Fargate側にPublicIPを付与する

Fargate: CannotPullContainer located on ECS registry

にもあるように、FargateはVPC内部で起動してくるため、VPC外部への通信経路を確保しておかないと、FargateがECRからコンテナイメージをおとしてくることができません。特にセキュリティ上などで問題なければ、AssignPublicIpを有効化しておきましょう。


CFn例

Service:

Type: AWS::ECS::Service
Properties:
ServiceName: !Ref RoleName
Cluster: !Ref ECSCluster
DesiredCount: 1
LaunchType: FARGATE
TaskDefinition: !Ref ECSTask
LoadBalancers:
-
ContainerName: !Sub ContainerName
ContainerPort: 3000
TargetGroupArn: !Ref ALBTargetGroup
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref ECSSecurityGroup
Subnets: !Ref SubnetIds


2-2.ECSの動的ポートマッピングは使えない

Fargateのデプロイ中は、下記の画像のような感じで、同一ポートでプライベートIPが異なるという状況になる。Fargateが使用するサブネットでプライベートIPが枯渇したときにどうなるかは未検証。Fargateの起動コンテナ数の制限とかないのであれば、サブネットマスクの設計とかはちょっと注意しておいた方がよさそう。

ss 2017-12-25 7.30.10.png


解決例:ホスト側のポートを固定する


CFn例

ECSTask:

Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref FamilyName
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: 512
ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
ContainerDefinitions:
-
Name: !Ref TaskName
Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RegistoryName}:${ImageTag}"
PortMappings:
-
ContainerPort: 3000
HostPort: 3000
Essential: "true"
Ulimits:
-
Name: nofile
SoftLimit: 65535
HardLimit: 65535
Environment:
-
Name: PORT
Value: 3000
-
Name: RACK_ENV
Value: production
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogGroup
awslogs-region: !Sub ${AWS::Region}
awslogs-stream-prefix: !Ref ImageTag



2-3.CodePipelineでFargateのデプロイを行う、CFnの記述方法がわからない

AWS CodePipeline に Amazon ECS および AWS Fargate のサポートを追加

CodePipeline で ECS にデプロイできるようになり、Docker 環境の継続的デリバリも簡単になりました

にもあるのですが、12/12に、CodePipeline上でFargateのデプロイがサポートされています。

ss 2017-12-25 16.47.19.png

ただ、上記のようなイメージで、CodePipelineとFargateを連携させようとしたときに、CFnのドキュメントからだとCFnでのサンプルが見つけられませんでした。

ひとまず、下記のように書いたら、CFnでも何とか通ったけど、合ってるのかしら(どこかに公式チュートリアルとか準備されてるかな)


解決例:CodeBuildでimagedefinitions.jsonを出力して、CodePipelineのDeployフェーズと連携させる


buildspec.yml例

version: 0.2

phases:
pre_build:
commands:
- $(aws ecr get-login --region $AWS_DEFAULT_REGION)
- REPOSITORY_URI="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}"
- IMAGE_TAG=${CODEBUILD_RESOLVED_SOURCE_VERSION}
build:
commands:
- echo Build started on `date`
- docker build -t "${IMAGE_REPO_NAME}:${IMAGE_TAG}" .
- docker tag "${IMAGE_REPO_NAME}:${IMAGE_TAG}" "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}"
post_build:
commands:
- echo Build completed on `date`
- docker push "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}"
- printf '[{"name":"container-name-sample","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
discard-paths: yes


imagedefinitions.jsonをCodeBuildで生成したフォルダ直下においておけば、

あとは下記のようなCodePipelineの書き方で、Fargateでもデプロイが可能になる。


CFn例

CodePipeline:

Type: AWS::CodePipeline::Pipeline
DependsOn: CodePipelineS3
Properties:
Name: codepipeline-sample
ArtifactStore:
Type: S3
Location: !Ref S3BucketName
RoleArn: !Ref RoleArn
Stages:
-
Name: Source
Actions:
-
Name: Source
RunOrder: 1
ActionTypeId:
Category: Source
Owner: ThirdParty
Version: 1
Provider: GitHub
Configuration:
Owner: hoge
Repo: fuga
Branch: master
OAuthToken: xxxxxxxxxxxx
OutputArtifacts:
- Name: Source
-
Name: Build
Actions:
-
Name: CodeBuild
RunOrder: 1
InputArtifacts:
- Name: Source
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
Configuration:
ProjectName: !Ref CodeBuild
OutputArtifacts:
- Name: Build
-
Name: Deploy
Actions:
-
Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: ECS
InputArtifacts:
- Name: Build
Configuration:
ClusterName: !Ref ClusterName
ServiceName: !Ref ServiceName


3.参考記事

他の方のアドベントカレンダーがすごく参考になったので、CFnまわりの実装で参考にさせていただいた記事をいくつか紹介させていただこうと思います。

Fargate を試した感想と ecs-deploy で Fargate にデプロイできるようにする話

AWS CloudFormationを使ってAWS Fargateの環境を作成してみる

ECS+EC2で動いているサービスをFargateにのせ替える


4.まとめ

社内で使っていたECSをFargate化したことで、EC2の管理(障害対応、およびセキュリティアップデート対応)をなくすことができました。VPCのサブネット設計など、Fargateにしてもインフラ面を意識しないといけないところはありそうなので、また知見がたまれば共有させていただこうと思います。

それでは、皆様メリークリスマス!