【反思】你的基礎設施是什么版本?
我一直認為每個人都應該認同應用程序版本管理的重要性,但有人并不這么認為。在某些場合下,我就遇到了一些堅稱不需要版本管理的人,我盯著他們并且內心咯噔了一下。幸運的是,大多數專家同意版本管理是重要的,只是在方法上意見不一。應用程序版本管理方案的辯論就像討論tabs vs spaces或者采用什么方法把廁紙卷起來一樣。我花了數小時的時間來辯論某種類型的版本管理方法相對于其他類型的優點,我在這里要說的是,我不在乎版本管理的風格,我在乎的是什么還沒有版本化。
盡管經過多年的集體痛苦嘗試獲得生產環境中運行的是什么樣的代碼,我們的行業還是僅僅處于把相同類型的問題歸咎于基礎設施的初級階段。試想一下,如果你知道軟件構件的版本但沒有確定正在運行的底層平臺是什么,你真的能確定你的應用程序是什么嗎?就像將應用程序版本升級到3.x或到4.x,也許是因為你漏掉了某些需要知道的正在確切運行的軟件關鍵部分信息(子版本)。第一個版本號讓你朦朦朧朧地知道發生了什么,但就是這樣,給我們帶來了如下問題:
“ 你能告訴我你的基礎設施是什么版本?
能夠回答這個問題至少跟能夠回答應用程序版本一樣重要。
你是怎么考慮基礎設施的?你的應用程序所運行的平臺對你有什么樣的意義?
當你報告了一個bug之后,你問的第一個問題是什么?
你如何知道運行時應用程序使用什么共享庫?
運行應用程序的框架或服務的版本是什么?
操作系統是什么版本?上次打補丁是什么時候?
自從安裝了之后,操作系統的配置有什么變化?但更好的是,設置時用了什么安裝選項?
應用程序所連接的數據庫的版本是什么?
保護您的應用程序所需要的防火墻規則是什么?
需要運行什么計劃工作來發揮應用程序功能?是否可以從另一個系統中調用?
你是否出售(比喻或字面意思)盒裝軟件產品,以至于無法控制軟件在哪里運行?在這種情況下,這篇文章可能不會非常有用,因為你運氣太糟糕了,除非你可以在一個容器中運行它,如果你能做到,然后就繼續閱讀下去。
如果你的答案涉及SSH登錄沙盒或檢查維基百科,你應該繼續閱讀下去。
聚集于永恒不變的基礎設施
在術語“永恒不變的基礎設施”使用之前的二十年里,軟件配置管理(SCM)是學術和專業人士的研究領域。這門學科非常寬泛,因為它聚集于可配置的軟件系統狀態的各個方面。然而,基礎設施內部的狀態變化是非常大的,所以為了公平起見,我們不能忽視SCM社區的努力而就一開始就討論永恒不變的基礎設施。此外,這門學科的創建帶來了生產環境應用程序可知性的飛躍,因為它給軟件部署帶來了一些小小的自我意識,因而基礎設施的狀態也可以變化了。
我不能確切的找出誰創造了“永恒不變的基礎設施”這個術語,但我從Chad Fowler的2013年歸檔沒有找到參考,直到2014年一月,這個術語的搜索才開始大量增長。我覺得這個術語有一個很好的近似表達但最終按字面意思解釋不是那么準確的描述。我不想說,完全不變的基礎架構是不可能的,但也有可能底層硬件狀態發生變化,所以我不認為在我一生中的任何時候會發生。你可以認為不變性是光譜的一端,一端有一個未知的可能變化的基礎設施,在另一端有一個完全可知不變的基礎設施。從這個意義上說,不變性可以與可知性可以劃等號。因此,如果你不知道狀態——你怎么可能聲稱與前一個狀態相比沒有變化呢?
所有的基礎設施都會在一定程度上改變,否則不會有用。沒有狀態變化,計算機將會咋樣?如果沒有輸入,拿什么進行計算?鑒于目前軟件系統復雜度的狀態,實際上沒有人能百分百地預測在任何給定的時間比如當一個Web服務調用時系統會運行什么指令?對于由應用程序執行的profile或者指令,當然可以有上百萬的參數,但是一個經典的多線程操作系統每天結束的時候都會充滿了無用的垃圾信息,我不相信任何一個非學術人員可以對于一個完整系統的實際指令有完整的認知圖譜。在我們開始更深入地去查看宇宙射線對于各種系統狀態的影響之前,很多事物都是相互作用的。這就是為什么停機問題是如此令人困惑。對于我們這個時代的經典系統,你甚至不能確定什么軟件實際上正在運行,以及未知的應用什么時候將結束。就是說,我確信在一定的理論意義上我是有技術性錯誤的,但我相信我實際上是正確的。
現在,如果我們遠離不變性的絕對定義,朝向更實用的不變性形式,這意味著什么呢?首先,這意味著更少地討論計算機學術問題,而是更多地討論有關創建人類可知系統的工藝技巧。因此,作為基礎設施的一種類型,我們可以得出一個對于永恒不變的基礎設施的可操作性的定義,既可以將未知狀態的改變最小化的基礎設施。
在虛擬機時代之前,技術上成熟的組織非常小心地標準化應用服務器集群的硬件和操作系統。這原先是一個手動的過程,現在借助于磁盤鏡像工具或網絡啟動技術變得日益自動化。這些都是巨大的進步,讓我們接近了一個更不可變的應用程序主機系統的定義,因為它們提供了關于運行應用程序的系統的初始狀態的可預測性。
此時,構建機器的安裝腳本基于專門的原語創建,沒有標準化。每個公司都有自己的構建系統,隨著時間的流逝,從同一個起點開始發展的不同系統會開始慢慢地發散,變得不那么可預測。它們可能有共同的模式,但沒有一個明確的版本可控的機制定義基礎設施和有爭議的磁盤鏡像異常。然而,我們也開始看到了更永恒不變的基礎設施的先驅者,在用戶登錄和恢復系統到預定狀態之間使用工具來重啟系統。
雖然VMware成立于1998, 但直到2001后,我們才開始看到虛擬機在數據中心的廣泛使用,這些技術一旦出現,運營效率開始了靜悄悄的革命。而通過提升底層硬件的利用率提高性價比(從功耗和能量密度而言),虛擬機也改善了基礎設施的可知性,這是通過允許將磁盤鏡像的自由交換作為操作系統定義的通用特性來操作的。虛擬機鏡像不僅可以提供一個已知的起點比如物理磁盤副本和網絡引導配置,還提供了快照副本,讓你可以恢復到已知的狀態。突然地你可以有效地開發標準化的操作系統鏡像從而可以在組織內分享。然而,虛擬機磁盤鏡像的大小限制了在開發者之間解決方案的可重用性,這恰恰是運營方的收益之處。
后來,構建和配置虛擬機的工具開始成熟,我們開始看到了各種使用腳本或配置文件來配置虛擬機基礎磁盤鏡像的工具,諸如Puppet、Chef和Vagrant這樣的工具都是按照這個原則工作的。通過對基礎虛擬機存儲標識符,還需要一些必需的構建步驟為應用程序安裝虛擬機,應用程序是版本可控的,這樣我們就離永恒不變的基礎設施的目標更近了一步。然而,這個模式有一個問題,就是沒有辦法保證虛擬機賴以運行的基礎磁盤鏡像針對不同的虛擬機是一致的,除非采用相同的工具集來定義。這導致了在從內部集成的運行VMware的服務器切換到公有云上的生產環境服務器時運行安裝腳本失敗。由于缺乏可移植性,各個操作系統經常有小的配置差異,所以經常導致很多重大的棘手問題,但這些解決方案的凈效益如此之好所以得到了廣泛應用。
一個舊的解決方案重出江湖
與此同時,腳本驅動型的配置開始江河日下了,基于云的PaaS平臺的市場份額日益增長。相對于腳本驅動配置和虛擬磁盤鏡像,PaaS承諾可以簡化可知的基礎設施。你可以開發一個應用程序然后運行在別人的基礎設施上。只要你的應用程序符合平臺的局限性,你就不必擔心底層的基礎設施。你可以移動滑塊來創建更多的程序運行副本,然后自動添加到負載均衡器。這是一個驚人的承諾——現在我們可以將基礎設施外包了。對于簡單的應用程序,這種模式過去可以很好的工作,現在仍然可以運轉良好。這就是為什么很多創業公司都會在Heroku上部署它們的應用的原因所在,在簡單的使用案例中,它可以節省你很多時間。然而,一旦你的應用程序開始需要更多的基礎設施底層資源,這種模式就無法承受了。大多數PaaS供應商都提供了定制基礎部署鏡像的方法,但是通常都是缺少文檔的,操作繁瑣并且只是針對某個具體供應商的。
近年來,容器化作為IaaS和PaaS中間的解決方案獲得了發展動力。有一個重要一課就是Docker作為容器的一種實現脫穎而出。Docker是由一家PaaS供應商DotCloud創造出來從而開啟了容器革命。容器化技術已經存在好多年了,從BSD Jails(2000年3月)到Solaris Zones(2004年2月)直到現在的LXC(2014年2月),我們看到了一個奇怪的降級特性集。Jails和Zones是更加成熟的技術,為什么LXC(也就是Docker)卻有這么大的影響力呢?可以說是因為Docker是Linux原生的,或者你可以說這是因為Docker關注開發者體驗的緣故。我想說的是,成功的關鍵不僅在于開發者體驗,也在于帶來了創建操作系統平臺的可知性。
對于Docker,你可以絕對肯定底層鏡像是不變的,同樣的判斷,對于虛擬機、Zones和Jails也成立。然而,關鍵的區別是對于基于不變的基礎設施擴展而來的上層每一個變化,都非常容易版本化和易發現。你獲得了一個按照Dockerfile格式社區標準化的文件,接著你可以追蹤制作平臺鏡像的構建步驟。每一個構建步驟都有它自己的鏡像并且可以和其他開發者共享,這使得另一個潮流可以進入軟件開發生命周期(SDLC)。現在針對運行應用程序的基礎設施可以獨立于應用程序版本化,有著它自己的SDLC和繼承模型,并有平臺鏡像專家在應用范圍之外來修改,所有的這些修改都是可知的,并且會被記錄下來。
分離基礎設施的SDLC和應用程序的SDLC現在非常容易
#p#
如果你不知道交付那容器有什么益處?
容器可以縮短基礎設施內的不可知的可變的時間,但是它不能解決底層主機平臺的可變性問題。我們只是對它的運行有信心,無論什么咒語我們也可以盡我們所能抵御來自于運行容器的主機的惡魔。然而,即使容器是朝著實現永恒不變的基礎設施的目標是一個偉大的進步,它只是一個單一的計算單元(即機器),他們沒有解決所有依賴系統的交互,比如通過網絡或者其他IO通道與其他應用交互。換句話說,版本化容器沒有解決一切除非你連接上它,但這是朝準確方向邁出的一步。
在某種程度上,其他系統(不是版本化的應用程序)都是可知的,我們的目標應該是引導它們走向一個一成不變的模式。這意味著我們需要對于系統的每一個模塊以及連接這些模塊的組件的所有版本有可知性。在這種模式中,你將可以洞察數據存儲、網絡設置和任何耦合服務的狀態。如果你能得到一個連貫的版本標識符,那么你將能夠構建一個完整的虛擬數據中心的版本標識符,這是純粹將單個組件的版本組合之后的散列值。記錄版本號并將它們關聯到一個版本化的產品就是持續集成和持續交付系統的通常工作。
怎么樣用版本表示虛擬數據中心可知狀態的一個例子
然后,可以通過追蹤構建系統的每一個版本號,并最終實現源代碼管理,可以在應用程序中直接嵌入版本號,這樣錯誤報告工具可以直接關聯整個基礎設施已部署的版本。
通過中央版本標識符,你會有一個中央權力機構,在一個抽象邊界(虛擬數據中心)內提供每一個單獨組件狀態的可知性。通過上述的版本ID,如果我想知道Rest框架正在運行的版本和為微服務提供請求服務的端口是什么,辦法非常簡單,諸如解析版本ID:micro-2.1-os-0.9,然后在源代碼管理系統中找到關聯的版本。關鍵點在于你的主版本標識符可以被解析這樣就可以找到產品中每個組件的準確版本,因為這些版本信息存儲在源代碼管理系統中。此外,它應該是可組合的,這樣就可以作為另外一個組件版本號的一部分,例如上圖中的版本號可以嵌入一個更大的軟件系統中。
讀者練習
正如我一開始提到的,我對沒有版本化的擔心甚于正在版本化的。本著這種精神,我已經為你的娛樂準備了一份問卷,然后檢查你正在做的每一個練習。
我忘記了什么版本?
- 所有從源代碼編譯而來的并且部署到生產環境中的軟件都存儲于源代碼管理系統中,你是否確定如此?
- 所有不從源代碼編譯而來的軟件(第三方二進制包)都有版本標識符。
- 我們知道并且可以追蹤組件依賴的所有的軟件庫的版本信息。
- 我們有系統設置可以檢測收斂的依賴關系,無需這些軟件就可以自動升級。
- 我們代理所有外部組件系統(比如Maven、Nuget、NPM、CPAN、gems等等),保證添新加庫的版本可以被發現和取證分析。
- 我們從不在我們的基礎設施上執行curl – shttp://dodgysite.com/useful-utility.sh | sudo bash,對吧?我們知道下載一個未知的版本會宕掉整個數據中心。
- 我們應該明白在基礎設施中什么被配置了,什么沒有被配置,你確定是否如此?
- 我們的構建和編譯環境、選項、鏈接庫和優化標志都來源于版本標識符。
- 我們知道客戶端通訊期待需要的所有API的版本。
- 我們知道服務器需要提供的所有的API的潛在版本。
- 我們的負載均衡、反向代理、應用交付控制、SSL終端設備或者任何網絡基礎設施的版本對于其他版本化的產品都是已知和可信的。
- 我們為數據存儲維護一個清晰的版本schema,了解運行時的數據存儲結構,并且可以關聯到任何版本的訪問器。
- 我們的所有防火墻規則都是文檔友好和版本化的,這樣的話沒有人可以在我們應該知道在哪里的地方偷偷摸摸的進行改動。
- 我們所有的網絡連接都是版本化的,了解什么組件在相互聯系,什么組件沒有,這樣發生變化時可以追蹤網絡組件是如何耦合的。
- 我們知道所有重復性工作的設置,我們確信這些是正確的設置。
- 操作系統的每個組件都是可以被清晰鑒定的,版本也是可發現的。
- 操作系統的每個組件配置都是版本化的,并且絕對可以確定是從已知狀態啟動軟件的。
- 所有的版本標識符都可以關聯到源代碼管理系統,或者這些標識符對于開發商是可以理解的。
- 所有的版本標識符都應該存儲在對于開發者、運營和產品經理有用的地方,這樣任何人都可以知道在生產、試運行或者測試環境中運行的版本是什么。
- 所有特定用途的硬件被記錄在案,底層硬件系統可以在前后一致的狀態中重建,不過這并不重要,因為我們可以使用容器或虛擬化技術。
- 如果我們使用容器或者虛擬化,底層的虛擬化或容器化框架設置應該是文檔友好的并且可被引用的。
- 當構建操作系統鏡像時,我們使用的框架應該是允許版本化的,并且可以將與部署相關的版本信息通過某種方式存儲下來。
- 對于下載并安裝在操作系統鏡像上的二進制發行包需要檢查其散列值。
- 我們需要允許進行產品調試分析和測試的系統設置,這樣的話我們就不必違反版本化的核心原則(比如開發人員在生產環境中沒有SSH終端來打開源代碼來診斷問題)。這些系統可以是金絲雀部署工具(比如Distelli)、適合生產的分析工具(比如Dtrace)或者應用程序性能監控工具(比如New Relic)。
- 當我們的軟件dump時,我們知道版本信息并且可以準確地在源代碼或者二進制發行包內找出原因。
- 我們知道是誰在什么時候在哪里改了什么東西。
我想我在寫上述這些列表的時候有一些職業回溯,因為它涉及到了一些痛苦的自我反省(請不要批判我的GitHub賬號!)。
長久地注視那些你還沒有檢查的列表選項,它們是否重要?如果不將它們與版本化關聯,會有什么風險?沒有版本化會有什么益處?
一旦你有了一個良好的對于收集的版本的處理方式,在一個對于你的組織有用的地方存儲版本標識符是很重要的。在Git里通過文本文件可以告知你什么將會被推送到試運行或者生產環境,這個文件非常有用,但更重要的是,有這樣一個系統存在。
下一步
我已經針對如何盡可能的設計一個擁有可知狀態的系統制定了概念性的框架,自然地,下一個問題就是:我們怎樣構建它?設置需要多少工作?對于可知性和生產力如何權衡?是否可以使用已有的項目來設置?
在關于永恒不變的基礎設施系列的第二部分,我講會回答上述這些問題并且深入探討你該做些什么使其工作在云端。