国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

你應(yīng)當(dāng)了解的Docker底層技術(shù)

paulli3 / 1324人閱讀

摘要:底層技術(shù)用于環(huán)境隔離,支持的包括以及新加入的等,用于隔離主機名和域名,使用標(biāo)識,用于隔離進(jìn)程間通信資源如消息隊列等,使用標(biāo)識,隔離進(jìn)程,用于隔離網(wǎng)絡(luò),用于隔離掛載點,用于隔離用戶組。

本文已獲得原作者_(dá)_七把刀__授權(quán)。

Docker 容器技術(shù)已經(jīng)發(fā)展了好些年,在很多項目都有應(yīng)用,線上運行也很穩(wěn)定。整理了部分 Docker 的學(xué)習(xí)筆記以及新版本特性,對Docker感興趣的同學(xué)可以看看,之前整理過的 Linux namespace 可以見之前的博文。
1容器 & Docker & 虛擬機

Container (容器)是一種輕量級的虛擬化技術(shù),它不需要模擬硬件創(chuàng)建虛擬機。在 Linux 系統(tǒng)里面,使用到 Linux kernel 的 cgroups,namespace(ipc,network, user,pid,mount),capability 等用于隔離運行環(huán)境和資源限制的技術(shù),我們稱之為容器。容器技術(shù)早就出現(xiàn)。例如 Solaris Zones 和 BSD jails 就是非 Linux 操作系統(tǒng)上的容器,而用于 Linux 的容器技術(shù)也有很多如 Linux-Vserver、OpenVZ 和 FreeVPS。雖然這些技術(shù)都已經(jīng)成熟,但是這些解決方案還沒有將它們的容器支持集成到主流 Linux 內(nèi)核。總的來說,容器不等同于 Docker,容器更不是虛擬機。

LXC 項目由一個 Linux 內(nèi)核補丁和一些 userspace 工具組成,它提供一套簡化的工具來維護(hù)容器,用于虛擬環(huán)境的環(huán)境隔離、資源限制以及權(quán)限控制。LXC 有點類似 chroot,但是它比 chroot 提供了更多的隔離性。

Docker 最初目標(biāo)是做一個特殊的 LXC 的開源系統(tǒng),最后慢慢演變?yōu)樗约旱囊惶兹萜鬟\行時環(huán)境。Docker 基于 Linux kernel 的 CGroups,Namespace,UnionFileSystem 等技術(shù)封裝成一種自定義的容器格式,用于提供一整套虛擬運行環(huán)境。毫無疑問,近些年來 Docker 已經(jīng)成為了容器技術(shù)的代名詞,如其官網(wǎng)介紹的Docker is world"s leading software containerization platform。本文會先簡單介紹 Docker 基礎(chǔ)概念,然后會分析下 Docker 背后用到的技術(shù)。Debian 上安裝 Docker 方法參見docker-ce-installation-in-debian。

2 Docker 基礎(chǔ) 2.1 Docker Engine

Docker 提供了一個打包和運行應(yīng)用的隔離環(huán)境,稱之為容器,Docker 的隔離和安全特性允許你在一個主機同時運行多個容器,而且它并不像虛擬機那樣重量級,容器都是基于宿主機的內(nèi)核運行的,它是輕量的,不管你運行的是ubuntu, debian 還是其他 Linux 系統(tǒng),用的內(nèi)核都是宿主機內(nèi)核。Docker 提供了工具和平臺來管理容器,而 Docker Engine 則是一個提供了大部分功能組件的CS架構(gòu)的應(yīng)用,如架構(gòu)圖所示,Docker Engine 負(fù)責(zé)管理鏡像,容器,網(wǎng)絡(luò)以及數(shù)據(jù)卷等。

2.2 Docker 架構(gòu)

Docker 更詳細(xì)的架構(gòu)如圖所示,采用CS架構(gòu),client 通過 RESTFUL API 發(fā)送 docker 命令到 docker daemon 進(jìn)程,docker daemon 進(jìn)程執(zhí)行鏡像編譯,容器啟停以及分發(fā),數(shù)據(jù)卷管理等,一個 client 可以與多個 docker daemon 通信。

Docker Daemon:Docker 后臺進(jìn)程,用于管理鏡像,容器以及數(shù)據(jù)卷。

Docker Client:用于與 Docker Daemon 交互。

Docker Registry:用于存儲 Docker 鏡像,類似 github,公共的 Registry 有 Docker Hub 和 Docker Cloud。

Images:鏡像是用于創(chuàng)建容器的一種只讀模板。鏡像通常基于一個基礎(chǔ)鏡像,在此基礎(chǔ)上安裝額外的軟件。比如你的 nginx 鏡像可能基于 debian 然后安裝 nginx 并添加配置,你可以從 Docker Hub 上拉取已有的鏡像或者自己通過 Dockerfile 來編譯一個鏡像。

Containers:容器是鏡像的一個可運行示例,我們可通過 Docker client 或者 API 來創(chuàng)建,啟停或者刪除容器。默認(rèn)情況下,容器與宿主機以及其他容器已經(jīng)隔離,當(dāng)然你可以控制隔離容器的網(wǎng)絡(luò)或者存儲的方式。

Services:服務(wù)是 docker swarm 引入的概念,可以用于在多宿主機之間伸縮容器數(shù)目,支持負(fù)載均衡已經(jīng)服務(wù)路由功能。

2.3 Docker底層技術(shù)概覽

通過下面命令運行一個 debian 容器,attach 到一個本機的命令行并運行/bin/bash。

docker run -i -t debian /bin/bash

這個命令背后都做了什么?

