5
2

More than 1 year has passed since last update.

RailsアプリをEC2からECS/Fargate構成に移行してホストする【ECRへイメージ登録編】

Last updated at Posted at 2021-12-12

はじめに

インフラ構成をEC2からECS/Fargateに移行した作業をまとめています。

アプリケーションのコードはRailsで記述しています。
(Nginxはまだ実装できていません…Vue実装時に考える予定です。)

RDSにはMySQLを使っていて、Railsがアクセスできる仕様です。

個人的にECS/Fargate構成で迷走してましたので、
自分が整理できるように記事を書きました。

同じように悩んでいる初学者の方の参考になれば幸いです。

使用技術

  • ECS/Fargate(blue/greenデプロイメント)
  • Rails6(Ruby2.7)
  • MySQL8

インフラ構成図 (EC2 → ECS/Fargate)

■移行前のインフラ構成
スクリーンショット 2021-12-12 6.15.16.png

■移行後のインフラ構成
スクリーンショット 2021-12-13 7.28.38.png

【移行前】EC2インフラの構成について

CicleCIを除く、移行前のインフラ構築は
下記に参照している@take18k_tech(やんばるエキスパート)さんのQiita記事を参考にさせて頂きました。

初学者にも分かりやすくまとめられているので、EC2インフラの振り返りにも使用しています。

※当記事のRDS、EC2も@take18k_tech(やんばるエキスパート)さんの記事をベースに作成しています。

記事: 4部構成

作業内容が多いので、以下の4つに記事を分けました。

  1. ECRへイメージ登録編
  2. ECS起動編
  3. HTTPS/固定ドメイン編
  4. CircleCI連携編(自動デプロイ)

作業内容

今回はECRへイメージ登録編で、作業内容は以下の通りです。

■第一部(ECRへイメージ登録編)
1. ネットワーク構築
2. RDSの作成
3. 管理用EC2の作成
4. ECR構築とイメージ保存

■第二部(ECS起動編)

■第三部(HTTPS/固定ドメイン編)

■第四部(CircleCI連携編)

※ エラー時のデバッグ

1. ネットワーク構築

まず、ネットワークのリソースをそれぞれCIDRブロックで構想します。

VPC:ネットワーク全体

VPC名称 CIDR アドレス範囲
app-production-vpc 10.0.0.0/16 10.0.0.0 - 10.0.255.255

サブネット:リソースごとの領域

サブネット名称 CIDR アドレス範囲 region 用途 ルートテーブル
app-production-ecs-1a-subnet 10.0.1.0/24 10.0.1.0 - 10.0.1.255 ap-northeast-1a ECS(ALB/fargate) Public
app-production-ecs-1c-subnet 10.0.2.0/24 10.0.2.0 - 10.0.2.255 ap-northeast-1c ECS(ALB/fargate) Public
app-production-db-1a-subnet 10.0.11.0/24 10.0.11.0 - 10.0.11.255 ap-northeast-1a DB Private
app-production-db-1c-subnet 10.0.12.0/24 10.0.12.0 - 10.0.12.255 ap-northeast-1c DB Private
app-production-management-1a-subnet 10.0.21.0/24 10.0.21.0 - 10.0.21.255 ap-northeast-1a 管理用EC2 Public
app-production-management-1c-subnet 10.0.22.0/24 10.0.22.0 - 10.0.22.255 ap-northeast-1c 管理用EC2 Public

インターネットゲートウェイ:インターネットに繋がる唯一の出入り口

インターネットゲートウェイ名称
app-production-gw

ルートテーブル:サブネットの通信経路を決める

ルートテーブル名称 送信先 ターゲット
Public 10.0.0.0/24, 0.0.0.0/0 local, app-production-gw
Private 10.0.0.0/24 local

サブネットグループ:サブネットをまとめた集合(RDSで必要)

サブネットグループ名称 グループ
app_rds_subnet_group 10.0.11.0/24, 10.0.12.0/24

セキュリティグループ:リソース同士の接続を制限するルール

