1. 程式人生 > >docker基本原理與docker 網路 記憶體 cpu資源控制

docker基本原理與docker 網路 記憶體 cpu資源控制

說 docker之前,有必要知道 LUX的基本概念

說LUX之前,首先要知道兩個概念,一個是cgroups,一個是namespace。

         cgroups 是用於資源的限制與資源的使用監控,cgroups能夠為程序分配資源,使linux的資源不再是全域性性的。被限制住的程序使用的資源,不能超過所分配閥值。例如該程序的記憶體被限制住了200個單位,當程序所使用的記憶體超過200就會報記憶體溢位錯誤

         namespace 用於程序的隔離,假如程序A的 namespace 為namespaceA,程序B的namespace為namespaceB,AB程序是無法互相感知的,但A 和 B為同一個namespace ,它們就能相互感知,就好比預設情況下,使用者B能用命令檢視使用者B所啟動的程序. namespace不是最近剛出來的新技術,早在Linux核心2.4.19中,就包含了最早的Mount Namespaces.但因為只有一個Mount Namespace,無法滿足實際複雜的場景需要,所有在隨後的核心版本里面,陸續添加了IPC,Network,PID,User和UTS。docker就是藉助這六個Namespace完成資源隔離。

          LXC 就是基於linux核心的 cgroups 與 namespace機制的容器虛擬化技術

AUFS檔案系統

         AUFS的功能簡單說就是,可以將分佈在不同地方的目錄掛載到同一個虛擬檔案系統當中

         docker 利用LXC虛擬化出來一個容器之後。docker參考linux的啟動過程,將一個readonly許可權的bootfs掛載到檔案系統中,然後通過AUFS,再將readonly許可權的rootfs新增到bootfs之上,當rootfs檢查完畢之後,再將使用者所要使用的檔案內容掛載到rootfs之上,同樣是readonly許可權。每次掛載一個FS檔案層,並且每層之間只會掛載增量

          而一個映象(image)就可以理解為:特定FS層的集合。假如,我們使用一個 tomcat 映象製造一個自己的映象,我們會複用原來的tomcat映象,我們所加的專案檔案,就相當於在最上層的可讀寫層級目錄上寫入資料,並不會改變原本tomcat映象中的任何東西。這就是docker的寫時複製。因為複用了映象檔案,這樣就可以減少硬碟的儲存空間,而且不會干擾到容器的執行

docker daemon

在docker當中,負責管理容器生命週期的是docker daemon,但daemon的作用並不限於管理容器,它可謂是docker環境的大管家,而docker cli就是一堆命令

daemon主要完成的工作是:

(1) daemon負責任務排程,它需要將客戶端傳來的命令請求轉化為特定的任務

(2) daemon 負責維護映象資料。一個完整的映象由許多子檔案層組成,而這個映象的依賴關係就是由daemon 來維護的

(3) daemon 負責資源的分配和資源的隔離

(4) daemon負責容器生命週期。daemon需要根據使用者指令和容器自身狀態來維護容器的生命週期

docker的 資源管理

docker 記憶體約束

與作業系統類似。容器可使用的記憶體包括實體記憶體和swap. docker通過下面兩組引數來控制容器記憶體的使用量

針對容器可使用的記憶體總量,docker有4種設定方式

a memory=-1,memory-swap=-1(預設設定)

預設情況下,對容器沒有記憶體使用限制,容器可以使用盡可能多的記憶體資源

b memory=L,memory-swap=-1

僅允許容器使用不超過L位元組的記憶體,但允許容器使用盡可能多的交換分割槽

例如 docker run -ti -m 300M --memory-swap -1 ubuntu:14.04 /bin/bash

c memory=L

在docker中,如果對--memory-swap沒有限制,那麼預設是 memory+swap=memory*2

例如 docker run -ti -m 300M ubuntu:14.04 /bin/bash 那麼 此時的交換分割槽為 300M

e memory=L ,memory-swap=M

我們還可以通過--memory-swap來設定允許使用的交換分割槽上限值

例如 docker run -ti -m 300M --memory-swap 1G ubuntu:14.04 /bin/bash

此時並沒有對--memory-swap作出限制,那麼預設就是 memory+swap=2memory

--oom-kill-disable

當docker 容器程序超過該資源限制的閥值,就會被作業系統發出OOM事件殺掉,當不想被作業系統殺掉,可以使用--oom-kill-disable 來禁用OOM-Killer,使用此引數時,如果docker 容器 使用 -m 引數,那麼當容器使用資源到達上限時,系統既不會停止掉,也不會分配資源給該容器,那麼該容器就會處於hung狀態, 如果 使用者使用了 --oom-kill-disable 引數,而又沒有使用-m引數,那麼該docker 容器就會盡可能多地使用主機記憶體資源

例如 限制容器記憶體使用總量,同時關閉OOM功能

docker run -ti -m 300M --oom-kill-disable ubuntu:14.04 /bin/bash

例如 沒有限制記憶體使用總量,同時還關閉了OOM功能

docker run -ti --oom-kill-disable ubuntu:14.04 /bin/bash

CPU限制

c或者-shares

預設情況下,所有容器都具有平等的CPU使用權重。但使用者可以通過Run命令來為每個容器設定CPU使用權重。CPU權重取值從0到1024,可以通過-c或者-shares(CPU共享使用權重)來修改預設值,

