テクノロジー

【AWS】ECSのサービス検出を作ってみました。【CloudFormation】

著者近影
ASUHARU

こんにちは。インフラ統括部のASUHARUです。

AWSに触れ始めて早半年。
CloudFormationで作成したECSのサービス検出を、備忘も兼ねて共有します。
本記事では初めてECSのサービス検出を構築する方を対象にしています。
ECS自体のテンプレートも記載してますので、初めてECSをCloudFormationで作成される方の参考にもなれば幸いです。

ECSのサービス検出(サービスディスカバリ)とは?

ECSの同一クラスター内で各コンテナ間の名前解決を可能にし、フロントエンドとバックエンドでECS サービスを分離するシステムの構成などで使われる機能です。


他コンテナへアクセスするためのDNS名に対応するレコードを、ECSとRoute 53が自動的に登録してくれます。
オートスケーリングでコンテナが増減した場合でも、連動してRoute 53のレコードが自動的に書き換えられます。

同じような機能としてバック側のコンテナの前にELBを配置し、フロント側ではそのELBに対して名前解決を行う方法があります。
一概にサービス検出がELBの代替にはならず、ELBでのパスルーティングや固定レスポンスの機能はサービス検出では設定できません。
ただコンテナ間での通信のみを目的とするのであれば、ECSの機能のみで解決でき設計時の考慮点が減る点からサービス検出の方が使い勝手が良いかなと思います。

作成【CloudFormation】

前提として起動タイプはFargateを採用します。
また、VPCやセキュリティグループ等は、事前に作成しているものを使用しています。

本記事ではCloudFormationのテンプレートのみ記載しておりますので、スタックの作成方法がご不明な方は以下公式リファレンスをご参照ください。

AWS CloudFormation の仕組み

○作成済みのリソース
・VPC
・サブネット
・セキュリティグループ
・タスク用ロール(「test-ecs-role」)
・タスク実行用ロール(「ecsTaskExecutionRole」)
・ECRリポジトリ(「test-ecr」)

AWSTemplateFormatVersion: 2010-09-09
Description: This CloudFormation template to create ECS

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
  ### VPC ID
  VpcId:
    Description: Select VPC-ID
    Type: AWS::EC2::VPC::Id
    
  ### Subnet ID
  SubnetIds:
    Description: Select two Subnet IDs
    Type: List<AWS::EC2::Subnet::Id>
    
  ### Source Security Group ID
  SourceSecurityGroupId:
    Description: Specify the security group ID of the access source.
    Type: AWS::EC2::SecurityGroup::Id

# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      ### FunctionConfigration
      - Label:
          default: Function Configuration
        Parameters:
          - VpcId
          - SubnetIds
          - SourceSecurityGroupId

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  ### ECS Cluster ###
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: test-ecs-cluster
      ClusterSettings:
        - Name: containerInsights
          Value: disabled
      Tags:
        - Key: Name
          Value: test-ecs-cluster
    
  ### ECS TaskDefinition ###
  ECSTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: test-ecs-task
      RequiresCompatibilities:
        - FARGATE
      TaskRoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/test-ecs-role
      NetworkMode: awsvpc
      RuntimePlatform:
        OperatingSystemFamily: LINUX
        CpuArchitecture: X86_64
      ExecutionRoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole
      Cpu: 256
      Memory: 512
      Tags:
        - Key: Name
          Value: test-ecs-task

  ### ECS Container ###
      ContainerDefinitions:
        - Name: test-ecs-container
          Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/test-ecr
          PortMappings:
            - ContainerPort: 80
              Protocol: tcp
          Essential: true

  ### ECS Service ###
  ECSService:
    Type: AWS::ECS::Service
    Properties:
      LaunchType: FARGATE
      TaskDefinition: !Ref ECSTaskDefinition
      PlatformVersion: LATEST
      Cluster: !Ref ECSCluster
      ServiceName: test-ecs-service
      SchedulingStrategy: REPLICA
      DesiredCount: 0
      DeploymentConfiguration:
        MinimumHealthyPercent: 100
        MaximumPercent: 200
      NetworkConfiguration: 
        AwsvpcConfiguration:
          SecurityGroups:
            - !Ref SourceSecurityGroupId
          Subnets:
            - !Select [ 0, !Ref SubnetIds]
            - !Select [ 1, !Ref SubnetIds]
          AssignPublicIp: ENABLED
      ServiceRegistries:
        - RegistryArn: !GetAtt ECSServiceDiscovery.Arn
      Tags:
        - Key: Name
          Value: test-ecs-service

  ### ECS Service Discovery ###
  ECSServiceDiscovery:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      HealthCheckCustomConfig:
        FailureThreshold: 1
      DnsConfig:
        DnsRecords:
          - Type: A
            TTL: 30
        NamespaceId: !GetAtt ECSPrivateDnsNamespace.Id
      Name: test-ecs-service

