前回の記事で予告したように、今回は複製元EC2と複製したEC2を使った負荷分散環境の構築に関して書きたいと思います。
複製したEC2(本記事では2号機とします)は元EC2(1号機とします)から作成したAMIをベースに生成したものです。
なお、過去記事でもしばしば触れてきましたが、私が扱うシステムはフロントエンドをNuxt、バックエンドをLaravelで構築し、両者はAjax(axios)で通信するのみで基本的にはそれぞれ独立した環境で運用する構成になっています。特にAWSにおいてはフロントエンドをS3の「静的ウェブサイトホスティング」機能で運用し、バックエンドのみをEC2で運用します。
また、バックエンドはフロントエンドのサブドメインに属する形にしてあります。例えばフロントエンドのドメインが「sample.co.jp」であれば、バックエンドは「api.sample.co.jp」と言ったサブドメインにしてあると言うことです。
開発中においてはフロントエンド、バックエンドをそれぞれ異なったマシン環境で開発しているケースが大半です。例えば、フロントエンドはIPが「192.168.0.1」のマシン、バックエンドはIPが「192.168.0.2」のマシンで開発すると言うような形になります。このような環境下において、それぞれを単体的に動作検証している段階は良いですが、両者を結合して動作検証を行いたい場合に両者が同一ドメインに属していると不都合が生じます。
例えばhosts等で「sample.co.jp」が「192.168.0.1」に名前解決されるようにしていた場合、同ドメイン名でバックエンド「192.168.0.2」にはアクセスできません。
逆に「sample.co.jp」が「192.168.0.2」に名前解決されるようにしてバックエンドにアクセスできるようにしておいて、フロントエンドには別名(例えば直接IPアドレスを指定するなど)でアクセスすると言う形にすれば両者へのアクセスが成立するように思えますが、これはCORSのルールに反するためフロントエンドからバックエンドへのAjax通信がエラーになります。
無論、バックエンド側で「Access-Control-Allow-Origin」をこまめに書き換えると言うようなことをすれば対応できなくもないですが、普通はそんな面倒なことはしたくないと思います。よって、フロントエンドを「sample.co.jp」バックエンドを「api.sample.co.jp」のようにドメインを分け、バックエンド側で「Access-Control-Allow-Origin」として「sample.co.jp」からのアクセスを許容するように設定しておけば、フロントエンドを動かす環境が複数あった場合でも、それぞれの環境で適切にhosts等で名前解決すれば問題なく動作させることができます。
以下の内容は上記のような環境構築を目指していることが前提となります。
EC2前提条件
今まで特に触れてきませんでしたが、実は将来的な負荷分散環境下での運用を考慮した設定を1号機に対して実施していました。
具体的には以下のような内容になります。
- セッション管理にDB(RDS)を使用
- アップロードファイルの保存先はS3
セッション管理はデフォルトではファイルを使用して行われるようになっていますが、負荷分散環境では個別サーバ環境に依存した管理方法は採用できません。Laravelとしてはセッション管理の方法がいくつか提供されており、memcachedやREDISを使用する方法もありますが、AWS環境で簡単に構築できる方法としてDBを使用する方法を採用しました。
また、同様にアップロードされたファイルに関しても負荷分散するEC2全体で共有できる必要があります。Laravelでは標準的なファイル管理機能が提供されており、同機能がファイルの実体をどのように扱うかに関していくつかの方法から選択できるようになっています(デフォルトでは当該サーバのディスク環境を使用する「local」が設定されています)が、S3を使用する方法も提供されていますので、これを使用することにしました。
これらの前提は複製されたEC2にも引き継がれることになります。
2号機での設定追加・変更
複製の段階で大半の設定は1号機から引き継がれますが、一部個別に設定する必要があるものや、逆に2号機固有の設定に書き換える必要があるものがあります。
具体的には以下のものになります(現時点で気付いているもの、と言うことになりますが)。
- Apacheの設定ファイルにおけるIP書き換え
- /etc/hosts書き換え
- IAMロールの設定
Virtualminを使用して環境構築していると言うことも関係しているかと思いますが、Apacheの当該ドメイン関連の設定ファイル「/etc/apache2/sites-available/<ドメイン>.conf」内では自身のローカルIP指定のアクセスを処理するような内容になっています。
単に複製しただけの状態ではこのローカルIPが1号機のままになっていますので2号機のローカルIPに書き換える必要があります。
/etc/hostsに関してもVirtualminが自動的に自ドメイン名とローカルIPの関連付けを設定しているようで、ここも当然ながら1号機の内容のままなので、2号機のローカルIPに書き換えます。
本ファイルに関しては運用するシステムの処理内容次第では不要かもしれませんが、今回構築しようとしているシステムでは同ファイルの内容に依存するような処理があったため、積極的に書き換える必要がありました。
IAMロールに関してはAMI生成元のEC2に設定してあった内容が引き継がれないようです。AMI(マシン環境を構築するためのイメージ)が同じだからと言って、そのインスタンスの役割まで同じになるとは限らないので当然のような気もしますが。
既に1号機用に作成したIAMロールが存在しているので、EC2のコンパネで2号機を選択し、「アクション」の「セキュリティ」から「IAMロールの変更」を選択して表示される「IAMロールを変更」画面で当該ロールを選択すれば良いだけです。
ELB(ALB)設定
ロードバランサーに関してはあくまでEC2付属の機能として存在しているようです。
EC2コンパネのメニューから「ロードバランサー」を選択します。
最初にALB(Application Load Balancer)かNLB(Network Load Balancer)かの選択があります。これはL7/L4のいずれのロードバランシングを行うかと言うことのようです。
個人的には過去にL4ロードバランサーしか使ったことがなく、L7のありがたみが良くわかっていない(一方でアクセス元IPが見え難くなるなどの弊害はなんとなく知っている)のですが、どうもALBの方が推奨されているようですし、せっかくですのでALBを選択します。
以降、以下のような設定を行います。
名前 | 任意(分かりやすいもの) |
ロードバランサーのプロトコル | HTTPS |
サブネットの選択 | 1,2号機それぞれが属するサブネット(別AZ) |
証明書 | 別途ACMで証明書を作成しておき、これを選択 |
セキュリティグループの設定 | 新しいセキュリティグループを生成し、HTTPSに対してソース「0.0.0.0/0」を設定 |
ターゲットグループ設定 | グループとして任意の名称を付け、ターゲットとして1,2号機を指定 |
少々雑な説明ですが、この辺の設定内容や画面の様子はネット情報を見ても頻繁に変わるようなので、とりあえずこの程度の内容にしておきます。
名前解決
上記で構築したALBに対してインターネットから接続できるようにすれば負荷分散環境構築としては完了です。
ドメインとしては最初の方で触れたように当該システムのドメイン(例:sample.co.jp)に対するサブドメイン(例:api.sample.co.jp)としますが、ベースとなるドメインのRoute53設定ができていれば、そこにレコードを追加するだけです。
具体的には以下のようなレコードを追加します。
レコード名 | ドメイン名(api.sample.co.jp) |
タイプ | Aレコード |
エイリアス | Yes |
エイリアス先 | 先に生成したALB |
上記の設定ができれば、ドメイン(api.sample.co.jp)に対するアクセス先がALBになり、そこからターゲットとして指定されている1,2号機にアクセスが割り振られることになります。
総括
ある程度周辺環境の構築が済んでいると言う前提であれば、AMIから複数EC2インスタンスを作成し、負荷分散環境を構築することは簡単にできそうです。今回の記事では触れませんでしたが、ALBにおけるターゲットの追加・削除も簡単で、システムの規模変更も柔軟に行えそうです。
なお、今回あえて触れなかった課題としては、個別のEC2で採取したログをどのように統合するかと言うものがあります。
ログに関しては不具合の解析や運用状況の把握など様々な目的で見たくなります。
Laravelでは「storage/logs」配下にログを残せますが、必要な設定やログ採取処理の埋め込みが済んでいれば、今回構築した環境でも既にログの採取は行われています。
しかし、それだけでは複数台のEC2でそれぞれに対するアクセスに関するログを個別に残しているだけなので、一連の処理の流れを俯瞰的に見ることが難しい状態になっています。また、特定のアクセス(アクション)に関するログに関しても、それが処理されたEC2のログを見る必要があるため、各EC2のログを順番に見て行きながら該当する記録を探す必要があり、かなり面倒です。
AWSではCloudWatchという機能があり、複数台のEC2で採取されたログを集約的に管理できるようなので、次回はこの辺を見て行きたいと思います。