0%

[Nginx] 使用 Nginx 代理動態 IP 的服務

前言

最近發現 Nginx 運行一段時間之後,就會出現以下幾種 error:

  • connect() failed (111: Connection refused) while connecting to upstream
  • upstream timed out (110: Connection timed out) while connecting to upstream

本來一直以為是 config 少設定了一些參數,後來才發現是 動態IP 的問題。
Nginx 在啟動時會向 DNS server 查詢 Domain 對應的 IP,之後就會 cache IP,直到下次重新載入 config 才會再更新,所以當我們服務的 IP 改變之後,Nginx 就會開始出現 Connect failed, Connection timeout 等 error.

解決方式

以下有幾種解決方式:

  1. 寫 cronjob, 偵測服務的 IP, 當 IP 有變動時, reload nginx.
  2. 在商業版的 Nginx 中,可以在 upstream 中使用 resolve.
  3. 在一般版的 Nginx 中,不使用 upstream,改用 set 將 url 設為變數,再傳遞給 proxy_pass.
  4. 使用 nginx-upstream-dynamic-servers module, 可以在 upstream 中使用 resolve.

其中第一個方式算是比較應急的做法,建議還是要設定 Nginx 更新 IP 比較好。
第二個方式是可以付費購買 Nginx Plus,接著修改 nginx.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
http {
resolver 8.8.8.8 valid=30s;

upstream backend {
zone upstream_dynamic 64k;
server server1.com.tw fail_timeout=60s max_fails=1 resolve;
server server2.com.tw backup resolve;
keepalive 256;
}

server {
listen 3000;
server_name localhost;

location / {
proxy_pass http://backend;

# ... 以下省略其他設定 ...
}
}
}

主要是在 http {} 中設定 resolver, 以及在 upstream {} 中加上 resolve.
resolver 是設定 DNS server, valid 是指 DNS server 回傳的 IP 會被 chache 的時間,在這個時間內不會再向 DNS server 詢問 IP.
設定完之後,resolver 就會按照 DNS server 的 TTL 來做更新。

第三個方式是將 url 設為變數,再傳給 proxy_pass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http {
resolver 8.8.8.8 valid=30s;

server {
listen 3000;
server_name localhost;
set $backend http://server1.com.tw;

location / {
proxy_pass $backend;

# ... 以下省略其他設定 ...
}
}
}

最後一個方式則是使用 nginx-upstream-dynamic-servers module, 依照以下方式安裝:

1
2
3
4
5
6
7
# Clone nginx-upstream-dynamic-servers
$ git clone https://github.com/GUI/nginx-upstream-dynamic-servers.git

# 增加 Nginx module
$ cd <NGINX_DIR>
$ ./configure --prefix=/home/<user>/nginx --add-module=/path/to/nginx-upstream-dynamic-servers
$ make && make install

再來修改 nginx.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
http {
resolver 8.8.8.8 valid=30s;

upstream backend {
zone upstream_dynamic 64k;
server server1.com.tw fail_timeout=60s max_fails=1 resolve;
server server2.com.tw backup resolve;
keepalive 256;
}

server {
listen 3000;
server_name localhost;

location / {
proxy_pass http://backend;

# ... 以下省略其他設定 ...
}
}
}

設定的內容和第二個方式一樣,resolver 就會按照 DNS server 的 TTL 來做更新了!

參考資料