セキュリティグループ名称 タイプ ソース 用途
app-ecs-sg HTTP, HTTP, HTTPS, HTTPS, カスタムTCP 0.0.0.0, 0::, 0.0.0.0, 0::, app-management-sg ALB/fargate
app-db-sg MySQL app-ecs-sg DB
app-management-sg SSH マイIP 管理用EC2

■セキュリティグループ補足:
 ・app-ecs-sgのみ、5つインバウンドルールを設定しています。
 ・アウトバウンドルールは全てのトラフィックとしています。

構想ができたら、AWSのマネジメントコンソール上のVPC画面で
ポチポチとネットワークを構築していきます。
(コンソールのGUIで構築していく手順は省略します。お許しください。)

2. RDSの作成

続いて、RDSを作成していきます。

構築手順は、@take18k_tech(やんばるエキスパート)さんの下記Qiita記事をベースに作成します。
(ここでもコンソール画面での手順は省略します…すいません。)

前工程のネットワーク構築で作成したVPC/サブネット(DB用)/セキュリティグループ(DB用)を
RDSに紐付けて作成を完了してください。

なお、AWS無料枠の対象となるMySQLを選択しています。

3. 管理用EC2の作成

次はEC2の作成です。

この作業は必須ではないはずですが

EC2がAWS無料枠で作成できる かつ blue/greenデプロイメントのテストリスナーを置きたい

といった観点から作成しています。

ここも@take18k_tech(やんばるエキスパート)さんの下記Qiita記事をベースに作成します。
(コンソール画面での手順は省略…ご了承を…)

前工程のネットワーク構築で作成したVPC/サブネット(管理用EC2)/セキュリティグループ(管理用EC2)を
EC2に紐付けて作成を完了してください。

構築後はローカルからEC2にSSH接続できるかを検証してみましょう。

ターミナル

# SSH接続に必要な情報(Host, Hostname, User, IdentityFile)が設定されているか確認
% vi ~/.ssh/config

# ManagementEC2
Host 任意のホスト名
  Hostname EC2に紐付けたElasticIPアドレス
  User ec2-user(EC2デフォルトのユーザー名)
  IdentityFile ~/.ssh/〇〇.pem(EC2作成に生成したkey)

# 確認後.ssh/configファイルをもとにSSH接続する
% ssh ホスト名

# 質問に回答する
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

### ログイン成功!
       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

4. ECR構築とイメージ保存

ここでは、ECR作成してリポジトリにイメージを保管します。

ECR: AWS版のレジストリサービス

DockerHubのようにDockerイメージを保管したり、取得したりできる場所です。

では、マネジメントコンソールを参照しながら説明します。

まず、AWSサービス一覧からECRを選択します。
スクリーンショット 2021-12-12 9.41.41.png

続いて、ECR画面からリポジトリを作成をクリックします。
スクリーンショット 2021-12-12 9.42.42.png

各種リポジトリの設定を行なって、リポジトリを作成を押します。
スクリーンショット 2021-12-12 9.46.32.png

自動的にページ遷移し、リポジトリ一覧が表示されます。
ここで、作成したリポジトリをチェックしてから
プッシュコマンドの表示
を押します。
スクリーンショット 2021-12-12 11.19.41.png

すると、DockerイメージをECRに保存するまでに必要なコマンドが
4つ自動表示されます。

スクリーンショット 2021-12-12 11.24.16.png

それぞれ簡単に説明すると以下の通りです。
一部編集するため、コマンド実行はまだしないでください。

ターミナル

# 1.Dockerコマンドが使用できるようにAWSに認証させる
% aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com

# 2.Dockerfileをもとにイメージを生成する
% docker build -t app-backend .

# 3.ECRに保存するためにAWS指定のタグ付けを行う
% docker tag app-backend:latest AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/app-backend:latest

# 4.指定タグをつけたイメージをECRに保存する
% docker push AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/app-backend:latest

上記コマンドを実行する前に、2つのファイルを準備します。

1.本番用Dockerfile: Dockerfile.production

Dockerイメージの生成に必要なファイルです。

ここでは、開発用Dockerfileと本番用Dockerfileを区別して
ファイルをDockerfile.productionとしています。

