• docker网络部分的视频我看了很多,讲解最透彻的还是https://www.bilibili.com/video/BV123411y7TB?p=8
  • 获取本文方式:见谷粒商城文尾,备注【docker网络】
  • k8s思维导图:https://www.processon.com/view/link/615bc61e637689127946ea5d

一、docker网络基础

docker的网络是基于Linux namespace虚拟化实现的。

Docker本身的技术依赖于Linux内核虚拟化技术的发展。所以Docker对Linux内核的特性有很强的依赖。本章主要介绍Docker所使用的Linux网络技术。

0、docker命令

1)命名空间相关命令

# 添加命名空间
ip netns add [namespace]# 查看命名空间
ip netns list# 进入命名空间
ip netns exec [namespace] bash

2)veth对相关命令

# 添加veth对
ip link add veth type veth peer name [vethName]# 查看当前命名空间下的veth对
ip link show

3)网桥相关命令

docker network [cmd]# 查看网桥
docker network ls# 创建网桥
docker network create [网桥名称]# 查看网桥信息
docker network inspect [网桥名称|ID]# 链接一个容器
docker network connect [网络名称] [容器名称]# 断开一个链接
docker network disconnect [网络名称] [容器名称]# 删除一个网络
docker network rm [网桥名称]# 清除所有网桥(清除除了默认的三个网桥和正在使用的)
docker network prune

1 网络基础

其中Docker使用到的与Linux网络有关的技术分别有:网络名称空间、Veth、Iptables、网桥、路由。

1.1 网络名称空间

为了支持网络协议栈的多个实例(多个docker容器,一个linux内核),在网络协议栈中引入了网络名称空间(Network Namespace),这些独立的协议栈被隔离到不同的命名空间中。处于不同的命名空间的网络协议栈是完全隔离的,彼此之间无法进行网络通信,就好像两个“平行宇宙”。通过这种对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境,而Docker正是利用这种网络名称空间的特性,实现了不同容器之间的网络隔离。在Linux的网络命名空间内可以有自己独立的Iptables来转发、NAT及IP包过滤等功能。

Linux的网络协议栈是十分复杂的,为了支持独立的协议栈,相关的这些全局变量都必须修改为协议栈私有。最好的办法就是让这些全局变量成为一个Net Namespace变量的成员,然后为了协议栈的函数调用加入一个Namespace参数。这就是Linux网络名称空间的核心。所以的网络设备都只能属于一个网络名称空间。当然,通常的物理网络设备只能关联到root这个命名空间中。虚拟网络设备则可以被创建并关联到一个给定的命名空间中,而且可以在这些名称空间之间移动。

创建一个命名空间
[root@k8s-node1 ~]# ip netns add test01
[root@k8s-node1 ~]# ip netns add test02
[root@k8s-node1 ~]# ip netns list
test02
test01

1.2 Veth设备

Veth设备对

veth技术是docker root和容器间使用的技术(不是容器与容器使用的技术)

引入Veth设备对是为了在不同的网络名称空间之间进行通信,利用它可以直接将两个网络名称空间链接起来。由于要连接的两个网络命名空间,所以Veth设备是成对出现的,很像一对以太网卡,并且中间有一根直连的网线。既然是一对网卡,那么我们将其中一端称为另一端的peer。在Veth设备的一端发送数据时,它会将数据直接发送到另一端,并触发另一端的接收操作。

Veth设备操作

  • 创建Veth设备对
