自己動(dòng)手寫docker筆記(1)Linux NameSpace

書中提到,docker最核心的就是通過Linux NameSpace,Cgroups以及Union FS構(gòu)造的。這里首先記錄一下NameSpace钩杰。

Linux NameSpace

Linux Namespace 是kernel 的一個(gè)功能,它可以隔離一系列系統(tǒng)的資源,比如PID(Process ID)羽历,User ID, Network等等。
類似與chroot允許把當(dāng)前目錄變成根目錄一樣(被隔離開來的)淡喜,Namesapce也可以在一些資源上秕磷,將進(jìn)程隔離起來,這些資源包括進(jìn)程樹炼团,網(wǎng)絡(luò)接口澎嚣,掛載點(diǎn)等等。
書中提到一個(gè)生動(dòng)的例子瘟芝。一家公司向外界出售自己的計(jì)算資源易桃,公司有一臺(tái)性能還不錯(cuò)的服務(wù)器,每個(gè)用戶買到一個(gè)tomcat實(shí)例用來運(yùn)行它們自己的應(yīng)用锌俱。有些調(diào)皮的客戶可能不小心進(jìn)入了別人的tomcat實(shí)例晤郑,修改或者關(guān)閉了其中的某些資源,這樣就會(huì)導(dǎo)致各個(gè)客戶之間互相干擾贸宏。
為此造寝,使用Namespace,
我們就可以做到UID級(jí)別的隔離吭练,也就是說诫龙,我們可以以UID為n的用戶,虛擬化出來一個(gè)namespace线脚,在這個(gè)namespace里面赐稽,用戶是具有root權(quán)限的。但是在真實(shí)的物理機(jī)器上浑侥,
他還是那個(gè)UID為n的用戶姊舵。這只是NameSpace的一個(gè)子功能。

除了User Namespace ,PID也是可以被虛擬的寓落。命名空間建立系統(tǒng)的不同視圖括丁, 對(duì)于每一個(gè)命名空間,從用戶看起來伶选,應(yīng)該像一臺(tái)單獨(dú)的Linux計(jì)算機(jī)一樣史飞,有自己的init進(jìn)程(PID為1)尖昏,
其他進(jìn)程的PID依次遞增,A和B空間都有PID為1的init進(jìn)程构资,子容器的進(jìn)程映射到父容器的進(jìn)程上抽诉,父容器可以知道每一個(gè)子容器的運(yùn)行狀態(tài),而子容器與子容器之間是隔離的吐绵。從圖中我們可以看到迹淌,進(jìn)程3在父命名空間里面PID 為3,但是在子命名空間內(nèi)己单,他就是1.也就是說用戶從子命名空間 A 內(nèi)看進(jìn)程3就像 init 進(jìn)程一樣唉窃,以為這個(gè)進(jìn)程是自己的初始化進(jìn)程,但是從整個(gè) host 來看纹笼,他其實(shí)只是3號(hào)進(jìn)程虛擬化出來的一個(gè)空間而已纹份。

當(dāng)前Linux一共實(shí)現(xiàn)六種不同類型的namespace。

Namespace類型 系統(tǒng)調(diào)用參數(shù) 內(nèi)核版本
Mount namespaces CLONE_NEWNS 2.4.19
UTS namespaces CLONE_NEWUTS 2.6.19
IPC namespaces CLONE_NEWIPC 2.6.19
PID namespaces CLONE_NEWPID 2.6.24
Network namespaces CLONE_NEWNET 2.6.29
User namespaces CLONE_NEWUSER 3.8

Namesapce 的API主要使用三個(gè)系統(tǒng)調(diào)用

  • clone() - 創(chuàng)建新進(jìn)程廷痘。根據(jù)系統(tǒng)調(diào)用參數(shù)來判斷哪種類型的namespace被創(chuàng)建蔓涧,而且它們的子進(jìn)程也會(huì)被包含到namespace中
  • unshare() - 將進(jìn)程移出某個(gè)namespace
  • setns() - 將進(jìn)程加入到namespacef中

UTS NameSpace

下面我們將使用Go來做一個(gè)UTS Namespace 的例子。

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

func main() {
    cmd := exec.Command("sh")//指定被fork()出來的新進(jìn)程內(nèi)的初始化進(jìn)程
    cmd.SysProcAttr = &syscall.SysProcAttr{
        //已經(jīng)封裝clone,直接進(jìn)行調(diào)用就好
        Cloneflags: syscall.CLONE_NEWUTS,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
}
Result1
Result2
Result3

IPC NameSpace

IPC Namespace 是用來隔離 System V IPC 和POSIX message queues.每一個(gè)IPC Namespace都有他們自己的System V IPC 和POSIX message queue牍疏。


添加代碼

可以看到我們僅僅增加syscall.CLONE_NEWIPC代表我們希望創(chuàng)建IPC Namespace蠢笋。

package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)
func main()  {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS|
            syscall.CLONE_NEWIPC,
    }
    cmd.Stdin=os.Stdin
    cmd.Stdout=os.Stdout
    cmd.Stderr=os.Stderr

    if err :=cmd.Run(); err!=nil{
        log.Fatal(err)
    }
}

