文章目录
相关文章:
Linux ip netns 命令
如下操作都是在centos7上执行,通过cat /proc/version查看系统信息。
1. 什么是Linux namespace
linux namespaces是Linux提供的一种内核级别环境隔离的方法,也是Container环境隔离的底层技术,Linux Namespaces共有如下种类
分类 | 系统调用参数 | 相关内核版本 |
---|---|---|
Mount namespaces | CLONE_NEWNS | Linux 2.4.19 |
UTS namespaces | CLONE_NEWUTS | Linux 2.6.19 |
IPC namespaces | CLONE_NEWIPC | Linux 2.6.19 |
PID namespaces | CLONE_NEWPID | Linux 2.6.24 |
Network namespaces | CLONE_NEWNET | Linux 2.6.29 |
User namespaces | CLONE_NEWUSER | Linux 3.8 |
Mount
: mount表空间隔离,配合chroot系统调用,使程序有自己的文件系统UTS
: hostname(本地主机名)和doaminname隔离,使应用有自己独立的主机名
假设分配UTS1、UTS2给2个进程P1、P2,那么P1看到的本地主机名H1,P2看到的本地主机名H2。放佛P1、P2是运行在2个不同的机器上一样。IPC
: 隔离进程间通讯PID
: 使程序包括子程序构造一个独立的程序集,最先创建的程序为1号pidNetwork
: 网络空间隔离,支持在此空间的程序拥有独立的网络栈。
一个进程属于什么Network命名空间,决定了运行在进程里的应用程序能看见什么网络端口。每个网络接口属于一个命名空间,但是可以从一个命名空间转移到另一个。每个容器都使用属于它自己的网络命名空间,因此每个容器技能看到它自己的一组网络接口。User
: 此空间下有独立的uid/gid系统
因此,一个容器拥有一组命名空间,即Mount1、UTS1、IPC1、PID1、Network1、User1组合。
Namespaces操作的可以通过三个系统调用完成clone(),unshare(),setns(),默认情况下子进程继承新父进程的namespaces
2. 探索namespaces
2.1 查询本机所有namespaces
2.1.1 通过lsns命令查看全部ns
❯ sudo lsns
NS TYPE NPROCS PID USER COMMAND
4026531836 pid 317 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531837 user 318 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531838 uts 317 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531839 ipc 317 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531840 mnt 312 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531856 mnt 1 91 root kdevtmpfs
4026531956 net 317 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026532215 mnt 1 5127 chrony /usr/sbin/chronyd
4026532216 mnt 2 5120 root /usr/sbin/NetworkManager --no-daemon
4026532219 mnt 1 6264 root /usr/bin/simple
4026532220 uts 1 6264 root /usr/bin/simple
4026532221 ipc 1 6264 root /usr/bin/simple
4026532222 pid 1 6264 root /usr/bin/simple
4026532224 net 1 6264 root /usr/bin/simple
4026532281 mnt 1 5438 root /usr/sbin/cupsd -f
lsns -t net
可以过滤出 network相关的命名空间
2.1.2 通过查看proc文件系统查看全部ns
每个进程可能有一组 命名空间
查看某个进程的下的命名空间
# ls -tl /proc/27293/ns/
total 0
lrwxrwxrwx 1 root root 0 Apr 12 15:49 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Apr 12 13:59 ipc -> ipc:[4026532452]
lrwxrwxrwx 1 root root 0 Apr 12 13:59 mnt -> mnt:[4026532450]
lrwxrwxrwx 1 root root 0 Apr 12 13:59 net -> net:[4026532455]
lrwxrwxrwx 1 root root 0 Apr 12 13:59 pid -> pid:[4026532453]
lrwxrwxrwx 1 root root 0 Apr 12 13:59 uts -> uts:[4026532451]
全部进程的命名空间
❯ sudo ls -l /proc/*/ns/ |awk '{print $11}' |sort -u
ipc:[4026531839]
ipc:[4026532221]
mnt:[4026531840]
mnt:[4026531856]
mnt:[4026532215]
mnt:[4026532216]
mnt:[4026532219]
mnt:[4026532281]
net:[4026531956]
net:[4026532224]
pid:[4026531836]
pid:[4026532222]
user:[4026531837]
uts:[4026531838]
uts:[4026532220]
2.2 查看某个docker 容器的namespaces
>sudo docker inspect -f "{{.State.Pid}}" 34b48c1d3093 //34b48c1d3093是容器id
>10427 //查询容器对应在主机上的pid
> sudo ls -l /proc/10427/ns //查看进程下的 ns
lrwxrwxrwx 1 root root 0 Dec 11 16:21 ipc -> ipc:[4026533018]
lrwxrwxrwx 1 root root 0 Dec 11 16:21 mnt -> mnt:[4026533016]
lrwxrwxrwx 1 root root 0 Dec 11 16:21 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Dec 11 16:21 pid -> pid:[4026533019]
lrwxrwxrwx 1 root root 0 Dec 11 16:21 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Dec 11 16:21 uts -> uts:[4026533017]
- 首先查找到容器id
- 根据容器id查找容器对应的pid
- 根据pid,查找 /proc/{pid}/ns路径下的命名空间
从结果看,一个容器对应6个命名空间,类型不重复,正好验证了我们之前的结论 “一个容器拥有一组命名空间,即Mount1、UTS1、IPC1、PID1、Network1、User1组合”。
2.3 进入命名空间命令nsenter
nsenter作用是查看某个命令空间内的信息。
nsenter的使用帮忙如下:
Usage:
nsenter [options] <program> [<argument>...]
Run a program with namespaces of other processes.
Options:
-t, --target <pid> target process to get namespaces from
-m, --mount[=<file>] enter mount namespace
-u, --uts[=<file>] enter UTS namespace (hostname etc)
-i, --ipc[=<file>] enter System V IPC namespace
-n, --net[=<file>] enter network namespace
-p, --pid[=<file>] enter pid namespace
-U, --user[=<file>] enter user namespace
-S, --setuid <uid> set uid in entered namespace
-G, --setgid <gid> set gid in entered namespace
<span> </span>--preserve-credentials do not touch uids or gids
-r, --root[=<*dir*>] set the root directory
-w, --wd[=<*dir*>] set the working directory
-F, --no-fork do not fork before exec'ing <program>
-Z, --follow-context set SELinux context according to --target PID
-h, --help display this help and exit
-V, --version output version information and exit
For more details see nsenter(1).
参数解释:
- t 基本每个命令都要带的
- m n p u U 参数是
互斥
的,因为一个docker映射一组命名空间,你只能进去其中的一个空间
操作特定进程命名空间中的信息,容器就是一个命名空间
进入网络命名空间
,查看进程网卡信息,10427 是一个docker的pid (docker inspect 中的pid字段):
❯ sudo nsenter -t 10427 -n ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
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
66: eth0@if67: <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
也可以通过 nsenter -t 10427 -n bash 进入network的命名空间,单独执行ip addr命令
查看容器的Host信息(主机名)
❯ sudo nsenter -t 10427 -u hostname
d911aa5d1a1c //hostname
等价于进入容器后,执行hostname命令
>docker exec -it 34b48c1d3093 sh //进入容器
>/home/zenap # hostname
d911aa5d1a1c //hostname
3. namespace相关系统调用
clone
创建新进程,flags参数可以用来创建新的namespace
setns
让进程加入存在的namespace
unshare
将调用进程移动到新的namespace中
ioctl_ns
查看namespace的信息
4. 转移设备
我们可以在不同的 Network Namespace 之间转移设备(如veth)。由于一个设备只能属于一个 Network Namespace ,所以转移后在这个 Network Namespace 内就看不到
这个设备了。
其中,veth设备属于可转移设备,而很多其它设备(如lo、vxlan、ppp、bridge等)是不可以转移的。
假设主机上有个veth199网卡,当执行下面命令后,在主机的default空间下,就看不到 了:
ip link set veth199 netns netns199 //把veth199 加入到这个新建的空间
你可以进入netns199 中进行查看
5. docker与 主机互找 veth对端
步骤1. 下面是查询docker ae1872516331 对应主机上的pid
$ docker inspect --format '{{.State.Pid}}' ae1872516331
849053
可以看到 pid =849053
步骤2. 根据pid,查看网络命名空间
$ ls -l /proc/849053/ns/net
lrwxrwxrwx 1 root root 0 Jul 10 14:47 /proc/849053/ns/net -> 'net:[4026542776]'
可以看到 网络命名空间的标识是 4026542776
步骤3. 根据这个网络标识,去查看 pid
$ lsns -t net |grep 4026542776
4026542776 net 10 152598 root 91 /run/docker/netns/89f768f4e3ad sleep 1800
这里面的 pid 是152598 , 请问 152598 和步骤1中的pid 849053 是什么关系 ?
我用ps -ef|grep 152598 没搜到进程
答:一般理论分析,应该是同一个pid,不知道为什么实际上是2个pid,并且152598 会变,而849053 不变
chartgpt反馈说,有可能152598 是一个管理ns的进程
注意:pid的值一直在变,这会变成248516 了:
$ lsns -t net |grep 4026542776
4026542776 net 10 248516 65535 91 /run/docker/netns/89f768f4e3ad /pause
也就是说 lsns -t net 命令里面的pid 不能和 docker inspect 中的 pid 划等号。
总结:
1)如果从docker 内 ip a 作为源头,想找到某个虚拟网口对应主机的,比较简单
/home/zenap # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
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
253: eth0@if252: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue state UP qlen 1000
link/ether d6:30:1b:47:dd:2f brd ff:ff:ff:ff:ff:ff
inet 100.100.2.201/16 brd 100.100.255.255 scope global eth0 没有link-netnsid,说明是default命名空间
valid_lft forever preferred_lft forever
inet6 fd01::2c9/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::d430:1bff:fe47:dd2f/64 scope link
valid_lft forever preferred_lft forever
直接 在主机上 ip a|grep 252即可
2)如果从主机 上ip a 作为源头,想找到对端是哪个docker 的(当然也不一定是docker的),稍微复杂
下面是主机的一个信息:
252: vethOv02n33b@if253: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether a6:f7:33:b5:39:d8 brd ff:ff:ff:ff:ff:ff link-netnsid 91
inet6 fe80::a4f7:33ff:feb5:39d8/64 scope link
valid_lft forever preferred_lft forever
直接 根据 link-netnsid 91 作为条件
ls -t net | grep 91 ,找到 网络命名空间的唯一标识符 4026542776 ,
然后写一个脚本,去/proc/[pid]/ns/net下扫描, 找到 值为 4026542776 的,此时[pid] 就是docker的pid,
再去所有的docker中捞pid为[pid]的那个