# 创建veth对
# add 名字 type 类型 peer name 名字
[root@k8s-node1 ~]# sudo ip link add veth type veth peer name veth001
[root@k8s-node1 ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether 08:00:27:b8:1f:f1 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000link/ether 08:00:27:8b:9b:3a brd ff:ff:ff:ff:ff:ff
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group defaultlink/ether 02:42:2c:7a:07:44 brd ff:ff:ff:ff:ff:ff
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group defaultlink/ether 72:f6:10:6e:5f:9d brd ff:ff:ff:ff:ff:ff
6: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default qlen 1000link/ether 6e:83:37:fa:b6:56 brd ff:ff:ff:ff:ff:ff
7: veth98a27231@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group defaultlink/ether fe:b6:4e:35:3d:d0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
8: vetha376bf13@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group defaultlink/ether 46:00:a0:2a:7a:57 brd ff:ff:ff:ff:ff:ff link-netnsid 1#
9: veth001@veth: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 22:c4:9d:c6:9b:06 brd ff:ff:ff:ff:ff:ff#
10: veth@veth001: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether be:10:28:7c:f0:c4 brd ff:ff:ff:ff:ff:ff

生成了两个veth设备, 互为对方的peer

  • 绑定命名空间
# 把刚才的peer一端放到test1中
[root@k8s-node1 ~]# sudo ip link set veth001 netns test01
[root@k8s-node1 ~]# ip link show | grep veth
7: veth98a27231@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
8: vetha376bf13@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default
# 会发现原来的veth001@veth不见了
10: veth@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000

已经查看不到veth001,当我们进入test01命名空间之后,就可以查看到

# 进入命名空间test01
[root@k8s-node1 ~]# sudo ip netns exec test01 bash
[root@k8s-node1 ~]# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00# 又找到啦
9: veth001@if10: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 22:c4:9d:c6:9b:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  • 将Veth分配IP
[root@k8s-node1 ~]# ip addr add 172.16.0.111/20 dev veth001
[root@k8s-node1 ~]# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
9: veth001@if10: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 22:c4:9d:c6:9b:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0 # 不知道为什么我这里还是没有ip,可能是因为与docker的ip冲突
# 查看对端veth设备
[root@k8s-node1 ~]# ip netns exec test01 ethtool -S veth001
NIC statistics:peer_ifindex: 10
[root@k8s-node1 ~]# ip a | grep 10
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
9: veth001@if10: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
[root@k8s-node1 ~]# sudo ip link set veth netns test02
[root@k8s-node1 ~]# sudo ip netns exec test02 bash
[root@k8s-node1 ~]# ip addr add 172.16.0.112/20 dev veth
[root@k8s-node1 ~]# ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
10: veth@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether be:10:28:7c:f0:c4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# 重启 # 需要进入每个命名空间
sudo ip netns exec test01 bash
ip link set dev veth001 down
ip link set dev veth001 up sudo ip netns exec test02 bash
ip link set dev veth001 down
ip link set dev veth001 up互相ping就能ping通了

docker就使用了veth技术。

但是docker仅在容器和root之间使用了veth,而容器与容器间使用的网桥

在docker中看veth对不太好看,但我们可以观察到每删除一个容器root也会删除一个pair

1.3 网桥

Linux 可以支持多个不同的网络,它们之间能够相互通信,就需要一个网桥。 网桥是二层的虚拟网络设备,它是把若干个网络接口“连接”起来,从而报文能够互相转发。网桥能够解析收发的报文,读取目标 MAC 地址的信息,和自己记录的 MAC 表结合,来决定报文的转发目标网口。

网桥设备 brO 绑定了 eth0、 eth1 。对于网络协议栈的上层来说,只看得到 brO 。因为桥接是在数据链路层实现的 ,上层不需要关心桥接的细节,于是协议栈上层需要发送的报文被送到 brO ,网桥设备的处理代码判断报文该被转发到 eth0 还是 ethl ,或者两者皆转发。反过来,从 eth0 或从 ethl 接收到的报文被提交给网桥的处理代码,在这里会判断报文应该被转发、丢弃还是提交到协议栈上层。 而有时 ethl 也可能会作为报文的源地址或目的地址 直接参与报文的发送与接收,从而绕过网桥。

1.4 Iptables

我们知道, Linux 络协议栈非常高效,同时比较复杂 如果我们希望在数据的处理过程中对关心的数据进行一些操作该怎么做呢? Linux 提供了一套机制来为用户实现自定义的数据包处理过程。

在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂接的钩子函数可以在Linux 网络栈处理数据包的过程中对数据包进行 些操作,例如过滤、修改、丢弃等 整个挂接点技术叫作 Netfilter lptables

Netfilter 负责在内核中执行各种挂接的规则,运行在内核模式中:而 lptables 是在用户模式下运行的进程,负责协助维护内核中 Netfilter 的各种规则表 通过 者的配合来实现整个 Linux网络协议战中灵活的数据包处理机制。

总结

设备 作用总结
network namespace 主要提供了关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表、防火墙、/proc/net目录、/sys/class/net目录、端口(socket)等。
linux Bridge 功能相当于物理交换机,为连在其上的设备(容器)转发数据帧。如docker0网桥。
iptables 主要为容器提供NAT以及容器网络安全。
veth pair 两个虚拟网卡组成的数据通道。在Docker中,用于连接Docker容器和Linux Bridge。一端在容器中作为eth0网卡,另一端在Linux Bridge中作为网桥的一个端口。

二、docker网络模式

Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关(本局域网内处理不了的请求就交给网关处理)。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。

docker的四种网络模式:

我们在使用docker run创建docker容器时,可以用-net选型指定容器的网络模式,这些网络模式的区别是容器命名空间和主机命名空间的关系。,docker有以下四种网络模式:

Docker网络模式 配置 说明
host模式 –net=host 容器和宿主机共享Network namespace。
container模式 –net=container:NAME_or_ID 容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。
none模式 –net=none 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。
bridge模式(默认) –net=bridge

启用方式如:

docker run -d --network=host
或者
--net host

1)bridge模式(NAT)

