Minimal Environment
This guide walks you through deploying the smallest viable CobaltCore environment: one control plane node running the Kubernetes control plane and OpenStack services, and one hypervisor node running KVM virtual machines.
This is not a production configuration - it has no high availability and no redundant storage. It is suitable for exploration, development, and learning how the components fit together.
What you will deploy
┌─────────────────────────────────┐
│ Control Plane Node │
│ Kubernetes · OpenStack (Helm) │
│ Ceph (single node) │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ Hypervisor Node │
│ GardenLinux · KVM · libvirt │
│ Hypervisor Operator agents │
└─────────────────────────────────┘Prerequisites
- Two machines (physical or virtual), each with:
- 8+ CPUs, 32 GB RAM, 200 GB disk
- Linux OS (Ubuntu 22.04 or equivalent)
- Network connectivity between them
kubectlinstalled on your workstationhelmv3 installed- Basic familiarity with Kubernetes
Step 1: Bootstrap the Kubernetes cluster
CobaltCore uses Gardener for cluster lifecycle management in production, but for a minimal setup you can use any conformant Kubernetes distribution (k3s, kubeadm, kind with enough resources).
The control plane must be reachable from the hypervisor node.
Step 2: Install the Hypervisor Operator
The Hypervisor Operator manages hypervisor node lifecycle. Install it into your cluster:
helm repo add cobaltcore https://cobaltcore-dev.github.io/charts
helm repo update
helm install hypervisor-operator cobaltcore/hypervisor-operator \
--namespace cobaltcore-system \
--create-namespaceSee Compute - Hypervisor for configuration options.
Step 3: Register the hypervisor node
Create a Hypervisor resource to register your compute node:
apiVersion: kvm.cloud.sap/v1
kind: Hypervisor
metadata:
name: hypervisor-01
spec:
lifecycleEnabled: true
highAvailability: false
version: latestApply it and verify the node is discovered:
kubectl apply -f hypervisor.yaml
kubectl get hypervisorsStep 4: Deploy storage (single-node Ceph)
For a minimal setup, deploy Ceph in single-replica mode via Rook:
helm repo add rook-release https://charts.rook.io/release
helm repo update
helm install rook-ceph rook-release/rook-ceph \
--namespace rook-ceph \
--create-namespace \
--version v1.20.7 \
--wait
helm repo add ceph-csi-operator https://ceph.github.io/ceph-csi-operator
helm repo update
helm install ceph-csi-drivers ceph-csi-operator/ceph-csi-drivers \
--namespace rook-ceph \
--version 1.0.4 \
--wait \
-f https://raw.githubusercontent.com/rook/rook/v1.20.7/deploy/charts/ceph-csi-drivers/values.yamlCreate minimal-ceph-cluster.yaml, replacing k3s-node and sdb with the Kubernetes node name and the dedicated raw device in your lab:
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
dataDirHostPath: /var/lib/rook
cephVersion:
image: quay.io/ceph/ceph:v20.2.4
mon:
count: 1
allowMultiplePerNode: true
mgr:
count: 1
allowMultiplePerNode: true
storage:
useAllNodes: false
useAllDevices: false
nodes:
- name: k3s-node
devices:
- name: sdb
cephConfig:
global:
osd_pool_default_size: "1"
mon_warn_on_pool_no_redundancy: "false"
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
replicated:
size: 1
requireSafeReplicaSize: false
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-block
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
clusterID: rook-ceph
pool: replicapool
imageFormat: "2"
imageFeatures: layering
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-publish-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-publish-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
csi.storage.k8s.io/fstype: ext4
allowVolumeExpansion: true
reclaimPolicy: DeleteThe device must be unformatted, unmounted, and dedicated to this disposable test cluster. Apply the manifest and wait for Ceph to become ready:
kubectl apply -f minimal-ceph-cluster.yaml
kubectl -n rook-ceph wait --for=condition=Ready cephcluster/rook-ceph --timeout=15mThis single-node configuration is for testing only. See Storage - Rook for the production three-node cluster configuration.
Step 5: Deploy OpenStack
OpenStack services are deployed via Helm. A minimal configuration enables Nova, Neutron, Cinder, Keystone, and Glance:
helm install openstack cobaltcore/openstack \
--namespace openstack \
--create-namespace \
--values minimal-values.yamlSee OpenStack for service-level configuration.
Step 6: Verify
Check that all components are running:
kubectl get pods -n cobaltcore-system
kubectl get pods -n rook-ceph
kubectl get pods -n openstack
kubectl get hypervisorsAccess the OpenStack API endpoint and create a test VM to confirm end-to-end functionality.
Next steps
- Full production deployment - add HA, stretched storage, and the management frontend
- Compute - Hypervisor - configure hypervisor agents and HA
- Storage - Ceph - tune storage pools and replication
- OpenStack - configure individual services