Docker介紹
當我們提到微服務部署或者虛擬化的時候,通常會想到docker跟kubernetes。docker自2013年以來發(fā)展火熱,現(xiàn)在已經(jīng)是虛擬化技術的標準方案。
Docker基于Golang語言實現(xiàn),能夠在單臺機器上部署多個互相隔離的應用,且能夠控制應用之間不發(fā)生CPU、內存等資源搶占。

Docker跟以往的VM虛擬機有什么區(qū)別?
為什么我們經(jīng)常說Docker是輕量級的虛擬機,它跟我們之前用的VMWare,VirtualBox等虛擬機有什么區(qū)別,輕在哪兒呢?讓我們首先看看兩者對比圖:

從上圖我們可以很直觀地看到這兩者的區(qū)別:
- VM虛擬機:從下到上,分別是Server(物理機,如Macbook)、Host OS(物理機的操作系統(tǒng),如macOS)、Hypervisor(用于運行虛擬機負責虛擬化的管理軟件,如VirtualBox)、Guest OS(在虛擬機上安裝的操作系統(tǒng),如Ubuntu,跟上層應用服務沒有明顯綁定關系)、Bins/Libs(App依賴的各種類庫)、Apps(應用程序)。
其中我們可以很明顯地看出來,App跟依賴庫(libs)以及虛擬機操作系統(tǒng)之間沒有很明顯的綁定關系,App在能夠到虛擬機操作系統(tǒng)上面運行之前,需要提前準備環(huán)境,增加不少運維成本,更重要的是,部署應用程序的時候,不能保證依賴的運行環(huán)境必然充分。 -
Docker:從下往上,分別是Server、Host OS(前二相同)、Docker Engine(Docker的運行引擎,守護進程,我們待會也會著重講解這塊的核心設計)、Containers(多個運行容器)。
Docker的精髓主要體現(xiàn)在Docker Engine層的資源隔離設計以及Container層的一體化打包方式上。另外,Docker比VM虛擬機輕的地方在于,由Docker Engine抽象出來的虛擬化技術,讓上層的Container服務可以設計得非常輕量:穩(wěn)定的鏡像+空間節(jié)約+秒級啟動。
Docker Engine
Docker基本思想
基礎設施即代碼(Infrastructure as Code)
Docker的出現(xiàn),主要是解決在開發(fā)、測試、線上運維的各個階段,需要一種虛擬化技術來解決環(huán)境不一致的問題。
Docker把基礎設施的構建過程通過dockerfile讓我們將應用程序的運行環(huán)境依賴跟業(yè)務代碼一起納入到版本控制中,避免因為環(huán)境不一致造成運行結果不符預期的可能。這種方式就是我們常說的基礎設施即代碼的思想。
不可變基礎設施(Immutable Infrastructure)
通過版本控制 + CI/CD過程 + Docker技術,我們可以在每次發(fā)布時構建出來一個不可變的鏡像,這個鏡像不管在什么平臺上面運行,都能借助Docker Engine的能力,維持應用程序的環(huán)境穩(wěn)定,減少運維維護成本。這就是“不可變基礎設施(Immutable Infrastructure)”。
Docker的出現(xiàn)確實是應時代所需,那么,Docker的這種虛擬化技術的出現(xiàn)有哪些核心技術的支撐呢?
Docker核心實現(xiàn)