当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备(可以理解为网线),Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡);另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。

bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。

如果不指定的话,默认就会使用bridge模式,bridge本意是桥的意思,其实就是网桥模式。我们怎么理解网桥,如果需要做类比的话,我们可以把网桥看出一个二层的交换机设备。

交换机通信简图

也叫NAT。不知道–net时默认就是网桥模式

[root@node4 ~]# docker run -ti --rm sunrisenan/alpine:3.10.3 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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 lovalid_lft forever preferred_lft forever
100: eth0@if101: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP # docker里为eth0
link/ether 02:42:ac:06:f4:02 brd ff:ff:ff:ff:ff:ff
inet 172.6.244.2/24 brd 172.6.244.255 scope global eth0 # IP为172.valid_lft forever preferred_lft forever

网桥模式示意图

Linux能够起到虚拟交换机作用的网络设备,是网桥。他是一个工作在数据链路层(data link)的设备,主要的功能是根据MAC地址将数据包转发到网桥的不同端口上

查看网桥

#下载安装
yum install -y  bridge-utils[root@localhost ~]# brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242cf195186       no              veth10904ebveth10ff0a9veth1787878veth8244792veth9760247vethbca21e7

关于brctl命令参数说明和示例

参数 说明 示例
addbr 创建网桥 brctl addbr br10
delbr 删除网桥 brctl delbr br10
addif 将网卡接口接入网桥 brctl addif br10 eth0
delif 删除网桥接入的网卡接口 brctl delif br10 eth0
show 查询网桥信息 brctl show br10
stp {on|off} 启用禁用 STP brctl stp br10 off/on
showstp 查看网桥 STP 信息 brctl showstp br10
setfd 设置网桥延迟 brctl setfd br10 10
showmacs 查看 mac 信息 brctl showmacs br10
docker网络

docker在启动一个容器时时如何实现容器间的互联互通的?

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

  • 创建一对虚拟接口/网卡,也就是一对虚拟接口(veth pair)
  • 本地主机一端桥接到默认的docker0或指定网桥上,并具有一个唯一的名字,如veth9953b75
  • 容器一端放到新启动的容器内部,并修改名字为eth0,这个网卡/接口只在容器的命名空间可见;
  • 从网桥可用地址段中(也就是与该bridge对应的network)获取一个空闲地址分配给容器的eth0
  • 配置默认路由到网桥

整个过程其实都是docker自动帮我们完成的,清理掉是所有的容器来验证:

基础知识

主机们在不在一个广播域,完全取决于主机连接的交换机端口们在不在同一个VLAN

1. 如果在同一个VLAN,即使主机们的网段不相同,也是工作在一个广播域。
1.1 主机们的网段相同,可以ARP发现彼此的MAC,直接通信,不需要任何三层设备(网关)的介入。

1.2 主机们的网段不相同,即使在一个广播域,也不能直接通信,需要三层设备(网关)的介入。

