Docker 使用 Flannel 跨主机通信
通信的方式
- 路由
- 虚拟包头
flannel 网络
概念
Flannel 是 Docker 提供的一种网络解决方案,它旨在为容器提供简单的 IP 地址管理和跨主机通信。Flannel 使用了一些现有的技术,比如 Linux 内核的 TUN/TAP 接口和 ETCD 分布式键值存储,来实现容器之间的通信。
原理
- Flannel 的工作原理是将一个大的 IP 地址段分成许多小的子网,并为每个子网分配一个唯一的子网前缀。flannel 为每个 host 分配一个 subnet
- 例如:每个主机是
/24
的子网- 因此主机 a 分配10.100.5.0/24
- 主机 B 分配10.100.18.0/24
- 容器从此 subnet 中分配 IP,这些 IP 可以在 host 间路由,并让属于不在一个 host 上的容器能够直接通过内网 IP 通信。
- 每个 subnet 都是从一个更大的 IP 池中划分的,flannel 会在每个主机上运行一个叫 flanneld 的代理程序,其职责就是负责从预配置地址空间中为每台主机分配一个网段。
- 为了在各个主机间共享信息,flannel 直接使用 Kubernetes API 或 ETCD(与 consul 类似的 key-value 分布式数据库)存储网络配置、已分配的 subnet、host 的 IP 等信息。
-
Flannel 将一个大的 IP 地址段分成许多小的子网,每个子网的大小由参数
--subnet
控制。 -
每个主机上的 Flannel 代理都会为容器分配一个唯一的 IP 地址,该地址由子网前缀和主机上的唯一标识符组成。其中,子网前缀是在第一步中分配给该主机的子网前缀,唯一标识符可以是主机的 MAC 地址或其他标识符。
-
当容器需要与其他容器通信时,它们会将数据包发送到目标 IP 地址,Flannel 代理会根据目标 IP 地址的子网前缀判断目标容器是否在同一子网中。如果目标容器在同一子网中,那么数据包将直接发送到目标容器,否则它将被发送到目标容器所在主机上的 Flannel 代理。
-
当数据包到达目标主机上的 Flannel 代理时,它会根据目标容器的 IP 地址将数据包转发到目标容器。
Flannel 还支持多种后端网络,包括 UDP、VXLAN、Host-GW 等。这些后端网络可以根据不同的场景和需求进行选择,以提高网络性能和可靠性。
ETCD
概念
- 官方仓库:Releases · etcd-io/etcd (github.com)
- etcd是一个开源的分布式键值存储系统,由CoreOS团队开发和维护。它是一个高可用、一致性的系统,用于共享配置和服务发现。etcd使用Raft算法来保证数据的一致性和可靠性。
解决的问题
使用etcd可以解决分布式系统中的很多问题,例如:
-
配置共享:etcd可以作为分布式系统中的配置中心,用于保存和共享应用程序的配置数据。
-
服务发现:etcd可以用于服务发现,可以在集群中注册和发现服务,以便于不同的服务之间进行通信。
-
分布式锁:etcd可以用于分布式锁的实现,保证在分布式环境下的数据一致性问题。
优点
etcd的优点包括:
-
高可用:etcd使用Raft算法保证数据的一致性和可靠性,可以实现高可用的分布式系统。
-
性能高:etcd的访问速度非常快,可以满足高并发、低延迟的需求。
-
易于使用:etcd提供了简单易用的API,可以方便地进行数据的读写和管理。
-
可靠性强:etcd使用多副本机制,即使在一些节点失效的情况下仍可以正常运行。
缺点
etcd也存在一些缺点:
-
存储容量受限:etcd的存储容量有限,如果存储的数据量超过了容量限制,可能会影响系统的性能。
-
部署复杂:etcd的部署需要考虑很多因素,如节点数量、网络拓扑结构等,需要一定的技术水平。
场景
- 例如,在 Kubernetes 中,etcd 作为后端存储用于存储整个集群的配置信息和状态信息,包括 Pod、Service、Volume 等资源的信息。
- Kubernetes 的各个组件通过 etcd API 访问 etcd 集群,以获取和更新集群中的配置数据。此时,2379 端口号就扮演了客户端访问 etcd 集群的重要角色。
- 另外,在 Docker Swarm 中,etcd 也可以作为后端存储用于存储集群的配置信息和状态信息,以确保各个 Docker 节点之间的数据一致性。在这种情况下,2379 端口号同样用于客户端访问 etcd 集群,以读写集群中的配置数据。
部署 flannel 网络的 vxlan 模式
实验环境
主机名 | IP | 部署服务 |
---|---|---|
Y 21 | 192.168.45.21 | flannel,docker |
Y 22 | 192.168.45.22 | flannel,docker |
Y 23 | 192.168.45.23 | etcd |
部署 ETCD
获取安装包
wget https://storage.googleapis.com/etcd/v3.4.13/etcd-v3.4.13-linux-amd64.tar.gz
tar -zxvf etcd-v3.4.13-linux-amd64.tar.gz -C /usr/local/
ls /usr/local/etcd-v3.4.13-linux-amd64/
初始化
- 软链接
ln -s /usr/local/etcd-v3.4.13-linux-amd64/etcd* /usr/local/bin/
- 启动 etcd
/usr/local/etcd-v3.4.13-linux-amd64/etcd
- 配置 etcd
mkdir /etc/etcd
vim /etc/etcd/etcd.conf
#[member]
ETCD_NAME="etcd"
ETCD_DATA_DIR="/data/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://192.168.45.23:2379,http://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS=http://192.168.45.23:2379,http://127.0.0.1:2379
ETCD_ENABLE_V2=true
配置文件解析
ETCD_NAME
节点名称ETCD_DATA_DIR
数据目录ETCD_LISTEN_CLIENT_URLS
客户端访问监听地址ETCD_ADVERTISE_CLIENT_URLS
客户端通告地址ETCD_ENABLE_V2
ETCD 3.4 版本ETCDCTL_API=3
和--enable-v2=false
成为了默认配置,- 如要使用 v 2 版本, 需要
ETCD_ENABLE_V 2=true
,否则会报错“404 page not found”
- 如要使用 v 2 版本, 需要
配置 etcd.service
vim /usr/lib/systemd/system/etcd.service
[Unit]
Description=Etcd Service
Documentation=https://coreos.com/etcd/docs/latest/
After=network.target
[Service]
Type=notify
ExecStart=/usr/local/bin/etcd
EnvironmentFile=-/etc/etcd/etcd.conf
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Alias=etcd3.service
systemctl start etcd
systemctl enable etcd
ss -antup|grep etcd
测试 etcd
etcdctl --endpoints=http://192.168.45.23:2379 put foo "bar"
etcdctl --endpoints=http://192.168.45.23:2379 get foo
使用 API 操作 etcd
创建一个 Key
curl -v -X PUT http://192.168.45.23:2379/v2/keys/key1?value="hello"
- 内容分析
{
"action":"set", // 操作方式
"node":{
"key":"/key1", // 存储路径
"value":"hello", // 值
"modifiedIndex":4, // 每增加一个值增加1
"createdIndex":4 // 每修改就增加1
}
}
查看 key
curl -L http://192.168.45.23:2379/v2/keys/key1
- 格式化以后查看
{
"action":"get",
"node":{
"key":"/key1",
"value":"hello",
"modifiedIndex":4,
"createdIndex":4
}
}
删除 key
curl -X DELETE http://192.168.45.23:2379/v2/keys/key1
- 格式化后查看
{
"action":"delete",
"node":{
"key":"/key1",
"modifiedIndex":5,
"createdIndex":4
},
"prevNode":{
"key":"/key1",
"value":"hello",
"modifiedIndex":4,
"createdIndex":4
}
}
将 flannel 网络的配置信息保存到 etcd
编写 flannel-config.json 文件
vim /data/etcd/flannel-config.json
{
"Network": "10.2.0.0/16",
"SubnetLen": 24,
"SubnetMin": "10.2.1.0",
"SubnetMax": "10.2.254.0",
"Backend":{
"Type": "vxlan"
}
}
参数说明
Network
: 指定网络的 IP 地址范围,这个网络的 IP 地址是10.2.x.x
,子网掩码是/16
,表示这个网络中有 256 个子网可用。SubnetLen
: 指定每个子网的子网掩码长度为 24,表示每个子网最多可以容纳 256 个 IP 地址。SubnetMin
: 指定子网的最小可用 IP 地址为10.2.1.0
,这意味着从这个 IP 开始,可以用来分配给子网中的各个设备。SubnetMax
: 指定子网的最大可用 IP 地址为10.2.254.0
,这表明子网允许分配的 IP 地址范围是10.2.1.0 - 10.2.254.255
,其中最后一个地址保留为广播地址。Backend
: 指定网络类型为vxlan
,这是一种用于虚拟化网络的技术,可以为容器提供一个隔离、安全的网络环境。该技术通过在主机的网络接口上构建虚拟二层网络的方式,实现了在容器之间的相互隔离和通信。
将配置存入etcd
- 注意:以下命令基于etcd v3,0.18版本之前使用的是etcd v2
etcdctl --endpoints http://192.168.45.23:2379 put /docker/network/config < /data/etcd/flannel-config.json
- 查看值
etcdctl --endpoints http://192.168.45.23:2379 get /docker/network/config
关于ETCD V3
在flannel中,使用etcd来实现网络配置的存储和共享,以及服务发现等功能。在0.18版本之前,flannel使用的是etcd v2版本作为数据存储后端。但是,随着etcd v3版本的发布,etcd v2版本已经逐渐被淘汰,所以flannel从0.18版本开始支持etcd v3版本。
etcd v3版本相比于v2版本,有着更好的性能和更丰富的功能。v3版本支持事务操作,可以支持更高的并发操作,同时还提供了更多的API接口。因此,flannel从0.18版本开始支持etcd v3版本,可以更好地满足用户的需求,提供更好的性能和功能。
- 以下命令基于ETCD V2,这里仅做拓展,不需要执行。
ETCDCTL_API=2 etcdctl --endpoints http://192.168.45.23:2379 set /docker/network/config < /data/etcd/flannel-config.json
部署 Docker
- 在 Y21 和 Y22 上部署
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine &&
yum install -y yum-utils device-mapper-persistent-data lvm2 &&
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo &&
yum -y install docker-ce
systemctl start docker
systemctl enable docker
- 同时配置镜像加速
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://7icsaa0c.mirror.aliyuncs.com"]
}
EOF
安装 flannel
- Y21 Y22两台机器一起操作
安装
wget https://github.com/flannel-io/flannel/releases/download/v0.18.1/flannel-v0.18.1-linux-amd64.tar.gz
mkdir /usr/local/flannel
tar -zxvf flannel-v0.18.1-linux-amd64.tar.gz -C /usr/local/flannel
ls /usr/local/flannel
初始化
- 软链接
ln -s /usr/local/flannel/* /usr/local/bin/
- 前台启动
flanneld -etcd-endpoints=http://192.168.45.23:2379 --iface=ens33 -etcd-prefix=/docker/network/
- 若出现报错
- 根据报错提示,发现是没有配置ETCD的键值对,重新配置ETCD
Couldn't fetch network config: rpc error: code = InvalidArgument desc = etcdserver: key not found
- 创建flannel服务启动脚本
vim /etc/systemd/system/flanneld.service
[Unit]
Description=Flanneld
Documentation=https://github.com/coreos/flannel
After=network.target
Before=docker.service
[Service]
User=root
ExecStartPost=/usr/local/bin/mk-docker-opts.sh
ExecStart=/usr/local/bin/flanneld \
-etcd-endpoints=http://192.168.45.23:2379 \
-iface=ens33 \
-ip-masq=true \
-etcd-prefix=/docker/network
Restart=on-failure
Type=notify
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
- 自启flanneld
systemctl start flanneld
systemctl enable flanneld
Flannel启动过程
- 从etcd中获取network的配置信息
- 划分subnet,并在etcd中进行注册
- 将子网信息记录到
/run/flannel/subnet.env
之中
cat /run/flannel/subnet.env
查看Flannel网卡
ip a
- 分配的子网分别是
10.2.72.0/32
10.2.40.0/32
配置Docker连接到flannel
修改Docker启动文件
- 主要是修改
mtu
的值和bip
,修改的依据是/run/flannel/subnet.env
vim /usr/lib/systemd/system/docker.service
- 修改的关键语句
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0 --bip=10.2.72.1/24 --mtu=1450
- Y21
- Y22
重启Docker生效配置
systemctl daemon-reload
systemctl restart docker
检查生效情况
查看路由情况
ip route
各个字段的含义如下:
-
default via 192.168.45.2 dev ens33 proto static metric 100
:默认路由,将所有不在本地子网范围内的数据包发送到网关192.168.45.2,通过ens33网卡进行传输,路由优先级为100。 -
10.2.40.0/24 via 10.2.40.0 dev flannel.1 onlink
:将目标网段10.2.40.0/24
的数据包通过flannel.1
网卡传输,网关为10.2.40.0
,onlink
表示网关是直接可达的,也就是在同一子网内。 -
10.2.72.0/24 dev docker0 proto kernel scope link src 10.2.72.1
:将目标网段10.2.72.0/24
的数据包通过docker0
网卡传输,源IP地址为10.2.72.1
。 -
192.168.45.0/24 dev ens33 proto kernel scope link src 192.168.45.21 metric 100
:将目标网段192.168.45.0/24
的数据包通过ens33网卡传输,源IP地址为192.168.45.21
,路由优先级为100。
其中,proto
字段表示路由协议,dev
字段表示出接口,src
字段表示源IP地址,metric
字段表示路由优先级。同时,onlink
表示网关直接可达,scope link
表示本地链路,即在同一子网内。
查看docker0 网卡
ip addr show docker0
- 从结果中我们发现:
flannel自动的更新了主机之间的路由表
docker0网卡的地址被替换为了 flannel 中定义的 IP 地址
问题 #problem
- 细心的朋友会发现这里 docker 0网卡的 MTU 还是 1500,但是我们在 service 文件中指定的 MTU 是 1450。
- 解答这个问题之前我们先了解一下 MTU 的概念。
MTU 的概念
基本概念
MTU(Maximum Transmission Unit)是指网络传输中数据帧的最大长度。路由在转发数据包时,也需要考虑 MTU 的限制。
为什么要修改 MTU
- 如果数据包的长度大于实际可用的 MTU,那么路由就会丢弃该数据包,并向源设备发送 ICMP 分片重组错误消息,通知源设备对数据包进行分片并重新组装。
- 路由中的 MTU 影响网络中容器之间的通信质量,特别是在使用容器网络插件进行容器间通信时。
- 由于容器之间的通信一般是通过虚拟网络设备进行的,而受到该虚拟设备的 MTU 限制。
- 因此,当网络中存在某些网络设备 MTU 值较小,或者容器之间要传输的数据包太大时,就需要调整路由中的 MTU 值来解决通信问题。
当 MTU 值不合理时
- 当 MTU 值过小
- 因为每个数据包需要被分割成多个片段,这会增加网络传输的开销,导致传输效率低下。
- 当 MTU 值过大
- 可能会导致路由设备缓冲区(buffer)的溢出,从而造成网络拥塞,并且这对于低宽带的网络环境来说可能会造成网络阻塞。
- 因此,需要合适的 MTU 值来保证网络中的数据传输效率和稳定性。
问题的解决
- 在后续进行测试以后重新查看网卡,MTU 数据更新为 1450
测试
准备测试容器
- Y 21 启动一个测试容器
docker run -it --rm --name test1 busybox
- Y 22启动一个测试容器
docker run -it --rm --name test2 busybox
网络分析
- 使用
ip a
查看 IP 情况 - 使用
ping
和traceroute
确定跨主机的互通性和路由跳转情况- 路由跳转情况分析:
- 容器-->容器网关-->flannel 虚拟网卡-->目标容器网关-->目标容器
- 容器-->容器网关-->flannel 虚拟网卡-->目标容器网关-->目标容器
- 路由跳转情况分析:
理解 Flannel 的工作流程
实验中 Flannel 工作流程理解
- 参考资料:Flannel 介绍及使用场景
-
容器 A 访问目标容器 B 的 IP 地址,通过容器的
eth0
网卡发送流量 -
容器的网卡跟宿主机网卡是成对的会直连到
docker0
tcpdump -i docker0 -p icmp -n
3. 随后这类外部容器 IP 的报文会转发到 flannel.1
虚拟网卡,这是一个 P2P 虚拟网卡,flannel
会从 ETSD 获取到各个容器的的路由信息,将数据通过宿主机网卡发送出去。
tcpdump -i flannel.1 -p icmp -n
4. 宿主机网卡会将报文 UDP 封装一层后发送到目标宿主机。
tcpdump -i ens33 -p udp -n
5. 报文到达目标主机以后往上,到传输层后,交给 flanneld
程序处理
6. 数据随后解包,发送给 flannel.1
虚拟网卡。
7. 根据路由表将报文转交给 docker0
后传递到目标容器。
什么是 P2P 网卡
- P 2 P 的虚拟网卡(Peer-to-Peer Virtual Network Interface Card)是指一种在容器间进行点对点连接的虚拟网卡。
- P 2 P 虚拟网卡的出现是为了支持容器间的直接通信,避免使用传统的 Overlay 网络,从而提高容器间通信的性能和效率。P 2 P 网卡可以让容器之间的通信直接发生在宿主主机的网卡和交换机之间,减少了数据包的网络传输开销,提高了传输速度和并发性。
配合插件使用
P 2 P 虚拟网卡通常需要配合一些容器网络插件使用,如 Weave、Calico、Flannel 等。这些插件可以通过不同的方式来创建 P 2 P 网卡,如使用 VXLAN、IPSec 等协议或技术实现虚拟网络设备。
特点
使用 P 2 P 虚拟网卡还有一个好处,就是可以避免在传统 Overlay 网络中出现的一些问题,如 IP 地址冲突、ARP 代理等。因为 P 2 P 网卡可以直接在容器网络层建立点对点连接,从而消除了这些问题。
容器网络解决方案的工作流程
在 Docker 中使用 Flannel 作为容器网络解决方案的工作流程如下:
-
Flannel 启动:在 Docker 宿主机上启动 Flannel 服务,Flannel 会创建一个名为 flannel.1 的虚拟网络接口,并配置该接口的 IP 地址和子网掩码等网络参数。
-
容器启动:当 Docker 容器启动时,Flannel 将为该容器分配一个唯一的 IP 地址,并将该 IP 地址转换为 VXLAN 网络中的地址。Flannel 会在宿主机上创建一个 VXLAN 隧道,将容器的网络流量封装在 VXLAN 隧道中,并通过 flannel.1接口发送到其他节点。
-
网络流量转发:当其他节点上的容器需要与该容器进行通信时,Flannel 会将网络流量从 VXLAN 隧道中解封装出来,并发送到目标容器所在的节点。然后,目标节点上的 Flannel 会将网络流量封装在 VXLAN 隧道中,并通过 flannel.1 接口发送到目标容器。
-
网络配置保存:Flannel 会将容器的网络配置信息保存在 etcd 中,包括容器的 IP 地址、VXLAN 地址、所在子网等信息。当容器重新启动时,Flannel 会从 etcd 中获取该容器的网络配置信息,并为其分配相同的 IP 地址。
总的来说,Flannel 将容器的 IP 地址转换为 VXLAN 网络中的地址,通过 VXLAN 隧道进行数据包的封装和解封装,实现了跨主机的容器通信;同时,Flannel 将容器的网络配置信息保存在 etcd 中,实现了容器网络的动态管理和配置。
部署 flannel 的 host-gw 模式
概念
- 之前部署的都是 vxlan 模式,而
host-gw
模式是 flannel 的另外一个模式。
跟 vxlan 的区别
-
实现方式不同:
- host-gw 模式使用 Linux 内核的路由功能完成容器网络的转发
- vxlan 模式使用 VXLAN 技术对数据包进行封装和解封,完成容器之间的通信。
-
性能和效率不同:
- host-gw 模式从性能上来说相对比较高效,因为它直接利用 Linux 内核的路由功能进行转发,所以转发的速度相较于 VXLAN 模式会更快。但是 host-gw 模式的缺点是需要在每个节点上配置静态路由,这会增加管理难度,使得扩展性不够好。
- vxlan 模式的优点是可以利用网络层的 IP 地址进行标识,便于扩展和管理,但是 vxlan 模式的缺点是需要进行数据包的封装和解封,增加了网络传输的开销。
-
可用性和可靠性不同:
- 在 host-gw 模式中,节点的可用性对容器网络的可用性有一定的影响。一旦某个节点宕机或者出现网络问题,对应的容器网络就会不可用。
- vxlan 模式则可以利用多路径 (Multipath) 和故障转移 (Failover) 等技术来提高容器网络的可用性和可靠性,在一定程度上降低了单点故障的风险。
Host-gw 模式的优点:
-
性能较好:Host-gw 模式是通过底层 Linux 路由功能实现容器的 IP 数据包转发的,因此在性能上比 VXLAN 模式更好。
-
管理简单:减少了节点的维护管理工作,因为只需关注底层网络,而不需要关注 VTEP 等其他网络组件。
-
更好的网络隔离:Host-gw 模式使用不同的网段来隔离不同的容器,可以有效避免地址冲突和 ARP 欺骗等问题。
Host-gw 模式的缺点:
-
扩展性受限:由于需要为每个节点配置静态路由,节点数目越多会增加管理成本,对规模化部署不友好。
-
依赖网络互通:在 Host-gw 模式下,需要保证各个节点之间的网络互通(即二层网络能够相互访问),容易出现网络故障导致容器节点失去连通性。
-
容易受到攻击:Host-gw 模式如果节点网络被攻击,攻击者有可能会进入到其他容器所在的节点以及容器内部网络中,从而造成安全风险。
VXLAN 模式的优点:
-
扩展性更好:VXLAN 可以支持动态路由协议和 ECMP(Equal-Cost Multi-Path)等特性,使得 VXLAN 模式的扩展性更好。
-
跨网络支持:VXLAN 可以跨越多个网络的部署,以及不同数据中心的部署,适用范围更广。
-
更好的安全性:VXLAN 可以支持数据报文的加密和隧道插入(Tunnel-In-Tunnel),从而提高了容器网络的安全性。
VXLAN 模式的缺点:
-
性能较差:VXLAN 模式基于封装技术实现容器的 IP 数据包转发,这会带来一定的网络传输开销,降低性能。
-
更复杂的管理:需要配置 VTEP 等其他网络组件来支持 VXLAN 模式,增加了部署和管理的复杂度。
-
部署限制:需要支持 UDP 和 IP 小分组重组,某些网络设备或云服务商可能不支持这些特性,因此可能需要使用其他网络模式。
因此,Host-gw 模式适合于规模较小的容器部署,对性能和管理的要求较高,而 VXLAN 模式适用于需要跨越多个网络部署、要求扩展性、安全性较高的场景。
部署
- 由于 flannel 的网络配置是通过 ETCD 获取配置数据
- 所以只需要修改一下配置文件即可,其它步骤一致。
配置 flannel-config
修改配置
vim /data/etcd/flannel-config.json
{
"Network": "10.2.0.0/16",
"SubnetLen": 24,
"SubnetMin": "10.2.1.0",
"SubnetMax": "10.2.254.0",
"Backend":{
"Type": "host-gw"
}
}
配置导入 ETCD
- 导入配置文件
etcdctl --endpoints http://192.168.45.23:2379 put /docker/network/config < /data/etcd/flannel-config.json
- 查看值
etcdctl --endpoints http://192.168.45.23:2379 get /docker/network/config
重启 flannel 查看路由情况
systemctl restart flannel
ip a
- 之前的路由表
- 在路由表中对比一下,发现没有了虚拟网卡
flannel.1
的路由。
容器内网络测试
- 之前的路由跳转
- 现在的路由跳转