AWS ECSのハンズオンやってみた

## はじめに
案件などでECSを使うことは多々あったのでコンテナって便利な概念だけど、インフラとして構成することができてもよく考えたらコンテナの中身ってどうなってるかよくわからない気がする。。。
ということで AWS が提供している ECS ハンズオンをやってみました。

https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-ECS-2022-reg-event.html

今回実施したハンズオンの流れとしては、以下のようになっています。
1. 準備
1. コンテナイメージ作成
1. ECSの作成
1. 自動復旧確認
1. リソース削除
※この記事では公開されているハンズオンと順番を入れ替えたり、省いて実施している部分があります。


おまけ(なんなら本編)として、2/5に公開されCloudFormationで管理外のリソースをCloudFormationのスタックとして取り込める機能が気になっていたのでついでに試してみました。良かったら最後までお読みください。

https://aws.amazon.com/about-aws/whats-new/2024/02/aws-cloudformation-templates-cdk-apps-minutes/


## 準備
準備としてCloud9の環境を整備します。
最初にCloud9用のインスタンスを起動するためのVPCを作成します。

設定値は画像の通りです。
今回は2AZのパブリックサブネットのみ作成します。
ハンズオンのためパブリックサブネットのみ作成しますが、実案件では**セキュリティ**を考慮して作成ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/7b1e1719-7f5c-a8c9-00c3-77913940580a.png)

:::note info
知らない内にできるようになっていた機能ですが、VPCの作成時にサブネットやNATゲートウェイなど関連リソースをまとめて作れるようになったのはとても嬉しいですね！
:::

VPCができたので、Cloud9を起動させます。
設定値は基本的にデフォルトです。
VPCは先程作成したものを選択しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/387adecd-c1fb-02b4-12bb-4221988786ca.png)
ステータスが準備完了になったらOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/902ecc06-e995-e9f0-2cf2-efc7804d706c.png)

:::note warn
注意点：Cloud9が起動するサブネットについて
* プライベートサブネットの場合は、NATゲートウェイへのルートがある
* パブリックサブネットの場合は、パブリックIPの自動割り当てが有効になっている
:::

https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/vpc-settings.html

私はこれを知らずに、Cloud9の起動に1度失敗しました。
ちなみにCloud9のネットワーク設定にはちゃんと記載があります。
~~ちゃんと読んでない証拠ですね~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/fdf375b2-c85f-3c55-5d97-10619c4eaba8.png)

## コンテナイメージ作成
準備ができたのでCloud9の環境に入ります。
接続したら最初に以下のコマンドでdockerが使えることを確認します。
```
docker version
```
dockerが使えることを確認したら、Dockerfileを作成します。
内容はハンズオンからコピペしたものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/399f7b75-efa4-8e48-8385-9510d0b4ebd1.png)
dockerについてあまり詳しくないので、以下の記事を参考にすると
* FROM：コンテナのベースイメージの指定
* RUN：Dockerイメージのビルド時に実行すること
* EXPOSE：コンテナがどのポートをlistenするか
* CMD：コンテナ起動時に実行する内容

https://zenn.dev/ama_c/articles/d40305757fea98

なので、今回ハンズオンで作成した内容を簡単にまとめると上から
* ubuntuで動くよ
* Dockerイメージをビルドする時にapacheをインストールして、apacheの設定をするよ
* ポートは80でよろしく
* コンテナ実行時にapacheを起動してね

です。

Dockerfileを作成できたらコンテナイメージとしてbuildします。
```
docker images
docker build -t {コンテナイメージ名}
docker images
```
コマンドの実行結果がこちらです。
最初コンテナイメージの一覧を確認した時は何も無いですが、buildした後にhello-worldの名前でイメージができています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/1ae65c94-5c7b-e5db-a6fb-df0dcca3d25d.png)

コンテナイメージが作成できたらCloud9上で動かしてみます。
```
docker ps
docker run -d -p 8080:80 --name {起動コンテナ名} {起動させたいコンテナイメージ名}
docker ps
```
コマンドの実行結果がこちらです。
先ほど作成したコンテナイメージを使って起動できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/b333c47c-61fd-3cfc-c69e-ed4e43425237.png)
localhostに接続してみるとちゃんと"hello world!"が返ってきます。

無事にコンテナが起動することを確認できたので、ECSで使用できるようにECRにプッシュします。
一度Cloud9の環境から離れ、ECRにリポジトリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/4777af35-3aae-c79b-e9c1-d86c07bb7e42.png)
リポジトリを作成したらCloud9に戻り、ECRにイメージをプッシュします。
```
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin {ECRのURI}
docker push {ECRのURI}/doi-handson-ecr:latest
```
コマンドの実行結果がこちらです。
認証トークンを取得し、ECRのリポジトリにコンテナイメージをプッシュしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/19878e4e-f91a-9316-8687-1e534af08895.png)

:::note info
ちなみにECRへのプッシュ用のコマンドはECRのコンソールから確認できます。
リポジトリを選択→プッシュコマンドの表示
:::

ECRにイメージをプッシュできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/3096187e-8622-d737-a35e-db141d632dee.png)

## ECSの作成
ECRにイメージをプッシュできたので、ECSを作成していきます。

準備としてセキュリティグループ(以下SG)を作成します。
必要なSGはALB用とECS用になります。
・ALB用のSG
HTTPのマイIPのみ許可
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/25dc9e08-2b90-c04f-28d1-e464c27fcddc.png)
・ECSのSG
HTTPのALBのSGのみ許可
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/dfb755e0-f530-0365-7e2e-1c70236732e6.png)
※ハンズオンではALBとECSに同一のSGを設定しています。
※ハンズオンでは後の復旧確認でCloud9から一定間隔でリクエストを投げるため、Cloud9のSGからの通信も許可していますが、復旧確認は省いたため今回は設定していません。

次にECSクラスターを作成します。
クラスターはタスクとサービスの論理グループで一番大枠にあたるものです。
設定は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/b4ebeb83-39a4-253d-de06-c864ccc3af36.png)

次にタスク定義を作成します。
タスク定義は名前の通りタスクをどのように起動させるかどのコンテナイメージを使うのかなどを定義するものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/a8c4d148-6a26-26c6-e81c-a6488ba28fa5.png)

次にサービスを作成します。
サービスは起動しているタスクを管理してくれるもので、何かしらの原因でタスクが落ちてしまった時に別のタスクを立ち上げてくれたり、負荷に合わせてタスクを増減してくれます。
ここで新規のALBとターゲットグループも作成することができます。(←これ便利)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/3f6ca495-be9e-6970-0b48-7c7332c6a02d.png)

サービスが起動したら、ALBのDNS名にアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/f79cbeae-518a-16e8-411f-cdffa74c6bbb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/94f0f48c-528f-30f0-de0a-1a9163773a23.png)
無事Hello World!が表示されました。

## IaCジェネレーター
ようやくリソースが完成したので本編です笑
気になっていたIaCジェネレーターを使っていきます。
IaCジェネレーターはCloudFormationのページのここにあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/5b0306ee-f3c8-f13a-58f2-de660b319e53.png)
使い方はページに書いてある通りですが、

:::note info
ステップ 1. アカウントのリソースをスキャンする
ステップ 2. CloudFormation テンプレートを作成する
ステップ 3: CloudFormation または CDK にインポートする
:::
IaCジェネレーターを開きスキャンのブロックの右上にある「新しいスキャンを開始」を押します。
ボタンを押すとスキャンが始まり、しばらく待っているとスキャンステータスが完了になります。
スキャンステータスが完了になったら「テンプレートを作成」からテンプレートを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/d7831eb6-275a-696f-971a-e36b7daa67a3.png)
最初のページでテンプレート名と削除ポリシー、置換ポリシーを決めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/b4f122ba-201c-75ae-00f4-9dc21622a27a.png)
次に取り込むリソースをスキャンしたリソースから選択していきます。
慣れないとリソースの検索がしにくかったですが、選択できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/5b7fd16b-07a1-afb2-96ef-b89adba79acc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/3eca359a-2aea-61c3-67cb-159d06c75817.png)

次に行くと選択したリソースの関連リソースを選択してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/f9297817-9e2d-bd26-156b-4ba5c0bea4e7.png)

最後に確認画面でテンプレートを作成すると簡単にテンプレートができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/23eebf52-5bed-f2a6-2ff0-e7f0ff1eea47.png)

このままスタックにインポートを押すと見慣れたCloudFormationのインポートの画面に遷移しました。
あとはいつもの流れでスタックにインポートができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1185845/56f6b7c1-b051-2ac6-b862-1d249d835535.png)

:::note warn
注意点
* 作成されたテンプレートは変数化されていない
→同じコードを使用して横展開する場合は注意が必要です。
* 既にスタック管理されているリソースは取り込めない
→今回取り込むリソースとしてハンズオンで作成したリソースでしたが、ECSのサービスはECSコンソールから作成しました。実はその時にもCloudFormationのスタックが生成されています。そのためECSのサービスや合わせて作成したALBなどは既にスタック管理されたリソースとなり、今回のテンプレートに取り込むことができませんでした。
→新規のテンプレートには既存のスタックで管理されているリソースは取り込めないので、ECSサービスも合わせたテンプレートを作成する時は、最初のページで「既存のスタックのテンプレートを更新」にチェックを付けて、ECSサービスを作成した時にできたスタックを選択すると良いです。
:::

## おわりに
今回は発表されてから試してみたいと思っていたCloudFormationのIaCジェネレータを使ってみました。
生成されたものをそのまま使うことはできないですが、初版のテンプレートを作成してもらえるのはとてもありがたい機能だと思いました。

そして忘れてはいけないECSハンズオンで少しはコンテナと仲良くなれた気がしました。
ECSを触ってみたい人やコンテナってなんだ？って人も同じハンズオンをやってみてはいかがでしょうか？