1.如果本機沒有 debian 鏡像,則會從你配置的 Registry 里面拉取一個 debian 的 latest 版本的鏡像,跟你運行了docker pull debian效果一樣。

2.創(chuàng)建容器。跟運行docker create一樣。

3.給容器分配一個讀寫文件系統(tǒng)作為該容器的 final layer,容器可以在它的文件系統(tǒng)創(chuàng)建和修改文件。

4.Docker 為容器創(chuàng)建了一套網(wǎng)絡(luò)接口,給容器分配一個 ip。默認(rèn)情況下,容器可以通過默認(rèn)網(wǎng)絡(luò)連通到外部網(wǎng)絡(luò)。

5.Docker啟動容器并執(zhí)行 /bin/bash。因為啟動時指定了 -i -t 參數(shù),容器是以交互模式運行且 attach 到本地終端,我們可以在終端上輸入命令并看到輸出。

6.運行 exit 可以退出容器,但是此時容器并沒有被刪除,我們可以再次運行它或者刪除它。

可以發(fā)現(xiàn),容器的內(nèi)核版本是跟宿主機一樣的,不同的是容器的主機名是獨立的,它默認(rèn)用容器 ID 做主機名。我們運行ps -ef可以發(fā)現(xiàn)容器進(jìn)程是隔離的,容器里面看不到宿主機的進(jìn)程,而且它自己有 PID 為1的進(jìn)程。此外,網(wǎng)絡(luò)也是隔離的,它有獨立于宿主機的 IP。文件系統(tǒng)也是隔離的,容器有自己的系統(tǒng)和軟件目錄,修改容器內(nèi)的文件并不影響宿主機對應(yīng)目錄的文件。

root@stretch:/home/vagrant# uname -r
4.9.0-6-amd64
root@stretch:/home/vagrant# docker run -it --name demo alpine /bin/ash
/ # uname -r   ## 容器內(nèi)
4.9.0-6-amd64

/ # ps -ef
PID   USER     TIME   COMMAND
    1 root       0:00 /bin/ash
    7 root       0:00 ps -ef
    
/ # ip a
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth0@if7:  mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

這些隔離機制并不是 Docker 新開發(fā)的技術(shù),而是依托 Linux kernel 以及一些已有的技術(shù)實現(xiàn)的,主要包括:

Linux Namespaces(Linux2.6.24后引入):命名空間用于進(jìn)程(PID)、網(wǎng)絡(luò)(NET)、掛載點(MNT)、UTS、IPC 等隔離。

Linux Control Groups(CGroups):用于限制容器使用的資源,包括內(nèi)存,CPU等。

Union File Systems:UnionFS 把多個目錄結(jié)合成一個目錄,對外使用,最上層目錄為讀寫層(通常只有1個),下面可以有一個或多個只讀層,見容器和鏡像分層圖。Docker 支持 OverlayFS,AUFS、DeviceMapper、btrfs 等聯(lián)合文件系統(tǒng)。

Container Format: Docker Engine 組合 Namespaces,CGroup 以及 UnionFS 包裝為一個容器格式,默認(rèn)格式為 libcontainer,后續(xù)可能會加入 BSD Jails 或 Solaris Zones 容器格式的支持。

3 Docker底層技術(shù) 3.1 Namespaces

Namespaces 用于環(huán)境隔離,Linux kernel 支持的 Namespace 包括UTS, IPC, PID, NET, NS, USER 以及新加入的 CGROUP 等,UTS 用于隔離主機名和域名,使用標(biāo)識 CLONE_NEWUTS,IPC 用于隔離進(jìn)程間通信資源如消息隊列等,使用標(biāo)識 CLONE_NEWIPC,PID 隔離進(jìn)程,NET 用于隔離網(wǎng)絡(luò),NS 用于隔離掛載點,USER 用于隔離用戶組。默認(rèn)情況下,通過 clone 系統(tǒng)調(diào)用創(chuàng)建子進(jìn)程的 namespace 與父進(jìn)程是一致的,而你可以在 clone 系統(tǒng)調(diào)用中通過flag參數(shù)設(shè)置隔離的名字空間來隔離,當(dāng)然也可以更加方便的直接用 unshare 命令來創(chuàng)建新的 namespace。查看一個進(jìn)程的各 Namespace 命令如下:

root@stretch:/home/vagrant# ls -ls /proc/self/ns/
0 lrwxrwxrwx 1 root root 0 May 17 22:04 cgroup -> cgroup:[4026531835]
0 lrwxrwxrwx 1 root root 0 May 17 22:04 ipc -> ipc:[4026531839]
0 lrwxrwxrwx 1 root root 0 May 17 22:04 mnt -> mnt:[4026531840]
0 lrwxrwxrwx 1 root root 0 May 17 22:04 net -> net:[4026531957]
0 lrwxrwxrwx 1 root root 0 May 17 22:04 pid -> pid:[4026531836]
0 lrwxrwxrwx 1 root root 0 May 17 22:04 user -> user:[4026531837]
0 lrwxrwxrwx 1 root root 0 May 17 22:04 uts -> uts:[4026531838]
PID Namespace

在容器中,有自己的 Pid namespace,因此我們看到的只有 PID 為1的初始進(jìn)程以及它的子進(jìn)程,而宿主機的其他進(jìn)程容器內(nèi)是看不到的。通常來說, Linux 啟動后它會先啟動一個 PID 為1的進(jìn)程,這是系統(tǒng)進(jìn)程樹的根進(jìn)程,根進(jìn)程會接著創(chuàng)建子進(jìn)程來初始化系統(tǒng)服務(wù)。PID namespace 允許在新的namespace 創(chuàng)建一棵新的進(jìn)程樹,它可以有自己的PID為1的進(jìn)程。在 PID namespace 的隔離下,子進(jìn)程名字空間無法知道父進(jìn)程名字空間的進(jìn)程,如在Docker容器中無法看到宿主機的進(jìn)程,而父進(jìn)程名字空間可以看到子進(jìn)程名字空間的所有進(jìn)程。如圖所示:

