前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Calico BGP 功能介绍:实现

Calico BGP 功能介绍:实现

作者头像
米开朗基杨
发布2021-06-09 16:56:31
2.8K0
发布2021-06-09 16:56:31
举报
文章被收录于专栏:云原生实验室云原生实验室

Calico 作为一种常用的 Kubernetes 网络插件,使用 BGP 协议对各节点的容器网络进行路由交换。本文是《Calico BGP 功能介绍》系列的第二篇,介绍 Calico 中 BGP 功能的实现。所使用的 Calico 版本为 v3.17.3。

Calico BGP 功能

BGP Peer

Calico 中通过定义 BGP Peer 对象,来建立 BGP 连接。BGP Peer 对象的主要参数如下:

参数

描述

node

指定 BGP Peer 应用在哪个 node 上。如果指定此字段,则为 node 级别,否则为 global 级别。

peerIP

指定远端的 Peer 地址,可以是 IP 加端口的形式,端口可选。支持 IPV4 和 IPV6。

asNumber

远端 Peer 的 AS 号。

nodeSelector

用于通过标签来选择一组 node,作为 BGP Peer 应用的节点,注意这里的 node 为 Calico 中的 node,而非 K8s 中的 node。如果指定了此字段,则 node 应该为空。

peerSelector

用于通过标签来选择一组 node(同样为 Calico 中的 node),作为远端 Peer 的节点。如果指定了此字段,则 peerIP 和 asNumber 都应该为空。

keepOriginalNextHop

对于 EBGP,保持并转发原始的 next hop,不将自身加入到 Path 中。

password

BGP 会话的身份验证。

在过去的版本,Calico 中包含了BGP Peer对象和Global BGP Peer对象,目前已统一为BGP Peer对象,根据是指定node参数还是nodeSelector参数来区分。

BGP Configuration

除了 BGP Peer 外,Calico 通过 BGP Configuration 对象来控制全局的 BGP 行为。主要参数包括:

参数

描述

默认值

nodeToNodeMeshEnabled

开启 Calico 节点之间的 node-to-node mesh。

true

asNumber

Calico node 默认的节点 AS。

64512

serviceClusterIPs

Calico 需要对外 BGP 的 service ClusterIP 地址段。

serviceExternalIPs

Calico 需要对外 BGP 的 service ExternalIPs 地址段。

communities

用于定义 BGP community,由 name 和 value 组成,value 支持标准 community 以及 large community。

prefixAdvertisements

指定网段与 community 的隶属关系,可以通过 communities 中的 name 指定,也可以通过 community value 直接指定。

默认情况下,Calico 所有节点通过 IBGP 来交换各个节点的 workload(容器)路由信息,由于从 IBGP Peer 中学习到的路由不会被再次转发,因此需要使用 node-to-node mesh 的方式两两互连。

serviceClusterIPsserviceExternalIPs字段的功能类似于 MetalLB 的 BGP 模式,可以将 K8s Service 的访问地址(ClusterIP 和 ExternalIP)BGP 到集群外的设备(例如 TOR)。结合 ECMP,可以将外部访问 K8s Service 的流量负载到 K8s 节点上,由 Kube-proxy 转发到真正的容器后端。

communitiesprefixAdvertisements可以控制 Calico BGP 路由的 community 字段,支持RFC 1997[1]中的well-known communities。使用样例如下:

代码语言:javascript
复制
communities:
- name: bgp-large-community
  value: 63400:300:100
prefixAdvertisements:
- cidr: 172.218.4.0/26
  communities:
  - bgp-large-community
  - 63400:120

Calico BGP 功能实现

简单来说,Calico 是通过 confd 组件来渲染 bird 的配置文件,动态配置 bird,从而实现 BGP 功能。其中,为了使 confd 支持 Calico 后端存储,在原生的 confd 上,添加了类型为 Calico 的 backend,主要逻辑是 watch 后端 BGPPeer、BGPConfiguration、Node 资源(由 libcalico-go 中的bgpsyncer实现),缓存到内存中,供 confd 渲染使用。

