はじめに
当記事はECS/Fargateインフラ構成を構築する第二部目の記事です。
第一部をまだ読まれてない方はそちらからご確認ください。
それでは、オーケストレータの構築を行なっていきます。
作業内容
- Blue/Greenデプロイメント用ALBの作成
- Railsにヘルスチェック用APIの作成
- CodeDeployを実行するIAMロールの作成
- SystemsManagerで秘匿情報の登録
- Railsのdatabase.ymlを編集
- ecsTaskExecutionRoleの作成
- SystemsManagerにアクセスするIAMポリシーをタスク実行ロールにアタッチ
- ECS(タスク定義、クラスター、サービス)の作成
※ECS起動編と題しながら、準備作業をモリモリ含んでおりすいません…
1. Blue/Greenデプロイメント用ALBの作成
前提となりますが、デプロイ方法はblue/greenデプロイメントを採用しています。
特徴は
・ダウンタイムなしで新旧タスクを切り替えする
・障害時のロールバックが高速である
・一定時間は2つのタスクが起動しているので料金コストがかかる
です。
Blue/Greenデプロイメントに必要になってくるALBから作成していきましょう。
では、AWSサービス一覧からEC2を選択します。
左のサイドタブからロードバランサーをクリックします。
ロードバランサーの作成画面に入りましょう。
はじめに、各種あるロードバランサーの中からALBを選択します。
スキームは
・ユーザーがALB経由でサイトアクセスできる
ようにインターネット向け
にします。
そして、ネットワークを構築したVPCおよびサブネットを関連付けます。
セキュリティ設定の構成は、何もせずに先に進みます。
既存のセキュリティグループを選択して、
ECS用に作成したセキュリティグループを設定します。
次はルーティングの設定です。
ターゲット種類は、ALBが80リスナーに対して振り分けるIP
を選択しておきましょう。
(ECS起動後にタスクが生成されると、自動的にIPアドレスが割り振られます)
ヘルスチェックは
・HTTP(80)で
・Railsのapi/healthcheckパスに
行うように設定しています。
そもそもヘルスチェックとは、
ALBがコンテナに対して安全確認のために、事前に通信確認をしておくものです。
このヘルスチェックが通れば、 そのタスクがターゲットグループ内のターゲットとして配置される
と
イメージを持つと良いと思います。
ヘルスチェックの詳細設定はデフォルトのままでも良いですが
成功とみなす条件を緩めれば、早くパスしますので
各自で考えましょう。
healthcheckプロトコルの注意
・Railsのproduction.rbでSSL通信を強制するコードをコメントインしていないか
-> ヘルスチェックはHTTP(80)のプロトコルで検証するもRailsコンテナはHTTPS(443)となっており、
プロトコルが一致せずに失敗するかもしれない…
=> なので設定していれば、念のためコメントアウト
しておく
require 'active_support/core_ext/integer/time'
Rails.application.configure do
### コメントインしてると、HTTP(80)ではなくHTTPS(443)で通信を求める?
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
end
続いてのターゲットの登録は、ALBが自動的に上手いことしてくれるので、
何もせずにそのままクリックします。
2. Railsにヘルスチェック用APIの作成
続いて、Rails側の編集になります。
まず、先ほどALBで設定したヘルスチェックのパスで
アクションが実行されるようにルーティング設定をroute.rbでします。
Rails.application.routes.draw do
--省略--
namespace :api do
resources :healthcheck, only: [:index]
end
end
更に、対象コントローラーで
200というHTTPコードがレスポンスされる処理を
indexアクションに定義します。
module Api
class HealthcheckController < ApplicationController
def index
head :ok
end
end
end
もし、docker-composeで開発環境の構築をされているなら
上手くレスポンスされるかを検証してみましょう。
ターミナルから下記コマンドを実行します。
# 停止しているコンテナも含めて削除する
% docker-compose down
# バックグラウンドで開発環境を構築する(RailsコンテナとDBコンテナを起動)
% docker-compose up -d
# 念のため、コンテナが起動しているか確認する
% docker-compose ps
開発用をdocker-composeで構築していなければ、
rails sコマンド
で開発用サーバーからでも確認できるはずです。
まず、ブラウザChromeでOpt+Cmd+i
を押して
ディベロッパーツールを開きましょう。
その後にURLの末尾にヘルスチェック用のパスをつけて
アクセスしてみます。
すると、Statusが200
と表示されているはずです。
上手く表示されていない場合はリロードしてみてください。
3. CodeDeployを実行するIAMロールの作成
ECSのblue/greenデプロイメントではCodeDeployが動作します。
CodeDeploy: AWSのデプロイ処理を行うサービス
そのCodeDeployを使うには、予めIAMロールを作成しておかないといけないので
AWS公式ページに従ってロールを作成します。
では、コンソール画面で説明します。
まずAWSサービス一覧からIAMを選択します。
左側のサイドバーからロールを選択します。
ロールを作成
を押して、作成画面に飛びます。
そして、AWSサービス → CodeDeploy → CodeDeploy-ECS
を選び
ユースケースを確定させます。
選択したユースケースでは、どういった権限が付与されているのかを確認できます。
(ECRのイメージ保管の実態として、S3が関係しているのでS3を読取する権限もあるはずです。)
次のタグの追加は、何もせずにそのままクリックします。
最後にロールの作成
を押して、CodeDeployを実行する権限を完成させます。
4. SystemsManagerで秘匿情報の登録
続いて、RailsがDB接続する際に必要な情報をSystemsManagerに登録します。
SystemsManager: 秘匿情報を管理するサービス
Railsは、database.ymlファイルで記されている
RDSのエンドポイント/パスワード/ユーザー名をもとに
DBに接続します。
開発者/管理者以外に知られてはいけない情報を管理するために
当記事ではSystemsManagerを使います。
(本番OS上に秘匿情報を記憶させるのはNGなので注意しておきます。)
では、コンソール画面からSystemsManagerを選択します。
サイドバーよりパラメーターストアを選択します。
パラメータの作成
をクリックします。
ここで、RailsがDB接続に必要な以下3つの情報を登録します。
番号 | パラメータ名称 | 値 |
---|---|---|
1 | /ecs-param/db-host | RDSのエンドポイント(コンソールRDS画面から確認) |
2 | /ecs-param/db-password | ログインに必要なパスワード(RDS作成時に自分で設定済み or AWS自動生成) |
3 | /ecs-param/db-username | 使用するユーザー(編集していなければadmin) |
RailsがDB接続を試みる際、database.ymlファイルを読み込みますが
本番環境はその情報をSystemsManagerから取得する訳ですね。
SystemsManagerに下記のように
host(RDSエンドポイント)に関する情報を登録できたら
続けて、password, usernameも登録して
3つの情報を登録完了してください。
5. Railsのdatabase.ymlを編集
先ほど、SystemsManagerに登録した秘匿情報を
タスク定義が取得して、それをRailsが利用する流れになります。
■秘匿情報の取得の流れ
SystemsManager -> task-def -> Railsコンテナ
タスク定義がSystemsManagerから秘匿情報を取得する設定を後で行いますので
ここでは、先にRails側のdatabase.ymlの設定を行います。
それでは、database.ymlファイルのdefault情報を
production部は、database, username, password, hostを
上書きするのに記述します。
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
socket: /tmp/mysql.sock
username: username
password: password
host: db
production:
<<: *default
database: RDSインスタンス作成時に設定した名称
username: <%= ENV['DB_USERNAME'] %>
password: <%= ENV['DB_PASSWORD'] %>
host: <%= ENV['DB_HOST'] %>
6. ecsTaskExecutionRoleの作成
次は、下記のECSで実行されるロールを作成していきます。
ecsTaskExecutionRole: タスク実行ロール
これも後の作業にはなりますが、ECSサービスを設定する際に
実行権限としてタスク実行ロール
を関連付けます。
そのタスク実行ロールに様々なポリシーをアタッチすることで
実行できる権限が広がり、インフラ構築の幅も広がります。
今回の場合は、SystemsManagerから秘匿情報をタスクが取得するために、
ecsTaskExecutionRoleにSystemsManagerを読み取るポリシーをアタッチします。
なので、ecsTaskExecutionRole自体を先に作っていきます。
AWS公式ページより手順が示されておりますので、
それに従ってIAMからロールを作成しましょう。
では、IAM画面からロール
を押します。
ロールを作成
を押します。
AWSサービス
-> Elastic Container Service
-> Elastic Container Service
と順に押して、次のステップ:アクセス権限
をクリックします。
AWSが指定しているワードを検索フォームに入力した後、
チェックボックスにチェックを入れて
、次のステップ:タグ
をクリックします。
タグの追加は、何もせずにそのまま進みます。
最後に、AWSが指定しているロール名を入力して
ロールの作成
をクリックします。
7. SystemsManagerにアクセスするIAMポリシーをタスク実行ロールにアタッチする
前項ではタスク実行ロールを作成しました。
そのタスク実行ロールに対して、
SystemsManagerにアクセスするためのポリシーをアタッチします。
こちらもAWS公式ページのドキュメントがありますので、そちらに従って設定を行なっていきます。
まず、コンソールのIAM画面に移り、サイドバーよりポリシー
を押します。
新しいポリシーを作成するので、ポリシーを作成
をクリックします。
AWS公式ページにあるJSONコードをコピペしつつ、今回は使用しない
・secretsmanager
・kms
に関するコードを削除します。
なお、*(ワイルドカード)を使って3つパラメータを取得できるように細工します。
AWSアカウントIDは、自分のものに置き換えてください。
タグの追加はせずに、そのままクリックします。
任意のポリシー名称をつけて、概要欄から
SystemsManagerへの読取が許可されている権限を確認し、ポリシーを作成します。
続いて、たった今作成したポリシーをタスク実行ロールにアタッチします。
まず、IAMのサイドバーよりロール
をクリックします。
ロール一覧からecsTaskExecutionRole
をクリックします。
ecsTaskExecutionRoleの画面に移ったら、ポリシーをアタッチします
をクリックします。
最後に、
先ほど作成したSystemsManagerへのアクセスを許可するポリシー名で検索をかけて、
同ポリシーのチェックボックスを押して、
ポリシーのアタッチ
をクリックします。
8. ECS(タスク定義、クラスター、サービス)の作成
さあ、7.までの作業で一通り準備が終わりました。
ここからは、ECSのコンソール画面からインフラを構築していきます。
・タスク定義
・クラスター
・サービス
の順番で実装していきます。
タスク定義の作成
実装に入る前にタスク内でコンテナが起動すること
を念頭に置いておきましょう。
タスク定義: タスクに各種設定を定義するもの
その上で、タスク定義は
・コンテナはどのイメージから生成するか
・そのコンテナにはどのくらいCPUやメモリのリソースを割り振るか
・環境変数を取得するか
・ログはどこに収集するか
などを色んな設定を定義するものだと捉えます。
では、コンソール画面でも説明していきます。
AWSサービス一覧からECSを選択します。
サイドバーからタスク定義
を押します。
もちろんFargate
を選択します。
ページ中央までスクロールして、コンテナの追加
をクリックします。
(先にコンテナ登録しておいたほうが分かりやすいのでこの順序で進めてます。)
コンテナの登録画面が横から出てきますので
各種項目を埋めていきます。
まず、スタンダードの設定から着手します。
イメージには、ECS準備編でECRに登録したイメージ
を設定しています。
ECSがECRからイメージをpullしていることを想像しておきましょう。
メモリ制限は512
MiBとしています。
1つのRailsコンテナが512MiB使用すると考えましょう。
ポートは、HTTPの80
番ポートを解放しています。
続いて、詳細コンテナ設定です。
ヘルスチェックはALBで設定済みなので、ここでは設定しません。
環境は、CPUユニット数と環境変数のみ編集します。
CPUユニット数は256
としています。
メモリと同様にRailsコンテナが256のリソースを使うことを想像します。
環境変数は、SystemsManagerで登録した秘匿情報をTaskが読み取るものです。
最終的にRailsに渡すイメージで良いと思います。
以下の点だけ注意して登録を行いましょう。
・Key:Railsのdatabase.ymlで展開する環境変数名と一致させる
・ValueFrom:SystemsManagerの登録名と一致させる
あとは、ログ収集に関する設定です。
デフォルトでAuto-configure CloudWatch LogsがONになっているはずです。
そのまま変更は加えずに、下にスクロールして追加
ボタンを押してコンテナを登録します。
すると、元のタスク定義の画面に戻ります。
上から順番にプルダウンを選択していきます。
ここで事前に作成しておいたタスク実行ロール
を紐付けます。
続けて、タスクサイズの設定を行います。
コンテナをまとめるタスクの設定です。
先ほど、Railsコンテナ側では
・メモリ:512
・CPU:256
に設定しています。
それに対して、タスクサイズは
・メモリ:1GB
・CPU:0.5vCPU
としています。
当記事では1つのコンテナしか作成しませんので
現状は意味はありませんが、タスクの残りメモリおよびCPUに余裕があるので
VueやNginxの別コンテナを追加する構成にしても対応できるような設計としています。
棒グラフからもタスクのメモリ/CPUがRailsコンテナで半分は使用されていることが確認できます。
と理解しておいて、下にスクロールして作成
を押して
タスク定義を完成させましょう。
クラスターの作成
次は、タスクを配置するクラスターの作成です。
サイドバーよりクラスター
を選択します。
続けてクラスターの作成
を押します。
クラスターテンプレートは、Fargateなのでネットワーキングのみ
を選択します。
障害時の原因を掴むために活用できる
CloudWatch Container Insightsにチェックを入れて、作成を押します。
サービスの作成
最後にサービスの作成となります。
ここでは、ECSとALBが連携して動作していることを意識しましょう。
それではコンソール画面を参照しながら説明します。
まず、ECSのサイドバーよりクラスター
を押して、
先ほど作成したクラスター名をクリックします。
対象のクラスター画面に移動したら、
そこで作成
を押して、サービス作成画面に入ります。
サービスの設定は、以下の通りに入力していきます。
項目 | 値 |
---|---|
起動タイプ | Fargate |
タスク定義 | app-backend-def(先ほど作成したもの) |
リビジョン | latest |
クラスター | app-backend-cluster(先ほど作成したもの) |
サービス名 | 任意の名称 |
タスクの数 | 1 |
リビジョンは、タスク定義のバージョンを表すもので
タスク定義を更新すると数字の順番で増えていきます。
最新バージョンにはlatestがつきます。
タスクの数は起動させる数なので、可用性をあげたい場合は
数を増やして設定します。
デプロイメントは、blue/greenデプロイメントを選択し
他項目はデフォルト通りで、次のステップをクリックします。
VPC・サブネット・セキュリティグループは、
ECS準備編のネットワーク構築で作成したものを選択します。
セキュリティグループの編集
を押すと
横から設定画面が出てきますので、既存のセキュリティグループの選択
から
作成済みのセキュリティグループにチェックをつけて保存します。
ヘルスチェックの猶予期間は、ロードバランシング欄を完了しないと
設定できませんので、注意してください。
先に、ロードバランサーの種類でALBを選択して、
プルダウンで作成済みのロードバランサーを選択します。
その後に、ヘルスチェックの猶予期間を設定するのですが、
長めに300秒(5分)としています。
この5分の間に、ALBで設定した
・成功とみなす連続回数で
・成功コードでレスポンスされれば
チェックがクリアとなり、タスクを配置してくれます。
続いて、ロードバランサーに追加
をクリックします。
ここでは、ALBに設定している
・リスナーポート
・ターゲットグループ
を設定していきます。
プロダクションリスナーポートは、一般ユーザー用のリスナーとして
80:HTTPを選択します。
テストリスナーのチェックボックスにチェックを入れて
テストリスナーポートは、別の10080ポートで設定します。
ターゲットグループは、blueとgreenのターゲットグループを設定します。
ALBがタスクセットする流れの整理(参考)
■ 1回目のデプロイ:
切り替えする元のタスクが存在しないので、blueターゲットグループのみにタスクセットされる
■ 2回目のデプロイ:
blueターゲットグループ内に存在する正常タスクと
新しく生成されるgreenのターゲットグループ内にセットされたタスクを切り替えされる
■ 3回目のデプロイ:
greenターゲットグループ内に存在する正常タスクと
新しく生成されるblueのターゲットグループ内にセットされたタスクを切り替えされる
サービスの検出は、必須ではないですが、
設定するタイミングがサービス作成時しかないので
念のため、作成しておきます。
オートスケーリングは非常に便利な機能ですが、
後でも編集可能なので、ここではそのまま進めます。
最後に、これまでの設定が上手くできているか一通り確認して
サービスの作成
を押します。
以上で、全ての作業が完了です。
これでサービスが自動的にタスクを動作されます。
ECS画面で
サイドバーのクラスター → 対象サービス名 → イベントのタブ とクリックし
service 対象サービス名 has reached a steady state.
のメッセージが
表示されていれば、タスクが正常に起動できています!!!
コンテナ起動の確認
最後に起動できたタスクにアクセスして、アプリが表示されるか確認しましょう。
ALBで自動生成されているDNSをブラウザにコピペ入力して
アクセスできるかを確かめます。
上手くいかない時
大変ですが、デバッグしましょう…
私自身、めちゃくちゃエラーが発生しましたので、
エラー文を拾って、ひたすら修正→起動…を繰り返してました。
デバッグについては、別記事でまとめておりますので
宜しければ、参考にしてください。
参考記事
続編
終わりに
画像をキャプチャーしてコメント入れての作業が多すぎてツリそうでした…
terraformでインフラ構成できるようになれば、
記事ももっとアッサリしてるだろうに…笑
こんな長い記事を最後までお読み頂き、ありがとうございました!!