Dockerfileのコードは、下記Qiita記事やUdemy動画をベースに
Try&Errorで作り直して現時点で私のRailsアプリが動作できているものですので
ご自身のアプリに合わせて作成してください。

Dockerfile.production
# ベースイメージは軽量なalpineを採用する(ローカルのrubyバージョンに合わせる)
FROM ruby:2.7.3-alpine

# Dockerfile内で使う変数としてRUNTIME_PACKAGESとDEV_PACKAGESの2つを定義する
ARG RUNTIME_PACKAGES="bash imagemagick nodejs yarn tzdata mysql-dev mysql-client git"
ARG DEV_PACKAGES="build-base curl-dev"

# コンテナの作業用ディレクトリを設定する
WORKDIR /app

# Railsの実行環境に関わる環境変数をproductionに変更する(コンテナに渡すのでENVとする)
ENV RAILS_ENV="production"

# ローカルにあるGemfileとGemfile.lockをコンテナに複製する
COPY Gemfile Gemfile.lock /app/

# alpine特有のapkコマンドを使って、パッケージのインストールと削除
RUN apk update && \
    apk upgrade && \
    apk add --no-cache ${RUNTIME_PACKAGES} && \
    apk add --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
    bundle install -j4 && \
    apk del build-dependencies

# ローカルのアプリコード一式をコンテナのappディレクトリに複製する
COPY . /app

# アセットのプリコンパイルを実行する
RUN SECRET_KEY_BASE=placeholder bundle exec rails assets:precompile \
    && yarn cache clean \
    && rm -rf node_modules tmp/cache

# 静的アセットの配信を行う設定
ENV RAILS_SERVE_STATIC_FILES="true"

# entrypoint.production.shを複製してシェルスクリプトを実行する
COPY entrypoint.production.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.production.sh
ENTRYPOINT ["entrypoint.production.sh"]

# コンテナはポート番号を80で開放する
EXPOSE 80

# Railsサーバーが本番環境で起動する(上記ENVで"production"としている)
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0", "-p", "80"]

2.シェルスクリプト: entrypoint.production.sh

Dockerfile.production側のコードから一時的に処理が渡されるファイルです。
こちらでRailsのマイグレーションを実行しておきます。

それ以外はDockerの公式ドキュメントに載っている
Rails用の設定コードから変更はありません。

entrypoint.production.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /app/tmp/pids/server.pid

# AWSコンソール画面で作成したRDSに対して、マイグレーションを実行する
# (データベース自体は作成されているので、マイグレーションファイルでテーブルを作成する)
rails db:migrate RAILS_ENV=production

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

以上で2つのファイルが作成できたら、先ほどの4つのコマンドを順番に実行していきます。

2つ目のコマンドのみ、Dockerfile.productionを指定するように修正します。
-> デフォルトだとDockerfile(開発用)が読み取られてしまうので注意します。

ターミナル

# 1.Dockerコマンドが使用できるようにAWSに認証させる
% aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com

###(ここだけ修正する)###
# 2.本番用のDockerfile.productionをもとにイメージを生成する
% docker build -f Dockerfile.production -t app-backend .

# 3.ECRに保存するためにAWS指定のタグ付けを行う
% docker tag app-backend:latest AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/app-backend:latest

# 4.指定タグをつけたイメージをECRに保存する
% docker push AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/app-backend:latest

最後にイメージがECRに保存できているか確認します。

ECR画面から対象リポジトリを選択します。
スクリーンショット 2021-12-12 12.38.31.png

最新のlatestタグがついたイメージが存在していることを確認します。
スクリーンショット 2021-12-12 12.39.55.png

以上で、第一部のECRへイメージ登録編が終了です。

続編

参考書籍

・AWSコンテナ設計・構築[本格]入門

参考記事

終わりに

ECS/Fargateは学習コストが低いとされているけど、コンテナを本番環境にホストが
初経験の自分では非常に難しかった…

これより更にムズイKubernetesは震えが止まりませんね。

無理はせずに、先にterraformを学習しようと思います!

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2