本文整理自一段真實排查經驗,所有公司與網域資訊已去識別化。
範例網域以demodomain.com表示,例如wmsadmin.demodomain.com。
架構概述
- server-a:AWS 上的 Apache 2.4(HTTPS 入口、反向代理)
- server-b:同一子網內的應用(WMS / API 後端)
- 需求:把外部對特定網域(例如
wmsadmin.demodomain.com)的 API 請求由 server-a 轉送至 server-b
典型 vhost(簡化示意):
<VirtualHost *:443>
ServerName wmsadmin.demodomain.com
ProxyPreserveHost On
ProxyRequests Off
# 轉送到內部後端
ProxyPass / http://10.0.10.20:16890/ connectiontimeout=5 timeout=120 keepalive=On retry=0
ProxyPassReverse / http://10.0.10.20:16890/
RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
RequestHeader set X-Forwarded-SSL expr=%{HTTPS}
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
# 某些後端不吃 chunked,上傳時轉為 Content-Length 比較穩
SetEnv proxy-sendcl 1
# SSL 憑證略
</VirtualHost>
問題現象
- 偶發 502 Bad Gateway / Proxy Error
- 後來確認根因是:外部大量上傳(大檔/大批量訂單)導致後端處理時間超過預設 timeout
- 調整 Proxy timeout 後問題消失
排查思路(速查)
- 對時:鎖定 502 發生秒級時間,交叉比對 server-a(Apache)與 server-b(後端) log
- 直接從 server-a 測後端(排除網路/防火牆):
curl -v --max-time 180 http://10.0.10.20:16890/health - 開啟(僅排查時)代理偵錯:
LogLevel proxy:debug proxy_http:trace5看 error log 是否有timeout、AH01102(讀取狀態行失敗)、Connection reset by peer等關鍵訊息
➜ 排查完務必改回:LogLevel warn - 確認 Docker/網路(若後端在容器內):容器 IP 是否固定?建議用自訂網路與服務名連線,避免容器重啟換 IP。
針對長耗時路徑「差異化」放寬 timeout
不要全站一刀切放到很長,避免把 worker 綁死;只對特定 URI 提高 timeout:
# 一般 API:保持較短
ProxyPass /api/ http://10.0.10.20:16890/api/ connectiontimeout=5 timeout=60 keepalive=On
ProxyPassReverse /api/ http://10.0.10.20:16890/api/
# 大量上傳/批處理:提高 timeout(例如 10 分鐘)
ProxyPass /orders/upload http://10.0.10.20:16890/orders/upload connectiontimeout=5 timeout=600 keepalive=On
ProxyPassReverse /orders/upload http://10.0.10.20:16890/orders/upload
若有啟用 mod_reqtimeout,避免慢速上傳被切斷:
RequestReadTimeout header=60-120,MinRate=0 body=120,MinRate=0
# 某些 client 的 Expect: 100-continue 會卡,必要時可拿掉:
# RequestHeader unset Expect early
如何判讀「滿版的 trace 訊息」?
看到像這樣的行:
[proxy_http:trace4] mod_proxy_http.c(...) Access-Control-Allow-Methods: *
[proxy_http:trace3] start body send
代表只是 偵錯等級很高時,Apache 把上游回應 header 與傳輸流程寫進 error log。
不是錯誤。要看真正異常,請降回 LogLevel warn,或在 access log 先找 502 再對照 error log 該時段的 warn/error 級別訊息。
vhost 各自分檔記錄(擺脫 other_vhosts_access.log)
在每個 vhost 內指定 ErrorLog/CustomLog,最簡單:
<VirtualHost *:443>
ServerName wmsadmin.demodomain.com
ErrorLog ${APACHE_LOG_DIR}/wmsadmin-ssl-error.log
CustomLog ${APACHE_LOG_DIR}/wmsadmin-ssl-access.log combined
...
</VirtualHost>
如不想再用全域 other_vhosts_access.log,可停用:
a2disconf other-vhosts-access-log
apache2ctl graceful
(注意:停用後,沒自訂 CustomLog 的 vhost 將不再被記錄。)
沒有 logrotate?用 Apache 內建 rotatelogs 最省事
不需要再跑 cron/logrotate,只要把 log 管線到 rotatelogs。
不同映像路徑不一樣,常見在:
- 官方
httpd:2.4:/usr/local/apache2/bin/rotatelogs - Debian/Ubuntu 套件:
/usr/sbin/rotatelogs(屬於apache2-utils)
查路徑:
command -v rotatelogs || find / -type f -name rotatelogs 2>/dev/null
設定範例(每日一檔+保留「當前檔」的 symlink):
ErrorLog "|/usr/local/apache2/bin/rotatelogs -l -f -L /var/log/apache2/wmsadmin-error.log /var/log/apache2/wmsadmin-error-%Y%m%d.log 86400"
CustomLog "|/usr/local/apache2/bin/rotatelogs -l -f -L /var/log/apache2/wmsadmin-access.log /var/log/apache2/wmsadmin-access-%Y%m%d.log 86400" combined
想改成「依大小輪替」:把最後的
86400改成10M等。
讓 access log 更有用:記錄 I/O 與耗時
加一個 combined_io 格式,把請求/回應位元組與處理時間寫進去,方便事後定位「大檔或耗時」:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O %D" combined_io
CustomLog ${APACHE_LOG_DIR}/wmsadmin-access.log combined_io
%I / %O:request / response 位元組數%D:處理耗時(微秒)
查大於 60 秒的請求(例):
awk '$NF>60000000' /var/log/apache2/wmsadmin-access.log
常見細節與最佳實務
- ProxyPreserveHost On:不少後端依 Host 路由/產 URL,建議開啟
- SetEnv proxy-sendcl 1:把 chunked 上傳改成帶
Content-Length,相容性更好 - retry=0:避免 Apache 判定後端暫時壞掉而長時間不嘗試
- keepalive=On:減少連線建立成本
- 資源與併發:監看
MaxRequestWorkers、CPU/RAM、ulimit -n;高峰期才 502 的,多半是併發/連線池不足 - CORS/RateLimit header:常見於上游回應,與 Apache 502 無直接關係;但請評估是否過度寬鬆(安全面)
驗證與監控
- curl 模擬長請求:
curl -v --max-time 900 -F "file=@big_orders.csv" https://wmsadmin.demodomain.com/orders/upload - mod_status(快速看 Busy/Idle worker)
- 日誌告警:以
%D(耗時)或 5xx 比例作為告警門檻 - 從流程面優化:長任務改 非同步(
202 Accepted+ 狀態輪詢/SSE/WebSocket),proxy timeout 就能回到合理值
最終小抄(Checklist)
- 只為「長耗時路徑」提高
ProxyPass ... timeout=... ProxyPreserveHost On、SetEnv proxy-sendcl 1RequestReadTimeout放寬(若有啟用)- vhost 各自
ErrorLog/CustomLog,配合rotatelogs分檔 combined_io記錄%I/%O/%D- 排查時才開
LogLevel proxy:debug proxy_http:trace5,完畢恢復warn - 直接從 server-a 用
curl -v測後端 - 觀察 worker/資源、後端連線池與 DB/GC 等瓶頸
- 考慮把巨量上傳/批量處理改為非同步
如果你使用的映像沒有 rotatelogs,用套件管理器安裝 apache2-utils(Debian/Ubuntu/Alpine 名稱相同)或改寫成正確路徑即可。