Docker:不是一個鏈接器

jopen 9年前發布 | 14K 次閱讀 Docker
 

【編者:作者應該是個Geeker,喜歡思考很多本質性的東西,并開創性的將Docker跟鏈接器來作比較,并提出了很多有意思的觀點如:應用程序棧的鏈接器等。不管對不對,至少他這種思考的方式值得我們學習。】

我必須告訴Docker,他們的創意完全滿足我對“天才般創新”的標準,Docker讓我花了一段時間來弄明白它到底是什么。

偉大的創新絕對不是發明能飛的汽車,而常常是一些很簡單但是細細想想背后的原理非常復雜的一類事物。舉個例子,比特幣就是一個絕好的發明。它很簡單,但是背后的故事非常復雜,仔細想想很多人都會發出疑問,它可以流通嗎?有股票價值嗎?可以用于債券嗎?這其實是個分類的問題,可能把所有的這些不同方面的屬性結合在一起就是個新的事物。

Docker絕對不是一個像OpenVZ或者BSD jails一樣的容器。當我第一次使用Docker的時候,我費了很多時間在找如何在一個運行的容器中配置一個/dev的node;最后我終于理解了 Docker:其實你沒有必要這樣做,你只需要啟動一個/dev的node的容器即可,Docker的容器是進程,而不是一個完整的虛擬機系統。

Docker是一個新的虛擬機:在不運行時表現為“靜止”(immutable)包含運行程序的容器(其實就是鏡像),然后啟動此鏡像后在系統中表現為一個進程。

但是,Docker的本質是什么呢?

這個問題我思考了好幾天,最后我頓悟了:Docker是個能夠將手動鏈接變得輕松的工具。更具體的說,是個可以手動鏈接應用程序還能將其保存的工具。(作者的意圖是從鏈接這個低層工具出發來思考Docker的價值,因為低層的鏈接器就是將不通程序模塊組合成一個程序來運行,從哲學層面上將確實跟 Docker做的事情有些類似,但是維度不一樣。)

但是后來我又仔細的思考了一下,好像又不是這樣的。

鏈接器是什么

如果你使用過gcc,clang或者Visual Studio,你就在使用編譯器來構造可以執行的程序。對于C或C++來說,編譯器會將源代碼編譯成一堆的目標文件;目標文件中包含了源代碼對應于目標平臺的機器代碼(或者是中間代碼)。但是,要使程序能夠運行,你的代碼一定會調用其他模塊的代碼——函數;所以鏈接過程就是把你寫的程序中所有依賴于其他模塊、庫中的代碼收集起來并鏈接到你的目標代碼中。(注:關于程序是如何執行這個問題,可以參考譯者之前的博文:程序是如何運行的)

在維基百科中有關于“鏈接器”的簡明的解釋。 舉個例子,比如你使用C語言寫了個程序,調用了“printf”這個函數,代碼通過編譯器、鏈接器處理過后會得到一個目標文件,中間會有這么一段“從這里開始調用printf函數”;所以printf不是我程序的一部分——它是一個叫做C語言標準庫的一部分(注:glibc,C語言運行時,分為靜態鏈接與動態鏈接兩個版本。)。鏈接器會分析glibc的靜態庫文件,收集我的程序中所需要重定位的符號信息,包括printf這個符號;然后將printf的代碼從glibc中抽取出來,鏈接到我的程序中,組成一個完整的可以運行的可執行文件;這樣我就能通過printf函數向終端輸出字符信息了。

但是真實環境更加復雜,還有個叫做動態鏈接的概念。很多時候,除開我使用Go語言,我不希望我的可執行文件過大;我會更加希望我編譯出來的文件模塊分明,不同的模塊包含在不同的文件中,我的入口程序在運行時來決定加載哪個模塊并運行——這就是動態鏈接。動態鏈接做的很多事情跟靜態鏈接類似,只是將鏈接的過程延遲到運行時完成罷了。

但是從概念上講,什么是鏈接器呢?

當程序員談起”鏈接器“時,他們第一反應是C/C++語言編譯出來的目標文件。但是,我們回過頭想想,忽略一些細節,鏈接器到底干了些什么呢?鏈接器的工作是:能夠自動完成一些將分散的代碼組合起來的復雜工作的程序。從學術上來說,Unix的”ld“,Windows中的”link“都是鏈接器;而廣義上來講,任何能夠自動將代碼片段鏈接在一起運行的程序都是鏈接器,比如以膠水工廠著稱的 SWIG

二進制元(META-BINARIES)

想象一下沒有鏈接器的情況,任何時候你生成一個程序后——或者運行一個需要眾多動態庫支持的程序——你都必須手工打開hex編輯器,從二進制代碼的層面手工進行符號收集與重定位工作。但是,廣義上來講,你不用去想象,你已經在做這部分工作了,只是層面不同,比如,你需要配置一個LAMP系統的時候,你需要編輯很多配置文件使各個系統能夠配合運行通暢,再比如,你需要增加Redis或Memcached組件時,你必須要更改配置文件一樣;你在進行手工鏈接,鏈接不同的程序。

再深入一點去思考的話,程序的鏈接跟應用程序級別的配置沒什么兩樣,只是前者是鏈接低層代碼段,后者是將不同的模塊、系統拼接起來運行。但是,市場上并沒有專注做“應用程序棧”鏈接的“鏈接器”,你必須要手動配置他們的運行參數來實現它們之間的協同工作,如:設置TCP監聽端口,設置訪問權限規則等等。這其實是高層次的鏈接過程,應用程序之間靠協議通行或者 ABIs 的調用。

Docker不是鏈接器

所以Docker是鏈接器了?答案是:不是!

Docker實際上是提供了一個環境,你可以在這個環境中將你要的”應用程序棧“配置好,然后保存你的設置,形成鏡像;然后你就能復制這些鏡像,多處運行,而不需要再次進行配置。

Docker還包含了很多配置工具,你可以配置容器運行時的網絡,運行時訪問安全,配置VXLAN overlays來實現同一個數據中心或者局域網中不同容器間的互訪。但是這些都是附加屬性,Docker的核心是可以讓你保存你的手動鏈接結果。撇去這個功能,Docker只是部署、運行服務器端應用程序的另一種方式而已。(注:Docker的基于進程的虛擬機模型也是一個亮點。)

應用程序棧的鏈接器?

是不是沒有基于“應用程序棧的鏈接器”呢?也不盡然,也有些人在嘗試,但是都遇到了困難,導致都不知道要做成什么了。比如, ChefPuppetSaltstack 這些產品,他們都提供了很多附屬工具,但是核心功能是用腳本化的鏈接方式把小的程序模塊組合成大的應用程序執行棧來運行。

但是,使用過這些工具的人都覺得它們復雜與笨重;我覺得主要是因為開發它們的程序員錯誤將它們定位在企業級服務器管理工具上了。他們應該開發更加通用的系統會更加實用。

如果我們能達到這些,我們還會需要Docker嗎?

可能吧,因為Docker很有用。但是,如果我們擁有一個“應用程序棧”級別的“ld.so”(linux下的動態鏈接器)就不好說了;因為,相比我們把應用程序棧打包生成一個Docker鏡像,我們只需要一個動態鏈接器就能把不同的應用程序鏈接在一起工作了。

原文鏈接: Docker: Not Even a Linker

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!