Skip to content

OpenStack + Ceph Integration

🎯 Introduction

Ceph is the most widely used storage backend for OpenStack in production, and it delivers:

  • Unified storage: Images, volumes and ephemeral instances on a single cluster
  • High availability: Native replication, no single point of failure
  • Scalability: Grows linearly as you add nodes
  • Performance: Copy-on-write, thin provisioning, instant snapshots
  • Native integration: RBD (RADOS Block Device) support built into OpenStack

Integration Architecture

graph TB
    subgraph OpenStack
        Glance[Glance<br/>Images]
        Cinder[Cinder<br/>Volumes]
        Nova[Nova<br/>Compute]
    end

    subgraph Ceph Cluster
        MON1[MON 1]
        MON2[MON 2]
        MON3[MON 3]
        OSD1[OSD 1<br/>Pool: images]
        OSD2[OSD 2<br/>Pool: volumes]
        OSD3[OSD 3<br/>Pool: vms]
    end

    Glance -->|RBD| OSD1
    Cinder -->|RBD| OSD2
    Nova -->|RBD| OSD3

    MON1 -.->|Cluster Map| OSD1
    MON2 -.->|Cluster Map| OSD2
    MON3 -.->|Cluster Map| OSD3

📋 Prerequisites

A Working Ceph Cluster

  • Version: Ceph Reef (18.x) or newer
  • Nodes: At least 3 MON + 3 OSD
  • Health: ceph -s must report HEALTH_OK
  • Network: 10 Gbps or better is recommended for storage traffic

OpenStack Already Deployed

  • Kolla-Ansible with the core services up and running
  • Controllers and computes able to reach the Ceph network

🔧 Step 1: Prepare Ceph

1.1 Create the Pools

Run this from a Ceph MON node:

# Calculate PGs (Placement Groups)
# Formula: (OSDs * 100) / replicas / pools
# Example: (30 OSDs * 100) / 3 replicas / 3 pools = ~333 PGs
# Round to a power of 2: 256

# Pool for Glance images
ceph osd pool create images 256
ceph osd pool application enable images rbd

# Pool for Cinder volumes
ceph osd pool create volumes 256
ceph osd pool application enable volumes rbd

# Pool for volume snapshots
ceph osd pool create backups 128
ceph osd pool application enable backups rbd

# Pool for Nova ephemeral instances
ceph osd pool create vms 256
ceph osd pool application enable vms rbd

# Set replication (size=3, min_size=2 by default)
ceph osd pool set images size 3
ceph osd pool set volumes size 3
ceph osd pool set vms size 3
ceph osd pool set backups size 3

1.2 Configure CRUSH Rules (Optional)

To get better performance out of SSD/NVMe devices:

# Create a rule for SSDs (if you have SSD-backed OSDs)
ceph osd crush rule create-replicated rule-ssd default host ssd

# Apply it to the critical pools
ceph osd pool set images crush_rule rule-ssd
ceph osd pool set volumes crush_rule rule-ssd

1.3 Create the Cephx Users

# User for Glance
ceph auth get-or-create client.glance \
  mon 'profile rbd' \
  osd 'profile rbd pool=images' \
  -o /etc/ceph/ceph.client.glance.keyring

# User for Cinder
ceph auth get-or-create client.cinder \
  mon 'profile rbd' \
  osd 'profile rbd pool=volumes, profile rbd pool=backups, profile rbd pool=vms' \
  -o /etc/ceph/ceph.client.cinder.keyring

# Note: Nova will reuse the Cinder user

# Check the permissions
ceph auth get client.glance
ceph auth get client.cinder

1.4 Generate ceph.conf

# Get a minimal ceph.conf
ceph config generate-minimal-conf

# Expected output:
# [global]
# fsid = a7f64266-0894-4f1e-a635-d0aeaca0e993
# mon_host = [v2:10.0.40.30:3300/0,v1:10.0.40.30:6789/0] [v2:10.0.40.31:3300/0,v1:10.0.40.31:6789/0] [v2:10.0.40.32:3300/0,v1:10.0.40.32:6789/0]

# Save it to a file
ceph config generate-minimal-conf > ceph.conf.minimal

🚀 Step 2: Configure OpenStack (Kolla-Ansible)

