EKS Anywhere, jiving with Cilium OSS and BGP Load Balancer
This article is part of the EKS Anywhere series EKS Anywhere, extending the Hybrid cloud momentum | by Ambar Hassani
Up until now, I had always used MetalLB as the LoadBalancer and NGINX Ingress Controller for my on premises EKS-Anywhere clusters. In that context, I wanted to swap out the CNI and Load-balancer to a full spectrum of capabilities provided by Cilium OSS.
This blog will focus on replacing the default Cilium installation on EKS Anywhere with Cilium OSS and deploy:
- BGP based load balancer for EKS Anywhere
- Replacement of Kube-proxy with eBPF
- Cilium Ingress with TLS
- Gateway API with TLS
- Hubble UI with Ingress and TLS
The below visual articulates the setup.
Note that EKS-Anywhere is by default bootstrapped with a minimal install of Cilium CNI. However, to achieve the below functionality, we need to switch over to Cilium OSS CNI.
Let’s begin!
Create the EKS Anywhere cluster. In the below logs, one can observe the creation of the cluster with Kubernetes version 1.29
One thing to observe is that we need to setup the cluster spec with the skip upgrade setting for default Cilium CNI
Your cluster.spec should look like this
apiVersion: anywhere.eks.amazonaws.com/v1alpha1
kind: Cluster
metadata:
annotations:
anywhere.eks.amazonaws.com/managed-by-cli: "true"
anywhere.eks.amazonaws.com/management-components-version: v0.19.5
name: eksanag23
namespace: default
spec:
clusterNetwork:
cniConfig:
cilium:
skipUpgrade: true
pods:
cidrBlocks:
- 192.168.0.0/16
services:
cidrBlocks:
- 10.96.0.0/12
Next, we create the cluster…
Creating new bootstrap cluster
Provider specific pre-capi-install-setup on bootstrap cluster
Installing cluster-api providers on bootstrap cluster
Provider specific post-setup
Installing EKS-A custom components on bootstrap cluster
Installing EKS-D components
Installing EKS-A custom components (CRD and controller)
Creating new workload cluster
Creating EKS-A namespace
Installing cluster-api providers on workload cluster
Installing EKS-A secrets on workload cluster
Moving cluster management from bootstrap to workload cluster
Installing EKS-A custom components on workload cluster
Installing EKS-D components
Installing EKS-A custom components (CRD and controller)
Applying cluster spec to workload cluster
Installing GitOps Toolkit on workload cluster
Enumerating objects: 620, done.
Counting objects: 100% (564/564), done.
Compressing objects: 100% (535/535), done.
Total 620 (delta 180), reused 0 (delta 0), pack-reused 56
Adding cluster configuration files to Git
Finalized commit and committed to local repository {"hash": "467c20947e16322097c94a29e20c6d632b300ea2"}
Writing cluster config file
Deleting bootstrap cluster
🎉 Cluster created!
--------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------
Installing helm chart on cluster {"chart": "eks-anywhere-packages", "version": "0.4.3-eks-a-65"}
ubuntu@eksa-admin-machine-new-1:~$
kubectl get nodes
NAME STATUS ROLES AGE VERSION
eksanag12-5jn9h Ready control-plane 32m v1.29.1-eks-61c0bbb
eksanag12-7mgxw Ready control-plane 33m v1.29.1-eks-61c0bbb
eksanag12-lvf7q Ready control-plane 30m v1.29.1-eks-61c0bbb
eksanag12-md-0-hqnhc-hf4xc Ready <none> 29m v1.29.1-eks-61c0bbb
eksanag12-md-0-hqnhc-npkhc Ready <none> 29m v1.29.1-eks-61c0bbb
Install Cilium CLI on the EKS-Anywhere admin machine
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/master/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
Install Hubble CLI on the EKS-Anywhere admin machine
export HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
HUBBLE_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sha256sum --check hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
rm hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
With these CLI utilities in place, let’s observe the default installation of Cilium on EKS-Anywhere. It’s important for us to note that the default deployment of Cilium on EKS-Anywhere is a minimal install and does not provide the full range of Cilium OSS capabilities.
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: disabled
\__/ ClusterMesh: disabled
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Containers: cilium Running: 5
cilium-operator Running: 2
Cluster Pods: 16/16 managed by Cilium
Helm chart version:
Image versions cilium public.ecr.aws/isovalent/cilium:v1.13.9-eksa.1: 5
cilium-operator public.ecr.aws/isovalent/operator-generic:v1.13.9-eksa.1: 2Since
Since EKS Anywhere comes with a default minimal install, we can check the cilium config of the default installation
cilium config view
agent-not-ready-taint-key node.cilium.io/agent-not-ready
arping-refresh-period 30s
auto-direct-node-routes false
bpf-lb-external-clusterip false
bpf-lb-map-max 65536
bpf-lb-sock false
bpf-map-dynamic-size-ratio 0.0025
bpf-policy-map-max 16384
bpf-root /sys/fs/bpf
cgroup-root /run/cilium/cgroupv2
cilium-endpoint-gc-interval 5m0s
cluster-id 0
cluster-name default
cni-chaining-mode portmap
cni-uninstall true
custom-cni-conf false
debug false
debug-verbose
disable-cnp-status-updates true
disable-endpoint-crd false
egress-gateway-healthcheck-timeout 2s
egress-gateway-reconciliation-trigger-interval 1s
enable-auto-protect-node-port-range true
enable-bgp-control-plane false
enable-bpf-clock-probe false
enable-cluster-aware-addressing false
enable-endpoint-health-checking true
enable-health-check-nodeport true
enable-health-checking true
enable-host-legacy-routing true
enable-hubble true
enable-inter-cluster-snat false
enable-ipv4 true
enable-ipv4-masquerade true
enable-ipv6 false
enable-ipv6-big-tcp false
enable-ipv6-masquerade true
enable-k8s-terminating-endpoint true
enable-l2-neigh-discovery true
enable-l7-proxy true
enable-local-redirect-policy false
enable-metrics true
enable-policy default
enable-remote-node-identity true
enable-sctp false
enable-svc-source-range-check true
enable-vtep false
enable-well-known-identities false
enable-xt-socket-fallback true
hubble-disable-tls false
hubble-listen-address :4244
hubble-socket-path /var/run/cilium/hubble.sock
hubble-tls-cert-file /var/lib/cilium/tls/hubble/server.crt
hubble-tls-client-ca-files /var/lib/cilium/tls/hubble/client-ca.crt
hubble-tls-key-file /var/lib/cilium/tls/hubble/server.key
identity-allocation-mode crd
identity-gc-interval 15m0s
identity-heartbeat-timeout 30m0s
install-no-conntrack-iptables-rules false
ipam kubernetes
kube-proxy-replacement disabled
monitor-aggregation medium
monitor-aggregation-flags all
monitor-aggregation-interval 5s
node-port-bind-protection true
nodes-gc-interval 5m0s
operator-api-serve-addr 127.0.0.1:9234
operator-prometheus-serve-addr :9963
policy-cidr-match-mode
preallocate-bpf-maps false
procfs /host/proc
prometheus-serve-addr :9962
proxy-prometheus-port 9964
remove-cilium-node-taints true
set-cilium-is-up-condition true
sidecar-istio-proxy-image cilium/istio_proxy
skip-cnp-status-startup-clean false
synchronize-k8s-nodes true
tofqdns-dns-reject-response-code refused
tofqdns-enable-dns-compression true
tofqdns-endpoint-max-ip-per-hostname 50
tofqdns-idle-connection-grace-period 0s
tofqdns-max-deferred-connection-deletes 10000
tofqdns-min-ttl 3600
tofqdns-proxy-response-max-delay 100ms
tunnel geneve
unmanaged-pod-watcher-interval 15
vtep-cidr
vtep-endpoint
vtep-mac
vtep-mask
Also observe the CRDs created for the default Cilium installed. As one can see there is nothing much in there for BGP, etc.
kubectl get crd | grep cilium
ciliumendpoints.cilium.io 2024-06-12T18:47:58Z
ciliumidentities.cilium.io 2024-06-12T18:47:58Z
ciliumnodes.cilium.io 2024-06-12T18:47:58Z
Before we moving to full Cilium OSS., we will need to pause reconciliation of the cluster.
kubectl -n eksa-system annotate clusters.cluster.x-k8s.io $CLUSTER_NAME cluster.x-k8s.io/paused=true
cluster.cluster.x-k8s.io/eksanag23 annotated
To deploy the full Cilium OSS, we need to overwrite the default Cilium deployment. This can be done via the helm, however with a caveat that there are previous resources of Service accounts, Secrets, ConfigMaps, RoleBindings that will need to be patched.
Patch the resources from the default Cilium installation
kubectl -n kube-system annotate serviceaccount cilium meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate serviceaccount cilium meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label serviceaccount cilium app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate serviceaccount cilium-operator meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate serviceaccount cilium-operator meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label serviceaccount cilium-operator app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate secret hubble-ca-secret meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate secret hubble-ca-secret meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label secret hubble-ca-secret app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate secret hubble-server-certs meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate secret hubble-server-certs meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label secret hubble-server-certs app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate configmap cilium-config meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate configmap cilium-config meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label configmap cilium-config app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate secret cilium-ca meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate secret cilium-ca meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label secret cilium-ca app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate service hubble-peer meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate service hubble-peer meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label service hubble-peer app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate daemonset cilium meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate daemonset cilium meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label daemonset cilium app.kubernetes.io/managed-by=Helm
kubectl -n kube-system annotate deployment cilium-operator meta.helm.sh/release-name=cilium
kubectl -n kube-system annotate deployment cilium-operator meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label deployment cilium-operator app.kubernetes.io/managed-by=Helm
kubectl annotate clusterrole cilium meta.helm.sh/release-name=cilium
kubectl annotate clusterrole cilium meta.helm.sh/release-namespace=kube-system
kubectl label clusterrole cilium app.kubernetes.io/managed-by=Helm
kubectl annotate clusterrole cilium-operator meta.helm.sh/release-name=cilium
kubectl annotate clusterrole cilium-operator meta.helm.sh/release-namespace=kube-system
kubectl label clusterrole cilium-operator app.kubernetes.io/managed-by=Helm
kubectl annotate clusterrolebinding cilium meta.helm.sh/release-name=cilium
kubectl annotate clusterrolebinding cilium meta.helm.sh/release-namespace=kube-system
kubectl label clusterrolebinding cilium app.kubernetes.io/managed-by=Helm
kubectl annotate clusterrolebinding cilium-operator meta.helm.sh/release-name=cilium
kubectl annotate clusterrolebinding cilium-operator meta.helm.sh/release-namespace=kube-system
kubectl label clusterrolebinding cilium-operator app.kubernetes.io/managed-by=Helm
kubectl annotate role cilium-config-agent -n kube-system meta.helm.sh/release-name=cilium
kubectl annotate role cilium-config-agent -n kube-system meta.helm.sh/release-namespace=kube-system
kubectl label role cilium-config-agent -n kube-system app.kubernetes.io/managed-by=Helm
kubectl annotate rolebinding cilium-config-agent -n kube-system meta.helm.sh/release-name=cilium
kubectl annotate rolebinding cilium-config-agent -n kube-system meta.helm.sh/release-namespace=kube-system
kubectl label rolebinding cilium-config-agent -n kube-system app.kubernetes.io/managed-by=Helm
Now we are ready to install Cilium with Helm. One can enable multiple capabilities of Cilium, however for now we are enabling the BGP control
helm install cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set eni.enabled=false \
--set ipam.mode=kubernetes \
--set tunnel=vxlan \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set bgpControlPlane.enabled=true
As one can see and compare the default installation (seen earlier) is now replaced with Cilium OSS and per above helm settings, we have enabled the BGP control plane
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 5
cilium-operator Running: 2
hubble-ui Running: 1
hubble-relay Running: 1
Cluster Pods: 18/18 managed by Cilium
Helm chart version:
Image versions cilium quay.io/cilium/cilium:v1.15.5@sha256:4ce1666a73815101ec9a4d360af6c5b7f1193ab00d89b7124f8505dee147ca40: 5
cilium-operator quay.io/cilium/operator-generic:v1.15.5@sha256:f5d3d19754074ca052be6aac5d1ffb1de1eb5f2d947222b5f10f6d97ad4383e8: 2
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.0@sha256:1e7657d997c5a48253bb8dc91ecee75b63018d16ff5e5797e5af367336bc8803: 1
hubble-ui quay.io/cilium/hubble-ui:v0.13.0@sha256:7d663dc16538dd6e29061abd1047013a645e6e69c115e008bee9ea9fef9a6666: 1
hubble-relay quay.io/cilium/hubble-relay:v1.15.5@sha256:1d24b24e3477ccf9b5ad081827db635419c136a2bd84a3e60f37b26a38dd0781: 1
We need to now resume the cluster reconciliation
kubectl -n eksa-system annotate clusters.cluster.x-k8s.io $CLUSTER_NAME cluster.x-k8s.io/paused-
Verify BGP control plane
cilium config view | grep -i bgp
bgp-secrets-namespace kube-system
enable-bgp-control-plane true
Create upstream BGP connection and IPAM pool for LoadBalancer services. Note that IPAM pool should be different than the subnet used for the actual machines. There are plenty of configuration options to target specific services for the Load Balancer IPAM pool and other settings. For now, we will work with the below simplicity. In the configurations we are also providing references to perform peering with the BGP BIRD ubuntu host.
cat <<EOF | kubectl apply -f -
---
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: ippool
spec:
cidrs:
- cidr: "172.26.1.0/24" #This range is outside of the host network.
disabled: false
---
apiVersion: "cilium.io/v2alpha1"
kind: CiliumBGPPeeringPolicy
metadata:
name: 01-bgp-peering-policy
spec:
nodeSelector:
matchLabels:
group: md-0
virtualRouters:
- localASN: 64512
exportPodCIDR: true
neighbors:
- peerAddress: '10.204.111.49/32' #establish iBGP with upstream Bird router
peerASN: 64513
serviceSelector:
matchExpressions:
- {key: somekey, operator: NotIn, values: ['never-used-value']}
EOF
Let’s observe the BGP status on Cilium. The upstream peer (ubuntu BIRD machine) is not yet setup for BGP peering and hence the peer status is IDLE.
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Uptime Family Received Advertised
eksanag12-md-0-hqnhc-hf4xc 64512 64513 10.204.111.49 idle 0s ipv4/unicast 0 0
ipv6/unicast 0 0
eksanag12-md-0-hqnhc-npkhc 64512 64513 10.204.111.49 idle 0s ipv4/unicast 0 0
ipv6/unicast 0 0
Next, we will need the worker node IP addresses as they will act as the remote peers for the BGP daemon running via BIRD on the ubuntu machine
kubectl get nodes --selector='!node-role.kubernetes.io/control-plane' \
-o template \
--template='{{range.items}}{{range.status.addresses}}{{if eq .type "InternalIP"}}{{.address}}{{end}}{{end}} {{end}}'
Setting up Upstream BGP BIRD on Ubuntu 20.04
Install and Configure BIRD on a ubuntu 20.04 machine to emulate an upstream L3 router. Adjust the configuration for the remote peers, etc.
echo "net.ipv4.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.conf.default.forwarding=1" | sudo tee -a /etc/sysctl.conf
sudo add-apt-repository ppa:cz.nic-labs/bird
sudo apt-get install bird
sudo cat <<EOF > /etc/bird/bird.conf
# This is a minimal configuration file, which allows the bird daemon to start
# but will not cause anything else to happen.
#
# Please refer to the documentation in the bird-doc package or BIRD User's
# Guide on http://bird.network.cz/ for more information on configuring BIRD and
# adding routing protocols.
# Change this into your BIRD router ID. It's a world-wide unique identification
# of your router, usually one of router's IPv4 addresses.
router id 10.204.111.49;
# The Kernel protocol is not a real routing protocol. Instead of communicating
# with other routers in the network, it performs synchronization of BIRD's
# routing tables with the OS kernel.
protocol kernel {
# learn; # Learn all alien routes from the kernel
persist; # Don't remove routes on bird shutdown
scan time 20; # Scan kernel routing table every 20 seconds
# import none; # Default is import all
export all; # Default is export none
# kernel table 5; # Kernel table to synchronize with (default: main)
}
# Advertise a dummy local route via bgp
protocol static static_bgp {
route 192.168.2.0/24 via 10.204.111.49;
}
# Create eBGP session to EKS-A Worker node-1
protocol bgp link1 {
description "BIRD BGP CONFIG";
local as 64513;
neighbor xx.xx.xx.xx as 64512;
graceful restart;
import all;
export where proto = "static_bgp";
}
# Create eBGP session to EKS-A worker node-2
protocol bgp link2 {
description "BIRD BGP CONFIG";
local as 64513;
neighbor xx.xx.xx.xx as 64512;
graceful restart;
import all;
export where proto = "static_bgp";
}
# The Device protocol is not a real routing protocol. It doesn't generate any
# routes and it only serves as a module for getting information about network
# interfaces from the kernel.
protocol device {
scan time 60;
}
log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug };
log stderr all;
EOF
Restart and Enable BIRD service on the ubuntu host
sudo systemctl restart bird
sudo systemctl enable bird
Access bird CLI and observe BGP related information.
sudo birdc
BIRD 1.6.8 ready.
bird> show protocols
name proto table state since info
kernel1 Kernel master up 06:14:15
static_bgp Static master up 06:14:15
link1 BGP master up 06:14:16 Established
link2 BGP master up 06:14:22 Established
device1 Device master up 06:14:15
Observe BGP information from Cilium end
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Uptime Family Received Advertised
eksanag12-md-0-hqnhc-hf4xc 64512 64513 10.204.111.49 established 58s ipv4/unicast 1 1
ipv6/unicast 0 0
eksanag12-md-0-hqnhc-npkhc 64512 64513 10.204.111.49 established 1m4s ipv4/unicast 1 1
ipv6/unicast 0 0
At this point our BGP peering is in an established state, and we are good to go.
Deploy a sample NGINX pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
labels:
app: simple-pod
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: simple-pod # Make sure this matches the label of the Pod
ports:
- protocol: TCP
port: 8080
targetPort: 80
type: LoadBalancer
EOF
Observe the service for the sample pod and note that we have received 172.26.1.1 as an External IP from LB IPAM pool
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 42m
my-service LoadBalancer 10.96.84.205 172.26.1.1 8080:31733/TCP 5s
At the same token, let’s observe the route information received at BIRD BGP machine
Note that Cilium will not advertise the entire LB IPAM pool as a single prefix. Instead, it granularly advertises each of the services External IPs assigned as /32 prefixes.
bird> show route
192.168.3.0/24 via 10.204.111.9 on ens160 [link2 05:09:34] * (100) [AS64512i]
192.168.4.0/24 via 10.204.110.28 on ens160 [link1 05:09:23] * (100) [AS64512i]
172.26.1.1/32 via 10.204.110.28 on ens160 [link1 05:21:53] * (100) [AS64512i]
via 10.204.111.9 on ens160 [link2 05:21:55] (100) [AS64512i]
172.16.2.0/24 via 10.204.111.49 on ens160 [static_bgp 05:09:21] ! (200)
Let’s curl up to validate application reachability to the NGINX pod from the BIRD BGP machine.
curl 172.26.1.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
The case for kube-proxy replacement
Now let’s replace the kube-proxy with Cilium and revalidate this scenario. kube-proxy
is generally a default component of Kubernetes that handles routing traffic for services within the cluster. There are two backends available for Layer 3/4 load balancing in upstream kube-proxy
- iptables and IPVS. Cilium’s kube-proxy replacement offers advanced configuration modes to cater to your specific needs. Features like client source IP preservation ensure that your service connections remain intact, while Maglev Consistent Hashing enhances load balancing and resiliency. With support for Direct Server Return (DSR) and Hybrid DSR/SNAT modes, you can optimize traffic routing and improve performance.
To replace the kube-proxy with Cilium, we need to remove the daemonset and configmap related to kube-proxy
Let’s delete our application and the BGP peering to a clean slate.
kubectl delete pod simple-pod
kubectl delete service my-service
kubectl delete CiliumBGPPeeringPolicy 01-bgp-peering-policy
kubectl delete CiliumLoadBalancerIPPool ippool
Verify Cilium BGP. All gone for good!
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Uptime Family Received Advertised
Delete kube-proxy references such that we can then use the Cilium attributes.
kubectl -n kube-system delete ds kube-proxy
kubectl -n kube-system delete cm kube-proxy
Upgrade Cilium setting the kube proxy replacement flag. Note that we will need to specify the k8s services host and port to ensure Cilium can act as a replacement for kube-proxy
helm upgrade cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set eni.enabled=false \
--set ipam.mode=kubernetes \
--set tunnel=vxlan \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set bgpControlPlane.enabled=true \
--set kubeProxyReplacement=true \
--set k8sServiceHost=10.204.111.57 \
--set k8sServicePort=6443 \
--set externalIPs.enabled=true
Verify Cilium status
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Containers: hubble-ui Running: 1
cilium Running: 5
cilium-operator Running: 2
hubble-relay Running: 1
Cluster Pods: 18/18 managed by Cilium
Helm chart version:
Image versions cilium quay.io/cilium/cilium:v1.15.5@sha256:4ce1666a73815101ec9a4d360af6c5b7f1193ab00d89b7124f8505dee147ca40: 5
cilium-operator quay.io/cilium/operator-generic:v1.15.5@sha256:f5d3d19754074ca052be6aac5d1ffb1de1eb5f2d947222b5f10f6d97ad4383e8: 2
hubble-relay quay.io/cilium/hubble-relay:v1.15.5@sha256:1d24b24e3477ccf9b5ad081827db635419c136a2bd84a3e60f37b26a38dd0781: 1
hubble-ui quay.io/cilium/hubble-ui:v0.13.0@sha256:7d663dc16538dd6e29061abd1047013a645e6e69c115e008bee9ea9fef9a6666: 1
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.0@sha256:1e7657d997c5a48253bb8dc91ecee75b63018d16ff5e5797e5af367336bc8803: 1
Redeploy the BGP peering policy and LB IPAM pool
cat <<EOF | kubectl apply -f -
---
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: ippool
spec:
cidrs:
- cidr: "172.26.1.0/24" #This range is outside of the host network.
disabled: false
---
apiVersion: "cilium.io/v2alpha1"
kind: CiliumBGPPeeringPolicy
metadata:
name: 01-bgp-peering-policy
spec:
nodeSelector:
matchLabels:
group: md-0
virtualRouters:
- localASN: 64512
exportPodCIDR: true
neighbors:
- peerAddress: '10.204.111.49/32' #establish iBGP with upstream Bird router
peerASN: 64513
serviceSelector:
matchExpressions:
- {key: somekey, operator: NotIn, values: ['never-used-value']}
EOF
Verify BGP on Cilium end
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Uptime Family Received Advertised
eksanag12-md-0-hqnhc-hf4xc 64512 64513 10.204.111.49 established 27s ipv4/unicast 1 1
ipv6/unicast 0 0
eksanag12-md-0-hqnhc-npkhc 64512 64513 10.204.111.49 established 29s ipv4/unicast 1 1
ipv6/unicast 0 0
Redeploy the test pod and service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
labels:
app: simple-pod
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: simple-pod # Make sure this matches the label of the Pod
ports:
- protocol: TCP
port: 8080
targetPort: 80
type: LoadBalancer
EOF
Verify if the sample service received an External IP
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 70m
my-service LoadBalancer 10.107.135.37 172.26.1.1 8080:30159/TCP 6s
Verify incoming prefix on the BIRD BGP ubuntu machine
BIRD 1.6.8 ready.
bird> show protocol
name proto table state since info
kernel1 Kernel master up 06:14:15
static_bgp Static master up 06:14:15
link1 BGP master up 06:41:40 Established
link2 BGP master up 06:41:42 Established
device1 Device master up 06:14:15
bird>
bird> show route
192.168.3.0/24 via 10.204.110.31 on ens160 [link2 06:41:42] * (100) [AS64512i]
192.168.4.0/24 via 10.204.110.30 on ens160 [link1 06:41:40] * (100) [AS64512i]
172.26.1.1/32 via 10.204.110.30 on ens160 [link1 06:43:02] * (100) [AS64512i]
via 10.204.110.31 on ens160 [link2 06:43:02] (100) [AS64512i]
172.16.2.0/24 via 10.204.111.49 on ens160 [static_bgp 06:14:15] ! (200)
bird>
bird>
Verify application accessibility from the BIRD BGP Ubuntu machine
curl 172.26.1.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Let’s move to building an Ingress Controller
Paint the wall clean
kubectl delete pod simple-pod
kubectl delete service my-service
kubectl delete CiliumBGPPeeringPolicy 01-bgp-peering-policy
kubectl delete CiliumLoadBalancerIPPool ippool
pod "simple-pod" deleted
service "my-service" deleted
ciliumbgppeeringpolicy.cilium.io "01-bgp-peering-policy" deleted
ciliumloadbalancerippool.cilium.io "ippool" deleted
Redeploy Cilium with Ingress controller and with load balancer mode as dedicated. In addition to this, we will enable a recommended setting for ebpf based masquerading along with creating a dedicated daemonset for Envoy based L7 proxy.
Very Important: I had issues with deploying Envoy as a dedicated daemonset via the below configuration. Each time I deployed the configuration, everything came up smooth except cilium-envoy daemonset pods, which went into pending state forever. Upon trial and error, figured out that the cilium daemonset has to be deleted via the below command and then apply the helm configuration.
kubectl delete daemonset cilium --namespace kube-system
helm upgrade cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set eni.enabled=false \
--set ipam.mode=kubernetes \
--set bpf.masquerade=true \
--set tunnel=vxlan \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set bgpControlPlane.enabled=true \
--set kubeProxyReplacement=true \
--set k8sServiceHost=10.204.111.57 \
--set k8sServicePort=6443 \
--set externalIPs.enabled=true \
--set bgp.announce.loadbalancerIP=true \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=dedicated \
--set envoy.enabled=true \
--set loadBalancer.l7.backend=envoy \
--set debug.enabled=true \
--set debug.verbose=flow
Release "cilium" has been upgraded. Happy Helming!
NAME: cilium
LAST DEPLOYED: Sun Jun 9 03:54:07 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 7
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.
Your release version is 1.15.5.
For any further help, visit https://docs.cilium.io/en/v1.15/gettinghelp
Let’s view the cilium config
agent-not-ready-taint-key node.cilium.io/agent-not-ready
arping-refresh-period 30s
auto-direct-node-routes false
bgp-secrets-namespace kube-system
bpf-lb-acceleration disabled
bpf-lb-external-clusterip false
bpf-lb-map-max 65536
bpf-lb-sock false
bpf-map-dynamic-size-ratio 0.0025
bpf-policy-map-max 16384
bpf-root /sys/fs/bpf
cgroup-root /run/cilium/cgroupv2
cilium-endpoint-gc-interval 5m0s
cluster-id 0
cluster-name default
cni-exclusive true
cni-log-file /var/run/cilium/cilium-cni.log
custom-cni-conf false
debug true
debug-verbose flow
dnsproxy-enable-transparent-mode true
egress-gateway-reconciliation-trigger-interval 1s
enable-auto-protect-node-port-range true
enable-bgp-control-plane true
enable-bpf-clock-probe false
enable-bpf-masquerade true
enable-endpoint-health-checking true
enable-envoy-config true
enable-health-check-loadbalancer-ip false
enable-health-check-nodeport true
enable-health-checking true
enable-hubble true
enable-hubble-open-metrics false
enable-ingress-controller true
enable-ingress-proxy-protocol false
enable-ingress-secrets-sync true
enable-ipv4 true
enable-ipv4-big-tcp false
enable-ipv4-masquerade true
enable-ipv6 false
enable-ipv6-big-tcp false
enable-ipv6-masquerade true
enable-k8s-networkpolicy true
enable-k8s-terminating-endpoint true
enable-l2-neigh-discovery true
enable-l7-proxy true
enable-local-redirect-policy false
enable-masquerade-to-route-source false
enable-metrics true
enable-policy default
enable-remote-node-identity true
enable-sctp false
enable-svc-source-range-check true
enable-vtep false
enable-well-known-identities false
enable-xt-socket-fallback true
enforce-ingress-https true
external-envoy-proxy true
hubble-disable-tls false
hubble-export-file-max-backups 5
hubble-export-file-max-size-mb 10
hubble-listen-address :4244
hubble-metrics dns drop tcp flow icmp http
hubble-metrics-server :9965
hubble-socket-path /var/run/cilium/hubble.sock
hubble-tls-cert-file /var/lib/cilium/tls/hubble/server.crt
hubble-tls-client-ca-files /var/lib/cilium/tls/hubble/client-ca.crt
hubble-tls-key-file /var/lib/cilium/tls/hubble/server.key
identity-allocation-mode crd
identity-gc-interval 15m0s
identity-heartbeat-timeout 30m0s
ingress-default-lb-mode dedicated
ingress-lb-annotation-prefixes service.beta.kubernetes.io service.kubernetes.io cloud.google.com
ingress-secrets-namespace cilium-secrets
ingress-shared-lb-service-name cilium-ingress
install-no-conntrack-iptables-rules false
ipam kubernetes
ipam-cilium-node-update-rate 15s
k8s-client-burst 20
k8s-client-qps 10
kube-proxy-replacement true
kube-proxy-replacement-healthz-bind-address
loadbalancer-l7 envoy
loadbalancer-l7-algorithm round_robin
loadbalancer-l7-ports
max-connected-clusters 255
mesh-auth-enabled true
mesh-auth-gc-interval 5m0s
mesh-auth-queue-size 1024
mesh-auth-rotated-identities-queue-size 1024
monitor-aggregation medium
monitor-aggregation-flags all
monitor-aggregation-interval 5s
node-port-bind-protection true
nodes-gc-interval 5m0s
operator-api-serve-addr 127.0.0.1:9234
operator-prometheus-serve-addr :9963
policy-cidr-match-mode
preallocate-bpf-maps false
procfs /host/proc
proxy-connect-timeout 2
proxy-idle-timeout-seconds 60
proxy-max-connection-duration-seconds 0
proxy-max-requests-per-connection 0
proxy-xff-num-trusted-hops-egress 0
proxy-xff-num-trusted-hops-ingress 0
remove-cilium-node-taints true
routing-mode tunnel
service-no-backend-response reject
set-cilium-is-up-condition true
set-cilium-node-taints true
sidecar-istio-proxy-image cilium/istio_proxy
skip-cnp-status-startup-clean false
synchronize-k8s-nodes true
tofqdns-dns-reject-response-code refused
tofqdns-enable-dns-compression true
tofqdns-endpoint-max-ip-per-hostname 50
tofqdns-idle-connection-grace-period 0s
tofqdns-max-deferred-connection-deletes 10000
tofqdns-proxy-response-max-delay 100ms
tunnel-protocol vxlan
unmanaged-pod-watcher-interval 15
vtep-cidr
vtep-endpoint
vtep-mac
vtep-mask
write-cni-conf-when-ready /host/etc/cni/net.d/05-cilium.conflist
Restart the operator and Cilium daemonset
kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart ds/cilium
deployment.apps/cilium-operator restarted
daemonset.apps/cilium restarted
Verify Cilium status
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode)
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Containers: cilium Running: 5
cilium-operator Running: 2
hubble-relay Running: 1
hubble-ui Running: 1
Cluster Pods: 19/19 managed by Cilium
Helm chart version:
Image versions hubble-relay quay.io/cilium/hubble-relay:v1.15.5@sha256:1d24b24e3477ccf9b5ad081827db635419c136a2bd84a3e60f37b26a38dd0781: 1
hubble-ui quay.io/cilium/hubble-ui:v0.13.0@sha256:7d663dc16538dd6e29061abd1047013a645e6e69c115e008bee9ea9fef9a6666: 1
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.0@sha256:1e7657d997c5a48253bb8dc91ecee75b63018d16ff5e5797e5af367336bc8803: 1
cilium quay.io/cilium/cilium:v1.15.5@sha256:4ce1666a73815101ec9a4d360af6c5b7f1193ab00d89b7124f8505dee147ca40: 5
cilium-operator quay.io/cilium/operator-generic:v1.15.5@sha256:f5d3d19754074ca052be6aac5d1ffb1de1eb5f2d947222b5f10f6d97ad4383e8: 2
Also observe that we are running Envoy as a separate daemonset providing additional benefits.
kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
cilium-45hl6 1/1 Running 0 11m
cilium-cssvt 1/1 Running 0 11m
cilium-envoy-4zlx7 1/1 Running 0 11m
cilium-envoy-jcrpg 1/1 Running 0 11m
cilium-envoy-pcnt9 1/1 Running 0 11m
cilium-envoy-rqlsg 1/1 Running 0 11m
cilium-envoy-xtgpd 1/1 Running 0 11m
cilium-gffkl 1/1 Running 0 11m
cilium-jm449 1/1 Running 0 10m
cilium-operator-748f8f9fb5-j7hcb 1/1 Running 0 11m
cilium-operator-748f8f9fb5-pgzxt 1/1 Running 0 11m
cilium-t64zf 1/1 Running 0 11m
coredns-7b5898fc49-22zrr 1/1 Running 0 13h
coredns-7b5898fc49-w5zgm 1/1 Running 0 13h
etcd-eksanag15-5667n 1/1 Running 0 13h
etcd-eksanag15-5xfrb 1/1 Running 0 13h
etcd-eksanag15-6g97k 1/1 Running 0 13h
hubble-relay-66db7678d6-qbvg6 1/1 Running 0 11m
hubble-ui-6548d56557-6zh5x 2/2 Running 0 11m
kube-apiserver-eksanag15-5667n 1/1 Running 0 13h
kube-apiserver-eksanag15-5xfrb 1/1 Running 0 13h
kube-apiserver-eksanag15-6g97k 1/1 Running 0 13h
kube-controller-manager-eksanag15-5667n 1/1 Running 0 13h
kube-controller-manager-eksanag15-5xfrb 1/1 Running 0 13h
kube-controller-manager-eksanag15-6g97k 1/1 Running 0 13h
kube-scheduler-eksanag15-5667n 1/1 Running 0 13h
kube-scheduler-eksanag15-5xfrb 1/1 Running 0 13h
kube-scheduler-eksanag15-6g97k 1/1 Running 0 13h
kube-vip-eksanag15-5667n 1/1 Running 0 13h
kube-vip-eksanag15-5xfrb 1/1 Running 0 13h
kube-vip-eksanag15-6g97k 1/1 Running 1 (13h ago) 13h
vsphere-cloud-controller-manager-hq6tv 1/1 Running 0 13h
vsphere-cloud-controller-manager-rv9q4 1/1 Running 1 (13h ago) 13h
vsphere-cloud-controller-manager-s4k5l 1/1 Running 1 (13h ago) 13h
vsphere-cloud-controller-manager-wr5k5 1/1 Running 1 (13h ago) 13h
vsphere-cloud-controller-manager-zcbdv 1/1 Running 1 (13h ago) 13h
Create the BGP policy and LB IPAM pool
cat <<EOF | kubectl apply -f -
---
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: ippool
spec:
cidrs:
- cidr: "172.26.1.0/24" #This range is outside of the host network.
disabled: false
---
apiVersion: "cilium.io/v2alpha1"
kind: CiliumBGPPeeringPolicy
metadata:
name: 01-bgp-peering-policy
spec:
nodeSelector:
matchLabels:
group: md-0
virtualRouters:
- localASN: 64512
exportPodCIDR: true
neighbors:
- peerAddress: '10.204.111.49/32' #establish iBGP with upstream Bird router
peerASN: 64513
serviceSelector:
matchExpressions:
- {key: somekey, operator: NotIn, values: ['never-used-value']}
EOF
Validate BGP sessions
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Uptime Family Received Advertised
eksanag15-md-0-wsxnd-f9hdk 64512 64513 10.204.111.49 established 5m58s ipv4/unicast 1 3
ipv6/unicast 0 0
eksanag15-md-0-wsxnd-xj97h 64512 64513 10.204.111.49 established 5m48s ipv4/unicast 1 3
ipv6/unicast 0 0
Create the NGINIX sample pod/service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
labels:
app: simple-pod
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: simple-pod # Make sure this matches the label of the Pod
ports:
- protocol: TCP
port: 8080
targetPort: 80
EOF
Create a TLS secret for our sample NGINX pod/service. I already have the ca certificate and key. You may wish to obtain it per your convenience.
kubectl create secret tls nginx-test-cert \
--key tls.key \
--cert tls.crt
Create the Ingress resource with the ingressClassName as cilium and specifying the backend along with TLS host and secret name
cat <<EOF | kubectl create -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
namespace: default
spec:
ingressClassName: cilium
rules:
- host: nginx-test.poc.thecloudgarage.com
http:
paths:
- backend:
service:
name: my-service
port:
number: 8080
path: /
pathType: Prefix
tls:
- hosts:
- nginx-test.poc.thecloudgarage.com
secretName: nginx-test-cert
EOF
Validate if a dedicated load balancer service has been created for our Ingress resource. As one can observe, there are two services, one by the name cilium-ingress which is the default shared loadbalancer service for Ingresses, and the other one which is cilium-ingress-tls-ingress. This is a dedicated loadbalancer service created for our Ingress resource named as tls-ingress.
kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
capi-kubeadm-bootstrap-system capi-kubeadm-bootstrap-webhook-service ClusterIP 10.99.116.161 <none> 443/TCP 13h
capi-kubeadm-control-plane-system capi-kubeadm-control-plane-webhook-service ClusterIP 10.107.89.240 <none> 443/TCP 13h
capi-system capi-webhook-service ClusterIP 10.109.185.214 <none> 443/TCP 13h
capv-system capv-webhook-service ClusterIP 10.99.205.167 <none> 443/TCP 13h
cert-manager cert-manager ClusterIP 10.104.55.142 <none> 9402/TCP 13h
cert-manager cert-manager-webhook ClusterIP 10.105.176.229 <none> 443/TCP 13h
cilium-test cilium-ingress-other-node NodePort 10.96.76.195 <none> 80:31906/TCP,443:32032/TCP 3h55m
cilium-test cilium-ingress-same-node NodePort 10.96.4.233 <none> 80:30331/TCP,443:31265/TCP 3h55m
cilium-test echo-other-node NodePort 10.110.154.84 <none> 8080:31358/TCP 3h55m
cilium-test echo-same-node NodePort 10.105.86.35 <none> 8080:31228/TCP 3h55m
default cilium-ingress-tls-ingress LoadBalancer 10.109.70.84 172.26.1.2 80:31232/TCP,443:32300/TCP 5m36s
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13h
default my-service ClusterIP 10.110.54.84 <none> 8080/TCP 5m56s
eksa-system eksa-webhook-service ClusterIP 10.100.92.78 <none> 443/TCP 13h
etcdadm-bootstrap-provider-system etcdadm-bootstrap-provider-webhook-service ClusterIP 10.106.23.14 <none> 443/TCP 13h
etcdadm-controller-system etcdadm-controller-webhook-service ClusterIP 10.96.105.0 <none> 443/TCP 13h
flux-system notification-controller ClusterIP 10.110.115.175 <none> 80/TCP 13h
flux-system source-controller ClusterIP 10.105.176.255 <none> 80/TCP 13h
flux-system webhook-receiver ClusterIP 10.108.157.179 <none> 80/TCP 13h
kube-system cilium-agent ClusterIP None <none> 9964/TCP 13h
kube-system cilium-envoy ClusterIP None <none> 9964/TCP 8m34s
kube-system cilium-ingress LoadBalancer 10.96.249.182 172.26.1.1 80:31393/TCP,443:32206/TCP 8m34s
kube-system cloud-controller-manager NodePort 10.109.52.141 <none> 443:32076/TCP 13h
kube-system hubble-metrics ClusterIP None <none> 9965/TCP 8m34s
kube-system hubble-peer ClusterIP 10.109.64.78 <none> 443/TCP 7h48m
kube-system hubble-relay ClusterIP 10.103.38.11 <none> 80/TCP 8m34s
kube-system hubble-ui ClusterIP 10.109.54.148 <none> 80/TCP 8m34s
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 13h
Validate if our Ingress resource is built and that it received an IP Address associated with the Ingress controller
kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
tls-ingress cilium nginx-test.poc.thecloudgarage.com 172.26.1.2 80, 443 7m40s
Additionally a CRD resource for CiliumEnvoyConfig has been instantiated for our this Ingress
kubectl get ciliumenvoyconfig
NAME AGE
cilium-ingress-default-tls-ingress 10m
kubectl describe ciliumenvoyconfig cilium-ingress-default-tls-ingress
Name: cilium-ingress-default-tls-ingress
Namespace: default
Labels: cilium.io/use-original-source-address=false
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumEnvoyConfig
Metadata:
Creation Timestamp: 2024-06-10T16:46:29Z
Generation: 1
Owner References:
API Version: networking.k8s.io/v1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: tls-ingress
UID: 5c4de0e3-ceda-4884-bde1-16d393e63557
Resource Version: 386548
UID: 675ae7ad-7497-400a-82ac-ca0e37d3c384
Spec:
Backend Services:
Name: my-service
Namespace: default
Number:
8080
Resources:
@type: type.googleapis.com/envoy.config.listener.v3.Listener
Filter Chains:
Filter Chain Match:
Transport Protocol: raw_buffer
Filters:
Name: envoy.filters.network.http_connection_manager
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
Common Http Protocol Options:
Max Stream Duration: 0s
Http Filters:
Name: envoy.filters.http.grpc_web
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
Name: envoy.filters.http.grpc_stats
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig
Emit Filter State: true
Enable Upstream Stats: true
Name: envoy.filters.http.router
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Rds:
Route Config Name: listener-insecure
Stat Prefix: listener-insecure
Upgrade Configs:
Upgrade Type: websocket
Use Remote Address: true
Filter Chain Match:
Server Names:
nginx-test.poc.thecloudgarage.com
Transport Protocol: tls
Filters:
Name: envoy.filters.network.http_connection_manager
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
Common Http Protocol Options:
Max Stream Duration: 0s
Http Filters:
Name: envoy.filters.http.grpc_web
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
Name: envoy.filters.http.grpc_stats
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig
Emit Filter State: true
Enable Upstream Stats: true
Name: envoy.filters.http.router
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Rds:
Route Config Name: listener-secure
Stat Prefix: listener-secure
Upgrade Configs:
Upgrade Type: websocket
Use Remote Address: true
Transport Socket:
Name: envoy.transport_sockets.tls
Typed Config:
@type: type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
Common Tls Context:
Tls Certificate Sds Secret Configs:
Name: cilium-secrets/default-nginx-test-cert
Listener Filters:
Name: envoy.filters.listener.tls_inspector
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
Name: listener
Socket Options:
Description: Enable TCP keep-alive (default to enabled)
Int Value: 1
Level: 1
Name: 9
Description: TCP keep-alive idle time (in seconds) (defaults to 10s)
Int Value: 10
Level: 6
Name: 4
Description: TCP keep-alive probe intervals (in seconds) (defaults to 5s)
Int Value: 5
Level: 6
Name: 5
Description: TCP keep-alive probe max failures.
Int Value: 10
Level: 6
Name: 6
@type: type.googleapis.com/envoy.config.route.v3.RouteConfiguration
Name: listener-insecure
Virtual Hosts:
Domains:
nginx-test.poc.thecloudgarage.com
nginx-test.poc.thecloudgarage.com:*
Name: nginx-test.poc.thecloudgarage.com
Routes:
Match:
Prefix: /
Redirect:
Https Redirect: true
@type: type.googleapis.com/envoy.config.route.v3.RouteConfiguration
Name: listener-secure
Virtual Hosts:
Domains:
nginx-test.poc.thecloudgarage.com
nginx-test.poc.thecloudgarage.com:*
Name: nginx-test.poc.thecloudgarage.com
Routes:
Match:
Prefix: /
Route:
Cluster: default:my-service:8080
@type: type.googleapis.com/envoy.config.cluster.v3.Cluster
Connect Timeout: 5s
Eds Cluster Config:
Service Name: default/my-service:8080
Name: default:my-service:8080
Outlier Detection:
Split External Local Origin Errors: true
Type: EDS
Typed Extension Protocol Options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
@type: type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
Common Http Protocol Options:
Idle Timeout: 60s
Use Downstream Protocol Config:
http2ProtocolOptions:
Services:
Listener:
Name: cilium-ingress-tls-ingress
Namespace: default
Events: <none>
And then we head over to the BGP BIRD Ubuntu machine to do a quick validation
curl -v https://nginx-test.poc.thecloudgarage.com
* Trying 172.26.1.2:443...
* Connected to nginx-test.poc.thecloudgarage.com (172.26.1.2) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=thecloudgarage.com
* start date: May 13 05:30:07 2024 GMT
* expire date: Aug 11 05:30:06 2024 GMT
* subjectAltName: host "nginx-test.poc.thecloudgarage.com" matched cert's "*.poc.thecloudgarage.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET / HTTP/1.1
> Host: nginx-test.poc.thecloudgarage.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< server: envoy
< date: Mon, 10 Jun 2024 16:58:25 GMT
< content-type: text/html
< content-length: 615
< last-modified: Tue, 28 May 2024 13:22:30 GMT
< etag: "6655da96-267"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 1
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host nginx-test.poc.thecloudgarage.com left intact
Let’s hurl in the Gateway API support.
While we are traversing this path, might as well implement the Gateway API support for Cilium. We will need to first install the pre-requisite CRDs
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.0.0/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/tlsroutes.gateway.networking.k8s.io created
We will continue with the Ingress configurations so as to have a co-existence of Ingress and Gateway API implementation. In that case, we are not recreating the LB IPAM Pool or the BGP peering policy as those already exist from the previous scenario.
Redeploy Cilium with Gateway API support
helm upgrade cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set eni.enabled=false \
--set ipam.mode=kubernetes \
--set bpf.masquerade=true \
--set tunnel=vxlan \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set bgpControlPlane.enabled=true \
--set kubeProxyReplacement=true \
--set k8sServiceHost=10.204.111.57 \
--set k8sServicePort=6443 \
--set externalIPs.enabled=true \
--set bgp.announce.loadbalancerIP=true \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=dedicated \
--set gatewayAPI.enabled=true \
--set envoy.enabled=true \
--set loadBalancer.l7.backend=envoy \
--set debug.enabled=true \
--set debug.verbose=flow
Restart Cilium operator and daemonset
kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart ds/cilium
Verify Cilium
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium-envoy Desired: 5, Ready: 5/5, Available: 5/5
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Containers: cilium-operator Running: 2
cilium Running: 5
hubble-ui Running: 1
hubble-relay Running: 1
cilium-envoy Running: 5
Cluster Pods: 25/25 managed by Cilium
Helm chart version:
Image versions cilium quay.io/cilium/cilium:v1.15.5@sha256:4ce1666a73815101ec9a4d360af6c5b7f1193ab00d89b7124f8505dee147ca40: 5
hubble-ui quay.io/cilium/hubble-ui:v0.13.0@sha256:7d663dc16538dd6e29061abd1047013a645e6e69c115e008bee9ea9fef9a6666: 1
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.0@sha256:1e7657d997c5a48253bb8dc91ecee75b63018d16ff5e5797e5af367336bc8803: 1
hubble-relay quay.io/cilium/hubble-relay:v1.15.5@sha256:1d24b24e3477ccf9b5ad081827db635419c136a2bd84a3e60f37b26a38dd0781: 1
cilium-envoy quay.io/cilium/cilium-envoy:v1.28.3-31ec52ec5f2e4d28a8e19a0bfb872fa48cf7a515@sha256:bc8dcc3bc008e3a5aab98edb73a0985e6ef9469bda49d5bb3004c001c995c380: 5
cilium-operator quay.io/cilium/operator-generic:v1.15.5@sha256:f5d3d19754074ca052be6aac5d1ffb1de1eb5f2d947222b5f10f6d97ad4383e8: 2
Verify BGP peering
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Uptime Family Received Advertised
eksanag15-md-0-wsxnd-f9hdk 64512 64513 10.204.111.49 established 40s ipv4/unicast 1 4
ipv6/unicast 0 0
eksanag15-md-0-wsxnd-xj97h 64512 64513 10.204.111.49 established 42s ipv4/unicast 1 4
ipv6/unicast 0 0
Build a new pod/service for NGINX sample app that will be used in combination with Gateway API
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: simple-pod-1
labels:
app: simple-pod-1
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-service-1
spec:
selector:
app: simple-pod-1 # Make sure this matches the label of the Pod
ports:
- protocol: TCP
port: 8080
targetPort: 80
EOF
Create the Gateway resource and HTTP route. Note we are reusing the same TLS cert as it includes a wildcard certificate for my domain.
cat <<EOF | kubectl create -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https-1
protocol: HTTPS
port: 443
hostname: "nginx-test-gateway.poc.thecloudgarage.com"
tls:
certificateRefs:
- kind: Secret
name: nginx-test-cert
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-app-route-1
spec:
parentRefs:
- name: tls-gateway
hostnames:
- "nginx-test-gateway.poc.thecloudgarage.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: my-service-1
port: 8080
EOF
Verify if a dedicated LoadBalancer service is created for the gateway resource
kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
capi-kubeadm-bootstrap-system capi-kubeadm-bootstrap-webhook-service ClusterIP 10.99.116.161 <none> 443/TCP 14h
capi-kubeadm-control-plane-system capi-kubeadm-control-plane-webhook-service ClusterIP 10.107.89.240 <none> 443/TCP 14h
capi-system capi-webhook-service ClusterIP 10.109.185.214 <none> 443/TCP 14h
capv-system capv-webhook-service ClusterIP 10.99.205.167 <none> 443/TCP 14h
cert-manager cert-manager ClusterIP 10.104.55.142 <none> 9402/TCP 14h
cert-manager cert-manager-webhook ClusterIP 10.105.176.229 <none> 443/TCP 14h
cilium-test cilium-ingress-other-node NodePort 10.96.76.195 <none> 80:31906/TCP,443:32032/TCP 4h37m
cilium-test cilium-ingress-same-node NodePort 10.96.4.233 <none> 80:30331/TCP,443:31265/TCP 4h37m
cilium-test echo-other-node NodePort 10.110.154.84 <none> 8080:31358/TCP 4h37m
cilium-test echo-same-node NodePort 10.105.86.35 <none> 8080:31228/TCP 4h37m
default cilium-gateway-tls-gateway LoadBalancer 10.102.173.94 172.26.1.3 443:31507/TCP 85s
default cilium-ingress-tls-ingress LoadBalancer 10.109.70.84 172.26.1.2 80:31232/TCP,443:32300/TCP 48m
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14h
default my-service ClusterIP 10.110.54.84 <none> 8080/TCP 48m
default my-service-1 ClusterIP 10.101.176.214 <none> 8080/TCP 6m41s
eksa-system eksa-webhook-service ClusterIP 10.100.92.78 <none> 443/TCP 14h
etcdadm-bootstrap-provider-system etcdadm-bootstrap-provider-webhook-service ClusterIP 10.106.23.14 <none> 443/TCP 14h
etcdadm-controller-system etcdadm-controller-webhook-service ClusterIP 10.96.105.0 <none> 443/TCP 14h
flux-system notification-controller ClusterIP 10.110.115.175 <none> 80/TCP 14h
flux-system source-controller ClusterIP 10.105.176.255 <none> 80/TCP 14h
flux-system webhook-receiver ClusterIP 10.108.157.179 <none> 80/TCP 14h
kube-system cilium-agent ClusterIP None <none> 9964/TCP 14h
kube-system cilium-envoy ClusterIP None <none> 9964/TCP 51m
kube-system cilium-ingress LoadBalancer 10.96.249.182 172.26.1.1 80:31393/TCP,443:32206/TCP 51m
kube-system cloud-controller-manager NodePort 10.109.52.141 <none> 443:32076/TCP 14h
kube-system hubble-metrics ClusterIP None <none> 9965/TCP 51m
kube-system hubble-peer ClusterIP 10.109.64.78 <none> 443/TCP 8h
kube-system hubble-relay ClusterIP 10.103.38.11 <none> 80/TCP 51m
kube-system hubble-ui ClusterIP 10.109.54.148 <none> 80/TCP 51m
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 14h
Let’s observe if the gateway resource is created properly and that it bears an IP address
kubectl get gateway
NAME CLASS ADDRESS PROGRAMMED AGE
tls-gateway cilium 172.26.1.3 True 13m
Observe that a new ciliumenvoyconfig resource is created for the gateway resource
kubectl get ciliumenvoyconfig
NAME AGE
cilium-gateway-tls-gateway 13m
cilium-ingress-default-tls-ingress 60m
kubectl describe ciliumenvoyconfig cilium-gateway-tls-gateway
Name: cilium-gateway-tls-gateway
Namespace: default
Labels: cilium.io/use-original-source-address=false
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumEnvoyConfig
Metadata:
Creation Timestamp: 2024-06-10T17:33:09Z
Generation: 2
Owner References:
API Version: gateway.networking.k8s.io/v1
Controller: true
Kind: Gateway
Name: tls-gateway
UID: e817fce7-99a5-41d1-8813-8fd42b322ba8
Resource Version: 408332
UID: 63129a5a-516f-4d63-ae5b-f38c3afabf41
Spec:
Backend Services:
Name: my-service-1
Namespace: default
Number:
8080
Resources:
@type: type.googleapis.com/envoy.config.listener.v3.Listener
Filter Chains:
Filter Chain Match:
Transport Protocol: raw_buffer
Filters:
Name: envoy.filters.network.http_connection_manager
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
Common Http Protocol Options:
Max Stream Duration: 0s
Http Filters:
Name: envoy.filters.http.grpc_web
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
Name: envoy.filters.http.grpc_stats
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig
Emit Filter State: true
Enable Upstream Stats: true
Name: envoy.filters.http.router
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Rds:
Route Config Name: listener-insecure
Stat Prefix: listener-insecure
Upgrade Configs:
Upgrade Type: websocket
Use Remote Address: true
Filter Chain Match:
Server Names:
nginx-test-gateway.poc.thecloudgarage.com
Transport Protocol: tls
Filters:
Name: envoy.filters.network.http_connection_manager
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
Common Http Protocol Options:
Max Stream Duration: 0s
Http Filters:
Name: envoy.filters.http.grpc_web
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
Name: envoy.filters.http.grpc_stats
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig
Emit Filter State: true
Enable Upstream Stats: true
Name: envoy.filters.http.router
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Rds:
Route Config Name: listener-secure
Stat Prefix: listener-secure
Upgrade Configs:
Upgrade Type: websocket
Use Remote Address: true
Transport Socket:
Name: envoy.transport_sockets.tls
Typed Config:
@type: type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
Common Tls Context:
Tls Certificate Sds Secret Configs:
Name: cilium-secrets/default-nginx-test-cert
Listener Filters:
Name: envoy.filters.listener.tls_inspector
Typed Config:
@type: type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
Name: listener
Socket Options:
Description: Enable TCP keep-alive (default to enabled)
Int Value: 1
Level: 1
Name: 9
Description: TCP keep-alive idle time (in seconds) (defaults to 10s)
Int Value: 10
Level: 6
Name: 4
Description: TCP keep-alive probe intervals (in seconds) (defaults to 5s)
Int Value: 5
Level: 6
Name: 5
Description: TCP keep-alive probe max failures.
Int Value: 10
Level: 6
Name: 6
@type: type.googleapis.com/envoy.config.route.v3.RouteConfiguration
Name: listener-secure
Virtual Hosts:
Domains:
nginx-test-gateway.poc.thecloudgarage.com
nginx-test-gateway.poc.thecloudgarage.com:*
Name: nginx-test-gateway.poc.thecloudgarage.com
Routes:
Match:
Prefix: /
Route:
Cluster: default:my-service-1:8080
@type: type.googleapis.com/envoy.config.cluster.v3.Cluster
Connect Timeout: 5s
Eds Cluster Config:
Service Name: default/my-service-1:8080
Name: default:my-service-1:8080
Outlier Detection:
Split External Local Origin Errors: true
Type: EDS
Typed Extension Protocol Options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
@type: type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
Common Http Protocol Options:
Idle Timeout: 60s
Use Downstream Protocol Config:
http2ProtocolOptions:
Services:
Listener:
Name: cilium-gateway-tls-gateway
Namespace: default
Events: <none>
Let’s verify access to our new NGINX sample app via the gateway resource by curling it from the BGP BIRD Ubuntu machine. Ensure that the hosts entry or DNS record is created accordingly.
curl -v https://nginx-test-gateway.poc.thecloudgarage.com
* Trying 172.26.1.3:443...
* Connected to nginx-test-gateway.poc.thecloudgarage.com (172.26.1.3) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=thecloudgarage.com
* start date: May 13 05:30:07 2024 GMT
* expire date: Aug 11 05:30:06 2024 GMT
* subjectAltName: host "nginx-test-gateway.poc.thecloudgarage.com" matched cert's "*.poc.thecloudgarage.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> GET / HTTP/1.1
> Host: nginx-test-gateway.poc.thecloudgarage.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< server: envoy
< date: Mon, 10 Jun 2024 17:50:02 GMT
< content-type: text/html
< content-length: 615
< last-modified: Tue, 28 May 2024 13:22:30 GMT
< etag: "6655da96-267"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 1
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host nginx-test-gateway.poc.thecloudgarage.com left intact
Why stop, let’s look at Hubble
In this stride, we will enable Hubble UI via Ingress with TLS.
Let’s begin by creating the TLS secret for Hubble in kube-system namespace
kubectl create secret tls hubble-cert -n kube-system --key tls.key --cert tls.crt
With the secret in place, let’s upgrade our Helm by including the Ingress configuration for Hubble-UI. I have chosen hubble1.poc.thecloudgarage.com as the preferred hostname for my UI.
helm upgrade cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set eni.enabled=false \
--set ipam.mode=kubernetes \
--set bpf.masquerade=true \
--set tunnel=vxlan \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.ui.ingress.enabled=true \
--set hubble.ui.ingress.className=cilium \
--set hubble.ui.ingress.hosts[0]="hubble1.poc.thecloudgarage.com" \
--set hubble.ui.ingress.tls[0].hosts[0]="hubble1.poc.thecloudgarage.com" \
--set hubble.ui.ingress.tls[0].secretName="hubble-cert" \
--set bgpControlPlane.enabled=true \
--set kubeProxyReplacement=true \
--set k8sServiceHost=10.204.111.57 \
--set k8sServicePort=6443 \
--set externalIPs.enabled=true \
--set bgp.announce.loadbalancerIP=true \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=dedicated \
--set gatewayAPI.enabled=true \
--set envoy.enabled=true \
--set loadBalancer.l7.backend=envoy \
--set debug.enabled=true \
--set debug.verbose=flow
Restart Cilium operator and daemonset
kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart ds/cilium
Let’s observe the Cilium configuration
agent-not-ready-taint-key node.cilium.io/agent-not-ready
arping-refresh-period 30s
auto-direct-node-routes false
bgp-secrets-namespace kube-system
bpf-lb-acceleration disabled
bpf-lb-external-clusterip false
bpf-lb-map-max 65536
bpf-lb-sock false
bpf-map-dynamic-size-ratio 0.0025
bpf-policy-map-max 16384
bpf-root /sys/fs/bpf
cgroup-root /run/cilium/cgroupv2
cilium-endpoint-gc-interval 5m0s
cluster-id 0
cluster-name default
cni-exclusive true
cni-log-file /var/run/cilium/cilium-cni.log
custom-cni-conf false
debug true
debug-verbose flow
dnsproxy-enable-transparent-mode true
egress-gateway-reconciliation-trigger-interval 1s
enable-auto-protect-node-port-range true
enable-bgp-control-plane true
enable-bpf-clock-probe false
enable-bpf-masquerade true
enable-endpoint-health-checking true
enable-envoy-config true
enable-gateway-api true
enable-gateway-api-secrets-sync true
enable-health-check-loadbalancer-ip false
enable-health-check-nodeport true
enable-health-checking true
enable-hubble true
enable-hubble-open-metrics false
enable-ingress-controller true
enable-ingress-proxy-protocol false
enable-ingress-secrets-sync true
enable-ipv4 true
enable-ipv4-big-tcp false
enable-ipv4-masquerade true
enable-ipv6 false
enable-ipv6-big-tcp false
enable-ipv6-masquerade true
enable-k8s-networkpolicy true
enable-k8s-terminating-endpoint true
enable-l2-neigh-discovery true
enable-l7-proxy true
enable-local-redirect-policy false
enable-masquerade-to-route-source false
enable-metrics true
enable-policy default
enable-remote-node-identity true
enable-sctp false
enable-svc-source-range-check true
enable-vtep false
enable-well-known-identities false
enable-xt-socket-fallback true
enforce-ingress-https true
external-envoy-proxy true
gateway-api-secrets-namespace cilium-secrets
hubble-disable-tls false
hubble-export-file-max-backups 5
hubble-export-file-max-size-mb 10
hubble-listen-address :4244
hubble-metrics dns drop tcp flow icmp http
hubble-metrics-server :9965
hubble-socket-path /var/run/cilium/hubble.sock
hubble-tls-cert-file /var/lib/cilium/tls/hubble/server.crt
hubble-tls-client-ca-files /var/lib/cilium/tls/hubble/client-ca.crt
hubble-tls-key-file /var/lib/cilium/tls/hubble/server.key
identity-allocation-mode crd
identity-gc-interval 15m0s
identity-heartbeat-timeout 30m0s
ingress-default-lb-mode dedicated
ingress-lb-annotation-prefixes service.beta.kubernetes.io service.kubernetes.io cloud.google.com
ingress-secrets-namespace cilium-secrets
ingress-shared-lb-service-name cilium-ingress
install-no-conntrack-iptables-rules false
ipam kubernetes
ipam-cilium-node-update-rate 15s
k8s-client-burst 20
k8s-client-qps 10
kube-proxy-replacement true
kube-proxy-replacement-healthz-bind-address
loadbalancer-l7 envoy
loadbalancer-l7-algorithm round_robin
loadbalancer-l7-ports
max-connected-clusters 255
mesh-auth-enabled true
mesh-auth-gc-interval 5m0s
mesh-auth-queue-size 1024
mesh-auth-rotated-identities-queue-size 1024
monitor-aggregation medium
monitor-aggregation-flags all
monitor-aggregation-interval 5s
node-port-bind-protection true
nodes-gc-interval 5m0s
operator-api-serve-addr 127.0.0.1:9234
operator-prometheus-serve-addr :9963
policy-cidr-match-mode
preallocate-bpf-maps false
procfs /host/proc
proxy-connect-timeout 2
proxy-idle-timeout-seconds 60
proxy-max-connection-duration-seconds 0
proxy-max-requests-per-connection 0
proxy-xff-num-trusted-hops-egress 0
proxy-xff-num-trusted-hops-ingress 0
remove-cilium-node-taints true
routing-mode tunnel
service-no-backend-response reject
set-cilium-is-up-condition true
set-cilium-node-taints true
sidecar-istio-proxy-image cilium/istio_proxy
skip-cnp-status-startup-clean false
synchronize-k8s-nodes true
tofqdns-dns-reject-response-code refused
tofqdns-enable-dns-compression true
tofqdns-endpoint-max-ip-per-hostname 50
tofqdns-idle-connection-grace-period 0s
tofqdns-max-deferred-connection-deletes 10000
tofqdns-proxy-response-max-delay 100ms
tunnel-protocol vxlan
unmanaged-pod-watcher-interval 15
vtep-cidr
vtep-endpoint
vtep-mac
vtep-mask
write-cni-conf-when-ready /host/etc/cni/net.d/05-cilium.conflist
Once the Cilium and BGP statuses are confirmed, observe that a new LoadBalancer service for cilium-ingress-hubble-ui has been created with an ExternalIP
kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
capi-kubeadm-bootstrap-system capi-kubeadm-bootstrap-webhook-service ClusterIP 10.99.116.161 <none> 443/TCP 25h
capi-kubeadm-control-plane-system capi-kubeadm-control-plane-webhook-service ClusterIP 10.107.89.240 <none> 443/TCP 25h
capi-system capi-webhook-service ClusterIP 10.109.185.214 <none> 443/TCP 25h
capv-system capv-webhook-service ClusterIP 10.99.205.167 <none> 443/TCP 25h
cert-manager cert-manager ClusterIP 10.104.55.142 <none> 9402/TCP 25h
cert-manager cert-manager-webhook ClusterIP 10.105.176.229 <none> 443/TCP 25h
cilium-test cilium-ingress-other-node NodePort 10.96.76.195 <none> 80:31906/TCP,443:32032/TCP 15h
cilium-test cilium-ingress-same-node NodePort 10.96.4.233 <none> 80:30331/TCP,443:31265/TCP 15h
cilium-test echo-other-node NodePort 10.110.154.84 <none> 8080:31358/TCP 15h
cilium-test echo-same-node NodePort 10.105.86.35 <none> 8080:31228/TCP 15h
default cilium-gateway-tls-gateway LoadBalancer 10.102.173.94 172.26.1.3 443:31507/TCP 10h
default cilium-ingress-tls-ingress LoadBalancer 10.109.70.84 172.26.1.2 80:31232/TCP,443:32300/TCP 11h
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 25h
default my-service ClusterIP 10.110.54.84 <none> 8080/TCP 11h
default my-service-1 ClusterIP 10.101.176.214 <none> 8080/TCP 10h
eksa-system eksa-webhook-service ClusterIP 10.100.92.78 <none> 443/TCP 25h
etcdadm-bootstrap-provider-system etcdadm-bootstrap-provider-webhook-service ClusterIP 10.106.23.14 <none> 443/TCP 25h
etcdadm-controller-system etcdadm-controller-webhook-service ClusterIP 10.96.105.0 <none> 443/TCP 25h
flux-system notification-controller ClusterIP 10.110.115.175 <none> 80/TCP 25h
flux-system source-controller ClusterIP 10.105.176.255 <none> 80/TCP 25h
flux-system webhook-receiver ClusterIP 10.108.157.179 <none> 80/TCP 25h
kube-system cilium-agent ClusterIP None <none> 9964/TCP 25h
kube-system cilium-envoy ClusterIP None <none> 9964/TCP 11h
kube-system cilium-ingress LoadBalancer 10.96.249.182 172.26.1.1 80:31393/TCP,443:32206/TCP 11h
kube-system cilium-ingress-hubble-ui LoadBalancer 10.107.249.246 172.26.1.4 80:31236/TCP,443:30647/TCP 59m
kube-system cloud-controller-manager NodePort 10.109.52.141 <none> 443:32076/TCP 25h
kube-system hubble-metrics ClusterIP None <none> 9965/TCP 11h
kube-system hubble-peer ClusterIP 10.109.64.78 <none> 443/TCP 19h
kube-system hubble-relay ClusterIP 10.103.38.11 <none> 80/TCP 11h
kube-system hubble-ui ClusterIP 10.109.54.148 <none> 80/TCP 11h
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 25h
We can also see an ingress resource with an IP address for the Hubble UI in the kube-system namespace
kubectl get ingress -n kube-system
NAME CLASS HOSTS ADDRESS PORTS AGE
hubble-ui cilium hubble1.poc.thecloudgarage.com 172.26.1.4 80, 443 61m
Let’s head over to the BGP BIRD Ubuntu host if this prefix has been received via the BGP route
ip route
default via 10.204.110.1 dev ens160 proto static
10.204.110.0/23 dev ens160 proto kernel scope link src 10.204.111.49
172.16.2.0/24 via 10.204.111.49 dev ens160 proto bird
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.18.0.0/16 dev br-65b73db83171 proto kernel scope link src 172.18.0.1 linkdown
172.19.0.0/16 dev br-367eea0c41bc proto kernel scope link src 172.19.0.1 linkdown
172.20.0.0/16 dev br-b1dd61ad5edf proto kernel scope link src 172.20.0.1 linkdown
172.21.0.0/16 dev br-a94ebdb591eb proto kernel scope link src 172.21.0.1 linkdown
172.22.0.0/16 dev br-e91c56ab7045 proto kernel scope link src 172.22.0.1 linkdown
172.23.0.0/16 dev br-f8107365302c proto kernel scope link src 172.23.0.1 linkdown
172.24.0.0/16 dev br-4cc2a2dd5387 proto kernel scope link src 172.24.0.1 linkdown
172.26.1.1 via 10.204.110.9 dev ens160 proto bird
172.26.1.2 via 10.204.110.9 dev ens160 proto bird
172.26.1.3 via 10.204.110.9 dev ens160 proto bird
172.26.1.4 via 10.204.110.9 dev ens160 proto bird
192.168.2.0/24 via 10.204.111.49 dev ens160 proto bird
192.168.3.0/24 via 10.204.110.32 dev ens160 proto bird
192.168.4.0/24 via 10.204.110.9 dev ens160 proto bird
Now that we have everything in place for the Hubble UI to be accessed via Ingress, add in the DNS record or a manual hosts entry in the BGP BIRD Ubuntu machine.
nslookup hubble1.poc.thecloudgarage.com
Server: 127.0.0.53
Address: 127.0.0.53#53
Name: hubble1.poc.thecloudgarage.com
Address: 172.26.1.4
For the sake of remote access, I will install xrdp on my Ubuntu host to gain desktop and browser access. In that way, we will be able to browse the Hubble UI from the BGP BIRD Ubuntu host itself.
sudo apt update
sudo apt install xrdp -y
With XRDP installed, we can RDP to the BGP BIRD Ubuntu host and browse the Hubble UI HTTPS
Let’s select the Kube-system namespace in the UI and there you have it., live flows from the hubble-ui
We can inspect any other pod/app in any of the given namespaces.
Oh yes., I forgot to share some more insight on BGP peering policy setting called exportPodCIDR
Generally speaking, one always needs a service to expose and communicate with pods. This is especially true when services are accessed from external entities via a service type LoadBalancer.
However, cilium BGP peering policy allows for a special setting of exposing podCIDRs directly to the peers
Let’s look at what this means in reality! If we pay attention to the BGP peering policy defined above while creating the Ingress controller, the setting exportPodCIDR was set to true
My EKS Anywhere cluster’s POD CIDR is set at the default value of 192.16.0.0/16 with two worker nodes. In that sense, the entire range is split into individual CIDR /24 blocks and assigned to each node allowing each to host a max of 256 pods. This setting can be changed but that’s a diffferent conversation.
Now that we know that the podCIDR is split into /24 blocks and assigned to each node, any new pod depending on node placement will get it’s IP from the respective /24 block.
When the setting exportPodCIDR is set as true in BGP peering policy, these individual node POD CIDR blocks are also advertised as /24 prefixes to the remote peer. These prefixes are on top of any prefixes which represent the LB IPAM pool, which are generally awarded to services with type LoadBalancer. We are talking about the podCIDR ranges being advertised across the peer. As a result, the remote peer can view the podCIDRs as routable via BGP and thus can directly access the pods directly on the container port (not the service port). This container port is also defined as the targetPort in the service YAML.
First let’s observe what Pod CIDRs are assigned to my 2 worker nodes. As you can see the two worker nodes have been assigned the Pod CIDRs as 192.168.3.0/24 and 192.168.4.0/24
kubectl describe ciliumnodes eksanag15-md-0-wsxnd-f9hdk
Name: eksanag15-md-0-wsxnd-f9hdk
Namespace:
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/instance-type=vsphere-vm.cpu-4.mem-8gb.os-ubuntu
beta.kubernetes.io/os=linux
group=md-0
kubernetes.io/arch=amd64
kubernetes.io/hostname=eksanag15-md-0-wsxnd-f9hdk
kubernetes.io/os=linux
node.cluster.x-k8s.io/esxi-host=mc-mg-node01.edub.csc
node.kubernetes.io/instance-type=vsphere-vm.cpu-4.mem-8gb.os-ubuntu
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNode
Metadata:
Creation Timestamp: 2024-06-10T03:17:08Z
Generation: 37
Owner References:
API Version: v1
Kind: Node
Name: eksanag15-md-0-wsxnd-f9hdk
UID: 802d1ca5-83d4-490b-a6d7-4f1dc2660ea1
Resource Version: 1456630
UID: bc843866-82c4-4430-9fc6-a7a7f891dbc2
Spec:
Addresses:
Ip: 10.204.110.9
Type: InternalIP
Ip: 10.204.110.9
Type: ExternalIP
Ip: 192.168.4.222
Type: CiliumInternalIP
Alibaba - Cloud:
Azure:
Bootid: 4e905fdb-8512-4db2-9c69-f31ee9bfa7de
Encryption:
Eni:
Health:
ipv4: 192.168.4.233
Ingress:
ipv4: 192.168.4.87
Ipam:
Pod CID Rs:
192.168.4.0/24
kubectl describe ciliumnodes eksanag15-md-0-wsxnd-xj97h
Name: eksanag15-md-0-wsxnd-xj97h
Namespace:
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/instance-type=vsphere-vm.cpu-4.mem-8gb.os-ubuntu
beta.kubernetes.io/os=linux
group=md-0
kubernetes.io/arch=amd64
kubernetes.io/hostname=eksanag15-md-0-wsxnd-xj97h
kubernetes.io/os=linux
node.cluster.x-k8s.io/esxi-host=mc-mg-node05.edub.csc
node.kubernetes.io/instance-type=vsphere-vm.cpu-4.mem-8gb.os-ubuntu
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNode
Metadata:
Creation Timestamp: 2024-06-10T03:17:08Z
Generation: 37
Owner References:
API Version: v1
Kind: Node
Name: eksanag15-md-0-wsxnd-xj97h
UID: c4c111c8-5fd7-4162-89c5-8dadda9b8aa0
Resource Version: 1456951
UID: 96a357c3-61e7-48d1-9556-884af5d6499b
Spec:
Addresses:
Ip: 10.204.110.32
Type: InternalIP
Ip: 10.204.110.32
Type: ExternalIP
Ip: 192.168.3.215
Type: CiliumInternalIP
Alibaba - Cloud:
Azure:
Bootid: 45ad0a2b-17fe-483b-a31e-86ed8c0d7f45
Encryption:
Eni:
Health:
ipv4: 192.168.3.104
Ingress:
ipv4: 192.168.3.102
Ipam:
Pod CID Rs:
192.168.3.0/24
Let’s view the Cilium prefix advertisements. As one can observe 192.168.3.0/24 and 192.168.4.0/24 are being advertised as the exportPodCIDR setting is set to true.
cilium bgp routes
(Defaulting to `available ipv4 unicast` routes, please see help for more options)
Node VRouter Prefix NextHop Age Attrs
eksanag15-md-0-wsxnd-f9hdk 64512 172.16.2.0/24 10.204.111.49 30m13s [{Origin: i} {AsPath: 64513} {Nexthop: 10.204.111.49}]
64512 172.26.1.1/32 0.0.0.0 30m21s [{Origin: i} {Nexthop: 0.0.0.0}]
64512 172.26.1.3/32 0.0.0.0 30m21s [{Origin: i} {Nexthop: 0.0.0.0}]
64512 172.26.1.4/32 0.0.0.0 30m21s [{Origin: i} {Nexthop: 0.0.0.0}]
64512 192.168.4.0/24 0.0.0.0 30m21s [{Origin: i} {Nexthop: 0.0.0.0}]
eksanag15-md-0-wsxnd-xj97h 64512 172.16.2.0/24 10.204.111.49 29m55s [{Origin: i} {AsPath: 64513} {Nexthop: 10.204.111.49}]
64512 172.26.1.1/32 0.0.0.0 30m0s [{Origin: i} {Nexthop: 0.0.0.0}]
64512 172.26.1.3/32 0.0.0.0 30m0s [{Origin: i} {Nexthop: 0.0.0.0}]
64512 172.26.1.4/32 0.0.0.0 30m0s [{Origin: i} {Nexthop: 0.0.0.0}]
64512 192.168.3.0/24 0.0.0.0 30m0s [{Origin: i} {Nexthop: 0.0.0.0}]
Let’s validate this on the BGP BIRD Ubuntu machine. Notice the last two entries, those are the POD CIDRs and their respective next-hops as the respective worker nodes as BGP peers
ip route
default via 10.204.110.1 dev ens160 proto static
10.204.110.0/23 dev ens160 proto kernel scope link src 10.204.111.49
172.16.2.0/24 via 10.204.111.49 dev ens160 proto bird
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.18.0.0/16 dev br-65b73db83171 proto kernel scope link src 172.18.0.1 linkdown
172.19.0.0/16 dev br-367eea0c41bc proto kernel scope link src 172.19.0.1 linkdown
172.20.0.0/16 dev br-b1dd61ad5edf proto kernel scope link src 172.20.0.1 linkdown
172.21.0.0/16 dev br-a94ebdb591eb proto kernel scope link src 172.21.0.1 linkdown
172.22.0.0/16 dev br-e91c56ab7045 proto kernel scope link src 172.22.0.1 linkdown
172.23.0.0/16 dev br-f8107365302c proto kernel scope link src 172.23.0.1 linkdown
172.24.0.0/16 dev br-4cc2a2dd5387 proto kernel scope link src 172.24.0.1 linkdown
172.26.1.1 via 10.204.110.9 dev ens160 proto bird
172.26.1.3 via 10.204.110.9 dev ens160 proto bird
172.26.1.4 via 10.204.110.9 dev ens160 proto bird
192.168.2.0/24 via 10.204.111.49 dev ens160 proto bird
192.168.3.0/24 via 10.204.110.32 dev ens160 proto bird
192.168.4.0/24 via 10.204.110.9 dev ens160 proto bird
Our NGINX sample apps are reachable via Ingress (simple-pod) and Gatway API (simple-pod-1) and in addition, they are also seen via their actual POD IP address.
Shall we validate direct access to pods?
kubectl get pods
NAME READY STATUS RESTARTS AGE
simple-pod 1/1 Running 0 17m
simple-pod-1 1/1 Running 0 18m
Both these pods have an IP from the POD CIDR and the Port as 80
kubectl describe pod simple-pod | grep IP:
IP: 192.168.4.223
kubectl describe pod simple-pod-1 | grep IP:
IP: 192.168.4.160
We have already verified above that the sample applications can via their exposed ExternalIPs. Now, we will try directly accessing the Pod from the BGP BIRD Ubuntu on its POD IP and container port. Notice we are not using the service port but the container port (80) itself
curl 192.168.4.223
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
curl 192.168.4.160
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Hopefully, that explains a very important attribute that can give rise to tremendous advantage from a container networking perspective
Finally, let’s dabble with Pod CIDR IPAM
In the default install of Cilium and/or the above scenarios, the PodCIDR IPAM is governed by Kubernetes, which can be limiting in advanced solutions.
Let’s switch to cluster-pool IPAM and observe.
Note: Per official recommendation, this should not be done on an existing cluster.
So, I have created a new cluster with skipUpgrade option for the default Cilium CNI. In addition, I have performed the steps (patching, pausing reconciliation, deleting kube-proxy and cilium daemonset) and then finally configure Cilium OSS on the cluster via Helm.
Observe the settings for IPAM which are now configured with cluster-pool and the CIDR List and per node mask size. If we do not specify the CIDR list, it will default to 10.0.0.0/8
helm install cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set eni.enabled=false \
--set ipam.mode=cluster-pool \
--set ipam.operator.clusterPoolIPv4PodCIDRList=192.168.0.0/16 \
--set ipam.operator.clusterPoolIPv4MaskSize=24 \
--set bpf.masquerade=true \
--set tunnel=vxlan \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}" \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set bgpControlPlane.enabled=true \
--set kubeProxyReplacement=true \
--set nodePort.enabled=true \
--set k8sServiceHost=10.204.111.57 \
--set k8sServicePort=6443 \
--set externalIPs.enabled=true \
--set bgp.announce.loadbalancerIP=true \
--set ingressController.enabled=true \
--set ingressController.loadbalancerMode=dedicated \
--set gatewayAPI.enabled=true \
--set envoy.enabled=true \
--set loadBalancer.l7.backend=envoy \
--set debug.enabled=true \
--set debug.verbose=flow
Once deployed, we can verify the cilium status
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
DaemonSet cilium-envoy Desired: 5, Ready: 5/5, Available: 5/5
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 5
cilium-operator Running: 2
hubble-ui Running: 1
hubble-relay Running: 1
cilium-envoy Running: 5
Cluster Pods: 18/18 managed by Cilium
Helm chart version:
Image versions cilium quay.io/cilium/cilium:v1.15.5@sha256:4ce1666a73815101ec9a4d360af6c5b7f1193ab00d89b7124f8505dee147ca40: 5
cilium-operator quay.io/cilium/operator-generic:v1.15.5@sha256:f5d3d19754074ca052be6aac5d1ffb1de1eb5f2d947222b5f10f6d97ad4383e8: 2
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.0@sha256:1e7657d997c5a48253bb8dc91ecee75b63018d16ff5e5797e5af367336bc8803: 1
hubble-ui quay.io/cilium/hubble-ui:v0.13.0@sha256:7d663dc16538dd6e29061abd1047013a645e6e69c115e008bee9ea9fef9a6666: 1
hubble-relay quay.io/cilium/hubble-relay:v1.15.5@sha256:1d24b24e3477ccf9b5ad081827db635419c136a2bd84a3e60f37b26a38dd0781: 1
cilium-envoy quay.io/cilium/cilium-envoy:v1.28.3-31ec52ec5f2e4d28a8e19a0bfb872fa48cf7a515@sha256:bc8dcc3bc008e3a5aab98edb73a0985e6ef9469bda49d5bb3004c001c995c380: 5
Resume the cluster reconciliation
kubectl -n eksa-system annotate clusters.cluster.x-k8s.io $CLUSTER_NAME cluster.x-k8s.io/paused-
Let’s verify cilium config for specific settings
cilium config view | egrep 'ipam|ipv4'
cluster-pool-ipv4-cidr 192.168.0.0/16
cluster-pool-ipv4-mask-size 24
enable-ipv4 true
enable-ipv4-big-tcp false
enable-ipv4-masquerade true
ipam cluster-pool
Observe the Cilium node configs
kubectl get ciliumnodes.cilium.io
NAME CILIUMINTERNALIP INTERNALIP AGE
eksanag23-2mzgt 192.168.0.224 10.204.111.25 47m
eksanag23-425c5 192.168.2.104 10.204.110.60 47m
eksanag23-md-0-wnmmn-2cm5z 192.168.4.106 10.204.110.65 46m
eksanag23-md-0-wnmmn-f9zgs 192.168.3.17 10.204.110.61 46m
eksanag23-phvvj 192.168.1.162 10.204.110.59 47m
kubectl describe ciliumnode eksanag23-md-0-wnmmn-2cm5z
Name: eksanag23-md-0-wnmmn-2cm5z
Namespace:
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/instance-type=vsphere-vm.cpu-4.mem-8gb.os-ubuntu
beta.kubernetes.io/os=linux
group=md-0
kubernetes.io/arch=amd64
kubernetes.io/hostname=eksanag23-md-0-wnmmn-2cm5z
kubernetes.io/os=linux
node.cluster.x-k8s.io/esxi-host=mc-mg-node04.edub.csc
node.kubernetes.io/instance-type=vsphere-vm.cpu-4.mem-8gb.os-ubuntu
Annotations: <none>
API Version: cilium.io/v2
Kind: CiliumNode
Metadata:
Creation Timestamp: 2024-06-13T03:35:26Z
Generation: 3
Owner References:
API Version: v1
Kind: Node
Name: eksanag23-md-0-wnmmn-2cm5z
UID: d5c708e5-81b6-403c-be3f-b8602b6ef28d
Resource Version: 9469
UID: 0aee23ef-37be-49e9-a3ab-82d1f9135274
Spec:
Addresses:
Ip: 10.204.110.65
Type: InternalIP
Ip: 10.204.110.65
Type: ExternalIP
Ip: 192.168.4.106
Type: CiliumInternalIP
Alibaba - Cloud:
Azure:
Bootid: a42638ef-068f-4f16-9374-cd3677e03f42
Encryption:
Eni:
Health:
ipv4: 192.168.4.228
Ingress:
ipv4: 192.168.4.177
Ipam:
Pod CIDRs:
192.168.4.0/24
Pools:
Status:
Alibaba - Cloud:
Azure:
Eni:
Ipam:
Operator - Status:
Events: <none>
A Final twist, will we have problems upgrading cluster with a Custom CNI… let’s verify!!!
I have bumped up the machine specs in the cluster template for the control plane and worker nodes from 4 vCPUs to 8 vCPUs
kubectl apply -f $HOME/$CLUSTER_NAME/$CLUSTER_NAME-eks-a-cluster.yaml --kubeconfig $HOME/$CLUSTER_NAME/$CLUSTER_NAME-eks-a-cluster.kubeconfig
The new nodes have started popping in
kubectl get nodes
NAME STATUS ROLES AGE VERSION
eksanag23-2mzgt Ready control-plane 84m v1.29.1-eks-61c0bbb
eksanag23-425c5 Ready control-plane 82m v1.29.1-eks-61c0bbb
eksanag23-md-0-jjzfs-h4zcv NotReady <none> 8s v1.29.1-eks-61c0bbb
eksanag23-md-0-wnmmn-2cm5z Ready <none> 81m v1.29.1-eks-61c0bbb
eksanag23-md-0-wnmmn-f9zgs Ready <none> 81m v1.29.1-eks-61c0bbb
eksanag23-phvvj Ready control-plane 83m v1.29.1-eks-61c0bbb
eksanag23-zhn5n NotReady control-plane 4s v1.29.1-eks-61c0bbb
Old nodes are being deleted
kubectl get nodes
NAME STATUS ROLES AGE VERSION
eksanag23-4lqgb Ready control-plane 3m54s v1.29.1-eks-61c0bbb
eksanag23-7gw6c Ready control-plane 6m38s v1.29.1-eks-61c0bbb
eksanag23-7mn5s Ready control-plane 10m v1.29.1-eks-61c0bbb
eksanag23-md-0-7w5j5-rmjqq Ready <none> 61s v1.29.1-eks-61c0bbb
eksanag23-md-0-jjzfs-h4zcv Ready,SchedulingDisabled <none> 12m v1.29.1-eks-61c0bbb
eksanag23-md-0-jjzfs-j8pnh Ready <none> 10m v1.29.1-eks-61c0bbb
All done!
kubectl get nodes
NAME STATUS ROLES AGE VERSION
eksanag23-4lqgb Ready control-plane 6m52s v1.29.1-eks-61c0bbb
eksanag23-7gw6c Ready control-plane 9m36s v1.29.1-eks-61c0bbb
eksanag23-7mn5s Ready control-plane 13m v1.29.1-eks-61c0bbb
eksanag23-md-0-7w5j5-gwcrl Ready <none> 113s v1.29.1-eks-61c0bbb
eksanag23-md-0-7w5j5-rmjqq Ready <none> 3m59s v1.29.1-eks-61c0bbb
Let’s verify cilium status
cilium status
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
DaemonSet cilium-envoy Desired: 5, Ready: 5/5, Available: 5/5
DaemonSet cilium Desired: 5, Ready: 5/5, Available: 5/5
Deployment hubble-ui Desired: 1, Ready: 1/1, Available: 1/1
Deployment hubble-relay Desired: 1, Ready: 1/1, Available: 1/1
Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2
Containers: cilium Running: 5
cilium-envoy Running: 5
hubble-ui Running: 1
cilium-operator Running: 2
hubble-relay Running: 1
Cluster Pods: 18/18 managed by Cilium
Helm chart version:
Image versions cilium quay.io/cilium/cilium:v1.15.5@sha256:4ce1666a73815101ec9a4d360af6c5b7f1193ab00d89b7124f8505dee147ca40: 5
cilium-envoy quay.io/cilium/cilium-envoy:v1.28.3-31ec52ec5f2e4d28a8e19a0bfb872fa48cf7a515@sha256:bc8dcc3bc008e3a5aab98edb73a0985e6ef9469bda49d5bb3004c001c995c380: 5
hubble-ui quay.io/cilium/hubble-ui:v0.13.0@sha256:7d663dc16538dd6e29061abd1047013a645e6e69c115e008bee9ea9fef9a6666: 1
hubble-ui quay.io/cilium/hubble-ui-backend:v0.13.0@sha256:1e7657d997c5a48253bb8dc91ecee75b63018d16ff5e5797e5af367336bc8803: 1
cilium-operator quay.io/cilium/operator-generic:v1.15.5@sha256:f5d3d19754074ca052be6aac5d1ffb1de1eb5f2d947222b5f10f6d97ad4383e8: 2
hubble-relay quay.io/cilium/hubble-relay:v1.15.5@sha256:1d24b24e3477ccf9b5ad081827db635419c136a2bd84a3e60f37b26a38dd0781: 1
All thumbs up and green! Successfuly upgraded the cluster with full version of Cilium OSS
So, there we have it., a full Cilium experience without Kube-proxy and BGP based load-balancing along with Ingress, Gateway API with TLS and Hubble UI!!!
Hope this was a worthwhile read into replacing the default Cilium CNI with Cilium OSS on EKS-Anywhere.
cheers,
Ambar@thecloudgarage
#iwork4dell