<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sri Sai Venkata Kasi Subbarayudu Kompella</title>
    <description>The latest articles on DEV Community by Sri Sai Venkata Kasi Subbarayudu Kompella (@sri_saivenkatakasisubb).</description>
    <link>https://dev.to/sri_saivenkatakasisubb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2573717%2Fe7323dc4-128a-4a5a-bcd1-cdb5c59fb941.png</url>
      <title>DEV Community: Sri Sai Venkata Kasi Subbarayudu Kompella</title>
      <link>https://dev.to/sri_saivenkatakasisubb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sri_saivenkatakasisubb"/>
    <language>en</language>
    <item>
      <title>Provisioning a Kubernetes Cluster on OpenStack Using Cluster API (CAPI)</title>
      <dc:creator>Sri Sai Venkata Kasi Subbarayudu Kompella</dc:creator>
      <pubDate>Sun, 29 Mar 2026 13:24:25 +0000</pubDate>
      <link>https://dev.to/sri_saivenkatakasisubb/provisioning-a-kubernetes-cluster-on-openstack-using-cluster-api-capi-2fn8</link>
      <guid>https://dev.to/sri_saivenkatakasisubb/provisioning-a-kubernetes-cluster-on-openstack-using-cluster-api-capi-2fn8</guid>
      <description>&lt;p&gt;This is my first blog post. I spent this weekend going deep into CAPI + CAPO + ORC. This guide covers everything - installation, configuration, manifests, and the full flow explained simply. If you want to understand CAPI, this guide is for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Is Cluster API and Why Should You Care?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Managing Kubernetes clusters manually is painful. You manually create infrastructure, install Kubernetes packages, SSH into VMs, run &lt;code&gt;kubeadm init&lt;/code&gt;, and then run &lt;code&gt;kubeadm join&lt;/code&gt; to join the worker nodes, set up CNI, etc. Cluster API (CAPI) solves this by treating cluster creation as a Kubernetes-native, declarative operation. Cluster API is a Kubernetes project that lets you manage Kubernetes clusters using Kubernetes-style APIs.&lt;/p&gt;

