ALBでVirtual Hostごとのアクセス数を確認したい場合はVirtual Hostごとでターゲットグループを分割すると良いかもしれない

ターゲットグループを分割した場合の影響範囲とアクセス数を確認する目的や精度などを考慮した上で分割しよう
2023.08.21

Virtual Hostで1つのEC2インスタンスで複数Webサイトをホスティングさせているけど、各Webサイトごとのアクセス数を確認したい

こんにちは、のんピ(@non____97)です。

皆さんはVirtual Hostで1つのEC2インスタンスで複数Webサイトをホスティングさせている環境で各Webサイトごとのアクセス数を確認したいと思ったことはありますか? 私はあります。

ALBにはActiveConnectionCountNewConnectionCountRequestCountなど接続数やリクエスト数のメトリクスが存在しています。

これらのメトリクスやALBやターゲットグループ単位のメトリクスになるので、Virtual HostでWebサイトを稼働させているとどのWebサイトへのアクセス数が多いのか把握することが難しいです。

「WebサーバーやALBのアクセスログを確認すればいいじゃん」と思われるかもしれませんが、グラフか何かで推移を簡単に確認したいところです。

そこで「Virtual Hostのホスト毎にターゲットグループを作成すれば良いんじゃないか」という理論で検証してみました。

いきなりまとめ

  • ホストごとにターゲットグループを作成すれば、各ホストごとのアクセス数をCloudWatchメトリクスRequestCountで確認できる
    • RequestCountはIPv4 および IPv6 経由で正常に処理されたリクエスト数
  • 細かいパフォーマンスを確認したいのであれば、ALBごと分割することになる
  • ヘルスチェックをしているホストの巻き込み事故を回避したいなら、Virtual Host自体を避けた方が良い

検証環境

検証環境は以下のとおりです。

ALBで各ホストへのアクセス数を確認したい場合はホストごとでターゲットグループを分割すると良いかもしれない検証環境構成図

Apache HTTP ServerのVirtual Hostでhoge.web.non-97.netfuga.web.non-97.netを稼働させています。

検証環境はAWS CDKでデプロイしました。使用したコードは以下リポジトリに保存しています。

デプロイ後、Route 53 Public Hosted ZoneのNSレコードを私のネームサーバーサービスに登録すると、以下のようにhoge.web.non-97.netfuga.web.non-97.netにアクセスできるようになりました。

> curl hoge.web.non-97.net
hoge

> curl fuga.web.non-97.net
fuga

各ドメインに対して何回かアクセスした後、ActiveConnectionCountNewConnectionCountRequestCountを確認してみます。

全Webサイトのアクセス数がまとめられる

はい、ALBもターゲットグループも同じなので、どちらのドメインに対して何回アクセスしたのか読み取れません。

試しに3分ほどhoge.web.non-97.netに0.5秒間隔でアクセスしてみます。

> while true; do
  curl -s hoge.web.non-97.net > /dev/null
  sleep 0.5
done

CloudWatchメトリクスは以下のとおりです。

3分ほどhoge.web.non-97.netに0.5秒間隔でアクセス

このように大量のアクセスが来ているのにも関わらず、どのドメインに対してのアクセスなのか分からないと、どちらのドメインで対応が必要なのかの判断に困りそうですね。

ターゲットグループを分けてみる

それではドメイン毎にターゲットグループを分けてみます。

リスナールールで各ホストヘッダーに対応したターゲットグループに転送するようにしてあげます。

AWS CDKのコードは以下のとおりです。

./lib/constructs/alb.ts

    const listener = this.alb.addListener("Listener", {
      port: 80,
      protocol: cdk.aws_elasticloadbalancingv2.ApplicationProtocol.HTTP,
      defaultTargetGroups: [targetGroup],
    });

    listener.addTargets("TargetsHoge", {
      priority: 1,
      conditions: [
        cdk.aws_elasticloadbalancingv2.ListenerCondition.hostHeaders([
          "hoge.web.non-97.net",
        ]),
      ],
      targets: [props.asg],
      port: 80,
    });
    listener.addTargets("TargetsFuga", {
      priority: 2,
      conditions: [
        cdk.aws_elasticloadbalancingv2.ListenerCondition.hostHeaders([
          "fuga.web.non-97.net",
        ]),
      ],
      targets: [props.asg],
      port: 80,
    });

※ どちらかのリスナールールのターゲットはlistener.addTargetGroups()でも良いです

デプロイ後のリスナールールを確認します。

hoge.web.non-97.netのターゲットグループ

HTTPホストヘッダーで転送先のターゲットグループが異なることが分かります。

Auto Scaling Groupを確認すると、複数のターゲットグループが選択されていることが分かります。

ターゲットグループが複数選択されている

それでは、タイミングをずらして各ドメインに対してアクセスします。

