はじめに
こんにちは、そしてこんばんは!
クラウドインテグレーション事業部の大嵩です。
アプリケーションの処理が滞留した際に、Amazon EC2 Auto Scaling グループ(以下ASG)の台数が不足し、処理が止まってしまうことがありました。
今回は、そんなときに台数不足を自動で解消する仕組みを検証した内容をご紹介します!
どういった状況が起きたのか
Apache + Tomcat の環境にて、想定を超えるユーザーの利用によりトラフィックを処理しきれず、Apache側で処理が停止する事象が発生しました。
その際に受け口を増やすため、手動でASGの最小台数を増加させることで解消しました。
これを自動化できないか考えたのが、今回の仕組みです。
今回の構成
少々条件は変わってしまいますが、以前のブログで使用した環境を流用してLAMP環境にて検証します。
簡単な構成図は以下のとおりです!

仕組みの概要
以下のような流れで実装します。
- ASG配下のAmazon EC2にAmazon CloudWatch Agentを導入し、Apache接続数のカスタムメトリクスを送信する
- カスタムメトリクスを閾値とするAmazon CloudWatch アラームを設定
- このアラームをもとにした動的スケーリングポリシーをASGに設定
- Amazon EC2内でコマンドを実行し、接続数を増やすことで台数変動を検証する
まずは接続を確認する
下記のようにASGを作成し、最終的にApplication Load Balancer(以下ALB)からの接続を確認します。
ALBの作成は割愛します。

このように、ブラウザからのアクセスが確認できました!

Apacheの接続状況を取得するための設定を実施する
無事に動作が確認できたところで、httpd.conf を編集していきます。
まず、情報取得のために必要な mod_status モジュールを有効化します。
# httpd.conf から抜粋 # # Dynamic Shared Object (DSO) Support # # To be able to use the functionality of a module which was built as a DSO you # have to place corresponding `LoadModule' lines at this location so the # directives contained in it are actually available _before_ they are used. # Statically compiled modules (those listed by `httpd -l') do not need # to be loaded here. # # Example: # LoadModule foo_module modules/mod_foo.so # Include conf.modules.d/*.conf LoadModule status_module modules/mod_status.so # 追記 Include conf/extra/httpd-info.conf # 追記
入力できたら、Includeに指定した「httpd-info.conf」を作成します!
# /etc/httpd/conf/extra/httpd-info.conf
<Location /server-status>
SetHandler server-status
Require local
</Location>
ここまで作成ができたら、httpdを再起動しましょう!
その後、localhostに対してcurlしてみると、BusyWorkersの値が取れていることがわかります!
[ec2-user@ip-10-0-4-177 ~]$ curl http://localhost/server-status?auto | grep BusyWorkers
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1184 100 1184 0 0 575k 0 --:--:-- --:--:-- --:--:-- 1156k
BusyWorkers: 1
[ec2-user@ip-10-0-4-177 ~]$
ここでApacheの設定は完了ですね!
Amazon CloudWatch Agentを導入する
Apacheの設定ができたので、ここからはAmazon CloudWatch Agent(以下CWA)を導入していきます。
ドキュメントのとおりインストールします。(インストール手順は割愛します。)
今回はカスタムメトリクスの送信を検討していたところ、CWAはcollectdからメトリクスを受信できることがわかりました。collectdにはApacheプラグインがあるため、これを活用します!
collectd をインストール
CWAはインストール済みですが、collectdがまだないのでインストールします。
[ec2-user@ip-10-0-4-177 ~]$ sudo yum install collectd collectd-apache -y Last metadata expiration check: 2:00:58 ago on Thu Dec 25 02:18:37 2025. Dependencies resolved. ============================================================================================================================================================================================ Package Architecture Version Repository Size ============================================================================================================================================================================================ Installing: collectd x86_64 5.12.0-16.amzn2023.0.4 amazonlinux 690 k collectd-apache x86_64 5.12.0-16.amzn2023.0.4 amazonlinux 18 k Installing dependencies: yajl x86_64 2.1.0-16.amzn2023.0.5 amazonlinux 39 k Transaction Summary ============================================================================================================================================================================================ Install 3 Packages
インストールができたら、情報取得のためcollectd.confを修正します。
主に以下の修正が必要です。
# コメントを外す
LoadPlugin apache
LoadPlugin network
# 修正する
<Plugin apache>
<Instance "local">
URL "http://localhost/server-status?auto"
</Instance>
</Plugin>
<Plugin network>
<Server "127.0.0.1" "25826">
</Server>
</Plugin>
修正が終わったら、collectdを再起動し、自動起動を有効にしておきましょう!
次にCWAの設定ファイルを書く
collectdの用意ができたら、次はCWAの設定ファイルを書きます。
今回はCPU使用率などは割愛し、Apacheの状態をカスタムメトリクスとして送信します。
# /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
{
"agent": {
"metrics_collection_interval": 60
},
"metrics": {
"append_dimensions": {
"AutoScalingGroupName": "${aws:AutoScalingGroupName}", // ASG名で集計するために必要
"ImageId": "${aws:ImageId}",
"InstanceId": "${aws:InstanceId}",
"InstanceType": "${aws:InstanceType}"
},
"aggregation_dimensions": [["AutoScalingGroupName"]], // ASG単位で集計
"metrics_collected": {
"collectd": {
"metrics_aggregation_interval": 60,
"service_address": "udp://127.0.0.1:25826", // collectdの送信先と合わせる
"collectd_security_level": "none"
}
}
}
}
書けたらCWAを起動し、設定ファイルの有効化コマンドを実行しましょう!
注意!
ここで私も失念していましたが、Amazon EC2のIAMロールにメトリクス書き込み用のポリシーをアタッチする必要があります!
これがないと、CWAからメトリクスの送信ができません!
- CloudWatchAgentServerPolicy
実際にカスタムメトリクスを確認する
問題なくCWAが動いていたら、Amazon CloudWatchのコンソールから確認してみましょう!
メトリクスから、Agentの名前空間が新しく追加されているはずです!
見てみると、collectd_apache_value として取得が確認できました!!!

