技術乾貨分享:Docker 核心技術與基本原理

2019-08-01     IT技術分享

相信在現在的工程開發中,大多數開發者或多或少都聽過一個概念:容器。

即使在前端開發的領域中,容器技術在前端 CI/CD,Node 服務等方面也發揮著重要的作用。Docker 提供了對應用進行打包、運行和部署的強大能力,並且隨著 K8s 等容器資源調度技術的發展,如今已經基本深入軟體工程開發。

今天希望跟大家分享一下,在容器這個外殼下,包含的一些原理以及運用的 Linux 內核技術。

一、容器 VS 虛擬機

容器和虛擬機是非常相似的技術概念,因為他們都提供了隔離應用和依賴環境的能力,都可以看作是提供了一個沙箱環境,使得應用都可以部署在任意宿主機。

但是不同的是,虛擬機依賴硬體設備來提供資源隔離,需要更大的資源開銷。

1、虛擬機

虛擬機理論上是一個真實的計算機作業系統的封裝,它運行在物理設備之上,通過 Hypervisor 進行建立和運行虛擬機體系。常見的虛擬基本架構如下

在 Host OS 的基礎上,通過 Hypervisor 來進行虛擬機資源控制,並擁有自己的 Guest OS,雖然隔離得更徹底,但是顯然資源的開銷會更大。

2、容器

不同於虛擬機提供在物理硬體級別的作業系統隔離,容器技術提供的是作業系統級別的進程隔離,Docker 本身只是作業系統的一個進程,只是在容器技術下,進程之間網絡、空間等等是隔離的,互不知道彼此。

容器與虛擬機技術最大的區別在於:多個容器之間是共享了宿主機的作業系統內核。,在 Host OS 上,通過 Docker Engine 共享 Host OS 的內核。

二、Docker 的資源隔離:NameSpace

1、Linux NameSpace

多個 Docker 容器之間需要保持相互隔離,來模擬 「獨立環境」,而 Docker 正式利用了 Linux NameSpace 來實現這一能力,Linux Namespace 是 Linux 提供的一種機制,可以實現不同資源的隔離。

Linux 提供的主要的 NameSpace

  • Mount NameSpace- 用於隔離文件系統的掛載點
  • UTS NameSpace- 用於隔離 HostName 和 DomianName
  • IPC NameSpace- 用於隔離進程間通信
  • PID NameSpace- 用於隔離進程 ID
  • Network NameSpace- 用於隔離網絡
  • User NameSpace- 用於隔離用戶和用戶組 UID/GID

2、Linux NameSpace API

在已經有了 Linux NameSpace 概念下,怎麼來利用這些 NameSpace 呢?其實我們知道,Docker 容器本身就是一個進程,那麼對容器的 NameSpace 操作,顯然也就是對進程的 API 操作。Linux 主要提供了以下對 NameSpace 的操作接口

Linux NameSpace API

  • clone - 創建一個新進程
  • setns - 允許進程重新加入一個已經存在的 NameSpace
  • unshare - 將調用進程移動到新的 NameSpace

clone

int clone(int (*fn)(void *), void *child_stack,
int flags, void *arg, ...
/* pid_t *ptid, void *newtls, pid_t *ctid */ );

當我們調用 clone() 接口新建進程時,會傳入 CLONE_* flag 用於指定 NameSpace

flags 用於創建已經介紹的幾種 Linux NameSpace

CLONE_NEWNS
CLONE_NEWUTS
CLONE_NEWIPC
CLONE_NEWPID
CLONE_NEWNET
CLONE_NEWUSER

setns

setns 調用主要是重新加入 Namespace, fd 是句柄, nstype 參數用於指定調用的命名空間類型,同 flags

int setns(int fd, int nstype);

unshare

int unshare(int flags);

將調用進程移動到新的 NameSpace

正是這三個 NameSpace 操作的 API,使得 Docker 可以在進程級別實現 「獨立環境」 的能力。

三、Docker 的資源限制:cgroups

1、cgroups

其實在調用了 Linux NameSpace 的情況下,還有一個問題沒有解決: 那就是資源的限制

雖然實現了資源的隔離,但是 Docker 的本質還是一個進程,在多個 Docker 進程的情況下,如果其中一個進程就占滿了所有的 CPU 與內存,其他進程處於忙等而導致服務無響應,這是難以想像的。因此除了 NameSpace 隔離,還需要通過另外一種技術來限制進程資源使用大小情況:cgroups(control groups)。

