AWS超初心者がFargateを使ってみた

この記事は[ユニークビジョン株式会社 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/uniquevision)の記事です。

### 動機

「会社はFargate使っててそのデプロイをさせてもらったことはあるけど、もはや手順はすべてドキュメントに従ってるだけで何をやってるのか、何が起きているのかさっぱりだ...これはまずい！」

### この記事のレベル

- AWSって、あれでしょ？なんかウェブサービス公開できるやつ
- デプロイ？なんとなくわかるけど言われた（書かれた）通りにしかできないなあ...
- インフラとかネットワークとか、単語は聞いたことあるけど意味は知らん！（開き直り）

　当てはまった人は安心してください。おそらく自分がこのレベルです。
　インフラ?系は特に感じますが、「超」初心者向けの記事って意外と見つけづらいですよね。もしくは、部分的に手順解説はあるけどいろいろ端折ってて、始まりから終わりまで一つの記事で完結してないみたいなことは多いです。そんな方へ刺さるような記事になればと思います。

# はじめに

　「プログラミングの練習としてウェブアプリ作ったけど、せっかくだし公開してみたいなあ」と思ったことありませんか？世の中的には最初の一歩レベルだとしても、初めて作った本人としてはみんなにぜひ触ってもらいたくなりますよね。そういったときに、聞いたことがあれば「AWSを使えば公開できるのかな？」となるかもしれません。知識ゼロだから調べるかとなっても、見る記事はどれも知らない単語で溢れている...そんな経験はないですか？この記事では、AWSに関する前知識ゼロから始められるようになっています。ただ書いている本人も細かい設定なんかは知識ゼロなので、とりあえず公開できてから細かい部分の知識を習得しよう、というスタンスでここはひとつ。

# 0. 事前準備

　以下のCLIをインストールしておく必要があります。環境に応じて事前に行っておいてください。

- aws-cli
- docker

# 1. Dockerfileの用意

　今回の手順では、**Dockerイメージ**というものを使用します。Dockerって聞いたことはあるけどコンテナってなんぞや？という方は別の紹介記事でお調べください。仕組みについては特に触れません。またDockerイメージの作成ができる方は飛ばしてしまって構いません。

　まずDockerイメージを作成するにあたり、その元となるDockerファイルについて軽く説明します。
　Dockerファイルとは、「環境（OS＋テーマに合うコマンドがプリインストール済み）」と「環境構築（実行に必要なコマンドたち）」を書き連ねたものになります。例えば「Rustのアプリケーションをlinuxで動かすぞ」（環境）＋「projectがあって `cargo build --release` して...」といったものを一つのファイルに記述することで、AWS上でそれをもとに環境構築を行ってくれます。
　また、後でDockerイメージの作成やアップロードを行うために、それぞれの環境に応じてDockerのCLIをインストールしておく必要があります。

　それではファイルの作成手順になります。