2.1 Copy the Ceph Files to Controllers and Computes

From the deployment node:

# Create the directories for the Ceph configs
sudo mkdir -p /etc/kolla/config/glance
sudo mkdir -p /etc/kolla/config/cinder
sudo mkdir -p /etc/kolla/config/nova

# Copy ceph.conf and the keyrings from the Ceph MON
# (assuming you have SSH access to storage01)

# ceph.conf
scp storage01:/etc/ceph/ceph.conf /tmp/ceph.conf
sudo cp /tmp/ceph.conf /etc/kolla/config/glance/
sudo cp /tmp/ceph.conf /etc/kolla/config/cinder/
sudo cp /tmp/ceph.conf /etc/kolla/config/nova/

# Keyrings
scp storage01:/etc/ceph/ceph.client.glance.keyring /tmp/
scp storage01:/etc/ceph/ceph.client.cinder.keyring /tmp/

sudo cp /tmp/ceph.client.glance.keyring /etc/kolla/config/glance/
sudo cp /tmp/ceph.client.cinder.keyring /etc/kolla/config/cinder/
sudo cp /tmp/ceph.client.cinder.keyring /etc/kolla/config/nova/ceph.client.cinder.keyring

# Fix the permissions
sudo chown -R kolla:kolla /etc/kolla/config/

2.2 Configure globals.yml

Edit /etc/kolla/globals.yml:

# Enable Ceph
enable_ceph: "no"  # Leave it as "no" when Ceph is external

# Ceph backend per service
glance_backend_ceph: "yes"
glance_backend_file: "no"
cinder_backend_ceph: "yes"
nova_backend_ceph: "yes"

# Ceph settings
ceph_glance_user: "glance"
ceph_glance_keyring: "ceph.client.glance.keyring"
ceph_glance_pool_name: "images"

ceph_cinder_user: "cinder"
ceph_cinder_keyring: "ceph.client.cinder.keyring"
ceph_cinder_pool_name: "volumes"
ceph_cinder_backup_pool_name: "backups"

ceph_nova_user: "cinder"  # Nova reuses the Cinder user
ceph_nova_keyring: "ceph.client.cinder.keyring"
ceph_nova_pool_name: "vms"

# Ceph cluster FSID (get it with: ceph -s)
ceph_cluster_fsid: "a7f64266-0894-4f1e-a635-d0aeaca0e993"

# RBD settings
ceph_rbd_cache: "true"
ceph_rbd_cache_writethrough_until_flush: "true"
ceph_rbd_cache_size: "67108864"  # 64 MB
ceph_rbd_cache_max_dirty: "50331648"  # 48 MB

2.3 Configure Cinder with the Ceph Backend

Create /etc/kolla/config/cinder/cinder-volume.conf:

[DEFAULT]
enabled_backends = rbd-1

[rbd-1]
volume_driver = cinder.volume.drivers.rbd.RBDDriver
volume_backend_name = rbd-1
rbd_pool = volumes
rbd_ceph_conf = /etc/ceph/ceph.conf
rbd_flatten_volume_from_snapshot = false
rbd_max_clone_depth = 5
rbd_store_chunk_size = 4
rados_connect_timeout = -1
rbd_user = cinder
rbd_secret_uuid = {{ cinder_rbd_secret_uuid }}  # Kolla generates this automatically

2.4 Configure Glance with the Ceph Backend

Create /etc/kolla/config/glance/glance-api.conf:

[glance_store]
stores = rbd
default_store = rbd
rbd_store_pool = images
rbd_store_user = glance
rbd_store_ceph_conf = /etc/ceph/ceph.conf
rbd_store_chunk_size = 8

2.5 Configure Nova to Use Ceph

Create /etc/kolla/config/nova/nova-compute.conf:

[libvirt]
images_type = rbd
images_rbd_pool = vms
images_rbd_ceph_conf = /etc/ceph/ceph.conf
rbd_user = cinder
rbd_secret_uuid = {{ cinder_rbd_secret_uuid }}
disk_cachemodes = "network=writeback"
hw_disk_discard = unmap

🔄 Step 3: Deploy the Configuration

3.1 Reconfigure

# Activate the virtualenv
source ~/kolla-venv/bin/activate

