こんにちは!株式会社アイスリーデザイン エンジニアリング部の中島です。
前回は実際のプロジェクトのAWS環境について記事を書きましたが、今回は個人的な勉強についての記事になります!
AWS初心者の方や、何となく知識はあるけどあまり手を動かしたことはない、という方に見ていただければ幸いです。
勉強から実践へ
AWSに関しては資格勉強などインプットばかりやってきた私ですが、2月から「ECS Delivery Certification」という認定を受けるためのチームで活動を始めました!
ECS Delivery Certificationとは?
Amazon ECS (Elastic Container Service) の導入や運用を支援するAWSパートナープログラムにおける認定です。この認定は、パートナーがECSに関する深い知識と経験を持ち、顧客に対してECSの導入や運用を成功裡に支援できることを示しています。
今までの知識を活かして頑張ろうと息巻いていた私ですが、認定取得に向けた活動中にあることに気付かされました。
「今まで勉強はそれなりにしてきたけど、実務に活かすには経験が足りなすぎる、、、」
どういうことかというと、認定取得に向けた活動では申請に必要なシートがあり、シートの各項目の問いに回答する必要があります。
しかし、このシートでは資格試験で問われないような実践的な問いが聞かれることがほとんどでした。
たとえば「なぜこのような設計を採用したのか」「各サービスにどのような設定を施しているのか」といった、実務での経験がなければ答えられない内容ばかりです。
一方、今まで勉強してきたSAAなどの資格試験自体は、ユースケースに応じて適切なサービスを選ぶための知識を問うものが多く、細かな設定値やコスト計算、運用方針の検討などはあまり出題されませんでした。
そのため、設計フェーズにおいては資格勉強で得た知識が一定の役割を果たすかもしれませんが、実際のコスト試算や開発する上での環境構築、運用設計といった領域では不十分だと痛感しました。
そしてコスト試算や環境構築、運用設計といった領域を学ぶにはとにかく実際に手を動かして学んでいくしかないです。
そこで今回は勉強だけやってきた初心者が実際にECSを触ってみる、という記事になります!
私と同じように実際に手を動かしたことがあまりない、という人はぜひ一緒にやってみてください!
ハンズオンの概要
早速ハンズオンをやろうと思い公式のハンズオンを探したところ、Cloud9を使うことを前提としているものがほとんどでした。
しかしCloud9は2024年7月をもって新規利用終了が発表されました。
そんなわけで今回はCloud9を使わないこちらのハンズオンを参考に進めていきます。
始める前に軽くハンズオンを読んでみたところ、以下のような記述がありました。

今回は実際の業務に活かすために初心者がサービスに触れてみる、という目的です。
そのためVS Code Serverについては飛ばし、ローカル環境で開発します。
また、ハンズオンではCI/CDやFargate Spotについても記載されているのですが、この記事は上述の通り初心者向けのため、公式ハンズオン内の3章であるECSハンズオンまでやりたいと思います。
CI/CDなど、より実践的な内容については今後また別の記事で書くかもしれません。
その時はぜひそちらもご一読ください。
では始めていきたいと思います!
1. Dockerハンズオン
まずはDockerからです。
環境の確認からしていきます。
docker --version
Docker version 27.5.1, build 9f9e405
続いてハンズオン用のディレクトリを用意して、Dockerfileを作成します。
コンテナの元となるコンテナイメージの作成手順を記述したものが Dockerfile です。
必要なパッケージやアプリケーションを記述していきますが、今回は勉強用ということでRailsアプリケーションのコンテナイメージを作成します。
また、今回利用しているPublic ECRでは誰でもアクセスできるコンテナイメージが共有されています。
Railsアプリケーション以外も使ってみたいという方はぜひいろんなコンテナイメージを試してみてください。
# ruby:3.2.1 というベースイメージを取得する
FROM public.ecr.aws/docker/library/ruby:3.2.1
# 必要なパッケージ群を取得する
RUN apt-get update -qq && \
apt-get install -y nodejs postgresql-client npm && \
rm -rf /var/lib/apt/lists/\*
# ローカルにあるファイルをコンテナイメージ内にコピーする
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
# Rails アプリケーションを作成する
RUN bundle install && \
rails new . -O && \
sed -i -e "52a\ config.hosts.clear\n config.web_console.allowed_ips = '0.0.0.0/0'\n config.action_dispatch.default_headers.delete('X-Frame-Options')" config/environments/development.rb
# Rails を 3000 番ポートで起動する
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]
次にGemfileを作成します。
Rubyアプリケーションで利用するgem(ライブラリ)の一覧を記述したものがGemfileです。
source 'https://rubygems.org'
gem 'rails', '7.2.2'
コンテナイメージをビルドする準備が完了したので、ビルドしていきます。
ビルドが完了したら完成したコンテナイメージを確認しましょう。
docker build -t rails-app .
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
rails-app latest 21269d607313 32 seconds ago 1.35GB
ここで一つ注意点があります。
上記のコマンドで一つ注意なんですが、アーキテクチャがホストに依存するようです。
後ほどタスク定義でアーキテクチャを選択するのですが、基本的にデフォルトのLINUX/X86_64を選択します。
しかしApple Silicon MACの場合はビルドした結果、LINUX/ARM64のイメージをビルドしてしまいます。
そのためLINUX/X86_64のイメージをビルドするように下記のコマンドを使用してください。
docker buildx build --platform=linux/amd64 -t app --load .
それではコンテナを実行してみましょう。
このコマンドでは、ホストの8081番ポートを、コンテナの3000番ポートにマッピングしています。
コンテナの 3000 番ポートでアプリケーションを実行するようにDockerfileを記述したので、ホストの8081番ポートにアクセスすることでRailsアプリケーションにアクセスできます。
docker run -d -p 8081:3000 rails-app:latest
もし実行した際に `〜 address already in use` といったエラーが出た際はすでにポート番号を使用しているということなので、ポートを解放する、あるいは8082番ポートなど別のホストポートを使いましょう。
無事にRailsアプリケーションが表示できました!
私はプロジェクトで8081番ポートを使用していたので、8082番ポートを使いました。

