容器技術是近幾年計算機領域的熱門技術,特別是隨著各種云服務的發展,越來越多的服務運行在以 Docker 為代表的容器之內。
本文我們就來分享一下容器化技術相關的知識。
容器化技術簡介
相比傳統虛擬化技術,容器技術是一種更加輕量級的操作系統隔離方案,可以將應用程序及其運行依賴環境打包到鏡像中,通過容器引擎進行調度,并且提供進程隔離和資源限制的運行環境。
虛擬化技術
虛擬化技術通過 Hypervisor 實現虛擬機與底層硬件的解耦,虛擬機實現依賴 Hypervisor 層,Hypervisor 是整個虛擬機的核心所在。
Hypervisor 是什么呢 ? 也可以叫作虛擬機監視器 VMM(Virtual Machine Monitor),是一種運行在基礎物理服務器和操作系統之間的中間軟件層,可允許多個操作系統和應用共享硬件。
Hypervisor 虛擬機可以模擬機器硬件資源,協調虛擬機對硬件資源的訪問,同時在各個虛擬機之間進行隔離。
每一個虛擬機都包括執行的應用,依賴的二進制和庫資源,以及一個完整的 OS 操作系統,虛擬機運行以后,預分配給它的資源將全部被占用。
容器化技術
在容器技術中,最具代表性且應用最廣泛的是 Docker 技術。
Docker 是一個開源的應用容器引擎,可以打包應用以及依賴包到一個可移植的容器中,然后發布到服務器上,Docker 容器基于鏡像運行,可部署在物理機或虛擬機上,通過容器引擎與容器編排調度平臺實現容器化應用的生命周期管理。
使用容器化技術有哪些好處呢?
Docker 不同于 VM,只包含應用程序及依賴庫,處于一個隔離的環境中,這使得 Docker 更加輕量高效,啟動容器只需幾秒鐘之內完成。由于 Docker 輕量、資源占用少,可以更方便地部署標準化應用,一臺主機上可以同時運行上千個 Docker 容器。
兩種虛擬化技術的對比
虛擬機是一個運行在宿主機之上的完整操作系統,虛擬機運行自身操作系統會占用較多的 CPU、內存、硬盤資源等。
虛擬化技術為用戶提供了一個完整的虛擬機,包括操作系統在內,容器化技術為應用程序提供了隔離的運行空間,容器之間共享同一個上層操作系統內核。虛擬化技術有更佳的隔離性和安全性,但是更新和升級困難,容器化具有快速擴展、靈活性和易用性等優勢,但其隔離性較差、安全性相對較低。
實際部署一般是把兩種技術結合起來,比如一個虛擬機中運行多個容器,這樣既保證了較好的強隔離性和安全性,也有了快速擴展、靈活性和易用性。
容器化的原理
容器技術的核心是如何實現容器內資源的限制,以及不同容器之間的隔離,這些是基于 Linux 的 Namespace 和 CGroups 技術。
Namespace
Namespace 的目的是通過抽象方法使得 Namespace 中的進程看起來擁有它們自己的隔離的全局系統資源實例。
Linux 內核實現了六種 Namespace:Mount namespaces、UTS namespaces、IPC namespaces、PID namespaces、Network namespaces、User namespaces,功能分別為:隔離文件系統、定義 hostname 和 domainame、特定的進程間通信資源、獨立進程 ID 結構、獨立網絡設備、用戶和組 ID 空間。
Docker 在創建一個容器的時候,會創建以上六種 Namespace 實例,然后將隔離的系統資源放入到相應的 Namespace 中,使得每個容器只能看到自己獨立的系統資源。
Cgroups
Docker 利用 CGroups 進行資源隔離。CGroups(Control Groups)也是 Linux 內核中提供的一種機制,它的功能主要是限制、記錄、隔離進程所使用的物理資源,比如 CPU、Mermory、IO、Network 等。
簡單來說,CGroups 在接收到調用時,會給指定的進程掛上鉤子,這個鉤子會在資源被使用的時候觸發,觸發時會根據資源的類別,比如 CPU、Mermory、IO 等,然后使用對應的方法進行限制。
CGroups 中有一個術語叫作 Subsystem 子系統,也就是一個資源調度控制器,CPU Subsystem 負責 CPU 的時間分配,Mermory Subsystem 負責 Mermory 的使用量等。Docker 啟動一個容器后,會在 /sys/fs/cgroup 目錄下生成帶有此容器 ID 的文件夾,里面就是調用 CGroups 的配置文件,從而實現通過 CGroups 限制容器的資源使用率。
微服務如何適配容器化
微服務的設計思想是對系統功能進行解耦,拆分為單獨的服務,可以獨立運行,而容器進一步對這種解耦性進行了擴展,應用容器技術可以對服務進行快速水平擴展,從而到達彈性部署業務的能力。在各種云服務概念興起之后,微服務結合 Docker 部署,更加方便微服務架構運維部署落地。
微服務結合容器有很多優點,但是另一方面,也給服務的部署和應用提出了一些新的問題。
以 Java 服務為例,容器與虛擬機不同,其資源限制通過 CGroup 來實現,而容器內部進程如果不感知 CGroup 的限制,就進行內存、CPU 分配的話,則可能會導致資源沖突的問題。
Java 8 之前的版本無法跟 Docker 很好的配合,JVM 通過容器獲取的可用內存和 CPU 數量并不是 Docker 允許使用的可用內存和 CPU 數量。
我們在開發中會應用一些線程池,通常會根據 CPU 核心數來配置,比如使用:
Runtime.getRuntime().availableProcessors()
在 1.8 版本更早的實現,在容器內獲取的是上層物理機或者虛擬機的 CPU 核心數,這就使得線程池配置不符合我們期望的設置。
另一個影響體現在 GC 中,JVM 垃圾對象回收對 Java 程序執行性能有一定的影響,默認的 JVM 使用公式“ParallelGCThreads = (ncpus <= 8) ? ncpus : 3 + ((ncpus * 5) / 8)” 來計算并行 GC 的線程數,其中 ncpus 是 JVM 發現的系統 CPU 個數。如果 JVM 應用了錯誤的 CPU 核心數,會導致 JVM 啟動過多的 GC 線程,導致 GC 性能下降,Java 服務的延時增加。
總結
這一課時和你分享了容器技術的發展,以 Docker 為代表的容器化技術的實現原理,以及大規模容器化之下,微服務如何適配等問題。
本課時的內容以概念為主,如果你在工作中沒有接觸過容器化場景,可以到 Docker 官網學習入門指南、了解 Docker 命令,并動手實踐一下 Docker 部署。
Docker環境準備教程(安裝與卸載).zip
Docker常用的基本命令大全