CloudFormationでFargate入門

目次

CloudFormationでFargateに入門するための構成

AWS FargateはサーバーレスでDockerコンテナを実行することができるサービスです。
今回はFargate入門ということで、CloudFormationでFargate上にコンテナを実行する構成をご紹介します。

構築する環境

Diagram of Introduction to Fargate with CloudFormation.

ECSクラスターを作成し、FargateタイプのECSタスクをパブリックサブネットに関連付けます。

タスクは以下の2種類のイメージを使用して作成します。

  1. DockerHubで公開されているイメージ
  2. 自作し、ECRにプッシュしたイメージ

両コンテナでは、Nginxを起動し、パブリックアドレスを付与した上で、HTTPサーバとして動作させます。

環境構築用のCloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置します。

https://github.com/awstut-an-r/awstut-fa/tree/main/018

テンプレートファイルのポイント解説

今回のアーキテクチャを構成するための、各テンプレートファイルのポイントを取り上げます。

ECRリポジトリ

ECRを定義します。

Resources:
  Repository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Ref Prefix
Code language: YAML (yaml)

コンテナ2は自作イメージを使用します。
そのため自作イメージを保存するリポジトリを用意します。

特別な設定は行いません。
RegistoryNameプロパティでリポジトリ名を設定するだけです。

ECSクラスターを定義する

ECSクラスター関係のリソースを作成します。
1つ目はクラスターです。

Resources:
  Cluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub "${Prefix}-cluster"
Code language: YAML (yaml)

クラスターはサービスやタスクをまとめるグループです。
コンテナが起動する基盤ということになります。

特別な設定は不要です。
ClusterNameプロパティでクラスター名のみ指定します。

タスクを実行するために必要な権限をIAMロールとして定義する

2つ目はIAMロールです。
タスクを実行すること、つまりコンテナを起動すること自体が、デフォルトでは許可されていません。例えばECRからイメージをプルするといったアクションが、タスク実行の内容に含まれています。
そのため専用のIAMロールを用意して、権限を付与する必要があります。

Resources:
  FargateTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      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
Code language: YAML (yaml)

AWS管理ポリシーには、タスク実行に関する最低限の権限をまとめた「AmazonECSTaskExecutionRolePolicy」が用意されてあります。
今回はこちらを使用します。
ちなみに同ポリシーで許可されているアクションは以下となります。

  • ecr:GetAuthorizationToken
  • ecr:BatchCheckLayerAvailability
  • ecr:GetDownloadUrlForLayer
  • ecr:BatchGetImage
  • logs:CreateLogStream
  • logs:PutLogEvents

ECRに対するイメージやイメージを構成するレイヤーの取得や、CloudWatch Logsに対するデータの書き込みを許可する内容であることがわかります。

タスク定義

タスク定義およびサービスを作成します。
まず2コンテナ用のタスク定義を確認します。
タスク定義はコンテナを実行するパラメータの集合体です。
実行するイメージや、CPU、メモリ等を指定します。

Resources:
  TaskDefinition1:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
        - FARGATE
      Cpu: !Ref TaskCpu
      Memory: !Ref TaskMemory
      NetworkMode: awsvpc
      ExecutionRoleArn: !Ref FargateTaskExecutionRole
      TaskRoleArn: !Ref TaskRole
      ContainerDefinitions:
        - Name: !Sub "${Prefix}-task1-container"
          Image: nginx:latest

  TaskDefinition2:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
        - FARGATE
      Cpu: !Ref TaskCpu
      Memory: !Ref TaskMemory
      NetworkMode: awsvpc
      ExecutionRoleArn: !Ref FargateTaskExecutionRole
      TaskRoleArn: !Ref TaskRole
      ContainerDefinitions:
        - Name: !Sub "${Prefix}-task2-container"
          Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RepositoryName}:latest"
Code language: YAML (yaml)

2つのリソースはほとんど同じです。

両者の違いはコンテナを元となるイメージです。
前者はDocker Hubに公開されている公式のnginxイメージです。
後者は先述のECRリポジトリを指定します。
ContainerDefinitions内のImageプロパティをイメージ名を指定します。

RequiresCompatibilitiesプロパティに「FARGATE」を指定しました。
本リソースで定義したタスクは、Fargateタイプのサービス上で動作させる必要があるということになります。

CpuプロパティおよびMemoryプロパティで、1つのタスクのCPUおよびメモリの割り当てを指定します。
今回は、CPUに512(0.5vCPU)、メモリに1024(1GB)を指定しました。
これ以下の値に設定すると、今回動作させるコンテナでは、正常にタスクが起動しなかったためご注意ください。

NetworkModeプロパティでは、タスクのネットワークモードを指定します。Fargateの場合、「awsvpc」に指定する必要があります。

If you are using the Fargate launch type, the awsvpc network mode is required.

AWS::ECS::TaskDefinition NetworkMode