Linux 內(nèi)核加入 PID Namespace 后,對 pid 結(jié)構(gòu)進(jìn)行了修改,新增的 upid 結(jié)構(gòu)用于跟蹤 namespace 和 pid。

## 加入PID Namespace之前的pid結(jié)構(gòu)
 struct pid {
    atomic_t count;             /* reference counter */
    int nr;                 /* the pid value */
    struct hlist_node pid_chain;        /* hash chain */
    ...
};

## 加入PID Namespace之后的pid結(jié)構(gòu)
struct upid {
    int nr;                 /* moved from struct pid */
    struct pid_namespace *ns;   
    struct hlist_node pid_chain;        /* moved from struct pid */
};

struct pid {
     ...
     int level;             /* the number of upids */
     struct upid numbers[0];
};

可以通過 unshare 測試下 PID namespace,可以看到新的 bash 進(jìn)程它的 pid namespace 與父進(jìn)程的不同了,而且它的 pid 是1。

root@stretch:/home/vagrant# unshare --fork --pid bash
root@stretch:/home/vagrant# echo $$
1
root@stretch:/home/vagrant# ls -ls /proc/self/ns/
0 lrwxrwxrwx 1 root root 0 May 19 15:24 cgroup -> cgroup:[4026531835]
0 lrwxrwxrwx 1 root root 0 May 19 15:24 ipc -> ipc:[4026531839]
0 lrwxrwxrwx 1 root root 0 May 19 15:24 mnt -> mnt:[4026531840]
0 lrwxrwxrwx 1 root root 0 May 19 15:24 net -> net:[4026531957]
0 lrwxrwxrwx 1 root root 0 May 19 15:24 pid -> pid:[4026532232]
0 lrwxrwxrwx 1 root root 0 May 19 15:24 user -> user:[4026531837]
0 lrwxrwxrwx 1 root root 0 May 19 15:24 uts -> uts:[4026531838]
NS Namespace

NS Namespace 用于隔離掛載點,不同 NS Namespace 的掛載點互不影響。創(chuàng)建一個新的 Mount Namespace 效果有點類似 chroot,不過它隔離的比 chroot 更加完全。這是歷史上的第一個 Linux Namespace,由此得到了 NS 這個名字而不是用的 Mount

在最初的 NS Namespace 版本中,掛載點是完全隔離的。初始狀態(tài)下,子進(jìn)程看到的掛載點與父進(jìn)程是一樣的。在新的 Namespace 中,子進(jìn)程可以隨意 mount/umount 任何目錄,而不會影響到父 Namespace。使用 NS Namespace完全隔離掛載點初衷很好,但是也帶來了某些情況下不方便,比如我們新加了一塊磁盤,如果完全隔離則需要在所有的 Namespace 中都掛載一遍。為此,Linux 在2.6.15版本中加入了一個 shared subtree 特性,通過指定 Propagation 來確定掛載事件如何傳播。比如通過指定 MS_SHARED 來允許在一個 peer group (子 namespace 和父 namespace 就屬于同一個組)共享掛載點,mount/umount 事件會傳播到 peer group 成員中。使用 MS_PRIVATE 不共享掛載點和傳播掛載事件。其他還有 MS_SLAVENS_UNBINDABLE 等選項。可以通過查看 cat /proc/self/mountinfo 來看掛載點信息,若沒有傳播參數(shù)則為 MS_PRIVATE 的選項。

例如你在初始 namespace 有兩個掛載點,通過 mount --make-shared /dev/sda1 /mntS 設(shè)置 /mntS 為shared類型,mount --make-private /dev/sda1 /mntP 設(shè)置 /mntP 為 private 類型。當(dāng)你使用 unshare -m bash 新建一個 namespace 并在它們下面掛載子目錄時,可以發(fā)現(xiàn) /mntS 下面的子目錄 mount/umount 事件會傳播到父 namespace,而 /mntP 則不會。關(guān)于 mount 各種模式詳解可以參考這篇文章。

在前面例子 Pid namespace 隔離后,我們在新的名字空間執(zhí)行 ps -ef 可以看到宿主機進(jìn)程,這是因為 ps 命令是從 /proc 文件系統(tǒng)讀取的數(shù)據(jù),而文件系統(tǒng)我們還沒有隔離,為此,我們需要在新的 NS Namespace 重新掛載 proc 文件系統(tǒng)來模擬類似 Docker 容器的功能。

root@stretch:/home/vagrant# unshare --pid --fork --mount-proc bash
root@stretch:/home/vagrant# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 15:36 pts/1    00:00:00 bash
root         2     1  0 15:36 pts/1    00:00:00 ps -ef

可以看到,隔離了 NS namespace 并重新掛載了 proc 后,ps 命令只能看到2個進(jìn)程了,跟我們在 Docker 容器中看到的一致。

NET Namespace

Docker 容器中另一個重要特性是網(wǎng)絡(luò)獨立(之所以不用隔離一詞是因為容器的網(wǎng)絡(luò)還是要借助宿主機的網(wǎng)絡(luò)來通信的),使用到 Linux 的 NET Namespace 以及 vet。veth 主要的目的是為了跨 NET namespace 之間提供一種類似于 Linux 進(jìn)程間通信的技術(shù),所以 veth 總是成對出現(xiàn),如下面的 veth0 和 veth1。它們位于不同的 NET namespace 中,在 veth 設(shè)備任意一端接收到的數(shù)據(jù),都會從另一端發(fā)送出去。veth 實現(xiàn)了不同namespace的網(wǎng)絡(luò)數(shù)據(jù)傳輸。

