容器底层核心技术

namespace&&cgroup

1. Linux Namespace技术

#这里docker主进程就是守护进程

namespace是Linux系统的底层概念,在内核层实现,即有一些不同类型的命名空间被部署在内核中,各个docker容器运行在同一个docker主进程并且公用同一个宿主机系统内核,各docker容器运行在宿主机的用户空间,每个容器都要有类似于虚拟机一样的相互隔离的运行空间,但是容器技术是在一个进程内实现运行指定服务的运行环境,并且还可以保护宿主机内核不受其他进程的干扰和影响,如文件系统空间、网络空间、进程空间等,目前主要通过以下技术实现容器运行空间的相互隔离

1.1 MNT Namespace:

  • 文件系统隔离
每个容器都要有独立的根文件系统有独立的用户空间,以实现在容器里面启动服务并且使用容器的运行环境,即一个宿主机是ubuntu的服务器,可以在里面启动一个centos运行环境的容器并且在容器里面启动一个Nginx服务,此Nginx运行时使用的运行环境就是centos系统目录的运行环境,但是在容器里面是不能访问宿主机的资源,宿主机是使用了chroot技术把容器锁定到一个指定的运行目录里面
  • ubuntu系统运行centos镜像
root@ubuntu:~/docker# docker run -it --name centos centos:latest

#这个容器里面运行的文件系统是与ubuntu宿主机系统是隔离的
[root@3b9049d2ff1e /]# ls /
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

1.2 IPC Namespace

  • 进程间隔离
一个容器内的进程间通信,允许一个容器内的不同进程的(内存、缓存等)数据访问,但是不能夸容器访问其他容器的数据

1.3 UTS Namespace

  • 主机名域名隔离
UTS namespace包含了运行内核的名称、版本、底层体系结构类型等信息、用于系统标识,其中包含了hostname和域名,他使得一个容器拥有属于自己hostname标识,这个主机名标识独立于宿主机系统和其他容器。

1.4 PID Namespace

Linux系统中,有一个PID为1的进程(init/systemd)是其他所有进程的父进程,那么在每个容器内也要有一个父进程来管理其下属的子进程,那么多个容器的进程通过PID namespace进程隔离(比如PID编号重复、容器内的主进程生成与回收子进程等)
  • 例如:下图是一个容器内使用top命令看到的PID为1的进程是nginx
root@b274984fe669:/# top
top - 04:03:02 up  2:34,  0 users,  load average: 0.10, 0.05, 0.01
Tasks:   4 total,   1 running,   3 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.2 us,  0.2 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  2006372 total,   229556 free,   522384 used,  1254432 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1303580 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    364 root      20   0   41036   3136   2660 R   0.3  0.2   0:00.02 top
      1 root      20   0   32628   4964   4288 S   0.0  0.2   0:00.05 nginx
      6 nginx     20   0   33108   2536   1500 S   0.0  0.1   0:00.00 nginx
      7 root      20   0   18128   3196   2712 S   0.0  0.2   0:00.05 bash

容器内nginx的master进程与worker进程:

root@b274984fe669:/# ps -ef | grep nginx
root           1       0  0 02:10 ?        00:00:00 nginx: master process nginx -g daemon off;
nginx          6       1  0 02:10 ?        00:00:00 nginx: worker process
  • 那么宿主机的PID究竟与容器内的PID是什么关系?

查看宿主机的PID信息

  • 方法就是找containerd与dockerd
root@ubuntu20:~#  ps -ef | grep docker
root        1091       1  0 09:29 ?        00:00:10 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root       89986    1091  0 10:10 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80
root       90066     863  0 10:10 ?        00:00:01 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/b274984fe669252c44bf26b1c6bf7c6e4a0634aa63b61441a41d3f814d6ca21c -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      100959    2763  0 10:11 pts/0    00:00:01 docker exec -it b274984fe669252c bash
root      118218    1091  0 10:41 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 81 -container-ip 172.17.0.3 -container-port 80
root      118226     863  0 10:41 ?        00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/50cd1523f7122c06f1b03f67f8ea1596b2ebfd41e63c67b47564bac5a771f998 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      118331    1091  0 10:41 ?        00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 82 -container-ip 172.17.0.4 -container-port 80
root      118338     863  0 10:41 ?        00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/b4f67771ca1aba32789e0cb48a8c14a3685bbdc124814c91914b4ad634acfdaa -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      120526  117130  0 12:05 pts/1    00:00:00 grep --color=auto docker
  • 查看其中的863PID 找containerd-shim 它会调用runc在运行服务