可以看到,主要的配置文件分为 6 个,其中 IPv4 和 IPv6 各 3 个。以 IPv4 为例,IPv6 类似,bird.cfg 为 bird 的主要配置文件,包括几个主要部分:

1)全局的配置:包括 route id、debug 属性、listen bgp port,这一部分主要由 node、bgp configuration 中的字段产生。

2)固定的协议配置:包括 kernel、device、direct;其功能在上一篇Calico BGP 功能介绍:BIRD 简介[2]中有详细介绍。

代码语言:javascript
复制
# Configure synchronization between routing tables and kernel.
protocol kernel {
  learn;             # Learn all alien routes from the kernel
  persist;           # Don't remove routes on bird shutdown
  scan time 2;       # Scan kernel routing table every 2 seconds
  import all;
  export filter calico_kernel_programming; # Default is export none
  graceful restart;  # Turn on graceful restart to reduce potential flaps in
                     # routes when reloading BIRD configuration.  With a full
                     # automatic mesh, there is no way to prevent BGP from
                     # flapping since multiple nodes update their BGP
                     # configuration at the same time, GR is not guaranteed to
                     # work correctly in this scenario.
  merge paths on;    # Allow export multipath routes (ECMP)
}

# Watch interface up/down events.
protocol device {
{{- template "LOGGING"}}
  scan time 2;    # Scan interfaces every 2 seconds
}

protocol direct {
{{- template "LOGGING"}}
  interface -"cali*", -"kube-ipvs*", "*"; # Exclude cali* and kube-ipvs* but
                                          # include everything else.  In
                                          # IPVS-mode, kube-proxy creates a
                                          # kube-ipvs0 interface. We exclude
                                          # kube-ipvs0 because this interface
                                          # gets an address for every in use
                                          # cluster IP. We use static routes
                                          # for when we legitimately want to
                                          # export cluster IPs.
}

kernel 中 export 用到了 filter calico_kernel_programming,其定义在 bird_ipam.cfg.template 中。主要分两部分:

一是获取/calico/rejectcidrs的值,对属于此 cidr 范围内的路由 reject。/calico/rejectcidrs的值是由 confd 的calico backend写入,其值为 BGP Configuration 中设置的serviceClusterIPsserviceExternalIPs。在开启了 Calico 的advertise service[3]功能后(通过配置 BGP Configuration 的serviceClusterIPsserviceClusterIPs),可以避免将其他节点发送过来的 Service 路由写入 kernel 路由表中。

二是对于属于 Calico IPPool 的路由,根据 Calico IPPool 设置的模式(VXLAN 或 IPIP),来判断是否需要写入 kernel 路由表。对于 VXLAN 模式,由 felix 负责数据包的路由,不再写入 kernel 中;对于 IPIP,根据 Calico IPPool 的 IPIP 配置(Always、Cross-subnet)以及 BGP 协议的 bgp_next_hop 属性(判断是否跨网段),决定是生成 IPIP 的路由,还是非 IPIP 路由。这里使用到的 bird 参数krt_tunnel来传递 IPIP 设备,krt_tunnel并非原生 bird 所提供的参数,而是由 Calico 添加的,以实现 bird 支持 IPIP 协议。

