天天看點

你真的了解Docker嗎?——Docker插件機制詳解

雲栖techday活動第十八期中,阿裡雲容器服務團隊的核心成員陳萌輝帶來了題為《docker插件機制詳解》的分享,分享中,他結合阿裡雲容器服務實踐介紹了docker插件的基本原理、實作方法以及插件機制未來的演進。

以下為現場分享觀點整理。

<b>為什麼需要docker插件?</b>

你真的了解Docker嗎?——Docker插件機制詳解

docker之是以這麼火并且有很多人願意使用它,其中涉及到很多方面的因素,例如功能性以及隔離性等各種各樣的原因。其中docker的開箱即用功能是一個非常具有特色的優點,docker安裝後即可使用,無需再做其他的配置;同時docker自包含的鏡像提取出來就是一個完整的系統;此外,它自帶網絡、程序、檔案系統的隔離,使用者可以很友善在一台機器上部署各種互相隔離的系統。是以,docker的應用就非常簡單、友善,但是它這種簡單友善也帶來一些問題。docker把一些場景固化掉了,例如docker的網絡模型和存儲,固化之後,當特定場景不符合docker預期的要求時,docker就無法滿足要求,此時需要重新定制docker。例如在你的網絡環境裡面,ip位址是預先配置設定好的,跟docker預留的不比對或者是你想自定義一個容器間互互相聯一個網絡,docker預留的那些方式滿足不了需求時,需要對它進行擴充。

<b>docker插件機制簡介</b>

docker公司對docker的擴充分成三個級别,從低到高分别是:user-facing api,該api是docker提供用于串聯出一些場景的api;第二層是插件(plugins),也是今天的主要介紹内容,plugins和docker是兩個互相獨立的程序,它們之間通過一些預定的通訊協定進行功能擴充;第三層是叫drivers,是docker實作功能的一些驅動,例如一些檔案存儲的驅動。目前,大多數drivers都是官方出的,當有特定需求或者是用到獨特技術時,開發者可以自己寫相應的驅動。

你真的了解Docker嗎?——Docker插件機制詳解

docker的插件是增加docker引擎功能的程序外擴充,也就是說它和docker engine是兩個獨立分開的程序。插件運作在docker daemon外,通過一些插件的發現機制和預定義協定進行互動通信,這樣做的好處是:當更新插件時,無需重新重新開機更新docker。

目前docker官方提供的插件的類型有四種:

第一種是授權(authz)。可以寫一個插件帶對每一個請求做驗證,例如,可以對公司的通信錄或者權限系統綁定,然後給一些請求做授權,使得一部分人能夠執行檢視類的操作,另外一部分人可以執行建立删除類的操作,這樣就可以将權限系統與docker結合起來了。

第二種是資料卷(volumedriver)插件。用于提供不同的存儲功能,是應用最多的一類插件。

第三種是網絡(networkdriver)插件。該插件用于提供容器之間網際網路路模型,這種插件也比較普遍,例如阿裡雲容器服務提供了針對阿裡雲的網絡的網絡插件,在vpc下,所有容器都是互相可以通路的,而且是沒有性能損耗的。

第四種是ip位址管理(ipamdriver)插件,當需要對ip位址的配置設定進行保留時,可能需要該插件。

你真的了解Docker嗎?——Docker插件機制詳解

下面介紹一下docker插件的發現機制。docker規定了三種插件發現的機制,因為每一個插件其實它本身就是一個http伺服器,docker是通過http通路和插件進行通信的。那麼docker是如何知道http伺服器的位址呢?它規定了三種方式:第一個是.sock檔案,在/run/docker/plugins下寫一個unix socket檔案,則docker認為這是一個插件,而且該檔案名就是插件的名字;第二類是.spec檔案,它是一個純文字檔案,檔案内容是插件通路位址;第三類.json檔案,它包含的内容比.spec檔案内容多,包括插件的name、addr、tlsconfig。

你真的了解Docker嗎?——Docker插件機制詳解

上文提到了插件和docker 引擎是兩個獨立進行,但插件的生命周期是由docker的官方文檔所規定的,插件必須先于docker daemon啟動,後于docker daemon停止,也就是插件的生命周期必須要長于docker daemon。但在具體是實作時,由于第一次通路插件時才激活,例如一個存儲插件,當第一次通路這一類存儲或建立這一類儲存卷時,才會真正激活插件,是以插件其實可以晚于docker daemon啟動,也可以在docker結束之間結束,這也為之後将插件放到容器裡面提供了很好的條件。

docker官方提供了插件sdk,使用者所需的發現相關的功能,在sdk中大部分都自帶,是以隻需簡單引用即可,将精力解放于如何實作問題上。

