Auto ScalingとS3で可用性の高いWebサーバーを作ってみよう

記事タイトルとURLをコピーする

この記事は約7分で読めます。

この記事では、Amazon EC2のAuto Scaling機能とAmazon S3を用いて、比較的安価に可用性の高いウェブページをデプロイする方法を紹介します。 今回は以下のような環境を作ってみます。

今回の構成

前提知識

VPC、EC2、ALB、IAM、S3の基本的な知識(触ったことがある、程度でOK)

なぜAuto Scalingを使うのか?

Auto Scalingは、複数AZ(アベイラビリティゾーン)に跨って自動でEC2インスタンスを起動・削除できる機能です。 Auto Scalingを使うことで、インスタンスを需要に応じて増減させ、コストを抑えつつ必要十分なだけのリソースを確保することができます。

例1)Webサーバーに対し、特定の時期や時間帯だけアクセス数が急増しており、サーバーに負荷がかかっている

→インスタンス台数をスケジューリングし、アクセスが増える時間帯だけ増やすことで、需要に合わせたリソースを確保

例2)予測できないアクセス増が見込まれる

→自動スケーリングを設定して、急なアクセス増加時もサーバーダウンを回避

Auto Scalingを用いる際は、AMI(Amazonマシンイメージ)と呼ばれるイメージを事前に準備し、それを元に新規サーバーを自動で起動します。 そのため、環境構築やコンテンツの更新には工夫が必要となります。

本記事では、S3バケットにコンテンツをアップロードし、Auto Scalingで作成されたインスタンスがバケット内のコンテンツを同期するような設定を行うことで、比較的安価でコンテンツ更新をしやすい環境の構築方法を紹介します。

やり方

コンソールから構築していきます。

VPC作成

まずはVPCを立ち上げます。

今回は以下のように、2AZ構成で構築します。コンソールの作成画面で「VPCなど」をオプションをクリックすると、関連リソースが一気に作成できて便利です。

  • 名前:auto-scaling-test-vpc
  • パブリックサブネット2つ(2AZ)
  • プライベートサブネット2つ(2AZ)
  • インターネットゲートウェイ
  • S3用のエンドポイントを作成

「VPCなど」オプションを選択していっぺんに作成

また、後ほどの検証でセッションマネージャーからログインするため、パブリックサブネットにNATゲートウェイを設置し、プライベートサブネットのルートテーブルにこのNATゲートウェイへのルートを追加しておきます。 (利用料がそこそこかかるので、NATゲートウェイやそこに紐づいたElastic IPは検証後に消すのをお忘れなく!)

S3バケット作成

続いて、コンテンツ格納用のS3バケットを作成します。

S3バケットの名前は世界で一意である必要があります。今回は以下のように命名します。

  • auto-scaling-test-contents-(AWSアカウントID)

その他の設定はデフォルトで大丈夫です。

S3バケットを作成

作成後、適当な内容のindex.htmlをアップロードしておきます。今回は以下のような内容のものを入れておきます。

<html>
<body>
Auto Scaling Test
</body>
</html>

IAMロール作成

次に、EC2インスタンス用のIAMロールを作成します。

デフォルトの状態では、EC2インスタンスにはS3へのアクセス権限が与えられていません。なので、今回の構成で作る各インスタンスがコンテンツを取得できるよう、S3へのアクセス権限を与えるIAMロールを作成します。

ユースケースは「EC2」、許可ポリシーはAmazonS3ReadOnlyAccessと、後の検証のためにAmazonSSMManagedInstanceCoreも選択しておきます。 ロール名は今回はauto-scaling-test-role-ec2としておきます。

ユースケースの選択画面

AMI作成用のインスタンス(ゴールデンイメージ)を起動

Auto Scalingの設定に必要なAMIを取得するためのEC2インスタンスを起動します。 今回は検証のため、小さめのインスタンスで起動していきます。

以下のように設定を変更して起動します。他は全てデフォルトで大丈夫です。

  • 名前:auto-scaling-test-gi
  • インスタンスタイプ:t3.micro
  • キーペアを作成or選択
  • VPC:auto-scaling-test-vpc
  • サブネットはいずれかのパブリックサブネットを選択
  • パブリックIP:有効化
  • セキュリティグループは新規に作成する
    • 名前:auto-scaling-sg-gi
    • インバウンドルール:自分のIPからのSSH接続を許可
  • 「高度な詳細」欄を開き、「IAMインスタンスプロフィール」からauto-scailng-test-role-ec2を選択

