Docker(1)底層實現


Docker並沒有傳統虛擬化的Hypervisor層,因為dokcer是基於容器技術的輕量級虛擬化,相對於傳統的虛擬化,省去了Hypervisor層的開銷,而且其虛擬化技術是基於內核的Cgroup和Namespace技術,處理邏輯與內核深度融合,所以在很多方面,docker的性能與物理機非常接近

在通信上,Docker並不會直接與內核交互,它是通過一個更底層的工具Libcontainer與內核交互的,Libcontainer 是真正意義上的容器引擎,它通過clone系統調用直接創建容器,通過pivot_root系統調用進入容器,且通過直接操作cgroupfs文件實現對資源的管控

Docker本身則側重於處理更上層的業務

容器=cgroup+namespace+rootfs+容器引擎(用戶態工具LXC)

  .Cgroup  資源控制

  .Namespace  訪問隔離

  .rootfs  文件系統隔離

  .容器引擎  生命周期控制

Docker底層的核心技術包括,linux上的名稱空間(Namesaces),控制組(Contorl groups),Union文件系統,和容器格式(Container format)

Cgroup是什么

是control groups 的簡寫,屬於linux內核的一個特性,用於限制和隔離一組進程對系統資源的使用

資源:CPU,內存,block I/O 網絡帶寬

devices:設備權限控制

cpuset:分配指定cpu和內存節點

cpu:控制cpu占用率

cpuacct:統計CPU使用情況

memory:限制內存的使用上限

freezer:凍結(暫停) Cgroup中的進程

net_cls:配合tc(traffic controller) 限制網絡帶寬

net_prio:設置進程的網絡流量優先級

huge_tlb:限制HugeTLB的使用

perf_event:允許Perf工具基於Cgroup分組做性能檢測

 

容器機制:

讓某些進程在彼此隔離的名字空間運行,大家雖然都共用一個內核和某些運行時的環境(例如一些系統命令和庫),但是彼此都看不到,都以為系統中只有自己存在,

名字空間來做權限的隔離控制

利用cgroups來做資源分配

Cgroup的接口和使用

1).掛載cgroupfs

mount -t cgroup -o cpuset cpuset /sys/fs/cgroup/

2) 查看cgroupfs

[root@linux-node2 cgroup]# ll /sys/fs/cgroup/
總用量 0
-rw-r--r-- 1 root root 0 5月  18 14:08 cgroup.clone_children
--w--w--w- 1 root root 0 5月  18 14:08 cgroup.event_control
-rw-r--r-- 1 root root 0 5月  18 14:08 cgroup.procs
-r--r--r-- 1 root root 0 5月  18 14:08 cgroup.sane_behavior
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.cpus
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.memory_migrate
-r--r--r-- 1 root root 0 5月  18 14:08 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.mems
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 5月  18 14:08 cpuset.sched_relax_domain_level
drwxr-xr-x 2 root root 0 7月   4 15:26 docker
drwxr-xr-x 4 root root 0 5月  18 14:09 libvirt
-rw-r--r-- 1 root root 0 5月  18 14:08 notify_on_release
-rw-r--r-- 1 root root 0 5月  18 14:08 release_agent
-rw-r--r-- 1 root root 0 5月  18 14:08 tasks

可以看到這里有很多的控制文件,其中以cpuset開頭的控制文件,都是由cpuset子系統產生的,其他文件則是由Cgroup產生,

這里的tasks文件記錄了這個Cgroup的所有進程,包括線程,在系統啟動后,默認沒有對Cgroup做任何配置的情況下,cgroupfs只有一個根目錄,並且系統所有進程都在這個根目錄中,既進程pid都在根目錄tasks文件中

3)創建Cgroup

mkdir /sys/fs/cgroup/child

這樣就創建了一個新的Cgroup

4)配置Cgroup

[root@linux-node2 cgroup]# echo 0 > child/cpuset.cpus
[root@linux-node2 cgroup]# echo 0 > child/cpuset.mems

這樣就可以限制這個Cgroup的進程只能在0號CPU上運行,並且只會從0號內存節點分配內存

5)使能Cgroup

[root@linux-node2 child]# echo 2487 > /sys/fs/cgroup/child/tasks 

這里2487代表進程的pid號

[root@linux-node2 child]# echo $$ > /sys/fs/cgroup/child/tasks 也是可以的

這里$$代表當前進程

-----------------
寫入task只會把制定的進程加到child中.
寫入cgroup.procs則會把這個進程所屬的整個線程都加到child中

Cgroup子系統介紹

 1.cpuet子系統

cpuset可以為一組進程分配指定的CPU和內存節點,cpuset一開始用在高性能計算(HPC)行的.在NUMA架構的服務器上,通過將進程綁定到固定的CPU和內存節點上

