对于容器来说,可以与host共享网络名字空间,也可以用单独的网络名字空间。这个可以通过container的配置文件来指定。

如果需要用单独的网络名字空间,可以指定容器的网络类型为veth:

lxc.network.type = veth

如果要跟host共享网络名字空间,那么可以指定容器的网络类型为none

lxc.network.type = none

  

在host上面,作为root用户,是有权限访问所有网络名字空间的数据的,但是大部分程序默认只是访问host所在的网络名字空间。如果我们想访问容器的网络名字空间的数据,那该怎么做呢?

 

下面讲一下ip netns的用法。

[router] / # 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
[router] / #

  

如果我们在host上面创建运行了多个容器,那么就应该会创建多个网络名字空间,用ip netns list应该可以列出来,但是在linux 4.4的host系统上面,我们发现这个命令显示不出任何信息。

 

实际上ip netns只能看到/var/run/netns里面的内容,但是对于lxc container来说,创建容器时,不会将网络名字空间加到这个目录。如果要使用ip netns,需要我们手动加入。

 

举例来说,比方说我们现在有个进程(pid=16674)运行在某个容器中,可以通过该进程的proc目录获取该进程所在的网络名字空间:

[router] /proc/16674/ns # ls -la
dr-x--x--x    2 root     root             0 Mar 30 20:37 .
dr-xr-xr-x    8 root     root             0 Mar 30 04:11 ..
lrwxrwxrwx    1 root     root             0 Mar 30 20:37 ipc -> ipc:[4026532966]
lrwxrwxrwx    1 root     root             0 Mar 30 20:37 mnt -> mnt:[4026532964]
lrwxrwxrwx    1 root     root             0 Mar 30 20:37 net -> net:[4026532969]
lrwxrwxrwx    1 root     root             0 Mar 30 20:37 pid -> pid:[4026532967]
lrwxrwxrwx    1 root     root             0 Mar 30 20:37 user -> user:[4026531837]
lrwxrwxrwx    1 root     root             0 Mar 30 20:37 uts -> uts:[4026532965]

  

我们可以创建一个软链接如下:

[router] /proc/16674/ns # ln -s /proc/16674/ns/net /var/run/netns/ns1

  

再执行ip netns list就可以看到这个网络名字空间了:

[router] /proc/16674/ns # ip netns list
ns1 (id: 2)

  

那么下面我们就可以用ip netns访问某个网络名字空间的数据。下面我们在容器的网络名字空间里执行iptables可以显示该网络名字空间里面的规则:

 

[router] /proc/16674/ns # ip netns exec ns1 iptables -nvL
Chain INPUT (policy ACCEPT 96591 packets, 7177K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 95210 packets, 7046K bytes)
 pkts bytes target     prot opt in     out     source               destination

  

可以看到里面的chain都是空的,跟host里面的规则完全不同。

 

至于这种机制是如何实现的,我们可以简单了解下。我们先看看下面的系统接口:

  • clone():创建新的进程
  • setns():允许指定进程加入特定的namespace
  • unshare():将指定进程移除指定的namespace

可以看看setns这个API,他可以将某个进程加入到特定的namespace。那么我们知道linux支持如下几种namespace:

Namespace  变量 隔离资源
IPC
CLONE_NEWIPC
System V IPC, POSIX 消息队列等
Network
CLONE_NEWNET
网络设备,协议栈、端口等
Mount 
CLONE_NEWNS 
挂载点
PID
CLONE_NEWPID
进程ID
User
CLONE_NEWUSER 
用户和group ID
UTS  
CLONE_NEWUTS
Hostname和NIS域名
     

 

如果我们想让一个进程在某个网络名字空间里运行,我们可以创建一个空的父进程,通过setns来指定这个进程的网络名字空间,然后在父进程里fork出子进程来运行所要执行的程序,默认情况下,子进程会继承父进程的名字空间。那么这样就能让程序在指定的名字空间里面执行了。