ちなみにタスク実行用ロール(「ExecutionRoleArn」)で選択している「ecsTaskExecutionRole」にアタッチされているポリシー「AmazonECSTaskExecutionRolePolicy」(AWS 管理ポリシー)の権限は以下になります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

ECRからDockerイメージを取得する場合やコンテナログをCloudWatchへ出力する場合には「AmazonECSTaskExecutionRolePolicy」に含まれている権限が必要となります。
(本記事で構築するECSではECRを使用するためこちらのポリシーを使用しています)
タスク実行用ロールについての詳細やユースケースは公式のドキュメントをご参照ください。
Amazon ECS タスク実行IAM ロール

以下、上記ECSのテンプレート内のサービス検出に関わる部分について説明します。
「ECSPrivateDnsNamespace」は名前空間の設定です。

NameではVPCに紐づく名前空間名を指定します。
VPCにプライベートな名前空間が作成されます。

  ###  ECS Private DnsNamespace ###
  ECSPrivateDnsNamespace:
    Type: AWS::ServiceDiscovery::PrivateDnsNamespace
    Properties:
      Vpc: !Ref VpcId
      Name: test-local

次に「ECSServiceDiscovery」の設定です。
ここではECSのサービス検出の設定を行なっています。

サービスの検出名(「Name」)は作成されるDNSレコードのプレフィックスとなる値になります。
「サービスの検出名.名前空間名」となるので、今回の設定では「test-ecs-service.test-local」というDNS名になります。
レコードのタイプについては公式リファレンスに以下のように記載されています。

サービスタスク定義が awsvpc ネットワークモードを使用する場合、各サービスタスクに A または SRV レコードを自由に組み合わせて作成できます。SRV レコードを使用する場合、ポートが必要です。

サービスタスク定義が bridge または host ネットワークモードを使用する場合、SRV レコードのみがサポートされる DNS レコードタイプです。各サービスタスクの SRV レコードを作成します。SRV レコードのコンテナ名とコンテナポートの組み合わせをタスク定義から指定する必要があります。

サービス検出

本記事ではレコードのタイプはAレコードを選択しています。
尚、ServiceDiscovery 関連リソースのテンプレート記載方法の参照先は、公式リファレンスの「Cloud Map」のセクションにあります。
AWS Cloud Map resource type reference

  ECSServiceDiscovery:
    Type: AWS::ServiceDiscovery::Service
    Properties:
      HealthCheckCustomConfig:
        FailureThreshold: 1
      DnsConfig:
        DnsRecords:
          - Type: A
            TTL: 30
        NamespaceId: !GetAtt ECSPrivateDnsNamespace.Id
      Name: test-ecs-service

挙動確認

作成したテンプレートを基にECSのサービスが構築できたので、挙動を確認していきます。
ECSの「名前空間」にプライベートホストゾーン「test-local」が出来ており、指定したVPCも関連づけられています。

それでは、構築されたホストゾーンにAレコードが作成されるか確認していきます。

ECS サービスのコンソール画面から、「サービスを更新」をクリックします。

更新画面にてデプロイ設定の「必要なタスク」を変更します。

Route 53のホストゾーン「test-local」を確認してみると、Aレコードが一つ作成されています。

試しにタスク数を4つにしてみましょう。

タスク数の増加に連動してAレコードも増加しています。
「必要なタスク」の数を0に変更すると、タスク数の減少に連動しAレコードも削除されました。

ちなみに、Aレコードに割り当てられるIPアドレスについては公式で以下のように記載されています。

パブリック名前空間が使用されている場合でも、 サービス用に作成された DNS レコードは、パブリック IP アドレスではなく、タスクのプライベート IP アドレスに常に登録されます。

サービス検出

まとめ

今回はECSのサービス検出について構築していきました。
便利な機能が多いECS、もっと使いこなしていければと思います。
本記事がECS初学習者の方の参考になれば幸いです。
それでは!

この記事を書いた人

著者近影

ASUHARU

ALH株式会社 インフラ統括部所属。
趣味はボルダリング、読書。
猫を飼おうか検討中。 このライターの他の記事を見る

この記事をシェアする

採用情報RECRUITING Info.