2. 如果不在一个VLAN,主机们不在一个广播域
2.1 一个VLAN对应一个网段,那么主机之间的通信需要三层设备(网关)的介入。

2.2 如果很不巧,两个VLAN里的主机使用相同的网段,主机并不知道有VLAN 的存在,所以依然认为其它主机和自己在一个广播域,但是这个广播域被交换机VLAN逻辑隔离,成为两个广播域,这时无法使用三层设备使得它们通信,唯一的方法,使用一个网桥将两个VLAN二层桥接起来,它们就可以通信了。

所谓网关,就是上文提到的三层设备,可以是路由器、或三层交换机、防火墙。

哈哈,这个2.2的情况估计没有几个看得懂,凡是没点赞的都是看不懂的,整个知乎用户能看懂2.2情况的不会超过1000人…

网关是负责不同网络通信使用的。不同网络指的是 ip 地址中的网络号不同,比如 192.168.2.3/24,这个 ip 表示网络号为192.168.2(前24位)

比如 a 节点 ip 为192.168.2.1/24, b 节点 ip 为 192.168.2.3/24 ,a 给 b 发送消息的时候,会先看是否在同一个网络,根据 b 的 ip 很容易判断出都是在 192. 168. 2这个网络内,所以 a 会直接给 b 发送消息(通过 b 的 mac 地址,这个 mac 地址是通过 arp协议获取的) 。

c节点 ip 地址为 192.168.3.2/24,a 发送消息给 c, a 很容易知道 c 的网络地址是 192.168.3,与自己的网络地址不一样,这时候就会把这个消息发送给网关(也是通过 mac 地址),网关负责把消息发送给 c。

说白了,就是通信协议规定了,在同网段内可以直接通过 mac 通信,不同网段需要通过网关通信。

# 清理所有容器
docker rm -f `docker ps -aq`
docker ps -a# 查看网桥中的接口,目前是没有的
[root@docker ~]# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242cfb7aaca   no      空# 创建测试容器test1
docker run -d --name test1 nginx:alpine# 查看网桥中的接口,已经把test1 的veth端接入到网桥中
[root@docker ~]# brctl show
bridge name bridge id       STP enabled interfaces
docker0     8000.0242cfb7aaca   no      veth95f0f29#已经在宿主机中可以查看到
[root@docker ~]# ip a | grep veth
131: veth95f0f29@if130: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default #进入容器查看容器的eth0网卡及分配的容器IP  # docker exec test1 ip a
[root@docker ~]# docker exec -ti test1 sh
/ # ifconfig | awk 'NR==2{print $2}'
addr:172.17.0.2
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         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# 再启动一个测试容器,测试容器间的通信
docker run -d --name test2 nginx:alpine
docker exec -it test2 sh
/# curl 172.17.0.2:80## 为啥可以通信
/ # route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         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 网卡是这个容器里的默认路由设备,所有对172.17.0.0/16网段的请求,也会被交给eth0来处理(第二条 172.17.0.0 路由规则),这条路由规则的网关(Gateway)是0.0.0.0,这就意味着这是一条直连规则,即:凡是匹配到这条规则的IP包,应该经过本机的eth0网卡,通过二层网络(数据链路层)直接发往目的主机。# 网桥会维护一份Mac映射表,我们可以通过命令来查看一下
[root@docker ~]# brctl showmacs docker0
port no mac addr        is local?   ageing timer1   32:76:d0:a3:0d:5c   yes        0.001    32:76:d0:a3:0d:5c   yes        0.002    86:4e:1c:5d:07:83   yes        0.002    86:4e:1c:5d:07:83   yes        0.00# 这些Mac地址是主机端的veth网卡对于的Mac,可以查看运行
ip a |grep -n3 eth

我们如何指定网桥上的这些虚拟网卡与容器端是如何对应?

通过ifindex,网卡索引号

# 分别查看test1,test2 容器的网卡索引
[root@docker ~]# docker exec -ti test1 cat /sys/class/net/eth0/ifindex
130
[root@docker ~]# docker exec -ti test2 cat /sys/class/net/eth0/ifindex
134#再通过在主机中找到虚拟网卡后面这个@ifxx的值,如果是同一个值,说明这个虚拟网卡和这个容器的eth0是配对的
[root@docker ~]#  ip a | grep @if
131: veth95f0f29@if130: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
135: veth25234c9@if134: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 

