February 7, 2020·4 min read·engineeringkubernetesopenstackcontrollersstorage

OpenStack integration moves into external Kubernetes controllers

A kubeadm deployment guide shows how external cloud initialization and Cinder CSI cooperate. The node initialization taint makes the boundary between cluster bootstrap and cloud identity visible.

A newly registered Kubernetes node can exist before the cluster knows which OpenStack virtual machine it represents. That gap matters when the cluster needs to attach a cloud volume to the machine running a pod. The OpenStack deployment guide published today makes the gap visible through a node taint. The node waits for an external controller to supply cloud information before normal scheduling can proceed.

The guide builds a single control-plane Kubernetes cluster using kubeadm on CentOS. Its example targets Kubernetes v1.15.1 and deploys the OpenStack cloud controller manager separately. It then installs the Cinder CSI components for persistent storage. This is a deployment account of an existing integration model, not an announcement that Kubernetes has just acquired OpenStack support.

I find this useful because the controller boundary is explicit. Kubernetes can expose the incomplete state of a node while a separate component reconciles the information it needs. That fits the part of Kubernetes I value most, its ability to converge declared and observed state through controllers with identifiable responsibilities.

Why extract the cloud integration

The Cloud Provider SIG's April 2019 explanation describes the cost of embedding provider knowledge in Kubernetes components. Each provider adds dependencies and increases binary size. Provider changes also become part of Kubernetes release work, even when they affect only one infrastructure platform.

An external cloud controller manager lets a provider develop, build, and release its integration independently of Kubernetes core. The SIG describes extensibility and cloud independence as the reasons for this direction. Extraction does not eliminate the integration's responsibilities. It changes where their implementation runs and which release process delivers it.

The OpenStack guide shows the resulting operational tradeoff. An operator deploys another controller and supplies its credentials, certificates, and permissions. In return, the provider-specific implementation has a deployment separate from the core components. The guide's manifests make that extra ownership concrete rather than leaving it as an architectural claim.

Initialization has a deliberate waiting state

The kubeadm configuration tells the kubelet to use an external cloud provider. The relevant configuration fragment is small, although the complete initialization file in the guide contains further settings.

nodeRegistration:
  kubeletExtraArgs:
    cloud-provider: "external"

After initialization, the guide shows the control-plane node with node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule. The external controller queries OpenStack for node information and removes that taint. Its example then shows a ProviderID containing the OpenStack VM UUID. This connects the Kubernetes node object to the infrastructure object the cloud APIs understand.

The cloud controller must be able to start before that initialization completes. The example deploys it as a DaemonSet restricted to control-plane nodes, using the historical node-role.kubernetes.io/master label. Its tolerations include the uninitialized and not-ready taints. It also uses host networking. Those details explain how the component responsible for completing bootstrap avoids waiting for ordinary workload readiness.

Removing the cloud taint does not complete every part of node setup. The guide next shows the node reporting an uninitialized CNI configuration. Installing its chosen network plugin completes that part of readiness. Cloud identity and pod networking therefore have distinct completion conditions, even though both affect whether the new cluster can run applications.

Storage has another controller boundary

The guide installs a Cinder CSI controller plugin and a node plugin. To create, attach, detach, and delete volumes, the controller communicates with Kubernetes and Cinder. On worker nodes, the node plugin binds the attached device into a pod. These are different operations with different locations and permissions.

That separation explains why a running cloud controller alone is insufficient evidence that persistent storage works. Cloud initialization establishes the mapping between Kubernetes nodes and OpenStack VMs. The CSI components perform the storage operations. A failure to mount a volume can require examining the storage components even when the node already has its cloud identity.

The node plugin manifest also shows why storage extensions need careful privileges. It uses privileged execution and host filesystem mounts to perform its device and mount work. Moving integration code outside Kubernetes core does not remove its access to sensitive host operations. It makes that access part of a separately deployed component's configuration.

The guide's final storage example creates a claim, observes a corresponding Cinder volume, and mounts that claim in a pod. OpenStack's attachment information identifies the worker VM. This closes the explanation of node identity with a concrete consumer of it. The storage operation has to reach the machine actually hosting the workload.

Credentials and cloud state remain prerequisites

The OpenStack preparation includes a project for the cluster and a user that can query node information and attach volumes. The configuration includes cloud authentication details and the certificate authority for the OpenStack API endpoint. Kubernetes Secrets supply configuration to the deployed components. The integration therefore depends on both Kubernetes-side deployment and valid cloud-side access.

This is the part of external controllers that I consider an operational cost. The software has a separate lifecycle, but its reconciliation still depends on another system's identity and availability. A node waiting for cloud initialization is a useful diagnostic state because it identifies unfinished work without implying that kubeadm itself has failed.

The guide leaves an operator with distinct objects to examine during bootstrap. The node's cloud taint records whether initialization remains outstanding. Its provider ID records the VM mapping after the cloud controller acts. CNI readiness describes another dependency, and the CSI controller and node plugin account for the subsequent storage operations. Those distinctions make the external integration understandable at the points where a deployment can stop progressing.