1. 程式人生 > >微服務架構之「 容器技術 」

微服務架構之「 容器技術 」

現在一聊到容器技術,大家就預設是指 Docker 了。但事實上,在 Docker 出現之前,PaaS社群早就有容器技術了,以 Cloud Foundry、OpenShift 為代表的就是當時的主流。

那為啥最終還是 Docker  火起來了呢?

因為傳統的PaaS技術雖然也可以一鍵將本地應用部署到雲上,並且也是採用隔離環境(容器)的形式去部署,但是其相容性非常的不好。因為其主要原理就是將本地應用程式和啟停指令碼一同打包,然後上傳到雲伺服器上,然後再在雲伺服器裡通過指令碼啟動這個應用程式。

這樣的做法,看起來很理想。但是在實際情況下,由於本地與雲端的環境差異,導致上傳到雲端的應用經常各種報錯、執行不起來,需要各種修改配置和引數來做相容。甚至在專案迭代過程中不同的版本程式碼都需要重新去做適配,非常耗費精力。

然而 Docker  卻通過一個小創新完美的解決了這個問題。在 Docker 的方案中,它不僅打包了本地應用程式,而且還將本地環境(作業系統的一部分)也打包了,組成一個叫做「 Docker映象 」的檔案包。所以這個「 Docker映象 」就包含了應用執行所需的全部依賴,我們可以直接基於這個「 Docker映象 」在本地進行開發與測試,完成之後,再直接將這個「 Docker映象 」一鍵上傳到雲端執行即可。

Docker 實現了本地與雲端的環境完全一致,做到了真正的一次開發隨處執行。

一、容器到底是什麼?

容器到底是什麼呢?也許對於容器不太瞭解,但我們對虛擬機器熟悉啊,那麼我們就先來看一下容器與虛擬機器的對比區別:

 

上圖的左側是虛擬機器的原理,右側是Docker容器的原理。

虛擬機器是在宿主機上基於 Hypervisor 軟體虛擬出一套作業系統所需的硬體裝置,再在這些虛擬硬體上安裝作業系統 Guest OS,然後不同的應用程式就可以執行在不同的 Guest OS 上,應用之間也就相互獨立、資源隔離了,但是由於需要 Hypervisor 來建立虛擬機器,且每個虛擬機器裡需要完整的執行一套作業系統 Guest OS,因此這個方式會帶來很多額外資源的開銷。

而 Docker容器 中卻沒有 Hypervisor 這一層,雖然它需要在宿主機中執行 Docker Engine,但它的原理卻完全不同於 Hypervisor,它並沒有虛擬出硬體裝置,更沒有獨立部署全套的作業系統 Guest OS。

Docker容器沒有那麼複雜的實現原理,它其實就是一個普通程序而已,只不過它是一種經過特殊處理過的普通程序。

我們啟動容器的時候(docker run …),Docker Engine 只不過是啟動了一個程序,這個程序就執行著我們容器裡的應用。但 Docker Engine 對這個程序做了一些特殊處理,通過這些特殊處理之後,這個程序所看到的外部環境就不再是宿主機的那個環境了(它看不到宿主機中的其它程序了,以為自己是當前作業系統唯一一個程序),並且 Docker Engine 還對這個程序所使用得資源進行了限制,防止它對宿主機資源的無限使用。

那 Docker Engine 具體是做了哪些特殊處理才有這麼神奇的效果呢?

二、容器是如何做到資源隔離和限制的?

Docker容器對這個程序的隔離主要採用2個技術點:

  • Namespace 技術

  • Cgroups 技術

弄清楚了這兩個技術點對理解容器的原理非常重要,它們是容器技術的核心。

