8 分鐘入門 K8s | 詳解容器基本概念

做者| 阿里巴巴高級開發工程師 傅偉golang

1、容器與鏡像

什麼是容器?

在介紹容器的具體概念以前,先簡單回顧一下操做系統是如何管理進程的。docker

首先,當咱們登陸到操做系統以後,能夠經過 ps 等操做看到各式各樣的進程,這些進程包括系統自帶的服務和用戶的應用進程。那麼,這些進程都有什麼樣的特色?網絡

  • 第一,這些進程能夠相互看到、相互通訊;
  • 第二,它們使用的是同一個文件系統,能夠對同一個文件進行讀寫操做;
  • 第三,這些進程會使用相同的系統資源。

這樣的三個特色會帶來什麼問題呢?架構

  • 由於這些進程可以相互看到而且進行通訊,高級權限的進程能夠攻擊其餘進程;
  • 由於它們使用的是同一個文件系統,所以會帶來兩個問題:這些進程能夠對於已有的數據進行增刪改查,具備高級權限的進程可能會將其餘進程的數據刪除掉,破壞掉其餘進程的正常運行;此外,進程與進程之間的依賴可能會存在衝突,如此一來就會給運維帶來很大的壓力;
  • 由於這些進程使用的是同一個宿主機的資源,應用之間可能會存在資源搶佔的問題,當一個應用須要消耗大量 CPU 和內存資源的時候,就可能會破壞其餘應用的運行,致使其餘應用沒法正常地提供服務。

針對上述的三個問題,如何爲進程提供一個獨立的運行環境呢?運維

  • 針對不一樣進程使用同一個文件系統所形成的問題而言,Linux 和 Unix 操做系統能夠經過 chroot 系統調用將子目錄變成根目錄,達到視圖級別的隔離;進程在 chroot 的幫助下能夠具備獨立的文件系統,對於這樣的文件系統進行增刪改查不會影響到其餘進程;
  • 由於進程之間相互可見而且能夠相互通訊,使用 Namespace 技術來實現進程在資源的視圖上進行隔離。在 chroot 和 Namespace 的幫助下,進程就可以運行在一個獨立的環境下了;
  • 但在獨立的環境下,進程所使用的仍是同一個操做系統的資源,一些進程可能會侵蝕掉整個系統的資源。爲了減小進程彼此之間的影響,能夠經過 Cgroup 來限制其資源使用率,設置其可以使用的 CPU 以及內存量。

那麼,應該如何定義這樣的進程集合呢?工具

其實,**容器就是一個視圖隔離、資源可限制、獨立文件系統的進程集合。**所謂「視圖隔離」就是可以看到部分進程以及具備獨立的主機名等;控制資源使用率則是能夠對於內存大小以及 CPU 使用個數等進行限制。容器就是一個進程集合,它將系統的其餘資源隔離開來,具備本身獨立的資源視圖。測試

容器具備一個獨立的文件系統,由於使用的是系統的資源,因此在獨立的文件系統內不須要具有內核相關的代碼或者工具,咱們只須要提供容器所需的二進制文件、配置文件以及依賴便可。只要容器運行時所需的文件集合都可以具有,那麼這個容器就可以運行起來。ui

什麼是鏡像?

綜上所述,咱們將這些容器運行時所須要的全部的文件集合稱之爲容器鏡像。spa

