[docker]docker實現原理


一,簡介

二,Docker Engine

三,Docker架構

四,底層技術

啟動一個容器實例,需要用到的最主要的基礎知識包括Control groups(控制組),Namespaces(命名空間),Union file systems(聯合文件系統),Container format(容器格式)

以下內容對三種技術做簡略的介紹

1, cgroup

此處參考內容有: https://time.geekbang.org/column/article/14642https://docs.docker.com/get-started/overview/

Linux Cgroups 的全稱是 Linux Control Group。它最主要的作用,就是限制一個進程組能夠使用的資源上限,包括 CPU、內存、磁盤、網絡帶寬等等。

此外,Cgroups 還能夠對進程進行優先級設置、審計,以及將進程掛起和恢復等操作。我們重點探討它與容器關系最緊密的“限制”能力,並通過一組實踐來帶你認識一下 Cgroups。

在 Linux 中,Cgroups 給用戶暴露出來的操作接口是文件系統,即它以文件和目錄的方式組織在操作系統的 /sys/fs/cgroup 路徑下。

在 Ubuntu 20.04 機器里,我可以用 mount 指令把它們展示出來,這條命令是:

$ mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)

 

它的輸出結果,是一系列文件系統目錄。如果你在自己的機器上沒有看到這些目錄,那你就需要自己去掛載 Cgroups,具體做法可以自行 Google。

可以看到,在 /sys/fs/cgroup 下面有很多諸如 cpuset、cpu、 memory 這樣的子目錄,也叫子系統。這些都是我這台機器當前可以被 Cgroups 進行限制的資源種類。而在子系統對應的資源種類下,你就可以看到該類資源具體可以被限制的方法。

Linux Cgroups 的設計還是比較易用的,簡單粗暴地理解呢,它就是一個子系統目錄加上一組資源限制文件的組合。

而對於 Docker 等 Linux 容器項目來說,它們只需要在每個子系統下面,為每個容器創建一個控制組(即創建一個新目錄),然后在啟動容器進程之后,把這個進程的 PID 填寫到對應控制組的 tasks 文件中就可以了。

2, namespace

此處參考內容有: https://time.geekbang.org/column/article/14642https://docs.docker.com/get-started/overview/

Namespace 的使用方式:它其實只是 Linux 創建新進程的一個可選參數。

我們知道,在 Linux 系統中創建線程的系統調用是 clone(),比如:

int pid = clone(main_function, stack_size, SIGCHLD, NULL); 

 這個系統調用就會為我們創建一個新的進程,並且返回它的進程號 pid。

而當我們用 clone() 系統調用創建一個新進程時,就可以在參數中指定 CLONE_NEWPID 參數,比如:

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL); 

 這時,新創建的這個進程將會“看到”一個全新的進程空間,在這個進程空間里,它的 PID 是 1。之所以說“看到”,是因為這只是一個“障眼法”,在宿主機真實的進程空間里,這個進程的 PID 還是真實的數值,比如 100。

當然,我們還可以多次執行上面的 clone() 調用,這樣就會創建多個 PID Namespace,而每個 Namespace 里的應用進程,都會認為自己是當前容器里的第 1 號進程,它們既看不到宿主機里真正的進程空間,也看不到其他 PID Namespace 里的具體情況。

而除了我們剛剛用到的 PID Namespace,Linux 操作系統還提供了 Mount、UTS、IPC、Network 和 User 這些 Namespace,用來對各種不同的進程上下文進行“障眼法”操作。

比如,Mount Namespace,用於讓被隔離進程只看到當前 Namespace 里的掛載點信息;Network Namespace,用於讓被隔離進程看到當前 Namespace 里的網絡設備和配置。

 

很多人會把 Docker 項目稱為“輕量級”虛擬化技術的原因,實際上就是把虛擬機的概念套在了容器上。可是這樣的說法,卻並不嚴謹。

上圖是虛擬化和容器的對比圖(注:在這個對比圖里,我們應該把 Docker 畫在跟應用同級別並且靠邊的位置。這意味着,用戶運行在容器里的應用進程,跟宿主機上的其他進程一樣,都由宿主機操作系統統一管理,只不過這些被隔離的進程擁有額外設置過的 Namespace 參數。而 Docker 項目在這里扮演的角色,更多的是旁路式的輔助和管理工作。)

在理解了 Namespace 的工作方式之后,你就會明白,跟真實存在的虛擬機不同,在使用 Docker 的時候,並沒有一個真正的“Docker 容器”運行在宿主機里面。

Docker 項目幫助用戶啟動的,還是原來的應用進程,只不過在創建這些進程時,Docker 為它們加上了各種各樣的 Namespace 參數。

這時,這些進程就會覺得自己是各自 PID Namespace 里的第 1 號進程,只能看到各自 Mount Namespace 里掛載的目錄和文件,只能訪問到各自 Network Namespace 里的網絡設備,就仿佛運行在一個個“容器”里面,與世隔絕。

以下為docker官方文檔的解釋

docker使用一種叫做namespace的技術來提供名為container的隔離工作區,當你開始運行一個容器,docker會為該容器創建一組命名空間.

這些命名空間提供了一個隔離層,容器的各個面都被限制在單獨的命名空間中運行,並且其訪問邊界也被限制在相應的命名空間中.

Docker Engine在linux中使用如下的命名空間:

  • pid namespace: 進程隔離(PID: Process ID).
  • net namespace: 管理網絡接口,網絡隔離
  • ipc namespace: 管理對ipc資源的訪問 (IPC: 進程間通信).
  • mnt namespace: 管理文件系統掛載點(MNT:mount)
  • uts namespace: 隔離內核和版本標識符(UTS: Unix Timesharing System).which defines the system name and domain

 

 

 


免責聲明!

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



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