2. ECSハンズオン
本題のECSに入っていきます。
そもそもECSについてあまり詳しくないと言う方は、先にこちらの記事を読むことをおすすめします。
> アプリ制作のオーケストラの指揮者?Amazon Elastic Container Service (Amazon ECS) のメリットとデメリットとは?
2.1 VPCとサブネットの作成
手順に沿ってVPCとパブリックサブネットを作成していきます。
今回は簡単に体験するためにインターネットから直接アクセスできるパブリックサブネットを使用します。
実際の環境ではデータベースサーバーやインターネットから直接アクセスする必要がないアプリケーションサーバーはプライベートサブネットに配置するように注意しましょう。

まず名前を変更し、プライベートサブネットの数を0に変更します。


あとはデフォルトのまま「VPCを作成」をクリックして、リソースを作成しましょう。
2.2 クラスターの作成
手順に沿ってECSクラスターを作成していきます。
画面右の「クラスターの作成」を選択し、クラスター名を入力します。
続いてクラスターの実行環境の設定です。
今回のハンズオンではFargateとEC2のどちらも使用するため、インフラストラクチャの設定でFargateとEC2のどちらも選択します。
EC2インスタンスタイプはt3.large、必要な容量(最小/最大)は1/1を選択したらあとはデフォルトで大丈夫です。

Fargateについてはこちらの記事で説明されています。EC2との違いについても記載されているので、ぜひ読んでみてください。
次にクラスターのネットワークを設定します。
先ほど作成したVPCを選択し、パブリックIPの自動割り当てをオンにします。

ここまでできたら作成をクリックで完了ですが、以下の2点にご注意ください。


1分ほど待つとクラスターが作成されます!
クラスターの作成に成功したら次へ進みましょう。
2.3 コンテナイメージの保存
リポジトリを作成していきます。

ハンズオンでは名前だけ変更して完了なんですが、イメージタグのミュータビリティとイメージのスキャン設定について警告が出ていますね。
この2つについては後ほど触れていきたいと思います。
続いて、先ほど作成したコンテナイメージをリポジトリにpushします。
リポジトリの詳細画面に移動すると、「プッシュコマンドの表示」からコンテナイメージをリポジトリにpushするためのコマンドが表示されます。
コマンドは各々違うため、自分で表示したものを使用するように注意してください。

コマンドを実行してpushできました!

では、リポジトリ作成時に触れなかった「イメージタグのミュータビリティ」と「イメージのスキャン設定」について見ていきましょう。
まずイメージタグのミュータビリティについてです。
画像にある通り、イミュータブルとはイメージタグを上書きできるか否かの設定です。
そのため、ミュータブルにしておくと悪意のある変更を受ける、固定のイメージタグを使用できることによってバージョンを戻せなくなるといったデメリットがあります。
ハンズオンではミュータブルですが、実際に開発する際は気をつけるべきでしょう。
こちらの記事が参考になりますので、気になる方はぜひご覧ください。
> ECRはイミュータブルにしておくと安全
次に、イメージのスキャン設定についてです。
そもそもイメージのスキャンとは、コンテナイメージへの脆弱性の混入を特定してくれる、というものです。
設定する場合は警告に出ていた通り、レジストリの設定から行うことができます。
リポジトリを作成すると、こちらの設定が適用されるようですね。

詳細についてはこちらの記事が参考になります。
> ECRのイメージスキャンはどちらのタイプを選ぶべきなのか
2.4 タスク定義の作成
ECSコンソールの「タスク定義」からタスク定義を作成していきます。
まずはタスク定義ファミリーを入力し、インフラストラクチャの要件を設定します。
起動タイプはFargateとEC2のどちらも選択、タスクサイズ(CPU/メモリ)は.25vCPU/.5GB、タスクロールはなし、あとはデフォルトで大丈夫です。