在 Docker 中,宿主機的 veth 端會橋接到網(wǎng)橋中,接收到容器中的 veth 端發(fā)過來的數(shù)據(jù)后會經(jīng)由網(wǎng)橋 docker0 再轉(zhuǎn)發(fā)到宿主機網(wǎng)卡 eth0,最終通過 eth0 發(fā)送數(shù)據(jù)。當(dāng)然在發(fā)送數(shù)據(jù)前,需要經(jīng)過iptables MASQUERADE 規(guī)則將源地址改成宿主機 ip,這樣才能接收到響應(yīng)數(shù)據(jù)包。而宿主機網(wǎng)卡接收到的數(shù)據(jù)會通過iptables DNAT 根據(jù)端口號修改目的地址和端口為容器的ip 和端口,然后根據(jù)路由規(guī)則發(fā)送到網(wǎng)橋 docker0 中,并最終由網(wǎng)橋 docker0 發(fā)送到對應(yīng)的容器中。

Docker 里面網(wǎng)絡(luò)模式分為 bridge,host,overlay 等幾種模式,默認(rèn)是采用 bridge 模式網(wǎng)絡(luò)如圖所示。如果使用 host 模式,則不隔離直接使用宿主機網(wǎng)絡(luò)。overlay 網(wǎng)絡(luò)則是更加高級的模式,可以實現(xiàn)跨主機的容器通信,后面會多帶帶總結(jié)下 Docker 網(wǎng)絡(luò)這個專題。

USER Namespace

user namespace 用于隔離用戶和組信息,在不同的 namespace 中用戶可以有相同的 UID 和 GID,它們之間互相不影響。父子 namespace 之間可以進(jìn)行用戶映射,如父 namespace (宿主機)的普通用戶映射到子 namespace (容器)的 root 用戶,以減少子 namespace 的 root 用戶操作父 namespace 的風(fēng)險。user namespace 功能雖然在很早就出現(xiàn)了,但是直到 Linux kernel 3.8之后這個功能才趨于完善。

創(chuàng)建新的 user namespace 之后第一步就是設(shè)置好 user 和 group 的映射關(guān)系。這個映射通過設(shè)置 /proc/PID/uid_map(gid_map) 實現(xiàn),格式如下,ID-inside-ns 是容器內(nèi)的 uid/gid,而 ID-outside-ns 則是容器外映射的真實 uid/gid。比如0 1000 1表示將真實的 uid =1000映射為容器內(nèi)的 uid=0,length 為映射的范圍。

ID-inside-ns   ID-outside-ns   length

不是所有的進(jìn)程都能隨便修改映射文件的,必須同時具備如下條件:

修改映射文件的進(jìn)程必須有 PID 進(jìn)程所在 user namespace 的 CAP_SETUID/CAP_SETGID 權(quán)限。

修改映射文件的進(jìn)程必須是跟 PID 在同一個 user namespace 或者 PID 的父 namespace。

映射文件 uid_map 和 gid_map 只能寫入一次,再次寫入會報錯。
docker 1.10之后的版本可以通過在 docker daemon 啟動時加上 --userns-remap=[USERNAME] 來實現(xiàn) USER Namespace 的隔離。我們指定了 username = ssj 啟動 dockerd,查看 subuid 文件可以發(fā)現(xiàn) ssj 映射的 uid 范圍是165536到165536+65536= 231072,而且在docker目錄下面對應(yīng) ssj 有一個獨立的目錄165536.165536存在。

root@stretch:/home/vagrant# cat /etc/subuid
vagrant:100000:65536
ssj:165536:65536

root@stretch:/home/vagrant# ls /var/lib/docker/165536.165536/
builder/  containerd/  containers/  image/  network/  ...

運行 docker images -a 等命令可以發(fā)現(xiàn)在啟用 user namespace 之前的鏡像都看不到了。此時只能看到在新的 user namespace 里面創(chuàng)建的 docker 鏡像和容器。而此時我們創(chuàng)建一個測試容器,可以在容器外看到容器進(jìn)程的 uid_map 已經(jīng)設(shè)置為 ssj,這樣容器中的 root 用戶映射到宿主機就是 ssj 這個用戶了,此時如果要刪除我們掛載的 /bin 目錄中的文件,會提示沒有權(quán)限,增強了安全性。

### dockerd 啟動時加了 --userns-remap=ssj
root@stretch:/home/vagrant# docker run -it -v /bin:/host/bin --name demo alpine /bin/ash
/ # rm /host/bin/which 
rm: remove "/host/bin/which"? y
rm: can"t remove "/host/bin/which": Permission denied

### 宿主機查看容器進(jìn)程uid_map文件
root@stretch:/home/vagrant# CPID=`ps -ef|grep "/bin/ash"|awk "{printf $2}"`
root@stretch:/home/vagrant# cat /proc/$CPID/uid_map
         0     165536      65536
其他Namespace

UTS namespace 用于隔離主機名等。可以看到在新的 uts namespace 修改主機名并不影響原 namespace 的主機名。

root@stretch:/home/vagrant# unshare --uts --fork bash
root@stretch:/home/vagrant# hostname
stretch
root@stretch:/home/vagrant# hostname modified
root@stretch:/home/vagrant# hostname
modified
root@stretch:/home/vagrant# exit
root@stretch:/home/vagrant# hostname
stretch

IPC Namespace 用于隔離 IPC 消息隊列等。可以看到,新老 ipc namespace 的消息隊列互不影響。