root@ubuntu20:~# ps -ef | grep 863
root         863       1  0 09:29 ?        00:00:42 /usr/bin/containerd
root       90066     863  0 10:10 ?        00:00:01 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/b274984fe669252c44bf26b1c6bf7c6e4a0634aa63b61441a41d3f814d6ca21c -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      118226     863  0 10:41 ?        00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/50cd1523f7122c06f1b03f67f8ea1596b2ebfd41e63c67b47564bac5a771f998 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      118338     863  0 10:41 ?        00:00:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/b4f67771ca1aba32789e0cb48a8c14a3685bbdc124814c91914b4ad634acfdaa -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root      120607  117130  0 12:09 pts/1    00:00:00 grep --color=auto 863

  • 在查看对应的父进程
root@ubuntu20:~# ps -ef | grep 90066
root       90066     863  0 10:10 ?        00:00:01 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/b274984fe669252c44bf26b1c6bf7c6e4a0634aa63b61441a41d3f814d6ca21c -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root       90111   90066  0 10:10 ?        00:00:00 nginx: master process nginx -g daemon off;
root      100980   90066  0 10:11 pts/0    00:00:00 bash
root      120778  117130  0 12:15 pts/1    00:00:00 grep --color=auto 90066
  • 整体的进程信息
root@ubuntu20:~# pstree -p
systemd(1)─┬─VGAuthService(103221)
           ├─accounts-daemon(61235)─┬─{accounts-daemon}(61236)
           │                        └─{accounts-daemon}(61238)
           ├─agetty(879)
           ├─atd(862)
           ├─blkmapd(526)
           ├─containerd(863)─┬─containerd-shim(90066)─┬─bash(100980)
           │                 │                        ├─nginx(90111)───nginx(90148)
           │                 │                        ├─{containerd-shim}(90093)
           │                 │                        ├─{containerd-shim}(90094)
           │                 │                        ├─{containerd-shim}(90095)
           │                 │                        ├─{containerd-shim}(90096)
           │                 │                        ├─{containerd-shim}(90099)
           │                 │                        ├─{containerd-shim}(90100)
           │                 │                        ├─{containerd-shim}(90101)
           │                 │                        └─{containerd-shim}(100060)
           │                 ├─containerd-shim(118226)─┬─nginx(118252)───nginx(118298)
           │                 │                         ├─{containerd-shim}(118227)
           │                 │                         ├─{containerd-shim}(118228)
           │                 │                         ├─{containerd-shim}(118229)
           │                 │                         ├─{containerd-shim}(118230)
           │                 │                         ├─{containerd-shim}(118231)
           │                 │                         ├─{containerd-shim}(118232)
           │                 │                         ├─{containerd-shim}(118233)
           │                 │                         └─{containerd-shim}(118418)
           │                 ├─containerd-shim(118338)─┬─nginx(118358)───nginx(118410)
           │                 │                         ├─{containerd-shim}(118339)
           │                 │                         ├─{containerd-shim}(118340)
           │                 │                         ├─{containerd-shim}(118341)
           │                 │                         ├─{containerd-shim}(118342)
           │                 │                         ├─{containerd-shim}(118343)
           │                 │                         ├─{containerd-shim}(118344)
           │                 │                         ├─{containerd-shim}(118345)
           │                 │                         └─{containerd-shim}(118346)

           ─dockerd(1091)─┬─docker-proxy(89986)─┬─-{docker-proxy}(89989)
           │               │                     ├─{docker-proxy}(89990)
           │               │                     ├─{docker-proxy}(89991)
           │               │                     ├─{docker-proxy}(89992)
           │               │                     └─{docker-proxy}(89993)
           │               ├─docker-proxy(118218)─┬─{docker-proxy}(118220)
           │               │                      ├─{docker-proxy}(118221)
           │               │                      ├─{docker-proxy}(118222)
           │               │                      ├─{docker-proxy}(118223)
           │               │                      ├─{docker-proxy}(118224)
           │               │                      └─{docker-proxy}(118225)
           │               ├─docker-proxy(118331)─┬─{docker-proxy}(118332)
           │               │                      ├─{docker-proxy}(118333)
           │               │                      ├─{docker-proxy}(118334)
           │               │                      ├─{docker-proxy}(118335)
           │               │                      ├─{docker-proxy}(118336)
           │               │                      └─{docker-proxy}(118337)

1.5 Net Namespace

  • 网络隔离
每个容器都类似于虚拟机一样有自己的网卡、监听端口、TCP/IP协议栈等,Docker使用network namespace启动一个vethX接口(就是网络接口),这样你的容器将拥有它自己的桥接IP地址,通常是docker0,而docker0实质就是Linux的虚拟网桥,网桥是在OSI七层模型的数据链路层的网络设备,通过mac地址对网络进行划分,并且在不同网络直接传递数据
  • 查看宿主机网卡信息
