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