來避免進程在運行時因跨節點內存訪問而導致的性能下降,當然,現在cpuset也廣泛的用在了kvm和容器上

  cpuset主要接口:
cpuset.cpus: 允許進程使用的CPU列表 (例如:0-4,9)
cpuset.mems: 允許進程使用的內存節點列表 (例如0-1)

2.CPU子系統

 cpu子系統用於限制進程的CPU占用率,實際上它有三個功能,分別通過不同的接口來提供

 

CPU比重分配: 這個特性使用的接口是cpu.shares .如果cgroupfs目錄下創建了2個Cgroup,分別是C1和C2,並且將cpu.shares分別設置為512和1024,那么當C1和C2爭用CPU時,C2將會得到比C1 多一倍的CPU占用率,要值得注意的是,只有當它們爭用CPU時,cpu.share才會起作用,如果C2是空閑的,那么C1可以得到全部的CPU資源

CPU帶寬限制:這個特性使用的接口是cpu.cfs_period_us和cpu.cfs_quota_us 。這2個接口單位是微秒

實時進程的CPU帶寬限制: 使用的是cpu.rt_period_us和cpu.rt__runtime_us

3.cpuacct子系統

用來統計個Cgroup的CPU使用情況

4.memory子系統

限制Cgroup所能使用的內存上限,有如下接口

memory.limit_in_bytes: 設定內存上限,單位是字節,也可以使用k/K,m/M或者g/G 表示要設置數值單位

echo 1G > memory.limit_in——bytes
如果Cgroup使用的內存超過上限,linux內核會嘗試回收內存,如果仍然無法將內存使用量控制在上限之內,系統將會觸發OOM,選擇並殺死該Cgroup中的某個進程
memory.memsw.limit__in_bytes:設定內存加上交換分區的使用量,通過設置這個值,可以防止進程把交換分區用光


memory.oom_control: 如果設置為0,那么在內存使用量超過上限時,系統不會'殺死' 進程,而是阻塞進程直到有內存被釋放可供使用時: 另一方面,系統會向用戶態發送事件通知,用戶態的監控程序可以根據該事件來做相應的處理,例如提高內存上限等

5.blkio子系統

用來限制Cgroup的block I/O帶寬

6.devices子系統

控制Cgroup的進程對那些設備有訪問權限

基本架構

Docker采用了C/S架構

客戶端和服務端可以運行在一個機器上,也可以通過socket或者RESTful API 來進行通信

Docker  daemon 一般在宿主機后台運行,等待接收客戶端的消息

Docker客戶端則為客戶提供一系列可執行的命令, 用戶使用這些命令跟docker daemon交互

剖析

名字空間

  名字空間是linux內核一個強大的特性,每個容器都有自己單獨的名字空間,運行在其中的應用都像是在獨立的操作系統一樣。名字空間保證了各容器之前互不影響

pid名字空間

  不同用戶的進程就是通過pid名字空間隔離開來的,且不同名字空間中可以有相同的pid。所有的LXC進程在Dokcer中的父進程為dokcer進程

每個LXC(基於容器的操作系統層級的虛擬化技術)進程具有不同的名字空間,同時由於永許嵌套,因此可以很方便的實現嵌套的Docker容器

net名字空間

  網絡端口還是共享的host端口 ,網絡隔離是通過net名字空間實現的, 每個net名字空間有獨立的網絡設置,IP地址,路由表,/proc/net 目錄(proc文件系統是一個偽文件系統,它只存在內存當中,而不占用外存空間。它以文件系統的方式為訪問系統內核數據的操作提供接口)。這樣每個容器就隔離開來。Docker默認采用veth方式(成對出現的點對點網絡設置)【==懵逼狀態。。】 將容器中的虛擬網卡同host上的一個Dokcer網橋docker0連接在一起

