Docker默认桥接网络是如何工作的
1. 启动一个Docker容器
一般来说,我们起一个容器比如一个简单的nginx服务会向这样
docker run -d --rm nginx:XXX
OK容器起来了,但是并不能通过宿主机被外面的机器访问,此时我们需要对容器做一个端口映射,像是这样
docker run -d --rm -p 80:80 nginx:XXX
这样外面就可以通弄映射出来的宿主机端口访问到容器里的服务了。那么这在这其中流量包时如何在网口,netfilter,路由,网络隔离的namespace,linux虚拟网桥,veth pair设备间送到指定的容器内进程呢?
2. 先说说docker的网络资源隔离是如何做到的
2.1 Network Namespace
linux network namespace(网络命名空间),linux 可以通过namespce对系统的资源做隔离,包括网络资源,我们先做一个小实验。对这一块比较熟悉的同学可以跳过。
建立一个net1和net2的网络名称空间
# 创建net1
ip netns add net1
# 创建net2
ip netns add net2
# 打印名称空间
ip netns list
# 在指定的名称空间执行命令,ip netns exec [network namespace name] [command]
ip netns exec net1 ip addr
我们可以看到回显为
[root@localhost ~]# ip netns exec net1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@localhost ~]#
OK, 看不到宿主机网络的任何信息,完全是一个空的网络配置。这就是network namespace的效果。
2.2 不同Network Namespace间的通讯
在docker 容器中,不同的容器是可以互相访问的,也就是说namespace间是可以互通的,那么应该如何做到?。
先来了解下veth pair这个东西,它是linux的中虚拟网络设备,注意是“虚拟”,是模拟出来的并不真实存在。veth设备总是成对出现,也叫veth pair,一端发送的数据会由另外一端接收。[滑稽]机灵的同学应该已经发现了,有两端而且两段的数据互通这特么不就是网线么,哎理解的很到位,再展开一下上面的net1, net2不就是两台虚机的主机(从网络资源上讲可以这么说)么,那么我把它们之间用虚拟的网线(veth pair)一连那么不就通了。bingo理论行对了,让我们来实践一下,继续上一实验的环境。
先创建一对veth pair设备 veth1, veth2
ip link add veth1 type veth peer name veth2
在用这对veth设备连接两个namespace
ip link set veth1 netns net1
ip link set veth2 netns net2
进入net1配置并启动虚拟网卡
[root@localhost ~]# ip netns exec ip addr
Cannot open network namespace "ip": No such file or directory
[root@localhost ~]# ip netns exec net1 bash
[root@localhost ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
7: veth1@if6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 0e:ad:28:91:48:51 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@localhost ~]# ip addr add 10.0.0.1/24 dev veth1
[root@localhost ~]# ip link set dev veth1 up
[root@localhost ~]#
进入net2配置并启动网卡
[root@localhost ~]# ip netns exec net2 bash
[root@localhost ~]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth2@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 4a:4a:a2:0a:cc:05 brd ff:ff:ff:ff:ff:ff link-netnsid 0
[root@localhost ~]# ip addr add 10.0.0.2/24 dev veth2
[root@localhost ~]# ip link set dev veth2 up
[root@localhost ~]#
OK, 理论上这就相当于两台直连主机(就好像你的一台pc直接把网线怼到另一台pc),让我们来ping一下
# 进入net1 ping net2的地址
[root@localhost ~]# ip netns exec net1 bash
[root@localhost ~]# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.069 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.055 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.065 ms
^C
--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 0.055/0.065/0.073/0.010 ms
[root@localhost ~]#
通了ok,让我们把vnet2 的veth2 down 了试下(相当于把对端pc的网线拔了)
[root@localhost ~]# ip netns exec net2 bash
[root@localhost ~]# ip link set dev veth2 down
[root@localhost ~]# exit
exit
[root@localhost ~]# ip netns exec net1 bash
[root@localhost ~]# ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
^C
--- 10.0.0.2 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 3999ms
[root@localhost ~]#
意料之中,完全不同(都拔线了咋可能通)。
2.3 比起插线(veth pair)更好的方案 bridge
细心的你一定想到了一个问题,我在机器上有这么多的容器每个容器都要实现互通,难道每个namespace之间都要通过veth pair来连接么,这样得差n的二次方的网线啊,这也太离谱的。
这个问题可以回归到现实是如果做到的,在公司的一个网段里有那么多的主机,互插线的话网口都不够用,那么插哪里呢?交换机。由交换机帮我把对应的数据送到对应的网口,像是物流站一样,所有的pc把网线插上去就完事。这里我们引入另一个linux的虚拟网络设备 bridge(网桥),你可以理解为这就是一个二层交换机的功能。所以无论我们新增多少容器,都可以通过veth pair 连接到 bridge上实现容器互通,而在docker里这个网桥就是docker0。
[root@localhost ~]# ip addr
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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:9b:21:ef brd ff:ff:ff:ff:ff:ff
inet 192.168.144.128/24 brd 192.168.144.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 fe80::2ac9:5d64:5e4b:6619/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:91:d3:be:6c 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:91ff:fed3:be6c/64 scope link
valid_lft forever preferred_lft forever
5: veth299c707@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 8a:eb:25:8f:78:f0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::88eb:25ff:fe8f:78f0/64 scope link
valid_lft forever preferred_lft forever
docker 会在启动是创建docker0网桥,在启动容器时创建一对veth pair设备,一端会放置在容器里命名为eth0,另一端会放在host主机的上叫做veth*****并加载网桥上。
我们可以手动模拟这个过程,我们新建一个网桥br0, 再建立两个网络名称空间net3, net4, 并通过veth pair把它们连接到这个网桥上。
# 创建网络名称空间net3 net4
ip netns add net3
ip netns add net4
# 创建两队veth pair设备 veth*in 放置于namespace, veth*out放置于网桥
ip link add veth3in type veth peer name veth3out
ip link add veth4in type veth peer name veth4out
# 连接net3并配置ip
ip link set veth3in netns net3
ip netns exec net3 ip addr add 10.0.0.3/24 dev veth3in
ip netns exec net3 ip link set dev veth3in up
# 连接net4并配置ip
ip link set veth4in netns net4
ip netns exec net4 ip addr add 10.0.0.4/24 dev veth4in
ip netns exec net4 ip link set dev veth4in up
# 可以试不连网桥是否ping通
# 创建br0网桥
ip link add name br0 type bridge
# 连接网桥
ip link set dev veth3out master br0
ip link set dev veth3out up
ip link set dev veth4out master br0
ip link set dev veth4out up
# 先网桥还没有启动你看看是否能ping通
# 启动网桥
ip link set br0 up
# 启动网桥后net3 终于能ping通net4了
如果宿主机想访问这个bridge上的namespace怎么办,再加个veth pair 给一端配置ip 另一端接到bridge上? 正确,但是不必这么麻烦。其实我们在ip addr 是看到的那个br0并不是网桥的本地,br0也只是网桥的一个口子罢了,我们是可给这个OK配置上IP直接访问在这个二层网络的,像是这样:
[root@localhost ~]# ip addr add 10.0.0.254/24 dev br0
[root@localhost ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.144.2 0.0.0.0 UG 100 0 0 ens33
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 br0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.144.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
[root@localhost ~]# ping 10.0.0.4
PING 10.0.0.4 (10.0.0.4) 56(84) bytes of data.
64 bytes from 10.0.0.4: icmp_seq=1 ttl=64 time=0.068 ms
64 bytes from 10.0.0.4: icmp_seq=2 ttl=64 time=0.051 ms
64 bytes from 10.0.0.4: icmp_seq=3 ttl=64 time=0.048 ms
^C
--- 10.0.0.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2024ms
rtt min/avg/max/mdev = 0.048/0.055/0.068/0.012 ms
[root@localhost ~]#
所以我们在安装了docker的主机中可以看到带有与ip 172.17.0.1 的docker0网桥。
2.4 小总结
到这一节,其实就是run一个容器时不加端口映射的全部情况了。可以梳理下就是。docker 通过Network Namespace, bridge和veth pair这三个虚拟设备实现一个简单的二层网络,不同的namespace实现了不同容器的网络隔离让他们分别有自己的ip,通过veth pair 连接到docker0网桥上实现了容器间和宿主机的互通。其实就这么简单。
3. docker端口映射原理
docker 的端口映射是通过netfilter实现的,netfilter是内核的一个数据包处理框架,可以在数据包在路由前,路由后,进入用户态前,从用户态出来后等等好几个点对数据包进行处理包括但不限于拦截,源地址转换,目的地址换等,用户态表现为iptables工具和防火墙。一般而言都喜欢称呼为docker的端口映射是通过iptables实现,其实iptables只是用户态的一个规则下发工具而已,实际工作的是netfilter,看个人理解吧,其实都差不多。关于iptables的使用内容太多,此处不加以赘述如果有同学这方面知识比较生疏强烈安利一篇博客[https://www.zsythink.net/archives/tag/iptables/]。
一下内容我都是简历在对iptables规则较为熟悉的前提下
3.1 数据包是如何到容器里面的又是如何从容器出去的
我们先创建一个容器在开放8000端口的服务到宿主机的8000端口,命令如下:
# 用别的服务也可以,都行,映射了端口就好
docker run -d -p 8000:8000 centos:7 python -m SimpleHTTPServer
确认端口开发成功,外部可以访问后我们先来看下docker在iptables配置了什么
# 查看nat表(这个表负责转发和地址转换)
[root@localhost ~]# iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 32 packets, 4465 bytes)
pkts bytes target prot opt in out source destination
2 112 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 25 packets, 3913 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 184 packets, 13926 bytes)
pkts bytes target prot opt in out source destination
10 740 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT 185 packets, 14010 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * docker0 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match src-type LOCAL
0 0 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:8000
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8000 to:172.17.0.2:8000
[root@localhost ~]#
可以看到在 PREROUTING 链中(路由前)有一条自定义的DOCKER链,DOCKER链中有一条规则,翻译下就是“只要是tcp的包目的地址的端口是8000的统统把目的地址改为172.17.0.2,目的端口改为8000”, 这样一来原先的数据包src_ip:src_port->dst_ip:8000就会在路由前变成src_ip:src_port->172.17.0.2:8000。再看路由表:
[root@localhost ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.144.2 0.0.0.0 UG 100 0 0 ens33
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 br0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.144.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
匹配到172.17.0.0这条,命中路由走input这条链,被直接发往Iface:docker0也就是网桥在host主机的这个口子上。然后这个数据包会通过veth pair 设备走到容器中。
3.2 抓包验证
口说无凭眼见为实,我觉得只要兄弟们不是明天项目要上限有闲功夫来看我得我文章的都应该去抓个包看看。
同事对物理网卡和docker0抓包
[root@localhost ~]# tcpdump -i ens33 -n -vvv tcp port 8000
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
00:31:38.768744 IP (tos 0x0, ttl 64, id 28655, offset 0, flags [DF], proto TCP (6), length 60)
192.168.144.129.47106 > 192.168.144.128.irdmi: Flags [S], cksum 0x3e4b (correct), seq 2908588551, win 29200, options [mss 1460,sackOK,TS val 12707304 ecr 0,nop,wscale 7], length 0
00:31:38.768941 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 60)
192.168.144.128.irdmi > 192.168.144.129.47106: Flags [S.], cksum 0xa281 (incorrect -> 0x90e0), seq 2883195023, ack 2908588552, win 28960, options [mss 1460,sackOK,TS val 12710174 ecr 12707304,nop,wscale 7], length 0
00:31:38.772124 IP (tos 0x0, ttl 64, id 28656, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 192.168.144.128.irdmi: Flags [.], cksum 0x2fe5 (correct), seq 1, ack 1, win 229, options [nop,nop,TS val 12707307 ecr 12710174], length 0
00:31:38.772185 IP (tos 0x0, ttl 64, id 28657, offset 0, flags [DF], proto TCP (6), length 136)
192.168.144.129.47106 > 192.168.144.128.irdmi: Flags [P.], cksum 0x0f8c (correct), seq 1:85, ack 1, win 229, options [nop,nop,TS val 12707307 ecr 12710174], length 84
00:31:38.772281 IP (tos 0x0, ttl 63, id 57777, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.128.irdmi > 192.168.144.129.47106: Flags [.], cksum 0xa279 (incorrect -> 0x2f90), seq 1, ack 85, win 227, options [nop,nop,TS val 12710177 ecr 12707307], length 0
00:31:38.779728 IP (tos 0x0, ttl 63, id 57778, offset 0, flags [DF], proto TCP (6), length 69)
192.168.144.128.irdmi > 192.168.144.129.47106: Flags [P.], cksum 0xa28a (incorrect -> 0x6fab), seq 1:18, ack 85, win 227, options [nop,nop,TS val 12710184 ecr 12707307], length 17
00:31:38.780273 IP (tos 0x0, ttl 63, id 57779, offset 0, flags [DF], proto TCP (6), length 990)
192.168.144.128.irdmi > 192.168.144.129.47106: Flags [FP.], cksum 0xa623 (incorrect -> 0x8580), seq 18:956, ack 85, win 227, options [nop,nop,TS val 12710185 ecr 12707307], length 938
00:31:38.780359 IP (tos 0x0, ttl 64, id 28658, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 192.168.144.128.irdmi: Flags [.], cksum 0x2f6d (correct), seq 85, ack 18, win 229, options [nop,nop,TS val 12707316 ecr 12710184], length 0
00:31:38.780566 IP (tos 0x0, ttl 64, id 28659, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 192.168.144.128.irdmi: Flags [.], cksum 0x2bb3 (correct), seq 85, ack 957, win 243, options [nop,nop,TS val 12707316 ecr 12710185], length 0
00:31:38.780806 IP (tos 0x0, ttl 64, id 28660, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 192.168.144.128.irdmi: Flags [F.], cksum 0x2bb2 (correct), seq 85, ack 957, win 243, options [nop,nop,TS val 12707316 ecr 12710185], length 0
00:31:38.780864 IP (tos 0x0, ttl 63, id 55149, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.128.irdmi > 192.168.144.129.47106: Flags [.], cksum 0x2bc1 (correct), seq 957, ack 86, win 227, options [nop,nop,TS val 12710186 ecr 12707316], length 0
^C
[root@localhost ~]# tcpdump -i docker0 -n -vvv tcp port 8000
tcpdump: listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
00:31:38.768858 IP (tos 0x0, ttl 63, id 28655, offset 0, flags [DF], proto TCP (6), length 60)
192.168.144.129.47106 > 172.17.0.2.irdmi: Flags [S], cksum 0xe360 (correct), seq 2908588551, win 29200, options [mss 1460,sackOK,TS val 12707304 ecr 0,nop,wscale 7], length 0
00:31:38.768929 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.irdmi > 192.168.144.129.47106: Flags [S.], cksum 0xfd6b (incorrect -> 0x35f6), seq 2883195023, ack 2908588552, win 28960, options [mss 1460,sackOK,TS val 12710174 ecr 12707304,nop,wscale 7], length 0
00:31:38.772152 IP (tos 0x0, ttl 63, id 28656, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 172.17.0.2.irdmi: Flags [.], cksum 0xd4fa (correct), seq 1, ack 1, win 229, options [nop,nop,TS val 12707307 ecr 12710174], length 0
00:31:38.772190 IP (tos 0x0, ttl 63, id 28657, offset 0, flags [DF], proto TCP (6), length 136)
192.168.144.129.47106 > 172.17.0.2.irdmi: Flags [P.], cksum 0xb4a1 (correct), seq 1:85, ack 1, win 229, options [nop,nop,TS val 12707307 ecr 12710174], length 84
00:31:38.772270 IP (tos 0x0, ttl 64, id 57777, offset 0, flags [DF], proto TCP (6), length 52)
172.17.0.2.irdmi > 192.168.144.129.47106: Flags [.], cksum 0xfd63 (incorrect -> 0xd4a5), seq 1, ack 85, win 227, options [nop,nop,TS val 12710177 ecr 12707307], length 0
00:31:38.779674 IP (tos 0x0, ttl 64, id 57778, offset 0, flags [DF], proto TCP (6), length 69)
172.17.0.2.irdmi > 192.168.144.129.47106: Flags [P.], cksum 0xfd74 (incorrect -> 0x14c1), seq 1:18, ack 85, win 227, options [nop,nop,TS val 12710184 ecr 12707307], length 17
00:31:38.780084 IP (tos 0x0, ttl 64, id 57779, offset 0, flags [DF], proto TCP (6), length 990)
172.17.0.2.irdmi > 192.168.144.129.47106: Flags [FP.], cksum 0x010e (incorrect -> 0x2a96), seq 18:956, ack 85, win 227, options [nop,nop,TS val 12710185 ecr 12707307], length 938
00:31:38.780389 IP (tos 0x0, ttl 63, id 28658, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 172.17.0.2.irdmi: Flags [.], cksum 0xd482 (correct), seq 85, ack 18, win 229, options [nop,nop,TS val 12707316 ecr 12710184], length 0
00:31:38.780578 IP (tos 0x0, ttl 63, id 28659, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 172.17.0.2.irdmi: Flags [.], cksum 0xd0c8 (correct), seq 85, ack 957, win 243, options [nop,nop,TS val 12707316 ecr 12710185], length 0
00:31:38.780818 IP (tos 0x0, ttl 63, id 28660, offset 0, flags [DF], proto TCP (6), length 52)
192.168.144.129.47106 > 172.17.0.2.irdmi: Flags [F.], cksum 0xd0c7 (correct), seq 85, ack 957, win 243, options [nop,nop,TS val 12707316 ecr 12710185], length 0
00:31:38.780847 IP (tos 0x0, ttl 64, id 55149, offset 0, flags [DF], proto TCP (6), length 52)
172.17.0.2.irdmi > 192.168.144.129.47106: Flags [.], cksum 0xd0d6 (correct), seq 957, ack 86, win 227, options [nop,nop,TS val 12710186 ecr 12707316], length 0
^C
11 packets captured
11 packets received by filter
0 packets dropped by kernel
可以明显看到目的地址192.168.144.128:8000 在路由前被篡改为了172.17.0.2:8000,并在数据包返回时iptables自动帮我们吧返回的数据包源地址改回了192。168.144.128:8000。
3.2 手动添加端口映射
既然docker自己都是通过iptables实现映射的那么,当然我们手敲命令也可以实现同样的效果。我们新增一条host主机9000端口,命令如下
iptables -t nat -A DOCKER -p tcp --dport 9000 -j DNAT --to-destination 172.17.0.2:8000
# 访问成功,丝滑
[root@localhost ~]# curl 192.168.144.128:9000
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
<li><a href=".dockerenv">.dockerenv</a>
<li><a href="anaconda-post.log">anaconda-post.log</a>
<li><a href="bin/">bin@</a>
<li><a href="dev/">dev/</a>
<li><a href="etc/">etc/</a>
<li><a href="home/">home/</a>
<li><a href="lib/">lib@</a>
<li><a href="lib64/">lib64@</a>
<li><a href="media/">media/</a>
<li><a href="mnt/">mnt/</a>
<li><a href="opt/">opt/</a>
<li><a href="proc/">proc/</a>
<li><a href="root/">root/</a>
<li><a href="run/">run/</a>
<li><a href="sbin/">sbin@</a>
<li><a href="srv/">srv/</a>
<li><a href="sys/">sys/</a>
<li><a href="tmp/">tmp/</a>
<li><a href="usr/">usr/</a>
<li><a href="var/">var/</a>
</ul>
<hr>
</body>
</html>
3.3 小节
docker对于转发的实现还是比较简单的,只要耐心的去查看相应的转发规则都可以读懂数据的走象,至于在容器里发出的请求,他的snat是如果做的,本地的docker-proxy的服务存在的意义可以自己好好思考下,我这里就不多BB了。
Docker默认桥接网络是如何工作的的更多相关文章
- docker 配置桥接网络
2.5 docker配置桥接网络(上): 为了使本地网络中的机器和Docker 容器更方便的通信,我们经常会有将Docker容器 配置到和主机同一网段的需求. 这个需求其实很容器实现, 我们只需要将D ...
- Docker之 默认桥接网络与自定义桥接网卡
docker引擎会默认创建一个docker0网桥,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和宿主机都放到同一个二层网络. 1. docker如何使用网桥 1.1 Linux虚拟网桥的特点 ...
- docker配置桥接网络
[root@localhost ~]# cd /etc/sysconfig/network-scripts/ [root@localhost network-scripts]# cp ifcfg-et ...
- docker从零开始网络(二)桥接网络
使用桥接网络 在网络方面,桥接网络是链路层设备,它在网络段之间转发流量.桥接网络可以是硬件设备或在主机内核中运行的软件设备. 就Docker而言,桥接网络使用软件桥接器,该软件桥接器允许连接到同一桥接 ...
- Docker、K8S网络工作原理
一.Docker 网络模式 在讨论 Kubernetes 网络之前,让我们先来看一下 Docker 网络.Docker 采用插件化的网络模式,默认提供 bridge.host.none.overlay ...
- 高级网络功能(Docker支持的网络定制配置)
网络的高级知识,包括网络的启动和配置参数.DNS的使用配置.容器访问和端口映射的相关实现. 在一些具体场景中,Docker支持的网络定制配置,通过Linux命令来调整.补充.甚至替换Docker默认的 ...
- docker单主机网络
当你安装Docker时,它会自动创建三个网络.你可以使用以下docker network ls命令列出这些网络: [root@localhost ~]# docker network ls NETWO ...
- Docker 四种网络模式
原文 https://www.cnblogs.com/gispathfinder/p/5871043.html 我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络 ...
- Kubernetes网络之Flannel工作原理
目录 1.Docker网络模式 1.1 bridge网络的构建过程 1.2 外部访问 2.Kubernetes网络模式 2.1 同一个Pod中容器之间的通信 2.2 不同Pod中容器之间的通信 2.3 ...
随机推荐
- Nacos源码系列—订阅机制的前因后果(上)
点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友. 项目源码地址:公众号回复 nacos,即可免费获取源码 前因 我们在了解Nacos订阅机制之前, ...
- 详细剖析pyecharts大屏的Page函数配置文件:chart_config.json
目录 一.问题背景 二.揭开json文件神秘面纱 三.巧用json文件 四.关于Table图表 五.同步讲解视频 5.1 讲解json的视频 5.2 讲解全流程大屏的视频 5.3 讲解全流程大屏的文章 ...
- 修改Docker容器默认时区
运行docker容器默认时区为0区 # 运行一个nginx服务器 docker run -d -p 80:80 --name nginx nginx # 进入容器shell docker exec - ...
- 自学c语言
C 语言是一种通用的.面向过程式的计算机程序设计语言. 当前最新的 C 语言标准为 C18 前期准备 C 编译器 写在源文件中的源代码是人类可读的源.它需要"编译",转为机器语言 ...
- 场景实践:基于 IntelliJ IDEA 插件部署微服务应用
体验简介 阿里云云起实验室提供相关实验资源,点击前往 本场景指导您把微服务应用部署到 SAE 平台: 登陆 SAE 控制台,基于 jar 包创建应用 基于 IntelliJ IDEA 插件更新 SAE ...
- [BJOI2014]想法
参考 P4581传送门 题意:给DAG,问每个点可以由多少个叶子到达. 思路: 随机化!!(题面有提示) 这道题利用在一个范围内随机的数期望均分范围的性质. 直接每个叶子在\([0,Max\_Rand ...
- MySql实例关于ifnull,count,case when,group by(转力扣简单)
给定表 customer ,里面保存了所有客户信息和他们的推荐人. id | name | referee_id|+------+------+-----------+| 1 | Will ...
- Anaconda新建虚拟环境并添加到Jupyter Notebook
可参考:https://www.jianshu.com/p/ab9ae548b253 虚拟环境是Python的隔离工作副本.这意味着每个环境都可以具有自己的依赖关系,甚至可以具有自己的Python版本 ...
- Linux查看系统参数配置
Linux查看系统参数 1.查看内存(以GB为单位) [root@rac1 ~]# free -g total :内存总数,物理内存总数 used :已使用内存 free :空闲的内存数 shared ...
- Apache Shiro反序列化漏洞(Shiro550)
1.漏洞原理: Shiro 是 Java 的一个安全框架,执行身份验证.授权.密码.会话管理 shiro默认使用了CookieRememberMeManager,其处理cookie的流程是:得到rem ...