程序员

「深入浅出」理解 Linux Network-Namespace

2020-12-14  本文已影响0人  聪明的奇瑞

Network-Namespace 是 Linux 内核提供的用于实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,该网络空间内的防火墙、网卡、路由表、邻居表、协议栈与外部都是独立的。不管是虚拟机还是容器,当运行在独立的命名空间时,就像是一台单独的主机一样。**

下面会通过一些例子来说明网络命名空间,以加深理解,会用到 iproute2 工具包的 ip 命令,需先自行安装,并且使用 root 权限操作。

  1. 在 Centos 下执行如下命令:
yum install iproute2  
  1. 验证安装完成:
[root@worker3 ~]# ip help
Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }
       ip [ -force ] -batch filename
where  OBJECT := { link | address | addrlabel | route | rule | neigh | ntable | tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm | netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila | vrf }
       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |
                    -h[uman-readable] | -iec |
                    -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |
                    -4 | -6 | -I | -D | -B | -0 |
                    -l[oops] { maximum-addr-flush-attempts } | -br[ief] |
                    -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |
                    -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]}

创建网络命名空间

ip 命令中用于操作网络命名空间的命令是 ip netns,用 help 来查看一下子命令有哪些

[root@worker3 ~]# 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-id

常用的也就是增删查命令,先来创建一个网络空间空间 ns1

ip netns add ns1

查看当前所有的网络命名空间

[root@worker3 ~]# ip netns list
ns1

在这有些人可能会很困惑,我主机上明明运行中好几个 docker 容器,按理说每个容器都运行在独立的网络命名空间,怎么这里没有列出来?不要着急,下面会提到。

先来感觉一下什么叫独立的网卡,独立的路由表,要查看 ns1 命名空间的网卡,iproute2 工具提供了命令 ip netns exec ns1,跟在这个命令后面的命令都会在这个网络命名空间中执行。

先查看一下主机的网卡和路由表

[root@worker3 ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 00:50:56:bb:ab:df brd ff:ff:ff:ff:ff:ff

[root@worker3 ~]# ip route
default via 10.57.4.1 dev eth0
10.1.2.0/24 dev br0 proto kernel scope link src 10.1.2.1

再看看 ns1 中的网卡和路由表

[root@worker3 ~]# ip netns exec ns1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    
[root@worker3 ~]# ip netns exec ns3 ip route

这样执行命令有点麻烦,也可以简单一点:

[root@worker3 ~]#ip netns exec ns1 bash
//这个命令后执行的命令就都是在ns1中执行了
[root@worker3 ~]#ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@worker3 ~]#ip route

exit 可以回到主机的默认空间。

ip netns add的原理

当我们在主机上执行 ip netns add ns1 后 ,实际是在 /var/run/netns 下创建了一个 ns1 的文件

[root@worker3 ~]# ls /var/run/netns
ns1

下面的命令可以模拟 ip netns add ns2 && ip netns exec ns2 bash

[root@worker3 ~]# touch /var/run/netns/ns2
[root@worker3 ~]# unshare --net bash
[root@worker3 ~]# mount --bin /proc/self/ns/net /var/run/netns/ns2
//上面的过程实际就是执行了ip netns add ns2 && ip netns exec ns2 bash

[root@worker3 ~]# ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

[root@worker3 ~]# exit
//退出,回到主机默认命名空间。用ip netns list查看,已经可以看到ns2
[root@worker3 ~]# ip netns list
ns2
ns1
//如果想再次进入ns2,还有一个方法:
[root@worker3 ~]# nsenter --net=/var/run/netns/ns2

从上面的示例可以看出,创建命名的 Network Namespace 其实就是创建一个文件,然后通过绑定挂载的方式将新创建的 Network Namespace 文件和进程的 /proc/self/ns/net 文件绑定。

查看容器的网络命名空间

接下来该回答上面的遗留问题,为什么当我在主机上 ip netns list 的时候看不到 docker 的网络命名空间?因为 ip netns list 的时候只会显示在 /var/run/netns 下的文件,而 docker 的文件默认是创建在 /var/run/docker/netns 下的。所以我们可以通过 ls /var/run/docker/netns 来显示当前的所有容器的网络命名空间,并且通过 nsenter --net=/var/run/docker/xxx 来进入容器的网络命名空间。

[root@worker3 ~]# ls /var/run/docker/netns
5bbd5f99d403  a2eabf9acccb  b63ec59b3d9e  d6e4ff961713  default
[root@worker3 ~]# nsenter --net=/var/run/docker/netns/b63ec59b3d9e
[root@worker3 ~]# 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
4: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether fa:a7:8d:05:03:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.0.11/32 scope global eth0
       valid_lft forever preferred_lft forever

如果想查看具体某个 docker 容器对应的文件,可以用:

docker inspect $CONTAINER_ID$|grep SandboxKey

注意如果是 K8S 拉起来的 docker,要拿非 hostNetwork=true 的 pause 容器来看。如果hostNetwork=true,那么下面的值为 /var/run/docker/netns/default,这是主机的默认网络命名空间。如果不是 pause 容器,那么下面的值为空,因为只有 pause 容器会创建一个新的网络命名空间,其它 container 都只是加入这个网络命名空间。

[root@worker3 ~]# docker inspect ebd6855901ef|grep SandboxKey
            "SandboxKey": "/var/run/docker/netns/b63ec59b3d9e",

还有另一个办法:

[root@worker3 ~]# docker inspect nginx|grep Pid
            "Pid": 31817,
            "PidMode": "",
            "PidsLimit": null,
[root@worker3 ~]# mkdir -p /var/run/netns/
[root@worker3 ~]# ln -s /proc/31817/ns/net /var/run/netns/ns100
[root@worker3 ~]# ip netns ls
ns100
ns2
ns1
[root@worker3 ~]# ip netns exec ns100 bash
[root@worker3 ~]# //这时候已经在容器网络里了,比nsenter还方便

这个小技巧在我们调试 Pod 的网络时非常有用,大多数时候 Pod 里面自带的工具非常少,没有 curl 没有 telnet,这时候用这个技巧先进入空器的网络空间,再执行命令就行了,因为只是切了网络命名空间,其它还在主机上,所以用的工具也全是主机的工具。

上一篇下一篇

猜你喜欢

热点阅读