基于Calico的CNI
Calico为容器或虚拟机提供安全的网络连接,它创建一个扁平化的第3层网络,为每个节点分配一个可路由的IP地址。网络中的节点不需要NAT或IP隧道就可以相互通信,因此性能很好,接近于物理网络,不使用网络策略的情况下,可能引入0.01毫秒级别的延迟,带宽影响不明显)。 在需要Overlay的应用场景下,Calico可以支持IP-in-IP隧道,也可以与其它Overlay网络(例如Flannel)配合,IP-in-IP隧道会带来较小的性能下降。
Calico支持动态的网络安全策略(NetPolicy),你可以细粒度的控制容器、虚拟机、物理机端点之间的网络通信。
Calico在每个节点运行一个虚拟路由器(vRouter),vRouter利用Linux内核自带的IP转发功能,工作负载依赖于此路由器和外部通信。节点上的代理组件Felix负责根据分配到节点上的工作负载的IP地址信息为vRouter提供L3转发规则。vRouter基于BIRD实现了边界网关协议(BGP)。通过vRouter,工作负载直接基于物理网络进行通信,甚至可以被分配外网IP并直接暴露到互联网上。
术语 | 说明 |
BGP |
边界网关协议(Border Gateway Protocol)是互联网上一个核心的去中心化自治路由协议,它通过维护IP路由表或前缀表来实现自治系统(AS)之间的可达性 大多数ISP使用BGP来与其他ISP创建路由连接,特大型的私有IP网络也可以使用BGP BGP的通信对端(对等实体,Peer)通过TCP(端口179)会话交换数据,BGP路由器会周期地发送19字节的保活消息来维护连接。在路由协议中,只有BGP使用TCP作为传输层协议 |
IBGP |
内部边界网关协议。同一个AS内部的两个或多个对等实体之间运行的BGP被称为IBGP |
IGP |
内部网关协议。同一AS内部的对等实体(路由器)之间使用的协议,它存在可扩容性问题:
IBGP和IGP都是处理AS内部路由的,仍然需要IGP的原因是:
|
EBGP |
外部边界网关协议。归属不同的AS的对等实体之间运行的BGP称为EBGP |
Border Router |
边界路由器,在AS边界上与其他AS交换信息的路由器 |
AS |
自治系统(Autonomous system),一个组织(例如ISP)管辖下的所有IP网络和路由器的整体 参与BGP路由的每个AS都被分配一个唯一的自治系统编号(ASN)。对BGP来说ASN是区别整个相互连接的网络中的各个网络的唯一标识。64512到65535之间的ASN编号保留给专用网络使用 |
Route Reflector |
同一AS内如果有多个路由器参与BGP路由,则它们之间必须配置成全连通的网状结构——任意两个路由器之间都必须配置成对等实体。由于所需要TCP连接数是路由器数量的平方,这就导致了巨大的TCP连接数 为了缓解这种问题,BGP支持两种方案:Route Reflector、Confederations 路由反射器(Route Reflector)是AS内的一台路由器,其它所有路由器都和RR直接连接,作为RR的客户机。RR和客户机之间建立BGP连接,而客户机之间则不需要相互通信 RR的工作步骤如下:
RR的一个优势是配置方便,因为只需要在反射器上配置 |
工作负载 | Workload,即运行在Calico节点上的虚机或容器 |
全互联 | 全互联网络(Full node-to-node Mesh)是指任何两个Calico节点都进行配对的L3连接模式 |
关于如何快速启用基于Calico的CNI网络连接,参考Kubernetes学习笔记。
Calico支持K8S的NetworkPolicy,下面是一个示例。
首先创建一个名字空间:
1 2 3 |
kubectl create ns dev # 为新名字空间的默认账户添加imagePullSecrets kubectl --namespace=dev edit serviceaccount default |
然后创建一个5实例的部署:
1 2 |
kubectl --namespace=dev run media-api --replicas=5 --image=docker.gmem.cc/media-api:1.1 kubectl --namespace=dev get pod |
将上述部署暴露为服务:
1 2 |
kubectl --namespace=dev expose deployment media-api --port=8800,7700 kubectl --namespace=dev get service |
执行类似下面的命令,确认服务能够正常响应HTTP请求:
1 |
curl http://media-api.dev.svc.k8s.gmem.cc:8800/media/newpub/2017-01-01 |
下面将上述名字空间dev隔离掉,Calico会阻止访问此名字空间中各的Pod:
1 2 3 4 5 6 7 8 9 10 11 |
kubectl create -f - <<EOF kind: NetworkPolicy apiVersion: extensions/v1beta1 metadata: name: default-deny namespace: dev spec: podSelector: # 默认拒绝任何访问 matchLabels: {} EOF |
这是再次执行curl命令,你无法得到响应。
下面我们再创建一个网络策略, 允许API网关层访问上面的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
kubectl create -f - <<EOF kind: NetworkPolicy apiVersion: extensions/v1beta1 metadata: name: allow-ingress-nginx namespace: dev spec: podSelector: matchLabels: # 下面的标签是自动添加的 run: media-api # 允许来自apigateway层的入站连接 ingress: - from: - podSelector: matchLabels: tier: apigateway EOF |
要删除网络策略,执行:
1 |
kubectl --namespace=dev delete networkpolicy default-deny |
策略 | 说明 | ||
禁止所有入站连接 |
|
||
禁止对特定Pod的入站连接 |
|
||
禁止所有出站连接 |
|
||
允许进行DNS查询的出站连接 |
|
||
允许对Nginx的出站访问 |
|
通常你应该使用Service(具有集群静态IP)而不是尝试为Pod指定静态IP。
如果非要这么做,Calico为Pod提供了一些注解:
1 2 3 4 5 6 7 8 9 |
# 分配给当前Pod的IP地址列表,请求的地址必须位于Calico的IPAM的地址池中 annotations: "cni.projectcalico.org/ipAddrs": "[\"192.168.0.1\"]" # 分配给当前Pod的IP地址列表,绕过IPAM # 你可以分配任何地址给Pod,但是仅仅当地址位于Calico的地址空间中时,Calico才负责分配路由 # 注意IP冲突问题 annotations: "cni.projectcalico.org/ipAddrsNoIpam": "[\"10.0.0.1\"]" |
Calico依赖于Etcd来存放配置信息,缺省情况下它会容器化安装单实例的Etcd。你可以修改其Configmap,指定使用外部的、高可用的Etcd:
1 2 3 4 5 6 7 8 |
kind: ConfigMap apiVersion: v1 metadata: name: calico-config namespace: kube-system data: # 可以和K8S共享Etcd集群 etcd_endpoints: "http://10.0.1.1:2379,http://10.0.2.1:2379,http://10.0.3.1:2379" |
所谓出站外部连接,是指从Calico端点到位于Calico集群外部的目的主机的连接。
最简单的实现外部出站连接的方式是为Calico池开启NAT:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 查看默认IP池的配置 # calicoctl get ipPool default-ipv4-ippool -o yaml # 输出: apiVersion: projectcalico.org/v3 kind: IPPool metadata: creationTimestamp: 2018-02-12T11:33:04Z name: default-ipv4-ippool resourceVersion: "5" uid: 7d7a9461-0fe8-11e8-a715-deadbeef00a0 spec: cidr: 192.168.0.0/16 ipipMode: Always # 已经启用出站NAT natOutgoing: true |
你可以针对某个IP池进行NAT设置。
所谓入站外部连接,是指从Calico集群外部主机发起的,针对Calico端点的连接。实现入站外部连接的方式主要由两种。
利用BGP配对,将外部网络的基础设施配对到Calico集群中的某些节点。这需要外部网络中包含支持BGP协议的交换机或者路由器,该路由器作为访问Calico集群内部端点的网关。
如果网络规模较小,你可以让外部路由器和所有Calico节点之间建立BGP会话;如果网络规模较大,可能需要利用RR来创建一个第三层拓扑。
下面是一个基于BIRD(支持BGP协议的软路由)的示例:Calico集群使用AS号65000,外部网络使用AS号65001,Calico集群内部节点10.0.0.100配对到外部路由器(BIRD)10.0.0.1,Calico内部使用节点全互联模式:
- Calico BGP Peer配置文件:
12345678apiVersion: projectcalico.org/v3kind: BGPPeermetadata:name: bgppeer-xenial-100-zirconspec:peerIP: 10.0.0.1node: xenial-100asNumber: 65001 - BIRD配置文件:
123456789protocol bgp xenial100 {description "10.0.0.100";local as 65001;neighbor 10.0.0.100 as 65000;source address 10.0.0.1;graceful restart;import all;export all;}
按照上面方式配置后,可以在BIRD上看到学习到的路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# sudo birdc show route 172.27.154.192/26 via 10.0.0.100 on virbr0 [xenial100 14:59:05] * (100) [AS65000i] 172.27.187.192/26 via 10.0.0.100 on virbr0 [xenial100 14:59:05] * (100) [AS65000i] 172.27.97.64/26 via 10.0.0.100 on virbr0 [xenial100 14:59:05] * (100) [AS65000i] 172.27.121.64/26 via 10.0.0.100 on virbr0 [xenial100 14:59:05] * (100) [AS65000i] 172.27.41.128/26 via 10.0.0.100 on virbr0 [xenial100 14:59:05] * (100) [AS65000i] 172.27.61.64/26 via 10.0.0.100 on virbr0 [xenial100 14:59:05] * (100) [AS65000i] # 同步的内核路由表 # route 172.27.41.128 xenial-100 255.255.255.192 UG 0 0 0 virbr0 172.27.61.64 xenial-100 255.255.255.192 UG 0 0 0 virbr0 172.27.97.64 xenial-100 255.255.255.192 UG 0 0 0 virbr0 172.27.121.64 xenial-100 255.255.255.192 UG 0 0 0 virbr0 172.27.154.192 xenial-100 255.255.255.192 UG 0 0 0 virbr0 172.27.187.192 xenial-100 255.255.255.192 UG 0 0 0 virbr0 |
此外,下一章的RR示例也可以支持入站外部链接。
Calico支持多种容器编排器(Orchestrator)特有的入站连接方式,例如Kubernetes的Service IP。
本节内容和Calico无直接关系,Calico仅仅为K8S提供容器连接性,它不知道K8S的服务(Service)是何物。
K8S中Service IP仅仅在集群内部可以访问,集群内节点或者Pod依赖Kube Proxy来访问Service IP。外部访问Service的规范化方式是,使用NodePort(可以配合LoadBalancer),向外部暴露服务。
但是,要直接暴露Service的IP地址到集群外部也是可以实现的。你只需要配置适当的路由规则,将Service IP子网的网关设置为K8S集群的任意节点即可:
1 2 3 4 |
# K8S 服务网段为10.96.0.0/12,10.0.0.100为集群中一个节点 route add -net 10.96.0.0 netmask 255.240.0.0 gw 10.0.0.100 # 设置完路由后,尝试连接集群的DNS服务:telnet 10.96.0.10 53,不要ping,不支持 |
较大规模的网络拓扑中,启用节点全互联需要大量的TCP连接,可以考虑引入Router Refactor并禁用全互联。实践中“较大规模”网络拓扑包含50+节点,但是也有超过100节点的正常运作的全互联网络。
BIRD是一个BGP实现,支持完全的动态路由,本章以BIRD软路由器作为RR。
1 2 3 |
sudo add-apt-repository ppa:cz.nic-labs/bird sudo apt-get update sudo apt-get install bird |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# 日志配置 log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug }; log stderr all; # 当前路由器(RR)全局唯一标识,通常设置为路由器的IP地址 router id 10.0.0.1; filter import_kernel { if ( net != 0.0.0.0/0 ) then { accept; } reject; } # 所有协议的全局性调试开关 debug protocols all; # 伪协议,不是和网络中其它路由器通信,而是每60秒将BIRD的路由表同步到OS内核 protocol kernel { scan time 60; import none; # 从内核路由表导入路由条目 export all; # 将BIRD的路由同步给内核 } # 伪协议,每2秒监控网络接口的信息 protocol device { scan time 2; } # 为拓扑中每个节点(每个连接到此RR的Peer)添加以下内容 protocol bgp xenial100 { # 可选的描述 description "10.0.0.100"; # 声明本路由器所属的AS # IP地址 如果不使用179 AS号 # local [ip] [port number] [as number] local as 65000; # BGP实例,指定当前路由器与自通信的邻居路由器的信息 # 如果和当前路由器AS一样则自动切换为IBGP # 可以不指定AS号而指定internal # 指定网络前缀而非精确的IP,则启用动态BGP行为,当前路由器会在BGP端口监听,当匹配 # 前缀的BGP连接到来后,spawn一个BGP实例处理它。普通BGP实例/动态BGP实例可混合使用 # neighbor [ip | range prefix] [port number] [as number] [internal|external] # neighbor range 10.0.3.0/24 as 65000; neighbor 10.0.0.100 as 65000; # 提示此邻居和本路由器(的某个接口)是直接相连的 direct; # 本路由器作为路由反射器,邻居作为RR客户端 rr client; # 当一个BGP speaker重启/崩溃后,邻居会丢弃从它接收到的所有路径。即使该speaker的转发平面仍然 # 继续工作,也会出现封包转发被干扰的情况。该选项用于缓和这个问题 graceful restart; import all; export all; } protocol bgp xenial101 { description "10.0.0.101"; local as 65000; neighbor 10.0.0.101 as 65000; direct; rr client; graceful restart; import all; export all; } protocol bgp xenial102 { description "10.0.0.102"; local as 65000; neighbor 10.0.0.102 as 65000; direct; rr client; graceful restart; import all; export all; } protocol bgp xenial103 { description "10.0.0.103"; local as 65000; neighbor 10.0.0.103 as 65000; direct; rr client; graceful restart; import all; export all; } protocol bgp xenial104 { description "10.0.0.104"; local as 65000; neighbor 10.0.0.104 as 65000; direct; rr client; graceful restart; import all; export all; } protocol bgp xenial105 { description "10.0.0.105"; local as 65000; neighbor 10.0.0.105 as 65000; direct; rr client; graceful restart; import all; export all; } |
配置完毕后,重启BIRD: sudo service bird restart
很快,你就可以看到RR从Calico节点同步了路由信息过来:
1 2 3 4 5 6 7 8 9 10 11 |
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default localhost 0.0.0.0 UG 0 0 0 wlan1 10.0.0.0 * 255.255.0.0 U 0 0 0 virbr0 172.17.0.0 * 255.255.0.0 U 0 0 0 docker0 172.18.0.0 * 255.255.0.0 U 0 0 0 docker_gwbridge 172.21.0.0 * 255.255.0.0 U 0 0 0 br-29f4509ebfd6 172.27.0.0 xenial-101 255.255.255.192 UG 0 0 0 virbr0 172.27.0.0 xenial-101 255.255.0.0 UG 0 0 0 virbr0 172.27.0.0 xenial-100 255.255.0.0 UG 0 0 0 virbr0 192.168.142.0 * 255.255.254.0 U 9 0 0 wlan1 |
首先禁用全互联,查看Calico的BGP选项,如果启用全互联则禁用之:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# calicoctl get bgpconfig -o yaml > /tmp/bgp.yaml apiVersion: projectcalico.org/v3 items: - apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: creationTimestamp: 2018-02-14T02:58:56Z name: default resourceVersion: "790" uid: ff92b98a-1132-11e8-937a-deadbeef00a0 spec: asNumber: 65000 logSeverityScreen: Info # 这里设置为false nodeToNodeMeshEnabled: false kind: BGPConfigurationList metadata: resourceVersion: "826" # calicoctl replace -f /tmp/bgp.yaml |
然后,添加一个全局的Peer,指向先前创建的RR:
1 2 3 4 5 6 7 8 9 10 11 12 |
cat << EOF | calicoctl create -f - apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: bgppeer-global-1 spec: peerIP: 10.0.0.1 asNumber: 65000 EOF # calicoctl get BGPPeer -o yaml > /tmp/bgpeer.yaml # calicoctl replace -f /tmp/bgpeer.yaml |
现在,登陆到某个Calico节点,查看其配对情况,应该仅仅和10.0.0.1进行了配对:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# calicoctl node status Calico process is running. IPv4 BGP status +--------------+-----------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-----------+-------+----------+-------------+ | 10.0.0.1 | global | up | 03:43:38 | Established | +--------------+-----------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. |
可以尝试在集群内连接某个Pod,如果可以连接,说明配置成功。
现在,你可以在任何物理节点上,执行calico node run命令,运行一个容器,将节点加入Calico CNI网络:
1 |
sudo calicoctl node run --ip-autodetection-method interface=virbr0 |
这样的节点可以直接作为路由反射器使用,也可以直连Pod。
从3.3开始,任何Calico节点均可以作为RR来运行,不需要基础设施或外部的专用RR节点。
最简单的配置,为Node加上routeReflectorClusterID字段即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: projectcalico.org/v3 kind: Node metadata: name: node-hostname # 标签影响配对 labels: routeReflector: 10.0.0.1 spec: bgp: asNumber: 64512 ipv4Address: 10.244.0.1/24 ipv6Address: 2001:db8:85a3::8a2e:370:7334/120 # 提示此节点是一个RR(同时也是一个普通Calico节点) # 将它的BGP Peers看作RR客户端,除非节点具有相同的routeReflectorClusterID, # 这会在BGP协议层次上产生一系列影响 routeReflectorClusterID: 10.0.0.1 |
3.3为BGPPeer资源添加了字段:
- nodeSelector:配对应该在该选择器匹配的节点上发生。原先只能指定节点名称
- peerSelector:配对应该在该选择器匹配的RR上发生。原先只能指定RR的IP
示例:
1 2 3 4 5 6 7 8 9 |
apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: cluster1 spec: # 所有节点 nodeSelector: "all()" # 所有具有routeReflector的RR peerSelector: "has(routeReflector)" |
一种典型的BGP拓扑划分方式:
- 将节点分组,每个组包含一个RR
- 所有RR进行全互联
概念 | 说明 |
Node-to-node mesh |
使用full node-to-node mesh选项,可以自动化的配置所有Calico节点之间的对等实体。该选项默认启用,每个Calico节点都自动和任何其它节点进行BGP Peering 适用于几十个节点的规模 |
Global BGP peers | 全局BGP Peer是一个BGP代理,它和网络中所有其它Calico节点进行Peering,典型应用场景是中等规模的部署,所有节点运行在同一个L2网络中,每个节点和RR(一个或一组)配对 |
默认AS号为64512。如果所有节点在一个AS内部但是你需要定制AS号(例如需要同边界路由器进行Peering),执行以下命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 检查默认BGP配置资源是否存在 calicoctl get bgpconfig default # 如果不存在,则: cat << EOF | calicoctl create -f - apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info nodeToNodeMeshEnabled: false # 禁用全节点BGP互联。如果没有进行适当配置,一旦禁用立即无法连通现有的Pod asNumber: 65000 # 使用另一个AS号 EOF # 如果存在,则先Dump在Replace calicoctl get bgpconfig default -o yaml > bgp.yaml calicoctl replace -f bgp.yaml |
修改sepc.nodeToNodeMeshEnabled即可。
如果你准备从零开始构建网络,并且不希望使用全互联,可以禁用之。如果你准备从全互联切换到其它方式,则需要预先配置好Peering然后再禁用全互联,以保证系统持续的提供对外服务。
如果你的网络拓扑中存在可以和任意Calico节点配对(Peering)的BGP Speakers,这种BGP Speaker被称为全局对等实体(Global Peer)。一旦配置了全局对等实体,Calico就会自动将所有节点与之配对。
全局对等实体以下场景中有价值:
- 添加了一个边界路由器,将其配对到全互联网络中
- 配置使用一个或两个RR的Calico网络,这种情况下每个节点都应该和RR配对,全互联应该禁用
配置示例:
1 2 3 4 5 6 7 8 9 10 11 12 |
cat << EOF | calicoctl create -f - apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: bgppeer-global-3040 spec: peerIP: 192.20.30.40 asNumber: 64567 EOF # 移除全局对等实体 calicoctl delete bgppeer bgppeer-global-3040 |
BGP网络拓扑更加复杂的情况下,你可能考虑针对每个Calico节点配置对等实体。示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
cat << EOF | calicoctl create -f - apiVersion: projectcalico.org/v3 kind: BGPPeer metadata: name: bgppeer-node-aabbff spec: # 将位于AS 64514中的Peer aa:bb::ff 和 Calico节点node1配对 peerIP: aa:bb::ff node: node1 asNumber: 64514 EOF # 查看配对 calicoctl get bgpPeer bgppeer-node-aabbff # 移除配对 calicoctl delete bgppeer bgppeer-node-aabbff |
要检查某个节点的BGP Peer,执行命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# SSH到node1,然后执行下面的命令,可以看到所有和node1配对的实体 calicoctl node status # Calico process is running. # IPv4 BGP status # +--------------+-------------------+-------+----------+-------------+ # | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | # +--------------+-------------------+-------+----------+-------------+ # | 10.0.0.101 | node-to-node mesh | up | 15:53:18 | Established | # | 10.0.0.102 | node-to-node mesh | up | 15:53:18 | Established | # | 10.0.0.103 | node-to-node mesh | up | 15:53:18 | Established | # | 10.0.0.104 | node-to-node mesh | up | 15:53:18 | Established | # | 10.0.0.105 | node-to-node mesh | up | 15:53:18 | Established | # +--------------+-------------------+-------+----------+-------------+ # IPv6 BGP status # No IPv6 peers found. |
1 2 3 4 5 6 7 8 9 10 11 12 |
apiVersion: projectcalico.org/v3 kind: BGPConfiguration metadata: # 资源的唯一性名称 name: default spec: # 全局日志级别:Debug, Info, Warning, Error, Fatal logSeverityScreen: Info # 是否启用节点全互联 nodeToNodeMeshEnabled: true # AS号 asNumber: 65000 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
apiVersion: projectcalico.org/v3 kind: FelixConfiguration metadata: # 资源的唯一性名称 name: default spec: # 指定Felix操控内核顶级Iptable规则链的方式 # Insert 默认,较安全,可以防止Calico的规则被Bypass # Append 需要注意规则链中更前面的规则可能导致Calico的规则被跳过 chainInsertMode: Insert # 从工作负载发送到其所属宿主机(通过端点egress策略之后)的流量的默认处理方式,可选值Drop, Return, Accept # 默认情况下Calico阻止从工作负载到其宿主机的流量,实现手段是使用Iptables规则的DROP目标 # Accept:Calico在处理完工作负载端点的Egress Policy之后,无条件的允许这种流量通过 # Return:使用INPUT链中的其它规则进行处理。Calico默认在INPUT链顶部插入规则,使用该选项后,Calico在处理 # 完工作负载端点的Egress Policy之后,即把封包归还给INPUT链下一条规则处理 defaultEndpointToHostAction: Drop # UDP/TCP的协议端口对,Felix总是允许这些宿主机端点上这些端口的入站流量 # 这可以防止意外的错误配置导致宿主机断开和外部的连接,默认允许SSH、etcd、BGP、DHCP failsafeInboundHostPorts: - protocol: tcp port: 22 - protocol: udp port: 68 - protocol: tcp port: 179 - protocol: tcp port: 2379 - protocol: tcp port: 2380 - protocol: tcp port: 6666 - protocol: tcp port: 6667 # UDP/TCP的协议端口对,Felix总是允许这些宿主机端点上针对这些端口的出站流量 failsafeOutboundHostPorts: - protocol: udp port: 53 - protocol: udp port: 67 - protocol: tcp port: 179 - protocol: tcp port: 2379 - protocol: tcp port: 2380 - protocol: tcp port: 6666 - protocol: tcp port: 6667 # 设置为true则允许Felix运行在具有RPF的系统上 ignoreLooseRPF: false # Felix解析宿主机端点时,需要排除的网络接口列表,逗号分隔 # 默认值是排除K8S内部使用的设备kube-ipvs0 interfaceExclude: kube-ipvs0 # 用于区分工作负载端点和宿主机端点的网络接口名前缀 interfacePrefix: cali # 是否在宿主机上配置一个IPinIP网络接口 # 如果你通过calico/node或calicoctl配置IPIP-enabled pool会自动设置为true ipipEnabled:false # 上述隧道接口的MTU ipipMTU:1440 # 是否启用IPv6支持 ipv6Support: false |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
apiVersion: projectcalico.org/v3 kind: HostEndpoint metadata: # 宿主机端点名称 name: some.name # 标签,用于应用对应的策略 labels: type: production spec: # 端点对应的网络接口 interfaceName: eth0 # 端点所在的宿主机 node: myhost # 期望和此网络接口对应的IP地址 expectedIPs: - 192.168.0.1 - 192.168.0.2 # 配置,用于应用对应的策略 profiles: - profile1 - profile2 # 命名端口,可以在Policy Rule中引用 ports: - name: some-port port: 1234 protocol: TCP - name: another-port port: 5432 protocol: UDP |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
apiVersion: projectcalico.org/v3 kind: WorkloadEndpoint metadata: # 资源名称 name: node1-k8s-frontend--5gs43-eth0 # 资源所属名字空间 namespace: default # 资源的标签 labels: app: frontend projectcalico.org/namespace: default projectcalico.org/orchestrator: k8s spec: # 此端点所属的工作负载的名字 workload: nginx # 工作负载所属的节点 node: node1 # 创建此端点的orchestrator orchestrator: k8s # 容器网络接口名 endpoint: eth0 # 工作负载端点的CNI容器ID containerID: 1337495556942031415926535 # 此工作负载端点所在的Pod名称 pod: my-nginx-b1337a # 在宿主机那一端,对接到工作负载的网络接口名 interfaceName: cali0ef24ba # 网络接口的MAC地址 mac: ca:fe:1d:52:bb:e9 # 分配到网络接口的CIDR ipNetworks: - 192.168.0.0/16 # 此工作负载的出站流量的网关 ipv4Gateway: 192.168.0.1 # 分配到此端点的Calico Profile profiles: - profile1 # 命名端口列表 ports: - name: some-port port: 1234 protocol: tcp - name: another-port port: 5432 protocol: udp # 此端点的NAT规则 ipNATs: # 内部IP地址 internalIP: # 外部IP地址 externalIP: |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: my.ippool-1 spec: # IP地址范围,端点IP从中分配 cidr: 10.1.0.0/16 # 何时使用IPinIP模式,Always, CrossSubnet, Never ipipMode: CrossSubnet # 如果启用,则利用该池中IP的容器的出站流量被IP遮掩 natOutgoing: true # 如果禁用,则Calico IPAM不会从该池中分配IP地址 disabled: false |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
apiVersion: projectcalico.org/v3 kind: Node metadata: name: node-hostname spec: bgp: # 此节点所属的AS,不指定则使用全局默认AS号 asNumber: 64512 # 此节点的IP和子网,会导出,作为该节点上的Calico端点的next-hop ipv4Address: 10.244.0.1/24 ipv6Address: 2001:db8:85a3::8a2e:370:7334/120 # IP-in-IP隧道中此节点的IP地址 ipv4IPIPTunnelAddr: 192.168.0.1 OrchRefs: - nodeName: node-hostname orchestrator: k8s |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
apiVersion: projectcalico.org/v3 kind: NetworkPolicy metadata: name: allow-tcp-6379 namespace: production spec: # 匹配的端点 selector: role == 'database' # 此策略针对的流量方向 types: - Ingress - Egress # 入站规则列表(有序) ingress: # 允许来自frontend的TCP/6379流量 # 匹配规则时的行为。可选值:Allow, Deny, Log, Pass - action: Allow # 匹配协议列表,可选值TCP, UDP, ICMP, ICMPv6, SCTP, UDPLite, 1-255 protocol: TCP # 不匹配协议列表,可选值TCP, UDP, ICMP, ICMPv6, SCTP, UDPLite, 1-255 notProtocol: UDP # 源匹配参数 source: # 根据端点选择器匹配 selector: role == 'frontend' # 根据端点所属CIDR匹配 nets: 172.21.0.0/16 # 不匹配的CIDR notNets: 172.21.0.0/16 # 根据名字空间匹配,如果指定,则仅仅目标名字空间中的工作负载端点才能匹配 namespaceSelector: env = 'dev' # 根据端点进行匹配 ports: 0 notPorts: 0 # 目的匹配参数 destination: ports: - 6379 # ICMP匹配规则 icmp: type: 0 code: 0 # ICMP不匹配规则 notICMP: # ICMP类型, 0-254 type: 0 # ICMP代码, 0-255 code: 0 # 出站规则列表 egress: - action: Allow |
Calico选择器语法:
语法 | 说明 |
all() | 匹配所有资源 |
k == ‘v’ | 匹配标签k的值为v的资源 |
k != ‘v’ | 匹配具有标签k,且其值不为v的资源 |
has(k) | 匹配具有标签k的资源 |
!has(k) | 匹配不具有标签k的资源 |
k in { ‘v1’, ‘v2’ } | 匹配标签k的值为v1或v2的资源 |
k not in { ‘v1’, ‘v2’ } | 匹配标签k的值不为v1或v2的资源,或者没有标签k的资源 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
apiVersion: projectcalico.org/v3 kind: Profile metadata: name: profile1 # 使用此Profile的端点,自动添加如下标签 labels: profile: profile1 spec: # 此Profile的网络策略 ingress: - action: Deny source: nets: - 10.0.20.0/24 - action: Allow source: selector: profile == 'profile1' egress: - action: Allow |
calicoctl是一个命令行工具,使用它可以创建、修改、删除Calico对象。你可以在任何能够访问Calico数据库的主机上使用该命令。
1 2 3 |
cd /usr/local/bin curl -O -L https://github.com/projectcalico/calicoctl/releases/download/v2.0.0/calicoctl chmod +x calicoctl |
很多calicoctl命令都需要访问Calico数据库,你需要通过配置文件来指定数据库的信息:
1 2 3 4 5 6 |
apiVersion: projectcalico.org/v3 kind: CalicoAPIConfig metadata: spec: datastoreType: "etcdv3" etcdEndpoints: "http://10.96.232.136:6666" |
注意:在Kubeadm部署方式下,Calico不会使用K8S的etcd,而是自己创建一个,执行下面的命令可以查看:
1 2 |
kubectl --namespace=kube-system get service # calico-etcd ClusterIP 10.96.232.136 <none> 6666/TCP 1h |
如果配置正确,下面的命令将会返回节点列表:
1 2 3 4 5 6 7 8 |
calicoctl get nodes # NAME # xenial-100 # xenial-101 # xenial-102 # xenial-103 # xenial-104 # xenial-105 |
子命令 | 说明 | ||
create | 根据指定的文件名或者从标准输入来创建资源 | ||
replace | 替换资源 | ||
apply | 应用资源:如果资源不存在则创建之,如果存在则替换之 | ||
delete | 删除资源 | ||
get |
获取资源信息,支持的资源包括: bgpConfiguration 示例:
|
||
convert | 转换不同API版本的配置文件 | ||
ipam | IP地址管理器相关命令:
|
||
node run |
注意:节点的很多配置可以通过环境变量提供 在当前节点上启动一个calico/node容器实例,以提供Calico网络、网络策略的支持:
|
||
node status | 检查节点的状态 | ||
node diags | 收集Calico节点的诊断信息 | ||
node checksystem | 检查节点内核,是否可以作为Calico节点 |
资源类型 | 说明 |
node |
表示运行Calico的节点,当添加一个主机到Calico集群中后,你需要创建相应的Node资源 当启动一个Calico节点时,它的名称必须和此资源的名称一致 默认情况下,启动一个calico/node实例会自动创建Node资源,使用机器的hostname作为节点名称 |
bgpPeer |
BGP Peer资源代表Calico集群中某个节点与之配对的远程BGP Peer 使用BGP Peer你可以将Calico Newtwork和数据中心结构(例如ToR)连接起来 |
bgpConfiguration |
表示集群的BGP相关的选项 |
felixConfiguration |
表示集群的Felix相关的选项 Felix是运行在任何提供端点(Endpoint)的机器(运行VM或容器的节点)上的守护程序,它负责编程式的路由和ACL,以及为机器上端点提供预期连接性所需的任何东西 |
hostEndpoint |
表示运行Calico的主机上的一个网络接口 每个HostEndpoint可以包含一系列的标签、一个Profile列表,Calico基于这些来应用策略到网络接口。如果没有任何标签、Profile则Calico不会应用任何策略 |
workloadEndpoint |
表示将基于Calico实现网络连接的VM、容器,连接到它们的宿主机的网络接口 WorkloadEndpoint是具有名字空间的资源,只有相同名字空间内定义的networkPolicy才能应用到其上 |
ipPool |
表示一个IP集合,工作负载端点的IP从此集合中分配 IP-in-IP隧道可以用在网络结构(network fabric )强制进行Src/Dest地址检查、并丢弃无法识别的地址的流量的场景下。在某些公有云环境下,你可能无法完全控制网络,特别是无法进行网络路由器和Calico节点之间的BGP配对、各Calico节点也不能L2直连,使用IP-in-IP封装可以确保跨工作负载(Inter-workload)流量正常传输 当启用IP-in-IP模式时,Calico在路由到IP池范围内的工作负载IP时,会对IP封包进行一次包装 设置ipipMode=Always,则从任何启用Calico的主机发往任何基于Calico网络的容器、虚拟机的流量,都会进行包装 设置ipipMode=CrossSubnet,则可以在下面的场景下优化性能:
|
globalNetworkPolicy | 全局性的网络策略,不具有名字空间,应用到任何名字空间中的工作负载端点、主机端点 |
networkPolicy |
应用到匹配标签选择器的端点的网络策略,网络策略是命名空间内部的资源 网络策略是一个有序的规则的集合,它应用到标签选择器所匹配的端点 |
profile | 包含应用到分配了Profile的单个的端点的网络策略 |
某次操作意外删除了节点上的calico_node容器,重新执行calicoctl node run恢复容器后,发现此节点无法连接到K8S容器网络,无法ping通任何容器地址。
最终发现,是此节点的配置出现问题(可能是calicoctl node run的参数不对引发),ipv4Address的掩码错误:
1 2 |
# 应该是 10.0.0.1/16,32明显错误 ipv4Address: 10.0.0.1/32 |
除了此节点以外,其上运行的虚拟机出现同样的问题。修复上述掩码后问题消失。
需要注意一点,正常的路由,出口应该是calicoctl node run时检测到的网络接口:
1 |
172.27.252.128 radon 255.255.255.192 UG 0 0 0 virbr0 |
而出现问题时,该节点上的出口从期望的virbr0变成了tunl0。
写的很好,感谢
:D