CloudWatchメトリクスを確認すると以下のようになっていました。

fugaのターゲットグループの方がアクセスが多くなったことが分かる

ターゲットグループが分かれているので、ターゲットグループのRequestCountでアクセス数が判断できますね。

では、ターゲットグループを分けまくれば良いのかというとそうではありません。

ターゲットが増えれば増えるほどヘルスチェックのリクエスト数が増大します。また、ターゲットグループで確認できるメトリクスは少ないです。細かいパフォーマンスを確認したいのであれば、ALBごと分割することになると考えます。

そのため、ターゲットグループで要件満たせるか評価すると良いでしょう。

そもそも、Virtual Hostの場合を避けた方が良いと個人的には思います。理由はヘルスチェックをしているホストの巻き込み事故を回避するためです。

ALBからのヘルスチェックの際にホストを指定できません。ヘルスチェックはデフォルトのホストに対して行われます。そのため、コンテンツ更新などでデフォルトのホストのコンテンツが一時的にステータスコード200を返さなくなると、そのターゲット自体にリクエストを振り分けなくなります。特にAuto ScalingでヘルスチェックにELBを使用している場合は、EC2インスタンスが削除されてしまいます。

特別にVirtual Hostにする必要がある背景がないのであれば、ホストごとにEC2インスタンスは分離させた方が良いと考えます。

実際に試してみましょう。

hoge.web.non-97.netindex.htmlを削除して、ヘルスチェックに失敗するようにします。

$ sudo rm -rf /var/www/html/hoge/index.html

$ sudo tail -f /var/log/httpd/hoge_access_log
10.10.10.36 - - [21/Aug/2023:06:23:41 +0000] "GET / HTTP/1.1" 200 5 "-" "ELB-HealthChecker/2.0"
10.10.10.36 - - [21/Aug/2023:06:23:41 +0000] "GET / HTTP/1.1" 200 5 "-" "ELB-HealthChecker/2.0"
10.10.10.26 - - [21/Aug/2023:06:23:41 +0000] "GET / HTTP/1.1" 200 5 "-" "ELB-HealthChecker/2.0"
10.10.10.26 - - [21/Aug/2023:06:23:41 +0000] "GET / HTTP/1.1" 200 5 "-" "ELB-HealthChecker/2.0"
10.10.10.26 - - [21/Aug/2023:06:23:56 +0000] "GET / HTTP/1.1" 200 5 "-" "ELB-HealthChecker/2.0"
10.10.10.36 - - [21/Aug/2023:06:23:56 +0000] "GET / HTTP/1.1" 200 5 "-" "ELB-HealthChecker/2.0"
10.10.10.36 - - [21/Aug/2023:06:24:11 +0000] "GET / HTTP/1.1" 403 45 "-" "ELB-HealthChecker/2.0"
10.10.10.36 - - [21/Aug/2023:06:24:11 +0000] "GET / HTTP/1.1" 403 45 "-" "ELB-HealthChecker/2.0"
10.10.10.26 - - [21/Aug/2023:06:24:11 +0000] "GET / HTTP/1.1" 403 45 "-" "ELB-HealthChecker/2.0"
10.10.10.26 - - [21/Aug/2023:06:24:11 +0000] "GET / HTTP/1.1" 403 45 "-" "ELB-HealthChecker/2.0"
10.10.10.26 - - [21/Aug/2023:06:24:26 +0000] "GET / HTTP/1.1" 403 45 "-" "ELB-HealthChecker/2.0"
10.10.10.36 - - [21/Aug/2023:06:24:26 +0000] "GET / HTTP/1.1" 403 45 "-" "ELB-HealthChecker/2.0"

ALBからのヘルスチェックに403を返すようになっていますね。

ターゲットグループ上からもunhealthyになりました。

ヘルスチェックに失敗

しばらくすると、新しいEC2インスタンスの起動とunhealthyのEC2インスタンスの削除が始まりました。

アクティビティ履歴

Auto Scaling Group上のインスタンス管理でもunhealthyとなっていますね。

i-09ecca8870daf8b4aが追加されたことが分かる

最終的には以下のようにunhealthyのEC2インスタンスは削除されました。

現在のEC2インスタンスの状態

ターゲットグループを分割した場合の影響範囲とアクセス数を確認する目的や精度などを考慮した上で分割しよう

ALBでVirtual Hostごとのアクセス数を確認したい場合はVirtual Hostごとでターゲットグループを分割すると良いかもしれないというお話をしました。

ターゲットグループを分割した場合の影響範囲ととアクセス数を確認する目的や精度などを考慮した上で分割しましょう。確認頻度が高くないのであれば必要になったタイミングでALBのアクセスログをAthenaで集計する方法でも良いかもしれません。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!