CNI 网络分析 4.2 Calico 介绍与原理(二)

Calico 介绍与原理(二)

CNI

  • 支持多种 datapath,默认是 linuxDataplane,支持 CRI 调用时 选择 grpc 实现的 dataplane,还支持 vpp 或自己的 dataplane
  • 通过 ipam 获取到 ip 后,DoNetworking
func (d *linuxDataplane) DoNetworking(
    ctx context.Context,
    calicoClient calicoclient.Interface,
    args *skel.CmdArgs,
    result *cniv1.Result,
    desiredVethName string,
    routes []*net.IPNet,
    endpoint *api.WorkloadEndpoint,
    annotations map[string]string,
) 
  • Ns 里创建 veth 设备
  • Host 端网卡 calixxx 配置 mac ee:ee:ee:ee:ee:ee
  • 如果有 ipv6 地址,关闭 DAD
  • Linkup 网卡
  • 通过 annotation 查 是否指定 mac,指定则配置指定的 mac
  • 配置 ns 里路由 default via 169.254.1.1 dev eth0,169.254.1.1 dev eth0 scope link
  • 如果有 ipv6,设置 /proc/sys/net/ipv6/conf/all/disable_ipv6 = 0, /proc/sys/net/ipv6/conf/default/disable_ipv6 = 0,/proc/sys/net/ipv6/conf/lo/disable_ipv6 = 0,创建 ip route6
  • 为 ns 里网卡配置 ip
  • 开启 ipforwarding
  • 开启 /proc/sys/net/ipv4/conf/calixxx/route_localnet = 1,Enable routing to localhost
  • /proc/sys/net/ipv4/neigh/calixxx/proxy_delay = 0,arp 延迟,calico 不需要
  • /proc/sys/net/ipv4/conf/calixxx/proxy_arp = 1,开启 arp 代理
  • /proc/sys/net/ipv4/conf/calixxx/forwarding。
  • 在 host 配置到该 ip 的路由指向 calixxx。
  • 通过 annotation 检查是否有浮动 ip 配置,创建 ipnat
  • 创建 WorkloadEndpoints
  • done

Pod 创建完后,pod 内部默认路由指向 169.254.1.1,且 veth host 端配置了 arp_proxy,配置的 mac 是 ee:ee:ee:ee:ee:ee,所以 pod 内部下一跳都是 169.254.1.1

# ip netns exec cni-138c93d8-c975-8b38-3154-1dccf5ed9d83 ip r
default via 169.254.1.1 dev eth0 
169.254.1.1 dev eth0 scope link 

# ip netns exec cni-138c93d8-c975-8b38-3154-1dccf5ed9d83 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default 
    link/ether 32:21:45:c4:c5:d5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.153.204/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::3021:45ff:fec4:c5d5/64 scope link 
       valid_lft forever preferred_lft forever

# ip netns exec cni-138c93d8-c975-8b38-3154-1dccf5ed9d83 ip neighbor
169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee REACHABLE 
172.18.22.111 dev eth0 lladdr ee:ee:ee:ee:ee:ee STALE

