Docker原理之Namespaces
命名空間(namespaces)是 Linux 為我們提供的用于分離進(jìn)程樹、網(wǎng)絡(luò)接口、掛載點(diǎn)以及進(jìn)程間通信等資源的方法。
一、Namespaces
在日常使用 Linux 或者 macOS 時(shí),我們并沒有運(yùn)行多個(gè)完全分離的服務(wù)器的需要,但是如果我們?cè)诜?wù)器上啟動(dòng)了多個(gè)服務(wù),這些服務(wù)其實(shí)會(huì)相互影響的,每一個(gè)服務(wù)都能看到其他服務(wù)的進(jìn)程,也可以訪問宿主機(jī)器上的任意文件,這是很多時(shí)候我們都不愿意看到的,我們更希望運(yùn)行在同一臺(tái)機(jī)器上的不同服務(wù)能做到完全隔離,就像運(yùn)行在多臺(tái)不同的機(jī)器上一樣。
在這種情況下,一旦服務(wù)器上的某一個(gè)服務(wù)被入侵,那么入侵者就能夠訪問當(dāng)前機(jī)器上的所有服務(wù)和文件,這也是我們不想看到的,而 Docker 其實(shí)就通過 Linux 的 Namespaces 對(duì)不同的容器實(shí)現(xiàn)了隔離。
Linux 的命名空間機(jī)制提供了以下六種不同的命名空間,包括 pid 命名空間、net 命名空間、ip c命名空間、m n t命名空間、UTS 命名空間、user 命名空間,通過這六個(gè)選項(xiàng)我們能在創(chuàng)建新的進(jìn)程時(shí)設(shè)置新進(jìn)程應(yīng)該在哪些資源上與宿主機(jī)器進(jìn)行隔離。
| UTS | 主機(jī)名與域名 | CLONE_NEWUTS |
| IPC | 信號(hào)量、消息隊(duì)列和共享內(nèi)容 | CLONE_NEWIPC |
| PID | 進(jìn)程編號(hào) | CLONE_NEWPID |
| Network | 網(wǎng)絡(luò)設(shè)備、網(wǎng)絡(luò)棧、端口 | CLONE_NEWNTE |
| Mount | 文件系統(tǒng) | CLONE_NEWNS |
| User | 用戶和用戶組 | CLONE_NEWUSER |
1、pid 命名空間(進(jìn)程ID)
不同用戶的進(jìn)程就是通過 pid 命名空間隔離開的,且不同命名空間中可以有相同 pid。所有的 LXC 進(jìn)程在 Docker 中的父進(jìn)程為Docker進(jìn)程,每個(gè) LXC 進(jìn)程具有不同的命名空間。同時(shí)由于允許嵌套,因此可以很方便的實(shí)現(xiàn)嵌套的 Docker 容器。
2、net 命名空間(網(wǎng)絡(luò))
有了 pid 命名空間, 每個(gè)命名空間中的 pid 能夠相互隔離,但是網(wǎng)絡(luò)端口還是共享 host 的端口。網(wǎng)絡(luò)隔離是通過 net 命名空間實(shí)現(xiàn)的, 每個(gè) net 命名空間有獨(dú)立的 網(wǎng)絡(luò)設(shè)備, IP 地址, 路由表, /proc/net 目錄。這樣每個(gè)容器的網(wǎng)絡(luò)就能隔離開來。Docker 默認(rèn)采用 veth 的方式,將容器中的虛擬網(wǎng)卡同 host 上的一 個(gè)Docker 網(wǎng)橋 docker0 連接在一起。
3、ipc 命名空間(進(jìn)程間通信)
容器中進(jìn)程交互還是采用了 Linux 常見的進(jìn)程間交互方法(interprocess communication - IPC), 包括信號(hào)量、消息隊(duì)列和共享內(nèi)存等。然而同 VM 不同的是,容器的進(jìn)程間交互實(shí)際上還是 host 上具有相同 pid 命名空間中的進(jìn)程間交互,因此需要在 IPC 資源申請(qǐng)時(shí)加入命名空間信息,每個(gè) IPC 資源有一個(gè)唯一的 32 位 id。
4、mnt 命名空間(掛載文件系統(tǒng))
類似 chroot,將一個(gè)進(jìn)程放到一個(gè)特定的目錄執(zhí)行。mnt 命名空間允許不同命名空間的進(jìn)程看到的文件結(jié)構(gòu)不同,這樣每個(gè)命名空間 中的進(jìn)程所看到的文件目錄就被隔離開了。同 chroot 不同,每個(gè)命名空間中的容器在 /proc/mounts 的信息只包含所在命名空間的 mount point。
5、UTS 命名空間(主機(jī)名/域名)
UTS(“UNIX Time-sharing System”) 命名空間允許每個(gè)容器擁有獨(dú)立的 hostname 和 domain name, 使其在網(wǎng)絡(luò)上可以被視作一個(gè)獨(dú)立的節(jié)點(diǎn)而非 主機(jī)上的一個(gè)進(jìn)程
6、user 命名空間(用戶)
每個(gè)容器可以有不同的用戶和組 id, 也就是說可以在容器內(nèi)用容器內(nèi)部的用戶執(zhí)行程序而非主機(jī)上的用戶。
二、掛載點(diǎn)
雖然我們已經(jīng)通過 Linux 的命名空間解決了進(jìn)程和網(wǎng)絡(luò)隔離的問題,在 Docker 進(jìn)程中我們已經(jīng)沒有辦法訪問宿主機(jī)器上的其他進(jìn)程并且限制了網(wǎng)絡(luò)的訪問,但是 Docker 容器中的進(jìn)程仍然能夠訪問或者修改宿主機(jī)器上的其他目錄,這是我們不希望看到的。
在新的進(jìn)程中創(chuàng)建隔離的掛載點(diǎn)命名空間需要在 clone 函數(shù)中傳入 CLONE_NEWNS,這樣子進(jìn)程就能得到父進(jìn)程掛載點(diǎn)的拷貝,如果不傳入這個(gè)參數(shù)子進(jìn)程對(duì)文件系統(tǒng)的讀寫都會(huì)同步回父進(jìn)程以及整個(gè)主機(jī)的文件系統(tǒng)。
如果一個(gè)容器需要啟動(dòng),那么它一定需要提供一個(gè)根文件系統(tǒng)(rootfs),容器需要使用這個(gè)文件系統(tǒng)來創(chuàng)建一個(gè)新的進(jìn)程,所有二進(jìn)制的執(zhí)行都必須在這個(gè)根文件系統(tǒng)中。
想要正常啟動(dòng)一個(gè)容器就需要在 rootfs 中掛載以上的幾個(gè)特定的目錄,除了上述的幾個(gè)目錄需要掛載之外我們還需要建立一些符號(hào)鏈接保證系統(tǒng) IO 不會(huì)出現(xiàn)問題。
為了保證當(dāng)前的容器進(jìn)程沒有辦法訪問宿主機(jī)器上其他目錄,我們?cè)谶@里還需要通過 libcotainer 提供的 pivor_root 或者 chroot 函數(shù)改變進(jìn)程能夠訪問個(gè)文件目錄的根節(jié)點(diǎn)。
// pivor_root put_old = mkdir(...); pivot_root(rootfs, put_old); chdir("/"); unmount(put_old, MS_DETACH); rmdir(put_old);// chroot mount(rootfs, "/", NULL, MS_MOVE, NULL); chroot("."); chdir("/");到這里我們就將容器需要的目錄掛載到了容器中,同時(shí)也禁止當(dāng)前的容器進(jìn)程訪問宿主機(jī)器上的其他目錄,保證了不同文件系統(tǒng)的隔離。
總結(jié)
以上是生活随笔為你收集整理的Docker原理之Namespaces的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: percona-toolkit---pt
- 下一篇: MongoDB(二):MongoDB的安