🎉

AWSのECS環境を構築してみる

2022/02/20に公開

概要

AWSにてコンテナ環境(ECS)環境を構築してみます。
ECSにはEC2タイプとFargateがありますが、ここではまずEC2タイプで構築します。
 ※Fargateにする場合、設定・管理する必要がある内容が減りますが、まずEC2タイプで理解を深めます。

また、キャパシティプロバイダーやスポットインスタンスといった機能を使用することで、価格を抑えながら柔軟にスケールするといった機能がありますが、今回は基本の学習のため使用しません。

手順

コンテナリポジトリ(ECR)を作成する

まず、コンテナイメージを格納するためのECRを作成します。

  1. Elastic Container Service-リポジトリを開きます。
  2. リポジトリを作成を押下し、設定を行います。
    1. 可視性設定はプライベートのままとします。
    2. リポジトリ名に任意の名前(例:samplerepo)を設定します。
    3. リポジトリを作成を押下します。

プッシュコマンドを確認する

AWSコンソールから以下の方法でプッシュコマンドを確認します。

  1. 作成したリポジトリを選択します。
  2. プッシュコマンドの表示を押下します。

以降、記載のコマンドに沿って操作していきます。

ECRにログインする

PC環境からAWS CLIを用いて、以下のコマンドを実行しdockerでECRにログインします。(dockerコマンド側がAWS ECRに対して、ログイン状態になります。)

shell
aws ecr get-login-password | docker login --username AWS --password-stdin https://<aws_account_id>.dkr.ecr.<region>.amazonaws.com

ローカルでDocker環境を作る

次のような内容で、Dockerの簡単な環境を作ります。

フォルダ構成
.
├ src
│ └ index.html
└ Dockerfile

HTMLページを用意する

index.htmlファイル内容
<h1>Hello World!</h1>

Dockerfileを作成する

Dockerfileファイル内容
FROM 'nginx:latest'
RUN service nginx start
COPY src /usr/share/nginx/html
VOLUME /usr/share/nginx/html

Docker イメージを構築する

shell
docker build -t samplerepo .

リポジトリにイメージをプッシュできるようタグをつける

shell
docker tag samplerepo:latest <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/samplerepo:latest

プッシュを実行する

shell
docker push <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/samplerepo:latest

ECRにプッシュされたことを確認する

AWSコンソールから対象のリポジトリを選択し、イメージがプッシュされたことを確認します。

補足:コマンドラインで、リポジトリの一覧を確認する。

shell
aws ecr describe-repositories --region ap-northeast-1

補足:ECRに格納されたイメージをPULLする

shell
docker pull <aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/samplerepo:latest

ECSクラスターを作成する

