heidsoft / cloud-bigdata-book

write book
56 stars 33 forks source link

linux veth虚拟网络实验步骤 #34

Open heidsoft opened 6 years ago

heidsoft commented 6 years ago

为了支持网络协议栈的多个实例,linux在网络协议栈引入了网络命名空间,这些独立的协议栈被隔离到不同 的命名空间中,处于不同的命名空间的网络协议栈事完全隔离的,彼此之间无法通信。docker 就是通过这种实现了不同容器之间的隔离。Veth这个设备对可以联通两个不同的命名空间,使得两个命名空间可以通信。 下面来模拟实现一下:

  1. 创建一个命名空间。必须root用户。 $ ip netns add test0 查看命名空间 $ ip netns show test0 查看命名空间下的设备,目前只有回环地址。 $ip netns exec test0 ip addr show 1: lo: mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

  2. 创建Veth设备对。 $ ip link add veth0 type veth peer name veth1 查看Veth设备对,成功创建了一对Veth。 $ ip link show 84: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether ce:47:38:33:e6:91 brd ff:ff:ff:ff:ff:ff 85: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 72:cf:5b:14:f0:b5 brd ff:ff:ff:ff:ff:ff

  3. 将其中的一个Veth设置到另个命名空间。 $ ip link set veth1 netns test0 再观察发现少了一组Veth设备 $ ip link show 85: veth0@if84: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 72:cf:5b:14:f0:b5 brd ff:ff:ff:ff:ff:ff

  4. 设置ip并启动 设置命名空间test0的 $ ip netns exec test0 /bin/bash $ ip link show 84: veth1@if85: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether ce:47:38:33:e6:91 brd ff:ff:ff:ff:ff:ff

$ ip addr add 192.168.5.3/24 dev veth1 $ ip link set dev veth1 up $ ip addr show 84: veth1@if85: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN qlen 1000 link/ether ce:47:38:33:e6:91 brd ff:ff:ff:ff:ff:ff inet 192.168.5.3/24 scope global veth1 valid_lft forever preferred_lft forever

设置默认命名空间的 $ ip addr add 192.168.5.2/24 dev veth0 $ ip link set veth0 up $ ip addr show 85: veth0@if84: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 72:cf:5b:14:f0:b5 brd ff:ff:ff:ff:ff:ff inet 192.168.5.2/24 scope global veth0 valid_lft forever preferred_lft forever inet6 fe80::70cf:5bff:fe14:f0b5/64 scope link valid_lft forever preferred_lft forever

$ ping 192.168.5.3 PING 192.168.5.3 (192.168.5.3) 56(84) bytes of data. 64 bytes from 192.168.5.3: icmp_seq=1 ttl=64 time=0.084 ms 64 bytes from 192.168.5.3: icmp_seq=2 ttl=64 time=0.044 ms

heidsoft commented 6 years ago

Docker 网络实现 Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。建议先熟悉了解这两部分的基本概念再阅读本章。

基本原理 首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。

Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势之一是转发效率较高。 Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。对于本地系统和容器内系统看来就像是一个正常的以太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。

Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做 veth pair)。

创建网络参数 Docker 创建一个容器的时候,会执行如下操作:

创建一对虚拟接口,分别放到本地主机和新容器中; 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 veth65f9; 容器一端放到新容器中,并修改名字作为 eth0,这个接口只在容器的命名空间可见; 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth65f9。 完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。

可以在 docker run 的时候通过 --net 参数来指定容器的网络配置,有4个可选值:

--net=bridge 这个是默认值,连接到默认的网桥。 --net=host 告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其它 root 进程一样可以打开低范围的端口,可以访问本地网络服务比如 D-bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。因此使用这个选项的时候要非常小心。如果进一步的使用 --privileged=true,容器会被允许直接配置主机的网络堆栈。 --net=container:NAME_or_ID 让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 lo 环回接口通信。 --net=none 让 Docker 将新容器放到隔离的网络栈中,但是不进行网络配置。之后,用户可以自己进行配置。 网络配置细节 用户使用 --net=none 后,可以自行配置网络,让容器达到跟平常一样具有访问网络的权限。通过这个过程,可以了解 Docker 配置网络的细节。

首先,启动一个 /bin/bash 容器,指定 --net=none 参数。

$ docker run -i -t --rm --net=none base /bin/bash root@63f36fc01b5f:/# 在本地主机查找容器的进程 id,并为它创建网络命名空间。

$ docker inspect -f '{{.State.Pid}}' 63f36fc01b5f 2778 $ pid=2778 $ sudo mkdir -p /var/run/netns $ sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid 检查桥接网卡的 IP 和子网掩码信息。

$ ip addr show docker0 21: docker0: ... inet 172.17.42.1/16 scope global docker0 ... 创建一对 “veth pair” 接口 A 和 B,绑定 A 到网桥 docker0,并启用它

$ sudo ip link add A type veth peer name B $ sudo brctl addif docker0 A $ sudo ip link set A up 将B放到容器的网络命名空间,命名为 eth0,启动它并配置一个可用 IP(桥接网段)和默认网关。

$ sudo ip link set B netns $pid $ sudo ip netns exec $pid ip link set dev B name eth0 $ sudo ip netns exec $pid ip link set eth0 up $ sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0 $ sudo ip netns exec $pid ip route add default via 172.17.42.1 以上,就是 Docker 配置网络的具体过程。

当容器结束后,Docker 会清空容器,容器内的 eth0 会随网络命名空间一起被清除,A 接口也被自动从 docker0 卸载。

此外,用户可以使用 ip netns exec 命令来在指定网络命名空间中进行配置,从而配置容器内的网络。

heidsoft commented 6 years ago

https://github.com/yeasy/docker_practice/blob/master/underly/network.md http://pipul.org/2016/02/create-the-container-virtual-network-by-veth-model/ http://pipul.org/2016/05/why-we-need-the-pod-and-service-of-kubernetes/ http://ceyes.github.io/2014-06/netns-beginning/