Dockerを用いたローカルのアプリをECSにデプロイする(その2)

少し前にDocker, Docker Composeを用いたローカルのアプリをECSにデプロイしたので手順を記述します。手順は3記事に分けて説明をする次第で、この記事は2つ目です。手順8から手順12までを記述しています。

全体の手順

  • VPC作成
  • パブリックサブネット・プライベートサブネット作成
  • インターネットゲートウェイ作成・追加設定
  • ルートテーブル作成・追加設定
  • クラスター作成
  • RDS用のサブネットグループ作成
  • RDSインスタンス作成
  • セキュリティグループ作成
  • ECRのレポジトリ作成・イメージのプッシュ
  • Route53の設定
  • ALB作成
  • アプリのドメインをALBのドメインと紐付け
  • entrypoint.shの作成
  • Dockerfileの修正
  • database.ymlの修正
  • タスク定義
  • サービス作成・タスク実行

デプロイ後の構成

f:id:rinda_1994:20210613220304p:plain

前提

  • Dockerfile, docker-compose.ymlを用いてローカルでアプリ用のコンテナを起動している
  • アプリにはRuby, Railsを使用している
  • dotenv-railsをインストールして.envファイルを作成している

8. RDSインスタンス作成

RDSはAWSRDBを作成するためのサービスです。この記事ではRDBMySQLを選択しています。

  • データベース作成方法:標準作成

  • エンジンのタイプ:MySQL

  • バージョン:MySQL 5.7.30

  • テンプレート:本番稼働用

f:id:rinda_1994:20210613124735p:plain f:id:rinda_1994:20210613125318p:plain

f:id:rinda_1994:20210613125334p:plain

  • VPC:手順1で作成したVPCを選択

  • サブネットグループ:手順6で作成したサブネットグループを選択

f:id:rinda_1994:20210613125541p:plain

  • VPC セキュリティグループ:既存の選択

  • 既存のVPC セキュリティグループ:手順7で作成したセキュリティグループを選択

f:id:rinda_1994:20210613231620p:plain

【追加設定】

  • 最初のデータベース名:自由な名前(必須)

f:id:rinda_1994:20210613233337p:plain

※手順8について、上記の項目以外はデフォルトで問題ありません

9. Route53の設定

Route53はAWSDNSサービスです。ACMなどAWSの他のサービスと連携しやすい感じになっているのでインフラ関連をAWSでまとめたい場合はドメイン管理・DNSサービスにRoute53を選択した方が安牌かもしれません。

ドメイン検索

入力スペースに自由にドメイン名を入力し、そのドメイン名が既に存在していないかを確認するためチェックボタンを押下。 (下のドメイン検索でのスクショでは適当に「rerejojfioaojfoian.com」というドメイン名にしています)

f:id:rinda_1994:20210613181017p:plain

②連絡先の詳細

氏名、電話番号、メールアドレスなど自分の個人情報を入力。

③確認と購入

ドメインを自動的に更新しますか?」の項目は「有効化」を選択し、「注文を完了ボタン」を押下してドメインを作成。

f:id:rinda_1994:20210613181138p:plain

10. ALB作成

ALBはAWSのロードバランサです。ユーザの端末から届いた通信を手順17で作成する2つのタスクに分配します。

f:id:rinda_1994:20210614210714p:plain

f:id:rinda_1994:20210614211522p:plain

  • アベイラビリティーゾーン:
    • VPC:手順1で作成したVPCを選択
    • 1つ目のサブネット

      • アベイラビリティーゾーン:ap-northeast-1a
      • サブネットの選択:手順1で作成したタスク用サブネットを選択
    • 2つ目のサブネット

      • アベイラビリティーゾーン:ap-northeast-1a
      • サブネットの選択:手順1で作成したタスク用サブネットを選択

f:id:rinda_1994:20210614212304p:plain

  • 証明書タイプ:ACMから証明書を選択する (推奨)

証明書タイプを選択した後、「ACMから新しい証明書をリクエスト」というリンクを押下

f:id:rinda_1994:20210614213451p:plain

f:id:rinda_1994:20210613211502p:plain

  • 検証方法の選択:DNSの検証

f:id:rinda_1994:20210613211515p:plain

どれくらいの時間か失念しましたが検証のための待ち時間があり、それが終わると証明書が正常に作成される形です。

証明書が作成されたことを確認した後、ALBの証明書タイプを選択した画面に戻り以下を選択します。

f:id:rinda_1994:20210614213451p:plain

  • セキュリティグループの割り当て:新しいセキュリティグループを作成する

  • セキュリティグループ名:デフォルト

  • ルール:デフォルト(ポート範囲が80と443のルールが勝手に作られていると思うのでそれをそのまま使用します)

f:id:rinda_1994:20210613211704p:plain

ターゲットグループはALBから特定のインスタンスに通信をルーティングするためのコンポーネントです。その際通信のポート番号も上書きできます。

f:id:rinda_1994:20210613211727p:plain

  • インスタンス:選択しない(ECSのタスクを設定する項目。手順16でタスクを作成したあとに追加で設定する)

f:id:rinda_1994:20210613211741p:plain

11. アプリのドメインをALBのドメインと紐付け

Route53のホストゾーン一覧のうち手順9で作成したドメイン名を選択したのち、以下画面で「レコードを作成」を押下

f:id:rinda_1994:20210614220230p:plain

  • レコードタイプ:A

「値」の「エイリアス」のボタンをONにした後プルダウンが表示されるので以下を選択

  • エンドポイントを選択:Application Load Balancer と Classic Load Balancer へのエイリアス

  • リージョンを選択:アジアパシフィック (東京) [ap-northeast-1]

  • ロードバランサーを選択:手順10で作成したALBを選択