タスク定義では、2つのIAMロールに関するパタメータがあります。
ExecutionRoleArnプロパティとTaskRoleArnプロパティです。
前者は先述の通りですが、改めて以下に整理します。

  • タスク実行IAMロール(ExecutionRoleArn):タスクを実行すること自体に必要な権限。ECRからDockerイメージをPullする権限など。
  • タスク用のロール(TaskRoleArn):タスク内で実行するコンテナに割り当てる権限。後述します。

それぞれ用意したIAMロールのARNを指定します。

サービスを用意する

次に2コンテナ用のサービスを定義します。
サービスはタスク数を管理・調整する機能です。
例えばタスクのスケーリング設定は、サービスに対して行います。

Resources:
  Service1:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref Cluster
      LaunchType: FARGATE
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition1
      ServiceName: !Sub "${Prefix}-service1"
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !Ref ContainerSecurityGroup
          Subnets:
            - !Ref PublicSubnet1

  Service2:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref Cluster
      LaunchType: FARGATE
      DesiredCount: 1
      TaskDefinition: !Ref TaskDefinition2
      ServiceName: !Sub "${Prefix}-service2"
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !Ref ContainerSecurityGroup
          Subnets:
            - !Ref PublicSubnet2
Code language: YAML (yaml)

Clusterプロパティで、本サービスを起動させるクラスターを指定します。

LaunchTypeプロパティでサービスの起動タイプを指定します。
今回はFargateタイプで起動しますので、「FARGATE」とします。

DesiredCountプロパティでサービス上で起動するタスクの希望数を指定できます。
今回は1つのタスクを起動させるように「1」とします。
仮にタスクが何らかの理由で停止した場合は、新たなタスクが自動的に起動して、希望数を満たすようにサービスが調整を行います。

TaskDefinitionプロパティで実行するタスク定義を指定します。
NetworkConfigurationプロパティ内のAwsvpcConfigurationプロパティで、タスクに割り当てられるセキュリティグループや配置されるサブネットを指定します。
今回は2つのパブリックサブネットを作成しますが、1つずつコンテナを配置するように指定します。

タスクが実行する内容のためのIAMロールを用意する

最後にタスク用のIAMロールを定義します。

Resources:
  TaskRole1:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ecs-tasks.amazonaws.com
            Action:
              - sts:AssumeRole
Code language: YAML (yaml)

中身は空っぽです。
今回作成するコンテナはWebサーバとしてだけ動作させ、特にAWSサービスにアクセスすることがないためです。

Dockerfileなど

自作するイメージ用のファイルを用意します。

Dockerfileです。

FROM nginx:latest

COPY index.html /usr/share/nginx/html

EXPOSE 80
Code language: Dockerfile (dockerfile)

nginxの公式イメージをベースとして、自作のindex.htmlファイルをメインページに配置します.

index.htmlです。

<html>
  <head>
  </head>
  <body>
    <h1>fa-018 index.html</h1>
  </body>
</html>
Code language: HTML, XML (xml)

シンプルなHTMLファイルです。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
今回は2回に分けてCloudFormationスタックを作成します。

ECRリポジトリを作成する

まずECRリポジトリを作成します。
以下はS3バケットに設置したテンプレートファイルからスタックを作成する例です。

$ aws cloudformation create-stack \
--stack-name fa-018-ecr \
--template-url https:/[bucket-name].s3.[region].amazonaws.com/fa-018-ecr.yaml
Code language: Bash (bash)

次に作成したECRリポジトリにDockerイメージをプッシュします。
必要なコマンドはAWS Management Consoleから確認できます。

Detail of ECR Repository 1.

上記コマンドを実行した結果は以下の通りです。

Detail of ECR 2.

正常にイメージがプッシュされていることが確認できます。

残りのCloudFormationスタックを作成する

引き続きCloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • ECSクラスター:fa-018-cluster
  • ECSサービス1名:fa-018-service1
  • ECSサービス2名:fa-018-service2

作成されたリソースをAWS Management Consoleから確認します。まずクラスターを確認します。

List of ECS clusters that have been created.

確かにクラスターが作成されていることがわかります。

次にクラスターの詳細を確認します。

Detail of ECS Cluster 2.

クラスター内に2つのサービスが動作していることがわかります。

それぞれのサービス上で動作しているタスクの詳細を確認します。

Detail of ECS Cluster 3.
Detail of ECS Task 2.

それぞれのタスクに割り当てられたパブリックアドレスが確認できます。

コンテナへアクセス

準備が整いましたので、実際にブラウザからアクセスします。

Result of ECS 1.
Result of ECS 2.

正常に各コンテナにアクセスすることができました。
前者はNginxの公式イメージのトップページ、後者は自作イメージのHTMLファイルです。

まとめ

CloudFormationを使用して、FargateタイプのECSコンテナを作成することができました。

目次