2、subsystem

Linux 有以下的 cgroups subsystem,用於資源控制

  • memory - 內存限制
  • hugetlb - huge pages 使用量
  • cpu - CPU 使用率
  • cpuacct - CPU 使用率
  • cpuset - 綁定 cgroups 到指定 CPUs 和 NUMA 節點
  • innodb_lock_wait_timeout - block 設備的 IO 速度
  • net_cls - 網絡接口設置優先級
  • devices - mknode 訪問設備權限
  • freezer - suspend 和 restore cgroups 進程
  • perf_event - 性能監控
  • pids - 限制子樹 cgroups 總進程數

3、cgroups 控制資源

cgroups 用來將進程統一分組,並用於控制進程的內存、CPU 以及網絡等資源使用。cgroups 會將系統進程組成成獨立的樹,樹的節點是進程組,每棵樹與一個或者多個 subsystem 關聯。subsystem 的作用就是對組進行操作。

四、Docker 存儲驅動:Union File Systems

1、AUFS

其實在有了 NameSpace 和 cgroups 的情況下,對於 Docker 項目的初始化可以簡單抽象為:

  1. 啟動 Namespace 配置
  2. 設置 cgroups 參數,對資源進行限制
  3. 切換進程的根目錄

但是還有另外一個問題, 是否每次打包、升級鏡像都要重新走一遍整個初始化流程 ?這顯然是不合理的。

Docker 中最典型的存儲驅動就是 AUFS(Advanced Multi-layered unification filesytem),可以將 AUFS 想像為一個可以 「棧式疊加」 的文件系統,AUFS 允許在一個基礎的文件系統的上,「增量式」 的增加文件。

AUFS 支持將不同目錄掛載到同一個目錄下,這種掛載對用戶來說是透明的。通常,AUFS 最上層是可讀寫層,而最底層是只讀層,每一層都是一個普通的文件系統。

Docker 的鏡像分層,正式通過 AUFS 通過分層結構、掛載等方式實現。在用戶製作鏡像的每一步,Docker 都會生成一個層,也就是增量 rootfs。

rootfs (root file system) ,根文件系統,本質上就是一個 Linux 系統中基本的文件目錄組織結構,包含的就是典型 Linux 系統中的 /dev, /proc, /bin, /etc 等標準目錄和文件。

同時,除了只讀層,可讀寫層,Docker 在 AUFS 中有一個單獨生成的內部層, init 層 ,用於存放 /etc/hosts 、 /etc/resolv.conf 等相關信息。

2、AUFS 的讀寫操作

讀操作

當需要 寫入 一個文件時,不存在時則在可讀寫層新建一個,否則一直從向下尋找。

寫操作

刪除 一個文件中,如果文件僅存在可讀寫層,則直接刪除這個文件。

但是又有一個問題,如果刪除的是只讀層的文件呢?所以在這種情況下,會先刪除可讀寫層中的備份,之後通過創建一個 whiteout 文件來標記文件不存在,這其實是一種 「遮擋」,只讀層文件卻不會被真正的刪除,但是表現上確是已經被 「刪除」 了。

新建 文件時,由於 whiteout 的 存在,所以需要先檢查 whiteout 是否存在,存在的情況下,需要先刪除再創建。

AUFS 只是 Docker 存儲驅動的其中一種,在有些場景下並不是最優的選擇,但都是屬於 Union File System,主要是基於 「寫時複製」 以及 「用時配置」 兩種方式,但它能夠有效幫助我們理解 Docker 的分層結構以及原理。其他的 Docker 存儲驅動還有 OverlayFS、Devicemapper、Btrfs、ZFS 等,這裡不再贅述。

五、總結

至此,我們已經介紹了 Docker 相關的幾個核心概念,使用 Linux NameSpace 進行網絡、進程空間、命名空間等資源的隔離,使用 Cgroups 技術對資源的占用、使用量進行限制,使用 AUFS 等存儲驅動來實現分層結構、增量更新等能力。但 Docker 技術的原理遠不止這些,但理解上述內容,已經能夠很好的幫助我們大致理解 Docker 的運行原理。

最後,Docker 基本上已經是部署和發布的必備工具,容器天然的一致性使得開發和運維人員從環境配置的苦海中脫離出來,在目前 K8s 等技術的快速發展下,相信 Docker 一定還有一些技術場景值得我們持續去探索。

文章來源: https://twgreatdaily.com/zh-tw/ylaGg2wBvvf6VcSZ-xio.html