セキュリティグループはインスタンス作成画面から同時に作成できます

「高度な詳細」を展開して設定します

内部作業

AMI取得用インスタンスの起動が完了したら、SSH接続してコンテンツ用S3バケットからコンテンツを同期するための設定を行います。

本記事ではAmazon Linux 2023とApacheを用いたやり方を紹介しますが、他のOSやWebサーバーアプリケーションを用いる場合でも、AWS CLIのS3 syncコマンドでS3の内容を定期的にドキュメントルートにコピーできればOKです。

Apacheの導入

# Apacheをインストール
$ sudo yum install -y httpd

# 自動起動をオンにする
$ sudo systemctl enable httpd.service

# デフォルトのDocumentRootに移動
$ cd /var/www/html

# 空のインデックスページを作成(ALBのヘルスチェックを通すため)
$ sudo touch index.html

Cronieの導入

S3と同期するシェルスクリプトを定期的に実行させる設定を行います。今回は簡単に実装するため、cronを使用します。 Amazon Linux 2023にはcronが標準搭載されていないため、Cronieのインストールから行います。(AWSアカウントID)の部分は実際のものに書き換えてください。

# Cronieのインストール
$ sudo yum install cronie -y

# ディレクトリ移動
$ cd /usr/local/bin/

# 起動時に同期するためのシェルスクリプトを作成
$ sudo touch s3sync_boot.sh

#シェルスクリプトを編集
$ sudo vim s3sync_boot.sh

# 以下をコピペし、保存する。(AWSアカウントID)の部分は実際のものに書き換える
sudo aws s3 sync --exact-timestamps s3://auto-scaling-test-contents-(AWSアカウントID)/var/www/html
sudo service httpd restart

# 定期的に同期するためのシェルスクリプトを作成
$ sudo touch s3sync_scheduled.sh

# シェルスクリプトを編集
$ sudo vim s3sync_scheduled.sh

# 以下をコピペし、保存する
sudo aws s3 sync --exact-timestamps s3://auto-scaling-test-contents-(AWSアカウントID)/var/www/html
sudo service httpd reload

# cronで実行できるように、シェルファイルの権限を変更
$ sudo chmod 755 s3sync_boot.sh
$ sudo chmod 755 s3sync_scheduled.sh

※小ネタ

aws s3 syncコマンドを用いた際は、変更があったオブジェクトのみが同期されます。ここで、変更後のオブジェクトのディレクトリ・オブジェクト名・サイズが完全に一致していると、変更されていないオブジェクトとみなされ、仮に内容が違っていても正常に同期されません。これを防ぐために、--exact-timestampsオプションを用いることで、オブジェクトの更新日時を参照して変更の判定を行うことができます。

crontabの編集

今回は起動時および5分間隔でS3バケットと同期する設定を行います。

# cronの設定ファイルを開く
$ crontab -e

# 以下をコピペして保存
@reboot /usr/local/bin/s3sync_boot.sh
*/5 * * * *  /usr/local/bin/s3sync_scheduled.sh

ここで、S3との同期がうまくいっているか確認してみましょう。インスタンスを再起動し、以下のコマンドを入力してみます。同期設定がうまくいっていれば、空のページではなく、先ほどS3バケットにアップロードしたindex.htmlの内容が表示されるはずです。

$ curl localhost

S3との同期に成功している時の画面

AMIの取得

インスタンス一覧から上で作成したインスタンスを選択し、「アクション」→「イメージとテンプレート」からAMIを取得します。

名前はauto-scaling-test-amiとしておきます。

ここからAMIを作成できます

ALB作成

Auto Scalingで起動するインスタンスを関連付けるためのALB(Application Load Balancer)を作成します。

ALB用のセキュリティグループ

事前準備として、ALB用のセキュリティグループを以下の設定で作成します。

  • 名前:auto-scaling-test-sg-alb
  • 説明:for test
  • VPC:auto-scaling-test-vpc
  • インバウンドルール
    • タイプ:HTTP
    • ソース:Anywhere IPv4
  • アウトバウンドルール
    • タイプ:すべてのトラフィック
    • 送信先:0.0.0.0/0

ALB本体

続いてALB本体を、以下の設定で作成します。

  • ロードバランサー名:auto-scaling-test-alb
  • VPC:auto-scaling-test-vpc
  • サブネット:作成した2つのパブリックサブネット
  • セキュリティグループ:auto-scaling-test-sg-alb
  • ターゲットグループは名前をauto-scaling-test-alb-tgとし、ターゲットを登録せずに作成

