# Episode 189 – Exploring Cluster API with Cilium **Watch the episode:** {%youtube nipb5VMQbQs %} ## News * [Cilium newsletter!](https://cilium.io/newsletter) * [LWKD](https://lwkd.info/2025/20250617) ## Upcoming Events * [Cilium Release 1.17 Webinar](https://isovalent.com/events/2025-06-26-cilium-117-release/?utm_source=hs_email&utm_medium=email)! ## Notes capi.sh ``` bash #create a kind cluster with no cni installed. kind create cluster --config ./kind/cluster.yaml # install cilium and hubble cilium install --set l2announcements.enabled=true \ --set socketLB.hostNamespaceOnly=true cilium hubble enable --ui #virtink needs certmanager so we will init clusterctl first clusterctl init --infrastructure docker,kubevirt KV_VER=$(curl "https://api.github.com/repos/kubevirt/kubevirt/releases/latest" | jq -r ".tag_name") # deploy required CRDs kubectl apply -f "https://github.com/kubevirt/kubevirt/releases/download/${KV_VER}/kubevirt-operator.yaml" # deploy the KubeVirt custom resource kubectl apply -f "https://github.com/kubevirt/kubevirt/releases/download/${KV_VER}/kubevirt-cr.yaml" kubectl wait -n kubevirt kv kubevirt --for=condition=Available --timeout=10m cat <<EOF | kubectl apply -f - --- apiVersion: "cilium.io/v2alpha1" kind: CiliumL2AnnouncementPolicy metadata: name: l2-default spec: serviceSelector: matchLabels: {} # match all interfaces: - ^eth[0-9]+ externalIPs: false loadBalancerIPs: true --- apiVersion: "cilium.io/v2alpha1" kind: CiliumLoadBalancerIPPool metadata: name: "l2-default" spec: blocks: - cidr: "172.18.200.0/24" EOF ``` cluster.yaml ``` yaml kind: Cluster name: capi apiVersion: kind.x-k8s.io/v1alpha4 networking: disableDefaultCNI: true podSubnet: "10.1.0.0/16" kubeProxyMode: "none" nodes: - role: control-plane extraMounts: - hostPath: ./kind/_default containerPath: /etc/containerd/certs.d/_default - hostPath: /var/run/docker.sock containerPath: /var/run/docker.sock - role: worker extraMounts: - hostPath: ./kind/_default containerPath: /etc/containerd/certs.d/_default - hostPath: /var/run/docker.sock containerPath: /var/run/docker.sock - role: worker extraMounts: - hostPath: ./kind/_default containerPath: /etc/containerd/certs.d/_default - hostPath: /var/run/docker.sock containerPath: /var/run/docker.sock - role: worker extraMounts: - hostPath: ./kind/_default containerPath: /etc/containerd/certs.d/_default - hostPath: /var/run/docker.sock containerPath: /var/run/docker.sock containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" ``` _default/hosts.toml ``` toml server = "https://registry-1.docker.io" [host."http://docker-cache:5000"] capabilities = ["pull", "resolve"] skip_verify = true server = "https://quay.io" [host."http://quay-cache:5000"] capabilities = ["pull", "resolve"] skip_verify = true server = "https://ghcr.io" [host."http://ghcr-cache:5000"] capabilities = ["pull", "resolve"] skip_verify = true server = "https://gcr.io" [host."http://gcr-cache:5000"] capabilities = ["pull", "resolve"] skip_verify = true server = "https://registry.k8s.io" [host."http://k8s-cache:5000"] capabilities = ["pull", "resolve"] skip_verify = true ``` Makefile ``` makefile DOCKER_CACHE := docker-cache QUAYIO_CACHE := quay-cache GHCRIO_CACHE := ghcr-cache GCRIO_CACHE := gcr-cache K8S_CACHE := k8s-cache KIND_NETWORK := kind cache.start: docker.cache quayio.cache ghcr.cache gcr.cache k8s.cache docker.cache: @echo -n 'docker.io cache running: ' @docker inspect -f '{{.State.Running}}' $(DOCKER_CACHE) 2>/dev/null || \ docker run -d --restart=always --network $(KIND_NETWORK) \ -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \ --name $(DOCKER_CACHE) registry:2 quayio.cache: @echo -n 'quay.io cache running: ' @docker inspect -f '{{.State.Running}}' $(QUAYIO_CACHE) 2>/dev/null || \ docker run -d --restart=always --network $(KIND_NETWORK) \ -e REGISTRY_PROXY_REMOTEURL=https://quay.io \ --name $(QUAYIO_CACHE) registry:2 ghcr.cache: @echo -n 'ghcr.io cache running: ' @docker inspect -f '{{.State.Running}}' $(GHCRIO_CACHE) 2>/dev/null || \ docker run -d --restart=always --network $(KIND_NETWORK) \ -e REGISTRY_PROXY_REMOTEURL=https://ghcr.io \ --name $(GHCRIO_CACHE) registry:2 gcr.cache: @echo -n 'gcr.io cache running: ' @docker inspect -f '{{.State.Running}}' $(GCRIO_CACHE) 2>/dev/null || \ docker run -d --restart=always --network $(KIND_NETWORK) \ -e REGISTRY_PROXY_REMOTEURL=https://gcr.io \ --name $(GCRIO_CACHE) registry:2 k8s.cache: @echo -n 'registry.k8s.io cache running: ' @docker inspect -f '{{.State.Running}}' $(K8S_CACHE) 2>/dev/null || \ docker run -d --restart=always --network $(KIND_NETWORK) \ -e REGISTRY_PROXY_REMOTEURL=https://registry.k8s.io \ --name $(K8S_CACHE) registry:2 cache.stop: docker-cache.stop quayio-cache.stop gcr-cache.stop ghcr-cache.stop k8s-cache.stop quayio-cache.stop: @docker inspect -f '{{.State.Running}}' $(QUAYIO_CACHE) 2>/dev/null && \ docker rm -f $(QUAYIO_CACHE) 2>/dev/null || echo 'quay-cache is not running' docker-cache.stop: @docker inspect -f '{{.State.Running}}' $(DOCKER_CACHE) 2>/dev/null && \ docker rm -f $(DOCKER_CACHE) 2>/dev/null || echo 'docker-cache is not running' ghcr-cache.stop: @docker inspect -f '{{.State.Running}}' $(GHCRIO_CACHE) 2>/dev/null && \ docker rm -f $(GHCRIO_CACHE) 2>/dev/null || echo 'ghcr-cache is not running' gcr-cache.stop: @docker inspect -f '{{.State.Running}}' $(GCRIO_CACHE) 2>/dev/null && \ docker rm -f $(GCRIO_CACHE) 2>/dev/null || echo 'gcr-cache is not running' k8s-cache.stop: @docker inspect -f '{{.State.Running}}' $(K8S_CACHE) 2>/dev/null && \ docker rm -f $(K8S_CACHE) 2>/dev/null || echo 'k8s-cache is not running' ``` ``` yaml apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: name: kv1 namespace: kv1 spec: clusterNetwork: pods: cidrBlocks: - 10.243.0.0/16 services: cidrBlocks: - 10.95.0.0/16 controlPlaneRef: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlane name: kv1-control-plane namespace: kv1 infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtCluster name: kv1 namespace: kv1 --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtCluster metadata: name: kv1 namespace: kv1 spec: controlPlaneServiceTemplate: spec: type: LoadBalancer --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate metadata: name: kv1-control-plane namespace: kv1 spec: template: spec: virtualMachineBootstrapCheck: checkStrategy: ssh virtualMachineTemplate: metadata: namespace: kv1 spec: runStrategy: Always template: spec: domain: cpu: cores: 2 devices: disks: - disk: bus: virtio name: containervolume networkInterfaceMultiqueue: true memory: guest: 4Gi evictionStrategy: External volumes: - containerDisk: image: mauilion/capk-2404:1.32.4 name: containervolume --- apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlane metadata: name: kv1-control-plane namespace: kv1 spec: kubeadmConfigSpec: clusterConfiguration: networking: podSubnet: 10.243.0.0/16 serviceSubnet: 10.95.0.0/16 initConfiguration: nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock skipPhases: - addon/kube-proxy joinConfiguration: nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock machineTemplate: infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate name: kv1-control-plane namespace: kv1 replicas: 1 version: 1.32.4 --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate metadata: name: kv1-md-0 namespace: kv1 spec: template: spec: virtualMachineBootstrapCheck: checkStrategy: ssh virtualMachineTemplate: metadata: namespace: kv1 spec: runStrategy: Always template: spec: domain: cpu: cores: 2 devices: disks: - disk: bus: virtio name: containervolume networkInterfaceMultiqueue: true memory: guest: 4Gi evictionStrategy: External volumes: - containerDisk: image: mauilion/capk-2404:1.32.4 name: containervolume --- apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate metadata: name: kv1-md-0 namespace: kv1 spec: template: spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: {} --- apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: name: kv1-md-0 namespace: kv1 spec: clusterName: kv1 replicas: 3 selector: matchLabels: null template: spec: bootstrap: configRef: apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate name: kv1-md-0 namespace: kv1 clusterName: kv1 infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate name: kv1-md-0 namespace: kv1 version: 1.32.4 ``` ``` yaml apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: name: kv2 namespace: kv2 spec: clusterNetwork: pods: cidrBlocks: - 10.242.0.0/16 services: cidrBlocks: - 10.95.0.0/16 controlPlaneRef: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlane name: kv2-control-plane namespace: kv2 infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtCluster name: kv2 namespace: kv2 --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtCluster metadata: name: kv2 namespace: kv2 spec: controlPlaneServiceTemplate: spec: type: LoadBalancer --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate metadata: name: kv2-control-plane namespace: kv2 spec: template: spec: virtualMachineBootstrapCheck: checkStrategy: ssh virtualMachineTemplate: metadata: namespace: kv2 spec: runStrategy: Always template: spec: domain: cpu: cores: 2 devices: disks: - disk: bus: virtio name: containervolume networkInterfaceMultiqueue: true memory: guest: 4Gi evictionStrategy: External volumes: - containerDisk: image: mauilion/capk-2404:1.32.4 name: containervolume --- apiVersion: controlplane.cluster.x-k8s.io/v1beta1 kind: KubeadmControlPlane metadata: name: kv2-control-plane namespace: kv2 spec: kubeadmConfigSpec: clusterConfiguration: networking: podSubnet: 10.242.0.0/16 serviceSubnet: 10.95.0.0/16 initConfiguration: nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock skipPhases: - addon/kube-proxy joinConfiguration: nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock machineTemplate: infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate name: kv2-control-plane namespace: kv2 replicas: 1 version: 1.32.4 --- apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate metadata: name: kv2-md-0 namespace: kv2 spec: template: spec: virtualMachineBootstrapCheck: checkStrategy: ssh virtualMachineTemplate: metadata: namespace: kv2 spec: runStrategy: Always template: spec: domain: cpu: cores: 2 devices: disks: - disk: bus: virtio name: containervolume networkInterfaceMultiqueue: true memory: guest: 4Gi evictionStrategy: External volumes: - containerDisk: image: mauilion/capk-2404:1.32.4 name: containervolume --- apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate metadata: name: kv2-md-0 namespace: kv2 spec: template: spec: joinConfiguration: nodeRegistration: kubeletExtraArgs: {} --- apiVersion: cluster.x-k8s.io/v1beta1 kind: MachineDeployment metadata: name: kv2-md-0 namespace: kv2 spec: clusterName: kv2 replicas: 3 selector: matchLabels: null template: spec: bootstrap: configRef: apiVersion: bootstrap.cluster.x-k8s.io/v1beta1 kind: KubeadmConfigTemplate name: kv2-md-0 namespace: kv2 clusterName: kv2 infrastructureRef: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 kind: KubevirtMachineTemplate name: kv2-md-0 namespace: kv2 version: 1.32.4 ```