IPAM

  • 默认使用 “calico-ipam” IPAM
  • 先获取 namespace 是否通过 annotation 配置 ippool,pod 是否配置 ippool
  • 获取 pod annotation 是否有 cni.projectcalico.org/ipAddrsNoIpam,cni.projectcalico.org/ipAddrs
    ipAddrsNoIpam := annot["cni.projectcalico.org/ipAddrsNoIpam"]
    ipAddrs := annot["cni.projectcalico.org/ipAddrs"]

    // Switch based on which annotations are passed or not passed.
    switch {
    case ipAddrs == "" && ipAddrsNoIpam == "":
    case ipAddrs != "" && ipAddrsNoIpam != "":
    case ipAddrsNoIpam != "":
    case ipAddrs != "":
  1. 全不配置时,直接代理调取 calico-ipam 二进制
  2. 全配置时,失败
  3. 只配置 ipAddrsNoIpam 时,必须是 calico-ipam 类型,会配置成所指定的 IP
  4. 只配置 ipAddrs 时,必须是 calico-ipam 类型,先 calico.IPAM().ReleaseIPs ip,再使用指定的 IP 去代理调用 calico-ipam
  • 通过调用 calicoClient.IPAM().AutoAssign(ctx, assignArgs) 获取 ip
  • 获取 预留 IP 和 cidr,预留 IP 和 cidr 可以通过 资源 IPReservation 进行配置
  • 通过 node 相关的 cidr 去获取一个 ip。

指定 IP

apiVersion: v1
kind: Pod
metadata:
  name: pod5
  namespace: default
  annotations:
    cni.projectcalico.org/ipAddrs: "[\"10.244.153.250\"]"
spec:
  containers:
  - name: pod5
    image: nginx
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
default       pod5    1/1     Running   0              115s    10.244.153.250

指定非 IPAM IP

需要先开启 feature

# kubectl edit configmap calico-config -n kube-system
// 添加
          "feature_control": {
              "ip_addrs_no_ipam": true,
              "floating_ips": true
          }
apiVersion: v1
kind: Pod
metadata:
  name: pod5
  namespace: default
  annotations:
    cni.projectcalico.org/ipAddrsNoIpam: "[\"80.90.100.200\"]"
spec:
  containers:
  - name: pod5
    image: nginx
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
pod5   1/1     Running   0             54s     80.90.100.200    node112

当然只有本节点能通,其他节点需要自己解决路由问题

Floating_ips
apiVersion: v1
kind: Pod
metadata:
  name: pod5
  namespace: default
  annotations:
    cni.projectcalico.org/floatingIPs: "[\"172.18.22.113\"]"
spec:
  containers:
  - name: pod5
    image: nginx
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
- apiVersion: projectcalico.org/v3
  kind: WorkloadEndpoint
  metadata:
    creationTimestamp: "2023-02-14T02:03:01Z"
    labels:
      projectcalico.org/namespace: default
      projectcalico.org/orchestrator: k8s
      projectcalico.org/serviceaccount: default
    name: node112-k8s-pod5-eth0
    namespace: default
    resourceVersion: "4509106"
    uid: beb345d3-6e9f-4144-918d-4f96fa8aada5
  spec:
    containerID: dc36d90f05c1890f78b8bb8ddc72426c2730ce60b58bf926b946d0131f620c78
    endpoint: eth0
    interfaceName: cali8e9fad9b07c
    ipNATs:
    - externalIP: 172.18.22.113
      internalIP: 10.244.146.203
    ipNetworks:
    - 10.244.146.203/32
    node: node112
    orchestrator: k8s
    pod: pod5
    ports:
    - hostIP: ""
      hostPort: 0
      name: nginx-port
      port: 80
      protocol: TCP
    profiles:
    - kns.default
    - ksa.default.default
    serviceAccountName: default
kind: WorkloadEndpointList
metadata:
  resourceVersion: "4509937"

NetworkPolicy

说明

除了支持 k8s api 定义的 networkPolicy,同样有一套自己定义的更丰富的 networkPolicy,和 k8s 定义的使用原则一样,白名单规则。selector 支持写表达式。

  • 策略可以应用于任何类型的端点:pod/容器、VM和/或主机接口
  • 策略可以定义适用于入口、出口或两者的规则
  • 策略规则支持:
    • 操作:允许、拒绝、记录、通过
    • 源和目标匹配条件:
      • 端口:编号、范围内的端口和Kubernetes命名的端口
      • 协议:TCP、UDP、ICMP、SCTP、UDPlite、ICMPv6、协议编号(1-255)
      • HTTP属性(如果使用Istio服务网格)
      • ICMP属性
      • IP版本(IPv4、IPv6)
      • IP或CIDR
      • 端点选择器(使用标签表达式选择pod、VM、主机接口和/或网络集)
      • 命名空间选择器
      • 服务帐户选择器
  • 可选的数据包处理控制:禁用连接跟踪、在DNAT之前应用、应用于转发流量和/或本地终止流量

Endpoints 定义

Endpoint 包括 workload endpoint 和 host endpoint,workload endpoint 对应 k8s 的 pod 或 openstack 的 vm。
Host endpoint 对应着 host 上的一组 interface

Global

Calico networkPolicy 是 namespaced 的资源,应用于 该 namespace,全局的则使用 global networkPolicy。这是两种资源

Traffic 行为

action:allow,deny,Log,Pass 支持四种方法

  • 如果 pod 没有规则,所有流量 allow
  • 如果 ingress 有规则,则只有匹配到规则的才可以进
  • 如果 egress 有规则,则只有匹配到规则的才可以出
  • Log 是会将流量打到 syslog
  • Pass 跳过其他的策略,跳到给 workloadendpoint 的第一个 profile

Order

策略应用顺序,序号越大越后执行,根据 iptables chain 顺序,order 越小优先级越高。kubernetes networkpolicy 默认 order 1000

Protocol

支持 TCP, UDP, ICMP, ICMPv6, SCTP, UDPLite, 1-255,同样支持 notProtocol

ICMP

支持 ICMP 和 notICMP

ipVersion

配置 4,6

http

http:
  methods: ['GET', 'PUT']
  paths:
    - exact: '/projects/calico'
    - prefix: '/users'

挖个坑,http 需结合 Istio,等到介绍 Istio 来进行分析

Selector

Selector 是一个表达式,它根据标签与资源匹配或不匹配。
Calico 标签选择器支持许多运算符,可以使用布尔运算符和括号将它们组合成更大的表达式。
例如

! has(my-label) || my-label starts with 'prod' && role in {'frontend','business'}

示例

  1. Ingress allow
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: allow-tcp-80
  namespace: default
spec:
  selector: app == 'nginx'
  ingress:
    - action: Allow
      protocol: TCP
      source:
        selector: role == 'pod1'
      destination:
        ports:
          - 80

app == nginx label 的 pod 只允许被 role == pod1 label 的且是 default ns 里的 pod 可以访问

  1. Ingress + namespaceselector allow
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: allow-tcp-80
  namespace: default
spec:
  selector: app == 'nginx'
  ingress:
    - action: Allow
      protocol: TCP
      source:
        selector: role == 'pod1'
        namespaceSelector: user == 'xujunjie'
      destination:
        ports:
          - 80

app == nginx label 的 pod 只允许被 role == pod1 label 的且所在 namespace 有 user == xujunjie label 里的 pod 可以访问 ;此外还支持 serviceAccountSelector

  1. Ingress + global deny
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-80
spec:
  selector: app == 'nginx'
  ingress:
    - action: Deny
      protocol: TCP
      source:
        selector: role == 'pod1'

App == ‘nginx’ ingress 全拒绝,因为是白名单,所以只要有 ingress 策略,就默认全不通,然后只有 deny,所以全不通。所以这么设置意义不大

  1. Ingress + global deny + allow + 表达式
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-80
spec:
  selector: app == 'nginx'
  ingress:
    - action: Deny
      protocol: TCP
      source:
        selector: role == 'pod1'
    - action: Allow
      protocol: TCP
      source:
        namespaceSelector: user == 'xujunjie' && (! has(privileged-namespace))

App == nginx 的 pod ingress 只允许 user == xujunjie 且没有 privileged-namespace 的 namespace 访问。

  1. Ingress + global deny + allow + multus selector
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-80
spec:
  selector: app == 'nginx'
  ingress:
    - action: Deny
      protocol: TCP
      source:
        selector: role == 'pod1'
    - action: Allow
      protocol: TCP
      source:
        selector: role == 'pod1'
        namespaceSelector: user == 'xujunjie' && (! has(privileged-namespace))

App == nginx 的 pod ingress 只允许 user == xujunjie 且没有 privileged-namespace 的 namespace 而且 pod 需要有 role == ‘pod1’ 访问。

  1. Egress + cidr + ports
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: egress80
spec:
  selector: role == 'pod1'
  types:
    - Egress
  egress:
    - action: Allow
      protocol: TCP
      destination:
        nets:
          - 10.244.146.192/26
        ports:
          - 80    

Role == ‘pod1’ 的 workload 只能访问 10.244.146.192/26 TCP 80 端口
Ports 支持单个 port,port 范围,或 port name(workload 里的 port name): [8080, ‘1234:5678’, ‘named-port’]

  1. Ingress + order
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-80
spec:
  order: 3000
  selector: app == 'nginx'
  ingress:
    - action: Deny
      protocol: TCP
      source:
        selector: role == 'pod1'

---
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: allow-80
spec:
  order: 2000
  selector: app == 'nginx'
  ingress:
    - action: Allow
      protocol: TCP
      source:
        selector: role == 'pod1'

Order 2000 执行,role == ‘pod1’ 可以访问 app == ‘nginx’

  1. Ingress + Log
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
Metadata:
  name: log-80
Spec:
  selector: app == 'nginx'
  types:
    - Ingress
  ingress:
    - action: Log
      protocol: TCP
      source:
        selector: role == 'pod1'
    - action: Allow
      protocol: TCP
      source:
        selector: role == 'pod1'

app == ‘nginx’ 可以被 role == ‘pod1’ 访问 且会有syslog。

# dmesg
[606874.480824] calico-packet: IN=calice0906292e2 OUT=calibd2348b4f67 MAC=ee:ee:ee:ee:ee:ee:32:21:45:c4:c5:d5:08:00 SRC=10.244.153.204 DST=10.244.153.201 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=59627 DF PROTO=TCP SPT=41528 DPT=80 WINDOW=64800 RES=0x00 SYN URGP=0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值