下面我們需要打開兩個(gè)shell 來演示隔離的效果。

查看現(xiàn)有的ipc Message Queues

root@taroballs-PC:/home/taroballs/GoglandProjects/awesomeDockerProject/IPC# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

下面我們創(chuàng)建一個(gè)message queue 然后再查看一下

root@taroballs-PC:/home/taroballs/GoglandProjects/awesomeDockerProject/IPC# ipcmk -Q
Message queue id: 0
root@taroballs-PC:/home/taroballs/GoglandProjects/awesomeDockerProject/IPC# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0xb06f1d25 0          root       644        0            0           

發(fā)現(xiàn)是可以看到一個(gè)queue了鳞陨。下面我們使用另外一個(gè)shell去運(yùn)行我們的程序。


成功隔離

通過這里我們可以發(fā)現(xiàn)瞻惋,在新創(chuàng)建的Namespace里面厦滤,我們看不到宿主機(jī)上已經(jīng)創(chuàng)建的message queue,說明我們的 IPC Namespace 創(chuàng)建成功歼狼,IPC 已經(jīng)被隔離掏导。

PID NameSpacce

PID namespace是用來隔離進(jìn)程 id。同樣的一個(gè)進(jìn)程在不同的 PID Namespace 里面可以擁有不同的 PID羽峰。

可以這樣理解趟咆,在 docker container 里面,我們使用ps -ef 發(fā)現(xiàn)梅屉,容器內(nèi)在前臺(tái)跑著的那個(gè)init進(jìn)程的 PID 是1值纱,但是我們?cè)谌萜魍猓褂?code>ps -ef會(huì)發(fā)現(xiàn)同樣的進(jìn)程卻有不同的 PID坯汤,這就是PID namespace 干的事情虐唠。
同上,我們添加了一個(gè)syscall.CLONE_NEWPID

package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main()  {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags : syscall.CLONE_NEWUTS|syscall.CLONE_NEWIPC|
            syscall.CLONE_NEWPID,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
}

可以看到惰聂,我們打印了當(dāng)前namespace的pid疆偿,發(fā)現(xiàn)是1咱筛,也就是說。這個(gè)20190 PID 被映射到 namesapce 里面的 PID 為1.這里還不能使用ps 來查看杆故,因?yàn)閜s 和 top 等命令會(huì)使用/proc內(nèi)容

Mount NameSpace

上一小節(jié)講到迅箩,暫時(shí)不能使用top和ps查看,因?yàn)槠鋾?huì)使用/proc內(nèi)容

  • 何為/proc文件呢

Linux系統(tǒng)上的/proc目錄是一種文件系統(tǒng)处铛,即proc文件系統(tǒng)沙热。與其它常見的文件系統(tǒng)不同的是,/proc是一種偽文件系統(tǒng)(也即虛擬文件系統(tǒng))罢缸,存儲(chǔ)的是當(dāng)前內(nèi)核運(yùn)行狀態(tài)的一系列特殊文件篙贸,用戶可以通過這些文件查看有關(guān)系統(tǒng)硬件及當(dāng)前正在運(yùn)行進(jìn)程的信息,甚至可以通過更改其中某些文件來改變內(nèi)核的運(yùn)行狀態(tài)枫疆。
基于/proc文件系統(tǒng)如上所述的特殊性爵川,其內(nèi)的文件也常被稱作虛擬文件

所謂Mount NameSpace隔離,是用來隔離各個(gè)進(jìn)程看到的掛載點(diǎn)視圖息楔。
在不同namespace中的進(jìn)程看到的文件系統(tǒng)層次是不一樣的寝贡。在mount namespace 中調(diào)用mount()umount()僅僅只會(huì)影響當(dāng)前namespace內(nèi)的文件系統(tǒng),而對(duì)全局的文件系統(tǒng)是沒有影響的值依。
看到這里圃泡,也許就會(huì)想到chroot()。它也是將某一個(gè)子目錄變成根節(jié)點(diǎn)愿险。但是mount namespace不僅能實(shí)現(xiàn)這個(gè)功能颇蜡,而且能以更加靈活和安全的方式實(shí)現(xiàn)。

我們針對(duì)上面的代碼做了一點(diǎn)改動(dòng)辆亏,代碼中增加了NEWNS 標(biāo)識(shí)风秤。


package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main()  {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS|syscall.CLONE_NEWIPC|
            syscall.CLONE_NEWPID|syscall.CLONE_NEWNS,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run();err!=nil{
        log.Fatal(err)
    }
}

首先我們運(yùn)行代碼后,查看一下/proc的文件內(nèi)容扮叨。proc 是一個(gè)文件系統(tǒng)缤弦,它提供額外的機(jī)制可以從內(nèi)核和內(nèi)核模塊將信息發(fā)送給進(jìn)程。


下面我們將/proc mount到我們自己的namesapce下面來彻磁。


