zhangguanzhang's Blog

跨VPC或者跨云供应商搭建K8S集群正确姿势

字数统计: 1.5k阅读时长: 5 min
2018/09/24 Share

        去年开始各大云厂商搞活动120元一年的云主机,当时还不会k8s,但是了解到搭建集群需要好几台,于是当时攒了好几台来做准备: tx一台,阿里两个别人账号买了两台,再加上很久之前几何云送了一台. 当时还有人怼说买这么多干啥,答曰:以后学k8s,此人曰:呵呵

        后面刚开始接触的时候不太了解网络,用的calico,跨vpc根本不行,有大佬说只能vxlan能跨SDN下的vpc之间
但是云厂商的主机都是做的ip的nat,网卡ip都是内网ip,kubelet上报的时候和flannel都会用这个ip

  • calico的BGP不能跨子网(好像需要人为干预配置才能跨)跨三层只能用ipip模式,另外个人网络知识不够,不懂BGP
  • flannel的host-gateway需要宿主机在同一个子网也就是说更不能跨vpc,另外host-gateway我们这的SDN会对包进行过滤,如果机器的ip和mac绑定(默认)下发的包和发出去的网卡的ip和mac对不上会被drop掉,而且host-gateway需要每个主机的docker0不同网段,似乎搭建的话有点麻烦。flannel的vxlan慢于calico的ipip模式,calico的ipip不了解(另外ipip安全性不如vxlan)

        最开始k8s对网络设计提出一下要求

  • 所有容器能够在没有 NAT 的情況下与其他容器通信。
  • 所有节点能夠在没有 NAT 情況下与所有容器通信(反之亦然)。
  • 容器看到的 IP 与其他人看到的 IP 是一样的。

        于是每个pod都需要一个ip,然后通过网络模型实现pod,node,非本机pod之前通信,于是

  • 高耦合的容器到容器通信(即一个pod内容器):通过 Pods 内 localhost 来通信。
  • Pod 到 Pod 的通信:通过实现网络模型来解决。
  • Pod 到 Service 通信:由 Service objects 结合 kube-proxy 解決。
  • 外部到 Service 通信:一样由 Service objects 结合 kube-proxy 解決。
    上面可以得出pod得有一个ip,但是又不得影响宿主机的原有网络,于是svc和pod的ip都是集群内的宿主机自嗨内网(人为干预可以让非集群机器访问到clusterip)

        在flannel的vxlan模式里flannel.1充当了vxlan里的vtep身份,垮主机的pod通信流程可以简化到如下
network
flannel查找到目标pod所在节点是通过查询etcd的node信息维护一张fdb表,可以通过下面命令查看

1
bridge fdb show dev flannel.1

kubelet上报的ip是主机的eth0的网卡ip,由于公有云ecs都是做的ip的nat,上报的ip是内网ip,各vpc里的主机向这个内网ip发包的话会在出公网后被运营商路由器drop掉(源目ip为内网ip不允许上公网)

        最开始我从calico换成flannel的时候没注意目录/etc/cni/net.d/残留有calico的配置文件,导致kubelet还认为是跑的calico,但是找不到calico一直报错,后面一气之下全部重装了操作系统
        后面又搭建起来后发现(只有同一个node上的pod能互相访问)跨节点的pod之间无法通信,然后云上开了flannel的udp端口8472后依然不通。

测试udp端口通不通可以在flannel跑起来后安装nc命令测,发现几何云udp包会被drop掉,阿里、腾讯和百度的不会

1
echo -n foo | nc -4u -w1 <dest_public_ip> 8472

抓包则安装tcpdump抓

1
tcpdump -nn dev flannel.1

最后找flannel的选项发现如下字段可以指定其他的vtep与自己通信的时候应该使用这个ip来通信

1
2
3
[root@k8s-m1 ~]# docker run --rm quay.io/coreos/flannel:v0.10.0-amd64 --help |& grep -A1 public
-public-ip string
IP accessible by other nodes for inter-host communication

        但是flannel的pod是ds跑的,如果每台机器都要配置各自的public ip,可以通过改写initContainers的逻辑,通过类似curl ip.sb查询自己主机的公网出口ip来注入到一个变量里,然后工作容器的ars加上-public-ip即可
另一种是flannel官方yaml里有RBAC能够读取node的状态,kubectl describe node nodeName信息发现有Annotations如下

1
2
3
4
5
6
Annotations:        flannel.alpha.coreos.com/backend-data: {"VtepMAC":"xxxx"}
flannel.alpha.coreos.com/backend-type: vxlan
flannel.alpha.coreos.com/kube-subnet-manager: true
flannel.alpha.coreos.com/public-ip: xxxxxxx
node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-detach: true

看到这个命名public-ip可以十分肯定的知道改这里也能行了,于是如下命令修改完每个node信息为对应node的公网IP即可通信

1
kubectl edit node <nodename>

然后去对应node的8472端口抓包发现能通
    后面发现kubectl execlog命令是走的node的ip(也就是kubelet上报的各个主机的eth0的内网ip)会导致这俩命令超时
于是添加DNAT规则解决

1
iptables -t nat -I OUTPUT -d 节点内网ip -j DNAT --to 节点的公网IP

    这里发现很小几率会出现做完上面的DNAT也不通和超时,我的某个node就出现这样情况,需要配合增加下面命令解决

1
iptables -t nat -I POSTROUTING -o eth0 -s <目标节点内网ip> -j MASQUERADE

然后测试pod的ip访问和svc的ip访问即可通信

一些flannel网络文章

http://yangjunsss.github.io/2018-07-21/%E5%AE%B9%E5%99%A8%E7%BD%91%E7%BB%9C-Flannel-%E4%B8%BB%E8%A6%81-Backend-%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86%E5%92%8C%E9%AA%8C%E8%AF%81/
https://blog.51cto.com/14146751/2334560?source=dra
https://ieevee.com/tech/2017/08/12/k8s-flannel-src.html

CATALOG
  1. 1. 一些flannel网络文章