前言
Docker 的底层原理是利用了 Linux 的 Namespace 和 Cgroups 技术,通过 Namespace 技术实现进程隔离,通过 Cgroups 技术实现容器进程可用资源的限制。
一、Namespace
1、Namespace简介
Namespace 是 Linux 内核提供的一个特性,namespace 将内核的全局资源进行封装,使得每个 namespace 都有一份独立的资源,因此不同进程在各自的 namespace 内对同一种资源的使用不会相互干扰。
用户运行在容器里的应用进程,跟宿主机上的其他进程一样,都由宿主机操作系统统一管理,只不过这些被隔离的进程拥有额外设置过的 Namespace 参数。
- Linux 内核提供了八种不同的 namespace:
###可以通过unshare命令帮助查看内核支持的namespace。
root@docker:~# unshare --help
Usage:
unshare [options] [<program> [<argument>...]]
Run a program with some namespaces unshared from the parent.
Options:
-m, --mount[=<file>] unshare mounts namespace
-u, --uts[=<file>] unshare UTS namespace (hostname etc)
-i, --ipc[=<file>] unshare System V IPC namespace
-n, --net[=<file>] unshare network namespace
-p, --pid[=<file>] unshare pid namespace
-U, --user[=<file>] unshare user namespace
-C, --cgroup[=<file>] unshare cgroup namespace
-T, --time[=<file>] unshare time namespace
...
2、Namespace隔离示例
###以交互模式运行一个centos容器,并在其中运行/bin/bash,使用--rm参数,容器关闭时自动删除。
root@docker:~# docker run -itd --rm centos /bin/bash
05126d3273f4977e62461160994e8cbc956dbe6e343b040407ceeabb6d58b1f6
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
05126d3273f4 centos "/bin/bash" 5 seconds ago Up 4 seconds boring_payne
###查看容器内进程。
root@docker:~# docker exec 05126d3273f4 ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:18 pts/0 00:00:00 /bin/bash
root 21 0 0 11:21 ? 00:00:00 ps -ef
###使用docker inspect查看容器进程在宿主机上的实际ID。
root@docker:~# docker inspect 05126d3273f4 | grep Pid
"Pid": 360783,
"PidMode": "",
"PidsLimit": null,
###查看宿主机上进程的信息。
root@docker:~# ps -ef|grep 360783
root 360783 360760 0 19:18 pts/0 00:00:00 /bin/bash
root 360989 341265 0 19:48 pts/2 00:00:00 grep --color=auto 360783
###分别在宿主机和容器查看容器进程的相关namespace信息,发现两个进程信息是一致的。
root@docker:~# ls -l /proc/360783/ns
total 0
lrwxrwxrwx 1 root root 0 May 28 19:21 cgroup -> 'cgroup:[4026532655]'
lrwxrwxrwx 1 root root 0 May 28 19:21 ipc -> 'ipc:[4026532595]'
lrwxrwxrwx 1 root root 0 May 28 19:21 mnt -> 'mnt:[4026532593]'
lrwxrwxrwx 1 root root 0 May 28 19:18 net -> 'net:[4026532597]'
lrwxrwxrwx 1 root root 0 May 28 19:21 pid -> 'pid:[4026532596]'
lrwxrwxrwx 1 root root 0 May 28 23:17 pid_for_children -> 'pid:[4026532596]'
lrwxrwxrwx 1 root root 0 May 28 23:17 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May 28 23:17 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May 28 23:17 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 May 28 19:21 uts -> 'uts:[4026532594]'
root@docker:~# docker exec -it 05126d3273f4 bash
[root@05126d3273f4 /]# ls -l /proc/1/ns
total 0
lrwxrwxrwx 1 root root 0 May 28 15:29 cgroup -> 'cgroup:[4026532655]'
lrwxrwxrwx 1 root root 0 May 28 15:29 ipc -> 'ipc:[4026532595]'
lrwxrwxrwx 1 root root 0 May 28 15:29 mnt -> 'mnt:[4026532593]'
lrwxrwxrwx 1 root root 0 May 28 15:29 net -> 'net:[4026532597]'
lrwxrwxrwx 1 root root 0 May 28 15:29 pid -> 'pid:[4026532596]'
lrwxrwxrwx 1 root root 0 May 28 15:29 pid_for_children -> 'pid:[4026532596]'
lrwxrwxrwx 1 root root 0 May 28 15:29 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May 28 15:29 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 May 28 15:29 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 May 28 15:29 uts -> 'uts:[4026532594]'
###因为有用户和用户组的隔离,宿主机和容器有各自自己的用户和群组,相互隔离,互不影响。
root@docker:~# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
demo:x:1000:1000:demo:/home/demo:/bin/bash
ftp:x:108:112:ftp daemon,,,:/srv/ftp:/usr/sbin/nologin
user7:x:1001:1001::/home/user7:/bin/sh
syslog:x:109:114::/home/syslog:/usr/sbin/nologin
uuidd:x:110:115::/run/uuidd:/usr/sbin/nologin
tcpdump:x:111:116::/nonexistent:/usr/sbin/nologin
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
root@docker:~# docker exec -it 05126d3273f4 bash
[root@05126d3273f4 /]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin
systemd-resolve:x:193:193:systemd Resolver:/:/sbin/nologin
二、Cgroups
1、Cgroups简介
Cgroups 是 control groups 的缩写, 它是 Linux 内核(2.6.24版本开始)提供的一个特性,通过这个功能可以限制应用对资源的使用,可以对系统资源做精细化控制。
Cgroups 主要提供了以下功能:
- Resource limitation(资源限制):限制资源的使用,CPU、内存等。
- Prioritization(优先级分配):应用的优先级控制。
- Accounting(资源统计):应用的审计和统计,可以统计资源的用量。
- Control(进程控制):实现对应用的控制,应用的挂起、恢复和执行等。
将一组进程放在一个 Cgroup 中,通过给这个 Cgroup 分配指定的可用资源,达到控制这一组进程可用资源的目的。
根据操作系统的发行版本,可以确定是否启用了 Linux CGroup。
###确定是否启用了Linux CGroup,参数值为y,表示已启用。
root@docker:~# cat /boot/config-$(uname -r) | grep CGROUP
CONFIG_CGROUPS=y
CONFIG_BLK_CGROUP=y
CONFIG_CGROUP_WRITEBACK=y
CONFIG_CGROUP_SCHED=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_RDMA=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_BPF=y
CONFIG_CGROUP_MISC=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_SOCK_CGROUP_DATA=y
CONFIG_BLK_CGROUP_RWSTAT=y
# CONFIG_BLK_CGROUP_IOLATENCY is not set
CONFIG_BLK_CGROUP_FC_APPID=y
CONFIG_BLK_CGROUP_IOCOST=y
CONFIG_BLK_CGROUP_IOPRIO=y
# CONFIG_BFQ_CGROUP_DEBUG is not set
CONFIG_NETFILTER_XT_MATCH_CGROUP=m
CONFIG_NET_CLS_CGROUP=m
CONFIG_CGROUP_NET_PRIO=y
CONFIG_CGROUP_NET_CLASSID=y
2、CPU资源限制
默认设置下,所有容器可以平等地使用宿主机的CPU资源并且没有限制。设置CPU资源的选项如下:
-c 或 --cpu-shares
该参数设置容器使用 CPU 的权重,如果不指定该参数,默认每个容器的权值为1024,通过该参数,可以设置容器使用 CPU 的优先级。
--cpus
从 v1.13版本开始,Docker 提供了--cpus 参数可以限定容器能使用的 CPU 核数,这个功能可以让我们更精确地设置容器 CPU 使用量,是一种更容易理解也常用的手段。
--cpuset-cpus限制容器运行在指定的CPU核心上。
####CPU权重:
###运行一个stress容器,设置CPU权重为1024,--cpu 4是stress容器的参数,我的虚拟机有4核心,所以为4,按自己的核心数设置。
[root@docker ~]# docker run --name stress1 -itd --rm -c 1024 progrium/stress --cpu 4
3417ec95d05a9f4fddc32e1e2fa84784d8ac9fa5067b3825f29476602a09ae44
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3417ec95d05a progrium/stress "/usr/bin/stress --v…" 5 seconds ago Up 4 seconds stress1
###使用TOP命令查看CPU使用情况,发现所有的核心CPU都已跑满。
[root@docker ~]# top
Tasks: 266 total, 5 running, 261 sleeping, 0 stopped, 0 zombie
%Cpu(s): 99.9 us, 0.1 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2870644 total, 1446380 free, 728800 used, 695464 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1959748 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3536 root 20 0 7304 100 0 R 100.0 0.0 1:33.12 stress
3537 root 20 0 7304 100 0 R 100.0 0.0 1:33.15 stress
3538 root 20 0 7304 100 0 R 100.0 0.0 1:33.23 stress
3535 root 20 0 7304 100 0 R 100.0 0.0 1:33.11 stress
...
...
###再运行一个stress0容器,设置CPU权重值为512。
[root@docker ~]# docker run --name stress2 -itd --rm -c 512 progrium/stress --cpu 4
5727f2bb3f1919a3767a3df4c405f54098d7dde70448e0835b46ac6ed621666b
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5727f2bb3f19 progrium/stress "/usr/bin/stress --v…" 4 seconds ago Up 3 seconds stress2
3417ec95d05a progrium/stress "/usr/bin/stress --v…" 2 minutes ago Up 2 minutes stress1
###使用TOP命令查看CPU使用情况,发现又产生了四个新的进程,CPU使用是之前进程的一半。
[root@docker ~]# top
Tasks: 270 total, 9 running, 261 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2870644 total, 1427136 free, 747048 used, 696460 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1941340 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3535 root 20 0 7304 100 0 R 66.7 0.0 26:43.44 stress
3536 root 20 0 7304 100 0 R 66.7 0.0 26:46.09 stress
3538 root 20 0 7304 100 0 R 66.7 0.0 26:44.27 stress
3537 root 20 0 7304 100 0 R 65.7 0.0 26:45.39 stress
3670 root 20 0 7304 92 0 R 35.3 0.0 12:07.88 stress
3669 root 20 0 7304 92 0 R 33.7 0.0 12:10.96 stress
3671 root 20 0 7304 92 0 R 33.0 0.0 12:08.90 stress
3668 root 20 0 7304 92 0 R 32.7 0.0 12:10.89 stress
...
...
###将stress1容器停掉,stress2容器也能占满CPU。
[root@docker ~]# docker stop stress1
stress1
[root@docker ~]# top
Tasks: 267 total, 5 running, 262 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2870644 total, 1431336 free, 742624 used, 696684 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1945868 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3669 root 20 0 7304 92 0 R 100.0 0.0 28:58.96 stress
3670 root 20 0 7304 92 0 R 100.0 0.0 28:54.07 stress
3671 root 20 0 7304 92 0 R 100.0 0.0 28:57.03 stress
3668 root 20 0 7304 92 0 R 100.0 0.0 28:58.83 stress
...
...
####CPU核心数:
###停掉之前运行的容器,再运行一个stress容器,限制CPU核心数为2,后面的--cpu 4是stress容器的参数,我的虚拟机有4核心,所以为4,按自己的核心数设置。
[root@docker ~]# docker run --name stress3 -itd --rm --cpus 2 progrium/stress --cpu 4
60bc8dfd1b1bda41d7860dc670390ad6855dc172f97996a49dd04b95713254f7
###使用top命令查看,top回车后,按1,可以显示每个核心的使用情况,每个CPU核心使用率50%,相当于使用了2核心。
[root@docker ~]# top
top - 15:40:17 up 2:47, 1 user, load average: 1.35, 2.31, 4.99
Tasks: 269 total, 5 running, 264 sleeping, 0 stopped, 0 zombie
%Cpu0 : 49.8 us, 0.0 sy, 0.0 ni, 50.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 49.8 us, 0.0 sy, 0.0 ni, 50.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 49.5 us, 0.0 sy, 0.0 ni, 50.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 50.3 us, 0.0 sy, 0.0 ni, 49.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2870644 total, 1432612 free, 741148 used, 696884 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1947148 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5517 root 20 0 7304 100 0 R 50.2 0.0 0:38.14 stress
5516 root 20 0 7304 100 0 R 49.8 0.0 0:38.22 stress
5518 root 20 0 7304 100 0 R 49.5 0.0 0:38.08 stress
5519 root 20 0 7304 100 0 R 49.5 0.0 0:38.03 stress
####限制使用指定的CPU核心。
###启动一个容器,限制使用第一个和第三个核心,从0开始算,所以是0和2。
[root@docker ~]# docker run --name stress4 -itd --rm --cpuset-cpus="0,2" progrium/stress --cpu 4
abcd730045431de50d52bcfbdb682cbf3d4fb39e0f16889fd81cc9398b255206
###使用top命令查看,第一个和第三个核心使用率100%。
[root@docker ~]# top
top - 16:19:35 up 3:26, 1 user, load average: 3.86, 2.16, 1.59
Tasks: 266 total, 7 running, 259 sleeping, 0 stopped, 0 zombie
%Cpu0 : 99.5 us, 0.5 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2870644 total, 1430388 free, 743524 used, 696732 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1944888 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6309 root 20 0 7304 96 0 R 50.5 0.0 1:42.59 stress
6307 root 20 0 7304 96 0 R 50.0 0.0 1:42.70 stress
6308 root 20 0 7304 96 0 R 50.0 0.0 1:42.83 stress
6310 root 20 0 7304 96 0 R 50.0 0.0 1:42.66 stress
...
...
3、内存资源限制
默认设置下,宿主机不限制容器对内存资源的使用,可使用如下参数控制容器对内存资源的使用:
-m 或 --memory
设置内存资源的使用限额,默认为 -1,即不限制。
--memory-swap
设置内存+swap资源使用的总限额,如果 --memory-swap 设置为与 --memory 相同的值,并且 --memory 为正整数,则该容器无权访问swap。如果在启动容器时,只指定 -m 而不指定 --memory-swap ,那么 --memory-swap 默认为 -m 的两倍。
###创建一个stress容器,设置最多使用100M内存和100M swap,开启一个进程循环分配和释放150M内存。
[root@docker ~]# docker run --name stress -itd --rm -m=100M --memory-swap=200M progrium/stress --vm 1 --vm-bytes 150M
bb49fb0e7c53a5d96b9dd9175aa0316977d1edf1644a9f20fe1297c069b7c2ba
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bb49fb0e7c53 progrium/stress "/usr/bin/stress --v…" 2 minutes ago Up 2 minutes stress
###使用top命令查看内存使用情况,
[root@docker ~]# top
top - 18:26:23 up 5:33, 2 users, load average: 1.06, 1.04, 0.83
Tasks: 264 total, 3 running, 261 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 19.0 sy, 0.0 ni, 72.5 id, 4.9 wa, 0.0 hi, 3.5 si, 0.0 st
KiB Mem : 2870644 total, 1282164 free, 890256 used, 698224 buff/cache
KiB Swap: 2097148 total, 1930492 free, 166656 used. 1797844 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8735 root 20 0 160908 98856 256 R 80.3 3.4 1:02.33 stress
...
...
总结
以上就是今天学习了解的内容。