Cilium is een open source-project om netwerken, beveiliging en observeerbaarheid te bieden voor cloud-native omgevingen zoals Kubernetes-clusters en andere containerorkestratieplatforms.
Hulp nodig met Cilium of Kubernetes?
Zorg ervoor dat jij onze whitepaper ontvangt of vraag een van de experts gewoon alles wat te maken heeft met Cilium, netwerken, Kubernetes, clustors of setups. We staan meer dan klaar om je te helpen!
Bekijk zeker onze laatste evenementen over Cilium TIP
Sluit je aan bij SUE HQ of meld je aan voor onze livestreams. Leer alles wat er te weten valt en inzichten over Cilium en zijn mogelijkheden. Leer meer >
BGP
Border Gateway Protocol (BGP) is een gestandaardiseerd extern gateway-protocol dat is ontworpen om routerings- en bereikbaarheidsinformatie uit te wisselen tussen autonome systemen (AS) op internet. BGP is geclassificeerd als een pad-vectorrouteringsprotocol. Het neemt routeringsbeslissingen op basis van paden, netwerkbeleid of regelsets die zijn geconfigureerd door een netwerkbeheerder.
Cilium and BGP
In release 1.10 integreerde Cilium BGP-ondersteuning met MetalLB, waardoor het Kubernetes Service-ip-adressen van het type LoadBalancer kan aankondigen met behulp van BGP. Het resultaat is dat services van buiten het Kubernetes-netwerk bereikbaar zijn zonder extra componenten, zoals een Ingress Router. Vooral het ‘zonder extra componenten’ gedeelte is fantastisch nieuws, aangezien elk onderdeel latency toevoegt – dus zonder die minder latency.
Lab environment
Laat ons eerst uitleggen hoe het lab is opgezet en wat het eindresultaat zal zijn.
Het lab bestaat uit een clientnetwerk (192.168.10.0/24) en een Kubernetes-netwerk (192.168.1.1/24). Wanneer een Service een LoadBalancer ip-adres krijgt, wordt dat adres bediend vanuit de pool 172.16.10.0/24. In ons lab zijn de volgende knooppunten aanwezig:
Name | IP addresses | Description |
---|---|---|
bgp-router1 | 192.168.1.1/24 (k8s network), 192.168.10.1/24 (client network) | The BGP router |
k8s-control | 192.168.1.5/24 (k8s network), 192.168.10.233/24 (client network) | Management node |
k8s-master1 | 192.168.1.10/24 (k8s network) | k8s master |
k8s-worker1 | 192.168.1.21/24 (k8s network) | k8s worker |
k8s-worker2 | 192.168.1.22/24 (k8s network) | k8s worker |
k8s-worker3 | 192.168.1.23/24 (k8s network) | k8s worker |
k8s-worker4 | 192.168.1.24/24 (k8s network) | k8s worker |
k8s-worker5 | 192.168.1.25/24 (k8s network) | k8s worker |
Nadat alle onderdelen zijn geconfigureerd, is het mogelijk om vanuit het clientnetwerk een Service in het Kubernetes-netwerk te bereiken met behulp van het aangekondigde LoadBalancer IP-adres. Zie afbeelding hieronder.
Lab configuration
BGP router
De router is een Red Hat 8-systeem met drie netwerkinterfaces (extern-, kubernetes- en clientnetwerk) met FRRouting (FRR) verantwoordelijk voor de afhandeling van het BGP-verkeer. FRR is een gratis en open source internetrouteringsprotocolsuite voor Linux- en Unix-platforms. Het implementeert veel routeringsprotocollen zoals BGP, OSPF en RIP. In ons lab is alleen BGP ingeschakeld.
dnf install frr systemctl enable frr firewall-cmd --permanent --add-port=179/tcp echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/01-sysctl.conf sysctl -w net.ipv4.ip_forward=1
Na het instaleren FRR, the BGP daemon is configured to start by changing bgpd=no to bgpd=yes in the configuration file /etc/frr/daemons, using the following BGP configuration in /etc/frr/bgpd.conf
log syslog notifications frr defaults traditional ! router bgp 64512 no bgp ebgp-requires-policy bgp router-id 192.168.1.1 neighbor 192.168.1.10 remote-as 64512 neighbor 192.168.1.10 update-source enp7s0 neighbor 192.168.1.21 remote-as 64512 neighbor 192.168.1.21 update-source enp7s0 neighbor 192.168.1.22 remote-as 64512 neighbor 192.168.1.22 update-source enp7s0 neighbor 192.168.1.23 remote-as 64512 neighbor 192.168.1.23 update-source enp7s0 neighbor 192.168.1.24 remote-as 64512 neighbor 192.168.1.24 update-source enp7s0 neighbor 192.168.1.25 remote-as 64512 neighbor 192.168.1.25 update-source enp7s0 address-family ipv4 unicast neighbor 192.168.1.10 next-hop-self neighbor 192.168.1.21 next-hop-self neighbor 192.168.1.22 next-hop-self neighbor 192.168.1.23 next-hop-self neighbor 192.168.1.24 next-hop-self neighbor 192.168.1.25 next-hop-self exit-address-family ! address-family ipv6 unicast exit-address-family ! line vty
In the config file above the AS number 64512 is used, which is reserved for private use. The Kubernetes master node and worker nodes are configured as neighbor. The ip address of the router’s interface in the Kubernetes network (192.168.1.1) is used as router id.
After the configuration above is applied and the FRR daemon is started using the systemctl start frr command, the command vtysh -c ‘show bgp summary’ shows the following output.
IPv4 Unicast Summary: BGP router identifier 192.168.1.1, local AS number 64512 vrf-id 0 BGP table version 0 RIB entries 0, using 0 bytes of memory Peers 6, using 86 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt
192.168.1.10 4 64512 0 0 0 0 0 never Active 0
192.168.1.21 4 64512 0 0 0 0 0 never Active 0
192.168.1.22 4 64512 0 0 0 0 0 never Active 0
192.168.1.23 4 64512 0 0 0 0 0 never Active 0
192.168.1.24 4 64512 0 0 0 0 0 never Active 0
192.168.1.25 4 64512 0 0 0 0 0 never Active 0
Total number of neighbors 6
Kubernetes and Cilium
It goes beyond the scope of this blog to explain how to install the Kubernetes nodes and the Kubernetes cluster. For your information: in this lab Red Hat 8 (minimal installation) is used as the operating system for all the nodes and Kubeadm was subsequently used to set up the cluster.
The Cilium Helm chart version v1.10.5 is used to install and configure Cilium on the cluster, using these values:
debug: enabled: true externalIPs: enabled: true hostPort: enabled: true hostServices: enabled: true kubeProxyReplacement: strict k8sServiceHost: 192.168.1.10 k8sServicePort: 6443 nodePort: enabled: true operator: replicas: 1 bgp: enabled: true announce: loadbalancerIP: true hubble: enabled: true metrics: enabled: - dns - drop - tcp - flow - port-distribution - icmp - http listenAddress: ":4244" relay: enabled: true ui: enabled: true
To get Cilium up and running with BGP, only the bgp key and subkeys are needed from the settings above. The other settings are used to get a fully working Cilium environment with,for example, the Hubble user interface.
Expose a service
When Kubernetes is running and Cilium is configured, it is time to create a deployment and expose it to the network using BGP. The following YAML file creates a Deployment web1, which is just a simple NGINX web server serving the default web page. The file also creates a Service web1-lb with a Service type LoadBalancer. This results in an external ip address that is announced to our router using BGP.
--- apiVersion: v1 kind: Service metadata: name: web1-lb spec: type: LoadBalancer ports: - port: 80 targetPort: 80 protocol: TCP name: http selector: svc: web1-lb
—
apiVersion: apps/v1
kind: Deployment
metadata:
name: web1
spec:
selector:
matchLabels:
svc: web1-lb
template:
metadata:
labels:
svc: web1-lb
spec:
containers:
– name: web1
image: nginx
imagePullPolicy: IfNotPresent
ports:
– containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
After applying the YAML file above, the command kubectl get svc shows that Service web1-lb has an external ip address:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d3h web1-lb LoadBalancer 10.106.236.120 172.16.10.0 80:30256/TCP 7d2h
The address 172.16.10.0 seems strange, but it is fine. Often the .0 address is skipped and the .1 address is used as the first address. One of the reasons is that in the early days the .0 address was used for broadcast, which was later changed to .255. Since .0 is still a valid address MetalLB, which is responsible for the address pool, hands it out as the first address. The command vtysh -c ‘show bgp summary’ on router bgp-router1 shows that it has received one prefix:
IPv4 Unicast Summary: BGP router identifier 192.168.1.1, local AS number 64512 vrf-id 0 BGP table version 17 RIB entries 1, using 192 bytes of memory Peers 6, using 128 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt
192.168.1.10 4 64512 445 435 0 0 0 03:36:56 1 0
192.168.1.21 4 64512 446 435 0 0 0 03:36:54 1 0
192.168.1.22 4 64512 445 435 0 0 0 03:36:56 1 0
192.168.1.23 4 64512 445 435 0 0 0 03:36:56 1 0
192.168.1.24 4 64512 446 435 0 0 0 03:36:56 1 0
192.168.1.25 4 64512 445 435 0 0 0 03:36:56 1 0
Total number of neighbors 6
The following snippet of the routing table (ip route) tells us that for that specific ip address 172.16.10.0, 6 possible routes/destinations are present. In other words, all Kubernetes nodes announced that they can handle traffic for that address. Cool!!
172.16.10.0 proto bgp metric 20 nexthop via 192.168.1.10 dev enp7s0 weight 1 nexthop via 192.168.1.21 dev enp7s0 weight 1 nexthop via 192.168.1.22 dev enp7s0 weight 1 nexthop via 192.168.1.23 dev enp7s0 weight 1 nexthop via 192.168.1.24 dev enp7s0 weight 1 nexthop via 192.168.1.25 dev enp7s0 weight 1
Indeed, the web page is now visible from our router.
$ curl -s -v http://172.16.10.0/ -o /dev/null * Trying 172.16.10.0... * TCP_NODELAY set * Connected to 172.16.10.0 (172.16.10.0) port 80 (#0) > GET / HTTP/1.1 > Host: 172.16.10.0 > User-Agent: curl/7.61.1 > Accept: */* > < HTTP/1.1 200 OK < Server: nginx/1.21.3 < Date: Sun, 31 Oct 2021 14:19:17 GMT < Content-Type: text/html < Content-Length: 615 < Last-Modified: Tue, 07 Sep 2021 15:21:03 GMT < Connection: keep-alive < ETag: "6137835f-267" < Accept-Ranges: bytes < { [615 bytes data] * Connection #0 to host 172.16.10.0 left intact
And a client in our client network can also reach that same page, since it uses bgp-router1 as default route.
More details
Nu werkt het allemaal, de meeste ingenieurs willen meer details zien, dus ik zal je niet teleurstellen.
Ping
Een van de eerste dingen die opvalt, is dat het LoadBalanced ip-adres niet bereikbaar is via ping. Een beetje dieper duiken onthult waarom, maar laten we eerst de Cilium-aliassen maken om het gemakkelijker te maken om cilium te gebruiken, dat aanwezig is in elke Cilium-agentpod.
CILIUM_POD=$(kubectl -n kube-system get pods -l k8s-app=cilium --output=jsonpath='{.items[*].metadata.name}' --field-selector=spec.nodeName=k8s-master1) alias cilium="kubectl -n kube-system exec -ti ${CILIUM_POD} -c cilium-agent -- cilium"
First see the output of this snippet of cilium bpf lb list, that shows the configured load balancing configuration inside Cilium for our Service *web1-lb`:
172.16.10.0:80 0.0.0.0:0 (5) [LoadBalancer] 10.0.3.150:80 (5)
Hier kan je zien dat er een mapping wordt gemaakt tussen bronpoort 80 en bestemmingspoort 80. Deze mapping wordt uitgevoerd met behulp van eBPF-logica op de interface en is aanwezig op alle knooppunten. Deze mapping laat zien dat alleen(!) verkeer voor poort 80 in evenwicht is. Al het andere verkeer, inclusief de ping, wordt niet opgevangen. Daarom kun je zien dat het icmp-pakket het knooppunt bereikt, maar er wordt nooit een antwoord verzonden.
Observe traffic
Hubble is het netwerk- en beveiligings observatieplatform dat is gebouwd op eBPF en Cilium. Via de opdrachtregel en via een grafische web-GUI is het mogelijk om actueel en historisch verkeer te zien. In dit lab wordt Hubble op de k8s-control node geplaatst, die direct toegang heeft tot de API van Hubble Relay. Hubble Relay is het onderdeel dat de benodigde informatie van de Cilium-knooppunten verkrijgt. Houd er rekening mee dat het hubble-commando ook aanwezig is in elke Cilium-agentpod, maar dat deze alleen informatie voor die specifieke agent zal tonen!
export HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt) curl -L --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-amd64.tar.gz{,.sha256sum} sha256sum --check hubble-linux-amd64.tar.gz.sha256sum sudo tar xzvfC hubble-linux-amd64.tar.gz /usr/local/bin rm hubble-linux-amd64.tar.gz{,.sha256sum}
The following outputs show the observer information which is a result of the curl http://172.16.10.0/ command on the router.
$ hubble observe --namespace default --follow
Oct 31 15:43:41.382: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: SYN)
Oct 31 15:43:41.384: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: ACK)
Oct 31 15:43:41.384: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: ACK, PSH)
Oct 31 15:43:41.385: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: ACK)
Oct 31 15:43:41.385: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: ACK)
Oct 31 15:43:41.386: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: ACK, FIN)
Oct 31 15:43:41.386: 192.168.1.1:36946 <> default/web1-696bfbbbc4-jnxbc:80 to-overlay FORWARDED (TCP Flags: ACK)
Eerder waarschuwde ik voor het niet gebruiken van het hubble-commando in de Cilium-agentpod, maar het kan ook zeer informatief zijn om het specifieke knooppuntverkeer te zien. In dit geval wordt een hubble-observatie –namespace default –follow uitgevoerd binnen elke Cilium-agentpod en wordt de krul van de router eenmaal uitgevoerd. Op het knooppunt waar de pod ‘leeft’ (k8s-worker2), zien we dezelfde uitvoer als hierboven. Op een andere pod (k8s-worker1) zien we echter de volgende uitvoer:
Oct 31 15:56:05.220: 10.0.3.103:48278 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: SYN) Oct 31 15:56:05.220: 10.0.3.103:48278 <- default/web1-696bfbbbc4-jnxbc:80 to-stack FORWARDED (TCP Flags: SYN, ACK) Oct 31 15:56:05.220: 10.0.3.103:48278 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: ACK) Oct 31 15:56:05.221: 10.0.3.103:48278 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: ACK, PSH) Oct 31 15:56:05.221: 10.0.3.103:48278 <- default/web1-696bfbbbc4-jnxbc:80 to-stack FORWARDED (TCP Flags: ACK, PSH) Oct 31 15:56:05.222: 10.0.3.103:48278 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: ACK, FIN) Oct 31 15:56:05.222: 10.0.3.103:48278 <- default/web1-696bfbbbc4-jnxbc:80 to-stack FORWARDED (TCP Flags: ACK, FIN) Oct 31 15:56:05.222: 10.0.3.103:48278 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: ACK) Oct 31 15:56:12.739: 10.0.4.105:36956 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: SYN) Oct 31 15:56:12.739: default/web1-696bfbbbc4-jnxbc:80 <> 10.0.4.105:36956 to-overlay FORWARDED (TCP Flags: SYN, ACK) Oct 31 15:56:12.742: 10.0.4.105:36956 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: ACK) Oct 31 15:56:12.742: 10.0.4.105:36956 -> default/web1-696bfbbbc4-jnxbc:80 to-endpoint FORWARDED (TCP Flags: ACK, PSH) Oct 31 15:56:12.745: default/web1-696bfbbbc4-jnxbc:80 <> 10.0.4.105:36956 to-overlay FORWARDED (TCP Flags: ACK, PSH) Oct 31 15:56:12.749: 10.0.4.105:36956 -