また、collectdで取得したApacheメトリクスの中には下記が内包されています。
- 総リクエスト数(Total Accesses)
- 総転送バイト数(Total kBytes)
- 現在のリクエスト数(現在処理中のリクエスト数)
- 稼働中の worker プロセス/スレッド数(BusyWorkers)
- 待機中の worker プロセス/スレッド数(IdleWorkers)
- 各 worker の状態(Reading, Writing, Keepalive, DNS lookup など)
- サーバー起動からの稼働時間(Uptime)
- CPU 使用時間(CPU User / CPU System など)
- 1秒あたりのリクエスト数(ReqPerSec)やバイト数(BytesPerSec, BytesPerReq)といった算出値
- 接続状態別のカウンタ(Waiting for Connection, Starting up, Reading Request など)
Amazon CloudWatch アラームを設定する
カスタムメトリクスの取得が確認できたところで、次はアラームの作成に移ります。
先ほどのグラフからそのままアラームを作成してみます。

これで完成!と思ったらよく見てみてください。
閾値は問題ないのですが、インスタンスIDまで指定してしまいました。
この設定だと、指定したインスタンスのメトリクスしか参照しない状態になり、ASG全体に対するApache接続数の監視になりません。
そこで、インスタンスIDの指定を外します!

下記のように、「collectd_apache_value」のうちtypeが「apache_connections」の平均を見る形に変更しました。
SELECT AVG(collectd_apache_value) FROM CWAgent WHERE AutoScalingGroupName = 'otake-test-lamp-asg' AND type = 'apache_connections'

Amazon EC2 Auto Scaling グループに動的スケーリングポリシーを設定する
アラームが作れたら、ようやくASGにスケーリングポリシーの追加ができます。
今回は、ステップスケーリングポリシーを使って実装します。
「シンプルなスケーリング」とは違い、メトリクスで取得した接続数に応じて段階的にインスタンスを増加させられます。
今回は単純に1段階のみで設定しますが、必要に応じて段階を増やすことも可能です!
スクリーンショットではインスタンスのウォームアップを60秒としていますが、デプロイしているアプリケーションによってはここを長くしないと動作が不安定になる場合があります。