これらの要件は本記事を書くきっかけでもある、資格勉強でわからない部分を勉強することに大いに関係してきます。
特にタスクサイズはどれくらいの負荷を想定するか、予算はどれくらいまで許容できるかなど実務において非常に重要な項目です。
他にも各パラメータの説明は以下の記事が参考になります!
> 【初心者向け】Amazon ECSのパラメータ一覧
続いて、実行するコンテナについて設定していきます。
イメージURIは先ほど作成したコンテナイメージのイメージURIをコピーしてきます。
そして「ログ収集の使用」のチェックを外して、タスク定義の作成は完了です。

次に、ALB用のセキュリティグループを作成します。
送信元に関しては警告の通り、必要に応じて許可するIPアドレスを変更してもいいかもしれません。
宛先はそのままにするようにハンズオンにあるので、警告が出ていても変更しないよう注意してください。


これでいよいよコンテナを動かす準備ができました!
2.5 EC2へのデプロイ
まず、サービスを作成していきます。
デフォルトのままだとEC2が選択されており、クラスター生成時にデフォルトで作成された新しいAuto Scalingがキャパシティプロバイダーとして指定されます。
これでAuto Scalingグループによって起動されたEC2でECSタスクが動きます。
ただしAuto Scalingの最小/最大容量を1としたため、実際にはスケーリングが行われないです。

続いて、サービスのデプロイ設定をします。
私が実践した時には公式の画像と項目の配置が違ったので、注意してください。
具体的には一部の項目が分割されてデプロイ設定の上に来ていました。
▼公式の画面

▼私が実践した時の画面


次に、サービスを設定するネットワークを設定します。
defaultのセキュリティグループに加えて、先ほど作成したセキュリティグループを設定するようにしてください。

最後に、タスクへのリクエストを受け付けるALBの設定をします。

デプロイされたアプリケーションの動作を確認しましょう。
ALBのDNS名をコピーしてブラウザで貼り付けます。
無事にRailsアプリケーションが表示されました!

タスクを増やすとどうなるでしょう、という問題が最後に書いてありました。
結果としては現在の設定では3つまでしかENIをアタッチできず、プライマリネットワークインターフェースも1つとしてカウントされるため、2つまでしかタスクを実行できない、ということでした。

公式ページによるとサポートされているインスタンスタイプを使用し、awsvpcTrunkingアカウント設定を有効にすると、新しく起動されたコンテナインスタンスで追加のENIを利用でき、各コンテナインスタンスにより多くのタスクを配置できるそうです。
詳細はこちらの公式ページをご覧ください。
> Amazon ECS Linux コンテナインスタンスのネットワークインターフェイスを増やす
2.6 Fargateへのデプロイ
今度はFargateへタスクをデプロイしていきます。
まずはサービスの作成からです。
キャパシティープロバイダーでFARGATEを選択すると、Fargateへタスクをデプロイできます。
サービス名、ロードバランサー名、ターゲットグループ名だけFargate用とわかるように付けて、他はEC2の時と同様に設定しましょう。

デプロイできたらブラウザからアクセスします。
無事に表示できました!

再びタスクを3つにするとどうなるでしょう、という問題が書いてありました。
サービスの設定で必要なタスクを3つにし、再度デプロイしてみます。
今度はタスクを3つ以上起動できました!
これがサーバーレスなFargateならではのメリットということですね。

2.7 オートスケーリングの設定
最後にオートスケーリングの設定です。
これまで説明したように現在の設定ではEC2用のサービスでタスクを3つ以上起動できないため、Fargate用のサービスの設定を更新します。


設定が終わったら今度はheyというツールを使ってリクエストを発生させます。
まずはインストールしていきます。
wget https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64
chmod +x hey_linux_amd64
sudo mv hey_linux_amd64 /usr/local/bin/hey
それでは実際にリクエストを発生させてみましょう。
FARGATE_ALB_NAME=ecs-fargate-alb
FARGATE_ALB_DNS=$(aws elbv2 describe-load-balancers --name ${FARGATE_ALB_NAME} --query "LoadBalancers[].DNSName" --output text)
hey -c 10 -z 10m http://${FARGATE_ALB_DNS}
タスク数が増加していることを確認できました!
他にもリクエスト数やスケーリングアクティビティ、CloudWatchのアラーム等からも負荷に応じて自動でスケーリングしていることを確認できます。

最後に
私はこのハンズオンを通じて、どこでどのような設定をして、結果として何をコントロールできるのか、ECSに対する理解が非常に深まったように思います。
皆様もハンズオンを実践し、同じような感想を持っていただけますと幸いです!
次回は公式ハンズオン内で発展として紹介されている、CI/CDハンズオンについて紹介できたらと考えています。
それではまた次の記事でお会いしましょう。
最後まで読んでいただきありがとうございました。
これまでの記事
📚 実際に使用するAWS環境ってどんな感じ?〜モバイルアプリ編〜
🗒️ 実際に使用するAWS環境ってどんな感じ?〜Webアプリ編〜