閒談集羣管理模式

Docker很火很紅,簡直到了沒有道理的地步了。Docker爲什麼這麼紅?因爲它是一種可以用來掀桌子的技術。在部署自動化這條產業上的工人和機牀製造商們,看家護院的 cmdb,分佈式腳本執行等所謂核心技術即便不會變成明日黃花,也會淪爲二流技術。僅僅把 Docker 當成一個輕量級 vmware 來使用,是沒法看穿其實質的。要理解 Docker 的意義,不能從 Docker 是什麼,能夠幹什麼說起。讓我們先來回憶一下集羣管理模式的發展歷程,以及這些落後的模式的種種弊端。


手工管理時代


IP地址是放在 excel 表裏的。管理是靠登陸跳板機,用 SSH 連接服務器。手工執行命令做新的服務器部署,已有服務器的程序版本升級,以及各種配置刷新修改的工作。


弊端不言而喻,主要有這麼幾點:

  • 缺乏一致性,因爲是手工操作所以服務器之間總是有一些差異

  • 效率低下,一個人可以管理的服務器數量非常有限

  • 自動化大躍進時代


業務數量的增長,很快使得機器的數量超過手工操作維護的極限。無論再爛的團隊,只要業務長到這個份上了,必然會出現大量的自動化工具用腳本自動化執行的方式快速地支撐業務。這個時代是一個黃金時代,運維真正長臉的時代。因爲沒有自動化的運維技術,業務就會遇到瓶頸。自動化技術的引入,切實地體現成了業務的收益。


這時代的特徵是兩個關鍵的系統

  • 把本地 excel 表格裏的 IP 地址用數據庫的方式管理起來,稱之爲 CMDB

  • 基於 SSH 或者 agent 的分佈式腳本執行平臺


效率低下了不再是主要問題,主要的弊端變爲了:

  • 大量的腳本,雜亂無章,內容重複,質量難以保證,最終給故障留下隱患

  • 沒有對現網預期狀態的定義和管理,所有的現網狀態都是腳本日積月累的產物,導致服務器狀態漂移,產生雪花服務器(每個機器都不一樣),進而給業務穩定性留下隱患


這些弊端短期對業務來說並沒有立竿見影的傷害,屬於內傷型的。而且很多隱患即便暴露了也會流於強調紀律,強調運維意識云云。很少會有人去追究背後的運維理念的問題。結果就是大部分公司都停留在這個階段了。畢竟運維是一個足夠用即可的支撐領域。運維搞得再高科技,特高可用,未必和創業公司的成功有多少直接聯繫。


開發鬧革命時代


伴隨 DevOps 同時出現的是 infrastructure as code 的提法。簡單來說就是一幫開發殺到運維領域之後,看見這些運維居然是這樣去管理現網狀態的。於是他們把寫代碼的經驗帶過來,將現網狀態建立成模型(所謂 code),把預期的狀態提交到版本控制中。就像寫代碼一樣,去管理服務器配置。


很多後臺開發主導的小創業公司直接跳過了上個時代,運維自動化體系從一開始就是基於 puppet 和 chef 來搞的。平心而論,用 puppet 的更多是缺少歷史包袱,而不是因爲運維問題有多複雜。很多管理的機器數量不超過十臺,卻在如何使用 puppet/chef 上浪費大把時間的團隊也是有的。相反很多大公司因爲有沉重的歷史包袱,和龐大的傳統運維團隊,這種開發鬧革命的路反而走不通。


這種做法主要是解決了腳本的管理問題,而且因爲直接定義了現網狀態,服務器之間的一致性也會好很多。但是光鮮亮麗的模型背後本質上還是一堆腳本來驅動的。上個時代的弊端只是經過了包裝和改良,並沒有辦法根除。


應用預期狀態到現網依靠的還是跑腳本。而且與之前不同,現在更多的是跑別人寫的cookbook了,質量也是良莠不齊的。


雖然定義了預期的現網狀態,但是起點不同(比如從a=>c, b=>c)需要做的升級操作可能完全是不同的。要編寫一個面面俱到的升級腳本其實非常困難。


還有哪些問題?


