Docker容器间网络互联原理,讲不明白算我输
sinye56 2024-12-07 16:50 6 浏览 0 评论
目录
- 一、今天我们要搞明白的实验
- 二、前置网络知识
- 2.1、docker默认为我们创建的网络
- 2.2、怎么理解docker0网桥
- 2.3、什么是veth-pair技术?
- 三、同一个局域网中不同主机的互联原理
- 四、容器网络互通原理
- 五、实验环境
一、今天我们要搞明白的实验#
如上红字所描述:同一个宿主机上的不同容器之间的网络如何互通的???
二、前置网络知识#
2.1、docker默认为我们创建的网络#
我们安装完docker之后,docker daemon会为我们自动创建3个网络,如下:
Copy~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
e71575e3722a bridge bridge local
ab8e3d45575c host host local
0c9b7c1134ff none null local
其实docker有4种网络通信模型,分别是:bridge、host、none、container
默认的使用的网络模型是bridge,也是我们生产上会使用到的网络模型。
下文中跟大家分享docker容器互通原理到时候呢,用到的也是bridge网络模型,另外如果你之前不了解也没关系,可以看下我下面的这篇文章补一下,不难,一看就懂!
1、白日梦的Docker网络入门笔记
2.2、怎么理解docker0网桥#
另外,当我们安装完docker之后,docker会为我们创建一个叫docker0的网络设备
通过ifconfig命令可以查看到它,看起来它貌似和eth0网络地位相当,像是一张网卡。然而并不是,docker0其实是一个Linux网桥
Copy[root@vip ~]# 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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:b4:97:ee brd ff:ff:ff:ff:ff:ff
inet 10.4.7.99/24 brd 10.4.7.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:feb4:97ee/64 scope link
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:db:fe:ff:db 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:dbff:fefe:ffdb/64 scope link
valid_lft forever preferred_lft forever
何以见得?可以通过下面的命令查看操作系统上的网桥信息
Copy ~]# yum install bridge-utils
~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242f0a8c0be no veth86e2ef2
vethf0a8bcb
那大家怎么理解Linux网桥的概念呢?
其实大家可以把docker0理解成一台虚拟的交换机!然后像下面这样类比着理解,就会豁然开朗
1、它好比是大学在机房上课时,老师旁边的那个大大的交换机设备。
2、把机房里的电脑都连接在交换机上,类比成docker 容器作为一台设备都连接着宿主机上的docker0。
3、把交换机和机房中的机器的ip在同一个网段,类比成docker0、和你启动的docker容器的ip也同属于172网段。
Copy# docker0 ip是:
~]# ifconfig
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:db:fe:ff:db 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:dbff:fefe:ffdb/64 scope link
valid_lft forever preferred_lft forever
# 进入容器中查看ip是:
/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 13 bytes 1102 (1.0 KiB)
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
类比成这样:
2.3、什么是veth-pair技术?#
我们刚才做类比理解docker0的时候说:把机房里的电脑都连接在交换机上,类比成docker 容器作为一台设备都连接着宿主机上的docker0。那具体的实现落地实现用的是啥技术呢?
答案是:veth pair
veth pair的全称是:virtual ethernet,就是虚拟的以太网卡。
说到以太网卡大家都不陌生呀,不就是我们常见的那种叫eth0或者是ens的网络设备吗?
那这个veth pair是怎么玩的呢?有啥用呢?大家可以看下面这张图
veth-pair设备总是会成对的出现,用于连接两个不同network-namespace.
就上图来说,从network-namespace1的veth0中发送的数据会出现在 network-namespace2的veth1设备中。
虽然这种特性很好,但是如果出现有多个容器,你就会发现组织架构会越来越复杂,越来越乱
不过好在我们已经循序渐进的了解Linux网桥(docker0),以及这里的veth-pair设备,于是我们可以把整体的架构图重新绘制成下面这样
因为不同容器有自己隔离后的network-namespace所以他们都有自己的网络协议栈
那我们能不能找到容器里面的网卡和物理机上的哪张卡是一对网络vethpair设备呢?
如下:
Copy# 进入容器
~]# docker exec -ti 545ed62d3abf /bin/bash
/# apt-get install ethtool
/# ethtool -S eth0
NIC statistics:
peer_ifindex: 55
回到宿主机
Copy~]# ip addr
...
55: vethf0a8bcb@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ae:eb:5c:2f:7d:c3 brd ff:ff:ff:ff:ff:ff link-netnsid 10
inet6 fe80::aceb:5cff:fe2f:7dc3/64 scope link
valid_lft forever preferred_lft forever
意思是就是说,容器545ed62d3abf的eth0网卡和宿主机通过ip addr命令查看的网络设备标号55的设备组成一对vethpair设备,彼此流量互通!
三、同一个局域网中不同主机的互联原理#
先看个简单的,同一个局域网中的不同主机A、B之间是如何互联交换数据的。如下图
那,既然是同一个局域网中,说明A、B的ip地址在同一个网段,如上图就假设它们都在192.168.1.0网段。
还得再看下面这张OSI 7层网络模型图。
主机A向主机B发送数据,对主机A来说数据会从最上层的应用层一路往下层传递。比如应用层使用的http协议、传输层使用的TCP协议,那数据在往下层传递的过程中,会根据该层的协议添加上不同的协议头等信息。
根据OSI7层网络模型的设定,对于接受数据的主机B来说,它会接收到很多数据包!这些数据包会从最下层的物理层依次往上层传递,依次根据每一层的网络协议进行拆包。一直到应用层取出主机A发送给他的数据。
那么问题来了,主机B怎么判断它收到的数据包是否是发送给自己的呢?万一有人发错了呢?
答案是:根据MAC地址,逻辑如下。
Copyif 收到的数据包.MAC地址 == 自己的MAC地址{
// 接收数据
// 处理数据包
}else{
// 丢弃
}
那对于主机A来说,它想发送给主机B数据包,还不能让主机B把这个数据包扔掉,它只能中规中矩的按以太网网络协议要求封装将要发送出去的数据包,往下传递到数据链路层(这一层传输的数据要求,必须要有目标mac地址,因为数据链路层是基于mac地址做数据传输的)。
那数据包中都需要哪些字段呢?如下:
Copysrc ip = 192.168.1.2 //源ip地址,交换机
dst ip = 192.168.1.3 //目标ip地址
//本机的mac地址(保证从主机B回来的包正常送达主机A,且主机A能正常处理它)
src mac = 主机A的mac地址
dst mac = 主机B的mac地址//目标mac地址
其中的dst ip好说,我们可以直接固定写,或者通过DNS解析域名得到目标ip。
那dst mac怎么获取呢?
这就不得不说ARP协议了! ARP其实是一种地址解析协议,它的作用就是:以目标ip为线索,找到目的ip所在机器的mac地址。也就是帮我们找到dst mac地址!大概的过程如下几个step
推荐阅读:白日梦的DNS笔记
简述这个过程:主机A想给主机B发包,那需要知道主机B的mac地址。
- 主机A查询本地的arp 高速缓存中是否已经存在dst ip和dst mac地址的映射关系了,如果已存在,那就直接用。
- 本地arp高速缓存中不存在dst ip和dst mac地址的映射关系的话那就只能广播arp请求包,同一网段的所有机器都能收到arp请求包。
- 收到arp请求包的机器会对比arp包中的src ip是否是自己的ip,如果不是则直接丢弃该arp包。如果是的话就将自己的mac地址写到arp响应包中。并且它会把请求包中src ip和src mac的映射关系存储在自己的本地。
补充:
交换机本身也有学习能力,他会记录mac地址和交换机端口的映射关系。比如:mac=a,端口为1。
那当它接收到数据包,并发现mac=a时,它会直接将数据扔向端口1。
嗯,在arp协议的帮助下,主机A顺利拿到了主机B的mac地址。于是数据包从网络层流转到数据链路层时已经被封装成了下面的样子:
Copysrc ip = 192.168.1.2
src mac = 主机A的mac地址
dst ip = 192.168.1.3
dst mac = 主机B的mac地址
网络层基于ip地址做数据做转发
数据链路基于mac地址做数据转发
根据OIS7层网络模型,我们都知道数据包经过物理层发送到机器B,机器B接收到数据包后,再将数据包向上流转,拆包。流转到主机B的数据链路层。
那主机B是如何判断这个在数据链路层的包是否是发给自己的呢?
答案前面说了,根据目的mac地址判断。
Copy// 主机B
if 收到的数据包.MAC地址 == 自己的MAC地址{
if dst ip == 本机ip{
// 本地处理数据包
}else{
// 查询路由表,根据路由表的规则,将数据包转某个某卡、或者默认网关
}
}else{
// 直接丢弃
}
这个例子比较简单,dst ip就是主机B的本机ip 所以它自己会处理这个数据包。
那数据包处理完之后是需要给主机A一个响应包,那问题又来了,响应包该封装成什么样子呢?对主机B来说响应包也需要src ip、src mac、dst ip、dst mac
Copysrc ip = 192.168.1.3
src mac = 主机B的mac地址
dst ip = 192.168.1.2
src mac = 主机A的mac地址 (之前通过arp记录在自己的arp高速缓存中了,所以,这次直接用)
同样的道理,响应包也会按照如下的逻辑被主机A接受,处理。
Copy// 主机A
if 收到的数据包.MAC地址 == 自己的MAC地址{
if dst ip == 本机ip{
// 本地处理数据包
}else{
// 查询路由表,根据路由表的规则,将数据包转某个某卡、或者默认网关
}
}else{
// 直接丢弃
}
这一次,让我在百度告诉你,当你请求www.baidu.com时都发生了什么?
四、容器网络互通原理#
有了上面那些知识储备呢?再看我们今天要探究的问题,就不难了。
如下红字部分:同一个宿主机上的不同容器是如何互通的?
那我们先分别登陆容器记录下他们的ip
Copy9001的ip是:172.17.0.2
9002的ip是:172.17.0.3
先看实验效果:在9001上curl9002
Copy/# curl 172.7.88.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
...
实验结果是网络互通!
我们再完善一下上面的图,把docker0、以及两个容器的ip补充上去,如下图:
那两台机器之前要通信是要遵循OSI网络模型、和以太网协议的。
我们管172.17.0.2叫做容器2
我们管172.17.0.3叫做容器3
比如我们现在是从:容器2上curl 容器3,那么容器2也必须按照以太网协议将数据包封装好,如下
Copysrc ip = 172.17.0.2
src mac = 容器2的mac地址
dst ip = 172.17.0.3
dst mac = 容器3的mac地址 ???
那现在的问题是容器3的mac地址是多少?
删掉所有容器,重新启动,方便实验抓包
容器2会先查自己的本地缓存,如果之前没有访问过,那么缓存中也没有任何记录!
Copy:/# arp -n
不过没关系,还有arp机制兜底,于是容器2会发送arp请求包,大概如下
Copy1、这是一个arp请求包
2、我的ip地址是:172.17.0.2
3、我的mac地址是:容器2的mac地址
4、请问:ip地址为:172.17.0.3的机器,你的mac地址是多少?
容器2会查询自己的路由表,将这个arp请求从自己的gateway发送出去
Copy/# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.7.88.1 0.0.0.0 UG 0 0 0 eth0
172.7.88.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
我们发现容器2的网关对应的网络设备的ip就是docker0的ip地址,并且经由eth0发送出去!
哎?eth0不就是我们之前说的veth-pair设备吗?
并且我们通过下面的命令可以知道它的另一端对应着宿主机上的哪个网络设备:
Copy/# ethtool -S eth0
NIC statistics:
peer_ifindex: 53
而且我们可以下面的小实验,验证上面的观点是否正确
Copy# 在容器中ping百度
~]# ping 220.181.38.148
# 在宿主机上抓包
~]# yum install tcpdump -y
~]# tcpdump -i ${vethpair宿主机侧的接口名} host 220.181.38.148
...
所以说从容器2的eth0出去的arp请求报文会同等的出现在宿主机的第53个网络设备上。
通过下面的这张图,你也知道第53个网络设备其实就是下图中的veth0-1
所以这个arp请求包会被发送到docker0上,由docker0拿到这个arp包发现,目标ip是172.17.0.3并不是自己,所以docker0会进一步将这个arp请求报文广播出去,所有在172.17.0.0网段的容器都能收到这个报文!其中就包含了容器3!
那容器3收到这个arp报文后,会判断,哦!目标ip就是自己的ip,于是它将自己的mac地址填充到arp报文中返回给docker0!
同样的我们可以通过抓包验证,在宿主机上
Copy# 在172.17.0.2容器上ping172.17.0.3
/# ping 172.17.0.3
~]# tcpdump -i vethdb0d222
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vethdb0d222, link-type EN10MB (Ethernet), capture size 262144 bytes
17:25:30.218640 ARP, Request who-has 172.17.0.3 tell 172.17.0.2, length 28
17:25:30.218683 ARP, Reply 172.17.0.3 is-at 02:42:ac:11:00:03 (oui Unknown), length 28
17:25:30.218686 IP 172.17.0.2.54014 > 172.17.0.3.http: Flags [S], seq 3496600258, win 29200, options [mss 1460,sackOK,TS val 4503202 ecr 0,nop,wscale 7], length 0
于是容器2就拿到了容器3的mac地址,以太网数据包需要的信息也就齐全了!如下:
Copysrc ip = 172.17.0.2
src mac = 容器2的mac地址
dst ip = 172.17.0.3
dst mac = 容器3的mac地址
再之后容器2就可以和容器3正常互联了!
容器3会收到很多数据包,那它怎么知道哪些包是发给自己的,那些不是呢?可以参考如下的判断逻辑
Copyif 响应包.mac == 自己的mac{
// 说明这是发给自己包,所以不能丢弃
if 响应包.ip == 自己的ip{
// 向上转发到osi7层网络模型的上层
}else{
// 查自己的route表,找下一跳
}
}else{
// 直接丢弃
}
五、实验环境#
Copy# 下载
~]# docker pull registry.cn-hangzhou.aliyuncs.com/changwu/nginx:1.7.9-nettools
# 先启动1个容器
~]# docker run --name mynginx1 -i -t -d -p 9001:80 nginx-1.7.9-nettools:latest
eb569b938c07e95ccccbfc654c1fee6364eea55b20f5394382ff42b4ccf96312
~]# docker run --name mynginx2 -i -t -d -p 9002:80 nginx-1.7.9-nettools:latest
545ed62d3abfd63aa9c3ae196e9d7fe6f59bbd2e9ae4e6f2bd378f23587496b7
# 验证
~]# curl 127.0.0.1:9001
- 上一篇:Linux接口之虚拟网络接口介绍
- 下一篇:Docker容器网络bridge
相关推荐
- linux 查看当前应用内存状况,以及内存参数含义
-
1、查看进程号ps-ef|greptomcat2、查看当前内存分配,200ms打印一次jstat-gc进程号2001jstat-gc344802001S0CS1C...
- 如何显示 Linux 系统上的可用内存?这几个命令很好用!
-
在Linux系统中,了解可用内存是优化系统性能、故障排查以及资源管理的重要一环。本文将详细介绍如何在Linux系统上显示可用内存,包括多种方法和工具的使用。在讨论可用内存之前,我们需要了解一些...
- Linux 下查看内存使用情况方法总结
-
Q:我想监视Linux系统的内存使用情况,在Linux下有哪些视图或者命令行工具可用呢?在做Linux系统优化的时候,物理内存是其中最重要的一方面。自然的,Linux也提供了非常多的方法来监控宝贵的内...
- 2、linux命令-用户管理
-
linux命令-用户管理用户切换[root@eric~]#sueric#切换到用户eric[eric@ericroot]$[eric@ericroot]$su#切换到rootPas...
- Centos 7 进入单用户模式详解
-
1、开机在启动菜单按e进入编辑模式找到linux16行,在最后添加init=/bin/sh编辑完后,按ctrl+x退出2、进单用户模式后,使用passwd修改密码,提示以下错误:passwd:Aut...
- 每日一个Linux命令解析——newusers
-
newusers:在Linux系统中,newusers是一个用于批量创建用户的命令。它从一个文件中读取多行用户信息,每行描述一个用户的详细信息,并根据这些信息创建多个用户或对现有用户进行批量修改。一...
- openEuler操作系统管理员指南:管理用户与用户组
-
在Linux中,每个普通用户都有一个账户,包括用户名、密码和主目录等信息。除此之外,还有一些系统本身创建的特殊用户,它们具有特殊的意义,其中最重要的是管理员账户,默认用户名是root。同时Linux也...
- Linux用户管理
-
1、用户信息文件/etc/passwdroot:x:0:0:root:/root:/bin/bash第一列:用户名第二列:密码位第三列:用户ID0超级用户UID。如果用户UID...
- centos7基础-用户、组、权限管理
-
用户和组(1)用户、组、家目录的概念linux系统支持多用户,除了管理员,其他用户一般不应该使用root,而是应该向管理员申请一个账号。组类似于角色,系统可以通过组对有共性的用户进行统一管理。每个用户...
- LINUX基础 ----------组及用户的概念
-
在Linux中,用户和组都是非常重要的概念,可以控制文件访问权限和资源的管理。用户是标识一个进程、应用程序或系统管理员的账号,Linux中每个用户用一个用户ID(UID)来标识。对于一个...
- 从零入门Linux(四)用户与权限管理
-
在Linux系统中,用户和权限管理是系统安全的重要组成部分。通过合理配置用户和权限,可以确保系统的安全性和资源的合理分配。以下是一些与用户和权限管理相关的常用命令和概念。1.用户管理1.1添加...
- 如何在 Linux 中管理用户?
-
在Linux系统中,用户是系统资源的主要使用者,每个用户都有一个唯一的标识符(用户ID)。为了更好地组织和管理用户,Linux还引入了用户组的概念。用户组是用户的集合,有助于更有效地分配权限和资...
- 在 Linux 中将用户添加到特定组的四种方法
-
在Linux多用户操作系统中,用户组管理是系统安全架构的基石。通过合理的组权限分配,管理员可以实现:精确控制文件访问权限(chmod775project/)简化批量用户权限管理(setfacl-...
- 我不是网管 - 如何在Ubuntu Linux下创建sudo用户
-
Sudo用户是Linux系统的普通用户,具有一定的管理权限,可以对系统执行管理任务。在Linux中,root是超级用户,拥有完全的管理权限,但不建议将root凭证授予其他用户或作为r...
- Linux创建普通用户,为密钥方式登录做准备
-
Hi,我是聪慧苹果8,就是江湖上人见人爱、花见花开,土到掉榨的Linux爱好者,一起学习吧!上一篇关于SSH安全加固的文字,有网友点评通过密钥登录更加安全,先创建一个普通用户,拒绝直接使用密码登录,这...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)