可以看到碍沐,在當(dāng)前namesapce里面,我們的sh 進(jìn)程是PID 為1 的進(jìn)程衷蜓。這里就說明累提,我們當(dāng)前的Mount namesapce 里面的mount 和外部空間是隔離的,mount 操作并沒有影響到外部恍箭。Docker volume 也是利用了這個(gè)特性刻恭。

User NameSpace

User namespace 主要是隔離用戶的用戶組ID。也就是說,一個(gè)進(jìn)程的User ID 和Group ID 在User namespace 內(nèi)外可以是不同的鳍贾。
比較常用的是鞍匾,在宿主機(jī)上以一個(gè)非root用戶運(yùn)行創(chuàng)建一個(gè)User namespace,然后在User namespace里面卻映射成root 用戶骑科。
這個(gè)進(jìn)程在User namespace里面有root權(quán)限橡淑,但是在User namespace外面卻沒有root的權(quán)限。

從Linux kernel 3.8開始咆爽,非root進(jìn)程也可以創(chuàng)建User namespace ,并且此進(jìn)程在namespace里面可以被映射成 root并且在 namespace內(nèi)有root權(quán)限梁棠。
繼續(xù)改動(dòng)了我們的代碼


package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
            syscall.CLONE_NEWUSER,
    }
    //cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1), Gid: uint32(1)}
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
    os.Exit(-1)
}

在原來的基礎(chǔ)上增加了syscall.CLONE_NEWUSER。首先我們以root來運(yùn)行這個(gè)程序斗埂,運(yùn)行前在宿主機(jī)上我們看一下當(dāng)前用戶和用戶組符糊。

可以看到。它們的UID是不同的呛凶,亦即User NameSpace生效了.

NetWork NameSpace

Network namespace 是用來隔離網(wǎng)絡(luò)設(shè)備男娄,IP地址端口等網(wǎng)絡(luò)棧的namespace。Network namespace 可以讓每個(gè)容器擁有自己獨(dú)立的網(wǎng)絡(luò)設(shè)備(虛擬的)漾稀,而且容器內(nèi)的應(yīng)用可以綁定到自己的端口模闲,每個(gè) namesapce 內(nèi)的端口都不會(huì)互相沖突。在宿主機(jī)上搭建網(wǎng)橋后崭捍,就能很方便的實(shí)現(xiàn)容器之間的通信尸折,而且每個(gè)容器內(nèi)的應(yīng)用都可以使用相同的端口。

同樣殷蛇,我們?cè)谠瓉淼拇a上增加一點(diǎn)实夹。我們?cè)黾恿?code>syscall.CLONE_NEWNET 這里標(biāo)識(shí)符。

package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
            syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
    }
    //cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1), Gid: uint32(1)}
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
    os.Exit(-1)
}

首先我們?cè)谒拗鳈C(jī)上查看一下自己的網(wǎng)絡(luò)設(shè)備晾咪。


可以看到宿主機(jī)上有l(wèi)o, eth0, eth1 等網(wǎng)絡(luò)設(shè)備收擦,而在Namespace 里面什么網(wǎng)絡(luò)設(shè)備都沒有。這樣就能展現(xiàn) Network namespace 與宿主機(jī)之間的網(wǎng)絡(luò)隔離谍倦。

可以發(fā)現(xiàn),實(shí)現(xiàn)LinuxNameSpace隔離就是如此簡(jiǎn)單泪勒。如有勘誤昼蛀,歡迎斧正~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市圆存,隨后出現(xiàn)的幾起案子叼旋,更是在濱河造成了極大的恐慌,老刑警劉巖沦辙,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夫植,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)详民,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門延欠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沈跨,你說我怎么就攤上這事由捎。” “怎么了饿凛?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵狞玛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我涧窒,道長(zhǎng)心肪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任纠吴,我火速辦了婚禮硬鞍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呜象。我一直安慰自己膳凝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布恭陡。 她就那樣靜靜地躺著蹬音,像睡著了一般。 火紅的嫁衣襯著肌膚如雪休玩。 梳的紋絲不亂的頭發(fā)上著淆,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音拴疤,去河邊找鬼永部。 笑死,一個(gè)胖子當(dāng)著我的面吹牛呐矾,可吹牛的內(nèi)容都是我干的苔埋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蜒犯,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼组橄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起罚随,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤玉工,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后淘菩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遵班,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狭郑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腹暖。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖愿阐,靈堂內(nèi)的尸體忽然破棺而出微服,到底是詐尸還是另有隱情,我是刑警寧澤缨历,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布以蕴,位于F島的核電站,受9級(jí)特大地震影響辛孵,放射性物質(zhì)發(fā)生泄漏丛肮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一魄缚、第九天 我趴在偏房一處隱蔽的房頂上張望宝与。 院中可真熱鬧,春花似錦冶匹、人聲如沸习劫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诽里。三九已至,卻和暖如春飞蛹,著一層夾襖步出監(jiān)牢的瞬間谤狡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工卧檐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留墓懂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓霉囚,卻偏偏與公主長(zhǎng)得像捕仔,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盈罐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345