はじめに
インフラ構成をEC2からECS/Fargateに移行した作業をまとめています。
アプリケーションのコードはRailsで記述しています。
(Nginxはまだ実装できていません…Vue実装時に考える予定です。)
RDSにはMySQLを使っていて、Railsがアクセスできる仕様です。
個人的にECS/Fargate構成で迷走してましたので、
自分が整理できるように記事を書きました。
同じように悩んでいる初学者の方の参考になれば幸いです。
使用技術
- ECS/Fargate(blue/greenデプロイメント)
- Rails6(Ruby2.7)
- MySQL8
インフラ構成図 (EC2 → ECS/Fargate)
【移行前】EC2インフラの構成について
**CicleCIを除く、
**移行前のインフラ構築は
下記に参照している@take18k_tech(やんばるエキスパート)さんのQiita記事を参考にさせて頂きました。
初学者にも分かりやすくまとめられているので、EC2インフラの振り返りにも使用しています。
※当記事のRDS、EC2も@take18k_tech(やんばるエキスパート)さんの記事をベースに作成しています。
記事: 4部構成
作業内容が多いので、以下の4つに記事を分けました。
- ECRへイメージ登録編
- ECS起動編
- HTTPS/固定ドメイン編
- 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イメージを保管したり、取得したりできる場所です。
では、マネジメントコンソールを参照しながら説明します。
各種リポジトリの設定を行なって、リポジトリを作成
を押します。
自動的にページ遷移し、リポジトリ一覧が表示されます。
ここで、作成したリポジトリをチェックしてから プッシュコマンドの表示
を押します。
すると、DockerイメージをECRに保存するまでに必要なコマンドが
4つ自動表示されます。
それぞれ簡単に説明すると以下の通りです。
一部編集するため、コマンド実行はまだしないでください。
# 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アプリが動作できているものですので
ご自身のアプリに合わせて作成してください。
# ベースイメージは軽量な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用の設定コードから変更はありません。
#!/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に保存できているか確認します。
最新のlatestタグがついたイメージが存在していることを確認します。
以上で、第一部のECRへイメージ登録編が終了です。
続編
参考書籍
・AWSコンテナ設計・構築[本格]入門
参考記事
終わりに
ECS/Fargateは学習コストが低いとされているけど、コンテナを本番環境にホストが
初経験の自分では非常に難しかった…
これより更にムズイKubernetesは震えが止まりませんね。
無理はせずに、先にterraformを学習しようと思います!