從進(jìn)程中去理解?Docker隔離技術(shù)
1、起源
“容器”這項(xiàng)技術(shù)的來(lái)龍去脈:
- 容器技術(shù)的興起源于 PaaS 技術(shù)的普及;
- Docker 公司發(fā)布的 Docker 項(xiàng)目具有里程碑式的意義;
- Docker 項(xiàng)目通過(guò)“容器鏡像”,解決了應(yīng)用打包這個(gè)根本性難題。
容器本身沒(méi)有價(jià)值,有價(jià)值的是“容器編排”。
也正因?yàn)槿绱?,容器技術(shù)生態(tài)才爆發(fā)了一場(chǎng)關(guān)于“容器編排”的“戰(zhàn)爭(zhēng)”。而這次戰(zhàn)爭(zhēng),最終以 Kubernetes 項(xiàng)目和 CNCF 社區(qū)的勝利而告終。
容器,到底是怎么一回事兒?
容器其實(shí)是一種沙盒技術(shù)。顧名思義,沙盒就是能夠像一個(gè)集裝箱一樣,把你的應(yīng)用“裝”起來(lái)的技術(shù)。這樣,應(yīng)用與應(yīng)用之間,就因?yàn)橛辛诉吔缍恢劣谙嗷ジ蓴_;而被裝進(jìn)集裝箱的應(yīng)用,也可以被方便地搬來(lái)搬去,這不就是 PaaS 最理想的狀態(tài)嘛。
不過(guò),這兩個(gè)能力說(shuō)起來(lái)簡(jiǎn)單,但要用技術(shù)手段去實(shí)現(xiàn)它們,可能大多數(shù)人就無(wú)從下手了。
2、容器類(lèi)比進(jìn)程
所以,我就先來(lái)跟你說(shuō)說(shuō)這個(gè)“邊界”的實(shí)現(xiàn)手段。
假如,現(xiàn)在你要寫(xiě)一個(gè)計(jì)算加法的小程序,這個(gè)程序需要的輸入來(lái)自于一個(gè)文件,計(jì)算完成后的結(jié)果則輸出到另一個(gè)文件中。
由于計(jì)算機(jī)只認(rèn)識(shí) 0 和 1,所以無(wú)論用哪種語(yǔ)言編寫(xiě)這段代碼,最后都需要通過(guò)某種方式翻譯成二進(jìn)制文件,才能在計(jì)算機(jī)操作系統(tǒng)中運(yùn)行起來(lái)。
而為了能夠讓這些代碼正常運(yùn)行,我們往往還要給它提供數(shù)據(jù),比如我們這個(gè)加法程序所需要的輸入文件。這些數(shù)據(jù)加上代碼本身的二進(jìn)制文件,放在磁盤(pán)上,就是我們平常所說(shuō)的一個(gè)“程序”,也叫代碼的可執(zhí)行鏡像(executable image)。
然后,我們就可以在計(jì)算機(jī)上運(yùn)行這個(gè)“程序”了。
首先,操作系統(tǒng)從“程序”中發(fā)現(xiàn)輸入數(shù)據(jù)保存在一個(gè)文件中,所以這些數(shù)據(jù)就被會(huì)加載到內(nèi)存中待命。同時(shí),操作系統(tǒng)又讀取到了計(jì)算加法的指令,這時(shí),它就需要指示 CPU 完成加法操作。而 CPU 與內(nèi)存協(xié)作進(jìn)行加法計(jì)算,又會(huì)使用寄存器存放數(shù)值、內(nèi)存堆棧保存執(zhí)行的命令和變量。同時(shí),計(jì)算機(jī)里還有被打開(kāi)的文件,以及各種各樣的 I/O 設(shè)備在不斷地調(diào)用中修改自己的狀態(tài)。
就這樣,一旦“程序”被執(zhí)行起來(lái),它就從磁盤(pán)上的二進(jìn)制文件,變成了計(jì)算機(jī)內(nèi)存中的數(shù)據(jù)、寄存器里的值、堆棧中的指令、被打開(kāi)的文件,以及各種設(shè)備的狀態(tài)信息的一個(gè)集合。像這樣一個(gè)程序運(yùn)起來(lái)后的計(jì)算機(jī)執(zhí)行環(huán)境的總和,就是我們今天的主角:進(jìn)程。
所以,對(duì)于進(jìn)程來(lái)說(shuō),它的靜態(tài)表現(xiàn)就是程序,平常都安安靜靜地待在磁盤(pán)上;而一旦運(yùn)行起來(lái),它就變成了計(jì)算機(jī)里的數(shù)據(jù)和狀態(tài)的總和,這就是它的動(dòng)態(tài)表現(xiàn)。
而容器技術(shù)的核心功能,就是通過(guò)約束和修改進(jìn)程的動(dòng)態(tài)表現(xiàn),從而為其創(chuàng)造出一個(gè)“邊界”。
3、隔離技術(shù)
對(duì)于 Docker 等大多數(shù) Linux 容器來(lái)說(shuō),Cgroups 技術(shù)是用來(lái)制造約束的主要手段,而 Namespace 技術(shù)則是用來(lái)修改進(jìn)程視圖的主要方法。
你可能會(huì)覺(jué)得 Cgroups 和 Namespace
這兩個(gè)概念很抽象,別擔(dān)心,接下來(lái)我們一起動(dòng)手實(shí)踐一下,你就很容易理解這兩項(xiàng)技術(shù)了。
假設(shè)你已經(jīng)有了一個(gè) Linux 操作系統(tǒng)上的 Docker 項(xiàng)目在運(yùn)行,比如我的環(huán)境是 Ubuntu 16.04 和 Docker CE 18.05
。
接下來(lái),讓我們首先創(chuàng)建一個(gè)容器來(lái)試試。
$ docker run -it busybox /bin/sh / #
這個(gè)命令是 Docker 項(xiàng)目最重要的一個(gè)操作,即大名鼎鼎的 docker run
。
而 -it 參數(shù)告訴了 Docker 項(xiàng)目在啟動(dòng)容器后,需要給我們分配一個(gè)文本輸入 / 輸出環(huán)境,也就是 TTY,跟容器的標(biāo)準(zhǔn)輸入相關(guān)聯(lián),這樣我們就可以和這個(gè) Docker 容器進(jìn)行交互了。而 /bin/sh 就是我們要在 Docker 容器里運(yùn)行的程序。
所以,上面這條指令翻譯成人類(lèi)的語(yǔ)言就是:請(qǐng)幫我啟動(dòng)一個(gè)容器,在容器里執(zhí)行 /bin/sh,并且給我分配一個(gè)命令行終端跟這個(gè)容器交互。
這樣,我的 Ubuntu 16.04
機(jī)器就變成了一個(gè)宿主機(jī),而一個(gè)運(yùn)行著 /bin/sh 的容器,就跑在了這個(gè)宿主機(jī)里面。
上面的例子和原理,如果你已經(jīng)玩過(guò) Docker,一定不會(huì)感到陌生。此時(shí),如果我們?cè)谌萜骼飯?zhí)行一下 ps 指令,
就會(huì)發(fā)現(xiàn)一些更有趣的事情:
/ # ps PID USER TIME COMMAND 1 root 0:00 /bin/sh 10 root 0:00 ps
可以看到,我們?cè)?Docker 里最開(kāi)始執(zhí)行的 /bin/sh,就是這個(gè)容器內(nèi)部的第 1 號(hào)進(jìn)程(PID=1),而這個(gè)容器里一共只有兩個(gè)進(jìn)程在運(yùn)行。這就意味著,前面執(zhí)行的 /bin/sh,以及我們剛剛執(zhí)行的 ps,已經(jīng)被 Docker 隔離在了一個(gè)跟宿主機(jī)完全不同的世界當(dāng)中。
這究竟是怎么做到呢?
本來(lái),每當(dāng)我們?cè)谒拗鳈C(jī)上運(yùn)行了一個(gè) /bin/sh 程序,操作系統(tǒng)都會(huì)給它分配一個(gè)進(jìn)程編號(hào),比如 PID=100。這個(gè)編號(hào)是進(jìn)程的唯一標(biāo)識(shí),就像員工的工牌一樣。所以 PID=100,可以粗略地理解為這個(gè) /bin/sh 是我們公司里的第 100 號(hào)員工,而第 1 號(hào)員工就自然是比爾 · 蓋茨這樣統(tǒng)領(lǐng)全局的人物。
而現(xiàn)在,我們要通過(guò) Docker 把這個(gè) /bin/sh 程序運(yùn)行在一個(gè)容器當(dāng)中。這時(shí)候,Docker 就會(huì)在這個(gè)第 100 號(hào)員工入職時(shí)給他施一個(gè)“障眼法”,讓他永遠(yuǎn)看不到前面的其他 99 個(gè)員工,更看不到比爾 · 蓋茨。這樣,他就會(huì)錯(cuò)誤地以為自己就是公司里的第 1 號(hào)員工。
這種機(jī)制,其實(shí)就是對(duì)被隔離應(yīng)用的進(jìn)程空間做了手腳,使得這些進(jìn)程只能看到重新計(jì)算過(guò)的進(jìn)程編號(hào),比如 PID=1。可實(shí)際上,他們?cè)谒拗鳈C(jī)的操作系統(tǒng)里,還是原來(lái)的第 100 號(hào)進(jìn)程。
這種技術(shù),就是 Linux 里面的 Namespace 機(jī)制。而 Namespace
的使用方式也非常有意思:它其實(shí)只是 Linux 創(chuàng)建新進(jìn)程的一個(gè)可選參數(shù)。我們知道,在 Linux 系統(tǒng)中創(chuàng)建線程的系統(tǒng)調(diào)用是 clone(),
比如:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
這個(gè)系統(tǒng)調(diào)用就會(huì)為我們創(chuàng)建一個(gè)新的進(jìn)程,并且返回它的進(jìn)程號(hào) pid。
而當(dāng)我們用 clone() 系統(tǒng)調(diào)用創(chuàng)建一個(gè)新進(jìn)程時(shí),就可以在參數(shù)中指定 CLONE_NEWPID 參數(shù),比如:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
這時(shí),新創(chuàng)建的這個(gè)進(jìn)程將會(huì)“看到”一個(gè)全新的進(jìn)程空間,在這個(gè)進(jìn)程空間里,它的 PID 是 1。之所以說(shuō)“看到”,是因?yàn)檫@只是一個(gè)“障眼法”,在宿主機(jī)真實(shí)的進(jìn)程空間里,這個(gè)進(jìn)程的 PID 還是真實(shí)的數(shù)值,比如 100。
當(dāng)然,我們還可以多次執(zhí)行上面的 clone() 調(diào)用,這樣就會(huì)創(chuàng)建多個(gè) PID Namespace,而每個(gè) Namespace 里的應(yīng)用進(jìn)程,都會(huì)認(rèn)為自己是當(dāng)前容器里的第 1 號(hào)進(jìn)程,它們既看不到宿主機(jī)里真正的進(jìn)程空間,也看不到其他 PID Namespace 里的具體情況。
而除了我們剛剛用到的 PID Namespace,Linux 操作系統(tǒng)還提供了 Mount、UTS、IPC、Network 和 User 這些 Namespace,用來(lái)對(duì)各種不同的進(jìn)程上下文進(jìn)行“障眼法”操作。
比如,Mount Namespace,用于讓被隔離進(jìn)程只看到當(dāng)前 Namespace 里的掛載點(diǎn)信息;Network Namespace,用于讓被隔離進(jìn)程看到當(dāng)前 Namespace 里的網(wǎng)絡(luò)設(shè)備和配置。
這,就是 Linux 容器最基本的實(shí)現(xiàn)原理了。
所以,Docker 容器這個(gè)聽(tīng)起來(lái)玄而又玄的概念,實(shí)際上是在創(chuàng)建容器進(jìn)程時(shí),指定了這個(gè)進(jìn)程所需要啟用的一組 Namespace 參數(shù)。這樣,容器就只能“看”到當(dāng)前 Namespace 所限定的資源、文件、設(shè)備、狀態(tài),或者配置。而對(duì)于宿主機(jī)以及其他不相關(guān)的程序,它就完全看不到了。
所以說(shuō),容器,其實(shí)是一種特殊的進(jìn)程而已。
4、總結(jié)
談到為“進(jìn)程劃分一個(gè)獨(dú)立空間”的思想,相信你一定會(huì)聯(lián)想到虛擬機(jī)。而且,你應(yīng)該還看過(guò)一張?zhí)摂M機(jī)和容器的對(duì)比圖。
這幅圖的左邊,畫(huà)出了虛擬機(jī)的工作原理。其中,名為 Hypervisor 的軟件是虛擬機(jī)最主要的部分。它通過(guò)硬件虛擬化功能,模擬出了運(yùn)行一個(gè)操作系統(tǒng)需要的各種硬件,比如 CPU、內(nèi)存、I/O 設(shè)備等等。然后,它在這些虛擬的硬件上安裝了一個(gè)新的操作系統(tǒng),即 Guest OS。
這樣,用戶(hù)的應(yīng)用進(jìn)程就可以運(yùn)行在這個(gè)虛擬的機(jī)器中,它能看到的自然也只有 Guest OS 的文件和目錄,以及這個(gè)機(jī)器里的虛擬設(shè)備。這就是為什么虛擬機(jī)也能起到將不同的應(yīng)用進(jìn)程相互隔離的作用。
而這幅圖的右邊,則用一個(gè)名為 Docker Engine 的軟件替換了 Hypervisor。這也是為什么,很多人會(huì)把 Docker 項(xiàng)目稱(chēng)為“輕量級(jí)”虛擬化技術(shù)的原因,實(shí)際上就是把虛擬機(jī)的概念套在了容器上。
可是這樣的說(shuō)法,卻并不嚴(yán)謹(jǐn)。
在理解了 Namespace
的工作方式之后,你就會(huì)明白,跟真實(shí)存在的虛擬機(jī)不同,在使用 Docker 的時(shí)候,并沒(méi)有一個(gè)真正的“Docker 容器”運(yùn)行在宿主機(jī)里面。Docker 項(xiàng)目幫助用戶(hù)啟動(dòng)的,還是原來(lái)的應(yīng)用進(jìn)程,只不過(guò)在創(chuàng)建這些進(jìn)程時(shí),Docker 為它們加上了各種各樣的 Namespace
參數(shù)。
這時(shí),這些進(jìn)程就會(huì)覺(jué)得自己是各自PID Namespace
里的第 1 號(hào)進(jìn)程,只能看到各自 Mount Namespace
里掛載的目錄和文件,只能訪問(wèn)到各自 Network Namespace
里的網(wǎng)絡(luò)設(shè)備,就仿佛運(yùn)行在一個(gè)個(gè)“容器”里面,與世隔絕。
到此這篇關(guān)于從進(jìn)程中去理解 Docker隔離技術(shù)的文章就介紹到這了,更多相關(guān)Docker隔離技術(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解docker 容器不自動(dòng)退出結(jié)束運(yùn)行的方法
本文主要簡(jiǎn)單介紹 docker 容器與前置進(jìn)程的關(guān)系,以及如何編寫(xiě) Dockerfile/docker-compose.yml 優(yōu)雅的讓容器可以常駐運(yùn)行。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01在Docker中的ubuntu中安裝Python3和Pip的問(wèn)題
這篇文章主要介紹了在Docker中的ubuntu中安裝Python3和Pip的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02docker搭建redis主從哨兵集群的實(shí)現(xiàn)步驟
本文主要介紹了docker搭建redis主從哨兵集群的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07手把手教你實(shí)現(xiàn)給Docker開(kāi)啟IPv6網(wǎng)絡(luò)支持
這篇文章主要為大家介紹了Docker開(kāi)啟IPv6網(wǎng)絡(luò)支持實(shí)現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08一步步教你用docker部署postgreSQL數(shù)據(jù)庫(kù)
這篇文章主要給大家介紹了關(guān)于如何使用docker部署postgreSQL數(shù)據(jù)庫(kù)的相關(guān)資料,PostgreSQL是一款功能豐富的關(guān)系型數(shù)據(jù)庫(kù),類(lèi)似于MySQL,它也是受歡迎程度非常高的,需要的朋友可以參考下2023-11-11解決使用Docker Compose管理容器的問(wèn)題
在Docker Compose中,根據(jù)一個(gè)配置文件,將所有與應(yīng)用系統(tǒng)相關(guān)的應(yīng)用和對(duì)應(yīng)的容器進(jìn)行配置,再根據(jù)Docker Compose提供的命令進(jìn)行啟動(dòng),就可以解決上面說(shuō)的多容器之間的復(fù)雜問(wèn)題,感興趣的朋友跟隨小編一起看看吧2021-09-09iptables使用及docker的iptables規(guī)則
Docker在創(chuàng)建容器時(shí),會(huì)自動(dòng)添加一些iptables規(guī)則來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)隔離和轉(zhuǎn)發(fā)等功能,本文主要介紹了iptables使用及docker的iptables規(guī)則,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12關(guān)于Docker容器內(nèi)部無(wú)法解析域名問(wèn)題的解決
最近工作中遇到一個(gè)問(wèn)題,項(xiàng)目?jī)?nèi)部需要訪問(wèn)外網(wǎng),但上傳文件,但是一直報(bào)unknown host,無(wú)法解析域名,所以下面這篇文章主要給大家介紹了關(guān)于Docker容器內(nèi)部無(wú)法解析域名問(wèn)題的解決方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-07-07