504 Gateway timeout 解決方法 on AWS


504 Gateway timeout 解決方法 on AWS

Webシステムで、バックエンドで少々長い時間が掛かる処理が終わるまで待つ必要があるケースがありますが、デフォルト設定だと、大体60秒程でタイムアウトとなり、「504 Gateway timeout」が発生するという事が多いです。

AWSで、ELB->Apache->PHPと言うよくある構成で、どの様に設定して行くと良いかをメモがてら残しておきます。

構成

クライアント(ブラウザ) -> ELB(ALB) -> WEB(Apache -> PHP) -> DB(RDS on MySQL)

今回、DBは割愛します。

タイムアウト調整

ユースケース次第ですがAWSのナレッジでは、KeepAliveTimeoutなどをロードバランサーのアイドルタイムアウトよりも長いキープアライブタイムアウトにするように推奨されています。
https://aws.amazon.com/jp/premiumsupport/knowledge-center/apache-backend-elb/

例)無限にはできませんので、システム要件等で、程よいタイムアウト値を検討する必要があります。
クライアント(デフォルト:ブラウザ依存) < ELB(60) < WEB(Apache 120) < WEB(PHP 120)

それぞれ設定すべき箇所

少々極端ですが、10分(600秒)タイムアウトさせたくないとします。

クライアント(ブラウザ) ※多分ここで調整することは無いと思います。

  • Chrome
    調べましたが、Chromeでタイムアウト設定を変更することはできないようです
    タイムアウトが何秒なのかなどのドキュメントも見当たらない・・・

  • Firefox
    about:configから、「network.http.connection-timeout」を調整

  • IE
    レジストリで、 KeepAliveTimeout、ServerInfoTimeoutのDWORD値を調整
    https://docs.microsoft.com/en-US/troubleshoot/browsers/change-keep-alive-time-out

  • Safari
    SafariNoTimeoutをアプリとしてインストールすると可能とのこと

ELB アイドルタイムアウト

Apache /etc/httpd/conf.d/aws_elb.confを作成して設定

例)600秒なので、1.25倍して、750秒にしてみました。

/etc/httpd/conf.d/aws_elb.conf
# クライアントヘッダータイムアウト
# ロードバランサーでアイドル接続を適切に切断できるように、ロードバランサーに設定されたアイドルタイムアウトよりも大きな値に設定します。
# ロードバランサーに適切に通知せずに、バックエンドサーバーで接続を終了すると、504 エラーが表示されることがあります。
Timeout 750

# キープアライブ
# CPU 使用率を削減し、応答時間を改善するには、キープアライブをオンにします。
# キープアライブをオンにすると、ロードバランサーで、HTTP リクエストの度に新しい TCP 接続を確立する必要がありません。
KeepAlive On

# キープアライブオプションが有効になっている場合は、ロードバランサーのアイドルタイムアウトよりも長いキープアライブタイムアウトを選択します。 
KeepAliveTimeout 750

# キープアライブリクエストの最大数
# このオプションでは、キープアライブがオンになった時に単一の TCP 接続で処理するリクエストの数を設定します。
# リソースの使用を最適化するために、キープアライブリクエストの最大数は 100 以上に設定します。
MaxKeepAliveRequests 100

# AcceptFilter
# AcceptFilter はデフォルトで有効になっており、接続に TCP_DEFER_ACCEPT オプションを使用するよう Apache に指示します。
# この設定では、TCP ソケットが「ハーフオープン」状態になることがあります。
# この場合、ロードバランサーでは接続が確立されているが、バックエンドインスタンスでは接続が確立されていないことを前提とします。
# ハーフオープン接続は、性能が高くないロードバランサーで最も一般的です。この場合、使用前の接続に時間がかかります。
AcceptFilter http none

# ELB access_log format
LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" combined

# Set over AWS ELB connection time out default 60s
# 長時間にわたる少量ずつの読み込み/書き込み処理を悪用する攻撃 (Slowloris など) に対して接続を自動的に閉じる
RequestReadTimeout header=750

PHP

  1. おなじみのphp.iniでmax_execution_timeの値で定義する方法

ただし、全体的にスクリプトが実行可能な秒数が伸びてしまうので注意が必要

/etc/php.ini
;;;;;;;;;;;;;;;;;;;
; Resource Limits ;
;;;;;;;;;;;;;;;;;;;

; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
; Note: This directive is hardcoded to 0 for the CLI SAPI
max_execution_time = 60
  1. 処理中に、set_time_limit(int $seconds): bool で実行時間の最大値を制限する

<?php

set_time_limit(750);

while ($i<=10)
{
        echo "i=$i ";
        sleep(100);
        $i++;
}

?>