容器与宿主机的通信

添加端口映射:

# 启动容器的时候通过-p 参数添加宿主机端口与容器内部服务端口的映射
docker run --name test -d -p 8080:80 nginx:alpine
curl localhost:8080

端口映射通过iptables如何实现

访问本机的8088端口,数据包会从流入方向进入本机,因此涉及到PREROUTING【路由前】和INPUT链,我们是通过做宿主机与容器之间加的端口映射,所以肯定会涉及到端口转换,那哪个表是负责存储端口转换信息的呢,那就是nat表,负责维护网络地址转换信息的。

# 查看一下PREROUTING链的nat表
[root@docker ~]# iptables -t nat -nvL PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)pkts bytes target     prot opt in     out     source               destination         26  1488 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL参数解释:
-t 对指定的表进行操作
-n 以数字的方式显示ip,它会将ip直接显示出来,如果不加-n,则会将ip反向解析成主机名。
-v 详细模式;-vvv :越多越详细
-L 列出链chain上面的所有规则,如果没有指定链,列出表上所有规则

规则利用了iptables的addrtype【地址类型】扩展,匹配网络类型为本地的包

# 如何确定哪些是匹配本地的
[root@docker ~]# ip route show table local type local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
local 172.17.0.1 dev docker0 proto kernel scope host src 172.17.0.1
local 192.168.178.79 dev ens33 proto kernel scope host src 192.168.178.79 

也就是说目标地址类型匹配到这些的,会转发到我们的TARGET中,TARGET是动作,意味着对符合要求的数据包执行什么样的操作,最常见的为ACCEPT(接受)和DROP(终止),此处的target(目标)为docker,很明显docker不是标准的动作,那docker是什么呢?我们通常会定义自定义的链,这样把某类对应的规则放在自定义链中,然后把自定义的链绑定到标准的链路中,因此此处docker是自定义的链。那我们现在就来看一下docker这个自定义链上的规则。

[root@docker ~]# iptables -t nat -nvL DOCKER
Chain DOCKER (2 references)pkts bytes target     prot opt in     out     source               destination         17  1020 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.2:80

此条规则就是对主机收到的目的端口为8080的tcp流量进行DNAT转换,将流量发往172.17.0.2:80,172.17.0.2地址是不是就是我们上面创建的docker容器的ip地址,流量走到网桥上了,后面就走网桥的转发就ok了。所以,外加只需要访问192.168.178.79:8080就可以访问到容器中的服务了。

数据包在出来方向走postrouting链,查看一下规则
[root@docker ~]#  iptables -t nat -nvL POSTROUTING
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)pkts bytes target     prot opt in     out     source               destination         96  5925 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:80MASQUERADE这个动作其实是一种更灵活的SNAT,把原地址转换成主机的出口ip地址,解释一下这条规则的意思:
这条规则会将原地址为172.17.0.0/16的包(也就是从docker容器产生的包),并且不是从docker0网卡发出的,进行原地址转换,转发成主机网卡的地址。大概的过程就是ACK的包在容器里面发出来,会路由到网桥docker0,网桥根据宿主机的路由规则会转给宿主机网卡eth0,这时候包就从docker0网卡转到eth0网卡了,并从eth0网卡发出去,这时候这条规则就会生效了,把源地址换成了eth0的ip地址。

注意一下,刚才这个过程涉及到了网卡间包的传递,那一定要打开主机的ip_forward转发服务,要不然包转不了,服务肯定访问不到。

2)host模式

如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。

容器内部不会创建网络空间,共享宿主机的网络空间。

比如直接通过host模式创建mysql容器:

$ docker run --net host -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
$ curl localhost:3306
5.7.3 [M/Z5NGRy}mysql_native_password!

容器启动后,会默认监听3306端口,由于网络是host,因为可以直接通过宿主机的3306端口进行访问服务,效果等同于在宿主机直接启动mysqld的进程。

下面我们再创建个容器后简单看一下