# Validate the configuration
kolla-ansible -i /etc/kolla/multinode prechecks

# Reconfigure the services
kolla-ansible -i /etc/kolla/multinode reconfigure --tags glance,cinder,nova

# Wait for the containers to be recreated (~2-5 minutes)
watch docker ps

3.2 Verify the Integration

source /etc/kolla/admin-openrc.sh

# Check the Cinder services
openstack volume service list

# Expected output:
# +------------------+------------------+------+---------+-------+----------------------------+
# | Binary           | Host             | Zone | Status  | State | Updated At                 |
# +------------------+------------------+------+---------+-------+----------------------------+
# | cinder-scheduler | controller01     | nova | enabled | up    | 2026-01-25T10:30:45.000000 |
# | cinder-volume    | controller01@rbd-1 | nova | enabled | up    | 2026-01-25T10:30:42.000000 |
# +------------------+------------------+------+---------+-------+----------------------------+

# Check the Cinder backend
openstack volume type list

# Create a volume type for Ceph
openstack volume type create ceph-rbd
openstack volume type set ceph-rbd --property volume_backend_name=rbd-1

# Check Glance
openstack image list

✅ Step 4: Functional Testing

4.1 Test Glance (Images on Ceph)

# Download a test image
wget http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img

# Upload it to Glance
openstack image create "cirros-ceph" \
  --file cirros-0.6.2-x86_64-disk.img \
  --disk-format qcow2 \
  --container-format bare \
  --public

# Check it on Ceph
# From a Ceph node:
rbd ls images
rbd info images/<image-UUID>

# Expected output:
# rbd image 'a7f64266-0894-...':
#     size 117 MiB in 15 objects
#     order 23 (8 MiB objects)
#     snapshot_count: 0
#     block_name_prefix: rbd_data.123456789
#     format: 2

4.2 Test Cinder (Volumes on Ceph)

# Create a volume
openstack volume create --size 10 --type ceph-rbd test-volume

# Check its status
openstack volume list

# Check it on Ceph
# From a Ceph node:
rbd ls volumes
rbd info volumes/volume-<UUID>

# Expected output:
# rbd image 'volume-a7f64266...':
#     size 10 GiB in 2560 objects
#     order 22 (4 MiB objects)

4.3 Test Nova (Ephemeral Instances on Ceph)

# Boot an instance from the Ceph-backed image
openstack server create \
  --flavor m1.small \
  --image cirros-ceph \
  --network demo-net \
  test-instance-ceph

# Check it on Ceph (may take a few seconds)
# From a Ceph node:
rbd ls vms

# Expected output:
# <instance-UUID>_disk

4.4 Test Snapshots

# Create a volume snapshot
openstack volume snapshot create --volume test-volume test-snapshot

# Check it on Ceph
rbd snap ls volumes/volume-<UUID>

# Create a volume from the snapshot
openstack volume create --snapshot test-snapshot --size 10 test-volume-from-snap

# Confirm it uses CoW (copy-on-write)
rbd info volumes/volume-<new-UUID>
# It should show: parent: volumes/volume-<original-UUID>@snapshot-<UUID>

🔍 Common Operations

Monitor Pool Usage

# From a Ceph node
ceph df

# Output:
# --- RAW STORAGE ---
# CLASS     SIZE    AVAIL     USED  RAW USED  %RAW USED
# ssd      30 TiB  25 TiB  5.0 TiB   5.0 TiB      16.67
# TOTAL    30 TiB  25 TiB  5.0 TiB   5.0 TiB      16.67
#
# --- POOLS ---
# POOL        ID  PGS  STORED  OBJECTS  USED    %USED  MAX AVAIL
# images       1  256  500 GiB    12.5k  1.5 TiB   6.00     8.3 TiB
# volumes      2  256  2.0 TiB      50k  6.0 TiB  24.00     8.3 TiB
# vms          3  256  1.5 TiB      37k  4.5 TiB  18.00     8.3 TiB
# backups      4  128  100 GiB     2.5k  300 GiB   1.20     8.3 TiB

Adjust Pool Quotas

# Cap the maximum pool size
ceph osd pool set-quota volumes max_bytes $((10 * 1024**4))  # 10 TB

# Cap the object count
ceph osd pool set-quota images max_objects 100000

