docker容器单机网络

前言

通过文章 容器的本质可知,容器只是一个进程,而容器所能看到的网络栈,是隔离在自己的 Network Namespace 中。docker 容器单机网络支持四种网络模式,也都是基于 Network Namespace 实现的。本文主要是介绍这四种模式的使用方法及实现原理。

host

使用该模式的容器和宿主机是在同一个 Network Namespace 中的,所以和宿主机用的是同一个网络栈,那么容器暴露的端口,也就是宿主机上端口。

注意,使用该模式,需要关注端口冲突

通过添加 --net=host 参数即可开启 host 模式

docker run -d --net=host nginx

因为和宿主机使用的是同一个网络栈,所以容器与宿主机是可以互相连通的,在宿主机上直接可以通过 127.0.0.1 访问到该容器的的端口。

curl 127.0.0.1

运行另一个容器进入其中执行 curl 127.0.0.1 可以看到一样可以访问到 nginx 暴露的 80 端口,因为都是使用宿主机网络栈。

 docker run -it --net=host curlimages/curl curl 127.0.0.1

bridge

原理

该模式为桥接模式,创建容器时会创建属于自己的 Network Namepsace,该容器和宿主机使用的是不同的 Network Namespace,也就是说它们使用的是不同的网络栈。

bridge 网络模型的实现原理可以参考文章 手动实现docker容器bridge网络模型

宿主机创建了 docker0 作为虚拟网桥,其作用主要是作为交换机在二层网络,再将使用 bridge 模式创建的容器通过 veth pair 连接到 dcoker0 上,这样连接到 docker0 上的容器都可以互相网络通信。

veth pair 类似一个管道,数据包会从一端到另一端。

验证

默认运行容器时使用的就是 bridge 模式,docker 会自动为容器添加 veth pair 并配置好其 ip 地址,这里的 eth0 就是其中的一端,可以看到其 ip 地址为 172.17.0.2

[root@localhost ~]# docker run -d --name nginx1 nginx

[root@localhost ~]# docker exec -it nginx1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

veth pair 的另一端会接入到 docker0 上,在容器中执行以下命令可以看到 veth pair 另一端的序号

[root@localhost ~]# docker exec nginx1 cat /sys/class/net/eth0/iflink
21

在宿主机上可以看到 21 序号上的 veth pair 的名称是 veth702ba20,也就是管道的另一端。

[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether fe:fc:fe:af:4b:ea brd ff:ff:ff:ff:ff:ff
    inet 10.61.74.37/23 brd 10.61.75.255 scope global noprefixroute ens18
       valid_lft forever preferred_lft forever
    inet6 fe80::1bdd:fe7:4a90:1a67/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:84:c1:3b:ea brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:84ff:fec1:3bea/64 scope link 
       valid_lft forever preferred_lft forever
21: veth702ba20@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 0a:21:51:0c:7e:db brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::821:51ff:fe0c:7edb/64 scope link 
       valid_lft forever preferred_lft forever

再查看 docker0 插入的设备可以发现 veth702ba20 是插入在 docker0 上的。

[root@localhost ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.024284c13bea       no              veth702ba20

容器网络互通

再创建另一个容器 nginx2,查看其 ip 为 172.17.0.3,可以发现 nginx1 是可以 ping 通 nginx2 的该 ip 的。

[root@localhost ~]# docker run -d --name nginx2 nginx
[root@localhost ~]# docker exec nginx2 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

为什么可以互通呢?

我们来看下 nginx1 的路由,当 ping nginx2 的 ip 时,会匹配到第二条路由,然后走 eth0 网卡,因为其是 veth pair 的一端,数据包会在另一端出现,另一端接入到了 docker0 上,最终数据包到达 docker0

[root@localhost ~]# docker exec nginx1 ip r
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  proto kernel  scope link  src 172.17.0.2 

当通过 nginx1 ping nginx2 的 ip 时,我过监听 docker0 网卡看一下数据包:

[root@localhost ~]# docker exec -it nginx1 ping 172.17.0.3 -c 3

[root@localhost ~]# tcpdump -i docker0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
04:32:57.596814 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28
04:32:57.596848 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28
04:32:57.596853 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 17, seq 1, length 64
04:32:57.596896 IP 172.17.0.3 > 172.17.0.2: ICMP echo reply, id 17, seq 1, length 64
04:32:58.596437 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 17, seq 2, length 64
04:32:58.596492 IP 172.17.0.3 > 172.17.0.2: ICMP echo reply, id 17, seq 2, length 64
04:32:59.596444 IP 172.17.0.2 > 172.17.0.3: ICMP echo request, id 17, seq 3, length 64
04:32:59.596491 IP 172.17.0.3 > 172.17.0.2: ICMP echo reply, id 17, seq 3, length 64
04:33:02.598361 ARP, Request who-has 172.17.0.2 tell 172.17.0.3, length 28
04:33:02.598386 ARP, Reply 172.17.0.2 is-at 02:42:ac:11:00:02 (oui Unknown), length 28

由上可知,nginx1(10.17.0.2) 会发送 ARP 获取 nginx2(10.17.0.3) 的 mac 地址,然后使用该 mac 地址通过二层设备 bridge 向 nginx2 转发数据包,进入到了 nginx2 的 Network Namespace 中,由它的网络栈处理该数据包,最后回包。

容器连接其他主机

容器内连接其他主机时,比如 ping 10.65.132.187 时,会先通过 docker0 达到宿主机上,然后通过宿主机的网络栈处理。

通过查看宿主机路由表,到达宿主机的数据表会走第一条默认路由,通过 eth0 网卡下一跳到 10.61.74.1,然后最终达到另一台主机的 eth0 中。

[root@localhost ~]# ip r
default via 10.61.74.1 dev eth0 proto static metric 100 
10.61.74.0/23 dev eth0 proto kernel scope link src 10.61.74.37 metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1

container

使用该模式的容器会加入到指定容器的 Network Namespace 中,也就是两个容器共用同一个网络栈。

首先使用 bridge 模式创建容器 nginx1,该容器会拥有自己的 Network Namespace,然后再使用 container 模式创建 nginx2 容器并加入 nginx1 的 Network Namespace 中。

通过查看两个容器的网卡可以发现两个是一样的。

[root@localhost ~]# docker run -d --name nginx1 nginx
[root@localhost ~]# docker exec -it nginx1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
56: eth0@if57: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

[root@localhost ~]# docker run -it --name nginx2 --net=container:nginx1 nginx /bin/bash

[ root@20069e4c2bde:/etc/nginx ]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
56: eth0@if57: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

none

该模式创建容器也会创建新的属于自己的 Network Namespace,但是容器内不会有任何的网络配置,没有网卡、路由、路由等信息,需要由我们自己去配置。

[root@localhost ~]# docker run -it --name nginx --net=none nginx /bin/bash

[ root@52480b0a4725:/etc/nginx ]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    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
[ root@52480b0a4725:/etc/nginx ]$ ip r

欢迎关注,互相学习,共同进步~

我的个人博客
公众号:编程黑洞

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值