root@stretch:/home/vagrant# ipcmk -Q
Message queue id: 0
root@stretch:/home/vagrant# ipcs -q

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

root@stretch:/home/vagrant# unshare --ipc --fork bash
root@stretch:/home/vagrant# ipcs -q

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

CGROUP Namespace 是 Linux 4.6以后才支持的新 namespace。容器技術(shù)使用 namespace 和 cgroup 實現(xiàn)環(huán)境隔離和資源限制,但是對于 cgroup 本身并沒有隔離。沒有 cgroup namespace 前,容器中一旦掛載 cgroup 文件系統(tǒng),便可以修改整全局的 cgroup 配置。有了 cgroup namespace 后,每個 namespace 中的進(jìn)程都有自己的 cgroup 文件系統(tǒng)視圖,增強了安全性,同時也讓容器遷移更加方便。在我測試的 Docker 18.03.1-ce 版本中容器暫時沒有用到 cgroup namespace,這里就不再展開。

3.2 CGroups

Linux CGroups 用于資源限制,包括限制 CPU、內(nèi)存、blkio 以及網(wǎng)絡(luò)等。通過工具 cgroup-bin (sudo apt-get install cgroup-bin) 可以創(chuàng)建 CGroup 并進(jìn)入該 CGroup 執(zhí)行命令。

root@stretch:/home/vagrant# cgcreate -a vagrant -g cpu:cg1
root@stretch:/home/vagrant# ls /sys/fs/cgroup/cpu/cg1/
cgroup.clone_children  cpu.cfs_period_us  cpu.shares  cpuacct.stat   cpuacct.usage_all     cpuacct.usage_percpu_sys   cpuacct.usage_sys   notify_on_release
cgroup.procs           cpu.cfs_quota_us   cpu.stat    cpuacct.usage  cpuacct.usage_percpu  cpuacct.usage_percpu_user  cpuacct.usage_user  tasks

cpu.cfs_period_us 和 cpu.cfs_quota_us,它們分別用來限制該組中的所有進(jìn)程在單位時間里可以使用的 cpu 時間,這里的 cfs(Completely Fair Scheduler) 是完全公平調(diào)度器的意思。cpu.cfs_period_us 是時間周期,默認(rèn)為100000,即100毫秒。而 cpu.cfs_quota_us 是在時間周期內(nèi)可以使用的時間,默認(rèn)為-1即無限制。cpu.shares 用于限制cpu使用的,它用于控制各個組之間的配額。比如組 cg1的 cpu.shares 為1024,組 cg2的cpu.shares 也是1024,如果都有進(jìn)程在運行則它們都可以使用最多50%的限額。如果 cg2 組內(nèi)進(jìn)程比較空閑,那么 cg1 組可以將使用幾乎整個 cpu,tasks 存儲的是該組里面的進(jìn)程 ID。( 注: debian8 默認(rèn)沒有 cfs 和 memory cgroup 支持,需要重新編譯內(nèi)核及修改啟動參數(shù),debian9 默認(rèn)已經(jīng)支持)

我們先在默認(rèn)的分組里面運行一個死循環(huán)程序 loop.py,因為默認(rèn)分組 /sys/fs/cgroup/cpu/cpu.cfs_period_uscfs_quota_us 是默認(rèn)值,所以是沒有限制 cpu 使用的。可以發(fā)現(xiàn)1個 cpu us 立馬接近100%了。

# loop.py
while True: pass

設(shè)置 cg1 組 的cfs_quota_us 位50000,即表示該組內(nèi)進(jìn)程最多使用50%的 cpu 時間,運行 cgexec 命令進(jìn)入 cg1 的 cpu 組,然后運行 loop.py,可以發(fā)現(xiàn) cpu us 在50%以內(nèi)了,此時也可以在 tasks 文件中看到我們剛剛 cgexec 創(chuàng)建的進(jìn)程 ID。

root@stretch:/home/vagrant# echo 50000 > /sys/fs/cgroup/cpu/cg1/cpu.cfs_quota_us
root@stretch:/home/vagrant# cgexec -g cpu:cg1 /bin/bash

Docker 里面要限制內(nèi)存和 CPU 使用,可以在啟動時指定相關(guān)參數(shù)即可。比如限制 cpu 使用率,加 cpu-period 和 cpu-quota 參數(shù),限制執(zhí)行的 cpu 核,加 --cpuset-cpus 參數(shù)。限制內(nèi)存使用,加--memory參數(shù)。當(dāng)然,我們可以看到在 /sys/fs/cgroup/cpu/docker/ 目錄下有個以 containerid 為名的分組,該分組下面的 cpu.cfs_period_us 和 cpu.cfs_quota_us 的值就是我們在啟動容器時指定的值。

root@stretch:/home/vagrant# docker run -i -t --cpu-period=100000 --cpu-quota=50000 --memory=512000000 alpine /bin/ash
3.3 Capabilities

我們在啟動容器時會時常看到這樣的參數(shù) --cap-add=NET_ADMIN,這是用到了 Linux 的 capability 特性。 capability 是為了實現(xiàn)更精細(xì)化的權(quán)限控制而加入的。我們以前熟知通過設(shè)置文件的 SUID 位,這樣非 root 用戶的可執(zhí)行文件運行后的 euid 會成為文件的擁有者 ID,比如 passwd 命令運行起來后有 root 權(quán)限,有 SUID 權(quán)限的可執(zhí)行文件如果存在漏洞會有安全風(fēng)險。(查看文件的 capability 的命令為 filecap -a,而查看進(jìn)程 capability 的命令為 pscap -a,pscap 和 filecap工具需要安裝 libcap-ng-utils這個包)。