Apacheの接続数を増加させて挙動を確認する
それではお待ちかね、ASGの台数が増えるのか確認しましょう!
検証用のPHPスクリプトを生成AIで作成しました。
# /var/www/html/sleep.php <?php // 60秒間、処理を停止(スリープ)します // この間、Apacheはこの接続を維持し続けるため、コネクション数としてカウントされます sleep(60); echo "Wake up!"; ?>
そうです、60秒sleepして待機させます!
このままだとブラウザから「/sleep.php」に複数のタブでアクセスしないといけないので面倒です。
そこで、ASGで作成されたインスタンス内部からワンライナーで接続数を一気に増やします!
for i in {1..10}; do curl -s "http://localhost/sleep.php" > /dev/null & done
ブラウザからアクセスするのが構成上は正しい検証方法です。ただし今回はメトリクス値の変動に対する反応を検証したいため、インスタンス内部から実行します。
コマンドを実行
実際にコマンドを実行します。
バックグラウンドで待機させているので、少々待ちます…
実行が完了したら、server-statusからBusyWorkers数を確認すると、11に変動していることがわかります。
sh-5.2$ for i in {1..10}; do curl -s "http://localhost/sleep.php" > /dev/null & done
[1] 3270
[2] 3271
[3] 3272
[4] 3273
[5] 3274
[6] 3275
[7] 3276
[8] 3277
[9] 3278
[10] 3279
sh-5.2$
sh-5.2$ curl -s http://localhost/server-status?auto | grep "BusyWorkers" | awk '{print $2}'
11
sh-5.2$
スケールの様子は…?
コマンド実行後にコンソールを見てみると、すでに2台増えていました!
カスタムメトリクスをもとにスケーリングポリシーが正しく動作してくれていますね!

少し待つと、最大数に設定した4台まで正常に増加していることが確認できました!

AWS CloudTrailも確認してみる
実際にインスタンスが追加されていることの裏付けとして、イベント履歴を確認してみました!
スケールアウト分と初期起動の1台を合わせて、4個のRunInstancesイベント発生が確認できました。

アラームも確認する
アラーム側では平均値を取っているため、個々のインスタンスとは値が異なりますが、しっかりと閾値超過を確認できました。

スケールインは…?
お気づきの方もいらっしゃると思いますが、そうです。
この状態では、スケールアウトしかしません。
そのため、スケールインするためのAmazon CloudWatch アラームとポリシーを別途作成する必要があります。
閾値は同様に設定し、アラーム状態とする条件を「OK」にします!


また、急に1台まで減らしてしまうとリスクがあるため、段階を踏んで台数を減らす設定にしておくのが安心です。
スケールアウト/インどちらも割合で指定できるため、柔軟な設定が可能です。

インスタンスを確認
スケールインを待っていると、いつの間にか最小台数まで減っていることを確認しました!

増加時と同様に、3台分のTerminateInstancesイベント履歴も確認できました!

以上で、Apacheの接続数をもとにASGの台数をスケールアウト/インする仕組みの紹介でした!
まとめ
今回は、Apacheの接続数(BusyWorkers)をカスタムメトリクスとしてAmazon CloudWatchに送信し、その値をもとにASGの台数を自動で増減させる仕組みを検証しました。
中でもポイントは下記です!
- collectdのApacheプラグインとAmazon CloudWatch Agentを組み合わせることで、Apacheの接続数をカスタムメトリクスとして簡単にAmazon CloudWatchへ送信できる
- Amazon CloudWatch アラームのメトリクスクエリではインスタンスIDを外してASG単位で集計するのがポイントで、ここを間違えると特定インスタンスしか監視できない
- スケールアウトだけでなくスケールインのアラーム・ポリシーも別途作成が必要で、急激な台数削減を避けるために段階的に減らす設定にしておくと安心
この記事がAWSを運用する中でどなたかの参考になれば幸いです!