Linux的Namespace[1]机制提供了一种资源隔离的解决方案,而目前Linux内核里面实现且支持的Namespace有7种,如下表:
名称定义说明CgroupCLONE_NEWCGROUPCgroup root directory (since Linux 4.6)IPCCLONE_NEWIPCSystem V IPC, POSIX message queues (since Linux 2.6.19)NetworkCLONE_NEWNETNetwork devices, stacks, ports, etc. (since Linux 2.6.24)MountCLONE_NEWNSMount points (since Linux 2.4.19)PIDCLONE_NEWPIDProcess IDs (since Linux 2.6.24)UserCLONE_NEWUSERUser and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)UTSCLONE_NEWUTSHostname and NIS domain name (since Linux 2.6.19)
但是今天只分析一下Network NameSpace以及管理,Network NameSpace是实现网络空间隔离或者说网络虚拟化的基础, 不管是Docker还是虚拟机技术上的程序也都是由Linux内核实现的Network NameSpace隔离开的命令空间内运行。ip netns就是管理命令空间的命令,在学习之前,先了解几个命令unshare、readlink、nsenter
运行一些与父级不共享的某些名称空间的程序。
root@node3:~# unshare --help Usage: unshare [options] <program> [<argument>...] Run a program with some namespaces unshared from the parent. Options: -h,--help 显示帮助文本并退出。 -i,-- ipc 取消共享IPC名称空间。 -m,-- mount 取消共享安装名称空间。 -n,-- net 取消共享网络名称空间。 -p,-- pid 取消共享pid名称空间。另请参见--fork和--mount-proc选项。 -u,-- uts 取消共享UTS名称空间。 -U,--user 取消共享用户名称空间。 -f,-将指定程序fork为取消共享的子进程,而不是直接运行它。这在创建新的pid名称空间时很有用。 --mount-proc [=mountpoint]在运行程序之前,将proc文件系统挂载到mountpoint (默认为/ proc)。这在创建新的pid名称空间时很有用。这也意味着创建一个新的挂载名称空间,因为/ proc挂载否则会破坏系统上的现有程序。新的proc文件系统显式安装为私有文件(由MS_PRIVATE | MS_REC)。 -r,-- map-root-user 仅在当前有效的用户和组ID已映射到新创建的用户名称空间中的超级用户UID和GID之后,才运行该程序。这样即使在没有特权的情况下运行,也可以方便地获得管理新创建的名称空间各个方面所需的功能(例如,在网络名称空间中配置接口或在安装名称空间中安装文件系统)。仅作为一项便利功能,它不支持更复杂的用例,例如映射多个范围的UID和GID。 For more details see unshare(1).readlink是Linux系统中一个常用工具,主要用来找出符号链接所指向的位置
root@node3:/etc# readlink /etc/resolv.conf ../run/resolvconf/resolv.conf一个可以在指定进程的命令空间下运行指定程序的命令。这个命令大家在容器网络调试下可能常用,比如在一些没有网络调试工具(ip address,ping,telnet,ss,tcpdump)的容器内利用宿主机上的命令进行容器内网络连通性的调试等等
root@node3:/etc# nsenter --help ...略 options: -t, --target pid:指定被进入命名空间的目标进程的pid -m, --mount[=file]:进入mount命令空间。如果指定了file,则进入file的命令空间 -u, --uts[=file]:进入uts命令空间。如果指定了file,则进入file的命令空间 -i, --ipc[=file]:进入ipc命令空间。如果指定了file,则进入file的命令空间 -n, --net[=file]:进入net命令空间。如果指定了file,则进入file的命令空间 -p, --pid[=file]:进入pid命令空间。如果指定了file,则进入file的命令空间 -U, --user[=file]:进入user命令空间。如果指定了file,则进入file的命令空间 -G, --setgid gid:设置运行程序的gid -S, --setuid uid:设置运行程序的uid -r, --root[=directory]:设置根目录 -w, --wd[=directory]:设置工作目录运行一个Demo演示一下
root@node3:/etc# docker run -d --name test nginx 31095893cbddd999a9f2d9e78cd4c057d4f5b3763fe1b5ecd5d68df4e9ce5943 root@node3:/etc# docker inspect -f {{.State.Pid}} test 26308 root@node3:/etc# nsenter -t 26308 -n root@node3:/etc# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever root@node3:/etc# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 root@node3:/etc# netstat -unlpt Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 26308/nginx: master tcp6 0 0 :::80 :::* LISTEN 26308/nginx: master root@node3:/etc# exit logout这样看起来是不是很清楚,以后排查容器网络感觉又方便不少了
前期准备工作做了不少,下面好好说说Network NameSpace如何通过ip netns进行管理
ip netns命令基本很少使用到,所以先来熟悉一番
root@node3:~# ip netns help Usage: ip netns list ip netns add NAME ip netns set NAME NETNSID ip [-all] netns delete [NAME] ip netns identify [PID] ip netns pids NAME ip [-all] netns exec [NAME] cmd ... ip netns monitor ip netns list-idLinux系统下默认是没有network namespace,我们可以通过ls或者ip netns查看
root@node3:~# ip netns list root@node3:~# ls -al /var/run/netns/ total 0 drwxr-xr-x 2 root root 40 Oct 6 05:07 . drwxr-xr-x 26 root root 1020 Oct 6 04:57 ..由ip netns add创建的network namespace会在/var/run/netns目录下创建一个同名的文件,
在创建了一个新的network namespace之后,很好奇里面究竟包含哪些东西,此时就可以通过exec进去查看,除了网络信息之外与宿主机是没有区别的
root@node3:~# ip netns add nsdemo3 root@node3:~# ip netns exec nsdemo3 ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 root@node3:~# ip netns exec nsdemo3 bash root@node3:~# ip addr # 每个network namespace都存在一个没有启用的lo本地回环网卡 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 root@node3:~# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface # 在进入nsdemo3这个network namespace之前,给nsdemo3的bash指定配置文件,用来修改bash前缀区分shell root@node3:~# ip netns exec nsdemo3 /bin/bash --rcfile <(echo "PS1=\"namespace nsdemo3> \"") namespace nsdemo3> ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 namespace nsdemo3> exit exit启用network namespace中的lo网卡
root@node3:~# ip netns del nsdemo3 root@node3:~# ip netns list root@node3:~# ip netns add nsdemo4 root@node3:~# ip netns list nsdemo4 root@node3:~# ip netns exec nsdemo4 /bin/bash --rcfile <(echo "PS1=\"namespace nsdemo4> \"") namespace nsdemo4> ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 namespace nsdemo4> ip link set lo up # 启用网卡lo namespace nsdemo4> ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever namespace nsdemo4> ping 192.168.99.128 # 查看与主机之间的网络状态,发现网络不同 connect: Network is unreachable namespace nsdemo4> exit exit root@node3:~# ip netns exec nsdemo4 /bin/bash --rcfile <(echo "PS1=\"namespace nsdemo4> \"") namespace nsdemo4> ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever默认情况下,隔离的网络命名空间是无法进行通信的,同样可以利用ip的子命令link创建veth和bridge类型的网卡实现两个或者多个隔离的Network NameSpace之间相互通信。这样更有助于理解容器网络之间的隔离与通信。
[1]
Linux Namespace: https://man7.org/linux/man-pages/man7/namespaces.7.html