我想為我的 Linux 伺服器新增輔助備份網路連線。我計劃使用 USB LTE 數據機來實現此目的。
由於此蜂窩連接將按流量計費,因此我希望將可消耗的數據量限制在必要的絕對最低限度。
我有一個自訂伺服器應用程序,我可以對其進行任何更改。它有一些任務,不間斷的連接至關重要,而其他任務則停機時間並不重要。
我正在想像這樣的事情:
- 伺服器需要發出外部 HTTP API 請求。第一次嘗試是透過系統的預設路由(即 eth0,主要網際網路連線)進行的。
- 如果請求失敗或逾時,請透過 LTE 介面重試請求。
只有我的伺服器進程明確想要透過 LTE 發送的流量才應該透過 LTE 傳送。來自系統任何部分的其他流量都不應通過 LTE。
- 具體來說,我將使用節點的
localAddress
套接字選項指定應透過 LTE 發出請求。 - 如何確保其他流量不會最終透過 LTE 介面路由(即使 eth0 已關閉)?
- DNS解析呢?
答案1
我最終透過配置實現了這一點備用路由表和一個路由策略規則為備份介面的來源位址。
我將 USB LTE 數據機呈現為 NDIS 設備,因此它僅顯示為eth1
IP 192.168.0.190,並且在內部執行 NAT 路由。我已經配置了eth1
靜態 IP 並手動配置了路由。
預設配置使用 DHCP,因此請關閉介面並確保刪除任何自動新增的路由。
為介面新增靜態 IP 配置並將其啟動。
1
將條目新增至子網路和預設閘道的備用路由表(我選擇的)。# ip route add 192.168.0.0/24 dev eth1 src 192.168.1.190 table 1 # ip route add default via 192.168.0.1 table 1
設定路由策略規則,以便明確使用 192.168.1.190 作為來源位址的應用程式將使用路由表 1 而不是預設路由表。
# ip rule add from 192.168.0.190/32 table 1 # ip rule add to 192.168.0.190/32 table 1
此時,您應該能夠測試您的連接性。
$ curl https://wtfismyip.com/text
1.2.3.4 # primary ISP external IP
$ curl --interface 192.168.0.190 https://wtfismyip.com/text
5.6.7.8 # backup LTE external IP
如果一切看起來都不錯,請將配置永久化。我添加到/etc/network/interfaces
:
iface eth1 inet static
address 192.168.0.190
netmask 255.255.255.0
post-up ip route add 192.168.0.0/24 dev eth1 src 192.168.0.190 table 1
post-up ip route add default via 192.168.0.1 table 1
post-up ip rule add from 192.168.0.190/32 table 1
post-up ip rule add to 192.168.0.190/32 table 1
現在,只有在建立傳出連線時明確綁定到 192.168.0.190 的應用程式才會透過備份連線進行路由。所有其他流量都會被路由eth0
(或[預設]路由表中配置的任何內容main
)。
這是可能的您有一些東西可以列舉所有可用的 IP 並嘗試從它們發送流量,這可能會導致備份連接上出現意外流量,但這不太可能。我沒有觀察到任何此類流量。
請注意,這不涉及 DNS 解析。在主連接離線的情況下,您可能會很幸運並從快取中進行查找,但這並不好依賴。我也不會配置系統範圍的解析器透過 LTE 介面發送請求。相反,您的應用程式可以在發出備份請求時手動處理 DNS 解析。
使用節點,從特定來源位址發出 HTTP 請求(或任何 TCP 連線)很容易。只需指定localAddress
選項,例如:
https.get('https://wtfismyip.com/text', { localAddress: '192.168.0.190' }, …);
解決 DNS 查找問題稍微棘手一些。還提供一個lookup
選項,可讓您覆寫預設的 DNS 解析過程。您可以使用自訂dns.Resolver
進行查找。不幸的是,node 沒有辦法指定 DNS 查找的來源位址,所以我添加了它。完成後,您可以將各個部分組合在一起:
const resolver = new dns.Resolver();
resolver.setServers(['8.8.8.8']);
resolver.setLocalAddress('192.168.0.190'); // requires node > v15.0.0
https.get('https://wtfismyip.com/text', {
localAddress: '192.168.0.190',
lookup: function(hostname, opts, cb) {
resolver.resolve(hostname, function(err, records) {
if (err) cb(err);
else if (!records[0]) cb(new Error('Missing DNS record'));
else cb(null, records[0], 4);
});
}
}, function(res) { … });