一致性和穩定性是最大的問題。服務器開機之後,常年是不重裝系統的。無數人在上面跑過腳本,執行過命令,定位過問題。服務器實際的狀態是沒有辦法精確管控的。infrastructure as code 是一種改良,但是仍未根除這個問題。每一次在服務器上跑腳本其實就是一種賭博,因爲沒有兩臺服務器是完全一樣的。在本地測試可行的腳本,未必在另外一臺上不會引起問題。這不是強調一下代碼裏不能 rm * ,而要 rm path/* 就可以解決的問題。


版本管理其實一直是沒有的。做過開發的人,可能還會用 git/svn 來作爲部署的基線,基本的版本都會提交到倉庫裏。更多的一線運維用的還是 rsync 的模式。rsync 的意思就是要安裝一個新服務器,需要找一臺“與之最像”的服務器。然後把文件拷貝到新服務器上,把配置修改一下,啓動完事。攜程出事了,我個人猜測應該與版本管理混亂有關係。


故障替換是非常困難的。先不說故障替換,就是故障機剔除就是一個頭疼的事情。比如ZooKeeper。各個客戶端都硬編碼三個 ip 地址。一旦其中一個 ip 掛掉了。zookeepr按照高可用協議可以保持正常,但是長期來說這個掛掉的ip還是要從各個使用方里剔除的。這個就且改了。一旦業務的高可用做得不好,需要運維來搞一些接告警之後替換故障機的事情,那就是各種腳本折騰各種配置文件的節奏了。


Docker 是如何掀桌子的


兩點神論,進入到 Docker 時代之後

  • CMDB 不再至關重要了。CMDB 連同IP,以及服務器資源變成底層藍領工人關心的問題了。上層的後臺開發和業務運維不再需要也無法再以 IP 爲中心的 CMDB 來管理配置。

  • 分佈式腳本執行平臺從核心作業系統退居二線。很簡單,服務器不再需要變更了,常規的上新服務器,發佈新版本都不再依賴腳本在一個已有的服務器上執行去修改狀態。而是創建一個新的容器。


Docker的實質是一個真正的版本管理工具。在 Docker 之前版本管理是各種拼湊的解決方案。什麼是版本,服務器是由三部分組成:版本、配置、數據。所謂版本就是操作系統,以及操作系統的配置。各種第三方包,開發給的可執行文件,和一部分配置文件。這些的集合是一個版本,其實就是一個完整的可執行環境。除此之外一般就是一個數據庫,裏面放了兩部分內容,一部分是管理員可以從頁面上修改的配置,一部分是業務數據。在 puppet 時代的版本,是一個申明文件。這個申明文件執行的時候,需要先從某個 ISO 安裝出一個操作系統,然後用 apt-get/yum 從某個鏡像源安裝一堆系統的包,然後用 pip/bundle 安裝一堆 python/ruby 語言層面的包,最後纔是開發給你的 git/svn/某個不知名的tar.gz。你以爲這些東西每次拼裝出來的東西都是同樣的版本麼?其實未必。想當年某牆幹掉 github 的時候,不知道多少人無法做發佈了。Docker 打包出的連繫統在一起的鏡像,其實是對版本的最好闡述。


使用 Docker 之後不再需要修改現網的 container 了。一個 container 如果需要升級,那麼就把它幹掉,再把預先做好的新的鏡像發佈成一個新的 container 替換上去。分佈式腳本執行,變成了分佈式容器替換了。當然這種標準化的操作,用 mesos marathon 已經完美解決了。


使用 Docker 之後,無法再基於 IP 做管理了。倒不是給每個 container 分配一個 IP 分配不過來,而是 IP 代表的靜態模型無法跟上時代了。基於 IP 管理,就意味你會基於 SSH 登陸這個 IP 來管理。這種思想從骨子裏就是落後的了。進程,進程組,模塊,set 這些纔是管理的粒度。至於進程是跑在哪個 IP 上的哪個容器裏,不再重要了。一圖可以說明這個問題:



上面這個擴容的按鈕點完之後有讓你填 IP 嗎?沒有!你只需要告訴marathon,我要32個進程實例。它就會去找這些資源運行這 32 個實例。業務最終需要的是 32 個進程,而不是 32 個 IP。IP只是運行進程需要的資源而已。實際運行的時候進程可能是在一個IP上啓動了32個端口,也可能是隨機分配了5個IP,每個各跑了一些端口。當然這些分配都是可以通過“約束”的方式表達的。而不是讓你去搞32個IP來,再跑個腳本去這些IP上部署這些進程。


The Missing Piece


拼圖遊戲就差最後這一塊了。Docker 做爲一個版本工具是絕對合格的。Marathon 以 Docker 的方式託管所有進程也是靠譜的。但是還不完整:

  • Docker鏡像作爲版本發佈到現網之後是無法運行的,因爲任何一個應用起碼都有好幾個服務要互相訪問。這些硬編碼在鏡像裏的 IP 地址換了一個環境是無法執行的。一個版本里任何配置都可以硬編碼,就是 IP 地址和端口是沒硬編碼的。

  • 擴容縮容可以很容易創建和銷燬容器,但是引用了這個容器的服務器的其他容器怎麼辦呢?

  • 發佈,故障替換都是同樣的問題


解決方案可以看這兩張圖:




方案其實非常簡單。把 app1 => app2 的網絡訪問關係,改成 app1 =local=> haproxy =network=> haproxy =local=> app2。通過在容器本地部署 haproxy “託管所有的端口”,也就是用 haproxy 在進程之間做聯線,而不是每個進程自己去負責連接網絡上的其他進程。


試想一下之前是在配置文件裏硬編碼 10.0.0.1:3306 是某臺數據庫。硬編碼是不對的,是要打屁股的。所以我們把硬編碼的 ip 地址改成 127.0.0.1:10010。這一次我們不再硬編碼任何 IP 了,我們只硬編碼一個特殊的端口號。每個進程都有一堆特殊的本地端口號用於訪問自己需要的上下游服務。這個端口號背後的進程到底在哪個 IP,哪個 端口,哪個 container 裏執行。做爲使用方不需要修改任何代碼(比如兼容什麼 ZooKeeper/etcd 神馬的),也不用關心。甚至這個端口後面是多個遠程的IP構成一個基於客戶端的高可用。代理甚至還可以做一些出錯換一個後端再重試的事情。


有了這種神器之後,擴容所容,發佈變更,故障替換都很輕鬆了。容器隨便新增,隨便刪除。網絡結構變化了之後,刷新各個地方的 haproxy 配置就是了。各種灰度,各種零停機替換方案都可以搞起。


名字服務與網絡


類似的方案有很多。最底層的方案是 SDN/IP 漂移,以及網絡的bonding。這種方案的特點是保持 IP 地址作爲最傳統的名字服務,妄圖延續其生命。


上層一點的方案是 DNS。再上層一些的方案是 ZooKeeper。


各種方案爭的就是服務如何註冊自己,如何彼此發現這個點。各種方案的優缺點可以自己去讀:

  • SmartStack: Service Discovery in the Cloud

  • DOCKERCON VIDEO: BUILDING A SMARTER APPLICATION STACK


btw,airbnb 在 13 年就把這套方案投入生產了。


最有意思的是把這種 haproxy 的方案與基於 SDN 的 IP 漂移方案做對比。haproxy 的就是替網絡做應用層進程之間聯線的事情,通過引入 haproxy 讓這種聯線更具有靈活性。 而 SDN 的方案是說,你現在的業務進程之間是通過 IP 之間靜態鏈接的,這種連接不夠靈活沒關係,路由器幫你整。一個 IP 掛掉了,可以把IP漂移到另外一臺機器上去繼續使用。其實就是在一個場景下實現兩個進程的重新聯線,突破兩 IP 之間靜態互訪的限制,給基於 IP 的部署方案續命。


兩者底層的技術是相通的。所謂 IP 漂移最後靠的是現代牛逼的CPU,和軟件路由技術。最後玩的都是用戶態轉發,dpdk神馬的。所以 haproxy 慢,轉發效率有問題神馬的,長期來看都不會是問題。用軟件來聯線,是趨勢。連路由器都開始這麼玩了,連硬件廠商都開始賣軟件了。


The Final Battle


集羣管理純粹變成進程管理,IP不再重要,狀態不再重要。CMDB會變得越來越邊緣化。


發佈變更不再是去修改服務器,而是新建銷燬容器,以及更新進程間網絡聯線關係。分佈式作業系統會越來越少用,跳板機就更加不允許使用了。


記住“immutable servers”這個提法吧,它終將會得到歷史的認可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章