<b>docker資料卷與存儲插件</b>

你真的了解Docker嗎?——Docker插件機制詳解

下面結合資料卷來講解一下存儲的插件。提到資料卷就不得不提docker的分層檔案系統,docker之是以風靡全球,分層檔案系統可能貢獻了其中三分之一的能量。如圖所示,分層檔案系統的最底層是bootfs,是容器和資料集共享的一層;在其上是base image層和image層,最上面才是可寫入的container。對于一個容器來說,它裡面所有的檔案都是可能處于不同層内,當修改該檔案時,其實是修改的拷貝檔案。比如說,你修改了一個系統的檔案,其實在磁盤上,原來作業系統的檔案并沒有發生改變,而是拷貝出來的另一份(容器可見的檔案)才是被你修改了。但如果其他人引用了同樣一個作業系統時,所看到的檔案是沒有被修改的檔案。分層檔案系統有一個很大的優點,它可以把軟體的建構變成類似可視化的方式。但分層檔案系統同時也帶來一些問題:第一,它的系統性能差,因為在修改檔案時,有一個拷貝過程,勢必會導緻性能的損耗,同時在讀取時,存在一個回溯操作,也就是它要從下面往上找,找到對應檔案所在的層,這也導緻了它性能比原作業系統差。

另一個問題是它的生命周期和容器相同,也就是說啟動一個容器,在容器被寫入例如一些日志或應用相關資料,當容器一旦被删除,這些資料也随之消失,是以一個需要長期保留的資料是無法寫入容器的。

為了解決上述問題,docker提供了資料卷功能,資料卷是将主機檔案夾挂在容器内部,繞過分層系統,相當于容器内部直接讀寫某主機上的某一檔案夾。

你真的了解Docker嗎?——Docker插件機制詳解

這樣一來它的性能和主機性能是一緻的,生命周期也脫離了容器的生命周期,在容器挂靠之後,資料依然存在主機的磁盤中。上圖形象地展示了這一過程,當用docker run -v建立一個資料卷時,其實是把這一檔案夾從主機挂到容器内部,該檔案夾其實已經存放在主機上了。

但docker官方提供的資料卷也有一些缺點:第一,僅能夠讀寫本地磁盤,當通路網絡資料時,資料卷就無能為力了;另外,資料卷不能随容器遷移,因為它是讀寫本機的磁盤,如果容器從a機器遷移到b機器,它無法幫你把資料一塊遷移過去,無法實作共享資料。

你真的了解Docker嗎?——Docker插件機制詳解

那為了解決上述問題,阿裡雲容器服務提供了兩類網絡存儲資料卷:第一類是ossfs資料卷,第二類是叫nas資料卷。

你真的了解Docker嗎?——Docker插件機制詳解

存儲插件的功能是管理資料卷的生命周期,它的主要工作是将第三方存儲映射到host本地檔案系統中,以便同期使用該資料卷。docker本身可以使用本地的資料卷,如果沒有修改docker是無法直接通路網絡資料卷的,将網絡資料卷映射到本地檔案系統的過程如上圖所示:通過指令行給docker daemon發送建立資料卷的指令,然後該指令通過plugin apis通路到volume drive插件,然後該插件将第三方的存儲映射到主控端上面,就可以實作網絡資料卷的通路。

你真的了解Docker嗎?——Docker插件機制詳解

docker定義了一些存儲插件api,是docker下發給存儲插件的,需要按照api的格式傳回相應的資料,一共有七個api:

/volumedriver.create:當需要建立資料 volume 時,調用該api

/volumedriver.remove:當需要删除資料volume時,調用該api

/volumedriver.mount:容器每次啟動時都會調用該api一次

/volumedriver.path:傳回volume在主機上的實際位置

/volumedriver.unmount:容器每次停止時,調用該api

/volumedriver.get:docker volume inspect時,調用該api

/volumedriver.list:激活插件時,調用該api,用于詢問目前已有的volume,防止重複建立。

從這些api可以看出,對存儲插件而言,docker daemon所做的事情很少,它就是将這些指令轉到volume plugin,然後所有的工作,包括所有狀态的存儲都是由volume plugin自身完成。 

資料的存儲插件的需要有一些持久存儲的東西。例如某些特定的參數持久化下來,當下次重新開機時,可以再次還原回來。

寫一個存儲插件時非常簡單的,隻需要把上面的七個請求實作即可,熟悉的人兩天就可以寫寫完,不是很熟練二代開發者一周内也可完成。但是功能完成後,還有其他的事情需要考慮。 

你真的了解Docker嗎?——Docker插件機制詳解