Docker的虛擬化技術借助了Linux系統(tǒng)的基礎技術,如使用Namespace來隔離資源,使用cgroups來隔離執(zhí)行單元,使用ufs來組合不同文件系統(tǒng)。
下面我們逐個對這些概念做下了解。
Namespace
Docker虛擬化的第一個問題,是如何在同一臺機器上,把進程、內存、文件系統(tǒng)、網(wǎng)絡這些基本要素隔離起來,讓每一個容器之間互不影響?
解決方案是Namespace,目前,Linux內核里面提供了7種不同類型的namespace:
名稱 宏定義 隔離內容
Cgroup CLONE_NEWCGROUP Cgroup root directory (since Linux 4.6)
IPC CLONE_NEWIPC System V IPC, POSIX message queues (since Linux 2.6.19)
Network CLONE_NEWNET Network devices, stacks, ports, etc. (since Linux 2.6.24)
Mount CLONE_NEWNS Mount points (since Linux 2.4.19)
PID CLONE_NEWPID Process IDs (since Linux 2.6.24)
User CLONE_NEWUSER User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)
UTS CLONE_NEWUTS Hostname and NIS domain name (since Linux 2.6.19)
其中,docker主要使用了其中的cgroups, ipc, network, mount, pid:
- 1、進程隔離(PID Namespace)
我們運行一個redis的docker容器,通過docker exec的方式進入容器內部,查看進程列表,可以看到:
# ps -ef
UID PID PPID C STIME TTY TIME CMD
redis 1 0 0 2018 ? 09:01:49 redis-server 0.0.0.0:6379
root 61599 0 1 15:18 ? 00:00:00 /bin/bash
root 61604 61599 0 15:18 ? 00:00:00 ps -ef
看到里面展示的只有這個Container的進程列表,卻對宿主機的其他進程一無所知,而且這個進程列表的第一條進程的PID是1,也就是init進程,這就不是簡單的進程過濾了,而是通過CLONE_NEWPID這個Namespace實現(xiàn)的,完全創(chuàng)建出來了一套獨立的進程管理體系來實現(xiàn)進程隔離。
- 2、網(wǎng)絡隔離(Network Namespace)
Docker中的服務,大部分是需要通過網(wǎng)絡來實現(xiàn)與外界通信的,那如何讓Container有自己的網(wǎng)絡地址避免端口沖突,又能通過宿主機跟外界交互呢?
Linux Network Namespace能讓進程擁有一個完全獨立的網(wǎng)絡協(xié)議棧視圖,而Docker利用它為每個Container提供一個獨立的虛擬網(wǎng)卡,并提供了4種網(wǎng)絡隔離的方式給Container使用,它們分別是:- host模式,--net=host,使用跟宿主機一樣的網(wǎng)絡,不會分配獨立的Network Namespace。
- container模式,--net=containerID,指定跟其他container共同使用同一個已創(chuàng)建的Network Namespace。
- none模式,--net=none,擁有空的獨立Network Namespace,但不會創(chuàng)建獨立虛擬網(wǎng)卡。
-
bridge模式,--net=bridge,是docker的默認方式,擁有獨立的Network Namespace及虛擬網(wǎng)卡、獨立IP,并通過虛擬網(wǎng)橋的方式連接到宿主機對外通信。
docker bridge模式
- 3、掛載點隔離(Mount Namespace)
Mount Namespace為Container提供了一個獨立的文件系統(tǒng)掛載視圖,跟Container的進程空間的一系列文件通過符號鏈接的方式關聯(lián)起來,每個Container都只能看到自己mount namespace的文件系統(tǒng)掛載點下的內容,從而實現(xiàn)對文件系統(tǒng)的隔離。
當傳入CLONE_NEWNS標志,使用clone函數(shù)創(chuàng)建一個mount namespace的時候,操作系統(tǒng)會從調用該函數(shù)的進程的mount namespace中拷貝一份出來,創(chuàng)建一個新的mount namespace,創(chuàng)建以后,兩個namespace基本就相互隔離了,隔離以后,再使用chroot對子進程的系統(tǒng)根目錄進行遷移,從而從根本上實現(xiàn)掛載點的隔離。
// 創(chuàng)建一個子進程,加入flag傳入的namespace
int clone(int (*child_func)(void *), void *child_stac, int flags, void *arg);

- 4、進程間通信(IPC Namespace)
Linux進程間通信的方式主要有管道、消息隊列、共享內存、信號量跟Socket套接字。Linux的IPC Namespace也是通過clone函數(shù)加上CLONE_NEWIPC參數(shù)創(chuàng)建,同一個IPC Namespace下的進程彼此可通信,與其他IPC Namespace下的進程則互相隔離。
Control groups
這里我們把cgroups單獨拿出來講,因為上面的namespace介紹的進程、網(wǎng)絡、IPC、文件系統(tǒng)等隔離機制,實際上是抽象資源層面的隔離,各個Container之間雖然互不知道彼此的存在,但它們卻真真實實地共享著同一個物理機器的物理資源,如CPU、內存、磁盤等??紤]一個場景,如果一個Container在調度的過程中搶占了大量的CPU資源,而其他Container在不發(fā)生任何變化的情況下卻受到了影響,這種不穩(wěn)定性對于生產(chǎn)級別的高可用應用程序來說,肯定是無法接受的。

