Veth Pair

这部分内容主要介绍一个设备: Veth Pair .

作为一个容器,它可以声明直接使用宿主机的网络栈,即:不开启 Network Namespace .在这种情况下,这个容器启动后,直接监听的就是宿主机的 80 端口.

像这样直接使用宿主机网络栈的方式,虽然可以为容器提供良好的网络性能,但也会不可避免地引入共享网络资源的问题,比如端口冲突.所以,在大多数情况下,我们都希望容器进程能使用自己 Network Namespace 里的网络栈,即:拥有属于自己的 IP 地址和端口.

但是这个时候,就有另外一个问题了:这个别隔离的容器进程,该如何跟其他 Network Namespace 里的容器进程进行交互呢?

最直接的办法,就是把它们用一根网线连接起来;而如果想要实现多台主机之间的通信,那就需要用网线,把它们连接在一台交换机上.

在 Linux 中,能够起到虚拟交换机作用的网络设备,是网桥( Bridge ).它是一个工作在数据链路层( Data Link )的设备,主要功能是根据 MAC 地址学习来将数据包转发到网桥的不同端口( Port )上.

为了实现这个目的, Docker 项目会默认在宿主机上创建一个名叫 docker0 的网桥,凡是连接在 docker0 网桥上的容器,都可以通过它来进行通信.

但是,我们又该如何把这些容器"连接"到 docker0 网桥上呢?此时,我们就需要使用一种名叫 Veth Pair 的虚拟设备了.

Veth Pair 设备的特点是:它被创建出来后,总是以两张虚拟网卡( Veth Peer )的形式成对出现的.并且,从其中一个"网卡"发出的数据包,可以直接出现在与它对应的另一张"网卡"上,哪怕这两个"网卡"在不同的 Network Namespace 里面.这样,用来连接不同 Network Namespace 的"网线"就成为了可能.

举个例子

比如,现在我们启动了一个叫做 nginx-1 的容器:

$ docker run –d --name nginx-1 nginx

然后我们可以进入到这个容器中,查看一下它的网络设备:

# 在宿主机上
$ docker exec -it nginx-1 /bin/bash
# 在容器里
root@2b3c181aecf1:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::42:acff:fe11:2 prefixlen 64 scopeid 0x20<link>
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 364 bytes 8137175 (7.7 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 281 bytes 21161 (20.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 $ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0

可以看到,在这个容器中,有一张叫做 eth0 的网卡,它正是一个 Veth Pair 设备在容器里的这一端.通过 route 命令,可以查看到 nginx-1 容器的路由表,能够看到,这个 eth0 网卡是这个容器里的默认路由设备;所有对 172.17.0.0/16 网段的请求,也会被交给 eth0 来处理(第二条 172.17.0.0 路由规则)

而这个 Veth Pair 设备的另一端,则在宿主机上.可以通过查看宿主机的网络设备看到它,如下所示:

# 在宿主机上
$ ifconfig
...
docker0 Link encap:Ethernet HWaddr 02:42:d8:e4:df:c1
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:d8ff:fee4:dfc1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:309 errors:0 dropped:0 overruns:0 frame:0
TX packets:372 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:18944 (18.9 KB) TX bytes:8137789 (8.1 MB)
veth9c02e56 Link encap:Ethernet HWaddr 52:81:0b:24:3d:da
inet6 addr: fe80::5081:bff:fe24:3dda/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:288 errors:0 dropped:0 overruns:0 frame:0
TX packets:371 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:21608 (21.6 KB) TX bytes:8137719 (8.1 MB) $ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242d8e4dfc1 no veth9c02e56

通过 ifconfig 命令的输出,我们可以看到, nginx-1 容器对应的 Veth Pair 设备,在宿主机上是一张虚拟网卡,它的名字是: veth9c02e56 .并且,通过 brctl show 的输出,可以看到这张网卡被"插"在了 docker0 上.

如果此时,我们在这台宿主机上启动另一个 Docker 容器,比如 nginx-2 :

$ docker run –d --name nginx-2 nginx
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242d8e4dfc1 no veth9c02e56
vethb4963f3

会发现,有一个新的,名叫 vethb4963f3 的虚拟网卡,也被"插"在了 docker0 网桥上.此时,如果在 nginx-1 容器中 ping 一下 nginx-2 容器的 IP 地址( 172.17.0.3 ),就会发现,在同一宿主机上的两个容器,默认就是相互连通的.

以上整个流程,来一张示意图:(这样可以更清楚一些)



此时,应该就可以理解了,在默认情况下,被限制在 Network Namespace 里的容器进程,实际上是通过 Veth Pair 设备 + 宿主机网桥的方式,实现了和其他容器的数据交换.

所以,如果遇到容器连不通"外网"时,第一反应应该是先试试 docker0 网桥能不能 ping 通,然后查看一下跟 docker0 和 Veth Pair 设备相关的 iptables 规则是不是有异常,一般就能够找到问题的答案.

以上介绍的是单机容器网络的实现原理和 docker0 网桥的作用.在这里的关键在于,容器如果想要和外界进行通信,它发出的 IP 包就必须从它的 Network Namespace 里出来,来到宿主机上,而解决这个问题的方法就是:为容器创建一个一端在容器里充当默认网卡,另一端在宿主机上的 Veth Pair 设备.

思维扩展

看到这里,估计你会有另外一个疑问,那我如果想要"跨主机通信"呢?

在 Docker 的默认配置下,一台宿主机上的 docker0 网桥,和其他宿主机上的 docker0 网桥,没有任何关联,它们互相之间就没办法连通.

但是如果我们通过软件的方式,创建一个整个集群"公用"的网桥呢?然后把集群里所有的容器都连接到这个网桥上,是不是就可以相互通信了?

一点儿毛病都没.

这样,我们就可以画出如下的示意图:



通过示意图,能够看到,构建这种容器网络的核心在于:我们需要在已有的宿主机网络上,再通过软件构建出一个覆盖在已有宿主机网络之上的,可以把所有容器连通在一起的虚拟网络.这种技术就是: Overlay Network (覆盖网络).

但是这只是理论上,落实到实际,还是有些出入的.

后面我看时间和精力,如果可能,再写一篇关于"跨主机"通信的讲解文章(感觉有些难度,我不确定能够讲清楚,但是尽量)

以上内容来自我学习<深入剖析Kubernetes>专栏文章之后的一些见解,有偏颇之处,还望指出.

感谢您的阅读~

[Kubernetes]浅谈容器网络的更多相关文章

  1. 浅谈通信网络(三)——TCP/IP协议

    简介 Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是Internet最基本的协议.In ...

  2. 从 Kubernetes 谈容器网络

    基本概念 在 Kubernetes 中.资源从管理粒度上分为三级:容器.Pod.Service. 容器 即 Docker 或者 Rocket 容器(1.0 中仅支持这两种容器). 容器是最低粒度的资源 ...

  3. 浅谈C#网络编程(一)

    阅读目录: 基础 Socket编程 多线程并发 阻塞式同步IO 基础 在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践. Socket是一种网络编程接口,它是对传输层T ...

  4. 浅谈iOS网络编程之一入门

    计算机网络,基本上可以抽象是端的通信.实际在通讯中会用到不同的设备,不同的硬件中,为了能友好的传输信息,那么建立一套规范就十分必要了.先来了解一些基本概念 了解网络中传输的都是二进制数据流.  2.了 ...

  5. [Cocos2d-x v3.x]浅谈容器Vector

    转载请注明来自:star特530的CSDN博客 http://blog.csdn.net/start530/article/details/19170853 前两天有人问我说在3.0 beta2版本号 ...

  6. 小dai浅谈通信网络(一)——引子

    说起通信网络,首先来看一个场景: 场景模式: 小明和小刚在闹市碰面. 小明对小刚大声喊道:"小刚,你好啊!" 小刚摇手答到:"你好,小明!" 就这么几句简单的话 ...

  7. 浅谈C#网络编程(二)

    阅读目录: 异步IO 非阻塞式同步IO 基于回调的异步IO并发 异步IO 上篇提到用多线程处理多个阻塞同步IO而实现并发服务端,这种模式在连接数量比较小的时候非常适合,一旦连接过多,性能会急速下降. ...

  8. Android项目实战(四十四):浅谈Postman (网络请求调试插件)

    前言: Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件.    在项目开发中,可以依赖此工具模拟API测试. 使用详解: 各种情况Api的模拟请求的Postman使用方 ...

  9. 【好书分享】容器网络到kubernetes网络

    Nginx 公司的 Michael Hausenblas 发布了一本关于 docker 和 kubernetes 中的容器网络的小册子.这份资料一共 72 页,是大家由浅入深的了解 Docker 和 ...

随机推荐

  1. HTML之表单

    目录 表单的结构 get方式与post方式 输入 单行文本输入框 提交按钮和重置按钮 密码输入框 复选框 单选按钮 图像按钮 文本选择输入框 隐藏框 多行文本输入框 下拉列表框 新增输入元素 新增其他 ...

  2. 优先队列Priority Queue和堆Heap

    对COMP20003中的Priority queue部分进行总结.图片来自于COMP20003 queue队列,顾名思义特点先进先出 priority queue优先队列,出来的顺序按照优先级prio ...

  3. Spark报错

    1. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.mysql.jdb ...

  4. nginx的概念与几种负载均衡算法

    Nginx的背景 Nginx和Apache一样都是一种WEB服务器.基于REST架构风格,以URI(Uniform Resources Identifier,统一资源描述符)或URL(Uniform ...

  5. ADC获取滑块的值(8通道)

    #include "TgcConfig.h"#include "my_usb.h" /************************************* ...

  6. hdu4843(NOI2000) 古城之谜 (trie树+DP)

    Description 著名的考古学家石教授在云梦高原上发现了一处古代城市遗址.让教授欣喜的是在这个他称为冰峰城(Ice-Peak City)的城市中有12块巨大石碑,上面刻着用某种文字书写的资料,他 ...

  7. 内部yum仓库制作

    有些安装收到网络隔离(申请一个到DMZ区的通行证很困难) 使用yum的命令工具,在有网络环境下同步我们的yum仓库,并用http服务器代理和制作repo源进行内部安装. 实操: [root@maste ...

  8. Azure Function & AWS Function With C#

    Using C# with Azure Functions Two important prerequisites need to be met to build Azure Functions ap ...

  9. [ffmpeg] AVOption

    在ffmpeg中,常使用AVOption的API来进行参数设置.AVOption的API主要分为设置参数以及提取参数两种,无论是哪一种API都主要分为两大步骤: 寻找出参数所在的内存位置. 如果是设置 ...

  10. 转载:原来JavaScript的闭包是这么回事!

    相关阅读:https://www.itcodemonkey.com/article/8565.html