對于 capability,可以看一個簡單的例子便于理解。如 Debian 系統(tǒng)中自帶的 ping 工具,它是有設(shè)置 SUID 位的。這里拷貝 ping 重命名為 anotherping,anotherping 的 SUID 位沒有設(shè)置,運行會提示權(quán)限錯誤。這里,我們只要將其加上 cap_net_raw 權(quán)限即可,不需要設(shè)置 SUID 位那么大的權(quán)限。

vagrant@stretch:~$ ls -ls /bin/ping
60 -rwsr-xr-x 1 root root 61240 Nov 10  2016 /bin/ping
vagrant@stretch:~$ cp /bin/ping anotherping
vagrant@stretch:~$ ls -ls anotherping 
60 -rwxr-xr-x 1 vagrant vagrant 61240 May 19 10:18 anotherping
vagrant@stretch:~$ ./anotherping -c1 yue.uu.163.com
ping: socket: Operation not permitted
vagrant@stretch:~$ sudo setcap cap_net_raw+ep ./anotherping 
vagrant@stretch:~$ ./anotherping -c1 yue.uu.163.com
PING yue.uu.163.com (59.111.137.252) 56(84) bytes of data.
64 bytes from 59.111.137.252 (59.111.137.252): icmp_seq=1 ttl=63 time=53.9 ms

--- yue.uu.163.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 53.919/53.919/53.919/0.000 ms
3.4 Union File System

UnionFS (聯(lián)合文件系統(tǒng))簡單來說就是支持將不同的目錄掛載到同一個目錄中的技術(shù)。Docker 支持的 UnionFS 包 括OverlayFS,AUFS,devicemapper,vfs 以及 btrfs 等,查看 UnionFS 版本可以用 docker info 查看對應(yīng)輸出中的 Storage 項即可,早期的 Docker 版本用 AUFS 和 devicemapper 居多,新版本 Docker 在 Linux 3.18之后版本基本默認(rèn)用 OverlayFS,這里以 OverlayFS 來分析。

OverlayFS 與早期用過的 AUFS 類似,不過它比 AUFS 更簡單,讀寫性能更好,在 docker-ce18.03 版本中默認(rèn)用的存儲驅(qū)動是 overlay2,老版本 overlay 官方已經(jīng)不推薦使用。它將兩個目錄 upperdir 和 lowdir 聯(lián)合掛載到一個 merged 目錄,提供統(tǒng)一視圖。其中 upperdir 是可讀寫層,對容器修改寫入在該目錄中,它也會隱藏 lowerdir 中相同的文件。而 lowdir 是只讀層, Docker 鏡像在這層。

在看 Docker 鏡像和容器存儲結(jié)構(gòu)前,可以先簡單操作下 OverlayFS 看下基本概念。創(chuàng)建了 lowerdir 和 upperdir 兩個目錄,然后用 overlayfs 掛載到 merged 目錄,這樣在 merged 目錄可以看到兩個目錄的所有文件 both.txt 和 only.txt。其中 upperdir 是可讀寫的,而 lowerdir 只讀。通過 merged 目錄來操作文件可以發(fā)現(xiàn):

讀取文件時,如果 upperdir 不存在該文件,則會從 lowerdir 直接讀取。

修改文件時并不影響 lowerdir 中的文件,因為它是只讀的。

如果修改的文件在 upperdir 不存在,則會從 lowerdir 拷貝到 upperdir,然后在 upperdir 里面修改該文件,并不影響 lowerdir 目錄的文件。

刪除文件則是將 upperdir 中將對應(yīng)文件設(shè)置成了 c 類型,即字符設(shè)備類型來隱藏已經(jīng)刪除的文件(與 AUFS 創(chuàng)建一個 whiteout 文件略有不同)。

root@stretch:/home/vagrant/overlaytest# tree -a
.
|-- lowerdir
|   |-- both.txt
|   `-- only.txt
|-- merged
|-- upperdir
|   `-- both.txt
`-- workdir
    `-- work

5 directories, 3 files
root@stretch:/home/vagrant/overlaytest# mount -t overlay overlay -olowerdir=./lowerdir,upperdir=./upperdir,workdir=./workdir ./merged
root@stretch:/home/vagrant/overlaytest# tree
.
|-- lowerdir
|   |-- both.txt
|   `-- only.txt
|-- merged
|   |-- both.txt
|   `-- only.txt
|-- upperdir
|   `-- both.txt
`-- workdir
    `-- work

5 directories, 5 files
root@stretch:/home/vagrant/overlaytest# tree -a
.
|-- lowerdir
|   |-- both.txt
|   `-- only.txt
|-- merged
|   |-- both.txt
|   `-- only.txt
|-- upperdir
|   `-- both.txt
`-- workdir
    `-- work

5 directories, 5 files
root@stretch:/home/vagrant/overlaytest# echo "modified both" > merged/both.txt 
root@stretch:/home/vagrant/overlaytest# cat upperdir/both.txt 
modified both
root@stretch:/home/vagrant/overlaytest# cat lowerdir/both.txt 
lower both.txt
root@stretch:/home/vagrant/overlaytest# echo "modified only" > merged/only.txt 
root@stretch:/home/vagrant/overlaytest# tree
.
|-- lowerdir
|   |-- both.txt
|   `-- only.txt
|-- merged
|   |-- both.txt
|   `-- only.txt
|-- upperdir
|   |-- both.txt
|   `-- only.txt
`-- workdir
    `-- work

5 directories, 6 files
root@stretch:/home/vagrant/overlaytest# cat upperdir/only.txt 
modified only
root@stretch:/home/vagrant/overlaytest# cat lowerdir/only.txt 
lower only.txt
root@stretch:/home/vagrant/overlaytest# tree -a
.
|-- lowerdir
|   |-- both.txt
|   `-- only.txt
|-- merged
|   |-- both.txt
|   `-- only.txt
|-- upperdir
|   |-- both.txt
|   `-- only.txt
`-- workdir
    `-- work