[root@node4 ~]# docker run -it --rm --net=host sunrisenan/alpine:3.10.3 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000link/ether 00:50:56:b3:4c:8f brd ff:ff:ff:ff:ff:ffinet 192.168.6.244/24 brd 192.168.6.255 scope global ens192valid_lft forever preferred_lft foreverinet6 fe80::250:56ff:feb3:4c8f/64 scope link valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN link/ether 02:42:65:95:28:d8 brd ff:ff:ff:ff:ff:ffinet 172.6.244.1/24 brd 172.6.244.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:65ff:fe95:28d8/64 scope link valid_lft forever preferred_lft forever

run --rm是什么:

在Docker容器退出时,默认容器内部的文件系统仍然被保留,以方便调试并保留用户数据。

但是,对于foreground容器,由于其只是在开发调试过程中短期运行,其用户数据并无保留的必要,因而可以在容器启动时设置–rm选项,这样在容器退出时就能够自动清理容器内部的文件系统。

显然,–rm选项不能与-d同时使用,即只能自动清理foreground容器,不能自动清理detached容器

3)container模式

这个模式指定新创建的容器和已经存在的一个容器共享一个network namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP,端口范围等。同样,两个容器除了网络方面,其他的如文件系统,进程列表等还是隔离的,两个容器的进程可以通过IO网卡设备通信。

# 启动容器测试,共享mysql的网络空间
$ docker run --net host -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7# 新建一个容器,该容器的网络指向已经存在的mysql容器的网络空间
$ docker run -ti --rm --net=container:mysql busybox sh
/ # ps aux # 查看进行是没有mysql进程的,但却可以看到3306端口
PID   USER     TIME  COMMAND1 root      0:00 sh12 root      0:00 ps aux
/ # ifconfig | awk 'NR==2{print $2}'
addr:172.17.0.1
/ # netstat -tlp | grep 3306
tcp        0      0 :::3306     :::*       LISTEN      -
/ # telnet localhost 3306
Connected to localhost
J
5.7.34Z.  vl0mysql_native_password!#08S01Got packets out of orderConnection closed by foreign host

–rm 退出容器后,容器会自动删除

在一下特殊的场景中非常有用,例如kubernetes的pod,kubernetes为pod创建一个基础设施容器,同一pod下的其他容器都以container模式共享这个基础设施的网络命名空间,相互之间以localhost访问,构成一个统一的整体。

联合网络

[root@node4 ~]# docker run -d sunrisenan/nginx:v1.12.2
76f844e6057b6493a4a8933819ade7c29325ef17bc1fddc88bdf4c7ec60c620b
[root@node4 ~]#
[root@node4 ~]# docker ps -qa
76f844e6057b
[root@node4 ~]# docker run -ti --rm --net=container:76f844e6057b sunrisenan/nginx:curl bash
root@76f844e6057b:/# curl localhost
<!DOCTYPE html>
<html>
<title>Welcome to nginx!</title>
...

4)none模式

使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。

这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。

None模式示意图:

[root@node4 ~]# docker run -ti --rm --net=none  sunrisenan/alpine:3.10.3 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever

待整理:k8s

  • Node网络
  • pod网络

172.7.0.0/16 pod 172.7.21.0/24 172.7.22.0/24

10.4.7.0/34 节点 10.4.7.21 10.4.7.22

192.168.0.0/16 service

核心附件CNI网络插件 -> flannel/calico服务发现用插件 -> coredns服务暴露用插件 -> traefikGUI管理插件 -> Dashboard

service的三种代理模式

  • UserSpace
  • Iptables
  • Ipvs

service的四种类型

  • ClusterIP
  • NodePort
  • LoadBalancer
  • ExternalName

kube-proxy提供的是集群网络,而不是podIP

常见的K8S安装部署方式:

1. Minikube单节点微型K8S(仅供学习、预览使用)
2. 二进制安装部署(生产首选,新手推荐)
3. 使用Kuberadmin进行部署,K8S的部署工具,跑在K8S里(相对简单,熟手推荐)

