Open1
ECSへのデプロイ(Rails7, Sidekiq, Elasticache)
AWS ECSへのデプロイ
とりあえずECSで動く状態で公開することを目指す。
まずはAWS環境を整える
VPC作成
- VPC一括作成機能で以下を一気に作成
- IGW作成とアタッチ
- 2AZで以下を作成
- パブリックサブネット
- プライベートサブネット
- NATGW作成
- ルートテーブル作成
- パブリックはIGWに、プライベートはNATGWにルーティング
- ルートテーブルのサブネットへのアタッチ
- EIP作成(あとでインスタンスにアタッチする)
SG作成
SG=FWみたいなもの。今回は「ユーザー>ALB(SSL化)>ECS(Webサーバーは建てないので直接3000ポートで待ち受け)>RDS(ポスグレ5432)orRedis(6379)」
- ALB用
- インバウンドルールで80,443を許可
- ECS用
- インバウンドルールでALBSGからの3000のみを許可
- RDS用
- インバウンドルールでECSSGからの5432のみを許可
- Redis用
- インバウンドルールでECSSGからの6379のみを許可
Route53
- ホストゾーン作成
- お名前ドットコムで取得したドメインのNSをRoute53のホストゾーンのものに変更
ACMで証明書取得
- 取得したドメインで証明書を取得
ALBの作成
- EC2のコンソールに移動しターゲットグループを作成(まだインスタンスを作成していないのでターゲットは一旦空でOK)
- ALBを作成
- マッピング先としてパブリックサブネットを選択
- デフォルトのセキュリティグループに加え、先ほど作成したALB用のセキュリティグループを追加し許容する通信を制限
- HTTPとHTTPSをリスニングする
- HTTPはHTTPSにリダイレクトするよう設定変更
RDSの作成
- 無料枠で作成(シングルAZ)
- サブネットグループを作成しプライベートサブネットを対象にする
- ※のちのち
database.yml
をRDSに合わせて書き換えること - password, hostはECSタスク作成時に環境変数を渡してあげる
Redisの作成
- 「独自のクラスターを作成」
- 今回は費用を抑えるため最小のインスタンスで作成
- クラスターを無効, 通信暗号化を無効
- こちらも本来はどちらも有効にすべきかもだが、後で作成したsidekiqコンテナからのキュー実行でコケたので、一旦単一ノードで尚且つ暗号化せず作成(※その代わりSGでしっかり通信は制御する)。本当ならsidekiqの方でクラスターモードでのredisとの通信の設定とかやりようはありそう…
- Redis用SGを設定
ECSデプロイの下準備
- 本番環境では
bin/dev
ではなくrails s
を使用したいのでDockerfileのCMD
を修正 - シェルスクリプトで環境変数を参照して
sidekiq
,rails s
,bin/dev
のいづれかを実行するようにするCMD ["sh", "-c", "if [ \"$SIDEKIQ_ENV\" = 'true' ]; then bundle exec sidekiq -C config/sidekiq.yml; elif [ \"$RAILS_ENV\" = 'production' ]; then rails s -b 0.0.0.0; else bin/dev; fi"]
- その他、テスト,開発環境でのみ実行するgemが読み込まれるような箇所があれば読み込まれないように修正(じゃないとコンテナ起動時に「そんなモジュール無いよ」と怒られてコンテナが終了する)
# application.rb
if Rails.env.test?
config.middleware.use RackSessionAccess::Middleware
end
- entrypoint.shでコンテナ起動時のコマンドを定義。アセットのプリコンパイルとDB関連を行う
- ※初回起動はDB作成やマイグレーションを行うが、二回目以降はエラーになるのでコメントアウトしてイメージをpushしなおす
if [ "$RAILS_ENV" = "production" ]; then
bundle exec rails assets:precompile
# bundle exec rails db:create
# bundle exec rails db:migrate
# bundle exec rails db:seed
fi
ECRへのイメージ登録
- ECRでリポジトリを作成
- イメージ登録のコマンドが出てくるのでローカルで実行
-
docker build -t beta-master-ecr .
でイメージをビルド -
docker tag beta-master-ecr:latest 908231638687.dkr.ecr.ap-northeast-1.amazonaws.com/beta-master-ecr:latest
でイメージにタグを付ける -
docker push xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/beta-master-ecr:latest
でイメージをpush
-
ECSへのデプロイ
ECSの概要
以下のような構成になっている。
- タスク(コンテナの起動の定義)
- どのイメージを利用するか
- 起動させるマシンのCPU,メモリ数
- コンテナ起動時に渡す環境変数
- サービス(タスクの集合体)
- タスクをいくつ実行するかの定義
- どのネットワークに配置するか
- コンテナに適用するSG
- ALBで負荷分散させるか
- クラスター(サービスの集合体)
- ※なんか、タスクとサービスで合わせてcompose.ymlでやっているような定義を行っているようなイメージ
クラスターの作成
- コンソールから良さげな名前でクラスターを作成する
コンテナに接続できるようIAMロールを編集しておく
- 後述するが、コンテナにはIAMロールを付与することができる。他のAWSサービスへのアクセスなどをする必要があれば定義しておく
- また、起動中のコンテナにCLIから接続するためにもIAMロールのアタッチが必要になる
- 接続に必要なポリシーを作成する
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
]
}
- ポリシーをIAMロールにアタッチする(今回はAWS管理の
ecsTaskExecutionRole
にアタッチ)
タスクの作成
今回はrails用サービス×2で負荷分散&sidekiqサービス×1で実行したいので、それぞれのタスクを作成する
- タスク名, CPU, メモリをよしなに設定(今回は最小にする)
- コンテナに使用するイメージを選択(ECRに登録したイメージのURIを確認し設定する)
- 公開するポートを設定
- railsコンテナならTCP,3000
- 環境変数の設定
-
BETA_MASTER_DATABASE_HOST
=作成したRDSのエンドポイント BETA_MASTER_DATABASE_PASSWORD
-
RAILS_ENV
=production -
RAILS_LOG_TO_STDOUT
=1 -
RAILS_MASTER_KEY
=masterkeyの値 -
RAILS_SERVE_STATIC_FILES
=1 -
REDIS_URL
=作成したredisのエンドポイントを用いる(redis://xxx:6379) -
SIDEKIQ_ENV
=sidekiqタスクのときだけtrueを設定
-
サービス作成
railsサービスとsidekiqサービスを定義する
- タスクを選択する
- 実行数を選択する
- 2にすればタスクで定義したコンテナが2つぶん起動する
- ネットワークを選択(VPC,サブネット,SG)
- ここで選択したサブネットによしなに起動される
- ロードバランサーの選択
- 作成済のロードバランサーを選択する
- ロードバランサーのリスナーを作成させられるが、あとで編集するので一旦適当に設定しておく
- ここまででデプロイが開始するので、ログを確認しながら無事起動するか確認する
ALBの設定
- サービスを作成すると自動的にターゲットグループが作成される
- ALBのリスナールールを以下のように設定
- HTTPS:ACMで作成した証明書を利用し、自動的に作成されたターゲットグループにリクエストを飛ばす
- HTTP:HTTPSにリクエストをリダイレクトさせる
Route53にレコード作成
- 作成したALBのエイリアスに向けてAレコードを登録
ここまででサービスにアクセスできるようになっているはず。
サービスを更新する場合
- タスクはリビジョンというバージョンのようなもので管理されている
- 何らかの更新がある場合はイメージをプッシュした後にコンソールからリビジョンを新たに作成する
- サービスのコンソールから更新を行う(「新しいデプロイの強制」にチェックを入れ、新しいリビジョンのタスクを選択すると更新できる)
ECSへの接続
- 起動中のコンテナにCLIから接続する方法がある
- まずコンテナへの接続ができるようCLIからサービスをアップデートする
aws ecs update-service --cluster [クラスター名] --service [サービス名] --enable-execute-command
- サービスを更新する(上記参照)
-
aws ecs execute-command --cluster [クラスター名] --task [タスク名] --container [コンテナ名] --interactive --command "bash"
でコンテナに接続 - もしできない場合、
enable-execute-command
が有効になっているか再度確認aws ecs describe-services --cluster (クラスター名) --services (サービス名) | grep enableExecuteCommand
aws ecs describe-services --cluster (クラスター名) --tasks (タスクID) | grep enableExecuteCommand