Kubernetes 網路運維
最近我一直在研究 Kubernetes 網路。我注意到一件事情就是,雖然關於如何設置 Kubernetes 網路的文章很多,也寫得很不錯,但是卻沒有看到關於如何去運維 Kubernetes 網路的文章、以及如何完全確保它不會給你造成生產事故。
在本文中,我將儘力讓你相信三件事情(我覺得這些都很合理 :)):
- 避免生產系統網路中斷非常重要
- 運維聯網軟體是很難的
- 有關你的網路基礎設施的重要變化值得深思熟慮,以及這種變化對可靠性的影響。雖然非常「牛x」的谷歌人常說「這是我們在谷歌正在用的」(谷歌工程師在 Kubernetes 上正做著很重大的工作!但是我認為重要的仍然是研究架構,並確保它對你的組織有意義)。
我肯定不是 Kubernetes 網路方面的專家,但是我在配置 Kubernetes 網路時遇到了一些問題,並且比以前更加了解 Kubernetes 網路了。
運維聯網軟體是很難的
在這裡,我並不討論有關運維物理網路的話題(對於它我不懂),而是討論關於如何讓像 DNS 服務、負載均衡以及代理這樣的軟體正常工作方面的內容。
我在一個負責很多網路基礎設施的團隊工作過一年時間,並且因此學到了一些運維網路基礎設施的知識!(顯然我還有很多的知識需要繼續學習)在我們開始之前有三個整體看法:
- 聯網軟體經常重度依賴 Linux 內核。因此除了正確配置軟體之外,你還需要確保許多不同的系統控制(
sysctl
)配置正確,而一個錯誤配置的系統控制就很容易讓你處於「一切都很好」和「到處都出問題」的差別中。 - 聯網需求會隨時間而發生變化(比如,你的 DNS 查詢或許比上一年多了五倍!或者你的 DNS 伺服器突然開始返回 TCP 協議的 DNS 響應而不是 UDP 的,它們是完全不同的內核負載!)。這意味著之前正常工作的軟體突然開始出現問題。
- 修復一個生產網路的問題,你必須有足夠的經驗。(例如,看這篇 由 Sophie Haskins 寫的關於 kube-dns 問題調試的文章)我在網路調試方面比以前進步多了,但那也是我花費了大量時間研究 Linux 網路知識之後的事了。
我距離成為一名網路運維專家還差得很遠,但是我認為以下幾點很重要:
- 對生產網路的基礎設施做重要的更改是很難得的(因為它會產生巨大的混亂)
- 當你對網路基礎設施做重大更改時,真的應該仔細考慮如果新網路基礎設施失敗該如何處理
- 是否有很多人都能理解你的網路配置
切換到 Kubernetes 顯然是個非常大的更改!因此,我們來討論一下可能會導致錯誤的地方!
Kubernetes 網路組件
在本文中我們將要討論的 Kubernetes 網路組件有:
- 覆蓋網路 的後端(像 flannel/calico/weave 網路/romana)
kube-dns
kube-proxy
- 入站控制器 / 負載均衡器
kubelet
如果你打算配置 HTTP 服務,或許這些你都會用到。這些組件中的大部分我都不會用到,但是我儘可能去理解它們,因此,本文將涉及它們有關的內容。
最簡化的方式:為所有容器使用宿主機網路
讓我們從你能做到的最簡單的東西開始。這並不能讓你在 Kubernetes 中運行 HTTP 服務。我認為它是非常安全的,因為在這裡面可以讓你動的東西很少。
如果你為所有容器使用宿主機網路,我認為需要你去做的全部事情僅有:
- 配置 kubelet,以便於容器內部正確配置 DNS
- 沒了,就這些!
如果你為每個 pod 直接使用宿主機網路,那就不需要 kube-dns 或者 kube-proxy 了。你都不需要一個作為基礎的覆蓋網路。
這種配置方式中,你的 pod 們都可以連接到外部網路(同樣的方式,你的宿主機上的任何進程都可以與外部網路對話),但外部網路不能連接到你的 pod 們。
這並不是最重要的(我認為大多數人想在 Kubernetes 中運行 HTTP 服務並與這些服務進行真實的通訊),但我認為有趣的是,從某種程度上來說,網路的複雜性並不是絕對需要的,並且有時候你不用這麼複雜的網路就可以實現你的需要。如果可以的話,儘可能地避免讓網路過於複雜。
運維一個覆蓋網路
我們將要討論的第一個網路組件是有關覆蓋網路的。Kubernetes 假設每個 pod 都有一個 IP 地址,這樣你就可以與那個 pod 中的服務進行通訊了。我在說到「覆蓋網路」這個詞時,指的就是這個意思(「讓你通過它的 IP 地址指向到 pod 的系統)。
所有其它的 Kubernetes 網路的東西都依賴正確工作的覆蓋網路。更多關於它的內容,你可以讀 這裡的 kubernetes 網路模型。
Kelsey Hightower 在 kubernetes 艱難之路 中描述的方式看起來似乎很好,但是,事實上它的作法在超過 50 個節點的 AWS 上是行不通的,因此,我不打算討論它了。
有許多覆蓋網路後端(calico、flannel、weaveworks、romana)並且規劃非常混亂。就我的觀點來看,我認為一個覆蓋網路有 2 個職責:
- 確保你的 pod 能夠發送網路請求到外部的集群
- 保持一個到子網路的穩定的節點映射,並且保持集群中每個節點都可以使用那個映射得以更新。當添加和刪除節點時,能夠做出正確的反應。
Okay! 因此!你的覆蓋網路可能會出現的問題是什麼呢?
- 覆蓋網路負責設置 iptables 規則(最基本的是
iptables -A -t nat POSTROUTING -s $SUBNET -j MASQUERADE
),以確保那個容器能夠向 Kubernetes 之外發出網路請求。如果在這個規則上有錯誤,你的容器就不能連接到外部網路。這並不很難(它只是幾條 iptables 規則而已),但是它非常重要。我發起了一個 拉取請求,因為我想確保它有很好的彈性。 - 添加或者刪除節點時可能會有錯誤。我們使用
flannel hostgw
後端,我們開始使用它的時候,節點刪除功能 尚未開始工作。 - 你的覆蓋網路或許依賴一個分散式資料庫(etcd)。如果那個資料庫發生什麼問題,這將導致覆蓋網路發生問題。例如,https://github.com/coreos/flannel/issues/610 上說,如果在你的
flannel etcd
集群上丟失了數據,最後的結果將是在容器中網路連接會丟失。(現在這個問題已經被修復了) - 你升級 Docker 以及其它東西導致的崩潰
- 還有更多的其它的可能性!
我在這裡主要討論的是過去發生在 Flannel 中的問題,但是我並不是要承諾不去使用 Flannel —— 事實上我很喜歡 Flannel,因為我覺得它很簡單(比如,類似 vxlan 在後端這一塊的部分 只有 500 行代碼),對我來說,通過代碼來找出問題的根源成為了可能。並且很顯然,它在不斷地改進。他們在審查拉取請求方面做的很好。
到目前為止,我運維覆蓋網路的方法是:
- 學習它的工作原理的詳細內容以及如何去調試它(比如,Flannel 用於創建路由的 hostgw 網路後端,因此,你只需要使用
sudo ip route list
命令去查看它是否正確即可) - 如果需要的話,維護一個內部構建版本,這樣打補丁比較容易
- 有問題時,向上游貢獻補丁
我認為去遍歷所有已合併的拉取請求以及過去已修復的 bug 清單真的是非常有幫助的 —— 這需要花費一些時間,但這是得到一個其它人遇到的各種問題的清單的好方法。
對其他人來說,他們的覆蓋網路可能工作的很好,但是我並不能從中得到任何經驗,並且我也曾聽說過其他人報告類似的問題。如果你有一個類似配置的覆蓋網路:a) 在 AWS 上並且 b) 在多於 50-100 節點上運行,我想知道你運維這樣的一個網路有多大的把握。
運維 kube-proxy 和 kube-dns?
現在,我有一些關於運維覆蓋網路的想法,我們來討論一下。
這個標題的最後面有一個問號,那是因為我並沒有真的去運維過。在這裡我還有更多的問題要問答。
這裡的 Kubernetes 服務是如何工作的!一個服務是一群 pod 們,它們中的每個都有自己的 IP 地址(像 10.1.0.3、10.2.3.5、10.3.5.6 這樣)
- 每個 Kubernetes 服務有一個 IP 地址(像 10.23.1.2 這樣)
kube-dns
去解析 Kubernetes 服務 DNS 名字為 IP 地址(因此,my-svc.my-namespace.svc.cluster.local 可能映射到 10.23.1.2 上)kube-proxy
配置iptables
規則是為了在它們之間隨機進行均衡負載。Kube-proxy 也有一個用戶空間的輪詢負載均衡器,但是在我的印象中,他們並不推薦使用它。
因此,當你發出一個請求到 my-svc.my-namespace.svc.cluster.local
時,它將解析為 10.23.1.2,然後,在你本地主機上的 iptables
規則(由 kube-proxy 生成)將隨機重定向到 10.1.0.3 或者 10.2.3.5 或者 10.3.5.6 中的一個上。
在這個過程中我能想像出的可能出問題的地方:
kube-dns
配置錯誤kube-proxy
掛了,以致於你的iptables
規則沒有得以更新- 維護大量的
iptables
規則相關的一些問題
我們來討論一下 iptables
規則,因為創建大量的 iptables
規則是我以前從沒有聽過的事情!
kube-proxy 像如下這樣為每個目標主機創建一個 iptables
規則:這些規則來自 這裡)
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -m statistic --mode random --probability 0.20000000019 -j KUBE-SEP-E4QKA7SLJRFZZ2DD[b][c]
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -m statistic --mode random --probability 0.25000000000 -j KUBE-SEP-LZ7EGMG4DRXMY26H
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-RKIFTWKKG3OHTTMI
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-CGDKBCNM24SZWCMS
-A KUBE-SVC-LI77LBOOMGYET5US -m comment --comment "default/showreadiness:showreadiness" -j KUBE-SEP-RI4SRNQQXWSTGE2Y
因此,kube-proxy 創建了許多 iptables
規則。它們都是什麼意思?它對我的網路有什麼樣的影響?這裡有一個來自華為的非常好的演講,它叫做 支持 50,000 個服務的可伸縮 Kubernetes,它說如果在你的 Kubernetes 集群中有 5,000 服務,增加一個新規則,將需要 11 分鐘。如果這種事情發生在真實的集群中,我認為這將是一件非常糟糕的事情。
在我的集群中肯定不會有 5,000 個服務,但是 5,000 並不是那麼大的一個數字。為解決這個問題,他們給出的解決方案是 kube-proxy 用 IPVS 來替換這個 iptables
後端,IPVS 是存在於 Linux 內核中的一個負載均衡器。
看起來,像 kube-proxy 正趨向於使用各種基於 Linux 內核的負載均衡器。我認為這只是一定程度上是這樣,因為他們支持 UDP 負載均衡,而其它類型的負載均衡器(像 HAProxy)並不支持 UDP 負載均衡。
但是,我覺得使用 HAProxy 更舒服!它能夠用於去替換 kube-proxy!我用谷歌搜索了一下,然後發現了這個 thread on kubernetes-sig-network,它說:
kube-proxy 是很難用的,我們在生產系統中使用它近一年了,它在大部分的時間都表現的很好,但是,隨著我們集群中的服務越來越多,我們發現它的排錯和維護工作越來越難。在我們的團隊中沒有 iptables 方面的專家,我們只有 HAProxy & LVS 方面的專家,由於我們已經使用它們好幾年了,因此我們決定使用一個中心化的 HAProxy 去替換分散式的代理。我覺得這可能會對在 Kubernetes 中使用 HAProxy 的其他人有用,因此,我們更新了這個項目,並將它開源:https://github.com/AdoHe/kube2haproxy。如果你發現它有用,你可以去看一看、試一試。
因此,那是一個有趣的選擇!我在這裡確實沒有答案,但是,有一些想法:
- 負載均衡器是很複雜的
- DNS 也很複雜
- 如果你有運維某種類型的負載均衡器(比如 HAProxy)的經驗,與其使用一個全新的負載均衡器(比如 kube-proxy),還不如做一些額外的工作去使用你熟悉的那個來替換,或許更有意義。
- 我一直在考慮,我們希望在什麼地方能夠完全使用 kube-proxy 或者 kube-dns —— 我認為,最好是只在 Envoy 上投入,並且在負載均衡&服務發現上完全依賴 Envoy 來做。因此,你只需要將 Envoy 運維好就可以了。
正如你所看到的,我在關於如何運維 Kubernetes 中的內部代理方面的思路還是很混亂的,並且我也沒有使用它們的太多經驗。總體上來說,kube-proxy 和 kube-dns 還是很好的,也能夠很好地工作,但是我仍然認為應該去考慮使用它們可能產生的一些問題(例如,」你不能有超出 5000 的 Kubernetes 服務「)。
入口
如果你正在運行著一個 Kubernetes 集群,那麼到目前為止,很有可能的是,你事實上需要 HTTP 請求去進入到你的集群中。這篇博客已經太長了,並且關於入口我知道的也不多,因此,我們將不討論關於入口的內容。
有用的鏈接
幾個有用的鏈接,總結如下:
- Kubernetes 網路模型
- GKE 網路是如何工作的:https://www.youtube.com/watch?v=y2bhV81MfKQ
- 上述的有關
kube-proxy
上性能的討論:https://www.youtube.com/watch?v=4-pawkiazEg
我認為網路運維很重要
我對 Kubernetes 的所有這些聯網軟體的感覺是,它們都仍然是非常新的,並且我並不能確定我們(作為一個社區)真的知道如何去把它們運維好。這讓我作為一個操作者感到很焦慮,因為我真的想讓我的網路運行的很好!:) 而且我覺得作為一個組織,運行你自己的 Kubernetes 集群需要相當大的投入,以確保你理解所有的代碼片段,這樣當它們出現問題時你可以去修復它們。這不是一件壞事,它只是一個事而已。
我現在的計劃是,繼續不斷地學習關於它們都是如何工作的,以儘可能多地減少對我動過的那些部分的擔憂。
一如繼往,我希望這篇文章對你有幫助,並且如果我在這篇文章中有任何的錯誤,我非常喜歡你告訴我。
via: https://jvns.ca/blog/2017/10/10/operating-a-kubernetes-network/
作者:Julia Evans 譯者:qhwdw 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive