《跟唐老师学习云网络》 - Docker网络实现

举报
tsjsdbd 发表于 2020/04/02 22:09:29 2020/04/02
【摘要】 我们来详细观察&理解Docker容器是如何实现它的网络的,以及解析一个容器是如何与本机、本机中的容器、其他Host、其他Host中的容器 等场景下分别是如何进行通信的详细原理。

 

本机容器网络大概生成过程:首先每个容器对应创建一个network namespace;然后将所有的容器的network namespace连接到Bridge网桥(docker0)上,使得容器间互相处于一个局域网内,方便连通。

1      Docker的网络命名空间

 

docker使用namespace实现容器网络,但是我们使用ip netns命令却无法在主机上看到任何network namespace,这是因为默认docker把创建的网络命名空间链接文件隐藏起来了。

2种进入这个“空间”的命令,我们以前的课都讲过。

 

1.1      通过ip netns exec

启动一个容器

docker run -tid ubuntu:18.04

 

这个时候,查询 network namespace,却发现是空的。

因为 ip netns 是去检查 /var/run/netns 目录的。而Docker这个软件,故意把容器对应的ns信息记录到了 /var/run/docker/netns 目录。所以ip netns查出来就是空的,我们想办法把ns信息翻出来就行。

 

1)恢复关联-方式1

所以我们,把这2个目录关联一下:

ln -s /var/run/docker/netns  /var/run/netns

 

接下来再敲:

ip netns

就可以看到容器的 network namespace 了。

不过可以发现列出来的nsID,和对应容器的ID,不是同一个。 它两有一个映射值,可以通过 docker inspect 的结果查到对应关系:

docker inspect 070044b2738f

image.png

 

2)恢复关联-方式2

找到容器主pid

pid=$(docker inspect -f '{{.State.Pid}}' ${container_id})

创建对应的ns记录:

mkdir -p /var/run/netns/

ln -sfT /proc/$pid/ns/net /var/run/netns/$container_id

image.png

 

1.2      通过 nsenter

找到容器里app的主pid

docker inspect ecf8689d3297

image.png

 

跑到这个pid对应的世界(namespace)里去。

nsenter -n -t 25977

这个时候,就是在容器里面的网络空间角度敲命令啦。

 

例如:

1)查询网卡:

ifconfig

image.png

2)抓包:

tcpdump -i eth0 -n

image.png

 

2      Docker 使用的Linux Bridge

关于Docker为什么要加个Bridge来连通所有的容器?其实不加Bridge,网络也能通。只是说有了Bridge,就有了覆盖更多复杂场景的能力。

这里直接引用Docker自己的描述:通过Bridge,可以使得连到这个Bridge的容器互相通信;同时和没有连到这个Bridge的容器保持网络隔离。(大意就是:容器可以按网络分组)

image.png

 

3      我怎么和本机Host主机通信

假设我就是那个Docker容器,那么我是如何与主机Host通信的呢。

3.1      本机Host怎么访问我

主机Host访问自己节点上的容器,答案是:直接访问就行了。

咱们先来看Host主机的路由表:

image.png

因为根据路由信息:

所有发往Docker容器的地址(即目标为 172.17.* )的报文,---> 统统走给 ---> docker0 网卡。而根据上面Bridge章节可以知道,这个docker0就是Bridge网桥,它是连着所有容器的Veth网线的。所以这个报文会发送到所有容器里面,那么目标容器就会应答你。

image.png

 

3.2      我怎么访问所在的Host主机

容器访问自己的Host主机,答案也是直接访问。

Docker容器里面,网络很简单,就一个eth0。所以你往外发报文,都是经过eth0网卡。而这个网卡是一个veth网线的一头,所以这个报文就会到达Bridge网桥(即docker0)。而这个网桥就是Host主机的一个网卡,所以就到达了目的地。

image.png

综上,Docker容器和Host主机,是可以自由通信的。

 

3.3      本机其他容器怎么访问我

这个问题,直接去Bridge章节看一下就行了。大家都通过docker0这个Bridge焊在一起,所以直接互访就行了。

 

4      我怎么和别的Host主机通信

别的Host主机,就是“爸爸(所在节点)的兄弟”。

 

4.1      别的Host怎么访问我

 

跨节点访问容器时,由于不知道目标容器是住在哪台Host主机上(要访问那个容器,必须经过它所在的Host),所以为了访问一个目标容器专门设置一条路由规则(当我访问xxx容器时,请经过yyy虚拟机,这种规则),并不方便。所以一般直接用端口映射来访问。  

即:目标容器所在的Host主机IP + 指定端口。然后当报文到达指定目标的Host主机时,通过指定端口Nat进入容器。

image.png

举例:

(1)       192.168.1.9这台机器上启动一个Nginx容器:注意这里-p参数告诉Host,请将主机上面的80端口,作为进入我的NAT入口。

docker run -rm  -p 80:80 nginx

 

(2)       然后咱们再另外找一台机器(与刚才192.168.1.9 这一台能连通)。

跨节点访问刚才那个容器:

curl -vvv 192.168.1.9:80

image.png

这里可以看到,主机跨节点访问容器时,必须通过指定端口NAT进入到目标容器。

image.png

直接访问IP是不通的(没有路由信息)

 

4.2      我怎么访问别的Host

这个答案比较简单:只要我所在的Host能通的地方,我就也能与它连通

image.png

你看主机上有一条:源地址NAT规则。

iptables -t nat –nL

意思是容器里面发出的报文,把源地址改成主机的,然后往外发。意思是跟主机一样往外发报文就完了(不理解的参见以前的NAT课程)。

image.png 

4.3      别的Host上的容器怎么访问我

也就跨节点的2个容器怎么互相通信。一般是2种方式:

4.3.1        NAT端口映射。

即通过指定目标端口,穿到容器中。

根据4.2章节可知:容器-》目标  == 容器所在Host节点》目标。

根据4.1章节可知:访问目标容器 == 指定IP+Port

所以容器里面直接用:指定IP+Port访问目标容器就行了。

 

举例:

(1)       192.168.1.9这台机器上启动一个Nginx容器:注意这里-p参数告诉Host,请将主机上面的80端口,作为进入我的NAT入口。

docker run -rm  -p 80:80 nginx

 

(2)       然后咱们再另外找一台机器(与刚才192.168.1.9 这一台能连通)。

进入一个容器,然后跨节点访问刚才那个容器:

docker exec -it ea60d3290dd5 /bin/bash

curl -vvv 192.168.1.9:80

image.png

 

4.3.2        隧道网络打通所有容器。

这种就稍微复杂一点,就是让所有容器处于同一个局域网中。

隧道模式,实现方式基本是各显神通了。除了新版本Docker有自己的实现,各大厂商也都有不一样的实现,比如现在各种flannelweavecalico等现实。

原理嘛,请参考早期的 隧道课程。(可以照着物理世界去考虑,好比为很多物理机里面的所有VM创建虚拟局域网类似)

image.png


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。