起動テンプレート・Auto Scalingグループ作成

セキュリティグループ

こちらも事前に、Auto Scalingグループ用のセキュリティグループを作成しておきます。

  • 名前:auto-scaling-test-sg-asg
  • 説明:for test
  • VPC:auto-scaling-test-vpc
  • インバウンドルール
    • タイプ:HTTP
    • ソース:ALBのセキュリティグループ

起動テンプレート

起動テンプレートを、先ほど作ったAMIを使って作成します。

  • 名前:auto-scaling-test-template
  • AMIは先ほど作ったauto-scaling-test-amiを指定
  • インスタンスタイプ:t3.micro
  • キーペアは先ほど作ったものを指定
  • サブネットは設定なし
  • セキュリティグループ:auto-scaling-test-sg-asg
  • 「高度な設定」→「IAMインスタンスプロフィール」からauto-scaling-test-role-ec2を選択

Auto Scalingグループ

続いて、このテンプレートを用いてAuto Scalingグループを作成します。

  • 名前:auto-scaling-test-asg
  • 起動テンプレート:auto-scaling-test-template
  • バージョンはLatestを選択
  • VPC:auto-scaling-test-vpc
  • サブネットは2つのプライベートサブネットを選択する
  • アベイラビリティゾーンのディストリビューションは「バランシング(ベストエフォート)」を選択
  • 「ロードバランシング」欄は、以下のようにします
    • 「既存のロードバランサーにアタッチする」、「ロードバランサーのターゲットグループから選択する」をそれぞれ選択
    • ターゲットグループはauto-scaling-test-alb-tgを選択
  • VPC Lattice、ARCゾーンシフトは今回はなし
  • 「Elastic Load Balancing のヘルスチェックをオンにする」にチェックを入れる
  • グループサイズ
    • 今回は検証なので、希望するキャパシティ1、最小容量1、最大容量2としておく

スケーリングポリシーについては、予測できるアクセスがある場合は「スケーリングポリシーなし」を選び、作成後にスケーリングをスケジュールします。

予測できないアクセス増がある場合は「ターゲット追跡スケーリングポリシー」を選び、自動スケーリングを設定します。 サーバーの処理能力がパンクしがちな場合はCPU使用率を閾値に、アクセスが多すぎて帯域がパンクしがちな場合はロードバランサーのリクエスト数を閾値にする、など状況に応じて設定を変えてみましょう。

今回は「平均CPU使用率」、ターゲット値は「50」としてみます。

スケーリングポリシーの設定

しばらく待つと、自動でインスタンスが1つ立ち上がります。 (今回は名前タグを設定していないので、インスタンス名がついていないものが起動します。)

インスタンスが起動

アクセス・負荷テストをしてみよう

アクセステスト

これで一通りの環境は完成です。実際にアクセスしてみましょう。

ALBのDNS名をコピペし、HTTPで接続してみます。

赤枠の箇所に記載のDNS名を使います

上手くいっていれば、先ほどS3バケットにアップロードしていたindex.htmlの内容が表示されます。

接続成功時の画面

S3バケットの内容を変えたりして、コンテンツが自動的に同期されるかどうかも確認してみてください。

自動スケーリングのテスト

試しに、立ち上がっているインスタンスを終了(削除)してみましょう。数分待つと、新しいインスタンスが自動で立ち上がります。

インスタンスに負荷をかけ、追加のインスタンスが自動で起動するかどうかも試してみましょう。 1つだけ起動しているインスタンスにセッションマネージャーからログインし、次のコマンドでCPU稼働率を100%にしてしばらく待ちます。

yes > /dev/null

しばらくすると、インスタンスが2個に増えます。yesコマンドをCtrl+Cで中断して再度しばらく待つと、インスタンスの個数は1個に戻ります。

設定が正しければ、自動的にもう1つ起動します

あとがき

今回の構成は、弊社サーバーワークスで私が模擬案件という研修を受けた際に構築したものでした。 当時の私はLinuxやAWSにまだあまり馴染んでいなかったので、この「自動でスケールするWebアプリ環境」を作るための構成と手順を用意するのにかなりの時間がかかってしまいましたが、まったくの素人からは一歩抜け出せたのかな?と思っています。

模擬案件やその他の研修の様子は弊社のもう1つのブログ「サバワク」で私の同期たちがレポートしていますので、そちらの記事も是非ご覧ください!

sabawaku.serverworks.co.jp

伊藤 風輝(執筆記事の一覧)

カスタマーサクセス部

2024年入社のエンジニアです。