那麼,通常都是經過什麼樣的方式來構建鏡像的呢?一般狀況下,咱們會採用 Dockerfile 來構建鏡像,這是由於 Dockerfile 提供了很是便利的語法糖,可以幫助咱們很好地描述構建的每一個步驟。固然,每一個構建步驟都會對已有的文件系統進行操做,這樣就會帶來文件系統內容的變化,咱們將這些變化稱之爲 changeset。當咱們把構建步驟所產生的變化依次做用到一個空文件夾上,就可以獲得一個完整的鏡像。   changeset 的分層以及複用特色可以帶來幾點優點:操作系統

  • 第一,可以提升分發效率,簡單試想一下,對於大的鏡像而言,若是將其拆分紅各個小塊就可以提升鏡像的分發效率,這是由於鏡像拆分以後就能夠並行下載這些數據;
  • 第二,由於這些數據是相互共享的,也就意味着當本地存儲上包含了一些數據的時候,只須要下載本地沒有的數據便可,舉個簡單的例子就是 golang 鏡像是基於 alpine 鏡像進行構建的,當本地已經具備了 alpine 鏡像以後,在下載 golang 鏡像的時候只須要下載本地 alpine 鏡像中沒有的部分便可;
  • 第三,由於鏡像數據是共享的,所以能夠節約大量的磁盤空間,簡單設想一下,當本地存儲具備了 alpine 鏡像和 golang 鏡像,在沒有複用的能力以前,alpine 鏡像具備 5M 大小,golang 鏡像有 300M 大小,所以就會佔用 305M 空間;而當具備了複用能力以後,只須要 300M 空間便可。

如何構建鏡像?

以下圖所示的 Dockerfile 適用於描述如何構建 golang 應用的。 圖片

如圖所示:

  1. FROM 行表示如下的構建步驟基於什麼鏡像進行構建,正如前面所提到的,鏡像是能夠複用的;
  2. WORKDIR 行表示會把接下來的構建步驟都在哪個相應的具體目錄下進行,其起到的做用相似於 Shell 裏面的 cd;
  3. COPY 行表示的是能夠將宿主機上的文件拷貝到容器鏡像內;
  4. RUN 行表示在具體的文件系統內執行相應的動做。當咱們運行完畢以後就能夠獲得一個應用了;
  5. CMD 行表示使用鏡像時的默認程序名字。

當有了 Dockerfile 以後,就能夠經過 docker build 命令構建出所須要的應用。構建出的結果存儲在本地,通常狀況下,鏡像構建會在打包機或者其餘的隔離環境下完成。

那麼,這些鏡像如何運行在生產環境或者測試環境上呢?這時候就須要一箇中轉站或者中心存儲,咱們稱之爲 docker registry,也就是鏡像倉庫,其負責存儲全部產生的鏡像數據。咱們只須要經過 docker push 就可以將本地鏡像推進到鏡像倉庫中,這樣一來,就可以在生產環境上或者測試環境上將相應的數據下載下來並運行了。

如何運行容器?

運行一個容器通常狀況下分爲三步:

  • 第一步:從鏡像倉庫中將相應的鏡像下載下來;
  • 第二步:當鏡像下載完成以後就能夠經過 docker images 來查看本地鏡像,這裏會給出一個完整的列表,咱們能夠在列表中選中想要的鏡像;
  • 第三步:當選中鏡像以後,就能夠經過 docker run 來運行這個鏡像獲得想要的容器,固然能夠經過屢次運行獲得多個容器。一個鏡像就至關因而一個模板,一個容器就像是一個具體的運行實例,所以鏡像就具備了一次構建、處處運行的特色。

小結

簡單回顧一下,容器就是和系統其它部分隔離開來的進程集合,這裏的其餘部分包括進程、網絡資源以及文件系統等。而鏡像就是容器所須要的全部文件集合,其具有一次構建、處處運行的特色。  

2、容器的生命週期

容器運行時的生命週期

容器是一組具備隔離特性的進程集合,在使用 docker run 的時候會選擇一個鏡像來提供獨立的文件系統並指定相應的運行程序。這裏指定的運行程序稱之爲 initial 進程,這個 initial 進程啓動的時候,容器也會隨之啓動,當 initial 進程退出的時候,容器也會隨之退出。

所以,能夠認爲容器的生命週期和 initial 進程的生命週期是一致的。固然,由於容器內不僅有這樣的一個 initial 進程,initial 進程自己也能夠產生其餘的子進程或者經過 docker exec 產生出來的運維操做,也屬於 initial 進程管理的範圍內。當 initial 進程退出的時候,全部的子進程也會隨之退出,這樣也是爲了防止資源的泄漏。   可是這樣的作法也會存在一些問題,首先應用裏面的程序每每是有狀態的,其可能會產生一些重要的數據,當一個容器退出被刪除以後,數據也就會丟失了,這對於應用方而言是不能接受的,因此須要將容器所產生出來的重要數據持久化下來。容器可以直接將數據持久化到指定的目錄上,這個目錄就稱之爲數據卷。

