Calico 3.6 转发外部流量到集群 Pod

由于开发有部份服务使用 GRPC 进行通讯,同时采用 Consul 进行服务发现;在微服务架构下可能会导致一些访问问题,目前解决方案就是打通开发环境网络与测试环境 Kubernetes 内部 Pod 网络;翻了好多资料发现都是 2.x 的,而目前测试集群 Calico 版本为 3.6.3,很多文档都不适用只能自己折腾,目前折腾完了这里记录一下

本文默认为读者已经存在一个运行正常的 Kubernetes 集群,并且采用 Calico 作为 CNI 组件,且 Calico 工作正常;同时应当在某个节点完成了 calicoctl 命令行工具的配置

一、问题描述

在微服务架构下,由于服务组件很多,开发在本地机器想测试应用需要启动整套服务,这对开发机器的性能确实是个考验;但如果直接连接测试环境的服务,由于服务发现问题最终得到的具体服务 IP 是 Kubernetes Pod IP,此 IP 由集群内部 Calico 维护与分配,外部不可访问;最终目标为打通开发环境与集群内部网络,实现开发网络下直连 Pod IP,这或许在以后对生产服务暴露负载均衡有一定帮助意义;目前网络环境如下:

开发网段: 10.10.0.0/24
测试网段: 172.16.0.0/24
Kubernetes Pod 网段: 10.20.0.0/16

二、打通网络

首先面临的第一个问题是 Calico 处理,因为如果想要让数据包能从开发网络到达 Pod 网络,那么必然需要测试环境宿主机上的 Calico Node 帮忙转发;因为 Pod 网络由 Calico 维护,只要 Calico Node 帮忙转发那么数据一定可以到达 Pod IP 上;

一开始我很天真的认为这就是个 ip route add 10.20.0.0/16 via 172.16.0.13 的问题… 后来发现

没那么简单

经过翻文档、issue、blog 等最终发现需要进行以下步骤

2.1、关闭全互联模式

注意: 关闭全互联时可能导致网络暂时中断,请在夜深人静时操作

首先执行以下命令查看是否存在默认的 BGP 配置

1
calicoctl get bgpconfig default

如果存在则将其保存为配置文件

1
calicoctl get bgpconfig default -o yaml > bgp.yaml

修改其中的 spec.nodeToNodeMeshEnabledfalse,然后进行替换

1
calicoctl apply -f bgp.yaml

如果不存在则手动创建一个配置,然后应用

1
2
3
4
5
6
7
8
9
10
 cat << EOF | calicoctl create -f -
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
logSeverityScreen: Info
nodeToNodeMeshEnabled: false
asNumber: 63400
EOF

本部分参考:

2.2、开启集群内 RR 模式

在 Calico 3.3 后支持了集群内节点的 RR 模式,即将某个集群内的 Calico Node 转变为 RR 节点;将某个节点设置为 RR 节点只需要增加 routeReflectorClusterID 既可,为了后面方便配置同时增加了一个 lable 字段 route-reflector: "true"

1
calicoctl get node CALICO_NODE_NAME -o yaml > node.yaml

然后增加 routeReflectorClusterID 字段,样例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: projectcalico.org/v3
kind: Node
metadata:
annotations:
projectcalico.org/kube-labels: '{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/hostname":"d13.node","node-role.kubernetes.io/k8s-master":"true"}'
creationTimestamp: 2019-06-17T13:55:44Z
labels:
beta.kubernetes.io/arch: amd64
beta.kubernetes.io/os: linux
kubernetes.io/hostname: d13.node
node-role.kubernetes.io/k8s-master: "true"
route-reflector: "true" # 增加 lable
name: d13.node
resourceVersion: "61822269"
uid: 9a1897e0-9107-11e9-bc1c-90b11c53d1e3
spec:
bgp:
ipv4Address: 172.16.0.13/19
ipv4IPIPTunnelAddr: 10.20.73.82
routeReflectorClusterID: 172.16.20.1 # 添加集群 ID
orchRefs:
- nodeName: d13.node
orchestrator: k8s

事实上我们应当导出多个 Calico Node 的配置,并将其配置为 RR 节点以进行冗余;对于 routeReflectorClusterID 目前测试只是作为一个 ID(至少在本文是这样的),所以理论上可以是任何 IP,个人猜测最好在同一集群网络下采用相同的 IP,由于这是真正的测试环境我没有对 ID 做过多的测试(怕玩挂)