5 directories, 6 files
root@stretch:/home/vagrant/overlaytest# rm merged/both.txt 
root@stretch:/home/vagrant/overlaytest# tree -a
.
|-- lowerdir
|   |-- both.txt
|   `-- only.txt
|-- merged
|   `-- only.txt
|-- upperdir
|   |-- both.txt
|   `-- only.txt
`-- workdir
    `-- work
root@stretch:/home/vagrant/overlaytest# ls -ls upperdir/both.txt 
0 c--------- 1 root root 0, 0 May 19 02:31 upperdir/both.txt

回到 Docker 里面,我們拉取一個 nginx 鏡像,有三層鏡像,可以看到在 overlay2 對應(yīng)每一層都有個目錄(注意,這個目錄名跟鏡像層名從 docker1.10 版本后名字已經(jīng)不對應(yīng)了),另外的 l 目錄是指向鏡像層的軟鏈接。最底層存儲的是基礎(chǔ)鏡像 debian/alpine,上一層是安裝了 nginx 增加的可執(zhí)行文件和配置文件,而最上層是鏈接 /dev/stdout 到 nginx 日志文件。而每個子目錄下面的 diff 目錄用于存儲鏡像內(nèi)容,work 目錄是 OverlayFS 內(nèi)部使用的,而 link 文件存儲的是該鏡像層對應(yīng)的短名稱,lower 文件存儲的是下一層的短名稱。

root@stretch:/home/vagrant# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
f2aa67a397c4: Pull complete 
3c091c23e29d: Pull complete 
4a99993b8636: Pull complete 
Digest: sha256:0fb320e2a1b1620b4905facb3447e3d84ad36da0b2c8aa8fe3a5a81d1187b884
Status: Downloaded newer image for nginx:latest

root@stretch:/home/vagrant# ls -ls /var/lib/docker/overlay2/
total 16
4 drwx------ 4 root root 4096 May 19 04:17 09495e5085bced25e8017f558147f82e61b012a8f632a0b6aac363462b1db8b0
4 drwx------ 3 root root 4096 May 19 04:17 8af95287a343b26e9c3dd679258773880e7bdbbe914198ba63a8ed1b4c5f5554
4 drwx------ 4 root root 4096 May 19 04:17 f311565fe9436eb8606f846e1f73f38287841773e8d041933a41259fe6f96afe
4 drwx------ 2 root root 4096 May 19 04:17 l

root@stretch:/var/lib/docker/overlay2# ls  09495e5085bced25e8017f558147f82e61b012a8f632a0b6aac363462b1db8b0/
diff  link  lower  work

從我們示例可以看到,三層中 f311是最頂層,下面分別是0949和8af9這兩層。

root@stretch:/var/lib/docker/overlay2# cat f311565fe9436eb8606f846e1f73f38287841773e8d041933a41259fe6f96afe/lower 
l/7B2WM6DC226TCJU6QHJ4ABKRI6:l/4FHO2G5SWWRIX44IFDHU62Z7X2
root@stretch:/var/lib/docker/overlay2# cat 09495e5085bced25e8017f558147f82e61b012a8f632a0b6aac363462b1db8b0/lower 
l/4FHO2G5SWWRIX44IFDHU62Z7X2
root@stretch:/var/lib/docker/overlay2# cat 8af95287a343b26e9c3dd679258773880e7bdbbe914198ba63a8ed1b4c5f5554/link 
4FHO2G5SWWRIX44IFDHU62Z7X2

此時我們啟動一個 nginx 容器,可以看到 overlay2 目錄多了兩個目錄,多出來的就是容器層的目錄和只讀的容器 init 層。容器目錄下面的 merged 就是我們前面提到的聯(lián)合掛載目錄了,而 lowdir 則是它下層目錄。而容器 init 層用來存儲與這個容器內(nèi)環(huán)境相關(guān)的內(nèi)容,如 /etc/hosts和/etc/resolv.conf 文件,它居于其他鏡像層之上,容器層之下。

root@stretch:/var/lib/docker/overlay2# docker run -idt --name nginx nginx 
01a873eeba41f00a5a3deb083adf5ed892c55b4680fbc2f1880e282195d3087b

root@stretch:/var/lib/docker/overlay2# ls -ls
4 drwx------ 4 root root 4096 May 19 04:17 09495e5085bced25e8017f558147f82e61b012a8f632a0b6aac363462b1db8b0
4 drwx------ 5 root root 4096 May 19 09:11 11b7579a1f1775ad71fe0f0f45fcb74c241fce319f5125b1b92cb442385065b1
4 drwx------ 4 root root 4096 May 19 09:11 11b7579a1f1775ad71fe0f0f45fcb74c241fce319f5125b1b92cb442385065b1-init
4 drwx------ 3 root root 4096 May 19 04:17 8af95287a343b26e9c3dd679258773880e7bdbbe914198ba63a8ed1b4c5f5554
4 drwx------ 4 root root 4096 May 19 04:17 f311565fe9436eb8606f846e1f73f38287841773e8d041933a41259fe6f96afe
4 drwx------ 2 root root 4096 May 19 09:11 l

root@stretch:/home/vagrant# ls -ls /var/lib/docker/overlay2/11b7579a1f1775ad71fe0f0f45fcb74c241fce319f5125b1b92cb442385065b1/
4 drwxr-xr-x 4 root root 4096 May 19 09:11 diff
4 -rw-r--r-- 1 root root   26 May 19 09:11 link
4 -rw-r--r-- 1 root root  115 May 19 09:11 lower
4 drwxr-xr-x 1 root root 4096 May 19 09:11 merged
4 drwx------ 3 root root 4096 May 19 09:11 work

root@stretch:/var/lib/docker/overlay2# ls  11b7579a1f1775ad71fe0f0f45fcb74c241fce319f5125b1b92cb442385065b1/merged/
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

root@stretch:/var/lib/docker/overlay2# ls 11b7579a1f1775ad71fe0f0f45fcb74c241fce319f5125b1b92cb442385065b1/diff/
run  var

如果我們在容器中修改文件,則會反映到容器層的 merged 目錄相關(guān)文件,容器層的 diff 目錄相當(dāng)于 upperdir,其他層是 lowerdir。如果之前容器層 diff 目錄不存在該文件,則會拷貝該文件到 diff 目錄并修改。讀取文件時,如果 upperdir 目錄找不到,則會直接從下層的鏡像層中讀取。

4 總結(jié)

隨著版本不斷更新,Docker 的一些技術(shù)細(xì)節(jié)也在變化,如鏡像層存儲目錄的變化,默認(rèn) UnionFileSystem 換成 OverlayFS,新的 Namespace 的支持等。這篇文章主要對以前的學(xué)習(xí)筆記和 Docker 的一些新的變化做了些許總結(jié),如想了解更詳細(xì)內(nèi)容,可以查看參考資料和 Docker 官方相關(guān)文檔。

作者:__七把刀__
鏈接:https://www.jianshu.com/p/7a1...
來源:簡書

更多相關(guān)閱讀:
Docker 容器操作
Docker 的那點事兒
Docker 基礎(chǔ)技術(shù)-Linux Namespace
docker-compose.yml 配置詳解

如果你還想了解更多,想和技術(shù)同僚分享切磋,可掃下方二維碼加好友,回復(fù)yw,加入掘金運維技術(shù)交流群

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/27364.html

相關(guān)文章

  • 應(yīng)當(dāng)了解Docker底層技術(shù)

    摘要:底層技術(shù)用于環(huán)境隔離,支持的包括以及新加入的等,用于隔離主機名和域名,使用標(biāo)識,用于隔離進(jìn)程間通信資源如消息隊列等,使用標(biāo)識,隔離進(jìn)程,用于隔離網(wǎng)絡(luò),用于隔離掛載點,用于隔離用戶組。 本文已獲得原作者_(dá)_七把刀__授權(quán)。 Docker 容器技術(shù)已經(jīng)發(fā)展了好些年,在很多項目都有應(yīng)用,線上運行也很穩(wěn)定。整理了部分 Docker 的學(xué)習(xí)筆記以及新版本特性,對Docker感興趣的同學(xué)可以看看,...

    MiracleWong 評論0 收藏0
  • 高效編寫Dockerfile幾條準(zhǔn)則

    摘要:本文已獲得原作者授權(quán)。在構(gòu)建鏡像的過程中會緩存一系列中間鏡像。鏡像時,會順序執(zhí)行中的指令,并同時比較當(dāng)前指令和其基礎(chǔ)鏡像的所有子鏡像,若發(fā)現(xiàn)有一個子鏡像也是由相同的指令生成,則命中緩存,同時可以直接使用該子鏡像而避免再去重新生成了。 本文已獲得原作者 CodeSheep 授權(quán)。 概述 Dockerfile 是專門用來進(jìn)行自動化構(gòu)建鏡像的編排文件(就像 Jenkins 2.0時代的 J...

    RyanQ 評論0 收藏0
  • 技術(shù)干貨 | Docker容器中需要避免十種常見誤區(qū)

    摘要:第二具備輕量化特性容器的體積非常小巧。他們大多認(rèn)為自己應(yīng)該將應(yīng)用程序部署至當(dāng)前正在運行的容器當(dāng)中。不要創(chuàng)建大型鏡像體積過大的鏡像會加大其發(fā)布難度。總體來講,在向生產(chǎn)環(huán)境中部署容器時,必須避免使用最新標(biāo)簽。 當(dāng)下最火爆的Docker,是一個開源的應(yīng)用容器引擎。大家已經(jīng)開始認(rèn)同并接受容器技術(shù),并意識到它能夠解決多種現(xiàn)實問題并具備一系列無可比擬的優(yōu)勢。今天小數(shù)就和大家聊一聊容器技術(shù)的優(yōu)勢和誤...

    Gu_Yan 評論0 收藏0
  • 貓頭鷹深夜翻譯:持久化容器存儲

    摘要:如果我們的容器使用,文件如下在這個例子中,我們可以重復(fù)創(chuàng)建和銷毀,同一個持久存儲會被提供給新的,無論容器位于哪個節(jié)點上。 前言 臨時性存儲是容器的一個很大的買點。根據(jù)一個鏡像啟動容器,隨意變更,然后停止變更重啟一個容器。你看,一個全新的文件系統(tǒng)又誕生了。 在docker的語境下: # docker run -it centos [root@d42876f95c6a /]# echo H...

    tianhang 評論0 收藏0
  • 貓頭鷹深夜翻譯:持久化容器存儲

    摘要:如果我們的容器使用,文件如下在這個例子中,我們可以重復(fù)創(chuàng)建和銷毀,同一個持久存儲會被提供給新的,無論容器位于哪個節(jié)點上。 前言 臨時性存儲是容器的一個很大的買點。根據(jù)一個鏡像啟動容器,隨意變更,然后停止變更重啟一個容器。你看,一個全新的文件系統(tǒng)又誕生了。 在docker的語境下: # docker run -it centos [root@d42876f95c6a /]# echo H...

    xiao7cn 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<