🎉

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