第一需要考慮是否可以在容器中運作插件。因為插件和docker daemon是兩個完全獨立的程序,但如果一個插件是在主機上的一個程序時,是有一定的不便之處。因為它的生命周期中有docker,docker中沒有友善的方法進行管理。另外插件執行時需要root權限,對于容器服務的産品,它是不适合的。

目前的設計方式是将存儲插件跑到容器中,這樣docker就可以獲得管理權限和root權限。但是插件在容器内運用存在一個令人頭疼的額問題:容器的mount namespace。上圖比較簡單地示範了該問題,上面是主機的檔案系統,它真實對應了硬碟的檔案系統。每一個容器都有自己的檔案系統。容器的檔案系統和主機檔案系統是完全隔離的,在容器内做一些mount操作,完全不影響主機上的檔案系統,主機上根本無法發現mount過的檔案。如果存儲插件運作在容器内時,無亂在容器内做任何mount操作,主機和其他容器都無法發現,這樣做就沒有任何意義。 

你真的了解Docker嗎?——Docker插件機制詳解

<b>突破mount space有兩種方式:</b>

第一種是nsenter,可以在容器中修改主句的mount點;第二種方式是docker1.10開始提供的shared_mount,它的意思是,雖然處于不同的空間,但是一個變化時可以通知另一個,例如容器檔案系統中,通過修改目錄,當容器裡面的插件由變化是,它會通知主機檔案系統,使得主機能夠看到該變化。

你真的了解Docker嗎?——Docker插件機制詳解

阿裡雲容器服務提供了三種資料卷:

ossfs資料卷:基于fuse,将oss bucket映射為本地檔案系統。

nas資料卷:基于nfs協定,按需擴容,高性能高可靠性。它的背景是一個高性能、高可靠性的服務。它預設資料是三份,不會丢資料。它是不同使用者之間會做隔離的,這樣的話a使用者的請求在性能上不會影響b使用者。

雲盤資料卷:将雲盤當成一個資料卷挂在容器中。

你真的了解Docker嗎?——Docker插件機制詳解

上圖對比三中資料卷的優缺點和使用範圍。

ossfs資料卷的優點在于它能跨主機共享,同時oss本身就是一個比較成熟的服務,是以很快能夠使用起來。但是它的缺點同樣明顯:首先讀寫/is性能低,讀寫性能低是由于每次修改檔案時,都會導緻檔案的重寫。is性能低是因為在擷取檔案時,需要伺服器将檔案羅列出來,當檔案較多時,往往會耗費10秒甚至更長的時間。ossfs資料卷的特點決定了它隻适合在小資料量、無修改的場景下使用。例如配置檔案和附件上傳。

nsa資料卷的優點也是跨主機共享的,同時它可以按需擴容,高性能、高可靠性,挂載速度高。nsa資料卷的缺點是成本略高。

它适應于一些共享資料的重io應用,例如檔案伺服器等需要快速遷移的重io應用。

雲盤資料卷目前尚未開放,還在等待中,但應該很快就會開放出來。它的優點就是性能很高;它的缺點是不能跨主機共享,一個雲盤隻能挂在一台主機上,無法同時挂載不同主機上。此外它的挂率也是比較慢的。因為雲盤資料卷适合如資料庫、redis等不需要共享資料的應用。

<b>docker插件系統的發展</b><b></b>

你真的了解Docker嗎?——Docker插件機制詳解

docker的插件的系統從docker1.6版本引入,目前是docker1.10版本。它存在一些曆史問題,現在新版本中也在改進這些問題。

第一個問題是它無法控制啟動順序。現在提到的一容器化方式考慮存儲、網絡類的插件,很大的一個問題就是:插件必須在其他容器啟動之間啟動,這樣才能使得其他容器正常運轉;同時必須在其他容器停止之後才能停止,否則就會影響其他容器的使用。在以前的版本内,是沒有辦法很好控制這個事情,通過同docker的修改,在阿裡雲容器服務中可以保證插件在使用者邏輯前啟動。

第二個問題是插件分發混亂,缺乏統一的管道。例如開發者新寫的插件想共享給别人,缺乏統一的方式和行為規範。

在docker1.12中開發了一些特性。第一個是highly available services,它可以把插件獨立于其他容器對待,使得插件和docker engine同時啟動停止。

同時,它還引入docker store,使得插件以鏡像的形式分發。

你真的了解Docker嗎?——Docker插件機制詳解

在docker1.13以及以後,他們列出了一些後續開發的項目:

per node plugin,保證plugin部署在每個節點上。

swarm-deployed plugins,可以在叢集範圍内部署plugin,集中管理插件。

提供編排插件,目前編排都是官方提供的,現在計劃将該功能開放出來,使得排程可以引入第三方排程政策,完成特定需求定制。

繼續閱讀