#为了明显先运行三个容器

[root@docker /]# docker run -d --name nginx-v1 -p 80:80 nginx:latest
f3cc3fcb041acb7076db44b8ff2a142b557c75efd3b4aa41c6a599f3d8d6ae9d
[root@docker /]# docker run -d --name nginx-v2 -p 81:80 nginx:latest
82f52b85d86a9332b4d54168e76d2112bb8cafcdbbd1818274ec65a856da0b74
[root@docker /]# docker run -d --name nginx-v3 -p 82:80 nginx:latest

  • 差宿主机桥接设备

通过brctl show命令查看桥接设备

#这样就可以看到怎么桥接的了 很明显名字都是对应的
[root@docker /]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242f271c208	no		veth638dbc6
							            vethb20ce92
							            vethf6af257
  • 网络桥接图

  • 网络转换需要提前优化内核参数,开启ipv4.ip_forward

  • 开启了之后才可以容器对外访问或者跨界点

root@ubuntu20:~# cat /etc/sysctl.conf
net.ipv4.ip_forward=1
vm.max_map_count=262144
kernel.pid_max=4194303
fs.file-max=1000000
net.ipv4.tcp_max_tw_buckets=6000
net.netfilter.nf_conntrack_max=2097152
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
  • 宿主机iptables规则

1.6 User Namespace

  • 用户用户组隔离
各个容器内可能会出现重名的用户和用户组名称,或重复的用户UID或者GID,那么怎么隔离各个容器内的用户空间呢?
User Namespace允许在各个宿主机的各个容器空间内创建相同的用户名以及相同的用户UID和GID,只是会把用户的作用范围限制在每个容器内,即A容器和B容器可以有相同的用户名称和ID的账户,但是此用户的有效范围仅是当前容器内,不能访问另外一个容器内的文件系统,即相互隔离,户部影响。

2. Linux control groups

在一个容器,如果不对其做任何资源限制,则宿主机会允许其占用无限大的内存空间,有时候会因为代码bug程序会一直申请内存,直到把宿主机内存占完,为了避免此类的问题出现,宿主机有必要对容器进行资源分配限制,比如CPU、内存等,Linux Cgroups的全称是Linux Control Groups,它最主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等。此外,还能够对进程进行优先级设置,以及将进程挂起和回复等操作
  • Centos 7.9.2009 cgroups

  • ubuntu cgroups

  • cgroups中内存模块

[root@docker /]# cat /boot/config-3.10.0-1160.el7.x86_64 | grep MEM | grep CG
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_MEMCG_KMEM=y
  • cgroups具体实现
blkio: 块设备IO限制
cpu: 使用调度程序为cgroup任务提供cpu的访问
cpuacct: 产生cgroup任务的cpu资源报告
cpuset: 如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存
devices: 允许或拒绝cgroup任务对设备的访问
freezer: 暂停或恢复cgroup任务
memory: 设置每个cgroup的内存限制以及产生内存资源报告
net_cls: 标记每个网络包以供cgroup方便使用
ns: 命名空间子系统
perf_event:增加了对每group的检测跟踪的能力,可以检测属于某个特定的group的所有线程以及运行在特定CPU上的线程
  • 查看系统cgroups
[root@docker /]# ll /sys/fs/cgroup/
total 0
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 blkio
lrwxrwxrwx. 1 root root 11 Nov 15 02:57 cpu -> cpu,cpuacct
lrwxrwxrwx. 1 root root 11 Nov 15 02:57 cpuacct -> cpu,cpuacct
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 cpu,cpuacct
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 cpuset
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 devices
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 freezer
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 hugetlb
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 memory
lrwxrwxrwx. 1 root root 16 Nov 15 02:57 net_cls -> net_cls,net_prio
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 net_cls,net_prio
lrwxrwxrwx. 1 root root 16 Nov 15 02:57 net_prio -> net_cls,net_prio
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 perf_event
dr-xr-xr-x. 2 root root  0 Nov 15 02:57 pids
dr-xr-xr-x. 4 root root  0 Nov 15 02:57 systemd

有了chroot、namespace、cgroup就具备了基础的容器运行环境,但还需要相应的容器创建与删除的管理工具、以及怎样把容器运行起来、容器的数据处理、启动与关闭等问题,于是容器管理技术就出现了。

posted @ 2022-11-15 16:18  YIDADA-SRE  阅读(364)  评论(0)    收藏  举报