修改完成后只需要应用一下就行

1
calicoctl apply -f node.yaml

接下来需要创建对等规则,规则文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kind: BGPPeer
apiVersion: projectcalico.org/v3
metadata:
name: peer-to-rrs
spec:
nodeSelector: "!has(route-reflector)"
peerSelector: has(route-reflector)
---
kind: BGPPeer
apiVersion: projectcalico.org/v3
metadata:
name: rr-mesh
spec:
nodeSelector: has(route-reflector)
peerSelector: has(route-reflector)

假定规则文件名称为 rr.yaml,则创建命令为 calicoctl create -f rr.yaml;此时在 RR 节点上使用 calicoctl node status 应该能看到类似如下输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Calico process is running.

IPv4 BGP status
+--------------+---------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+--------------+---------------+-------+----------+-------------+
| 172.16.0.19 | node specific | up | 05:43:51 | Established |
| 172.16.0.16 | node specific | up | 05:43:51 | Established |
| 172.16.0.17 | node specific | up | 05:43:51 | Established |
| 172.16.0.13 | node specific | up | 13:01:17 | Established |
+--------------+---------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

PEER ADDRESS 应当包含所有非 RR 节点 IP(由于真实测试环境,以上输出已人为修改)

同时在非 RR 节点上使用 calicoctl node status 应该能看到以下输出

1
2
3
4
5
6
7
8
9
10
11
12
Calico process is running.

IPv4 BGP status
+--------------+---------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+--------------+---------------+-------+----------+-------------+
| 172.16.0.10 | node specific | up | 05:43:51 | Established |
| 172.16.0.13 | node specific | up | 13:01:20 | Established |
+--------------+---------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

PEER ADDRESS 应当包含所有 RR 节点 IP,此时原本的 Pod 网络连接应当已经恢复

本部分参考:

2.3、调整 IPIP 规则

先说一下 Calico IPIP 模式的三个可选项:

  • Always: 永远进行 IPIP 封装(默认)
  • CrossSubnet: 只在跨网段时才进行 IPIP 封装,适合有 Kubernetes 节点在其他网段的情况,属于中肯友好方案
  • Never: 从不进行 IPIP 封装,适合确认所有 Kubernetes 节点都在同一个网段下的情况

在默认情况下,默认的 ipPool 启用了 IPIP 封装(至少通过官方安装文档安装的 Calico 是这样),并且封装模式为 Always;这也就意味着任何时候都会在原报文上封装新 IP 地址,在这种情况下将外部流量路由到 RR 节点,RR 节点再转发进行 IPIP 封装时,可能出现网络无法联通的情况(没仔细追查,网络渣,猜测是 Pod 那边得到的源 IP 不对导致的);此时我们应当调整 IPIP 封装策略为 CrossSubnet

导出 ipPool 配置

1
calicoctl get ippool default-ipv4-ippool -o yaml > ippool.yaml

修改 ipipMode 值为 CrossSubnet

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
creationTimestamp: 2019-06-17T13:55:44Z
name: default-ipv4-ippool
resourceVersion: "61858741"
uid: 99a82055-9107-11e9-815b-b82a72dffa9f
spec:
blockSize: 26
cidr: 10.20.0.0/16
ipipMode: CrossSubnet
natOutgoing: true
nodeSelector: all()

重新使用 calicoctl apply -f ippool.yaml 应用既可

本部分参考:

2.4、增加路由联通网络

万事俱备只欠东风,最后只需要在开发机器添加路由既可

将 Pod IP 10.20.0.0/16 和 Service IP 10.254.0.0/16 路由到 RR 节点 172.16.0.13

1
2
3
4
# Pod IP
ip route add 10.20.0.0/16 via 172.16.0.13
# Service IP
ip route add 10.254.0.0/16 via 172.16.0.13

当然最方便的肯定是将这一步在开发网络的路由上做,设置完成后开发网络就可以直连集群内的 Pod IP 和 Service IP 了;至于想直接访问 Service Name 只需要调整上游 DNS 解析既可


Calico 3.6 转发外部流量到集群 Pod
https://mritd.com/2019/06/18/calico-3.6-forward-network-traffic/
作者
Kovacs
发布于
2019年6月18日
许可协议