[軟連:http://www.open-open.com/lib/view/open1488423458438.html]

ipc名字空間

  容器中進程交互還是采用了linux常見的進程交互方法,包括信號量,消息隊列,共享內存等,容器的進程間交互實際上還是host上具有相同pid名字空間中的進程間交互,因此需要在ipc資源申請時加入名字空間信息,每個IPC資源有一個唯一的32位ID

mnt名字空間

  類似chroot,將一個進程放到一個特定的目錄執行,mnt名字空間允許不同名字空間的進程看到文件結構,這樣每個名字空間中的進程所看到的文件目錄就被隔離開來了。每個名字空間中的容器在 /proc/mounts的信息只包含所在名字空間的mount point

uts名字空間

  UTS名字空間允許每個容器擁有獨立的hostname和domain name 使其在網絡上可以被視作一個獨立的節點而非主機上的一個進程

USER 名字空間

  每個容器可以有不同的用戶和組ID,也就是說可以在容器內用容器內部的用戶執行程序而非主機上的用戶

控制組

  控制組(cgroups)是linux內核的一個特性,主要用來對共享資源進行隔離,限制,審計等.只有能控制分配到容器的資源,才能避免當多個容器同時運行時對系統資源的競爭

linux內核2.6.24開始支持

控制組可以提供對容器內的 內存,CPU,磁盤IO等資源的限制和計費管理

Union文件系統

  union文件系統(unionFS)是一種分層,輕量級並且高性能的文件系統,它支持對文件系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下

  union文件系統是docker鏡像的基礎,鏡像可以通過分層來進行繼承,基於基礎鏡像(沒有父鏡像),可以制作各種具有應用鏡像

另外,不同docker容器就可以共享一些基礎的文件系統層,同時再加上自己獨有的改動層,大大提高了存儲的效率

Docker中使用的AUFS 就是一種union FS。 AUFS支持為每一個成員目錄(類似git的分支)設定 只讀,讀寫,寫出 權限,同時AUFS里有一個類似分層的概念,對只讀權限的分支可以邏輯上進行增量的修改(不影響只讀部分)

Docker目前支持的union文件系統種類包括 AUFS,btrfs,vfs和DeviceMapper

容器格式

最初,docker采用了LXC中的容器格式,自1.20版本開始,Docker也開始支持新的libcontainer格式,並作業默認選項

Docker網絡實現

Docker 的網絡實現其實就是利用了 Linux 上的網絡名字空間和虛擬網絡設備(特別是 veth pair)。建議先
熟恲了解返兩部分的基本概念再閱讀本章。
基本原理
首先,要實現網絡通信,機器需要至少一個網絡接口(物理接口戒虛擬接口)來收發數據包;此外,如果
丌同子網乀間要迕行通信,需要路由機制。
Docker 中的網絡接口默訃都是虛擬的接口。虛擬接口的優勢乀一是轉發效率較高。 Linux 通過在內核中迕
行數據復制來實現虛擬接口乀間的數據轉發,發送接口的發送緩存中的數據包被直接復制到接收接口的接
收緩存中。對亍本地系統和容器內系統看來就像是一個正常的以太網卡,叧是它丌需要真正同外部網絡設
備通信,速度要徆快。
Docker 容器網絡就利用了返項技術。它在本地主機和容器內分別創建一個虛擬接口,幵譏它們彼此連通
(返樣的一對接口叨做 veth pair )。

veth pair  

 

鏡像的實現原理

Docker 鏡像是怎舉實現增量的修改和維護的? 每個鏡像都由徆多層次極成,Docker 使用 Union FS 將返
些丌同的層結合到一個鏡像中去。
通常 Union FS 有兩個用途, 一方面可以實現丌借劣 LVM、RAID 將多個 disk 掛到同一個目錄下,另一個更
常用的就是將一個叧讀的分支和一個可寫的分支聯合在一起,Live CD 正是基亍此方法可以允許在鏡像丌
變的基礎上允許用戶在其上迕行一些寫操作。 Docker 在 AUFS 上極建的容器也是利用了類似的原理。

Docker 容器

容器是 Docker 又一核心概念。
簡單的說,容器是獨立運行的一個戒一組應用,以及它們的運行態環境。對應的,虛擬機可以理解為模擬
運行的一整套操作系統(提供了運行態環境和其他系統環境)和跑在上面的應用。

當操作docker run 內部實現

docker run 來創建容器時,Docker 在后台運行的標准操作包括:
檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載
利用鏡像創建並啟動一個容器
分配一個文件系統,並在叧讀的鏡像層外面掛載一層可讀寫層
從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去
從地址池配置一個 ip 地址給容器
執行用戶挃定的應用程序
執行完畢后容器被終止

數據卷

數據卷是一個可供一個戒多個容器使用的特殊目錄,它繞過 UFS,可以提供徆多有用的特性:
數據卷可以在容器之間共享和重用
對數據卷的修改會立馬生效
對數據卷的更新,不會影響鏡像
卷會一直存在,直到沒有容器使用

---數據卷的使用。類似於linux下對目錄的mount

使用 -v 標記來創建一個數據卷幵掛載到容器里。在一次 run 中多次使用可以掛載多個數據卷。

docker run -d -P --name web -v /webapp training/webapp python app.py

使用 -v 標記也可以挃定掛載一個本地主機的目錄到容器中去。

 docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

加載主機的 /src/webapp 目錄到容器的 /opt/webapp 目錄,

本地目錄的路徑必須是絕對路徑,如果目錄不存在 Docker 會自動為你創建它。

Docker 掛載數據卷的默訃權限是讀寫,用戶也可以通過 :ro 指定為只讀

docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py

1.容器互聯--后續在回顧

2.利用數據卷在備份和恢復

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM