Docker容器到底是什么?


Docker是一個開源的應用容器引擎,是近些年最火的技術之一,Docker公司從Docker項目開源之后發家致富把公司商標改為了Docker,收購了fit項目,整合為了docker-compose,前景一片大好,但是據說Docker在社區中話語權過於強硬,得罪了不少公司,google與rethub等牽頭發起了kubernetes項目,雖說讓Docker在市場上損失很大,但為相信Docker未來前景會很好,k8s雖然強大,但主流也是采用了Docker的容器規范,只會更好。

Docker創建一個容器的方式很簡單,使用docker run命令。

docker run -it busybox /bin/sh

-it 參數告訴了Docker項目在啟動容器后,需要給我們分配一個文本輸入輸出環境,也就是TTY,跟容器的標准輸入相關聯,這樣我們就可以和這個Docker容器進行交互了。

/bin/sh 就是我們要在 Docker 容器里運行的程序。

所以,上面這條指令翻譯過來就是:請幫我啟動一個容器,在容器里執行/bin/sh,並且給我分配一個命令行終端跟這個容器交互。

在這之前,我們先看看宿主機存在的進程。

image.png

可以看到當前管理員下sudo的PID為4987,在此基礎上我們bash的PID為4988,bash中執行了ps命令,PID與PPID的關系很明顯。

此時運行我們的docker容器。

在容器中查看該容器中的進程,出去這個ps,只有一個PID為1的/bin/sh進程。

image.png

再來從我們的宿主機查看一下進程信息,排除掉sudo,bash和ps的進程,只有一個sh,可以與容器中的/bin/sh容器作聯系,可以看到他的進程PID是7355,父進程是7332。

image.png

找到PPID為7332的這個進程,可以知道是一個叫containerd-shim的進程創建了該容器。

image.png

先不考慮containerd-shim進程的事,你應該已經注意到容器是什么了,其實就是一個“特殊”的進程。

特殊在何處?

我們宿主機ps查看進程是可以看到容器進程的,而在容器中使用ps命令查看卻只能看到容器他本身,一個PID為1的進程,PID為1在Linux有着特殊的意義。

PID為0的進程是idle進程,是由系統自動創建,運行在內核態,創建第一個用戶進程,也就是PID為1的init進程,init進程是linux系統中其它所有用戶進程的祖先進程,主要作用是處理僵屍進程。

當某個父進程比子進程提前消亡時,父進程會給子進程重新尋找“養父進程”,一般就是init進程,由init進程負責處理該子進程的消亡。

在容器中的進程居然是一個PID為1的進程,他可以管理他所能看到的進程生死,而看不到外面的宿主機進程,一葉障目,不見泰山。

這時候引入一張Docker商標圖片最為合適了。

image.png

鯨魚背上的一個個集裝箱就是所謂的容器,完全封閉起來,我們可以看到它,在它里面卻不知道我們。

這就是Docker的容器封閉技術了。

他的實現其實很簡單,Linux的進程創建函數clone,可以接受一個參數CLONE_NEWPID,這樣創建的這個進程將會“看到”一個全新的進程空間,在這個進程空間里,它的PID是1。之所以說“看到”,是因為這只是一個“障眼法”,在宿主機真實的進程空間里,這個進程的 PID 還是真實的數值。

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

這種技術叫做NameSpace,上面說的是PID NameSpace,當然Linux其他的東西也可以有NameSpace,比如說Mount、UTS、IPC、Network和User。

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

綜上,可以發現容器其實就是一個帶了NameSpace參數特殊創建的進程。

這暴露出了幾個問題,第一是所有容器都是共用了同一個宿主機的操作系統內核,第二是如果我們在低版本的Linux上使用高版本的容器,是會出現問題的,還有第三是這種NameSpace的隔離並不如虛擬化技術隔離的徹底。

Linux中其實有很多資源都是無法做到隔離的,比如說時間,如果你的容器中嘗試使用修改時間的系統調用,就會影響到宿主機的時間。

所以說,Docker的容器其實還是有未知的隱患存在。

容器雖然覺得自己是PID為1的init進程,但是對於我們宿主機來說,他是一個正常的進程,和其它進程一樣,競爭着系統中的資源。

為此,Linux內核誕生了一個新特性,Linux Cgroups,可以為進程設置資源限制指標。

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

Cgroups的使用接口是利用文件系統開放的,你可以在這個目錄下發現很多以資源命名的文件。

image.png

image.png

在創建容器的時候你可以指定資源分配的參數,如下。

docker run -it --cpu-period=100000 --cpu-quota=20000 busybox /bin/sh

使用該命令創建運行容器之后,可以看到運行的容器id為f0c0f3e5d0d2。

image.png

image.png

此時你可以在之前所說的sys/fs/cgroup下找到一個docker目錄,其中就有一個f0c0f3e5d0d2開頭的目錄。

image.png

在這個目錄中就可以找到容器f0c0f3e5d0d2的資源分配參數,提供給Cgroups,來為Docker容器分配資源。

image.png

還有一個重要地方,是關於容器本身的文件系統,當然和PID NameSpace一樣,是利用Mount NameSpace實現的,獨立出一片獨立空間的文件系統。

雖說是這樣,但有一點細節需要知道,Mount NameSpace創建一片獨立空間時是會繼承宿主機本身的已掛載空間的,所以在創建之后需要重新掛載所有節點。

此處細節,可以看左耳朵耗子的這篇博客,非常詳細。

https://coolshell.cn/articles/17010.html


免責聲明!

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



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