1. [Docker Hub](https://hub.docker.com/)という、公式or独自のベースイメージ（環境）がまとめられているところから名前を探して指定します。例えばRustの環境であれば、Docker Hub上部の検索窓に「rust」と入力すると一番上にRust公式の提供しているコンテナが出てきます。選択しTagsに切り替えると、何種類か並んでいるのが確認できると思います。Dockerイメージができるだけ小さくなるように、slimなどと入っているものを選択するとよいです。このとき右に表示されている`docker pull XXX`の`XXX`の部分を後で使用します。
2. プロジェクトに戻り、`Dockerfile`という名前でファイルを作成します。このファイルを置く階層は、このとき環境に含めたいファイル群が現在のディレクトリ以下に全てあるような場所（現在ディレクトリから一つでも上に辿ることがない場所）にします。
3. ファイルの中にはDockerfile用のコマンドを並べていきます。
  - `FROM`：先ほどのDocker Hubで確認したコンテナを指定するコマンド。`FROM rust:1.48.0-slim`のように記述します。
  - `WORKDIR`：作業ディレクトリを設定します。`mkdir`と`cd`の合わせ技のようなコマンドです。`WORKDIR /app`のように記述します。
  - `COPY`：アプリの実行に必要なローカルのファイル群をDockerイメージの環境内にコピーします。`COPY (ローカルのディレクトリパス) (Dockerイメージの環境のコピー先パス)`のように記述します。似たようなコマンドで`ADD`というものがありますが、基本的にはこちらの方が良いらしいです。
  - `RUN`：一般的なコマンドを実行するコマンド。`RUN cargo build --release`のように記述します。起動するコマンドは後で別に指定するので、環境構築を完成させるためのコマンドを羅列します。ファイルサイズの小さいイメージをビルドするためにRUNコマンドの数は少ない方が良いそうです。

　最終的に以下のようなファイルを作成します。自分はRustで書いているので以下のような構成になります。

```dockerfile
# ベースイメージの選択
FROM rust:1.48.0-slim

# 作業ディレクトリの指定（`/app`は無いので作られる）
WORKDIR /app

# 必要なファイルをコピー
COPY . /app

# 依存関係のインストールや必要であればコンパイルなどを行う
RUN cargo build --release
RUN mkdir bin \
    cp target/release/web_app bin/web_app \
    cargo clean
```

# 2. AWSコンソールへサインイン

　まずAWSコンソールとはなんぞや、という方向けです。アカウントの作成までできている方は次の章へお進みください。

1. AWSコンソールとは、AWSの各種サービスをブラウザで管理できるようAmazonが提供しているものです。[こちら](https://console.aws.amazon.com/console/home)よりサインイン画面が開けます。
2. 開いたら「新しいAWSアカウントの作成」を押します。『AWS アカウントには 12 ヶ月の無料利用枠が含まれています』という文言から察する通り、本来はお金がかかるサービスです（まあ当たり前といえば当たり前ですが...）。しかし個人利用するだけなら十分すぎるくらいの無料枠が提供されています。Amazonの資金力に感謝👏
3. メールアドレス、パスワード、アカウント名と入力したら、さらに入力を求められます。アカウントの種類は「パーソナル」を選択し、必要項目を埋めていきましょう。
4. 最後にクレジットカード情報の入力が必要になります。最初は無料だと（ECSも無料枠に含まれていると）思い込んでおり、試行錯誤のために何度もデプロイしたり作成やら削除やらしまくっていましたが、それでも$0.12なのでほぼないに等しいです。
5. 無事に本人確認も終えたら、晴れてAWSコンソールにログインできるようになります。**S3**や**EC2**などよく言われている各種サービスは、画面上部のナビバー左部の「サービス」からすべて見ることができます。

　余談ですがAWSではリージョンというものがあり、世界のどの地域でサービスを提供したいかによって切り替えることができます。コストやレイテンシ、特定の地域限定のサービスを利用するなどで判断すると良いです。

# 3. AWSコマンド用のクレデンシャルの作成

　awsコマンドを実行するためには、クレデンシャル（アクセスキーとアクセスシークレット）をローカルに登録しておく必要があります。以下の手順で確認と登録を行います。

1. AWSコンソールへログイン後、画面上部ナビバーのユーザー名をクリックすると表示されるリストから「マイセキュリティ資格情報」を選択します。
2. アクセスキー（アクセスキー ID とシークレットアクセスキー）を展開し、「新しいアクセスキーの作成」を押します。CSVファイルのダウンロードは行っておきましょう。
3. ターミナルで`aws configure`とコマンドを打つと、クレデンシャルの作成が始まります。名前を付ける場合は`aws configure --profile <name>`のように指定してください。指定がない場合、`default`という名前で作成されます。
  - `AWS Access Key ID`：CSVファイルの`AWSAccessKeyId`を入力する
  - `AWS Secret Access Key`：CSVファイルの`AWSSecretKey`を入力する
  - `Default region name`：AWSで選んでいる現在のリージョンを入力（例：`ap-northeash-1`）。リージョンはAWSコンソールの上部ナビバーのアカウント名の横から確認可能。
  - `Default output format`：特に指定がなければ空欄のままEnter
4. プロファイル名を指定した場合、以降のawsコマンドで今回作成したプロファイルを使用するために次のコマンドを使用しておきます。
  - `export AWS_PROFILE=<name>`

# 4. ECR(Elastic Container Registry)でリポジトリの作成

　今回の目標である「自作ウェブアプリをFargateで動かす」ために行う作業は大まかに二つあります。一つは、DockerイメージをAWSにアップロードすること。もう一つは、そのイメージの実行方法を登録することです。それを行うために付随する作業がいろいろあるのですがその都度説明を加えたいと思います。

　ECRでは、そのDockerイメージをアップロードして保管しておくリポジトリを作成します。[こちら](https://ap-northeast-1.console.aws.amazon.com/ecr/repositories?region=ap-northeast-1)（リージョンは適宜変更をお願いします）よりECRのリポジトリ一覧が確認できますが、最初はおそらく空でしょう。「リポジトリを作成」を押してリポジトリを作成しましょう。設定は特に変更せずデフォルトのままでよいと思います。作成が完了したら、イメージのアップロードは後で行うので、いったんここでの作業は完了となります。

# 5. Dockerイメージのプッシュ

　次はDockerイメージをECRレポジトリへプッシュする作業段階になります。まずECRへDockerイメージをプッシュするための認証を行います。ECRのレポジトリ一覧から一つ選択し、「プッシュコマンドの表示」ボタンを押します。するといくつかコマンドが表示されるので、1番目のコマンド（`aws ecr get-login-password`から始まるもの）をコピペして実行します。数行出力された後、`Login Succeeded`と表示されれば成功です。もし`Cannot autolaunch D-Bus without X11 $DISPLAY`というエラーが表示されてしまったら、以下のコマンドを実行すると解決できるようです（[こちらの記事](https://qiita.com/developer-kikikaikai/items/03131dd1a9ac2b99e340)を参考にさせていただきました）。

```sh
sudo apt install gnupg2 pass
```

　次に、Dockerイメージのビルドとプッシュを行います。まずECRのレポジトリの一覧から、先ほど作成したレポジトリのURIをコピーします。ここで一度ローカルの作業環境に戻り、Dockerfileのあるディレクトリで以下のコマンドを叩きます。

```sh
export ECR_URI=XXX # XXX=ECRのレポジトリURI
export ECR_TAG=0.1.0 # バージョンや種類を区別するもの

# Dockerfileのビルド
docker build -t $ECR_URI:$ECR_TAG .
# Dockerイメージのプッシュ
docker push $ECR_URI:$ECR_TAG
```

# 6. ECS(Elastic Container Service)でクラスターの作成

### 概要

　クラスターとは、あるサービス（今回でいうウェブサーバーなど）のグループのことです。クラスターやサービスとは、すごく雑に説明するとフォルダのように階層構造になっている枠組みのようなものでしょうか。正確にはもっと複雑な役割があるので、詳しく知りたい方は調べてみてください。
　これから行う作業を簡単に並べると以下のようになっています。

1. 「example」というクラスターを作成します
2. exampleクラスターに「web-app」というサービスを一つ作成します
3. web-appサービスに「run-server」というタスクを持たせます
4. run-serverタスクはウェブサーバーを立ち上げるコマンドを実行します。このとき、どのDockerイメージを使うのか、ハードのスペックをどうするのか、どんなコマンドで実行するのか、といったことをまとめた「タスク定義」を作成しそれもとにタスクが実行されています

　なんとなくクラスター・サービス・タスクの立ち位置がわかったのではないでしょうか？それでは手順の説明に移ります。

### クラスター・サービス・タスクの作成

　[こちら](https://ap-northeast-1.console.aws.amazon.com/ecs/home?region=ap-northeast-1#/clusters)からクラスターの一覧が表示できます。今回は「クラスターの作成」ではなく「今すぐ始める」ボタンを押します。図もあってわかりやすく、また上記の作成手順をまとめて行うことができます。

1．コンテナの定義は、4つ並んでいるボタンのうち「sample-app」を選び特に何も変更せず作成まで完了させると動くものが用意できるので、気になった方は試してみると良いです。
2. 自分でアップするDockerイメージを使用したいのでcustomを選択します。
3. コンテナの編集というウィンドウが出てくるので、次のように設定していきます
  - コンテナ名：サービスの名前になります。web-appと入力しておきましょう
  - イメージ：ECRのレポジトリURIを指定します。ECRのレポジトリ一覧を開き、Dockerイメージをプッシュした際にもコピーしたレポジトリのURIをペーストします。またコピーしたURIの末尾に`:0.1.0`と付けましょう（`0123456789.dkr.ecr.ap-southeast-1.amazonaws.com/web_app:0.1.0`のようになっていると思います）。
  - メモリ制限：ソフト制限, 512（自由）
  - ポートマッピング：80（自由）
  - CPUユニット数：256（自由）
  - コマンド：Dockerfileのコマンドをすべて行った状態を想定し実行するコマンドを入力します
  - 作業ディレクトリ：Dockerイメージ内部の環境に対しどのディレクトリでコマンドを実行するかを指定します。`/app`のように指定します。
4. 「更新」ボタンを押し編集を完了します
5. クラスター名を「example」とし、それ以外は特に変更の必要はありません。
6. 「作成」ボタンを押すといろいろ作成が開始されます。これらが終わるまで待ってクラスター～タスクまでの作成が完了です。

# 7. ウェブアプリへのアクセス

　では実際にアドレスバーにURLを入力してアクセスしてみましょう。先ほど作成したクラスターのサービスを選択します。「タスク」タブからタスク（おそらく一つ）を選びパブリックIPを確認します。これをURL（`http://WW.XX.YY.ZZ/...`）の`http://`の次に入力し、`/`以下はアプリケーションごとにつながるものを入力してください。
　以下のように無事アクセスできれば成功です。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/355274/814e6122-55e0-7387-b027-4ea911ced46a.png)

　ちなみにこれを停止するには、クラスター→サービス→「更新」（画面右上）→タスクの数を0にします。また新しくイメージをプッシュしたとき、現在動いているものを置き換えるには、クラスター→サービス→「更新」（画面右上）→新しいデプロイの強制にチェック→skip to review→サービスの更新を行うことで反映させることができます。

# おわりに

　とりあえずFargateでサービスを動かすことができました！最初は何が原因で動いていないのかわからず、Auto Scalingグループやロールの設定を調べたりしていましたが、最低限動かすだけなら結果的にこれだけでよかったんですね。
　このままでは他人へ公開するにはいろいろ足りていませんが、今後も学習と改善を重ねていこうと思います。