本連載では、以下のイメージの構成にあるAWSリソース基盤自動化環境の構築を実践しています。

本連載で構築していく基盤自動化環境のイメージ

前回は、ALB、ElastiCache、S3といったリソースアクセス、SQSへのキュー送信を行うFrontend Webアプリケーションにおいて、Spring Cloud AWSを用いて取得したスタック情報を使ったアプリケーションの設定実装を紹介しました。

今回からは実装したアプリケーションを実行するためのECSクラスタ、タスク定義、サービスと、順次CloudFormationテンプレートを作成して実行していきます。

なお、実際のソースコードはGitHub上にコミットしています。以降のソースコードでは本質的でない記述を一部省略しているので、実行コードを作成する場合は、必要に応じて適宜GitHub上のソースコードも参照してください。

事前準備

事前準備として、前回実装したアプリケーションのコンテナイメージを作成し、DockerHubへプッシュしておきましょう。作業の方法は連載「AWSで作るクラウドネイティブアプリケーションの基本」の第7回を参考にしてください。Backend Serviceアプリケーションと、Frontend Webアプリケーションのコンテナイメージを作成するDockerfileのサンプルはそれぞれ以下の通りです。

# Dockerfile for sample service using embedded tomcat server

FROM centos:centos7
MAINTAINER debugroom

RUN yum install -y \
    java-1.8.0-openjdk \
    java-1.8.0-openjdk-devel \
    wget tar iproute git

RUN rm -f /etc/rpm/macros.image-language-conf && \
    sed -i '/^override_install_langs=/d' /etc/yum.conf && \
    yum -y update glibc-common && \
    yum clean all

ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"

RUN wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
RUN sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
RUN yum install -y apache-maven
ENV JAVA_HOME /etc/alternatives/jre
RUN git clone https://github.com/debugroom/mynavi-sample-aws-cloudformation.git /usr/local/mynavi-sample-aws-cloudformation
RUN mvn install -f /usr/local/mynavi-sample-aws-cloudformation/common/pom.xml
RUN mvn package -f /usr/local/mynavi-sample-aws-cloudformation/backend-service/pom.xml
RUN cp /etc/localtime /etc/localtime.org
RUN ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

EXPOSE 8080

CMD java -jar -Dspring.profiles.active=$ENV_TYPE /usr/local/mynavi-sample-aws-cloudformation/backend-service/target/mynavi-sample-cloudformation-backend-0.0.1-SNAPSHOT.jar
# Dockerfile for sample service using embedded tomcat server

FROM centos:centos7
MAINTAINER debugroom

RUN yum install -y \
    java-1.8.0-openjdk \
    java-1.8.0-openjdk-devel \
    wget tar iproute git

RUN rm -f /etc/rpm/macros.image-language-conf && \
    sed -i '/^override_install_langs=/d' /etc/yum.conf && \
    yum -y update glibc-common && \
    yum clean all

ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8"

RUN wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
RUN sed -i s/\$releasever/6/g /etc/yum.repos.d/epel-apache-maven.repo
RUN yum install -y apache-maven
ENV JAVA_HOME /etc/alternatives/jre
RUN git clone https://github.com/debugroom/mynavi-sample-aws-cloudformation.git /usr/local/mynavi-sample-aws-cloudformation
RUN mvn install -f /usr/local/mynavi-sample-aws-cloudformation/common/pom.xml
RUN mvn package -f /usr/local/mynavi-sample-aws-cloudformation/frontend-webapp/pom.xml
RUN cp /etc/localtime /etc/localtime.org
RUN ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

EXPOSE 8080

CMD java -jar -Dspring.profiles.active=$ENV_TYPE /usr/local/mynavi-sample-aws-cloudformation/frontend-webapp/target/mynavi-sample-cloudformation-frontend-0.0.1-SNAPSHOT.jar

ECSクラスタスタック構築テンプレート

ECSクラスタは、連載「AWSで作るクラウドネイティブアプリケーションの基本」の第8回と同等のものを構築します。ECSクラスタをCloudFormationで構築する場合、リソースタイプが「AWS::ECS::Cluster」で、クラスタの実行に必要なIAMロール「AWS::IAM::Role」と、そのロールを設定したインスタンスプロファイル「AWS::IAM::InstanceProfile」、オートスケールルールを設定した「AWS::AutoScaling::AutoScalingGroup」に加えて、起動設定を定義した「AWS::AutoScaling::LaunchConfiguration」が必要です。プロパティとして設定可能な属性は、上記リンク先の通りですが、加えて、ECSを「商用環境」「ステージング環境」「開発環境」という3つのパターンに分けて作成するようにします。

テンプレートのサンプルは以下の通りです。

AWSTemplateFormatVersion: '2010-09-09'

// omit

Parameters:
  ECSAMI:
    Description: AMI ID
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>                                       #(A)
    Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id
  EnvType:
    Description: Which environments to deploy your service.
    Type: String
    AllowedValues:
      - Dev
      - Staging
      - Production
    Default: Dev

Mappings:
  FrontendClusterDefinitionMap:                                                                 #(B)
    Production:
      "InstanceType" : "r4.large"
      "DesiredCapacity" : 1
      "EC2InstanceMaxSizeOfECS": 3
      "KeyPairName" : "test"
 // omit

