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

Posted by 蜜意罗兰 on June 18, 2019

由于开发有部份服务使用 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 配置

calicoctl get bgpconfig default

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

calicoctl get bgpconfig default -o yaml > bgp.yaml

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

calicoctl apply -f bgp.yaml

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

 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"

calicoctl get node CALICO_NODE_NAME -o yaml > node.yaml

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

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 做过多的测试(怕玩挂)

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

calicoctl apply -f node.yaml

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

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 应该能看到类似如下输出

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 应该能看到以下输出

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 配置

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

修改 ipipMode 值为 CrossSubnet

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

# 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 解析既可

转载请注明出处,本文采用 CC4.0 协议授权