代码语言:javascript
复制
{{$network_key := printf "/bgp/v1/host/%s/network_v4" (getenv "NODENAME")}}
filter calico_kernel_programming {
{{- $reject_key := "/rejectcidrs"}}
{{- if ls $reject_key}}

  # Don't program static routes into kernel.
  {{- range ls $reject_key}}
    {{- $parts := split . "-"}}
    {{- $cidr := join $parts "/"}}
  if ( net ~ {{$cidr}} ) then { reject; }
  {{- end}}

{{- end}}
{{- if exists $network_key}}{{$network := getv $network_key}}
{{range ls "/v1/ipam/v4/pool"}}{{$data := json (getv (printf "/v1/ipam/v4/pool/%s" .))}}
  if ( net ~ {{$data.cidr}} ) then {
{{- if $data.vxlan_mode}}
    # Don't program VXLAN routes into the kernel - these are handled by Felix.
    reject;
  }
{{- else if $data.ipip_mode}}{{if eq $data.ipip_mode "cross-subnet"}}
    if defined(bgp_next_hop) && ( bgp_next_hop ~ {{$network}} ) then
      krt_tunnel = "";                     {{- /* Destination in ipPool, mode is cross sub-net, route from-host on subnet, do not use IPIP */}}
    else
      krt_tunnel = "{{$data.ipip}}";       {{- /* Destination in ipPool, mode is cross sub-net, route from-host off subnet, set the tunnel (if IPIP not enabled, value will be "") */}}
    accept;
  } {{- else}}
    krt_tunnel = "{{$data.ipip}}";         {{- /* Destination in ipPool, mode not cross sub-net, set the tunnel (if IPIP not enabled, value will be "") */}}
    accept;
  } {{- end}} {{- else}}
    krt_tunnel = "{{$data.ipip}}";         {{- /* Destination in ipPool, mode field is not present, set the tunnel (if IPIP not enabled, value will be "") */}}
    accept;
  } {{- end}}
{{end}}
{{- end}}{{/* End of 'exists $network_key' */}}
  accept;                                  {{- /* Destination is not in any ipPool, accept  */}}
}

3)BGP 协议部分。

BGP 协议部分是最主要的部分,使用了 bird 的 template,template 中 export 方向使用 filter calico_export_to_bgp_peers过滤,其定义在 bird_ipam.cfg.template 中,主要功能是:先调用 bird.cfg.template 中的apply_communities()方法(根据 BGP Configuration 中的communitiesprefixAdvertisements字段),为发送的 BGP 路由添加 community 参数;调用 bird_aggr.cfg.template 中的calico_aggr()方法,确保宣告的 BGP 路由的目标地址段为完整的 block;最后,判断路由的目标地址是否在/calico/staticroutes或 Calico IPPool 所指定的地址范围内,若在,则 accept,其他的 reject。

Calico 中 block 是容器 IP 资源池最小的分配单位,最初 Calico 会为每个节点分配一个 block,当某个节点 block 使用完,则会再次为节点分配一个 block

代码语言:javascript
复制
filter calico_export_to_bgp_peers {
  # filter code terminates when it calls `accept;` or `reject;`, call apply_communities() before calico_aggr()
  apply_communities();
  calico_aggr();
{{- $static_key := "/staticroutes"}}
{{- if ls $static_key}}

  # Export static routes.
  {{- range ls $static_key}}
    {{- $parts := split . "-"}}
    {{- $cidr := join $parts "/"}}
  if ( net ~ {{$cidr}} ) then { accept; }
  {{- end}}
{{- end}}
{{range ls "/v1/ipam/v4/pool"}}{{$data := json (getv (printf "/v1/ipam/v4/pool/%s" .))}}
  if ( net ~ {{$data.cidr}} ) then {
    accept;
  }
{{- end}}
  reject;
}

/calico/staticroutes中的值也是由 confd 的calico backend写入,其值主要包含两部分:BGP Configuration 中设置的serviceClusterIPsserviceExternalIPsexternalTrafficPolicy=Local的 Service 地址。因此 filter calico_export_to_bgp_peers保证了 Calico 只对容器网络相关的路由进行 BGP,对于手动配置或从其他 EBGP 学习到的非容器网络相关的路由,则不会进行 BGP。

BGP 协议部分主要分三部分:node-to-node mesh 的配置、global peers 的配置、node-specific peers 的配置,都是使用上面的 template 完成。

其中 node-to-node mesh 配置部分,会判断两个 node ip,当远端 peer 的 node ip“较大”时,开启passive,也就是说,始终由“较大”IP 的 node 发起 BGP 连接,保证 mesh 是单向的。

