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,并且給我分配一個命令行終端跟這個容器交互。
在這之前,我們先看看宿主機存在的進程。
可以看到當前管理員下sudo的PID為4987,在此基礎上我們bash的PID為4988,bash中執行了ps命令,PID與PPID的關系很明顯。
此時運行我們的docker容器。
在容器中查看該容器中的進程,出去這個ps,隻有一個PID為1的/bin/sh進程。
再來從我們的宿主機查看一下進程信息,排除掉sudo,bash和ps的進程,隻有一個sh,可以與容器中的/bin/sh容器作聯系,可以看到他的進程PID是7355,父進程是7332。
找到PPID為7332的這個進程,可以知道是一個叫containerd-shim的進程創建了該容器。
先不考慮containerd-shim進程的事,你應該已經注意到容器是什麼了,其實就是一個“特殊”的進程。
特殊在何處?
我們宿主機ps查看進程是可以看到容器進程的,而在容器中使用ps命令查看卻隻能看到容器他本身,一個PID為1的進程,PID為1在Linux有着特殊的意義。
PID為0的進程是idle進程,是由系統自動創建,運行在内核态,創建第一個用戶進程,也就是PID為1的init進程,init進程是linux系統中其它所有用戶進程的祖先進程,主要作用是處理僵屍進程。
當某個父進程比子進程提前消亡時,父進程會給子進程重新尋找“養父進程”,一般就是init進程,由init進程負責處理該子進程的消亡。
在容器中的進程居然是一個PID為1的進程,他可以管理他所能看到的進程生死,而看不到外面的宿主機進程,一葉障目,不見泰山。
這時候引入一張Docker商标圖片最為合适了。
鲸魚背上的一個個集裝箱就是所謂的容器,完全封閉起來,我們可以看到它,在它裡面卻不知道我們。
這就是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的使用接口是利用文件系統開放的,你可以在這個目錄下發現很多以資源命名的文件。
在創建容器的時候你可以指定資源分配的參數,如下。
docker run -it --cpu-period=100000 --cpu-quota=20000 busybox /bin/sh
使用該命令創建運行容器之後,可以看到運行的容器id為f0c0f3e5d0d2。
此時你可以在之前所說的sys/fs/cgroup下找到一個docker目錄,其中就有一個f0c0f3e5d0d2開頭的目錄。
在這個目錄中就可以找到容器f0c0f3e5d0d2的資源分配參數,提供給Cgroups,來為Docker容器分配資源。
還有一個重要地方,是關于容器本身的文件系統,當然和PID NameSpace一樣,是利用Mount NameSpace實現的,獨立出一片獨立空間的文件系統。
雖說是這樣,但有一點細節需要知道,Mount NameSpace創建一片獨立空間時是會繼承宿主機本身的已挂載空間的,所以在創建之後需要重新挂載所有節點。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!