數據卷有一些特色,其中很是明顯的就是數據卷的生命週期是獨立於容器的生命週期的,也就是說容器的建立、運行、中止、刪除等操做都和數據卷沒有任何關係,由於它是一個特殊的目錄,是用於幫助容器進行持久化的。簡單而言,咱們會將數據卷掛載到容器內,這樣一來容器就可以將數據寫入到相應的目錄裏面了,並且容器的退出並不會致使數據的丟失。

一般狀況下,數據卷管理主要有兩種方式:

  • 第一種是經過 bind 的方式,直接將宿主機的目錄直接掛載到容器內;這種方式比較簡單,可是會帶來運維成本,由於其依賴於宿主機的目錄,須要對於全部的宿主機進行統一管理。
  • 第二種是將目錄管理交給運行引擎。

3、容器項目架構

moby 容器引擎架構

moby 是目前最流行的容器管理引擎,moby daemon 會對上提供有關於容器、鏡像、網絡以及 Volume的管理。moby daemon 所依賴的最重要的組件就是 containerd,containerd 是一個容器運行時管理引擎,其獨立於 moby daemon ,能夠對上提供容器、鏡像的相關管理。

containerd 底層有 containerd shim 模塊,其相似於一個守護進程,這樣設計的緣由有幾點:

  • 首先,containerd 須要管理容器生命週期,而容器多是由不一樣的容器運行時所建立出來的,所以須要提供一個靈活的插件化管理。而 shim 就是針對於不一樣的容器運行時所開發的,這樣就可以從 containerd 中脫離出來,經過插件的形式進行管理。
  • 其次,由於 shim 插件化的實現,使其可以被 containerd 動態接管。若是不具有這樣的能力,當 moby daemon 或者 containerd daemon 意外退出的時候,容器就沒人管理了,那麼它也會隨之消失、退出,這樣就會影響到應用的運行。
  • 最後,由於隨時可能會對 moby 或者 containerd 進行升級,若是不提供 shim 機制,那麼就沒法作到原地升級,也沒法作到不影響業務的升級,所以 containerd shim 很是重要,它實現了動態接管的能力。

本節課程只是針對於 moby 進行一個大體的介紹,在後續的課程也會詳細介紹。  

4、容器 VS VM

容器和 VM 之間的差別

VM 利用 Hypervisor 虛擬化技術來模擬 CPU、內存等硬件資源,這樣就能夠在宿主機上創建一個 Guest OS,這是常說的安裝一個虛擬機。

每個 Guest OS 都有一個獨立的內核,好比 Ubuntu、CentOS 甚至是 Windows 等,在這樣的 Guest OS 之下,每一個應用都是相互獨立的,VM 能夠提供一個更好的隔離效果。但這樣的隔離效果須要付出必定的代價,由於須要把一部分的計算資源交給虛擬化,這樣就很難充分利用現有的計算資源,而且每一個 Guest OS 都須要佔用大量的磁盤空間,好比 Windows 操做系統的安裝須要 10~30G 的磁盤空間,Ubuntu 也須要 5~6G,同時這樣的方式啓動很慢。正是由於虛擬機技術的缺點,催生出了容器技術。   容器是針對於進程而言的,所以無需 Guest OS,只須要一個獨立的文件系統提供其所須要文件集合便可。全部的文件隔離都是進程級別的,所以啓動時間快於 VM,而且所需的磁盤空間也小於 VM。固然了,進程級別的隔離並無想象中的那麼好,隔離效果相比 VM 要差不少。

整體而言,容器和 VM 相比,各有優劣,所以容器技術也在向着強隔離方向發展。  

本文總結

  • 容器是一個進程集合,具備本身獨特的視圖視角;
  • 鏡像是容器所須要的全部文件集合,其具有一次構建、處處運行的特色;
  • 容器的生命週期和 initial 進程的生命週期是同樣的;
  • 容器和 VM 相比,各有優劣,容器技術在向着強隔離方向發展。
相關文章
相關標籤/搜索