global peers 配置和 node-specific peers 配置部分基本相同,会根据 BGP Peer 中的keepOriginalNextHoppassword配置协议的next hop keep以及password属性,另外会根据本节点是否配置 route reflector clusterID,决定是否开启rr client(用于 Calico route reflector 模式)。

4)static 协议部分在 bird_aggr.cfg.template 中,主要是将本机的 block 和/calico/staticroutes中的值配置为 Blackhole 路由。这样一来,即可通过“bird 路由表”,由 BGP 协议将本机的容器网络和 Service 网络的路由信息发送出去。而根据上面/calico/staticroutes的介绍,对于externalTrafficPolicy=Cluster的 Service 是以整个 ServiceCIRD(BGP Configuration 中设置的serviceClusterIPsserviceExternalIPs)作为目标地址进行 BGP 的,对于externalTrafficPolicy=Local的 Service,则会判断本节点上是否有相应的 workloadEndpoint,如果有,则以单个地址(子网掩码/32 或/128)作为目标地址进行 BGP。以此,Calico 实现了对 Service externalTrafficPolicy 属性的支持。

代码语言:javascript
复制
{{- $block_key := printf "/calico/ipam/v2/host/%s/ipv4/block" (getenv "NODENAME")}}
{{- $static_key := "/calico/staticroutes"}}
{{if or (ls $block_key) (ls $static_key)}}
protocol static {
{{- if ls $block_key}}
   # IP blocks for this host.
{{- range ls $block_key}}
{{- $parts := split . "-"}}
{{- $cidr := join $parts "/"}}
   route {{$cidr}} blackhole;
{{- end}}
{{- end}}
{{- if ls $static_key}}
   # Static routes.
{{- range ls $static_key}}
{{- $parts := split . "-"}}
{{- $cidr := join $parts "/"}}
   route {{$cidr}} blackhole;
{{- end}}
{{- end}}
}
{{else}}# No IP blocks or static routes for this host.{{end}}

这里有个问题,由于 kernel 中的 export filter 过滤了目标地址属于 BGP Configuration 的serviceClusterIPsserviceClusterIPs的路由,实际上 static 协议中的/calico/staticroutes部分的 Blackhole 是无法配置到节点的路由表上的(externalTrafficPolicy=Cluster的 Service 地址也在这个范围内)。这会导致在使用 Calico 与 TOR 进行 BGP 的场景中,在开启了 advertise service 后,容器访问某个属于 ServiceCIDR 范围内,但并未分配给任何 Service 的地址时,数据包会根据默认路由发送到 TOR,再由 TOR 发送回集群的某个节点,如此反复直至 TTL 消耗完。而如果尝试将 ServiceCIDR 对应的 Blackhole 路由写入系统的路由表中,可以解决这个问题,但又会导致宿主机对 Service 无法访问,流量直接被丢弃。

参考

  • https://github.com/projectcalico/confd/pull/322[4]
  • https://github.com/projectcalico/confd[5]
  • https://github.com/projectcalico/calico/issues/3689[6]

脚注

[1]RFC 1997: http://www.rfc-editor.org/info/rfc1997

[2]Calico BGP 功能介绍:BIRD 简介: https://maao.cloud/2021/01/26/Calico-BGP功能介绍:BIRD简介/

[3]advertise service: https://docs.projectcalico.org/networking/advertise-service-ips

[4]https://github.com/projectcalico/confd/pull/322: https://github.com/projectcalico/confd/pull/322

[5]https://github.com/projectcalico/confd: https://github.com/projectcalico/confd

[6]https://github.com/projectcalico/calico/issues/3689: https://github.com/projectcalico/calico/issues/3689

原文链接:https://maao.cloud/2021/02/23/Calico-BGP%E5%8A%9F%E8%83%BD%E4%BB%8B%E7%BB%8D%EF%BC%9A%E5%AE%9E%E7%8E%B0/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云原生实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Calico BGP 功能
    • BGP Peer
      • BGP Configuration
      • Calico BGP 功能实现
      • 参考
        • 脚注
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档