當 Nginx/Puma Rails App 伺服器通過防火牆聯繫 PostgreSQL 後端時,最終的 504 網關超時
我遇到了一個問題,即 Rails 應用程序伺服器(nginx/puma)和 PostgreSQL 數據伺服器在我們的 DMZ 上的同一個 VLAN 上保持一致通信,但是當數據庫被隔離到另一個 VLAN 並且應用程序伺服器仍然在DMZ,使用者訪問應用程序伺服器最終只會遇到來自 nginx 的 504(網關超時)錯誤。這些最終的超時似乎與應用程序的實際最終使用者使用情況無關(潛在的連接分配不足、連接用完等),因為我注意到這個問題可能發生在周末,幾乎可以肯定沒有使用者在系統。從第一個 504 網關超時開始,對伺服器的所有後續請求都會出現更多 504 網關超時頁面。我會說這是由於我的連接配置欠佳,*作品。*當配對處於“不良”配置時,連接工作但只能在可變的時間段內工作,通常是一個小時左右。
彪馬配置如下:
#!/usr/bin/env puma directory "/var/www/my_app/current" preload_app! environment "production" daemonize true pidfile "/var/www/my_app/shared/tmp/pids/my_app.pid" state_path "/var/www/my_app/shared/puma/my_app.state" stdout_redirect '/var/www/my_app/shared/log/production.log', '/var/www/my_app/shared/log/production_err.log', false threads 0, 16 bind "unix:///var/www/my_app/shared/tmp/sockets/my_app.sock" workers 8 on_worker_boot do require "active_record" ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished ActiveRecord::Base.establish_connection(YAML.load_file("/var/www/my_app/current/config/database.yml")["production"]) end before_fork do ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished end
Nginx 配置如下:
upstream my_app { server unix:///var/www/my_app/current/tmp/sockets/my_app.sock; } server { listen 80 default; listen [::]:80 default; return 301 https://$host$request_uri; } server { listen 443 ssl default; listen [::]:443 ssl default; server_name my_server.domain.com; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"; root /var/www/my_app/current/public; ssl_certificate /etc/ssl/certs/my_app_crt; ssl_certificate_key /etc/ssl/private/my_app_key; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; ssl_prefer_server_ciphers on; #See https://weakdh.org/ ssl_dhparam /etc/ssl/private/dhparams.pem; client_max_body_size 500M; location / { if (-f $document_root/maintenance.html) { return 503; } proxy_pass http://my_app; # match the name of upstream directive which is defined above proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; } location ~* ^/assets/ { # Per RFC2616 - 1 year maximum expiry expires 1y; add_header Cache-Control public; # Some browsers still send conditional-GET requests if there's a # Last-Modified header or an ETag header even if they haven't # reached the expiry date sent in the Expires header. add_header Last-Modified ""; add_header ETag ""; break; } error_page 503 @maintenance; location @maintenance { rewrite ^(.*)$ /maintenance.html break; } }
我認為防火牆可能是問題所在,但我們在 Palo Alto 防火牆中看不到任何有關阻塞連接的資訊。我們嘗試只打開 postgresql 流量,然後擴大到埠 5432 上的僅 tcp 流量,問題仍然存在。postgres 配置非常標準,max_connections 超過了應用伺服器可以建立的最大可能連接數。
只是一個瘋狂的猜測,但也許防火牆“忘記”了 TCP 會話?許多防火牆對“未使用”的 TCP 會話有超時。
當您的 Rails 應用程序啟動並連接到數據庫時,一切正常。當 Rails 應用程序和數據庫伺服器之間存在較長時間的靜默時,防火牆會達到其 tcp 會話超時並認為會話已關閉,而兩端(rails 和數據庫伺服器)都認為它是打開的。當 rails 現在想要查詢數據庫時,它將被防火牆阻止,因為包與已知的 tcp 會話不匹配。
如果您讓您的導軌定期執行“選擇 1”或類似的東西,則不應再斷開連接。
您也可以嘗試重新配置 postgresql 的 tcp keepalive 行為。在
postgresql.conf
你可以設置tcp_keepalives_idle = 60
tcp_keepalives_interval = 1
tcp_keepalives_count = 5
這告訴TCP堆棧每60秒發送一個keepalive數據包,並在5個這樣的封包遺失時將連接標記為死亡。keepalive 數據包本身應該足以使防火牆保持連接打開。Linux 上 tcp_keepalives_idle 的預設值應為 7200,如果防火牆在 3600 秒後丟棄 tcp 會話,則該值太高。您可能希望通過所有主機上的 sysctl 參數調整核心,以使所有程序在該特定防火牆上更好地工作:
net.ipv4.tcp_keepalive_time = 3500
這將預設的保活時間設置為 3500 秒(這比您的防火牆 TCP 超時略短)