&lt;p&gt;Instead of running commands, you write YAML manifests and apply them to a "management cluster." CAPI controllers read those manifests and provision your cluster automatically — creating VMs, configuring networking, running kubeadm — all without you touching a single VM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three core concepts to understand:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Management Cluster&lt;/strong&gt; — A small existing Kubernetes cluster that runs CAPI controllers and manages other clusters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workload Cluster&lt;/strong&gt; — The cluster CAPI creates for you, where your actual apps run&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Providers&lt;/strong&gt; — Plugins that tell CAPI how to talk to specific infrastructure (OpenStack, AWS, GCP, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Role of controllers:
&lt;/h3&gt;

&lt;p&gt;CAPI installs multiple controllers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Controller&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CAPI Core&lt;/td&gt;
&lt;td&gt;Orchestrates cluster lifecycle — watches Cluster, Machine, MachineDeployment objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAPO (Cluster API Provider OpenStack)&lt;/td&gt;
&lt;td&gt;Talks to OpenStack APIs — creates Nova VMs, Octavia LBs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CABPK (Kubeadm Bootstrap Provider)&lt;/td&gt;
&lt;td&gt;Generates cloud-init scripts that run kubeadm on VMs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KCP (Kubeadm Control Plane Provider)&lt;/td&gt;
&lt;td&gt;Manages control plane machine lifecycle and scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All are installed together via clusterctl.&lt;/p&gt;

&lt;p&gt;If you have been reading about CAPO, you may have come across ORC — the OpenStack Resource Controller. ORC is a separate project and is not part of CAPO. It needs to be installed separately. ORC is a separate, optional Kubernetes controller that lets you manage OpenStack resources — Glance images, Nova keypairs, Neutron networks and routers — as Kubernetes custom resources, declaratively. The idea is that instead of manually uploading your node image to Glance and copying its UUID into your CAPI manifests, you declare an Image CRD in your management cluster, and ORC ensures it exists in OpenStack. It ensures the resources referenced in the CAPI manifest exist before creating the cluster. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before installing CAPI, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running management cluster (even a single-node kind or minikube cluster works)&lt;/li&gt;
&lt;li&gt;clusterctl CLI installed&lt;/li&gt;
&lt;li&gt;OpenStack credentials (your clouds.yaml file)&lt;/li&gt;
&lt;li&gt;OpenStack environment with:

&lt;ul&gt;
&lt;li&gt;At least one available flavor (e.g., m1.medium)&lt;/li&gt;
&lt;li&gt;A Glance image with Kubernetes pre-installed (qcow image with kubeadm/kubelet/kubectl pre-installed)&lt;/li&gt;
&lt;li&gt;A provider network created&lt;/li&gt;
&lt;li&gt;Octavia (Load Balancer service) enabled&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Install &lt;code&gt;clusterctl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/kubernetes-sigs/cluster-api/releases/latest/download/clusterctl-linux-amd64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; clusterctl
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x clusterctl
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;clusterctl /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clusterctl version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;clusterctl version: &amp;amp;version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.3", GitCommit:"1a1852c74072febc3fbf9823c9dd32f4cd6cc747", GitTreeState:"clean", BuildDate:"2026-02-17T17:46:30Z", GoVersion:"go1.24.13", Compiler:"gc", Platform:"linux/amd64"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Initialize CAPI with OpenStack Provider
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clusterctl init &lt;span class="nt"&gt;--infrastructure&lt;/span&gt; openstack:v0.14.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CAPI core controllers&lt;/li&gt;
&lt;li&gt;CAPO (OpenStack infrastructure provider)&lt;/li&gt;
&lt;li&gt;CABPK (kubeadm bootstrap provider)&lt;/li&gt;
&lt;li&gt;KCP (kubeadm control plane provider)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sample Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fetching providers
Installing cert-manager version="v1.19.3"
Waiting for cert-manager to be available...
spec.privateKey.rotationPolicy: In cert-manager &amp;gt;= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="cluster-api" version="v1.12.4" targetNamespace="capi-system"
spec.privateKey.rotationPolicy: In cert-manager &amp;gt;= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="bootstrap-kubeadm" version="v1.12.4" targetNamespace="capi-kubeadm-bootstrap-system"
spec.privateKey.rotationPolicy: In cert-manager &amp;gt;= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="control-plane-kubeadm" version="v1.12.4" targetNamespace="capi-kubeadm-control-plane-system"
spec.privateKey.rotationPolicy: In cert-manager &amp;gt;= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="infrastructure-openstack" version="v0.14.1" targetNamespace="capo-system"
spec.privateKey.rotationPolicy: In cert-manager &amp;gt;= v1.18.0, the default value changed from `Never` to `Always`.
Your management cluster has been initialized successfully!

You can now create your first workload cluster by running the following:

  clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify all controllers are running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;kubectl get pods -A | grep -E "capi|capo|cert-manager"
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-controller-manager-5b6b785789-r62sl      1/1     Running   0          42m
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-controller-manager-95df764cf-hh7km   1/1     Running   0          42m
capi-system                         capi-controller-manager-6bbbfd79d8-b2dm4                        1/1     Running   0          42m
capo-system                         capo-controller-manager-6469cf765b-xhv8s                        1/1     Running   0          42m
cert-manager                        cert-manager-77f7698dcf-76hsq                                   1/1     Running   0          43m
cert-manager                        cert-manager-cainjector-557b97dd67-52fnw                        1/1     Running   0          43m
cert-manager                        cert-manager-webhook-5b7654ff4-cmgc8                            1/1     Running   0          43m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Set Environment Variables for Template Generation
&lt;/h2&gt;

&lt;p&gt;clusterctl can generate cluster manifests from a template. You need to export these environment variables first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_CLOUD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openstack &lt;span class="c"&gt;# name should match the cloudname in clouds.yaml&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_CLOUD_CACERT_B64&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;export  &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_CLOUD_YAML_B64&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w0&lt;/span&gt; clouds.yaml&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# get clouds.yaml from openstack horizon dashboard&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_NODE_MACHINE_FLAVOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;m1.medium &lt;span class="c"&gt;# instance type for ctrl plane node&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ubuntu-2404-kube &lt;span class="c"&gt;# Name of the image used for kubernetes cluster.&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_FAILURE_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nova  &lt;span class="c"&gt;# avail zone usually nova.&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_EXTERNAL_NETWORK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5068d3a6-f560-4d79-b834-cd6943dc5de5 &lt;span class="c"&gt;# ex: openstack network list | grep provider, get the id.&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_DNS_NAMESERVERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8.8.8.8
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENSTACK_SSH_KEY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kashi"&lt;/span&gt; &lt;span class="c"&gt;# name of the SSH key uploaded in OpenStack and you want to be uploaded to cluster nodes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image used in CAPI is prebuilt image that contains kubeadm, kubectl, kubelet all the kubernetes packages preinstalled.&lt;/p&gt;

&lt;p&gt;You can build the image by running the following Makefile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;SHELL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; /bin/bash

&lt;span class="nv"&gt;IMAGE_BUILDER_REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/kubernetes-sigs/image-builder.git
&lt;span class="nv"&gt;IMAGE_BUILDER_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;image-builder

&lt;span class="k"&gt;ifndef&lt;/span&gt; &lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ubuntu-2404-kube-v1.34.3
&lt;span class="nl"&gt;$(warning IMAGE_NAME is undefined, using default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(IMAGE_NAME))&lt;/span&gt;
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="k"&gt;ifndef&lt;/span&gt; &lt;span class="nv"&gt;IMAGE_FILE&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;output/ubuntu-2404-kube-v1.34.3/ubuntu-2404-kube-v1.34.3
&lt;span class="nl"&gt;$(warning IMAGE_FILE is undefined, using default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(IMAGE_FILE))&lt;/span&gt;
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="nv"&gt;OS_IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;OS_DISK_FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qcow2
&lt;span class="nv"&gt;OS_CONTAINER_FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bare
&lt;span class="nv"&gt;OS_VISIBILITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public
&lt;span class="k"&gt;ifndef&lt;/span&gt; &lt;span class="nv"&gt;ADMIN_RC_PATH&lt;/span&gt;
&lt;span class="nv"&gt;ADMIN_RC_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kolla/admin-openrc.sh
&lt;span class="nl"&gt;$(warning ADMIN_RC_PATH is undefined, using default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(ADMIN_RC_PATH))&lt;/span&gt;
&lt;span class="k"&gt;endif&lt;/span&gt;


&lt;span class="nl"&gt;img-builder-clone&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;IMAGE_BUILDER_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        git clone &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_BUILDER_REPO&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nl"&gt;capi-deps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;img-builder-clone&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_BUILDER_DIR&lt;span class="p"&gt;)&lt;/span&gt;/images/capi &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make deps-qemu


&lt;span class="nl"&gt;build-capi&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;capi-deps&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;IMAGE_BUILDER_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/images/capi/&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;IMAGE_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_BUILDER_DIR&lt;span class="p"&gt;)&lt;/span&gt;/images/capi &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make build-qemu-ubuntu-2404 &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;


&lt;span class="nl"&gt;capi-upload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
     &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;ADMIN_RC_PATH&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; openstack image create &lt;span class="p"&gt;$(&lt;/span&gt;OS_IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--file&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_BUILDER_DIR&lt;span class="p"&gt;)&lt;/span&gt;/images/capi/&lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_FILE&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--disk-format&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OS_DISK_FORMAT&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--container-format&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OS_CONTAINER_FORMAT&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--public&lt;/span&gt;


&lt;span class="nl"&gt;capi-image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build-capi capi-upload&lt;/span&gt;