Clean Up Orphaned Images

# Sometimes images are left behind in Ceph with no reference in Glance
# List the images in Glance
openstack image list -f value -c ID > /tmp/glance_images.txt

# List the images in Ceph
rbd ls images > /tmp/ceph_images.txt

# Compare and remove the orphans (BE CAREFUL!)
# Review them by hand before deleting anything

Migrate a Volume Between Backends

# If you run multiple backends (e.g., LVM + Ceph)
openstack volume migrate <volume-id> --host controller01@rbd-1

🛡️ Best Practices

1. Performance Settings

# In cinder-volume.conf and nova-compute.conf
rbd_cache = true
rbd_cache_writethrough_until_flush = true
rbd_cache_max_dirty = 50331648  # 48 MB
rbd_cache_target_dirty = 33554432  # 32 MB

2. Networking

  • Split the networks: Storage front-end (client) and back-end (OSD replication)
  • Jumbo frames MTU: 9000 on storage networks
  • Bonding: LACP or active-backup for redundancy

3. Pool Sizing

# Rule of thumb for PGs:
# Total PGs = (Total OSDs * 100) / Replicas
# Then divide by the number of pools

# Example with 30 OSDs, 3 replicas, 4 pools:
# (30 * 100) / 3 = 1000 PGs total
# 1000 / 4 pools = 250 PGs per pool
# Round to the nearest power of 2: 256

4. Backups

# Enable Cinder backups to Ceph
# In /etc/kolla/config/cinder/cinder-backup.conf
[DEFAULT]
backup_driver = cinder.backup.drivers.ceph.CephBackupDriver
backup_ceph_conf = /etc/ceph/ceph.conf
backup_ceph_user = cinder
backup_ceph_chunk_size = 134217728  # 128 MB
backup_ceph_pool = backups
backup_ceph_stripe_unit = 0
backup_ceph_stripe_count = 0

5. Monitoring

# Prometheus exporters
# Enable in globals.yml:
enable_prometheus_ceph_mgr_exporter: "yes"

# Preconfigured Grafana dashboards:
# - Ceph Cluster Overview
# - Ceph Pools
# - Ceph OSDs

🐛 Common Troubleshooting

Problem: cinder-volume does not start

# Check the logs
docker logs cinder_volume

# Common error: "No secret with matching uuid"
# Fix: make sure cinder_rbd_secret_uuid is registered in libvirt
# On every compute node:
docker exec nova_libvirt virsh secret-list

# If it is missing, recreate it:
kolla-ansible -i /etc/kolla/multinode reconfigure --tags nova

Problem: Glance cannot upload images

# Check the keyring permissions
docker exec glance_api ls -la /etc/ceph/
docker exec glance_api cat /etc/ceph/ceph.client.glance.keyring

# Check connectivity to Ceph
docker exec glance_api ceph -s --id glance

Problem: Instances fail to boot

# Check the Nova logs
docker logs nova_compute

# Common error: "Permission denied on RBD"
# Make sure the cinder user has permissions on the vms pool
ceph auth get client.cinder

# It must include:
# osd 'profile rbd pool=vms'

Problem: Poor performance

# Check Ceph latency
ceph osd perf

# Check that PGs are balanced
ceph pg dump pgs | awk '{print $1, $15}'

# Tune the RBD cache
# Show the current settings:
docker exec nova_compute cat /etc/nova/nova.conf | grep rbd_cache

📊 Reference Metrics

Expected Performance (SSD)

  • IOPS (4K random read): 20,000+ per OSD
  • Throughput (sequential read): 500+ MB/s per OSD
  • Latency (avg): <1ms for reads, <5ms for writes

Example Sizing

Environment VMs Volumes OSDs Raw Capacity Ceph RAM
Small 50 100 6 12 TB 48 GB
Medium 200 500 15 45 TB 120 GB
Large 1000 3000 30 120 TB 240 GB

📚 References

🎓 Next Steps

  1. Optimization: See Ceph Tuning
  2. Troubleshooting: See Common Ceph Problems
  3. Day-2 Operations: See Advanced Operations

Integration Complete

If you made it this far, you have a fully working OpenStack + Ceph setup! 🎉

Performance Tuning

For heavy workloads, review the settings in Ceph Tuning.