6/4 10:30 〜 11:50 (JST) App Runner 環境で発生した、遅延とエラー率が上昇する事象について、実施した対応について紹介させていただきます。
App Runner
App Runnerのダッシュボードを利用して、稼働状態の確認。
- 正常応答(HTTP 2xx)数が減少
- エラー応答(HTTP 4xx、5xx)と、レイテンシーが増加
の発生を確認しました。
App Runnerのアプリログを、エラー応答、レイテンシーが増加した時間帯を指定して確認。 タイムアウトに伴うエラーが記録されていました。
アプリケーションログ、 CloudWatch Logsの 「ログ異常検出」でも、タイムアウトに関連するログの急増が確認できました。
アクセスログ調査
準備
外部からの大量アクセス、DoS、DDoSの可能性を確認するため、App Runnerの前段の CloudFrontの標準アクセスログの調査を行いました。
対象の CloudFront、標準アクセスログはS3にそのまま出力する設定で利用中。
Athenaで不要なフルスキャンを避けるために、作業用のS3バケットを用意しました。
TEMP_S3='s3://athena-temp-ap-northeast-1-000000000000/'
aws s3 mb ${TEMP_S3}
s3 syncコマンドを利用して、調査対象となる時間帯を含むログを作業用S3バケットにコピーしました。
LOG_S3='s3://cloudfront-logs-ap-northeast-1-00000000/xxxx/'
aws s3 sync --dryrun --exclude '*' --include '*2024-06-04-0[0,1,2,3]*.gz' ${LOG_S3} ${TEMP_S3}
aws s3 sync --exclude '*' --include '*2024-06-04-0[0,1,2,3]*gz' ${LOG_S3} ${TEMP_S3}
Athenaのテーブルを AWSドキュメントに記載された、CloudFrontの標準ログ用のサンプルを踏襲、 作業用S3バケットのログが対象となる指定で作成しました。
CREATE EXTERNAL TABLE IF NOT EXISTS cloudfront_standard_logs (
`date` DATE,
time STRING,
x_edge_location STRING,
sc_bytes BIGINT,
c_ip STRING,
cs_method STRING,
cs_host STRING,
cs_uri_stem STRING,
sc_status INT,
cs_referrer STRING,
cs_user_agent STRING,
cs_uri_query STRING,
cs_cookie STRING,
x_edge_result_type STRING,
x_edge_request_id STRING,
x_host_header STRING,
cs_protocol STRING,
cs_bytes BIGINT,
time_taken FLOAT,
x_forwarded_for STRING,
ssl_protocol STRING,
ssl_cipher STRING,
x_edge_response_result_type STRING,
cs_protocol_version STRING,
fle_status STRING,
fle_encrypted_fields INT,
c_port INT,
time_to_first_byte FLOAT,
x_edge_detailed_result_type STRING,
sc_content_type STRING,
sc_content_len BIGINT,
sc_range_start BIGINT,
sc_range_end BIGINT
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LOCATION 's3://athena-temp-ap-northeast-1-000000000000/'
TBLPROPERTIES ( 'skip.header.line.count'='2' )
Athena解析
IPアドレス、UserAgent
過負荷の原因となったIPアドレス、UserAgentを求めました。
select
a.*,
a.required_time_sum / b.required_time_sum * 100
from
(
SELECT
c_ip,
substr(cs_user_agent, 1, 32) as cs_ua,
count(1) as request_count,
sum(time_taken) as required_time_sum
FROM
"default"."cloudfront_standard_logs"
group by
c_ip,
cs_user_agent
order by
sum(time_taken) desc
) a, (
SELECT
sum(time_taken) as required_time_sum
FROM
"default"."cloudfront_standard_logs"
) b
order by
a.required_time_sum desc
limit
10
負荷の10%相当が2つのIPである事。 UserAgent情報より、Pythonスクリプトと、kendraによるクローラーによる過負荷の可能性が確認されました。
c_ip | cs_ua | request_count | required_time_sum | 所要時間比率(%) |
---|---|---|---|---|
2600:1900:2000::::* | python-requests/2.32.3 | 18353 | 22470.455 | 6.7 |
13.114.. | amazon-kendra-customer-id-ca0d58 | 5160 | 16303.138 | 4.8 |
54.249.. | HatenaBlog-bot/0.02%20(+https:// | 1228 | 6086.256 | 1.8 |
54.249.. | HatenaBlog-bot/0.02%20(+https:// | 1141 | 5512.005 | 1.6 |
43.206.**.*** | curl/8.3.0 | 207 | 4162.735 | 1.2 |
107.178.. | Hatena-Favicon/2%20(https://www. | 989 | 2848.572 | 0.8 |
3.224.. | Mozilla/5.0%20(Macintosh;%20Inte | 602 | 1863.912 | 0.6 |
40.77.. | Mozilla/5.0%20AppleWebKit/537.36 | 351 | 1732.096 | 0.5 |
52.70.. | Mozilla/5.0%20(Macintosh;%20Inte | 539 | 1717.954 | 0.5 |
107.178.. | Hatena-Favicon/2%20(https://www. | 481 | 1573.606 | 0.5 |
10分毎の遷移
上位2つのIPアドレスに限定し、リクエスト数、所要時間の遷移を確認しました。
SELECT
substr(time, 1, 4) || '0' as time_HHm0,
count(1) as request_count,
sum(time_taken) as required_time_sum
FROM
"default"."cloudfront_standard_logs"
where
c_ip = '2600:1900:2000:**::*:**'
group by
substr(time, 1, 4) || '0'
order by
time_HHm0
Python
time_HHm0 | request_count | required_time_sum |
---|---|---|
0:00 | 588 | 23.019001 |
0:10 | 1175 | 21.003 |
0:20 | 1357 | 102.297 |
0:30 | 261 | 4.396 |
0:40 | 908 | 40.65 |
0:50 | 461 | 15.831 |
1:00 | 641 | 20.357 |
1:10 | 1600 | 107.948 |
1:20 | 942 | 156.14 |
1:30 | 516 | 115.716 |
1:40 | 989 | 757.788 |
1:50 | 162 | 267.442 |
2:00 | 654 | 1783.432 |
2:10 | 1530 | 4276.497 |
2:20 | 905 | 1023.052 |
2:30 | 428 | 1915.644 |
2:40 | 738 | 4008.708 |
2:50 | 409 | 2037.077 |
3:00 | 697 | 1458.379 |
3:10 | 1375 | 1519.558 |
3:20 | 1045 | 2004.031 |
3:30 | 461 | 302.908 |
3:40 | 511 | 508.582 |
- 遅延やエラー応答の割合が顕著に増加した11時台、多数のリクエストと、長い応答時間が記録されていました。
kendra
time_HHm0 | request_count | required_time_sum |
---|---|---|
1:00 | 275 | 467.917 |
1:10 | 716 | 2171.106 |
1:20 | 349 | 2188.694 |
1:30 | 521 | 2165.101 |
1:40 | 354 | 2153.986 |
1:50 | 517 | 2113.016 |
2:00 | 309 | 1976.889 |
2:10 | 361 | 741.236 |
2:20 | 281 | 261.597 |
2:30 | 65 | 250.167 |
2:40 | 60 | 313.376 |
2:50 | 125 | 799.051 |
3:10 | 99 | 192.564 |
3:20 | 100 | 210.747 |
3:30 | 56 | 136.44 |
3:40 | 1 | 1.92 |
- 11時台、エラー率や遅延が増加した時間帯のリクエストは少なく、主原因ではないと判断しました。
対策
暫定対策 1
Pythonスクリプトの実行元IPアドレスを AWS WAFの IP Setsに登録。 WAF ACLsで遮断ルールの対象とする事で、暫定処置をとりました。
暫定対策 2
異なるIPアドレスからの再発に備え、過負荷対策としてレートルールの導入に向けた準備に取り掛かりました。
Bot Control ルールを有効化。Bot Categories情報の判定結果を評価中です。
検索エンジン(search_engine)、SNS(social_media)以外のBotを対象としたレートルール制限を導入する事で、 サーバ側の性能に対し、高性能すぎるクローラー対策とする予定です。
Bot Control ルールの精度や、レートルールとの組み合わせ利用については、追って紹介させて頂きたいと思います。