リバースプロキシとして使用しているapacheに出力されるDNS lookupエラーについて質問です。
前提
-
SSLのためにリバースプロキシを使用
-
リバースプロキシサーバーは CentOS 7.1 + apache(2.4.33)で構成
-
1日あたり約70万/日のアクセスがある
-
構成は下記のようなイメージです。
クライアント <---> インターネット <---> ファイアウォール <---> リバースプロキシ(apache)[今回の質問対象] <---> ロードバランサー <---> コンテンツサーバー(apache)
エラー発生時の状況
- HTTP/2.0に対応させるため、apacheの設定を下記のとおり変更し、restartした。(apache event + http/2)
主なapache設定変更点 LoadModule mpm_event_module modules/mod_mpm_event.so ←追加した #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so ←コメントアウトした ~~~~ Protocols h2 http/1.1 ←追加した #Protocols http/1.1 ←コメントアウトした ~~~~ 下記設定を追加 <IfModule mpm_event_module> MaxRequestWorkers 400 ServerLimit 8 ThreadLimit 50 ThreadsPerChild 50 StartServers 8 MinSpareThreads 100 MaxSpareThreads 400 MaxConnectionsPerChild 0 MaxMemFree 2048 </IfModule>
- 設定変更後、1万~10万アクセスに一度、下記のようなapacheエラーログが出力されるようになった。
[Tue Oct 16 12:43:25.460055 2018] [proxy:error] [pid 12345:tid 140458525089774] "example.jp" [client 192.168.X.X:45678] AH00898: DNS lookup failure for: www.hogehoge.example.jp returned by /example/hogehoge/
- その時、アクセスログでは502エラーが返っている
- 99.99%以上のアクセスは問題なし
- 現在は切り戻しており、再現の条件ははっきりと分かっていない。(大量のアクセスが条件?)
確認したこと
- CPU、メモリ、I/O → 負荷はみられない
- /var/log/messages、dmesg等のシステムログ → 特に変わったところはない
- apcheのソース(後述)
質問内容
- "DNS lookup failure" が出力される原因は何なのでしょうか
- 本当に名前解決できていないのでしょうか(apacheが誤解している?)
これまで apache prefork + http/1.1 で運用しており、"DNS lookup failure" が出力されたことはありません。
ネットで色々調べたものの、類似の事象が見つけられず困り果ております。
追加情報
確認したapacheのソース(コメント一部割愛)
apacheエラーログのエラーコード(AH00898)より下記ソースの動作であることは確認しました。
err変数が途中で書き換わるとDNS lookup failureが出力される様子
proxy_util.c 2189行目 int server_port; apr_status_t err = APR_SUCCESS; apr_status_t uerr = APR_SUCCESS; const char *uds_path;
proxy_util.c 2356行目 if (err != APR_SUCCESS) { return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "DNS lookup failure for: ", conn->hostname, NULL)); }
現状の設定だとerr変数が書き換わるのはどうもここっぽい
proxy_util.c 2306行目 if (!will_reuse) { err = apr_sockaddr_info_get(&(conn->addr), conn->hostname, APR_UNSPEC, conn->port, 0, conn->pool); }
apr_sockaddr_info_get関数のソース
httpd-2.4.XX/srclib/apr/network_io/unix/sockaddr.c にあった
sockaddr.c 605行目 APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa, const char *hostname, apr_int32_t family, apr_port_t port, apr_int32_t flags, apr_pool_t *p) { apr_int32_t masked; *sa = NULL; if ((masked = flags & (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK))) { if (!hostname || family != APR_UNSPEC || masked == (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK)) { return APR_EINVAL; } if (flags & APR_IPV6_ADDR_OK) { return APR_ENOTIMPL; } } if (family == APR_UNSPEC) { family = APR_INET; } return find_addresses(sa, hostname, family, port, flags, p); }
returnのfind_addressesのソースを読むとcall_resolverが呼び出されているので
call_resolverのソースを見てみる
sockaddr.c 307行目 static apr_status_t call_resolver(apr_sockaddr_t **sa, const char *hostname, apr_int32_t family, apr_port_t port, apr_int32_t flags, apr_pool_t *p) { struct addrinfo hints, *ai, *ai_list; apr_sockaddr_t *prev_sa; int error; char *servname = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; if (family == APR_UNSPEC) { hints.ai_flags = AI_ADDRCONFIG; } ~~~略~~~ error = getaddrinfo(hostname, servname, &hints, &ai_list); ~~~略~~~
getaddrinfoは、アドレス情報を決定するためのヒントを与えることでsockaddr型構造体を得ることができる関数らしい。
ヒントとなる情報を格納した不完全なaddrinfo型構造体を与えると、必要なメンバが全てそろったaddrinfo型構造体を返すとのこと。
第1~3引数で情報を与え、第4引数に結果(メモリアドレス)が返る。ここでは&ai_listに結果が返る
sockaddr.c 403行目 prev_sa = NULL; ai = ai_list; while (ai) { /* while more addresses to report */ apr_sockaddr_t *new_sa; if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { if (ai->ai_family != AF_INET) { ai = ai->ai_next; continue; } new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t)); new_sa->pool = p; memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen); apr_sockaddr_vars_set(new_sa, ai->ai_family, port); if (!prev_sa) { /* first element in new list */ if (hostname) { new_sa->hostname = apr_pstrdup(p, hostname); } *sa = new_sa; } else { new_sa->hostname = prev_sa->hostname; prev_sa->next = new_sa; } prev_sa = new_sa; ai = ai->ai_next; } freeaddrinfo(ai_list); if (prev_sa == NULL) { return APR_EGENERAL; } return APR_SUCCESS; }
上記でやってること?
getaddrinfoから得た情報(ai)のメモリ情報をコピーして新しいソケットアドレス(new_sa)を作成
最初に見つけたソケットアドレスをsaをセット(saは後にコネクション確立する用?)
最終的にprev_sa(new_sa)に値が入っているとAPR_SUCCESSが返る
要するに新しいソケットアドレスが作成できなかったときに"DNS lookup failure"が発生する?(自信なし)
リバースプロキシのhttpd.conf (一部省略しています)
# Server Base ServerRoot "/usr/local/apache2" PidFile "/var/run/httpd.pid" DefaultRuntimeDir "/var/run/apache2" Listen 80 Listen 443 UseCanonicalName Off HostnameLookups On ServerTokens Prod ServerSignature Off Timeout 120 KeepAlive On MaxKeepAliveRequests 50 KeepAliveTimeout 2 TraceEnable Off FileETag MTime Size ServerAdmin root@localhost ServerName localhost User www Group www DocumentRoot "/var/www" # Load Module LoadModule authz_host_module modules/mod_authz_host.so LoadModule authz_core_module modules/mod_authz_core.so LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule socache_dbm_module modules/mod_socache_dbm.so LoadModule reqtimeout_module modules/mod_reqtimeout.so LoadModule http2_module modules/mod_http2.so LoadModule log_config_module modules/mod_log_config.so LoadModule env_module modules/mod_env.so LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule ssl_module modules/mod_ssl.so LoadModule mpm_event_module modules/mod_mpm_event.so #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so LoadModule unixd_module modules/mod_unixd.so LoadModule dir_module modules/mod_dir.so ## prefork MPM <IfModule mpm_prefork_module> ServerLimit 500 StartServers 50 MinSpareServers 50 MaxSpareServers 50 MaxClients 400 MaxRequestsPerChild 1000 </IfModule> # Event MPM <IfModule mpm_event_module> MaxRequestWorkers 400 ServerLimit 8 ThreadLimit 50 ThreadsPerChild 50 StartServers 8 MinSpareThreads 100 MaxSpareThreads 400 MaxConnectionsPerChild 0 MaxMemFree 2048 </IfModule> # Proxy ProxyRequests Off ProxyVia Off ProxyPreserveHost On # HTTP2 <IfModule http2_module> ProtocolsHonorOrder On Protocols h2 http/1.1 #Protocols http/1.1 LogLevel http2:info H2MaxWorkerIdleSeconds 120 H2ModernTLSOnly On H2Push On H2MaxSessionStreams 30 H2SessionExtraFiles 30 H2TLSWarmUpSize 65535 </IfModule> # SSL SSLRandomSeed startup builtin SSLRandomSeed connect builtin SSLProtocol -All +TLSv1.2 +TLSv1.1 +TLSv1 SSLCipherSuite (割愛) SSLHonorCipherOrder On SSLCompression Off SSLSessionCache dbm:/hoge/hoge Mutex file:/var/tmp SSLPassPhraseDialog exec:/hoge/hoge SSLSessionCacheTimeout 300 SSLSessionTickets Off SSLUseStapling On SSLStaplingResponderTimeout 5 SSLStaplingReturnResponderErrors Off SSLStaplingCache shmcb:/var/run/apache2/ocsp(128000) # configure <VirtualHost 192.168.1.10> SSLEngine On ServerName sslproxy.example.jp SSLCertificateKeyFile /hoge/hoge.key SSLCertificateFile /hoge/hoge.crt </VirtualHost> <VirtualHost 192.168.1.11> SSLEngine On ServerName hoge.example.jp SSLCertificateKeyFile /hoge/hoge.key SSLCertificateFile /hoge/hoge.crt <IfDefine !AP22> ProxyPass / http://www.hogehoge.example.jp/ </IfDefine> <IfDefine AP22> ProxyPass / http://www.hogehoge.example.jp/ disablereuse=On </IfDefine> </VirtualHost>
回答1件
あなたの回答
tips
プレビュー