Resources:
  ECSRole:                                                                                      #(C)
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role

  IamInstanceProfile:                                                                           #(D)
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref ECSRole

  FrontendECSCluster:                                                                           #(E)
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Sub sample-frontend-cluster-${EnvType}
      Tags:
        - Key: Name
          Value: !Sub FrontendECSCluster-${EnvType}

  // omit

  FrontendECSAutoScalingGroup:                                                                  #(F)
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
        - Fn::ImportValue: !Sub ${VPCName}-PublicSubnet1
        - Fn::ImportValue: !Sub ${VPCName}-PublicSubnet2
      LaunchConfigurationName: !Ref FrontendECSLaunchConfiguration
      MinSize: '0'
      MaxSize: !FindInMap [FrontendClusterDefinitionMap, !Ref EnvType, EC2InstanceMaxSizeOfECS] #(G)
      DesiredCapacity: !FindInMap [FrontendClusterDefinitionMap, !Ref EnvType, DesiredCapacity]
      Tags:
        - Key: Name
          Value: !Sub FrontendECSCluster-${EnvType}
          PropagateAtLaunch: true
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M
    UpdatePolicy:
      AutoScalingReplacingUpdate:
        WillReplace: true

  FrontendECSLaunchConfiguration:                                                                #(H)
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !Ref ECSAMI
      InstanceType: !FindInMap [FrontendClusterDefinitionMap, !Ref EnvType, InstanceType]
      IamInstanceProfile: !Ref IamInstanceProfile
      KeyName: !FindInMap [FrontendClusterDefinitionMap, !Ref EnvType, KeyPairName]
      SecurityGroups:
        - Fn::ImportValue: !Sub ${VPCName}-SecurityGroupFrontendEcsCluster
      AssociatePublicIpAddress: true
      UserData:                                                                                  #(I)
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          echo ECS_CLUSTER=${FrontendECSCluster} >> /etc/ecs/ecs.config
          yum install -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource FrontendECSAutoScalingGroup --region ${AWS::Region}

// omit

ECSクラスタのテンプレートの記述の基本となるポイントは下表の通りです。

記述 説明
A 起動するECSのAmazon Linux AMIをSystems Manager Parameter Store経由で取得し、パラメータとして定義します
B パラメータEnvTypeに応じて、適用するパラメータ値を変更するよう、Mappings要素を定義します
C ECSクラスタの実行に必要なAmazonEC2ContainerServiceforEC2Roleポリシーが付与されたIAMロールを定義します。詳細はAWS::IAM::Roleを参照してください
D CのIAMロールが付与されたインスタンスプロファイルを定義します。詳細は  AWS::IAM::InstanceProfileを参照してください。
E ECSクラスタを定義します。詳細はAWS::ECS::Clusterを参照してください
F クラスタのオートスケーリンググループを定義します。詳細はAWS::AutoScaling::AutoScalingGroupを参照してください
G FindInMap関数を用いて、作成する環境に応じてパラメータを切り替えます
H ECSクラスタの起動オプション設定を定義します。詳細はAWS::AutoScaling::LaunchConfigurationを参照してください
I クラスタ起動に実行する起動スクリプトを定義します。ecs.configへのクラスタ定義の追加や、aws-cfn-bootstrapのインストール、cfn-signalの実行がECSクラスタの起動に必要になります

なお、プライベートサブネットにECSクラスタを構築する場合は、起動スクリプト内のyumコマンドを実行するため、NATGatewayを設置して、ルートテーブルをプライベートサブネットに関連付けておく必要があります。上記ではNATGatewayの設定をテンプレート記述に含めていませんが、本連載の第27回を参考にNATGatewayを事前に設定しておいてください。

ヘルパースクリプトの実行

作成したテンプレートに対し、以下のように、スタック名とテンプレートパスを変更してヘルパースクリプトを実行します。

#!/usr/bin/env bash

stack_name="mynavi-sample-ecs-cluster"
template_path="sample-ecs-cluster-cfn.yml"

parameters="EnvType=Dev"

aws cloudformation deploy --stack-name ${stack_name} --template-file ${template_path} --parameter-overrides ${parameters} --capabilities CAPABILITY_IAM

実行が正常に終了すると、ECSクラスタが作成されます。

以上、今回はECSクラスタを構築するCloudFormationテンプレートを実装しました。次回は、ECSタスク定義を行うCloudFormationテンプレートを作成する手順を紹介します。

著者紹介


川畑 光平(KAWABATA Kohei) - NTTデータ

金融機関システム業務アプリケーション開発・システム基盤担当、ソフトウェア開発自動化関連の研究開発を経て、デジタル技術関連の研究開発・推進に従事。

Red Hat Certified Engineer、Pivotal Certified Spring Professional、AWS Certified Solutions Architect Professional等の資格を持ち、アプリケーション基盤・クラウドなど様々な開発プロジェクト支援にも携わる。AWS Top Engineers & Ambassadors選出。

本連載の内容に対するご意見・ご質問は Facebook まで。