例如(容器C0的CPU使用權重為1024 那麼 -c=1024)

如果,當前主機系統中執行3個容器:C1,C2,C3。C1的cpu使用權重為1024,另外兩個為512,那麼C1就會得到50%的CPU時間片,而另外兩個就會得到25%的CPU時間片,例外

在一個多核CPU體系中,CPU時間片是所有CPU核總共時間片的總和。

–cpu-period、–cpu-quota

docker提供了–cpu-period、–cpu-quota兩個引數控制容器可以分配到的CPU時鐘週期。–cpu-period是用來指定容器對CPU的使用要在多長時間內做一次重新分配

cpu-period和cpu-quota的單位為微秒(μs)。cpu-period的最小值為1000微秒,最大值為1秒(10^6 μs),預設值為0.1秒(100000 μs)。cpu-quota的值預設為-1,表示不做控制

例如 docker run -ti --cpu-period=50000 --cpu-quota=25000 ubuntu:14.04 /bin/bash

容器程序需要每0.05秒使用單個CPU 0.025秒時間

--cpuset-cpus

除了可以設定CPU使用權重外,我們還可以設定容器可以使用CPU核數

docker run -ti --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash

設定容器僅能使用1號CPU和3號CPU

cgroups 詳細介紹

上面提到docker 的 docker 分配系統資源的基本用法,但是docker是怎麼做到的呢

docker底層是通過cgroups 來管理資源的,

cgroups 可以限制,記錄,隔離程序組所使用的物理資源(包括CPU,memory,I/O等)

cgroups 機制中有四個需要理解的的概念

(1) 任務(task).一個任務對應宿主機環境當中的一個程序

(2) 子系統(subsystem) 每一個子系統是對某一項具體物理資源的控制器。例如cpu 子系統就是對CPU資源的控制,記憶體子系統就是對記憶體資源的控制,

例如子系統cpuset可以在多個CPU系統中,為cgroup中的任務分配獨立的CPU和記憶體節點

子系統 memory,可以使在cgroup中任務使用的記憶體作出限制,同時生產任務的記憶體資源使用報告

(3) 控制組(control group) cgroups當中最基本的控制單元。假設這裡有個程序A,我們要限制其使用的系統記憶體總量的10%,我們就可以建立一個memory佔用10%的cgroup。然後,將程序A新增到cgroup,那麼程序A最多佔用記憶體總量的10%

(4)層級樹(hierarchy)。cgroups的排程單位,由一個或多個group組成的樹狀結構.每個層級樹通過繫結對應的子系統進行資源排程,同時子節點繼承父節點的屬性。整個系統可以有多個

層級樹

本質上,幾十docker daemon將此容器所有的程序都新增到一個group中,所以才能對docker作出資源的限制

docker 網路:

Veth 裝置對

引入Veth 裝置對是為了在不同的網路名稱空間之間進行通訊,利用它可以直接將兩個網路名稱空間連線起來。由於要連線兩個網路名稱空間,所以Veth裝置對都是成對出現的,

很像一對乙太網卡,並且中間有一根直連的網線。既然是一對網絡卡,那麼我們將其中一端稱為另一端的peer。在veth裝置的一端傳送資料時,它會將資料直接傳送到另一端,

並觸發另一端的接收資料

建立裝置對

ip link add veth0 type veth peer name veth1

檢視網路裝置 ip link show

9: [email protected]: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000

link/ether 6a:49:b0:44:c4:90 brd ff:ff:ff:ff:ff:ff

10: [email protected]: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000

link/ether fa:1f:57:16:78:41 brd ff:ff:ff:ff:ff:ff

我們可以看到,有兩個裝置生成了

現在這兩個裝置都在自己的名稱空間內

其實在docker 內部,veth裝置對也是聯絡容器到外面的重要裝置,離開它是不行的

容器網路設定引數

容器的網路設定引數共包括: --dns,--net,--add-host 和 mac-address四個引數

預設情況下,容器會複用主機的DNS設定,但是使用者可以通過--dns來覆蓋容器內的dns設定. docker每次建立容器時,都會生成一個虛擬MAC地址,使用者可以通過

--mac-address來重新設定容器的MAC地址

容器的四種網路連結方式

(1)bridge模式

在bridge模式下,docker daemon第一次啟動時會建立一個虛擬的網橋,預設的名字是docker0,在私有的網路空間中給這個網橋分配一個子網。針對由docker創建出來

的每一個容器,都會建立一個虛擬的乙太網裝置(veth裝置對),其中一端關聯到網橋上,另一端使用linux的網路名稱空間技術,對映到容器內的eth0裝置,然後從網橋的地址

段內給eth0介面分配一個IP地址

(2) none模式 使用--net=none指定

docker 允許通過docker run --net none來關閉網路介面,此時將關閉所有網路資料的輸入輸出,使用者只能通過STDIN,STDOUT或者files來完成I/O操作

(3) host模式: 使用--net=host指定

當網路模式設定為host時,這個容器完全共享host的網路堆疊。host所有的網路介面將完全對容器開放。容器的主機名也會存在於host的hostname中。這時,容器所有對

外暴露的port和對其他容器的link,將完全失效

(4) 複用容器模式

當網路模式設定為container時,這個容器將完全複用另一個容器的網路堆疊。格式為 --net container:<name|id>

如 docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1