ECSクラスターは、コンテナを配置するマシンリソースプールの設定になります。

  1. AWSコンソールで、ECS-クラスター-クラスターの作成を押下します。
  2. EC2 Linux + ネットワーキングを選択し、次のステップを押下します。
  3. クラスター名を任意の名前で設定します。(例:cluster01)
  4. EC2 インスタンスタイプを選択します。ここでは、今回は動作確認用なので、手動入力としてt2.smallを選択します。
  5. インスタンス数2に設定します。
  6. キーペアは既存のものを選択します。(設定しない場合、直接EC2に入って調査ができなくなります)
  7. ECSを配置するVPCとサブネットを設定します。ここでは既存のVPCと既存のサブネット(-1a、-1c)を選択します。
  8. Tagsを設定します(例:キーNameECSInstance01
  9. 作成ボタンを押下します。しばらく待つと完了します。
  10. クラスターの表示を押下します。
  11. ECSインスタンスタブを選択し、インスタンスが起動していることを確認します。

タスク定義を設定する

タスクは、ECS上に配置される、コンテナの設計図になります。

  1. AWSコンソールで、ECS-タスク定義-新しいタスクの作成を押下します。
  2. 起動タイプの互換性の設定で、EC2を選択し次のステップを押下します。
  3. タスクとコンテナ定義の設定を行います。
    1. タスク定義名:任意の名前を設定します。(例:testtask01)
    2. タスクロール:他のAWSリソースにアクセスする権限(例:RDS)を付与したロールを選択します。※実際には、あらかじめロールを作っておく必要があります。今回はなしにします。
    3. ネットワークモードbridgehostawsvpcなしから選択できます。今回はbridgeを選択します。
    4. タスクメモリ:タスクに割り当てるメモリを設定します。(例:256
    5. タスクCPU:タスクに割り当てるCPUを設定します。(例:0.5 vcpu
    6. コンテナの追加を押下し、コンテナの設定を行います。
      1. コンテナ名:任意のコンテナ名を設定します。(例:container01
      2. イメージ:ECRのリポジトリのURLを設定します。(例:<aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/samplerepo
      3. メモリ制限:使用するメモリの制限を設定します。これはdocker run--memory--memory-reservationに対応しています。ハード制限の値を超えるとプロセスが強制終了されます。ソフト制限はコンテナ用にメモリが予約されます。ハード制限>ソフト制限である必要があります。タスクレベルで設定を行っている場合は、オプションになります。(もう少し調べる必要がありますが、タスクレベルかコンテナレベルかどちらかの設定に寄せる形になるのかと思います)今回は設定なしにしておきます。
      4. ポートマッピング:ホスト側のポートとコンテナ側のポートを紐づけます。今回は、8080を設定しておきます。
      5. ヘルスチェックdocker run--health-cmdに対応しています。例えば、特定のwebページが正常か一定間隔で確認する際に使用します。今回は設定なしとします。
      6. 環境 CPUユニット数docker run--cpusに対応しています。
      7. エントリポイントコマンド作業ディレクトリ:Dockerfileの定義を上書きする場合設定できます。今回は設定なしにしておきます。
      8. 環境変数docker run-eあるいは--envに対応しています。今回は設定なしにしておきます。
      9. コンテナタイムアウト:1つのタスク定義に複数のコンテナを設定している場合、コンテナ間の起動順序に依存関係がある場合、起動順序を設定できます。今回は設定なしにしておきます。
      10. ネットワーク設定 リンクdocker run--linkに対応しています。
      11. ネットワーク設定 ホスト名docker run--hostnameに対応しています。
      12. ネットワーク設定 DNSdocker run--dnsに対応しています。
      13. ログ設定:チェックを入れることでCloudWatchLogsに自動出力できます。
      14. セキュリティ 特権付与:チェックを入れるとコンテナにrootの特権が与えられる。
      15. セキュリティ ユーザー:コンテナ内で使用するユーザを設定できる。
  4. 制約:タスクの配置戦略について設定します。配置戦略とは、AMIの種類やAZなど特定の条件のインスタンスでフィルタして、そのタスクを配置するといった制御を行うことができます。今回は設定なしにしておきます。
  5. 作成を押下し、タスク定義の設定を完了します。

備考:ネットワークモード

設定項目 説明
bridge コンテナ内外で任意のポート番号で紐づける。そのためコンテナ側のポート番号が重複してもマッピングして使用できるが外部から使用できるポート番号は重複できない。
host コンテナ内外で同じポート番号で待ち受ける。1つのホストで同じポートで待ち受けることはできない。
awsvpc EC2のeni(ネットワークインターフェース)を複数利用して、タスクごとにeniを割り当て、外部からの待ち受けポートも重複することができる。※インスタンスタイプによって使えるeniの数に制限がある。
なし 外部に接続できない。

タスクを実行する

  1. 対象のタスクを選択し、アクションからタスクの実行を選択して実行します。
  2. 起動タイプ:EC2を選択します。
  3. クラスター:起動対象のクラスターを選択します。ここでは、事前に作成したcluster01を選択します。
  4. タスクの数:起動するタスクの数を設定します。1を設定します。
  5. タスクグループ:任意のグループ名を設定します。設定なしにします。(例:ecsgroup01
  6. タスクの実行を押下します。
  7. タスクが正常に作成されましたが表示されればOKです。

ECSの比較的新しい機能であるキャパシティプロバイダーを用いて、より柔軟にコンピューティングリソースの割り当てができるようですが、今回は使用せずそのまま実行します。(いずれやってみます)

また、タスク実行時に、タスク定義で設定したタスクロールやコマンド、環境設定などの上書きをしての起動ができるようになっています。(今回は使用しません)

動作確認:ボリュームマウントの設定(タスク定義の更新)

続いて、EC2側のフォルダとコンテナ内のフォルダをマウントで接続してファイルをを操作できるようにしてみます。
ボリュームのマウントの設定をして、更新を反映していきます。
設定を更新する場合は、タスク定義を更新して新しいリビジョンの定義を作成し、それを実行することで反映できます。

  1. タスク定義-タスク定義名:Revionを選択して、新しいリビジョンの作成を押下します。
  2. ボリュームからボリュームの追加を選択します。
    1. 名前:任意の名前を設定します。(例:testvolume
    2. ボリュームタイプをDockerに設定します。
    3. 追加を押下します。
  3. 一旦、タスク定義(親画面)の作成ボタンを押下しリビジョンを作成します。(ボリューム作成後、一度リビジョンを作成しないと、ボリュームを使用したマウント設定が反映できないためです)
  4. 再度、新しいリビジョンの作成を押下します。
  5. コンテナの定義から設定済みのコンテナ定義を選択します。
  6. ストレージとログの、マウントポイント-コンテナパスにマウントしたいポイントを定義します。(例:/usr/share/nginx/html/
  7. コンテナの定義の更新を押下します。
  8. タスク定義(親画面)の作成ボタンを押下します。
  9. タスク定義の新しいリビジョンが作成されます。
  10. 作成したりビジョンのアクションから、タスクの実行を選択します。
  11. 起動タイプEC2を選択します。
  12. タスクの実行を押下します。
  13. 起動タイプをEC2を選択して、タスクの実行を押下します。
  14. クラスターからタスクを選択し、古いリビジョンのタスクを選択して停止しておきます。
  15. コンテナインスタンスからパブリックIPを確認して、SSH接続します。(例:ssh -i key.pem ec2-user@[IPアドレス]
  16. ディレクトリを/var/lib/docker/volumes/に移動します。ここにecs-から始まるフォルダがあり、これが、ボリュームの追加で追加したボリュームになります。
  17. さらにそのフォルダ内に入っていくと、_dataディレクトリがあり、その下にindex.htmlファイルがあります。このファイルを更新すると、Webブラウザから表示される画面にも反映されることが分かります。

サービスの設定

サービスを設定することで、タスクの実行数を定義でき、タスクの起動や運用を自動化できます。
サービスとタスクは1:1の関係になります。
また、複数コンテナを立ち上げ、ロードバランサ(ALB)でバランシングするため、固定のポート番号ではなく、動的に割り当てられるポート番号を使用できるようにポートマッピングの設定を変更します。
80と設定していたホスト側のポート番号を0とすることで、一時的なポート番号が使用できるようになります。

タスク定義の変更

  1. タスク定義から作成したタスクを選択します。
  2. 最新リビジョンのタスクを選択し、新しいリビジョンのタスクを作成を押下します。
  3. コンテナの定義から対象のコンテナを選択し、ポートマッピングホストポート0に設定します。
  4. タスク定義(親画面)の作成ボタンを押下します。

ロードバランサの作成

  1. AWSコンソールからEC2-ロードバランシング-ロードバランサーを選択します。
  2. ロードバランサーの作成を押下します。
  3. Application Load Balancer作成を押下します。
  4. ロードバランサ名(Load balancer name)に任意の名前を設定します。(例:loadBalancer01
  5. アベイラビリティゾーンは、クラスタを作成したVPCサブネットに合わせて設定します。
  6. セキュリティグループは新たに作成します。
    1. セキュリティグループ名:任意の名前を設定します。(例:ecsALBSecurityGroup
    2. 80番ポートですべての通信0.0.0.0/0を受け付けるように設定します。
    3. 作成したセキュリティグループのみを紐づけます。
  7. ターゲットグループは新たに作成します。
    1. ターゲットタイプはinstancesのままで、80ポートが解放されていることを確認します。
    2. ターゲットグループ名(Target group name)に任意の名称を設定します(例:ecsALBTargetGroup)
    3. 次へ(Next)を押下します。
    4. ターゲットインスタンスは指定せずそのまま作成します。
    5. 作成したターゲットグループを、ロードバランサ作成画面側から紐づけます。
  8. ロードバランサの作成(Create load balancer)を実行します。
  9. ロードバランサが作成されたことを確認します。

ECS用のAutoScallingグループに設定されたセキュリティグループの変更

上記の通り、1サーバに複数コンテナ立ち上げた状態でうまくバランシングるために、コンテナは固定のポート番号ではなくランダムで割り振られたポート番号で待ち受けます。そのため、セキュリティグループが80しか許可していないと通信ができません。
そのため、ECSインスタンスが属するセキュリティグループ(AutoScallingグループに適用されたセキュリティグループ)にて、インバウンドですべてのTCP通信を、ALBのセキュリティグループからのみ受け付けるように変更します。

  1. クラスター-ECSインスタンスからいずれかのECSインスタンスを選択します。
  2. そのインスタンス(EC2)に適用されているセキュリティグループを確認し、セキュリティグループ更新に進みます。
  3. インバウンドルール設定で、すべてのTCPで、ソースを、セキュリティグループの中から、ALB用のセキュリティグループ(例:ecsALBSecurityGroup)を選択して許可します。

サービスの作成

  1. 対象のクラスターのサービスから作成を押下します。
  2. 起動タイプはEC2を選択します。
  3. タスク定義は事前に作成した最新リビジョン(latest)の定義を参照します。
  4. サービス名を任意の名前で設定します。(例:service01
  5. タスクの数を設定します。今回は4を設定します。
  6. 次のステップを押下します。
  7. ロードバランサの種類Application Load Balancerを選択します。
  8. サービス用の IAM ロールの選択であらかじめ用意されているAWSServiceRoleForECSを選択します。
  9. ロードバランサー名で先ほど作成したロードバランサーを選択します。(例:loadBalancer01
  10. ロードバランス用のコンテナで、対象のコンテナ(例:container01)を選択し、ロードバランサに追加を押下します。
  11. プロダクションリスナーポート80:HTTPを選択します。(ロードバランサ側の既存リスナーポートから選択するか、新たに作成できます)
  12. ターゲットグループ名で先ほど作成したターゲットグループecsALBTargetGroupを選択します。
  13. 次のステップを押下します。
  14. AutoScalling画面に進みます。そのまま次のステップを押下します。
  15. 確認画面で、サービスの作成を押下します。
  16. サービスの表示を押下して、4つのタスクが起動したことを確認します。
  17. ロードバランサーの画面からDNS名を確認してブラウザでアクセスします。画面が表示されればOKです。

コンテナイメージを差し替える場合

  1. タスクを新しいリビジョンで作成し、コンテナの定義で対象コンテナのイメージを新しいリポジトリのパスに差し替えます。
  2. クラスターからサービスを開き、チェックを入れて更新を押下します。
  3. サービスの設定リビジョンlatestのリビジョンに変更します。
  4. 最小ヘルス率:サービスの更新中に稼働しているコンテナを減らさない場合は100とします。
  5. 最大率:サービスの更新中に最大で入れ替え前のコンテナ、入れ替え後のコンテナを完全に同時に稼働した状態(2倍の状態)とする場合には、200を設定します。

サービスのAutoScallingを設定する

  1. 対象のクラスターを選択し、サービスの更新に進みます。
  2. 基本設定及びネットワーク構成は変えず、次へ進みます。
  3. AutoScalling(オプション)画面で、Service Auto Scaling の設定を変更することで、サービスの必要数を調整するを選択します。
  4. タスクの最小値を1、必要数を2、最大数を4と設定します。
  5. スケーリングポリシーの追加を押下します。
  6. スケーリングポリシータイプターゲットの追跡にします。(ターゲットの追跡は規定した値に合わせるように上下します。ステップスケーリングはメトリクスの閾値に応じてスケーリングします。)
  7. ポリシー名を任意の名前に設定します。(例:tracking
  8. ECSサービスメトリクスECSServiceAverageCPUUtilizationを選択します。CPUの平均使用率を使用します。
  9. ターゲット値に基準とする値を設定します。(例:1と設定するとCPU使用率が1%になるように調整する動きになります)今回は動きを見るために1を設定してみます。
  10. スケールアウトクールダウン間隔スケールインクールダウン間隔にスケーリングアクション毎の間隔を定義します。今回はすぐ動きが分かるように10を設定します。
  11. 次のステップを押下します。
  12. サービスの更新を押下します。

補足:ステップスケーリングについて

ステップスケーリングを使用すると、メモリの使用率などの閾値でアラームを設定して、その条件を満たしたときにコンテナ数を増減させられます。

補足:EC2側のAutoScalling設定について

EC2インスタンス数のスケーリングに関しては、EC2のAutoScallingグループ側の設定が必要になります。AutoScallingを設定する際には、アラームで閾値を設定しますが、その際に、ECSクラスターのReservation系のメトリクス(CPUやメモリをどの程度予約しているか)でスケーリングを行うことができます(例:メモリを70%以上予約したらEC2の台数を増やす、など)

補足:インスタンス数を手動スケールする場合

紐づけられているAutoScallingグループの設定に移動し、グループの詳細-編集希望する容量および最大キャパシティを増やすことで増加させることができます。

Discussion