而Linux的Control Groups,也就是我們常說的cgroups,就是用來對物理資源進行隔離的。
cgroups組包含了7個子模塊,分別用來限制進程組使用的不同模塊:
- cpu:限制CPU使用比重。
- cpuset:多核系統(tǒng)上分配及限制核心使用數(shù)量。
- cpuacct:生成CPU使用報告。
- blkio:塊設備IO資源(磁盤、USB等)的讀寫次數(shù)、帶寬等限制。
- devices:控制設備訪問。
- freezer:cgroups的任務調度控制。
- memory:限制內存上限。

具體到使用中,我們看幾個使用docker run的參數(shù),為某個Container指定資源的限制:
memory子系統(tǒng):
- 限制只能使用512MB內存
docker run --memory 512MB
- 限制內存最大使用512MB,且交換分區(qū)及內存總和限制1GB以內
docker run --memory 512MB --memory-swap 1G
- 限制內核內存使用最多100MB
docker run --kernel-memory 50M
- 設置當系統(tǒng)發(fā)現(xiàn)oom(內存溢出)時候不殺死容器(非常規(guī)場景使用)
docker run --oom-kill-disable=true
cpu子系統(tǒng):
- 限制CPU使用周期,需結合cpu-period與cpu-quota一起使用,前者指定總量,后者指定占總量的比重,下面命令指定限制的CPU比重為50%
docker run --cpu-period=50000 --cpu-quota=25000
cpuset子系統(tǒng):
- 限制CPU使用個數(shù)為2個。
docker run --cpuset-cpus 2
Docker鏡像原理
上文講到了Docker Engine的核心實現(xiàn)原理,但除了這部分的實現(xiàn)很精髓之外,Docker鏡像的打包原理也同樣非常優(yōu)秀,下面我們來看看。
- 鏡像分層
先看一個dockerfile的內容:
FROM debian:latest
RUN apt-get install emacs
RUN apt-get install apache2
上面的dockerfile的目標是打包出一個具備emacs編輯器以及Apache服務器的Docker基礎鏡像,對應的構建過程圖是這樣的:

-
Copy on Write
Docker的鏡像是分層的,每一層都是只讀的,上一層做的修改不會影響底層的基礎設施。這樣做的好處是,如果本地/鏡像倉庫里面已經(jīng)有了鏡像A,那么基于鏡像A構建的其他鏡像,拉取/提交的時候就不需要整個鏡像倉庫傳輸了,而只需要拉取差異化的部分內容,從而極大地提高了構建速度及傳輸效率。
當Docker鏡像通過docker run指令被啟動成一個容器(Container)的時候,會在只讀的各個分層頂端創(chuàng)建一個讀寫層,運行容器的過程中我們對其做的任何修改,都只會作用在該讀寫層。
容器讀寫層
我們會發(fā)出疑問,基于這樣的設計,那么當我們對文件進行增刪改查的時候,實際上是如何操作的呢?
- 1)添加文件:直接在容器讀寫層對應目錄增加。
- 2)刪除文件:在容器讀寫層標記該文件已刪除。
- 3)修改文件:自上而下逐層找到對應文件,復制到容器讀寫層,然后對文件進行修改。
- 4)查詢文件:自上而下逐層找到對應文件,發(fā)起文件讀取。
上述的過程,稱之為Copy on Write。
總結
Docker作為虛擬化技術的實際標準,跟以往的VM虛擬機相比更輕量,具備穩(wěn)定的鏡像+空間節(jié)約+秒級啟動的優(yōu)點。
Docker借助了Linux平臺的許多優(yōu)秀設計,包括Namespce、Control Groups、UnionFS等,在理念、速度、穩(wěn)定性、靈活性方面都遠超以往的VM虛擬機。而隨著docker虛擬化技術的普及,容器編排工具蓬勃發(fā)展,目前已經(jīng)形成以kubernetes為事實標準的容器編排方案。
參考
Linux命名空間概述
Docker 核心技術與實現(xiàn)原理
Docker overview
Docker 背后的內核知識——Namespace 資源隔離
docker的cgroup篇
Docker 資源管理探秘:Docker 背后的內核 Cgroups 機制
10張圖帶你深入理解Docker容器和鏡像
第八篇:Docker鏡像結構原理