【k8s】docker网络模式(必知)相关推荐

  1. 【计算机网络】网络基础必知必会

    网络基础必知必会 网络协议的体系结构 OSI 参考模型:应用层.表示层.会话层.传输层.网络层.数据链路层.物理层 五层协议的体系结构:应用层.传输层.网络层.数据链路层.物理层 TCP / IP 体 ...

  2. docker网络--理解linux底层实现机制、docker网络模式

    1. Linux网络基础 Linux内核具有非常成熟和高性能的TCP / IP堆栈实现.Docker网络使用linux内核的网络堆栈作为低级原语来创建高级网络驱动程序.简而言之,Docker网络就是 ...

  3. Docker网络模式与数据管理

    Docker网络模式与数据管理 前言 一.四种网络模式 (1)Host模式 (2)Container模式 (3)Bridge模式(默认) (4)None模式 (5)overlay2 二.自定义网络 ( ...

  4. Docker网络模式解析

    目录 前言 一.常用基本命令 (一)查看网络 (二)创建网络 (三)查看网络源数据 (四)删除网络 二.网络模式 (一)总体介绍 (二)容器实例内默认网络IP生产规则 (三)案例说明 1.bridge ...

  5. docker学习笔记(四):docker网络模式及桥接配置

    2019独角兽企业重金招聘Python工程师标准>>> 1.docker网络模式:有如下五种: host模式(--net=host)            container模式  ...

  6. 【Docke进阶篇】Docker网络模式与容器间通信

    本篇主要是Docker网络模式与容器间通信. 上一篇:[Docker基础篇]Docker核心概念与指令 文章目录 一.预备知识点 二.Docker网络模式 1.查看Docker网络模式 2.bridg ...

  7. docker网络模式

    目录 一.四种网络模式 1.1 Host模式 1.2 Container模式 1.3 Bridge模式(默认) 1.4 None模式(躺平) 二.自定义网络 2.1 查看网络模式列表 2.2 查看容器 ...

  8. 七、Docker网络模式详解

    目录 一.docker网络概述 1.docker网络实现的原理 2.容器的端口映射 1).端口映射 2).四种端口映射 3).端口映射演示 (1).随机端口映射(-P) (2).指定端口映射(-p 宿 ...

  9. Docker网络模式详解

    文章目录 一.docker网络概述 1.docker网络实现的原理 二. docker的网络模式 1.默认网络 2.使用docker run 创建Docker容器时,可以用--net或--networ ...

最新文章

  1. Thymeleaf 入门
  2. MySQL中的多表插入更新与MS-SQL的对比
  3. Python+Selenium操作select下拉框
  4. Ubuntu系统下载缓慢,以及更新源卡住不动(终极解决方案)
  5. C# PagedList 真分页
  6. Go语言【第九篇】:Go数据结构之:数组
  7. Spring Boot:(六)默认日志logback配置解析
  8. appuim + python 实现 趣头条 自动阅读
  9. PPT幻灯片母版在制作时的应用
  10. 贪吃蛇源码(C语言版)-学习版
  11. C++ rand的用法
  12. nodejs 中读取 package.json 文件内容
  13. 自定义通用信号处理核心板CMB-2C6657-1K7
  14. 成为一名优秀的Python工程师的方法
  15. 腾讯广告算法大赛2019
  16. 英语绕口令(转)[Blog synchronous]
  17. 直播画面已被主播锁定!输入正确的密码后可解锁画面。斗鱼直播主播锁定画面解锁方法(网页版)
  18. ubuntu慢?如何给 ubuntu 换源 提速
  19. 根据网络上的视频的m3u8文件通过ffmpeg进行合成视频
  20. Web Player TcPlayer

热门文章

  1. pgAdmin4卡在Please enter the password for the user ‘postgres‘ to connect the server - “PostgreSQL“
  2. 百度CEO李彦宏:电子商务平台将在年前发布
  3. java 声明式编程_声明式编程 - SegmentFault 思否
  4. Ubuntu 14.04 安装 USB无线网卡驱动
  5. 2023 华为 Datacom-HCIE 真题题库 06/12--含解析
  6. 数字图像位移传感器在大坝安全监测运用
  7. 电脑桌面计算机打开很慢,我的电脑打开很慢怎么办
  8. 仿百度贴吧回帖功能分析及代码示例
  9. 计算机网络:P3.1-数据链路层(上)
  10. 2021SC@SDUSC-SDUDOC-blog00