摘要:先說(shuō)結(jié)論,容器真的很好,很輕量級(jí),功能又很重量級(jí)。是的版本完成以后,這個(gè)的文件系統(tǒng)就是一個(gè)標(biāo)準(zhǔn)的的文件系統(tǒng)了,里面的基本工具一應(yīng)俱全。
容器,目前最火的話題了,在后端的開(kāi)發(fā)中,容器的運(yùn)用也已經(jīng)是主流技術(shù)了,今天,我們就來(lái)說(shuō)說(shuō)容器技術(shù),之前我對(duì)這一塊的了解不是很多,但是最近有些特殊原因轉(zhuǎn)成運(yùn)維工程師了,而公司的全線服務(wù)都是docker的,以一個(gè)開(kāi)發(fā)人員的習(xí)慣,轉(zhuǎn)成運(yùn)維以后,還是想對(duì)這種東西總想深入了解一下,于是看了不少相關(guān)資料并且看了一下docker的源代碼,發(fā)現(xiàn)這東西確實(shí)很厲害,和之前腦中的docker印象完全不同,于是有了這篇文章。
先說(shuō)結(jié)論,容器真的很好,很輕量級(jí),功能又很重量級(jí)。
前言首先,雖然目前docker技術(shù)如此火爆,但是其實(shí)容器技術(shù)本上并不是什么高大上的東西,總的來(lái)講,就是對(duì)目前的Linux底層的幾個(gè)API的封裝,然后圍繞著這幾個(gè)API開(kāi)發(fā)出了一套周邊的環(huán)境。
之前所有的講關(guān)于容器的文章,一開(kāi)始就開(kāi)始講UTC隔離,PID隔離,IPC隔離,文件系統(tǒng)隔離,CGroups系統(tǒng),今天這一篇,我們換一個(gè)視角,我們從以下幾個(gè)方面來(lái)說(shuō)一下容器技術(shù)。
首先,我們從容器和虛擬機(jī)說(shuō)起,都說(shuō)容器是非常輕量級(jí)的,那么和虛擬機(jī)比起來(lái),到底輕在什么地方呢。
第二部分,我們會(huì)通過(guò)一步一步的說(shuō)明,通過(guò)構(gòu)造一個(gè)監(jiān)獄,來(lái)說(shuō)明如何建立一個(gè)簡(jiǎn)單的容器,會(huì)涉及到容器的各種技術(shù),當(dāng)然還有一些沒(méi)有涉及到的,我認(rèn)為不影響理解。
第三部分,我們會(huì)通過(guò)代碼實(shí)戰(zhàn)一把,看看如何一步一步按照第二部分的說(shuō)明啟動(dòng)一個(gè)容器并運(yùn)行自己的代碼,這一部分的全部代碼都在github上。
最后,我會(huì)再說(shuō)一下docker技術(shù),因?yàn)閐ocker從代碼來(lái)看,容器技術(shù)只是他的一小部分,完整的docker遠(yuǎn)比單純的容器要復(fù)雜,我會(huì)簡(jiǎn)單的說(shuō)一下我對(duì)docker的理解,包括docker使用的其他技術(shù)點(diǎn)。
容器和虛擬機(jī)要說(shuō)容器,跑不了和虛擬機(jī)進(jìn)行比較,虛擬機(jī)是比較古老的技術(shù)了,虛擬機(jī)的架構(gòu)圖如下所示。
虛擬機(jī)核心是什么?是模擬硬件,讓虛擬機(jī)的操作系統(tǒng)以為自己跑在一個(gè)真實(shí)的物理機(jī)器上,用軟件模擬出來(lái)CPU,內(nèi)存,硬盤(pán),網(wǎng)卡,讓虛擬機(jī)里面的操作系統(tǒng)覺(jué)得自己是在操作真實(shí)的硬件,所以虛擬機(jī)里面的CPU啊,內(nèi)存啊都是假的,都是軟件模擬出來(lái)的(現(xiàn)在有硬件虛擬化技術(shù)了,比純軟件模擬要高級(jí)一些,但操作系統(tǒng)不管這些),既然操作系統(tǒng)都騙過(guò)去了,當(dāng)然跑在操作系統(tǒng)上的進(jìn)程同樣也騙過(guò)去了唄,所以這些進(jìn)程都完全感知不到底層硬件的區(qū)別,還以為自己很歡樂(lè)的跑在一臺(tái)真實(shí)的物理機(jī)上了。
那么容器又是什么鬼呢?容器的架構(gòu)圖如下(這張圖網(wǎng)上找的,侵權(quán)馬上刪)
和虛擬機(jī)一個(gè)很明顯的區(qū)別就是容器其實(shí)并沒(méi)有模擬硬件,還是那個(gè)硬件,還是那個(gè)操作系統(tǒng),只不過(guò)是在操作系統(tǒng)上做了一點(diǎn)文章【這張圖中就是docker engine了】,讓進(jìn)程以為自己運(yùn)行在了一個(gè)全新的操作系統(tǒng)上,有一個(gè)很形象的詞來(lái)描述他就是軟禁!就是把進(jìn)程軟禁在一個(gè)環(huán)境中,讓進(jìn)程覺(jué)得自己很happy,其實(shí)一切盡在操作系統(tǒng)的掌控之中,其實(shí)虛擬機(jī)也是,虛擬機(jī)是把操作系統(tǒng)軟禁起來(lái)了,讓操作系統(tǒng)覺(jué)得很happy,容器是把進(jìn)程軟禁起來(lái),你看,一個(gè)是軟禁操作系統(tǒng),一個(gè)是軟禁進(jìn)程,這兩個(gè)明顯不是一個(gè)級(jí)別的東西,誰(shuí)輕誰(shuí)重不用說(shuō)了吧。
既然容器和虛擬機(jī)的區(qū)別在于一個(gè)是通過(guò)模擬硬件來(lái)軟禁操作系統(tǒng),一個(gè)是通過(guò)做做操作系統(tǒng)的手腳來(lái)軟禁進(jìn)程,那么他們能達(dá)到的效果也是不一樣的。
對(duì)于虛擬機(jī)來(lái)說(shuō),既然是模擬的硬件,那么就可以在windows上裝linux虛擬機(jī)了,因?yàn)榉凑悄M硬件嘛,虛擬機(jī)內(nèi)部的操作系統(tǒng)也不知道外面的宿主機(jī)是什么系統(tǒng)。
容器就不一樣了,因?yàn)槭窃诓僮飨到y(tǒng)上做文章,所以不可能在linux上裝windows了,并且還有一點(diǎn)就是,容器內(nèi)的操作系統(tǒng)其實(shí)就是外面宿主機(jī)的操作系統(tǒng),兩者其實(shí)是一個(gè),你在容器內(nèi)用uname -a看到的內(nèi)核版本和外面看到的是一樣的。本質(zhì)上容器就是一個(gè)進(jìn)程,他和宿主機(jī)上任何其他進(jìn)程沒(méi)什么本質(zhì)的區(qū)別。
建造容器監(jiān)獄如何來(lái)做一個(gè)容器呢?或者說(shuō)容器是怎么實(shí)現(xiàn)的呢?我們從幾個(gè)方面來(lái)說(shuō)一下容器的實(shí)現(xiàn),一是最小系統(tǒng),二是網(wǎng)絡(luò)系統(tǒng),三是進(jìn)程隔離技術(shù),四是資源分配。最小系統(tǒng)告訴你軟禁進(jìn)程所需要的那個(gè)舒適的監(jiān)獄環(huán)境,網(wǎng)絡(luò)系統(tǒng)告訴你軟禁的進(jìn)程如何和外界交互,進(jìn)程隔離技術(shù)告訴你如果把進(jìn)程關(guān)到這個(gè)舒適的監(jiān)獄中去,資源分配告訴你監(jiān)獄里的進(jìn)程如何給他分配資源讓他不能胡來(lái)。
建設(shè)基本監(jiān)獄【最小系統(tǒng)打造】要軟禁一個(gè)進(jìn)程,當(dāng)然需要有個(gè)監(jiān)獄了,在說(shuō)監(jiān)獄之前,我們先看看操作系統(tǒng)的結(jié)構(gòu),一個(gè)完整的操作系統(tǒng)【Linux/Unix操作系統(tǒng)】分成三部分,如下圖所示【本圖也是網(wǎng)上找的,侵權(quán)馬上刪,這個(gè)圖是四個(gè)部分,包括一個(gè)boot參數(shù)部分,這不是重點(diǎn)】。
首先是bootloader,這部分啟動(dòng)部分是匯編代碼,CPU從一個(gè)固定位置讀取第一行匯編代碼開(kāi)始運(yùn)行,bootloader先會(huì)初始化CPU,內(nèi)存,網(wǎng)卡(如果需要),然后這部分的主要作用是把操作系統(tǒng)的kernel代碼從硬盤(pán)加載到內(nèi)存中,然后bootloader使命完成了,跳轉(zhuǎn)到kernel的main函數(shù)入口開(kāi)始執(zhí)行kernel代碼,kernel就是我們熟悉的linux的內(nèi)核代碼了,大家說(shuō)的看內(nèi)核代碼就是看的這個(gè)部分了,kernel代碼啟動(dòng)以后,會(huì)重新初始化CPU,內(nèi)存,網(wǎng)卡等設(shè)備,然后開(kāi)始運(yùn)行內(nèi)核代碼,最后,啟動(dòng)上帝進(jìn)程(init),開(kāi)始正常運(yùn)行kernel,然后kernel會(huì)掛載文件系統(tǒng)。
好了,到這里,對(duì)進(jìn)程來(lái)說(shuō)都是無(wú)意義的,因?yàn)檫M(jìn)程不關(guān)心這些,進(jìn)程產(chǎn)生的時(shí)候這些工作已經(jīng)做完了,進(jìn)程能看到的就是這個(gè)文件系統(tǒng)了,對(duì)進(jìn)程來(lái)說(shuō),內(nèi)存空間,CPU核心數(shù),網(wǎng)絡(luò)資源,文件系統(tǒng)是他唯一能看得見(jiàn)使用得到的東西,所以我們的監(jiān)獄環(huán)境就是這么幾項(xiàng)核心的東西了。
kernel和文件系統(tǒng)是可以分離的,比如我們熟悉的ubuntu操作系統(tǒng),可能用的是3.18的Linux Kernel,再加上一個(gè)自己的文件系統(tǒng),也可以用2.6的Kernel加上同樣的操作系統(tǒng)。每個(gè)Linux的發(fā)行版都是這樣的,底層的Kernel可能都是同一個(gè),不同的只是文件系統(tǒng)不同,所以,可以簡(jiǎn)單的認(rèn)為,linux的各種發(fā)行版就是kernel內(nèi)核加上一個(gè)獨(dú)特的文件系統(tǒng),這個(gè)文件系統(tǒng)上有各種各樣的工具軟件。
既然是這樣,那么我們要軟禁一個(gè)進(jìn)程,最基礎(chǔ)的當(dāng)然要給他一個(gè)文件系統(tǒng)啦,文件系統(tǒng)簡(jiǎn)單的說(shuō)就是一堆文件夾加上一堆文件組成的,我們先來(lái)生成一個(gè)文件系統(tǒng),我之前是做嵌入式的,嵌入式的Linux系統(tǒng)生成文件系統(tǒng)一般用busybox,只需要在在ubuntu上執(zhí)行下面的命令,就能生成一個(gè)文件系統(tǒng)
apt-get install busybox-static
mkdir rootfs;cd rootfs
mkdir dev etc lib usr var proc tmp home root mnt sys
/bin/busybox --install -s bin
大概這么幾步就制作完成了一個(gè)文件系統(tǒng),也就是監(jiān)獄的基本環(huán)境已經(jīng)有了,記得把lib文件夾的內(nèi)容拷過(guò)去。制作完了以后,文件系統(tǒng)就這樣了。
還有一種方式,就是使用debootstap這個(gè)工具來(lái)做,也是幾行命令就做完了一個(gè)debian的文件系統(tǒng)了,里面連apt-get都有,docker的基礎(chǔ)文件系統(tǒng)也是這個(gè)。
apt-get install qemu-user-static debootstrap binfmt-support
mkdir rootfs
debootstrap --foreign wheezy rootfs //wheezy是debian的版本
cp /usr/bin/qemu-arm-static rootfs/usr/bin/
完成以后,這個(gè)wheezy的文件系統(tǒng)就是一個(gè)標(biāo)準(zhǔn)的debian的文件系統(tǒng)了,里面的基本工具一應(yīng)俱全。
OK,基本的監(jiān)獄環(huán)境已經(jīng)搭建好了,進(jìn)程住進(jìn)去以后就跟在外面一樣,啥都能干,但就是跑不出來(lái)。
要測(cè)試這個(gè)環(huán)境,可以使用linux的chroot命令,chroot ./rootfs就進(jìn)入了這個(gè)制作好的文件系統(tǒng)了,你可以試試,看不到外面的東西了哦。
打造探視系統(tǒng)【網(wǎng)絡(luò)系統(tǒng)】剛剛只建立了一個(gè)基本的監(jiān)獄環(huán)境,對(duì)于現(xiàn)代的監(jiān)獄,只有個(gè)房子不能上網(wǎng)怎么行?所以對(duì)于監(jiān)獄環(huán)境,還需要建立一個(gè)網(wǎng)絡(luò)環(huán)境,好讓里面的進(jìn)程們可以很方便的和監(jiān)獄外的親友們聯(lián)系啊,不然誰(shuí)愿意一個(gè)人呆在里面啊。
如何來(lái)建立一個(gè)網(wǎng)絡(luò)呢?對(duì)于容器而言,很多地方是可配置的,這里說(shuō)可配置,其實(shí)意思就是可配置也可以不配置,對(duì)于網(wǎng)絡(luò)就是這樣,一般的容器技術(shù),對(duì)網(wǎng)絡(luò)的支持有以下幾個(gè)方式。
無(wú)網(wǎng)絡(luò)模式,就是不配置模式了,不給他網(wǎng)絡(luò),只有文件系統(tǒng),適合單機(jī)版的程序。
直接和宿主機(jī)使用同一套網(wǎng)絡(luò),也是不配置模式,但是這個(gè)不配置是不進(jìn)行網(wǎng)絡(luò)隔離,直接使用宿主機(jī)的網(wǎng)卡,ip,協(xié)議棧,這是最奔放的模式,各個(gè)容器如果啟動(dòng)的是同一套程序,那么需要配置不同的端口了,比如有3個(gè)容器,都是redis程序,那么需要啟動(dòng)三個(gè)各不同的端口來(lái)提供服務(wù),這樣各個(gè)容器沒(méi)有做到完全的隔離,但是這也有個(gè)好處,就是網(wǎng)絡(luò)的吞吐量比較高,不用進(jìn)行轉(zhuǎn)發(fā)之類的操作。
網(wǎng)橋模式,也是docker默認(rèn)使用的模式,我們安裝完docker以后會(huì)多一個(gè)docker0的網(wǎng)卡,其實(shí)這是一個(gè)網(wǎng)橋,一個(gè)網(wǎng)橋有兩個(gè)端口,兩個(gè)端口是兩個(gè)不同的網(wǎng)絡(luò),可以對(duì)接兩塊網(wǎng)卡,從A網(wǎng)卡進(jìn)去的數(shù)據(jù)會(huì)從B網(wǎng)卡出來(lái),就像黑洞和白洞一樣,我們建立好網(wǎng)橋以后,在容器內(nèi)建一塊虛擬網(wǎng)卡,把他和網(wǎng)橋?qū)?,在容器外的宿主機(jī)上也建立一塊虛擬網(wǎng)卡,和網(wǎng)橋?qū)?,這樣容器里面的進(jìn)程就可以通過(guò)網(wǎng)橋這個(gè)探視系統(tǒng)和監(jiān)獄外聯(lián)系了。
我們可以直接使用第二種不配置模式,直接使用宿主機(jī)的網(wǎng)絡(luò),這也是最容易最方便的,但是我們?cè)谶@里說(shuō)的時(shí)候稍微說(shuō)一下第三種的網(wǎng)橋模式吧。
網(wǎng)橋最開(kāi)始的作用主要是用來(lái)連接兩個(gè)不同的局域網(wǎng)的,更具體的應(yīng)用,一般是用來(lái)連接兩個(gè)不同的mac層的局域網(wǎng)的,比如有線電視網(wǎng)和以太網(wǎng),一般網(wǎng)橋只做數(shù)據(jù)的過(guò)濾和轉(zhuǎn)發(fā),也可以適當(dāng)?shù)淖鲆恍┫蘖鞯墓ぷ?,沒(méi)有路由器那么復(fù)雜,實(shí)現(xiàn)起來(lái)也比較簡(jiǎn)單,對(duì)高層協(xié)議透明,他能操作的都是mac報(bào)文,也就是在ip層以下的報(bào)文。
對(duì)于容器而言,使用網(wǎng)橋的方式是在宿主機(jī)上使用brctl命令建立一個(gè)網(wǎng)橋,作為容器和外界交互的渠道,也就是大家使用docker的時(shí)候,用ifconfig命令看到的docker0網(wǎng)卡,這實(shí)際上就是一個(gè)網(wǎng)橋,然后每啟動(dòng)一個(gè)容器,就用brctl命令建立一對(duì)虛擬網(wǎng)卡,一塊給容器,一塊連到網(wǎng)橋上。這樣操作下來(lái),容器中發(fā)給虛擬網(wǎng)卡的數(shù)據(jù)都會(huì)發(fā)給網(wǎng)橋,而網(wǎng)橋是宿主機(jī)上的,是能連接外網(wǎng)的,所以這樣來(lái)做到了容器內(nèi)的進(jìn)程能訪問(wèn)外網(wǎng)。
容器的網(wǎng)絡(luò)我沒(méi)有深入研究,感覺(jué)不是特別復(fù)雜,最復(fù)雜的方式就是網(wǎng)橋的方式了,這些網(wǎng)絡(luò)配置都可以通過(guò)命令行來(lái)進(jìn)行,但是docker的源碼中是自己通過(guò)系統(tǒng)調(diào)用實(shí)現(xiàn)的,說(shuō)實(shí)話我沒(méi)怎么看明白,功力還是不夠啊。
我使用的就是最最簡(jiǎn)單的不隔離,和宿主機(jī)共用網(wǎng)卡,只能通過(guò)端口來(lái)區(qū)分不同容器中的服務(wù)。
好了,監(jiān)獄已經(jīng)建好了,探視系統(tǒng)也有了,得抓人了來(lái)軟禁了,把進(jìn)程抓進(jìn)來(lái)吧。我們以一個(gè)最最基本的進(jìn)程/bin/bash為例,把這個(gè)進(jìn)程抓進(jìn)監(jiān)獄吧。
說(shuō)到抓進(jìn)程,這時(shí)候就需要來(lái)聊聊容器的底層技術(shù)了,Linux提供幾項(xiàng)基礎(chǔ)技術(shù)來(lái)進(jìn)行輕量級(jí)的系統(tǒng)隔離,這些個(gè)隔離技術(shù)組成了我們熟悉的docker的基礎(chǔ)。本篇不會(huì)大段的描述這些技術(shù),文章后面我會(huì)給出一些參考鏈接,因?yàn)檫@類文章到處都可以找到,本篇只是讓大家對(duì)容器本身有個(gè)了解。
下面所說(shuō)的所有基礎(chǔ)技術(shù),其實(shí)就是一條系統(tǒng)調(diào)用,包括docker的基礎(chǔ)技術(shù),也是這么一條系統(tǒng)調(diào)用(當(dāng)然,docker還有很多其他的,但是就容器來(lái)說(shuō),這條是核心的了)
clone(進(jìn)程函數(shù), 進(jìn)程棧空間, CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET |CLONE_NEWUSER | CLONE_NEWIPC , NULL)
這是一條C語(yǔ)言的clone系統(tǒng)調(diào)用,實(shí)際上就是啟動(dòng)一個(gè)新的進(jìn)程,后面的參數(shù)就是各種隔離了,包括UTS隔離,PID隔離,文件系統(tǒng)隔離,網(wǎng)絡(luò)隔離,用戶隔離,IPC通訊隔離。
在go語(yǔ)言中,沒(méi)有clone這個(gè)系統(tǒng)調(diào)用(不知道為什么不做這個(gè)系統(tǒng)調(diào)用,可能是為了多平臺(tái)的兼容吧),必須使用exec.Cmd這個(gè)對(duì)象來(lái)啟動(dòng)進(jìn)程,在linux環(huán)境下,可以設(shè)置Cmd的attr屬性,其中有個(gè)屬性叫CloneFlags,可以把上面那些個(gè)隔離信息設(shè)置進(jìn)去,這樣,啟動(dòng)的進(jìn)程就是我們需要的了,我們可以這么來(lái)啟動(dòng)這個(gè)進(jìn)程
cmd := exec.Command("./container", args...) cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET, } cmd.Run()
這樣,通過(guò)這個(gè)cmd命令啟動(dòng)的./container進(jìn)程就是一個(gè)隔離進(jìn)程了,也就是我們把這個(gè)進(jìn)程給關(guān)起來(lái)了,他已經(jīng)看不到其他東西了,是不是很簡(jiǎn)單?但是你要是就直接這么運(yùn)行,還是看不到什么特別的地方。
在這個(gè)之后,我們需要按照上面所說(shuō)的,把監(jiān)獄先建立好,監(jiān)獄的建立在./container中進(jìn)行,建立監(jiān)獄也比較簡(jiǎn)單,基本上也是一堆系統(tǒng)調(diào)用,比如文件系統(tǒng)的軟禁,就像下面的一樣
syscall.Mount(rootfs, tmpMountPoint, "", syscall.MS_BIND, "") //掛載根文件系統(tǒng) syscall.Mount(rootfs+"/proc", tmpMountPoint+"/proc", "proc", 0, ""); //掛載proc文件夾 syscall.PivotRoot(tmpMountPoint, pivotDir) //把進(jìn)程軟禁到根文件系統(tǒng)中
關(guān)于上面proc文件夾,做了特殊處理,在Linux中,proc文件夾的地位比較特殊,具體作用可以自行查文檔,簡(jiǎn)單的說(shuō)就是保存系統(tǒng)信息的文件夾。在這里,dev和sys這兩個(gè)特殊文件夾也需要做特殊處理的,這里沒(méi)有寫(xiě)出來(lái)而已。
這些都做完了以后,就可以啟動(dòng)真正需要執(zhí)行的進(jìn)程了,比如/bin/bash,或者你自己的程序,這樣啟動(dòng)的/bin/bash或者你自己的程序就是在監(jiān)獄中啟動(dòng)的了,那么他看到的所有東西都是監(jiān)獄中的了,外面宿主機(jī)的一切對(duì)他來(lái)說(shuō)都是屏蔽的了,這樣,一個(gè)docker的雛形就產(chǎn)生了。
這里多說(shuō)一下,通過(guò)clone系統(tǒng)調(diào)用啟動(dòng)的進(jìn)程,它自己看到自己的PID是1,也就是上帝進(jìn)程了,這個(gè)上帝進(jìn)程可以來(lái)造基礎(chǔ)監(jiān)獄【文件系統(tǒng)】,打造放風(fēng)系統(tǒng)【網(wǎng)絡(luò)系統(tǒng)】,然后再通過(guò)它來(lái)生成新的進(jìn)程,這些進(jìn)程出來(lái)就在監(jiān)獄中了,我們使用docker的時(shí)候,自己的服務(wù)實(shí)際上就是這些個(gè)在監(jiān)獄中出生的進(jìn)程【可能我的描述不太正確啊,我沒(méi)有仔細(xì)看docker的源碼,我自己感覺(jué)是這樣的】。
至此,我們來(lái)總結(jié)一下,啟動(dòng)一個(gè)最簡(jiǎn)單的容器并運(yùn)行你自己的進(jìn)程,需要幾步。
建立一個(gè)監(jiān)獄【文件系統(tǒng)】,使用busybox或者debootstrap建立。
建立一個(gè)放風(fēng)系統(tǒng)【網(wǎng)絡(luò)系統(tǒng)】,使用網(wǎng)橋或者不隔離網(wǎng)絡(luò),直接使用宿主機(jī)的網(wǎng)卡。
抓一個(gè)皮卡丘【啟動(dòng)上帝進(jìn)程】并放到監(jiān)獄中【掛載文件系統(tǒng),初始化網(wǎng)絡(luò)】,配置Cloneflags的值,并通過(guò)exec.Cmd來(lái)進(jìn)行上帝進(jìn)程的啟動(dòng)
讓皮卡丘生個(gè)孩子【啟動(dòng)你自己的程序】,直接調(diào)用exec.Cmd的run方法啟動(dòng)你自己的進(jìn)程
完成
通過(guò)上面幾步,最簡(jiǎn)容器就完成了,是不是很簡(jiǎn)單?但是容器僅僅有這些是不夠的,我們還有三個(gè)隔離沒(méi)有講,這里稍微提一下吧。
一個(gè)是UTS隔離,主要是用來(lái)隔離hostname和域名的。
一個(gè)是User隔離,這樣容器里面的用戶和宿主機(jī)用戶可以做映射,意思就是里面雖然看到的是root用戶,但是實(shí)際上并不是root,不能夠瞎搞系統(tǒng),這樣容器的安全性會(huì)有保障。
一個(gè)是IPC隔離,這個(gè)是進(jìn)程通訊的隔離,這樣容器里面的進(jìn)程和容器外面的進(jìn)程就不能進(jìn)行進(jìn)程間通訊了,保證了比較強(qiáng)的隔離性。
給犯人分配食物【資源配置】我們知道,一般的監(jiān)獄中的食物是定量的,畢竟不是每個(gè)監(jiān)獄都可以吃自助餐的,容器也一樣,要是我們就啟個(gè)容器啥都不限制,里面要是有個(gè)牛逼的程序員寫(xiě)的牛逼程序,瞬間就把你的內(nèi)存和CPU給干沒(méi)了。比如像下面這個(gè)fork炸彈?!鞠旅娉绦蛘?qǐng)不要嘗試?。 ?/p>
int main(){ while(fork()); }
在容器技術(shù)中,Cgroups【control groups】就是干這個(gè)事情的,cgroups負(fù)責(zé)給監(jiān)獄設(shè)定資源,比如能用幾個(gè)cpu啊,cpu能給你多少百分比的使用量啊,內(nèi)存能用多少啊,磁盤(pán)能用多少啊,磁盤(pán)的速度能給你多少啊,各種資源都可以從cgroups來(lái)進(jìn)行配置,把這些東西配置給容器以后,就算容器里面運(yùn)行一個(gè)fork炸彈也不怕了,反正影響不到外面的宿主機(jī),到這里,容器已經(jīng)越來(lái)越像虛擬機(jī)了。
cgroups是linux內(nèi)核提供的API,雖然是API,但它的整個(gè)實(shí)現(xiàn)完美滿足了Linux兩大設(shè)計(jì)哲學(xué)之一:一切皆文件(還有一個(gè)哲學(xué)是通訊全管道),對(duì)API的調(diào)用實(shí)際上是操作文件。
我們以cpu的核心數(shù)看看如何來(lái)做一個(gè)cgroups的資源管理。假設(shè)我們的物理機(jī)是個(gè)8核的CPU,而我們剛剛啟動(dòng)的容器我只想讓他使用其中的兩個(gè)核,很簡(jiǎn)單,我們用命令行直接操作sys/fs/cgroups文件夾下的文件來(lái)進(jìn)行。這個(gè)配置我們可以在啟動(dòng)的上帝進(jìn)程中進(jìn)行,也可以在容器外部進(jìn)行,都是直接操作文件。
關(guān)于cgroups這個(gè)東西很復(fù)雜也很強(qiáng)大,其實(shí)在容器出來(lái)之前,好的運(yùn)維工程師就已經(jīng)把這個(gè)玩得很溜了。docker也只是把這些個(gè)文件操作封裝了一下,變成了docker的啟動(dòng)和配置參數(shù)而已。
親自抓一次進(jìn)程吧好了,該說(shuō)的都說(shuō)了,我們來(lái)實(shí)戰(zhàn)一把,自己?jiǎn)⒁粋€(gè)容器吧,并且啟動(dòng)以后為了更直觀的看到效果,我們啟動(dòng)一個(gè)ssh服務(wù),打開(kāi)22332端口,然后外面就可以通過(guò)ssh連到容器內(nèi)部了,這時(shí)候你愛(ài)干什么干什么了。
制作文件系統(tǒng)文件系統(tǒng)制作我們直接使用debootstrap進(jìn)行制作,在/root/目錄下建立一個(gè)rootfs的文件夾,然后使用debootstrap --foreign wheezy rootfs 制作文件系統(tǒng),制作完了以后,文件系統(tǒng)就是下面這個(gè)樣子
制作初始化腳本初始化腳本就做兩件事情,一是啟動(dòng)ssh服務(wù),一是啟動(dòng)一個(gè)shell,提前先把/etc/ssh/sshd_config中的端口改成23322。
#!/bin/bash service ssh start /bin/bash
然后把這個(gè)腳本放到制作的文件系統(tǒng)的root目錄下,加上執(zhí)行權(quán)限。
啟動(dòng)上帝進(jìn)程文件系統(tǒng)制作完成了,啟動(dòng)腳本也做完了,我們看看我們這個(gè)容器的架構(gòu),架構(gòu)很簡(jiǎn)單,整個(gè)容器分為兩個(gè)獨(dú)立的進(jìn)程,兩份獨(dú)立的代碼。
一個(gè)是主進(jìn)程【wocker.go】,這個(gè)進(jìn)程本身就是一個(gè)http的服務(wù),通過(guò)get方法接收參數(shù),參數(shù)有rootfs的地址,容器的hostname,需要監(jiān)禁的進(jìn)程名稱(這里就是我們的第二個(gè)進(jìn)程【startContainer.go】),然后通過(guò)exec.Cmd這個(gè)包啟動(dòng)這個(gè)進(jìn)程。
第二個(gè)進(jìn)程啟動(dòng)就是以隔離方式啟動(dòng)的了,就是容器的上帝進(jìn)程了,這個(gè)進(jìn)程中進(jìn)行文件系統(tǒng)掛載,hostname設(shè)置,權(quán)限系統(tǒng)的設(shè)定,然后啟動(dòng)正式的服務(wù)進(jìn)程(也就是我們的啟動(dòng)腳本/root/start_container.sh)
掛載文件系統(tǒng)第二個(gè)進(jìn)程是容器的上帝進(jìn)程,在這里進(jìn)行文件系統(tǒng)的掛載,最重要的代碼如下
syscall.Mount(rootfs, tmpMountPoint, "", syscall.MS_BIND, "") //掛載根文件系統(tǒng) syscall.Mount(procpath, tmpMountPointProc, "proc", 0, "") //掛載proc文件夾,用來(lái)看系統(tǒng)信息的 syscall.Mount(syspath, tmpMountPointSys, "sysfs", 0, "") //掛載sys文件夾,用來(lái)做權(quán)限控制的 syscall.Mount("udev", tmpMountPointDev, "devtmpfs", 0, "") //掛載dev,用來(lái)使用設(shè)備的 syscall.PivotRoot(tmpMountPoint, pivotDir)//進(jìn)入到文件系統(tǒng)中
具體代碼可以看github上的文件,這樣,根文件系統(tǒng)就掛載完了,已經(jīng)進(jìn)入了基本監(jiān)獄中了。
啟動(dòng)初始化腳本文件系統(tǒng)掛載完了以后,然后啟動(dòng)初始化腳本,這個(gè)就比較簡(jiǎn)單了,一個(gè)exec.Cmd的Run方法調(diào)用就搞定了。
cmd := exec.Command("/root/start_container.sh")
這樣,ssh服務(wù)就在容器中啟動(dòng)了,可以看到一行Starting OpenBSD Secure Shell server: sshd.的打印信息,容器啟動(dòng)完成,這時(shí)候,我們可以通過(guò)ssh root@127.0.0.1 -p 23322這個(gè)命令登錄進(jìn)我們的容器了,然后你就可以為所欲為了。
上面那個(gè)圖,我們看到登錄進(jìn)來(lái)以后,hostname已經(jīng)顯示為我們?cè)O(shè)定的hello了,這時(shí)這個(gè)會(huì)話已經(jīng)在容器里面了,我們ps一下看看進(jìn)程們。
看到pid為1的進(jìn)程了么,那個(gè)就是啟動(dòng)這個(gè)容器的上帝進(jìn)程了。恩,到這里,我們已經(jīng)在容器中了,這里啟動(dòng)的任何東西都和我們知道的docker中的進(jìn)程沒(méi)什么太大區(qū)別了。
但在這里,我缺失了權(quán)限的部分,大家可以自己加上去,主要是各種文件操作比較麻煩。。。
關(guān)于Docker的思考docker這門(mén)最近兩年非?;鸬募夹g(shù),光從容器的角度來(lái)看的話,也不算什么新的牛逼技術(shù)了,和虛擬機(jī)比起來(lái)還是要簡(jiǎn)單不少,當(dāng)然,docker本身可完全不止容器技術(shù)本身,還有AUFS文件分層技術(shù),還有etcd集群技術(shù),最關(guān)鍵的是docker通過(guò)自己的整個(gè)生態(tài)把容器包裹在里面了,提供了一整套的容器管理套件,這樣讓容器的使用變得異常簡(jiǎn)單,所以docker才能這么流行吧。
和虛擬機(jī)比起來(lái),docker的優(yōu)點(diǎn)實(shí)在是太多了。
首先,從易用性的角度來(lái)說(shuō),管理一個(gè)虛擬機(jī)的集群,有一整套軟件系統(tǒng),比如openstack這種,光熟悉這個(gè)openstack就夠喝一壺的了,而且openstack的網(wǎng)絡(luò)管理異常復(fù)雜,哦,不對(duì),是{{BANNED}}級(jí)的復(fù)雜,要把網(wǎng)絡(luò)調(diào)通不是那么容易的事情。
第二,從性能上來(lái)看看,我們剛剛說(shuō)了容器的原理,所以實(shí)際上容器不管是對(duì)CPU的利用,還是內(nèi)存的操作或者外部設(shè)備的操作,對(duì)一切硬件的操作實(shí)際上都是直接操作的,并沒(méi)有經(jīng)過(guò)一個(gè)中間層進(jìn)行過(guò)度,但是虛擬機(jī)就不一樣了,虛擬機(jī)是先操作假的硬件,然后假硬件再操作真硬件,利用率從理論上就會(huì)比容器的要差,雖然現(xiàn)在有硬件虛擬化的技術(shù)了能提升一部分性能,但從理論上來(lái)說(shuō)性能還是沒(méi)有容器好,這部分我沒(méi)有實(shí)際測(cè)試過(guò)啊,只是從理論上這么覺(jué)得的,如果有不對(duì)的歡迎拍磚啊。
第三,從部署的易用性上和啟動(dòng)時(shí)間上,容器就完全可以秒了虛擬機(jī)了,這個(gè)不用多說(shuō)吧,一個(gè)是啟動(dòng)一臺(tái)假電腦,一個(gè)是啟動(dòng)一個(gè)進(jìn)程。
那么,docker和虛擬機(jī)比起來(lái),缺點(diǎn)在哪里呢?
我自己想了半天,除了資源隔離性沒(méi)有虛擬機(jī)好以外,我實(shí)在是想不出還有什么缺點(diǎn),因?yàn)?b>cgroups的隔離技術(shù)只能設(shè)定一個(gè)上限,比如在一臺(tái)4核4G的機(jī)器上,你可能啟動(dòng)兩個(gè)docker,給他們的資源都是4核4G,如果有個(gè)docker跑偏了,一個(gè)人就干掉了4G內(nèi)存,那么另外一個(gè)docker可能申請(qǐng)不到資源了。而虛擬機(jī)就不存在這個(gè)問(wèn)題,但是這也是個(gè)雙刃劍,docker的這種做法可以更多的榨干系統(tǒng)資源,而虛擬機(jī)的做法很可能在浪費(fèi)系統(tǒng)資源。
除了這個(gè),我實(shí)在是想不出還有其他缺點(diǎn)。網(wǎng)上也有說(shuō)權(quán)限管理沒(méi)有虛擬機(jī)好,但我覺(jué)得權(quán)限這東西,還是得靠人,靠軟件永遠(yuǎn)靠不住。
最后,代碼都在github上,只有非常非常簡(jiǎn)單的三個(gè)文件【一個(gè)Container.go是容器類,一個(gè)wocker.go沒(méi)內(nèi)容,一個(gè)startContainer.go啟動(dòng)容器】,那個(gè)http服務(wù)留著沒(méi)寫(xiě),后面寫(xiě)http服務(wù)的時(shí)候在用一下。
恩,docker確實(shí)是個(gè)好東西。
如果你覺(jué)得不錯(cuò),歡迎轉(zhuǎn)發(fā)給更多人看到,也歡迎關(guān)注我的公眾號(hào),主要聊聊搜索,推薦,廣告技術(shù),還有瞎扯。。文章會(huì)在這里首先發(fā)出來(lái):)掃描或者搜索微信號(hào)XJJ267或者搜索西加加語(yǔ)言就行
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/26669.html
摘要:此刻的后手指依舊飛速地敲打鍵盤(pán),絲毫沒(méi)有要停不下來(lái)意思。閱讀本期技術(shù)周刊,你不光能弄明白什么是,使用的意義何在,還將被傳授秘籍,以達(dá)的境界。周刊篩選的每篇內(nèi)容,是作者的獨(dú)到見(jiàn)解,踩坑總結(jié)和經(jīng)驗(yàn)分享。 showImg(https://segmentfault.com/img/bVC5qJ?w=900&h=385); 啪嗒啪嗒,啪嗒啪嗒,聽(tīng)到后排動(dòng)感十足的清脆鍵盤(pán)響,我就能猜到公司程序員定...
摘要:這個(gè)實(shí)際上給出了通過(guò)集合代數(shù)發(fā)展出來(lái)的關(guān)系型數(shù)據(jù)庫(kù)怎么進(jìn)行數(shù)據(jù)操作和檢索的。 本篇趟個(gè)雷,把數(shù)據(jù)庫(kù)納入到輪子中了,前面說(shuō)到了數(shù)據(jù)庫(kù)其實(shí)不算輪子,也說(shuō)到了其實(shí)我寫(xiě)不出來(lái)數(shù)據(jù)庫(kù),這里所說(shuō)的數(shù)據(jù)庫(kù)嚴(yán)格來(lái)說(shuō)是關(guān)系型數(shù)據(jù)庫(kù),他比輪子復(fù)雜多了,是一個(gè)和操作系統(tǒng)差不多復(fù)雜度的東西,所以才能通過(guò)一個(gè)oralce養(yǎng)活一家全球50強(qiáng)的公司,其次,數(shù)據(jù)庫(kù)太復(fù)雜了,要寫(xiě)出來(lái)實(shí)在是力所不能及,但是后來(lái)有想了一下...
摘要:前端技術(shù)棧還是非常龐大的,為了能夠借助已經(jīng)存在的輪子來(lái)造出一輛車(chē),所以我選擇了進(jìn)行實(shí)踐。狀態(tài)的管理的狀態(tài)管理依靠完成,用其來(lái)管理的所有組件狀態(tài)。私有云客戶端打造主頁(yè)面首先是主頁(yè)面,可以打開(kāi)任何一個(gè)云主機(jī)系統(tǒng)的頁(yè)面看,基本類似。 showImg(https://segmentfault.com/img/remote/1460000013930354); 【利用K8S技術(shù)棧打造個(gè)人私有...
摘要:前端技術(shù)棧還是非常龐大的,為了能夠借助已經(jīng)存在的輪子來(lái)造出一輛車(chē),所以我選擇了進(jìn)行實(shí)踐。狀態(tài)的管理的狀態(tài)管理依靠完成,用其來(lái)管理的所有組件狀態(tài)。私有云客戶端打造主頁(yè)面首先是主頁(yè)面,可以打開(kāi)任何一個(gè)云主機(jī)系統(tǒng)的頁(yè)面看,基本類似。 showImg(https://segmentfault.com/img/remote/1460000013930354); 【利用K8S技術(shù)棧打造個(gè)人私有...
閱讀 1807·2023-04-26 02:14
閱讀 3729·2021-11-23 09:51
閱讀 1387·2021-10-13 09:39
閱讀 3976·2021-09-24 10:36
閱讀 3016·2021-09-22 15:55
閱讀 3524·2019-08-30 12:57
閱讀 2041·2019-08-29 15:30
閱讀 1988·2019-08-29 13:19