容器(四)實現容器的底層技術【25】
(九)實現容器的底層技術
為了更好地理解容器的特性,本節我們將討論容器的底層實現技術。 cgroup 和 namespace 是最重要的兩種技術。cgroup 實現資源限額, namespace 實現資源隔離。
(1)cgroup
cgroup 全稱 Control Group。Linux 作業系統通過 cgroup 可以設定程序使用 CPU、記憶體 和 IO 資源的限額。相信你已經猜到了:前面我們看到的--cpu-shares
、-m
、--device-write-bps
實際上就是在配置 cgroup。
cgroup 到底長什麼樣子呢?我們可以在 /sys/fs/cgroup 中找到它。還是用例子來說明,啟動一個容器,設定--cpu-shares=512
root@cuiyongchao:~# docker run -it --cpu-shares 512 progrium/stress -c 1
stress: info: [1] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [6] forked
檢視容器ID:
root@cuiyongchao:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b473e4497b3b progrium/stress "/usr/bin/stress --v…" 17 seconds ago Up 16 seconds friendly_kepler
在 /sys/fs/cgroup/cpu/docker 目錄中,Linux 會為每個容器建立一個 cgroup 目錄,以容器長ID 命名:
root@cuiyongchao:~# ls /sys/fs/cgroup/cpu/docker/b473e4497b3b63c0ea4907857bcd4ef0ea3a455bce20f0715ecfeb5afd047bba/ cgroup.clone_children cpuacct.usage_all cpuacct.usage_sys cpu.shares cgroup.procs cpuacct.usage_percpu cpuacct.usage_user cpu.stat cpuacct.stat cpuacct.usage_percpu_sys cpu.cfs_period_us notify_on_release cpuacct.usage cpuacct.usage_percpu_user cpu.cfs_quota_us tasks root@cuiyongchao:~# cat /sys/fs/cgroup/cpu/docker/b473e4497b3b63c0ea4907857bcd4ef0ea3a455bce20f0715ecfeb5afd047bba/cpu.shares 512 root@cuiyongchao:~#
目錄中包含所有與 cpu 相關的 cgroup 配置,檔案 cpu.shares 儲存的就是 --cpu-shares
的配置,值為 512。同樣的,/sys/fs/cgroup/memory/docker 和 /sys/fs/cgroup/blkio/docker 中儲存的是記憶體以及 Block IO 的 cgroup 配置。
(2)namespace
在每個容器中,我們都可以看到檔案系統,網絡卡等資源,這些資源看上去是容器自己的。拿網絡卡來說,每個容器都會認為自己有一塊獨立的網絡卡,即使 host 上只有一塊物理網絡卡。這種方式非常好,它使得容器更像一個獨立的計算機。
Linux 實現這種方式的技術是 namespace。namespace 管理著 host 中全域性唯一的資源,並可以讓每個容器都覺得只有自己在使用它。換句話說,namespace 實現了容器間資源的隔離。Linux 使用了六種 namespace,分別對應六種資源:Mount、UTS、IPC、PID、Network 和 User,下面我們分別討論。
(3)Mount namespace
Mount namespace 讓容器看上去擁有整個檔案系統。容器有自己的 /
目錄,可以執行 mount
和 umount
命令。當然我們知道這些操作只在當前容器中生效,不會影響到 host 和其他容器。
(4)UTS namespace
簡單的說,UTS namespace 讓容器有自己的 hostname。 預設情況下,容器的 hostname 是它的短ID,可以通過-h
或--hostname
引數設定。
root@cuiyongchao:~# docker run -it -h zhangsan ubuntu
root@zhangsan:/# hostname
zhangsan
root@zhangsan:/#
(5)IPC namespace
IPC namespace 讓容器擁有自己的共享記憶體和訊號量(semaphore)來實現程序間通訊,而不會與 host 和其他容器的 IPC 混在一起。
(6)PID namespace
我們前面提到過,容器在 host 中以程序的形式執行。例如當前 host 中運行了兩個容器:
root@cuiyongchao:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c07fc518fbc ubuntu "/bin/bash" 29 minutes ago Up 29 minutes upbeat_knuth
b473e4497b3b progrium/stress "/usr/bin/stress --v…" 55 minutes ago Up 55 minutes friendly_kepler
137cc65e3025 progrium/stress:latest "/usr/bin/stress --v…" 14 hours ago Up 14 hours container_B
2d54da22fd5f progrium/stress:latest "/usr/bin/stress --v…" 14 hours ago Up 14 hours (Paused) container_A
root@cuiyongchao:~#
通過ps axf
可以檢視容器程序:
24709 ? Ssl 4:43 /usr/bin/containerd
88927 ? Sl 0:00 \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.cont
88948 pts/0 Ds+ 0:00 | \_ /usr/bin/stress --verbose --cpu 4
88990 pts/0 D+ 1:25 | \_ /usr/bin/stress --verbose --cpu 4
88991 pts/0 D+ 1:25 | \_ /usr/bin/stress --verbose --cpu 4
88992 pts/0 D+ 1:25 | \_ /usr/bin/stress --verbose --cpu 4
88993 pts/0 D+ 1:25 | \_ /usr/bin/stress --verbose --cpu 4
89014 ? Sl 0:00 \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.cont
89037 pts/0 Ss+ 0:00 | \_ /usr/bin/stress --verbose --cpu 4
89075 pts/0 R+ 139:11 | \_ /usr/bin/stress --verbose --cpu 4
89076 pts/0 R+ 139:22 | \_ /usr/bin/stress --verbose --cpu 4
89077 pts/0 R+ 138:13 | \_ /usr/bin/stress --verbose --cpu 4
89078 pts/0 R+ 136:01 | \_ /usr/bin/stress --verbose --cpu 4
90634 ? Sl 0:00 \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.cont
90659 pts/0 Ss+ 0:00 | \_ /usr/bin/stress --verbose -c 1
90701 pts/0 R+ 55:18 | \_ /usr/bin/stress --verbose -c 1
91164 ? Sl 0:00 \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.cont
91192 pts/0 Ss+ 0:00 \_ /bin/bash
27166 ? Ss 0:00 /usr/sbin/sshd -D
86477 ? Ss 0:00 \_ sshd: root@pts/3
86604 pts/3 Ss 0:00 | \_ -bash
88907 pts/3 Sl+ 0:00 | \_ docker run --name container_A -it -c 1024 progrium/stress:la
88307 ? Ss 0:00 \_ sshd: root@pts/4
88391 pts/4 Ss 0:00 | \_ -bash
88994 pts/4 Sl+ 0:00 | \_ docker run --name container_B -it -c 512 progrium/stress:lat
88502 ? Ss 0:00 \_ sshd: root@pts/5
88585 pts/5 Ss+ 0:00 | \_ -bash
88603 ? Ss 0:00 \_ sshd: root@pts/6
88686 pts/6 Ss+ 0:00 | \_ -bash
89700 ? Ss 0:00 \_ sshd: root@pts/7
89828 pts/7 Ss 0:00 | \_ -bash
90615 pts/7 Sl+ 0:00 | \_ docker run -it --cpu-shares 512 progrium/stress -c 1
90702 ? Ss 0:00 \_ sshd: root@pts/8
90782 pts/8 Ss 0:00 | \_ -bash
91146 pts/8 Sl+ 0:00 | \_ docker run -it -h zhangsan ubuntu
91249 ? Ss 0:00 \_ sshd: root@pts/0
91371 pts/0 Ss 0:00 \_ -bash
91397 pts/0 R+ 0:00 \_ ps -axf
28349 ? Ss 0:01 /lib/systemd/systemd --user
28350 ? S 0:00 \_ (sd-pam)
83248 ? Ss 0:00 /lib/systemd/systemd-networkd
87520 ? Ssl 0:02 /usr/bin/dockerd --insecure-registry 10.0.0.20:5000
root@cuiyongchao:~#
所有容器的程序都掛在 dockerd 程序下,同時也可以看到容器自己的子程序。 如果我們進入到某個容器,ps
就只能看到自己的程序了:
root@cuiyongchao:~# docker exec -it 1c07fc518fbc bash
root@zhangsan:/# ps axf
PID TTY STAT TIME COMMAND
16 pts/1 Ss 0:00 bash
25 pts/1 R+ 0:00 \_ ps axf
1 pts/0 Ss+ 0:00 /bin/bash
root@zhangsan:/#
而且程序的 PID 不同於 host 中對應程序的 PID,容器中 PID=1 的程序當然也不是 host 的 init 程序。也就是說:容器擁有自己獨立的一套 PID,這就是 PID namespace 提供的功能。
(7)Network namespace
Network namespace 讓容器擁有自己獨立的網絡卡、IP、路由等資源 。
(8)User namespace
User namespace 讓容器能夠管理自己的使用者,host 不能看到容器中建立的使用者。
root@cuiyongchao:~# docker run -it -h zhangsan ubuntu
root@zhangsan:/#
root@zhangsan:/# userzhangsan333
bash: userzhangsan333: command not found
root@zhangsan:/# useradd zhangsan333
root@zhangsan:/# exit
exit
root@cuiyongchao:~# su - zhangsan333
No passwd entry for user 'zhangsan333'
在容器中建立了使用者 cloudman,但 host 中並不會建立相應的使用者。
(9)小結
本章首先通過大量實驗學習了容器的各種操作以及容器狀態之間如何轉換,然後討論了限制容器使用 CPU、記憶體和 Block IO 的方法,最後學習了實現容器的底層技術:cgroup 和 namespace。
下面是容器的常用操作命令:
create 建立容器
run 執行容器
pause 暫停容器
unpause 取消暫停繼續執行容器
stop 傳送 SIGTERM 停止容器
kill 傳送 SIGKILL 快速停止容器
start 啟動容器
restart 重啟容器
attach attach 到容器啟動程序的終端
exec 在容器中啟動新程序,通常使用 "-it" 引數
logs 顯示容器啟動程序的控制檯輸出,用 "-f" 持續列印
rm 從磁碟中刪除容器