docker的Namespace技术介绍
三、Linux Namespace 技术
当一个宿主机运行了 N 个容器,多个容器带来的以下问题怎么解决: 1.怎么样保证每个容器都有不同的文件系统并且能互不影响? 2.一个 docker 主进程内的各个容器都是其子进程,那么实现同一个主进程下不 同类型的子进程?各个进程间通信能相互访问(内存数据)吗? 3.每个容器怎么解决 IP 及端口分配的问题? 4.多个容器的主机名能一样吗? 5.每个容器都要不要有 root 用户?怎么解决账户重名问题? Linux Namespace 技术: namespace 是 Linux 系统的底层概念,在内核层实现,即有一些不同类型的命名 空间被部署在核内,各个 docker 容器运行在同一个 docker 主进程并且共用同一 个宿主机系统内核, 各 docker 容器运行在宿主机的用户空间,每个容器都要有 类似于虚拟机一样的相互隔离的运行空间,但是容器技术是在一个进程内实现 运行指定服务的运行环境, 并且还可以保护宿主机内核不受其他进程的干扰和 影响,如文件系统空间、网络空间、进程空间等,目前主要通过以下技术实现 容器运行空间的相互隔离: 隔离类型 功能 系统调用参数 内核版本 MNT Namespace(mount) 提供磁盘挂载点和文件系统的隔离能力 CLONE_NEWNS Linux 2.4.19 IPC Namespace(Inter-Process Communication) 提供进程间通信的隔离能力 CLONE_NEWIPC Linux 2.6.19 UTS Namespace(UNIX Timesharing System) 提供主机名隔离能力 CLONE_NEWUTS Linux 2.6.19 PID Namespace(Process Identification) 提供进程隔离能力 CLONE_NEWPID Linux 2.6.24 Net Namespace(network) 提供网络隔离能力 CLONE_NEWNET Linux 2.6.29 User Namespace(user) 提供用户隔离能力 CLONE_NEWUSER Linux 3.8
1.MNT Namespace
每个容器都要有独立的根文件系统有独立的用户空间,以实现在容器里面启动 服务并且使用容器的运行环境,即一个宿主机是 ubuntu 的服务器,
可以在里面 启动一个 centos 运行环境的容器并且在容器里面启动一个 Nginx 服务,此 Nginx 运行时使用的运行环境就是 centos 系统目录的运行环境,
但是在容器里面是不 能访问宿主机的资源,宿主机是使用了 chroot 技术把容器锁定到一个指定的运 行目录里面。
例如:/var/lib/containerd/容器 ID
[root@localhost7C ~]# docker info | grep "Docker Root Dir" WARNING: You're not using the default seccomp profile Docker Root Dir: /var/lib/docker [root@localhost7C ~]# docker run -d --name nginx2 -p 81:80 nginx [root@localhost7C ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5668e1f40b8e nginx "/docker-entrypoin..." 4 minutes ago Up 4 minutes 0.0.0.0:81->80/tcp nginx2 [root@localhost7C ~]# ll /var/lib/docker/containers 总用量 0 drwx------ 5 root root 168 10月 11 17:06 5668e1f40b8eea1ddc982a57b7f52069ba9d3e3dd1302856e3e14f4feb0d371d
[root@localhost7C ~]# docker exec -it nginx2 sh
# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
2。IPC Namespace:
一个容器内的进程间通信,允许一个容器内的不同进程的(内存、缓存等)数据访 问,但是不能夸容器访问其他容器的数据。
3。UTS Namespace: UTS namespace(UNIX Timesharing System 包含了运行内核的名称、版本、底层 体系结构类型等信息)用于系统标识,其中包含了 hostname 和域名 domainname ,
它使得一个容器拥有属于自己 hostname 标识,这个主机名标识 独立于宿主机系统和其上的其他容器。
[root@localhost7C ~]# docker exec -it nginx2 sh # cat /etc/issue Debian GNU/Linux 11 \n \l
# apt install procps (top 命令)
# apt install iputils-ping (ping 命令)
# apt install net-tools (网络工具)
# apt-get update Get:1 http://deb.debian.org/debian bullseye InRelease [116 kB] Get:2 http://deb.debian.org/debian-security bullseye-security InRelease [48.4 kB] Get:3 http://deb.debian.org/debian bullseye-updates InRelease [44.1 kB] Get:4 http://deb.debian.org/debian bullseye/main amd64 Packages [8184 kB] Get:5 http://deb.debian.org/debian-security bullseye-security/main amd64 Packages [189 kB] Get:6 http://deb.debian.org/debian bullseye-updates/main amd64 Packages [6344 B] Fetched 8587 kB in 2s (3687 kB/s) Reading package lists... Done # uname -a Linux 5668e1f40b8e 3.10.0-1062.el7.x86_64 #1 SMP Wed Aug 7 18:08:02 UTC 2019 x86_64 GNU/Linux
4。PID Namespace:
Linux 系统中,有一个 PID 为 1 的进程(init/systemd)是其他所有进程的父进程,那么在每个 容器内也要有一个父进程来管理其下属的子进程,
那么多个容器的进程通 PID namespace 进程 隔离(比如 PID 编号重复、器内的主进程生成与回收子进程等)。 例如:下图是在一个容器内使用 top 命令看到的 PID 为 1 的进程是 nginx:
dockerd: 被client直接访问,其父进程为宿主机的systemd守护进程。
docker-proxy: 每个进程docker-proxy实现对应一个需要网络通信的容器,管理宿主机和容器的之间端口映射,其父进程为dockerd,如果容器不需要网络则无需启动
containerd: 被dockerd进程调用以实现与runc交互
containerd-shim: 真正运行容器的载体,每个容器对应一个containerd-shim进程,其父进程为containerd
dockerf进程之间关系图:
老版本说明:
新版本说明:
5.Net Namespace:
每一个容器都类似于虚拟机一样有自己的网卡、监听端口、TCP/IP 协议栈等, Docker 使用 network namespace 启动一个 vethX 接口,这样你的容器将拥有它 自己的桥接 ip 地址,通常是 docker0,
而 docker0 实质就是 Linux 的虚拟网桥,网 桥是在 OSI 七层模型的数据链路层的网络设备,通过 mac 地址对网络进行划 分,并且在不同网络直接传递数据。
查看宿主机的网卡信息和 桥接设备:
[root@localhost7B ~]# ifconfig docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:9ff:fed6:cd79 prefixlen 64 scopeid 0x20<link> ether 02:42:09:d6:cd:79 txqueuelen 0 (Ethernet) RX packets 2473 bytes 102859 (100.4 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3062 bytes 10048794 (9.5 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 vethd305791: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::bc3a:f0ff:fed5:8eaa prefixlen 64 scopeid 0x20<link> ether be:3a:f0:d5:8e:aa txqueuelen 0 (Ethernet) RX packets 2473 bytes 137481 (134.2 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3057 bytes 10047634 (9.5 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 vethfdbd9e2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet6 fe80::b8df:b1ff:fe8b:b771 prefixlen 64 scopeid 0x20<link> ether ba:df:b1:8b:b7:71 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 22 bytes 2514 (2.4 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 virbr0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255 ether 52:54:00:ec:da:b8 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 [root@localhost7B ~]# [root@localhost7B ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.024209d6cd79 no vethd305791 vethfdbd9e2
virbr0 8000.525400ecdab8 yes virbr0-nic
6:User Namespace: 各个容器内可能会出现重名的用户和用户组名称,或重复的用户 UID 或者 GID,那么怎么隔离各个容器内的用户空间呢?
User Namespace 允许在各个宿主机的各个容器空间内创建相同的用户名以及相 同的用户 UID 和 GID,只是会把用户的作用范围限制在每个容器内,
即 A 容器 和 B 容器可以有相同的用户名称和 ID 的账户,但是此用户的有效范围仅是当前 容器内,不能访问另外一个容器内的文件系统,即相互隔离、互补影响、永不 相见
[root@localhost7B ~]# docker exec -it nginx1 bash root@41da1df38e16:/# root@41da1df38e16:/# root@41da1df38e16:/# id uid=0(root) gid=0(root) groups=0(root) root@41da1df38e16:/# cat /etc/passwd list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin ...
... nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin nginx:x:101:101:nginx user,,,:/nonexistent:/bin/false