f:id:rinda_1994:20210613213739p:plain

12. entrypoint.shの作成

手順12から手順14はAWSの設定ではなくローカルでファイルを追加作成・修正するという話になります。

この記事の上の方にdocker-compose.ymlを用いていることを前提として記述しましたが・・・。その前提を満たしている場合、ローカルではdocker-compose.ymlを用いてコンテナを起動し、そのコンテナの中では何かWebサーバ用のアプリケーションを起動している形と思います(「Webサーバ用のアプリケーション」という表現がどうなのかはちょっと分からないですが・・・この記事ではそう書いておきます)。

それを踏まえて本番環境であるECSでも同様のことをするためにはですが・・・

コンテナの起動に関しては手順18でタスクの実行を行うことでできますが、コンテナの中でWebサーバ用のアプリケーションを起動することに関しては追加で設定を行う必要があります。 その設定手段として一つあるのがentrypoint.shの作成です。

entrypoint.shって何だという話ですが・・・ まずentrypointを大文字にしたENTRYPOINTという言葉はDockerfileに記述する命令の一つです。この命令はイメージをもとにコンテナを起動する際に実行するLinuxコマンドを指定します。例えば

ENTRYPOINT ["ping","-c","3"]

とDockerfileに記述をするとコンテナ起動時にpingが3回実行されます。 つまりENTRYPOINTとは「コンテナを起動する際に実行するLinuxコマンドを指定するためのもの」と考えて問題なさそうです。

続いて.shはそのファイルがシェルスクリプトであることを示す拡張子です。 調べた感じシェルスクリプトとはざっくり「複数のLinuxコマンドをまとめて順々に実行するためのファイル」のようなので、要約するとentrypoint.shは「コンテナを起動する際にまとめて実行するLinuxコマンドを指定しているファイル」と考えてよさそうです。

この手順では、ECSのコンテナ起動と一緒にコンテナ内でWebサーバ用アプリケーションを起動するための設定をしたいので、そのために実行する必要のあるLinuxコマンドをentrypoint.shに記述する形になります。

以下がentrypoint.shの内容です。以下内容の前提として、筆者は自分のアプリをRailsで書いていて、本番環境のWebサーバ用のアプリケーションにはPumaを使っています。

#!/bin/sh

yarn install
rm -f /myapp/tmp/pids/server.pid
bin/rails s -p 3000 -b '0.0.0.0' -e production
bundle exec rails db:migrate

それぞれの行の役割は以下です。やや詳しめに書いています。

  • #!/bin/sh

    • このentrypoint.shのインタプリタとして、UNIXの標準のシェル(sh)を使うことを指定しています。このシェルによって以下のコマンドが、コンテナの基盤にあるホストOS内のカーネルが実行できる何かに変換されるみたいです。とりあえずこの行を書いていないと以下のコマンドは実行されません。
  • yarn install

    • yarnをインストールしています。yarnはJavaScriptのライブラリを管理するのに必要で、RailsではRails6から必要になったみたいです。yarnがインストールされていない時点でrails sなどのrailsコマンドを叩くと実行できない旨のエラーが出ます。
  • rm -f /myapp/tmp/pids/server.pid

    • server.pidを削除しています。このファイルがあるとPumaを起動できない時があるので削除しています。
    • server.pidって何なんだという点ですが、Pumaのプロセスに関する情報が入っているファイルです。プロセスに関する情報といってもファイルの中身はプロセスIDである1という数字が書かれているのみですが・・・。プロセスはサーバ(今回でいうとPuma)が実行する1つ1つの処理のことをそう呼ぶとのこと。
    • (残るのがこの場合のみなのかは分かりませんが)コンテナが強制終了された時にコンテナは停止しているけどこのserver.pidはなんか残っている、みたいなことがあります。そうした場合に改めてコンテナおよびPumaを起動しようとすると、コンテナは起動するもののPumaが起動しません。Rails側で「server.pidがあるということはPumaがなんか処理しているってことなので、Pumaの起動はしない」という処理がされるためです。
    • そんな感じでコンテナ起動時にserver.pidがあるとPumaを起動できないので、rails sする前はとりあえず削除のコマンドを入れとこうという意図の行です。
    • ただ筆者が開発する中でserver.pidがあっても普通にPuma起動できた時もあったような気がするので、ちょっとその辺りはちゃんと分かれていないかもなところです・・・
  • bin/rails s -p 3000 -b '0.0.0.0' -e production

    • Pumaを起動しています。
    • -e production
      • 起動する環境が本番環境であると指定しています。たしか指定しないとエラーが出ます。指定しない場合、環境についてはデフォルトで設定されている開発環境としてPumaが起動しようとするわけですが、なぜ「実際の環境:本番環境、Puma側での環境:開発環境」だとエラーが出るのかはよく分かりません。
    • -p 3000
      • ポート番号を3000番で開けています。ただ調べるとデフォルトで3000番が開いているっぽいのでこのオプションは書かなくても大丈夫そうです・・・
    • -b '0.0.0.0'
      • Pumaのプロセスをコンテナが持っている全ての仮想インタフェースに接続しています。0.0.0.0というのが「不明」という意味を持つIPらしいので、具体的にIPを指定はせずにどのIPを持ったインターフェースも接続の対象にする、みたいなことだと思います。この設定により、コンテナの外部からコンテナ内にアクセスできるようになります。多分この設定をしないとローカルの端末からこのECSのコンテナにSSH接続などをしようとしてもできなさそうです。
  • bundle exec rails db:migrate
    • DBのマイグレーションをします。これしないと本番環境のDBにローカルと同じ変更内容を加えられず、あとそもそもエラーが出てアプリを表示できません。