背景
以前伺服器都是直接配置LNMP環境,最近手頭正好有一臺需要重新配置,想嘗試使用docker來配置多PHP版本環境。docker配置十分順利,感謝有明大佬的小冊,和DNMP專案。
待解決
伺服器配置好後,在幾天的試用過程中,發現如下兩個問題:
- NGINX PHP容器中無法獲取request的真實IP
- PHP容器中無法訪問公網
過程
無法訪問公網,無法獲取真實IP,首先想到了防火牆的問題;關閉iptables,問題解決。可是iptables不能關呀,雖說現在雲伺服器都有安全組過濾,但防火牆是最後一道防線,不能在雲廠商的懷抱裡裸奔啊……
檢視了docker的網路部分,docker暴露容器埠是由docker-proxy來實現的,至於docker-proxy是什麼略過不表。肯定是iptables影響了docker-proxy導致資料包的源ip發生了改變,無法獲取真實IP;無法訪問公網,則是資料包找不到出口,被iptables攔截掉了,在內網轉圈圈
- 既然docker的各容器處於內網中,於是我想到了iptables的轉發功能。
解決
-
假設我的公網IP為 117.25.140.71
-
NGINX容器內網IP為 172.18.0.2
-
PHP容器內網IP為 172.18.0.3 172.19.0.3
把公網來的資料包直接轉發給NGINX容器,跳過docker-proxy
新增如下規則
-A PREROUTING -d 117.25.140.71 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80
-A PREROUTING -d 117.25.140.71 -p tcp -m tcp --dport 443 -j DNAT --to-destination 172.18.0.2:443
複製程式碼
重啟iptables 無法獲取真實IP問題解決
將內網的資料包轉發到公網
我的PHP容器訪問公網,需求大抵是傳送簡訊之類(無郵件傳送),用的都是http協議,所以這裡我沒有選擇全部轉發,而是隻轉發目的埠為80和443的資料包
新增如下規則
-A POSTROUTING -s 172.19.0.3 -p tcp -m tcp --dport 80 -j SNAT --to 117.25.140.71
-A POSTROUTING -s 172.19.0.3 -p tcp -m tcp --dport 443 -j SNAT --to 117.25.140.71
複製程式碼
重啟iptables發現仍然無法訪問公網,是不是落下了什麼。。 對了,既然http協議訪問其他域名提供的api服務,怎麼能少了DNS解析呢?新增DNS的支援(注意:DNS解析使用的是udp協議)
補充如下規則
-A POSTROUTING -s 172.19.0.3 -p udp -m udp --dport 53 -j SNAT --to 117.25.140.71
複製程式碼
重啟iptables,大功告成!
最後總結幾點:
- 之所以能用宿主機iptables實現轉發,是因為宿主機是內網的閘道器
- 我限制了PHP容器訪問公網的目的埠,如果需要傳送郵件或者訪問8080等其他埠,仍需單獨新增規則或者不限制具體埠號
- 轉發內網資料包到公網,不要使用地址偽裝(MASQUERADE),否則會影響本文中新增的PREROUTING規則,仍然無法獲取request的真實IP(使用其他方法除外)
- 如果直接service iptables stop,這篇文章就不用看了。。。