下面來詳細解釋一下:

  1. Namespace 技術

    Namespace 並不是一個什麼新技術,它是Linux作業系統預設提供的API,包括 PID Namespace、Mount Namespace、IPC Namespace、Network Namespace等等。

    以 PID Namespace 舉例,它的功能是可以讓我們在建立程序的時候,告訴Linux系統,我們要建立的程序需要一個新的獨立的程序空間,並且這個程序在這個新的程序空間裡的PID=1,也就是說這個程序只看得到這個新程序空間裡的東西,看不到外面宿主機環境裡的東西,也看不到其它程序(不過這只是一個虛擬空間,事實上這個程序在宿主機裡PID該是啥還是啥,沒有變化,只不過在這個程序空間裡,該程序以為自己的PID=1)。

    打個比方,就像是一個班級,每個人在這個班裡都有一個編號,班裡有90人,然後來了一位新同學,那他在班裡的編號就是91,可是老師為了給這位同學特別照顧,所以在班裡開闢了一塊獨立的看不到外面的小隔間,並告訴這個同學他的編號是1,由於這位同學在這個小空間裡隔離著,所以他真的以為自己就是班上的第一位同學且編號為1,當然了,事實上這位同學在班上的編號依然是91。

    另外,Network Namespace 的技術原理也是類似的,讓這個程序只能看到當前Namespace空間裡的網路裝置,看不到宿主機真實情況。同理,其它 Mount、IPC等 Namespace 也是這樣。

    Namespace 技術其實就是修改了應用程序的視覺範圍,但應用程序的本質卻沒有變化。

    不過,Docker容器裡雖然帶有一部分作業系統(檔案系統相關),但它並沒有核心,因此多個容器之間是共用宿主機的作業系統核心的。這一點與虛擬機器的原理是完全不一樣的。

  2. Cgroups 技術

    Cgroup 全稱是 Control Group,其功能就是限制程序組所使用的最大資源(這些資源可以是 CPU、記憶體、磁碟等等)。

    既然 Namespace 技術 只能改變一下程序組的視覺範圍,並不能真實的對資源做出限制。那麼為了防止容器(程序)之間互相搶資源,甚至某個容器把宿主機資源全部用完導致其它容器也宕掉的情況發生。因此,必須採用 Cgroup 技術對容器的資源進行限制。

    Cgroup 技術也是Linux預設提供的功能,在Linux系統的 /sys/fs/cgroup 下面有一些子目錄 cpu、memory等,Cgroup技術提供的功能就是可以基於這些目錄實現對這些資源進行限制。

    例如:在 /sys/fs/cgroup/cpu 下面建立一個 dockerContainer 子目錄,系統就會自動在這個新建的目錄下面生成一些配置檔案,這些配置檔案就是用來控制資源使用量的。例如可以在這些配置檔案裡面設定某個程序ID對CPU的最大使用率。

    Cgroup 對其它記憶體、磁碟等資源也是採用同樣原理做限制。

三、容器的映象是什麼?

一個基礎的容器映象其實就是一個 rootfs,它包含作業系統的檔案系統(檔案和目錄),但並不包含作業系統的核心。

rootfs 是在容器里根目錄上掛載的一個全新的檔案系統,此檔案系統與宿主機的檔案系統無關,是一個完全獨立的,用於給容器進行提供環境的檔案系統。

對於一個Docker容器而言,需要基於 pivot_root 指令,將容器內的系統根目錄切換到rootfs上,這樣,有了這個 rootfs,容器就能夠為程序構建出一個完整的檔案系統,且實現了與宿主機的環境隔離,也正是有了rootfs,才能實現基於容器的本地應用與雲端應用執行環境的一致。

另外,為了方便映象的複用,Docker 在映象中引入了層(Layer)的概念,可以將不同的映象一層一層的迭在一起。這樣,如果我們要做一個新的映象,就可以基於之前已經做好的某個映象的基礎上繼續做。

如上圖,這個例子中最底層是作業系統引導,往上一層就是基礎映象層(Linux的檔案系統),再往上就是我們需要的各種應用映象,Docker 會把這些映象聯合掛載在一個掛載點上,這些映象層都是隻讀的。只有最上面的容器層是可讀可寫的。

這種分層的方案其實是基於 聯合檔案系統UnionFS(Union File System)的技術實現的。它可以將不同的目錄全部掛載在同一個目錄下。舉個例子,假如有資料夾 test1 和 test2 ,這兩個資料夾裡面的檔案 有相同的,也有不同的。然後我們可以採用聯合掛載的方式,將這兩個資料夾掛載到 test3 上,那麼 test3 目錄裡就有了 test1 和 test2 的所有檔案(相同的檔案有去重,不同的檔案都保留)。

這個原理應用在Docker映象中,比如有2個同學,同學A已經做好了一個基於Linux的Java環境的映象,同學S想搭建一個Java Web環境,那麼他就不必再去做Java環境的映象了,可以直接基於同學A的映象在上面增加Tomcat後生成新映象即可。

以上,就是對微服務架構之「 容器技術 」的一些思考。

碼字不易啊,喜歡的話不妨轉發朋友,或點選文章右下角的“在看”吧。