&lt;span class="nl"&gt;capi-delete&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
     &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;ADMIN_RC_PATH&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; openstack image delete &lt;span class="p"&gt;$(&lt;/span&gt;OS_IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;capi-image&lt;/span&gt;

&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; 
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_BUILDER_DIR&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;$(&lt;/span&gt;DISKIMAGE_DIR&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;OCTAVIA_REPO&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the makefile: &lt;code&gt;make all&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can list the required environment variables to be set by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clusterctl generate cluster &lt;span class="nt"&gt;--infrastructure&lt;/span&gt; openstack:v0.14.1 &lt;span class="nt"&gt;--list-variables&lt;/span&gt; capi-quickstart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Required Variables:
  - KUBERNETES_VERSION
  - OPENSTACK_CLOUD
  - OPENSTACK_CLOUD_CACERT_B64
  - OPENSTACK_CLOUD_YAML_B64
  - OPENSTACK_CONTROL_PLANE_MACHINE_FLAVOR
  - OPENSTACK_DNS_NAMESERVERS
  - OPENSTACK_EXTERNAL_NETWORK_ID
  - OPENSTACK_FAILURE_DOMAIN
  - OPENSTACK_IMAGE_NAME
  - OPENSTACK_NODE_MACHINE_FLAVOR
  - OPENSTACK_SSH_KEY_NAME

Optional Variables:
  - CLUSTER_NAME                 (defaults to capi-quickstart)
  - CONTROL_PLANE_MACHINE_COUNT  (defaults to 1)
  - WORKER_MACHINE_COUNT         (defaults to 0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4 — Generate the Cluster Manifests&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clusterctl generate cluster hind-cluster &lt;span class="nt"&gt;--infrastructure&lt;/span&gt; openstack:v0.14.1   &lt;span class="nt"&gt;--kubernetes-version&lt;/span&gt; v1.34.3  &lt;span class="nt"&gt;--control-plane-machine-count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1   &lt;span class="nt"&gt;--worker-machine-count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; capi-quickstart.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command generates cluster manifests. Let's understand the generated manifests.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Cluster&lt;/code&gt; manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster.x-k8s.io/v1beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;clusterNetwork&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cidrBlocks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.0.0/16&lt;/span&gt;
    &lt;span class="na"&gt;serviceDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster.local&lt;/span&gt;
  &lt;span class="na"&gt;controlPlaneRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;controlplane.cluster.x-k8s.io&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KubeadmControlPlane&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane&lt;/span&gt;
  &lt;span class="na"&gt;infrastructureRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;infrastructure.cluster.x-k8s.io&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenStackCluster&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the main object — it ties everything together. It says:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pods get IPs from 192.168.0.0/16 &lt;/li&gt;
&lt;li&gt;Control plane is defined by KubeadmControlPlane&lt;/li&gt;
&lt;li&gt;OpenStack infrastructure is defined by OpenStackCluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;OpenStackCluster&lt;/code&gt; - OpenStack Infrastructure Definition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;infrastructure.cluster.x-k8s.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenStackCluster&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;apiServerLoadBalancer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;externalNetwork&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;c926f5e2-830e-43b0-9d06-89641bb8e131&lt;/span&gt;
  &lt;span class="na"&gt;identityRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cloudName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openstack&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-cloud-config&lt;/span&gt;
  &lt;span class="na"&gt;managedSecurityGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;allNodesSecurityGroupRules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Created by cluster-api-provider-openstack - BGP (calico)&lt;/span&gt;
      &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress&lt;/span&gt;
      &lt;span class="na"&gt;etherType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IPv4&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BGP (Calico)&lt;/span&gt;
      &lt;span class="na"&gt;portRangeMax&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;179&lt;/span&gt;
      &lt;span class="na"&gt;portRangeMin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;179&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
      &lt;span class="na"&gt;remoteManagedGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;controlplane&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Created by cluster-api-provider-openstack - IP-in-IP (calico)&lt;/span&gt;
      &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress&lt;/span&gt;
      &lt;span class="na"&gt;etherType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IPv4&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IP-in-IP (calico)&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4"&lt;/span&gt;
      &lt;span class="na"&gt;remoteManagedGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;controlplane&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
  &lt;span class="na"&gt;managedSubnets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cidr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.6.0.0/24&lt;/span&gt;
    &lt;span class="na"&gt;dnsNameservers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8.8.8.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CAPO controller in the &lt;code&gt;capo-system&lt;/code&gt; namespace reads this manifest and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks the existence of the external network using the ID provided in the above manifest, also creates an internal network for our cluster, subnet, a router, and attaches this internal network to the router so the nodes in the cluster get internet access (outbound) and a security group that allows only the load balancer to access the backend cluster nodes. The client should be able to access the cluster only through the loadbalancer IP and the backend control plane IPs, or their identities are not exposed.&lt;/li&gt;
&lt;li&gt;Creates security groups for the control plane and workers.&lt;/li&gt;
&lt;li&gt;Creates an Octavia LoadBalancer for the Kubernetes API endpoint. This is especially useful when u have multiple control plane nodes, and the load balancer will load balance the requests across the control planes.&lt;/li&gt;
&lt;li&gt;Sets the LB IP as the cluster's controlPlaneEndpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;kubeadmControlPlane&lt;/code&gt; definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;controlplane.cluster.x-k8s.io/v1beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KubeadmControlPlane&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kubeadmConfigSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;clusterConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;controllerManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;extraArgs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud-provider&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external&lt;/span&gt;
    &lt;span class="na"&gt;initConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;nodeRegistration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;kubeletExtraArgs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud-provider&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provider-id&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openstack:///'{{ instance_id }}'&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;local_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
    &lt;span class="na"&gt;joinConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;nodeRegistration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;kubeletExtraArgs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud-provider&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provider-id&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openstack:///'{{ instance_id }}'&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;local_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
  &lt;span class="na"&gt;machineTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;infrastructureRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;infrastructure.cluster.x-k8s.io&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenStackMachineTemplate&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.34.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cloud-provider: external&lt;/code&gt; on kubelet: When kubelet starts, it adds a taint node.cloudprovider.kubernetes.io/uninitialized on the node. No pods schedule here until the OpenStack Cloud Controller Manager (CCM) initializes the node and removes this taint. This flag tells kubelet, "don't try to handle cloud stuff yourself, wait for external CCM." &lt;/p&gt;

&lt;p&gt;&lt;code&gt;cloud-provider: external&lt;/code&gt; on controller-manager - Disables the built-in cloud loops in kube-controller-manager (node lifecycle, service LoadBalancer, routes). These are handed off to the OpenStack CCM pod. When a load-balancer type service is created, it is the responsibility of the CCM to create an LB for you behind the scenes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;provider-id: openstack:///{{ instance_id }}&lt;/code&gt; - A unique ID linking the Kubernetes Node object to the actual Nova VM. {{ instance_id }} is a cloud-init template variable resolved to the real VM UUID at boot time. The OpenStack CCM uses this to fetch instance metadata.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;OpenStackMachineTemplate&lt;/code&gt; for Control Plane:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;infrastructure.cluster.x-k8s.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenStackMachineTemplate&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-md-0&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;flavor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;m1.medium&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-2404-kube&lt;/span&gt;
      &lt;span class="na"&gt;sshKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kashi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells CAPO what Nova flavor and Glance image to use for control plane VMs. The CAPO looks up the image in Glance and creates the VMs using the image UID fetched from Glance.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;MachineDeployment&lt;/code&gt; — Worker Node Pool&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster.x-k8s.io/v1beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MachineDeployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-md-0&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;clusterName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;configRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bootstrap.cluster.x-k8s.io&lt;/span&gt;
          &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KubeadmConfigTemplate&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-md-0&lt;/span&gt;
      &lt;span class="na"&gt;clusterName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
      &lt;span class="na"&gt;failureDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nova&lt;/span&gt;
      &lt;span class="na"&gt;infrastructureRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;infrastructure.cluster.x-k8s.io&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenStackMachineTemplate&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-md-0&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.34.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like a Deployment for VMs. It ensures the desired number of worker nodes exists. It references KubeadmConfigTemplate for the cloud-init script and OpenStackMachineTemplate for VM specs.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;KubeadmConfigTemplate&lt;/code&gt; — Worker Bootstrap Config&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bootstrap.cluster.x-k8s.io/v1beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KubeadmConfigTemplate&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-md-0&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;joinConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;nodeRegistration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;kubeletExtraArgs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud-provider&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;external&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provider-id&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openstack:///'{{ instance_id }}'&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;local_hostname&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workers only have joinConfiguration — they never run kubeadm init, only kubeadm join. The same &lt;code&gt;cloud-provider: external&lt;/code&gt; and &lt;code&gt;provider-id&lt;/code&gt; flags are set here for the same reasons as the control plane.&lt;/p&gt;

&lt;p&gt;So, when you apply &lt;code&gt;capi-quickstart.yaml&lt;/code&gt;, CAPI sees the cluster object, and CAPO sees the OpenstackCluster object and creates the internal network, subnet, router, attaches subnet to the router for internet access ( router will have a port from the provider network as external gw ). It also creates security groups and the loadbalancer for kubernetes API server endpoint.&lt;/p&gt;

&lt;p&gt;You should see the below events once when you describe the &lt;code&gt;OpenstackCluster&lt;/code&gt; resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Events:
  Type    Reason                         Age   From                  Message
  ----    ------                         ----  ----                  -------
  Normal  Successfulcreatenetwork        54m   openstack-controller  Created network k8s-clusterapi-cluster-default-hind-cluster with id 05371ddd-a37e-43d6-ba6d-58599c4d2b7b
  Normal  Successfulcreatesubnet         54m   openstack-controller  Created subnet k8s-clusterapi-cluster-default-hind-cluster with id ea8186bc-88f7-4cc6-aa27-bf9b5a1154c5
  Normal  Successfulcreaterouter         54m   openstack-controller  Created router k8s-clusterapi-cluster-default-hind-cluster with id 8790e4f0-3b55-4d4e-acf4-8ce37e5719f4
  Normal  Successfulcreatesecuritygroup  54m   openstack-controller  Created security group k8s-cluster-default-hind-cluster-secgroup-controlplane with id e08613ca-135c-4f64-ba67-def6a1ffd7e1
  Normal  Successfulcreatesecuritygroup  54m   openstack-controller  Created security group k8s-cluster-default-hind-cluster-secgroup-worker with id 4ed61b8a-a9b0-4fb8-a3db-af6e457aea8a
  Normal  Successfulcreateloadbalancer   54m   openstack-controller  Created load balancer k8s-clusterapi-cluster-default-hind-cluster-kubeapi with id 421cd8a9-a7ba-4f41-8f03-1cdc4d958358
  Normal  Successfulcreatefloatingip     52m   openstack-controller  Created floating IP 10.31.0.101 with id 433d5f18-34dc-489d-b2ae-19ba2a167f8b
  Normal  Successfulassociatefloatingip  52m   openstack-controller  Associated floating IP 10.31.0.101 with port 6f0f82d9-bc8e-42e1-88eb-65ec33469d6e
  Normal  Successfulcreatelistener       52m   openstack-controller  Created listener k8s-clusterapi-cluster-default-hind-cluster-kubeapi-6443 with id c66b31a9-f9ee-4bfd-b61f-b402c266a8bf
  Normal  Successfulcreatepool           52m   openstack-controller  Created pool k8s-clusterapi-cluster-default-hind-cluster-kubeapi-6443 with id 465f565c-3a84-4b4d-938b-c28dfcc009af
  Normal  Successfulcreatemonitor        52m   openstack-controller  Created monitor k8s-clusterapi-cluster-default-hind-cluster-kubeapi-6443 with id 1349252f-b42b-4393-bab3-9243ff9b282e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Also verify the network, subnet, router, security groups in your openstack cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(kolla-venv) root@control-1:~#  openstack network list
+--------------------------------------+---------------------------------------------+--------------------------------------+
| ID                                   | Name                                        | Subnets                              |
+--------------------------------------+---------------------------------------------+--------------------------------------+
| 05371ddd-a37e-43d6-ba6d-58599c4d2b7b | k8s-clusterapi-cluster-default-hind-cluster | ea8186bc-88f7-4cc6-aa27-bf9b5a1154c5 |
| 40c70b44-1c45-407f-b590-5c019814ef57 | trove-mgmt                                  | 22b30e5a-4cbb-422d-9e9b-6f0ee2736c46 |
| 75bba094-d69a-4060-a271-a5d529915f01 | private-net                                 | e2c43b43-2936-4839-b57c-8c955f44f913 |
| 7e72fa18-46b5-4bfb-96bf-5666df021dec | lb-mgmt-net                                 | 2183795b-8723-490f-aaa9-5bb49f2d9af1 |
| c926f5e2-830e-43b0-9d06-89641bb8e131 | provider                                    | 4073439c-176e-46b7-8f78-9e7debdfef02 |
+--------------------------------------+---------------------------------------------+--------------------------------------+
(kolla-venv) root@control-1:~#  openstack subnet list
 +--------------------------------------+---------------------------------------------+--------------------------------------+-----------------+
| ID                                   | Name                                        | Network                              | Subnet          |
+--------------------------------------+---------------------------------------------+--------------------------------------+-----------------+
| 2183795b-8723-490f-aaa9-5bb49f2d9af1 | lb-mgmt-subnet                              | 7e72fa18-46b5-4bfb-96bf-5666df021dec | 10.1.0.0/24     |
| 22b30e5a-4cbb-422d-9e9b-6f0ee2736c46 | trove-mgmt-subnet                           | 40c70b44-1c45-407f-b590-5c019814ef57 | 10.33.0.0/24    |
| 4073439c-176e-46b7-8f78-9e7debdfef02 | provider-subnet                             | c926f5e2-830e-43b0-9d06-89641bb8e131 | 10.31.0.0/24    |
| e2c43b43-2936-4839-b57c-8c955f44f913 | private-subnet                              | 75bba094-d69a-4060-a271-a5d529915f01 | 192.168.50.0/24 |
| ea8186bc-88f7-4cc6-aa27-bf9b5a1154c5 | k8s-clusterapi-cluster-default-hind-cluster | 05371ddd-a37e-43d6-ba6d-58599c4d2b7b | 10.6.0.0/24     |
+--------------------------------------+---------------------------------------------+--------------------------------------+-----------------+
(kolla-venv) root@control-1:~#  
(kolla-venv) root@control-1:~#  openstack router list 
+--------------------------------------+---------------------------------------------+--------+-------+----------------------------------+-------------+-------+
| ID                                   | Name                                        | Status | State | Project                          | Distributed | HA    |
+--------------------------------------+---------------------------------------------+--------+-------+----------------------------------+-------------+-------+
| 40625a37-d00e-422c-a5b4-577c4d4c2a81 | trove-mgmt-router                           | ACTIVE | UP    | 2c2343fd2bd141e8a18229afefb9c4ff | False       | False |
| 8790e4f0-3b55-4d4e-acf4-8ce37e5719f4 | k8s-clusterapi-cluster-default-hind-cluster | ACTIVE | UP    | 2c2343fd2bd141e8a18229afefb9c4ff | False       | False |
+--------------------------------------+---------------------------------------------+--------+-------+----------------------------------+-------------+-------+
(kolla-venv) root@control-1:~# 
(kolla-venv) root@control-1:~# 
(kolla-venv) root@control-1:~# 
(kolla-venv) root@control-1:~#  openstack router show k8s-clusterapi-cluster-default-hind-cluster
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field                   | Value                                                                                                                                                     |
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
| admin_state_up          | UP                                                                                                                                                        |
| availability_zone_hints |                                                                                                                                                           |
| availability_zones      | nova                                                                                                                                                      |
| created_at              | 2026-03-29T11:26:23Z                                                                                                                                      |
| description             | Created by cluster-api-provider-openstack cluster default-hind-cluster                                                                                    |
| distributed             | False                                                                                                                                                     |
| enable_ndp_proxy        | None                                                                                                                                                      |
| external_gateway_info   | {"network_id": "c926f5e2-830e-43b0-9d06-89641bb8e131", "external_fixed_ips": [{"subnet_id": "4073439c-176e-46b7-8f78-9e7debdfef02", "ip_address":         |
|                         | "10.31.0.246"}], "enable_snat": true}                                                                                                                     |
| flavor_id               | None                                                                                                                                                      |
| ha                      | False                                                                                                                                                     |
| id                      | 8790e4f0-3b55-4d4e-acf4-8ce37e5719f4                                                                                                                      |
| interfaces_info         | [{"port_id": "1e65d210-c1e7-4118-a847-d27b9058077b", "ip_address": "10.6.0.1", "subnet_id": "ea8186bc-88f7-4cc6-aa27-bf9b5a1154c5"}]                      |
| name                    | k8s-clusterapi-cluster-default-hind-cluster                                                                                                               |
| project_id              | 2c2343fd2bd141e8a18229afefb9c4ff                                                                                                                          |
| revision_number         | 3                                                                                                                                                         |
| routes                  |                                                                                                                                                           |
| status                  | ACTIVE                                                                                                                                                    |
| tags                    |                                                                                                                                                           |
| updated_at              | 2026-03-29T11:26:25Z                                                                                                                                      |
+-------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+
(kolla-venv) root@control-1:~# 
(kolla-venv) root@control-1:~# 
(kolla-venv) root@control-1:~# 
(kolla-venv) root@control-1:~#  openstack loadbalancer list
 +-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| id                                  | name                                | project_id                       | vip_address | provisioning_status | operating_status | provider |
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| 421cd8a9-a7ba-4f41-8f03-            | k8s-clusterapi-cluster-default-     | 2c2343fd2bd141e8a18229afefb9c4ff | 10.6.0.107  | ACTIVE              | ONLINE           | amphora  |
| 1cdc4d958358                        | hind-cluster-kubeapi                |                                  |             |                     |                  |          |
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
(kolla-venv) root@control-1:~#  
(kolla-venv) root@control-1:~#  openstack  security group list
+--------------------------------------+--------------------------------------------------------+---------------------------+----------------------------------+------+--------+
| ID                                   | Name                                                   | Description               | Project                          | Tags | Shared |
+--------------------------------------+--------------------------------------------------------+---------------------------+----------------------------------+------+--------+
| 188e18b6-cc7e-42d6-8b56-a03cb9af5e5a | lb-421cd8a9-a7ba-4f41-8f03-1cdc4d958358                |                           | f37717d8ea254fdfb12bff38e8834f9d | []   | False  |
| 266e525f-2e15-4f72-9c11-7448c47d9abf | trove-sec-grp                                          | trove-sec-grp             | 2c2343fd2bd141e8a18229afefb9c4ff | []   | False  |
| 3a9617cd-6e92-4c7f-bad5-fd3dfb1996ed | default                                                | Default security group    | 2c2343fd2bd141e8a18229afefb9c4ff | []   | False  |
| 429b9c68-e2db-437c-b4db-bf7f8bad6d60 | default                                                | Default security group    | f37717d8ea254fdfb12bff38e8834f9d | []   | False  |
| 4ed61b8a-a9b0-4fb8-a3db-af6e457aea8a | k8s-cluster-default-hind-cluster-secgroup-worker       | Cluster API managed group | 2c2343fd2bd141e8a18229afefb9c4ff | []   | False  |
| 64ad5172-e754-416b-a228-b1bd9d98e96a | test                                                   | test                      | 2c2343fd2bd141e8a18229afefb9c4ff | []   | False  |
| 80fbc123-07f5-4b7d-bc2c-92891fc96668 | lb-mgmt-sec-grp                                        |                           | f37717d8ea254fdfb12bff38e8834f9d | []   | False  |
| e08613ca-135c-4f64-ba67-def6a1ffd7e1 | k8s-cluster-default-hind-cluster-secgroup-controlplane | Cluster API managed group | 2c2343fd2bd141e8a18229afefb9c4ff | []   | False  |
| e8fc39aa-668a-404a-afe0-98ffa118f4a1 | lb-health-mgr-sec-grp                                  |                           | f37717d8ea254fdfb12bff38e8834f9d | []   | False  |
+--------------------------------------+--------------------------------------------------------+---------------------------+----------------------------------+------+--------+
(kolla-venv) root@control-1:~# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the resources created by the clusterAPI are prefixed with &lt;code&gt;k8s-clusterapi&lt;/code&gt;, and security groups are prefixed with &lt;code&gt;k8s-cluster&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, you can see that a load balancer has been created that will act as the frontend for our control plane nodes.&lt;/p&gt;

&lt;p&gt;Then the capi-kubeadm-control-plane-controller-manager will see the &lt;code&gt;KubeadmControlPlane&lt;/code&gt; object and create the kubeadmconfig and machine objects in the cluster.&lt;/p&gt;

&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get kubeadmconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                               CLUSTER        DATA SECRET CREATED   AGE
hind-cluster-control-plane-wggsv   hind-cluster   true                  57m
hind-cluster-md-0-xg7gb-qjlvj      hind-cluster   true                  57m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see 2 objects, one for controlplane and the other for the worker node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                               CLUSTER        NODE NAME                          READY     AVAILABLE   UP-TO-DATE   PHASE     AGE   VERSION
hind-cluster-control-plane-wggsv   hind-cluster   hind-cluster-control-plane-wggsv   Unknown   False       True         Running   57m   v1.34.3
hind-cluster-md-0-xg7gb-qjlvj      hind-cluster   hind-cluster-md-0-xg7gb-qjlvj      True      True        True         Running   57m   v1.34.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And capi-kubeadm-bootstrap-controller-manager, does a crucial job. It generates the cloud-init script that contains the actual kubeadm commands to be run during the booting process of our nodes in the cluster. This cloud-init script is actually stored in a secret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnnh7ynggm5wnitvt3af.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnnh7ynggm5wnitvt3af.png" alt=" " width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Highlighted above are the secrets created by the controller that contain the cloud-init script. These secrets will be referenced by the machine objects created by kubeadm-controlplane-mgr.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;kubectl get machine hind-cluster-control-plane-wggsv -o yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cluster.x-k8s.io/v1beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Machine&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pre-terminate.delete.hook.machine.cluster.x-k8s.io/kcp-cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="na"&gt;creationTimestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-03-29T11:28:04Z"&lt;/span&gt;
  &lt;span class="na"&gt;finalizers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;machine.cluster.x-k8s.io&lt;/span&gt;
  &lt;span class="na"&gt;generation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cluster.x-k8s.io/cluster-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
    &lt;span class="na"&gt;cluster.x-k8s.io/control-plane&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="na"&gt;cluster.x-k8s.io/control-plane-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane-wggsv&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;ownerReferences&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;controlplane.cluster.x-k8s.io/v1beta2&lt;/span&gt;
    &lt;span class="na"&gt;blockOwnerDeletion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KubeadmControlPlane&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane&lt;/span&gt;
    &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;d25ecc23-dd2c-417a-b891-f88eb85225af&lt;/span&gt;
  &lt;span class="na"&gt;resourceVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;15813"&lt;/span&gt;
  &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4b4725ad-f1ee-4232-8c06-4cd2f14ed025&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;configRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bootstrap.cluster.x-k8s.io&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;KubeadmConfig&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane-wggsv&lt;/span&gt;
    &lt;span class="na"&gt;dataSecretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane-wggsv&lt;/span&gt;
  &lt;span class="na"&gt;clusterName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster&lt;/span&gt;
  &lt;span class="na"&gt;deletion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;nodeDeletionTimeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;failureDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nova&lt;/span&gt;
  &lt;span class="na"&gt;infrastructureRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;infrastructure.cluster.x-k8s.io&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OpenStackMachine&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hind-cluster-control-plane-wggsv&lt;/span&gt;
  &lt;span class="na"&gt;providerID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openstack:///3570638e-3d1c-4ccb-b758-86321356ea0d&lt;/span&gt;
  &lt;span class="na"&gt;readinessGates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;conditionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APIServerPodHealthy&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;conditionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ControllerManagerPodHealthy&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;conditionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SchedulerPodHealthy&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;conditionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EtcdPodHealthy&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;conditionType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EtcdMemberHealthy&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.34.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the data secretName above. The value matches one of the secrets listed in the output of the &lt;code&gt;get&lt;/code&gt; secrets command.&lt;/p&gt;

&lt;p&gt;CAPO controller sees the machine object and calls the NOVA service to create the VM. &lt;/p&gt;

&lt;p&gt;Also, while &lt;code&gt;kubeadmcontrolplane&lt;/code&gt; is used for control plane nodes, &lt;code&gt;machinedeployment&lt;/code&gt; is used for worker nodes.&lt;/p&gt;

&lt;p&gt;After you applied the clusterAPI, manifests, get the kubeconfig of the workload cluster using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clusterctl get kubeconfig hind-cluster &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; hind-cluster.kubeconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export the kubeconfig and verify the cluster status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hind-cluster.kubeconfig
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                               STATUS     ROLES           AGE   VERSION
hind-cluster-control-plane-wggsv   NotReady   control-plane   13m   v1.34.3
hind-cluster-md-0-xg7gb-qjlvj      NotReady   &amp;lt;none&amp;gt;          11m   v1.34.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nodes are in NotReady state. And it is completely expected. Because we need to install CNI.&lt;/p&gt;

&lt;p&gt;You can install the CNI of your choice, I am going with calico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hind-cluster.kubeconfig &lt;span class="se"&gt;\&lt;/span&gt;
  apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing the CNI, nodes should transition to Ready state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get nodes
NAME                               STATUS   ROLES           AGE   VERSION
hind-cluster-control-plane-wggsv   Ready    control-plane   70m   v1.34.3
hind-cluster-md-0-xg7gb-qjlvj      Ready    &amp;lt;none&amp;gt;          68m   v1.34.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, remember that we discussed about the role of openstack cloud controller manager above. In the above capi manifests, especially for kubeadmcontrolplane under kubeadmconfigspec, and in the kubeadmconfigtemplate of machine deployment. we specified extra args. i.e cloud-provider set to external. &lt;/p&gt;

&lt;p&gt;Since these flags are set, the worker node and control plane node both will register themselves with a taint: &lt;code&gt;node.cluster.x-k8s.io/uninitialized:NoSchedule&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So unless you install Openstack cloud controller manager the nodes will stay unschedulable. Let's verify this by logging into the nodes.&lt;br&gt;
Verify the kubelet status on control plane node:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp19niedi7c1876kkzsnb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp19niedi7c1876kkzsnb.png" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On worker node:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73hbzatykvkrk2mvz17m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73hbzatykvkrk2mvz17m.png" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You notice the following flags on both the nodes.&lt;br&gt;
On worker node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/usr/bin/kubelet &lt;span class="nt"&gt;--bootstrap-kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/bootstrap-kubelet.conf &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/kubelet.conf &lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/kubelet/config.yaml &lt;span class="nt"&gt;--cloud-provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;external &lt;span class="nt"&gt;--pod-infra-container-image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;registry.k8s.io/pause:3.10.1 &lt;span class="nt"&gt;--provider-id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openstack:///d9ae4dde-b281-421b-ad1b-36a2ee7db110 &lt;span class="nt"&gt;--register-with-taints&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;node.cluster.x-k8s.io/uninitialized:NoSchedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider ID here corresponds to nova VM UUID. This is how OpenStack Cloud Controller Manager maps the Kubernetes node to nova vm object in OpenStack. Also, the cloud-provider is set to &lt;code&gt;external&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On control plane:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;usr/bin/kubelet &lt;span class="nt"&gt;--bootstrap-kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/bootstrap-kubelet.conf &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/kubernetes/kubelet.conf &lt;span class="nt"&gt;--config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/kubelet/config.yaml &lt;span class="nt"&gt;--cloud-provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;external &lt;span class="nt"&gt;--pod-infra-container-image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;registry.k8s.io/pause:3.10.1 &lt;span class="nt"&gt;--provider-id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openstack:///3570638e-3d1c-4ccb-b758-86321356ea0d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now check the taints.&lt;br&gt;
For control plane:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfurry7t9hq0wwe8wo1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfurry7t9hq0wwe8wo1g.png" alt="taints-on-control-plane" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For worker node:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nurff9jdhd1lcxi28ij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nurff9jdhd1lcxi28ij.png" alt="taints-on-worker-node" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, now let's deploy OpenStack Cloud Controller Manager. Run the following steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://raw.githubusercontent.com/kubernetes-sigs/cluster-api-provider-openstack/master/templates/env.rc &lt;span class="nt"&gt;-O&lt;/span&gt; /tmp/env.rc
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tmp/env.rc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing old cloud.conf files..."&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/cloud.conf&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating cloud.conf file from clouds.yaml..."&lt;/span&gt;
/tmp/env.rc ./clouds.yaml openstack
&lt;span class="nb"&gt;cp&lt;/span&gt; /tmp/cloud.conf&lt;span class="k"&gt;*&lt;/span&gt; ./cloud.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating secret in the kube-system ns..."&lt;/span&gt;
kubectl &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hind-cluster.kubeconfig &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system create secret generic cloud-config &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cloud.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploying cloud controller in the workload cluster..."&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hind-cluster.kubeconfig &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/cloud-controller-manager-roles.yaml
kubectl apply &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hind-cluster.kubeconfig &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/cloud-controller-manager-role-bindings.yaml
kubectl apply &lt;span class="nt"&gt;--kubeconfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hind-cluster.kubeconfig &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/kubernetes/cloud-provider-openstack/master/manifests/controller-manager/openstack-cloud-controller-manager-ds.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;tmp.rc is the script used to generate cloud.conf file from clouds.yaml. Download the clouds.yaml file from your OpenStack cloud and specify the path to this script as the first argument. And the second argument is the cloud name in the clouds.yaml. &lt;/p&gt;

&lt;p&gt;Once you install the cloud controller manager, it will remove the taint, and you should see the coredns pods move to the running state from pending.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6k4ur3w9ywsx5kuztqfx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6k4ur3w9ywsx5kuztqfx.png" alt="status" width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a workload and Test the cluster:
&lt;/h2&gt;

&lt;p&gt;Now lets create a small nginx pod and then expose it using Loadbalancer service. We will see if the openstack CCM creates an LB or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@hind-cluster-control-plane-wggsv:~# kubectl create deploy nginx &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
deployment.apps/nginx created
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# kubectl get pods
NAME                     READY   STATUS              RESTARTS   AGE
nginx-66686b6766-549cc   0/1     ContainerCreating   0          4s
root@hind-cluster-control-plane-wggsv:~# kubectl expose deploy nginx &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
service/nginx exposed
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# 
root@hind-cluster-control-plane-wggsv:~# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;        AGE
kubernetes   ClusterIP      10.96.0.1       &amp;lt;none&amp;gt;        443/TCP        90m
nginx        LoadBalancer   10.99.239.196   &amp;lt;pending&amp;gt;     80:31426/TCP   4s
root@hind-cluster-control-plane-wggsv:~# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, it will be in the pending state. Describe the service and verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@hind-cluster-control-plane-wggsv:~# kubectl describe svc nginx 
Name:                     nginx
Namespace:                default
Labels:                   app=nginx
Annotations:              &amp;lt;none&amp;gt;
Selector:                 app=nginx
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.99.239.196
IPs:                      10.99.239.196
Port:                     &amp;lt;unset&amp;gt;  80/TCP
TargetPort:               80/TCP
NodePort:                 &amp;lt;unset&amp;gt;  31426/TCP
Endpoints:                192.168.69.193:80
Session Affinity:         None
External Traffic Policy:  Cluster
Internal Traffic Policy:  Cluster
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  EnsuringLoadBalancer  12s   service-controller  Ensuring load balancer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also verify in the openstack cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(kolla-venv) root@control-1:~#  openstack loadbalancer list
  +-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| id                                  | name                                | project_id                       | vip_address | provisioning_status | operating_status | provider |
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| 421cd8a9-a7ba-4f41-8f03-            | k8s-clusterapi-cluster-default-     | 2c2343fd2bd141e8a18229afefb9c4ff | 10.6.0.107  | ACTIVE              | ONLINE           | amphora  |
| 1cdc4d958358                        | hind-cluster-kubeapi                |                                  |             |                     |                  |          |
| 5289ba7f-dc63-4db2-a6a4-            | kube_service_kubernetes_default_ngi | 2c2343fd2bd141e8a18229afefb9c4ff | 10.6.0.203  | PENDING_CREATE      | ONLINE           | amphora  |
| f72d2e86f33d                        | nx                                  |                                  |             |                     |                  |          |
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally after sometime, the status should change to active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(kolla-venv) root@control-1:~#   openstack loadbalancer list
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| id                                  | name                                | project_id                       | vip_address | provisioning_status | operating_status | provider |
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
| 421cd8a9-a7ba-4f41-8f03-            | k8s-clusterapi-cluster-default-     | 2c2343fd2bd141e8a18229afefb9c4ff | 10.6.0.107  | ACTIVE              | ONLINE           | amphora  |
| 1cdc4d958358                        | hind-cluster-kubeapi                |                                  |             |                     |                  |          |
| 5289ba7f-dc63-4db2-a6a4-            | kube_service_kubernetes_default_ngi | 2c2343fd2bd141e8a18229afefb9c4ff | 10.6.0.203  | ACTIVE              | ONLINE           | amphora  |
| f72d2e86f33d                        | nx                                  |                                  |             |                     |                  |          |
+-------------------------------------+-------------------------------------+----------------------------------+-------------+---------------------+------------------+----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the status of the service should change as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@hind-cluster-control-plane-wggsv:~# kubectl describe svc nginx 
Name:                     nginx
Namespace:                default
Labels:                   app=nginx
Annotations:              loadbalancer.openstack.org/load-balancer-address: 10.31.0.48
                          loadbalancer.openstack.org/load-balancer-id: 5289ba7f-dc63-4db2-a6a4-f72d2e86f33d
Selector:                 app=nginx
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.99.239.196
IPs:                      10.99.239.196
LoadBalancer Ingress:     10.31.0.48 (VIP)
Port:                     &amp;lt;unset&amp;gt;  80/TCP
TargetPort:               80/TCP
NodePort:                 &amp;lt;unset&amp;gt;  31426/TCP
Endpoints:                192.168.69.193:80
Session Affinity:         None
External Traffic Policy:  Cluster
Internal Traffic Policy:  Cluster
Events:
  Type    Reason                Age                 From                Message
  ----    ------                ----                ----                -------
  Normal  EnsuringLoadBalancer  23s (x2 over 117s)  service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer   23s (x2 over 23s)   service-controller  Ensured load balancer

root@hind-cluster-control-plane-wggsv:~# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1       &amp;lt;none&amp;gt;        443/TCP        95m
nginx        LoadBalancer   10.99.239.196   10.31.0.48    80:31426/TCP   4m49s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can see that the service got an IP from provider network. Let's verify it through curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Welcome to nginx!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;35em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Tahoma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Verdana&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to nginx!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you see this page, nginx is successfully installed and working.
Further configuration is required for the web server, reverse proxy, 
API gateway, load balancer, content cache, or other features.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;For online documentation and support please refer to
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://nginx.org/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;nginx.org&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
To engage with the community please visit
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://community.nginx.org/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;community.nginx.org&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
For enterprise grade support, professional services, additional 
security features and capabilities please refer to
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://f5.com/nginx"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;f5.com/nginx&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&lt;/span&gt;Thank you for using nginx.&lt;span class="nt"&gt;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
root@hind-cluster-control-plane-wggsv:~# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's scale the pods to 2 replicas and see the loadbalancing in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@hind-cluster-control-plane-wggsv:~# kubectl scale deploy nginx &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
deployment.apps/nginx scaled

&lt;span class="c"&gt;# In the first replica:&lt;/span&gt;
root@nginx-66686b6766-549cc:/usr/share/nginx/html# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Replica-1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; index.html 
&lt;span class="c"&gt;# In the second replica:&lt;/span&gt;
root@nginx-66686b6766-mgks5:/usr/share/nginx/html# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Replica-2"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; index.html 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify loadbalancing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
Replica-2
root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
Replica-1
root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
Replica-1
root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
Replica-1
root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
Replica-1
root@hind-cluster-control-plane-wggsv:~# curl  10.31.0.48
Replica-2
root@hind-cluster-control-plane-wggsv:~# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;That's all for now. If you found this helpful or got stuck somewhere, drop a comment below. This stuff took me a long time to piece together — happy to help you shortcut that journey.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>clusterapi</category>
      <category>kubernetes</category>
      <category>openstack</category>
      <category>containers</category>
    </item>
  </channel>
</rss>
