Linux Network Namespace 和 Veth Pair 简介

What & Why


我们平时使用的 network interface 和 路由规则等是在整个操作系统共享的, 当然我们有些场景并想这样.
当这些共享的东西很多时, 我们想要分类和隔离, 让他们之间尽可能少的互相影响.

在 Linux 中, Network Namespace 正是我们需要的. 它提供了一个完全独立的网络协议栈, 包括了 network interface / IPv4 / IPv4 / 路由表 / iptables 等, 这个特性也是许多虚拟化和容器化技术的基础.

如何使用 Network Namespace


那么如果使用 Network Namespace 呢? 使用 ip 工具集.

查看帮助:

ip netns help
Usage: ip netns list
ip netns add NAME
ip netns delete NAME
ip netns identify PID
ip netns pids NAME
ip netns exec NAME cmd ...
ip netns monitor

创建:

ip netns add nstest1

在某 namespace 中执行命令:

ip netns exec nstest1 <cmd>

或者, 进入 namespace 的 bash shell

ip netns exec nstest1 bash

创建之后, 通过 ip 命令可以看到里边只有一个 lo 设备:

ip netns exec nstest1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
ip netns exec nstest1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

设备是可以转移到不同 namespace 的, 不过有些则不可以.

ethtool -k docker0 | grep netns
netns-local: on [fixed]

如上, 如果 netns-local: on 则说明是不可转移的.

如何通信 & Veth Pair


创建了一些 Network Namespace, 理所当然的, 我们需要他们之间能够互相通信.

Linux 引入了 Veth Pair 来解决这个问题, 顾名思义, veth 是成对出现的, 好比一对用网线连着的网卡, 用来连接两个 namespace.

接下来我们来实际操作一下.

创建 veth pair:

$ ip link add vethtest0 type veth peer name vethtest1
$ sudo ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
5: vethtest1@vethtest0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether ba:97:41:e5:5e:4b brd ff:ff:ff:ff:ff:ff
6: vethtest0@vethtest1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether de:56:07:73:83:f1 brd ff:ff:ff:ff:ff:ff

如上, 我们可看到当前 namespace 中已经创建了两个 veth .
如果我们要让 root namespace 和 nstest1 namespace 之间能够通信, 我们需要把 veth pair 中的一个放到 nstest1 中:

ip link set vethtest1 netns nstest1

可以看到 vethtest1 已经被移至其他 nstest1 namespace, 但是 index id 是保留的.

#
$ ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
6: vethtest0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether de:56:07:73:83:f1 brd ff:ff:ff:ff:ff:ff

#
$ sudo ip netns exec nstest1 ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: vethtest1@if6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether ba:97:41:e5:5e:4b brd ff:ff:ff:ff:ff:ff

分配 IP 给 vethtest1 :

-> % sudo ip netns exec nstest1 ip addr add 10.1.2.1/24 dev vethtest1

-> % sudo ip netns exec nstest1 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
5: vethtest1@if6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether ba:97:41:e5:5e:4b brd ff:ff:ff:ff:ff:ff
inet 10.1.2.1/24 scope global vethtest1
valid_lft forever preferred_lft forever

分配 IP 给 vethtest0 :

-> % sudo ip addr add 10.1.2.2/24 dev vethtest0    

-> % sudo 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
6: vethtest0@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether de:56:07:73:83:f1 brd ff:ff:ff:ff:ff:ff
inet 10.1.2.2/24 scope global vethtest0
valid_lft forever preferred_lft forever

启动 veth:

-> % sudo ip netns exec nstest1 ip link set dev vethtest1 up
-> % sudo ip link set dev vethtest0 up

现在可以互相通信了.

-> % ping 10.1.2.1
PING 10.1.2.1 (10.1.2.1) 56(84) bytes of data.
64 bytes from 10.1.2.1: icmp_seq=1 ttl=64 time=0.120 ms
64 bytes from 10.1.2.1: icmp_seq=2 ttl=64 time=0.045 ms

以上就是 Linux Network Namespace 和 Veth Pair 的概要, 也是了解 Docker 和 Kubernetes 的网络实现时需要的基础知识.

References