Summary

This is the starting point for future ceph labs or test. It is designed with a mixture of drive sizes to allow for different labs and scenarios.

It should take around an hour to build from scratch using the quick setup scripts

Setup VMs

There will be 13 VMs set up and 2 networks. The Detailed Setup shows the full setup on 1 OSD node. Using the Quick Setup script will create the environment from scratch.

Base System Requirements (Does not include optional nodes)

  • CPU >= 44
  • Memory >= 82GB
  • Disk >= 580GB
HostRoleCountvCPUMemoryDisk SizeOSD DisksOSD Disk SizeOptional
bastionbastion122GB40GB0No
grafanagrafana124GB40GB0No
monitormon/mgr344GB40GB0No
t1-osdosd448GB40GB45GBNo
t2-osdosd448GB40GB410GBNo
rgwrgw224GB40GB0Yes
mdsmds248GB40GB0Yes
iscsiiscsi248GB40GB0Yes

Detailed Setup

Create Networks

  • Ceph presentation network

<network>
  <name>ceph-presentation</name>
  <bridge name="virbr3300"/>
  <forward mode="nat"/>
  <domain name="ceph.lab"/>
  <ip address="10.44.20.1" netmask="255.255.255.0">
    <dhcp>
      <range start="10.44.20.200" end="10.44.20.210"/>
    </dhcp>
  </ip>
</network>
  • Ceph replication network

<network>
  <name>ceph-replication</name>
  <bridge name="virbr3301"/>
  <ip address="172.16.20.1" netmask="255.255.255.0">
    <dhcp>
      <range start="172.16.20.200" end="172.16.20.210"/>
    </dhcp>
  </ip>
</network>
  • Create these in libvirt


virsh net-define ceph-presentation.xml 

virsh net-start ceph-presentation

virsh net-autostart ceph-presentation 

virsh net-define ceph-replication.xml 

virsh net-start ceph-replication 

virsh net-autostart ceph-replication 

Create VM Example

This will create an OSD node. For other nodes, there wont be the need for as many drives to be created.

  • Create the OS drive for the node

qemu-img create -f qcow2 /var/lib/libvirt/images/ceph-osd-t1-node01.qcow2 40G
  • Expand the OS base image into the drive. For this setup it will be using CentOS 7

virt-resize --expand /dev/sda1 /var/lib/libvirt/images/iso/CentOS-7-x86_64-GenericCloud.qcow2 /var/lib/libvirt/images/ceph-osd-t1-node01.qcow2
  • Customise the OS so it can be used

virt-customize -a /var/lib/libvirt/images/ceph-osd-t1-node01.qcow2 \
  --root-password password:password \
  --uninstall cloud-init \
  --hostname ceph-osd-t1-node01 \
  --ssh-inject root:file:/root/.ssh/id_ed25519.pub \
  --selinux-relabel
  • Create the 4 OSD drives (5GB for t1 nodes)

for i in `seq -w 01 04`; do 
  qemu-img create -f qcow2 /var/lib/libvirt/images/ceph-osd-t1-node01-osd$i.qcow2 40G;
done
  • Define the VM, with both networks and all drives attached. Remove --dry-run and --print-xml in order to create the domain.

virt-install --name ceph-osd-t1-node01.ceph.lab \
  --virt-type kvm \
  --memory 16384 \
  --vcpus 4 \
  --boot hd,menu=on \
  --disk path=/var/lib/libvirt/images/ceph-osd-t1-node01.qcow2,device=disk \
  --disk path=/var/lib/libvirt/images/ceph-osd-t1-node01-osd01.qcow2,device=disk \
  --disk path=/var/lib/libvirt/images/ceph-osd-t1-node01-osd02.qcow2,device=disk \
  --disk path=/var/lib/libvirt/images/ceph-osd-t1-node01-osd03.qcow2,device=disk \
  --disk path=/var/lib/libvirt/images/ceph-osd-t1-node01-osd04.qcow2,device=disk \
  --os-type Linux \
  --os-variant centos7 \
  --network network:ceph-presentation \
  --network network:ceph-replication \
  --graphics spice \
  --noautoconsole \
  --dry-run \
  --print-xml

Quick Setup

Script options are set as variables. By default it won’t build any of the optional nodes. If the vars are set to yes this will be build them. It is somewhat idempotent as well. A teardown script is also available to clean this all up.

The nodes built by the script (including optional)

HostnamePublic IPReplication IP
bastion.ceph.labDHCPNone
grafana.ceph.labDHCPNone
ceph-mon01.ceph.lab10.44.20.21172.16.20.21
ceph-mon02.ceph.lab10.44.20.22172.16.20.22
ceph-mon03.ceph.lab10.44.20.23172.16.20.23
ceph-t1-osd01.ceph.lab10.44.20.31172.16.20.31
ceph-t1-osd02.ceph.lab10.44.20.32172.16.20.32
ceph-t1-osd03.ceph.lab10.44.20.33172.16.20.33
ceph-t1-osd04.ceph.lab10.44.20.34172.16.20.34
ceph-t2-osd01.ceph.lab10.44.20.41172.16.20.41
ceph-t2-osd02.ceph.lab10.44.20.42172.16.20.42
ceph-t2-osd03.ceph.lab10.44.20.43172.16.20.43
ceph-t2-osd04.ceph.lab10.44.20.44172.16.20.44
ceph-rgw01.ceph.lab10.44.20.111None
ceph-rgw02.ceph.lab10.44.20.112None
ceph-mds01.ceph.lab10.44.20.121None
ceph-mds02.ceph.lab10.44.20.122None
ceph-iscsi01.ceph.lab10.44.20.131None
ceph-iscsi02.ceph.lab10.44.20.132None

Scripts can also be found on GitHub


#!/bin/bash

# Node building vars
image_dir="/var/lib/libvirt/images"
base_os_img="/var/lib/libvirt/images/iso/CentOS-7-x86_64-GenericCloud.qcow2"
ssh_pub_key="/root/.ssh/id_ed25519.pub"

# Network Vars
dns_domain="ceph.lab"

# Extra Vars
root_password="password"
os_drive_size="40G"
tmp_dir="/tmp"

# Ceph Extra nodes 
rgw=no
mds=no
iscsi=no


##### Start #####

# Exit on any failure

set -e

# Create Network files

echo "Creating ceph-presentation xml file"

cat <<EOF > $tmp_dir/ceph-presentation.xml
<network>
  <name>ceph-presentation</name>
  <bridge name="virbr3300"/>
  <forward mode="nat"/>
  <domain name="ceph.lab"/>
  <ip address="10.44.20.1" netmask="255.255.255.0">
    <dhcp>
      <range start="10.44.20.200" end="10.44.20.210"/>
    </dhcp>
  </ip>
</network>
EOF

echo "Creating ceph-replicaton xml file"
cat <<EOF >$tmp_dir/ceph-replication.xml
<network>
  <name>ceph-replication</name>
  <bridge name="virbr3301"/>
  <ip address="172.16.20.1" netmask="255.255.255.0">
    <dhcp>
      <range start="172.16.20.200" end="172.16.20.210"/>
    </dhcp>
  </ip>
</network>
EOF

echo "Creating Ceph networks in libvirt"

check_rep=$(virsh net-list --all | grep ceph-replication >/dev/null && echo "0" || echo "1")
check_pres=$(virsh net-list --all | grep ceph-presentation >/dev/null && echo "0" || echo "1")

networks=()

if [[ $check_rep == "1" ]]; then
  networks+=("ceph-replication")
fi

if [[ $check_pres == "1" ]]; then
  networks+=("ceph-presentation")
fi

net_len=$(echo "${#networks[@]}")

echo "Creating networks ${#networks[@]}"

if [ "$net_len" -ge 1 ]; then
  for network in ${networks[@]}; do 
    virsh net-define $tmp_dir/$network.xml
    virsh net-start $network
    virsh net-autostart $network
  done
fi

# Check OS image exists

if [ -f "$base_os_img" ]; then
  echo "Base OS image exists"
else
  echo "Base image doesn't exist ($base_os_img). Exiting"
  exit 1
fi

# Build OS drives for machines

echo "Starting build of VMs"

echo "Building Bastion & Grafana drives"

for node in bastion grafana; do 
  check=$(virsh list --all | grep $node.$dns_domain > /dev/null && echo "0" || echo "1" )
  if [[ $check == "0" ]]; then
    echo "$node.$dns_domain exists"
  else
    echo "Starting $node"
    echo "Creating $image_dir/$node.$dns_domain.qcow2 at $os_drive_size"
    qemu-img create -f qcow2 $image_dir/$node.$dns_domain.qcow2 $os_drive_size
    echo "Resizing base OS image"
    virt-resize --expand /dev/sda1 $base_os_img $image_dir/$node.$dns_domain.qcow2
    echo "Customising OS for $node"
    virt-customize -a $image_dir/$node.$dns_domain.qcow2 \
      --root-password password:$root_password \
      --uninstall cloud-init \
      --hostname $node.$dns_domain \
      --ssh-inject root:file:$ssh_pub_key \
      --selinux-relabel
  fi
done

check=$(virsh list --all | grep bastion.$dns_domain > /dev/null && echo "0" || echo "1" )
if [[ $check == "1" ]]; then
  echo "Defining Bastion VM"
  virt-install --name bastion.$dns_domain \
    --virt-type kvm \
    --memory 2048 \
    --vcpus 2 \
    --boot hd,menu=on \
    --disk path=$image_dir/bastion.$dns_domain.qcow2,device=disk \
    --os-type Linux \
    --os-variant centos7 \
    --network network:ceph-presentation \
    --graphics spice \
    --noautoconsole
fi

check=$(virsh list --all | grep grafana.$dns_domain > /dev/null && echo "0" || echo "1" )
if [[ $check == "1" ]]; then
  echo "Defining Grafana VM"
  virt-install --name grafana.$dns_domain \
    --virt-type kvm \
    --memory 4096 \
    --vcpus 2 \
    --boot hd,menu=on \
    --disk path=$image_dir/grafana.$dns_domain.qcow2,device=disk \
    --os-type Linux \
    --os-variant centos7 \
    --network network:ceph-presentation \
    --graphics spice \
    --noautoconsole
fi

echo "Building Monitor VMs"

count=1

for mon in `seq -w 01 03`; do 
  check=$(virsh list --all | grep ceph-mon$mon.$dns_domain > /dev/null && echo "0" || echo "1" )
  if [[ $check == "0" ]]; then
    echo "ceph-mon$mon.$dns_domain already exists"
    count=$(( $count + 1 ))
  else
    echo "Creating eth0 ifcfg file"
    mkdir -p $tmp_dir/ceph-mon$mon
    cat <<EOF > $tmp_dir/ceph-mon$mon/ifcfg-eth0
TYPE=Ethernet
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.44.20.2$count
NETMASK=255.255.255.0
GATEWAY=10.44.20.1
DNS1=10.44.20.1
ONBOOT=yes
DEFROUTE=yes
EOF
    echo "Creating eth1 ifcfg file"
    cat <<EOF > $tmp_dir/ceph-mon$mon/ifcfg-eth1
TYPE=Ethernet
NAME=eth1
DEVICE=eth1
BOOTPROTO=static
IPADDR=172.16.20.2$count
NETMASK=255.255.255.0
EOF
    echo "Starting ceph-mon$mon"
    echo "Creating $image_dir/ceph-mon$mon.$dns_domain.qcow2 at $os_drive_size"
    qemu-img create -f qcow2 $image_dir/ceph-mon$mon.$dns_domain.qcow2 $os_drive_size
    echo "Resizing base OS image"
    virt-resize --expand /dev/sda1 $base_os_img $image_dir/ceph-mon$mon.$dns_domain.qcow2
    echo "Customising OS for ceph-mon$mon"
    virt-customize -a $image_dir/ceph-mon$mon.$dns_domain.qcow2 \
      --root-password password:$root_password \
      --uninstall cloud-init \
      --hostname ceph-mon$mon \
      --ssh-inject root:file:$ssh_pub_key \
      --copy-in $tmp_dir/ceph-mon$mon/ifcfg-eth0:/etc/sysconfig/network-scripts/ \
      --copy-in $tmp_dir/ceph-mon$mon/ifcfg-eth1:/etc/sysconfig/network-scripts/ \
      --selinux-relabel
    echo "Defining ceph-mon$mon.$dns_domain"
    virt-install --name ceph-mon$mon.$dns_domain \
      --virt-type kvm \
      --memory 4096 \
      --vcpus 4 \
      --boot hd,menu=on \
      --disk path=$image_dir/ceph-mon$mon.$dns_domain.qcow2,device=disk \
      --os-type Linux \
      --os-variant centos7 \
      --network network:ceph-presentation \
      --network network:ceph-replication \
      --graphics spice \
      --noautoconsole
    count=$(( $count + 1 ))
  fi
done

echo "Building OSD T1 drives"

count=1

for i in `seq -w 01 04`; do 
  check=$(virsh list --all | grep ceph-t1-osd$i.$dns_domain > /dev/null && echo "0" || echo "1" )
  if [[ $check == "0" ]]; then
    echo "ceph-t1-osd$i.$dns_domain already exists"
    count=$(( $count + 1 ))
  else
    echo "Creating eth0 ifcfg file"
    mkdir -p $tmp_dir/ceph-t1-osd$i
    cat <<EOF > $tmp_dir/ceph-t1-osd$i/ifcfg-eth0
TYPE=Ethernet
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.44.20.3$count
NETMASK=255.255.255.0
GATEWAY=10.44.20.1
DNS1=10.44.20.1
ONBOOT=yes
DEFROUTE=yes
EOF
    echo "Creating eth1 ifcfg file"
    cat <<EOF > $tmp_dir/ceph-t1-osd$i/ifcfg-eth1
TYPE=Ethernet
NAME=eth1
DEVICE=eth1
BOOTPROTO=static
IPADDR=172.16.20.3$count
NETMASK=255.255.255.0
EOF
    echo "Starting ceph-t1-osd$i"
    echo "Creating $image_dir/ceph-t1-osd$i.$dns_domain.qcow2 at $os_drive_size"
    qemu-img create -f qcow2 $image_dir/ceph-t1-osd$i.$dns_domain.qcow2 $os_drive_size
    for c in {1..4}; do 
      qemu-img create -f qcow2 $image_dir/ceph-t1-osd$i-disk$c.$dns_domain.qcow2 5G
    done
    echo "Resizing base OS image"
    virt-resize --expand /dev/sda1 $base_os_img $image_dir/ceph-t1-osd$i.$dns_domain.qcow2
    echo "Customising OS for ceph-t1-osd$i"
    virt-customize -a $image_dir/ceph-t1-osd$i.$dns_domain.qcow2 \
      --root-password password:$root_password \
      --uninstall cloud-init \
      --hostname ceph-t1-osd$i \
      --ssh-inject root:file:$ssh_pub_key \
      --copy-in $tmp_dir/ceph-t1-osd$i/ifcfg-eth0:/etc/sysconfig/network-scripts/ \
      --copy-in $tmp_dir/ceph-t1-osd$i/ifcfg-eth1:/etc/sysconfig/network-scripts/ \
      --selinux-relabel
    echo "Defining ceph-t1-osd$i"
    virt-install --name ceph-t1-osd$i.$dns_domain \
      --virt-type kvm \
      --memory 8192 \
      --vcpus 4 \
      --boot hd,menu=on \
      --disk path=$image_dir/ceph-t1-osd$i.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t1-osd$i-disk1.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t1-osd$i-disk2.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t1-osd$i-disk3.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t1-osd$i-disk4.$dns_domain.qcow2,device=disk \
      --os-type Linux \
      --os-variant centos7 \
      --network network:ceph-presentation \
      --network network:ceph-replication \
      --graphics spice \
      --noautoconsole
    
    count=$(( $count + 1 ))
  fi
done

echo "Building OSD T2 drives"

count=1

for i in `seq -w 01 04`; do 
  check=$(virsh list --all | grep ceph-t2-osd$i.$dns_domain > /dev/null && echo "0" || echo "1" )
  if [[ $check == "0" ]]; then
    echo "ceph-t2-osd$i.$dns_domain already exists"
    count=$(( $count + 1 ))
  else
    echo "Creating eth0 ifcfg file"
    mkdir -p $tmp_dir/ceph-t2-osd$i
    cat <<EOF > $tmp_dir/ceph-t2-osd$i/ifcfg-eth0
TYPE=Ethernet
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.44.20.4$count
NETMASK=255.255.255.0
GATEWAY=10.44.20.1
DNS1=10.44.20.1
ONBOOT=yes
DEFROUTE=yes
EOF
    echo "Creating eth1 ifcfg file"
    cat <<EOF > $tmp_dir/ceph-t2-osd$i/ifcfg-eth1
TYPE=Ethernet
NAME=eth1
DEVICE=eth1
BOOTPROTO=static
IPADDR=172.16.20.4$count
NETMASK=255.255.255.0
EOF
    echo "Starting ceph-t2-osd$i"
    echo "Creating $image_dir/ceph-t2-osd$i.$dns_domain.qcow2 at $os_drive_size"
    qemu-img create -f qcow2 $image_dir/ceph-t2-osd$i.$dns_domain.qcow2 $os_drive_size
    for c in {1..4}; do
      qemu-img create -f qcow2 $image_dir/ceph-t2-osd$i-disk$c.$dns_domain.qcow2 10G
    done
    echo "Resizing base OS image"
    virt-resize --expand /dev/sda1 $base_os_img $image_dir/ceph-t2-osd$i.$dns_domain.qcow2
    echo "Customising OS for ceph-t2-osd$i"
    virt-customize -a $image_dir/ceph-t2-osd$i.$dns_domain.qcow2 \
      --root-password password:$root_password \
      --uninstall cloud-init \
      --hostname ceph-t2-osd$i \
      --ssh-inject root:file:$ssh_pub_key \
      --copy-in $tmp_dir/ceph-t2-osd$i/ifcfg-eth0:/etc/sysconfig/network-scripts/ \
      --copy-in $tmp_dir/ceph-t2-osd$i/ifcfg-eth1:/etc/sysconfig/network-scripts/ \
      --selinux-relabel
  
    echo "Defining ceph-t2-osd$i"
    virt-install --name ceph-t2-osd$i.$dns_domain \
      --virt-type kvm \
      --memory 8192 \
      --vcpus 4 \
      --boot hd,menu=on \
      --disk path=$image_dir/ceph-t2-osd$i.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t2-osd$i-disk1.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t2-osd$i-disk2.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t2-osd$i-disk3.$dns_domain.qcow2,device=disk \
      --disk path=$image_dir/ceph-t2-osd$i-disk4.$dns_domain.qcow2,device=disk \
      --os-type Linux \
      --os-variant centos7 \
      --network network:ceph-presentation \
      --network network:ceph-replication \
      --graphics spice \
      --noautoconsole
    count=$(( $count + 1 ))
  fi
done

## Build extra ceph nodes if defines

# If rgw is "yes"

if [[ $rgw == "yes" ]]; then
  count=1
  for rgw in `seq -w 01 02`; do 
    check=$(virsh list --all | grep ceph-rgw$rgw.$dns_domain > /dev/null && echo "0" || echo "1" )
    if [[ $check == "0" ]]; then
      echo "ceph-rgw$rgw.$dns_domain already exists"
      count=$(( $count + 1 ))
    else
      echo "Creating eth0 ifcfg file"
      mkdir -p $tmp_dir/ceph-rgw$rgw
      cat <<EOF > $tmp_dir/ceph-rgw$rgw/ifcfg-eth0
TYPE=Ethernet
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.44.20.11$count
NETMASK=255.255.255.0
GATEWAY=10.44.20.1
DNS1=10.44.20.1
ONBOOT=yes
DEFROUTE=yes
EOF
      echo "Starting ceph-rgw$rgw"
      echo "Creating $image_dir/ceph-rgw$rgw.$dns_domain.qcow2 at $os_drive_size"
      qemu-img create -f qcow2 $image_dir/ceph-rgw$rgw.$dns_domain.qcow2 $os_drive_size
      echo "Resizing base OS image"
      virt-resize --expand /dev/sda1 $base_os_img $image_dir/ceph-rgw$rgw.$dns_domain.qcow2
      echo "Customising OS for ceph-rgw$rgw"
      virt-customize -a $image_dir/ceph-rgw$rgw.$dns_domain.qcow2 \
        --root-password password:$root_password \
        --uninstall cloud-init \
        --hostname ceph-rgw$rgw \
        --ssh-inject root:file:$ssh_pub_key \
        --copy-in $tmp_dir/ceph-rgw$rgw/ifcfg-eth0:/etc/sysconfig/network-scripts/ \
        --selinux-relabel
      echo "Defining ceph-rgw$rgw.$dns_domain"
      virt-install --name ceph-rgw$rgw.$dns_domain \
        --virt-type kvm \
        --memory 4096 \
        --vcpus 2 \
        --boot hd,menu=on \
        --disk path=$image_dir/ceph-rgw$rgw.$dns_domain.qcow2,device=disk \
        --os-type Linux \
        --os-variant centos7 \
        --network network:ceph-presentation \
        --graphics spice \
        --noautoconsole
      count=$(( $count + 1 ))
    fi
  done
fi

# If mds set to "yes"

if [[ $mds == "yes" ]]; then
  count=1
  for mds in `seq -w 01 02`; do
    check=$(virsh list --all | grep ceph-mds$mds.$dns_domain > /dev/null && echo "0" || echo "1" )
    if [[ $check == "0" ]]; then
      echo "ceph-mds$mds.$dns_domain already exists"
      count=$(( $count + 1 ))
    else
      echo "Creating eth0 ifcfg file"
      mkdir -p $tmp_dir/ceph-mds$mds
      cat <<EOF > $tmp_dir/ceph-mds$mds/ifcfg-eth0
TYPE=Ethernet
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.44.20.12$count
NETMASK=255.255.255.0
GATEWAY=10.44.20.1
DNS1=10.44.20.1
ONBOOT=yes
DEFROUTE=yes
EOF
      echo "Starting ceph-mds$mds"
      echo "Creating $image_dir/ceph-mds$mds.$dns_domain.qcow2 at $os_drive_size"
      qemu-img create -f qcow2 $image_dir/ceph-mds$mds.$dns_domain.qcow2 $os_drive_size
      echo "Resizing base OS image"
      virt-resize --expand /dev/sda1 $base_os_img $image_dir/ceph-mds$mds.$dns_domain.qcow2
      echo "Customising OS for ceph-mds$mds"
      virt-customize -a $image_dir/ceph-mds$mds.$dns_domain.qcow2 \
        --root-password password:$root_password \
        --uninstall cloud-init \
        --hostname ceph-mds$mds \
        --ssh-inject root:file:$ssh_pub_key \
        --copy-in $tmp_dir/ceph-mds$mds/ifcfg-eth0:/etc/sysconfig/network-scripts/ \
        --selinux-relabel
      echo "Defining ceph-mds$mds.$dns_domain"
      virt-install --name ceph-mds$mds.$dns_domain \
        --virt-type kvm \
        --memory 8192 \
        --vcpus 4 \
        --boot hd,menu=on \
        --disk path=$image_dir/ceph-mds$mds.$dns_domain.qcow2,device=disk \
        --os-type Linux \
        --os-variant centos7 \
        --network network:ceph-presentation \
        --graphics spice \
        --noautoconsole
      count=$(( $count + 1 ))
    fi
  done
fi

# If iscsi set to "yes"

if [[ $iscsi == "yes" ]]; then
  count=1
  for iscsi in `seq -w 01 02`; do
    check=$(virsh list --all | grep ceph-iscsi$iscsi.$dns_domain > /dev/null && echo "0" || echo "1" )
    if [[ $check == "0" ]]; then
      echo "ceph-iscsi$iscsi.$dns_domain already exists"
      count=$(( $count + 1 ))
    else
      echo "Creating eth0 ifcfg file"
      mkdir -p $tmp_dir/ceph-iscsi$iscsi
      cat <<EOF > $tmp_dir/ceph-iscsi$iscsi/ifcfg-eth0
TYPE=Ethernet
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.44.20.13$count
NETMASK=255.255.255.0
GATEWAY=10.44.20.1
DNS1=10.44.20.1
ONBOOT=yes
DEFROUTE=yes
EOF
      echo "Starting ceph-iscsi$iscsi"
      echo "Creating $image_dir/ceph-iscsi$iscsi.$dns_domain.qcow2 at $os_drive_size"
      qemu-img create -f qcow2 $image_dir/ceph-iscsi$iscsi.$dns_domain.qcow2 $os_drive_size
      echo "Resizing base OS image"
      virt-resize --expand /dev/sda1 $base_os_img $image_dir/ceph-iscsi$iscsi.$dns_domain.qcow2
      echo "Customising OS for ceph-iscsi$iscsi"
      virt-customize -a $image_dir/ceph-iscsi$iscsi.$dns_domain.qcow2 \
        --root-password password:$root_password \
        --uninstall cloud-init \
        --hostname ceph-iscsi$iscsi \
        --ssh-inject root:file:$ssh_pub_key \
        --copy-in $tmp_dir/ceph-iscsi$iscsi/ifcfg-eth0:/etc/sysconfig/network-scripts/ \
        --selinux-relabel
      echo "Defining ceph-iscsi$iscsi.$dns_domain"
      virt-install --name ceph-iscsi$iscsi.$dns_domain \
        --virt-type kvm \
        --memory 8192 \
        --vcpus 4 \
        --boot hd,menu=on \
        --disk path=$image_dir/ceph-iscsi$iscsi.$dns_domain.qcow2,device=disk \
        --os-type Linux \
        --os-variant centos7 \
        --network network:ceph-presentation \
        --graphics spice \
        --noautoconsole
      count=$(( $count + 1 ))
    fi
  done
fi

# Print running VMs

virsh list

Demo

Adding More Disks

If there’s a capacity or a need to add some more drives to the OSD nodes this example will add more drives to the OSD VMs

{f..g} will add 2 more drives to dev/vdf and /def/vdg. Change this to add more.



path=/var/lib/libvirt/images/
size=50G
server=$(virsh list | grep osd | awk '{print $2}')

for s in $server; do \
   d=1; \
  for l in {f..g}; do \
  qemu-img create -f qcow2 $path/$s-extradisk$d.qcow2 $size; \
  virsh attach-disk $s $path/$s-extradisk$d.qcow2 vd$l --subdriver qcow2 --persistent; \
  d=$(( $d + 1 )); \
  done; \
done

Cleanup

Cleanup bash script to remove all the parts of the Ceph lab

Scripts can also be found on GitHub



#!/bin/bash

# Node building vars
image_dir="/var/lib/libvirt/images"
base_os_img="/var/lib/libvirt/images/iso/CentOS-7-x86_64-GenericCloud.qcow2"
ssh_pub_key="/root/.ssh/id_ed25519.pub"

# Network Vars
dns_domain="ceph.lab"

# Extra Vars
root_password="password"
os_drive_size="40G"
tmp_dir="/tmp"


##### Start #####

# Destroy & Undefine all nodes

echo "Destroy & Undefine all nodes"
virsh destroy bastion.$dns_domain
virsh destroy grafana.$dns_domain
virsh undefine bastion.$dns_domain --remove-all-storage
virsh undefine grafana.$dns_domain --remove-all-storage

echo "Removing Monitor nodes"
for mon in `seq -w 01 03`; do
  virsh destroy ceph-mon$mon.$dns_domain
  virsh undefine ceph-mon$mon.$dns_domain --remove-all-storage
done

echo "Removing OSD nodes"
for i in `seq -w 01 04`; do
  virsh destroy ceph-t1-osd$i.$dns_domain
  virsh destroy ceph-t2-osd$i.$dns_domain
  virsh undefine ceph-t1-osd$i.$dns_domain --remove-all-storage
  virsh undefine ceph-t2-osd$i.$dns_domain --remove-all-storage
done

echo "Removing other nodes"
for i in `seq -w 01 02`; do
  virsh destroy ceph-rgw$i.$dns_domain
  virsh destroy ceph-mds$i.$dns_domain
  virsh destroy ceph-iscsi$i.$dns_domain
  virsh undefine ceph-rgw$i.$dns_domain --remove-all-storage
  virsh undefine ceph-mds$i.$dns_domain --remove-all-storage
  virsh undefine ceph-iscsi$i.$dns_domain --remove-all-storage
done

# Remove ifcfg files

echo "Removing monitor ifcfg files"
for mon in `seq -w 01 03`; do
  rm $tmp_dir/ceph-mon$mon -rf
  rm $tmp_dir/ceph-mds$mon -rf
  rm $tmp_dir/ceph-iscsi$mon -rf
  rm $tmp_dir/ceph-rgw$mon -rf
done

echo "Removing OSD ifcfg files"
for t in 1 2; do
  for i in `seq -w 01 04`; do
    rm $tmp_dir/ceph-t$t-osd$i -rf
  done
done

# Remove Network files

echo "Removing ceph-presentation xml file"

rm $tmp_dir/ceph-presentation.xml -rf

echo "Removing ceph-replicaton xml file"

echo "Removing ceph networks in libvirt"

for network in ceph-presentation ceph-replication; do
  virsh net-destroy $network
  virsh net-undefine $network
done

Ceph Install

Requirements

This guide will use ceph-deploy so requires these steps before starting

  • Chrony or NTP
  • LVM2

Example Ansible inventory file for confirming and setting up requirements.


---

ceph:
  hosts:
    grafana.ceph.lab:
      ansible_host: 10.44.20.16
    ceph-mon01.ceph.lab:
      ansible_host: 10.44.20.21
    ceph-mon02.ceph.lab:
      ansible_host: 10.44.20.22
    ceph-mon03.ceph.lab:
      ansible_host: 10.44.20.23
    ceph-t1-osd01.ceph.lab:
      ansible_host: 10.44.20.31
    ceph-t1-osd02.ceph.lab:
      ansible_host: 10.44.20.32
    ceph-t1-osd03.ceph.lab:
      ansible_host: 10.44.20.33
    ceph-t1-osd04.ceph.lab:
      ansible_host: 10.44.20.34
    ceph-t2-osd01.ceph.lab:
      ansible_host: 10.44.20.41
    ceph-t2-osd02.ceph.lab:
      ansible_host: 10.44.20.42
    ceph-t2-osd03.ceph.lab:
      ansible_host: 10.44.20.43
    ceph-t2-osd04.ceph.lab:
      ansible_host: 10.44.20.44
    ceph-rgw01.ceph.lab:
      ansible_host: 10.44.20.111
    ceph-rgw02.ceph.lab:
      ansible_host: 10.44.20.112
bastion:
  hosts:
    bastion.ceph.lab:
      ansible_host: 10.44.20.90
This playbook will do the below requirement steps. Also available on GitHub

  • Enable and start chronyd service

ansible -i inventory.yaml all -m service -a "name=chronyd state=started enabled=true"
  • Confirm chrony is working

ansible -i inventory.yaml all -m shell -a "chronyc tracking"
  • Install podman

ansible -i inventory.yaml all -m package -a "name=podman state=installed"
  • Ensure python3 is installed

ansible -i inventory.yaml all -m package -a "name=python3 state=installed"

Requirements Playbook


---

- name: Ceph Lab Requirements
  hosts: all                 
  gather_facts: false                                                                                                                                                
  tasks:                                                                          
    - name: Ensure packages are installed 
      package:
        name: "{{item }}"            
        state: installed
      loop:
        - epel-release
        - chrony
        - python3
        - lvm2
        - vim
        - bash-completion
    - name: Start and enable chronyd
      service:
        name: chronyd
        state: started
        enabled: true
    - name: Build hosts file
      lineinfile: 
        dest: /etc/hosts
        regexp: '.*{{ item }}$'
        line: "{{ hostvars[item]['ansible_host'] }} {{item}} {{ hostvars[item]['inventory_hostname_short'] }}"
        state: present
      loop: "{{ groups['all'] }}"

- name: Install ceph-deploy
  hosts: bastion
  gather_facts: false
  tasks:
    - name: Add Ceph repo
      yum_repository:
        name: ceph
        description: "Ceph Nautilus repo" 
        baseurl: "https://download.ceph.com/rpm-nautilus/el7/$basearch"
        enabled: yes
        gpgcheck: yes
        gpgkey: https://download.ceph.com/keys/release.asc
        priority: "2"
    - name: Add Ceph noarch repo
      yum_repository:
        name: ceph-noarch
        description: "Ceph Nautilus noarch repo"
        baseurl: "https://download.ceph.com/rpm-nautilus/el7/noarch"
        enabled: yes
        gpgcheck: yes
        gpgkey: https://download.ceph.com/keys/release.asc
    - name: Install ceph-deploy
      package:
        name: ceph-deploy
        state: installed
    - name: Install ceph common
      package:
        name: ceph-common
        state: installed

Ceph-Deploy

Following steps are run from the bastion.ceph.lab node

This section is described in detail on the Ceph Docs Site

Monitors and Managers

  • Make new ceph directory on the bastion node

mkdir ~/ceph
  • Change to the new ceph directroy

cd ceph
  • Create the cluster

ceph-deploy new ceph-mon01 ceph-mon02 ceph-mon03
  • Add the below lines to ceph.conf

public_network = 10.44.20.0/24
cluster_network = 172.16.20.0/24
  • Install Ceph packages to the monitors

ceph-deploy install --release nautilus ceph-mon01 ceph-mon02 ceph-mon03
  • Deploy the initial monitors

ceph-deploy mon create-initial
  • Deploy the admin keys to the monitors

ceph-deploy admin ceph-mon01 ceph-mon02 ceph-mon03
  • Add the MGR daemon to the first 2 monitors

ceph-deploy mgr create ceph-mon01 ceph-mon02
  • Ensure ceph-common package is installed on the bastion and copy all config to /etc/ceph/

cp /root/ceph/* /etc/ceph
  • Check ceph commands work from the bastion node

ceph -s
  • Example output
    
      cluster:
        id:     8c010781-6ede-46d2-84c8-2736c18eb382
        health: HEALTH_WARN
                OSD count 0 < osd_pool_default_size 3
     
      services:
        mon: 3 daemons, quorum ceph-mon01,ceph-mon02,ceph-mon03 (age 4m)
        mgr: ceph-mon01(active, since 3m), standbys: ceph-mon02
        osd: 0 osds: 0 up, 0 in
     
      data:
        pools:   0 pools, 0 pgs
        objects: 0 objects, 0 B
        usage:   0 B used, 0 B / 0 B avail
        pgs:
    

OSDs

  • Install ceph packages on all the OSD nodes

ceph-deploy install --release nautilus \
  ceph-t1-osd01 \
  ceph-t1-osd02 \
  ceph-t1-osd03 \
  ceph-t1-osd04 \
  ceph-t2-osd01 \
  ceph-t2-osd02 \
  ceph-t2-osd03 \
  ceph-t2-osd04
  • Create OSDs on the available drives. In this example /dev/vdb -> /dev/vde are available for OSDs

for n in ceph-t{1..2}-osd{01..04}; do 
  for d in {b..e}; do
    ceph-deploy osd create --data /dev/vd$d $n;
  done
done
  • Once this has completed, check that all OSDs have been registered in the cluster

ceph -s
  • Example output
    
      cluster:
        id:     8c010781-6ede-46d2-84c8-2736c18eb382
        health: HEALTH_OK
     
      services:
        mon: 3 daemons, quorum ceph-mon01,ceph-mon02,ceph-mon03 (age 40m)
        mgr: ceph-mon01(active, since 39m), standbys: ceph-mon02
        osd: 32 osds: 32 up (since 37s), 32 in (since 37s)
     
      data:
        pools:   0 pools, 0 pgs
        objects: 0 objects, 0 B
        usage:   33 GiB used, 207 GiB / 240 GiB avail
        pgs:
    

Add Rados Gateway

For other daemon types, Ceph docs details how to configure them.

  • Install Ceph on the RGW nodes

ceph-deploy install --release nautilus ceph-rgw01 ceph-rgw02
  • Add the below lines to ceph.conf to deploy RGWs with Beast as the front end

[client.rgw.ceph-rgw01]
rgw frontends = "beast port=80"

[client.rgw.ceph-rgw02]
rgw frontends = "beast port=80"
  • Deploy the RadosGW daemons

ceph-deploy rgw create ceph-rgw01 ceph-rgw02
  • Now there should be 4 new pools created for the RGW objects

ceph df
  • Example output
    
    RAW STORAGE:
        CLASS     SIZE        AVAIL       USED        RAW USED     %RAW USED 
        hdd       240 GiB     207 GiB     559 MiB       33 GiB         13.57 
        TOTAL     240 GiB     207 GiB     559 MiB       33 GiB         13.57 
     
    POOLS:
        POOL                    ID     PGS     STORED      OBJECTS     USED        %USED     MAX AVAIL 
        .rgw.root                1      32     1.2 KiB           4     768 KiB         0        60 GiB 
        default.rgw.control      2      32         0 B           8         0 B         0        60 GiB 
        default.rgw.meta         3      32         0 B           0         0 B         0        60 GiB 
        default.rgw.log          4      32         0 B         175         0 B         0        60 GiB
    

Ceph Dashboard

These steps outline how to enable the ceph dashboard

  • Install the Ceph Dashboard package on the monitor nodes

ansible -i inventory.yaml ceph-mon* -m package -a "name=ceph-mgr-dashboard state=installed"
  • Enable the Dashboard module

ceph mgr module enable dashboard
  • Disable SSL on the Dashboard

ceph config set mgr mgr/dashboard/ssl false
  • Setup the admin user and password for the dashboard

ceph dashboard ac-user-create admin password administrator

Access to the dashboard should now be availabe at http://10.44.20.21:8080 using the above crentials

Add RGW Management to Dashboard

  • Create an admin RGW user

radosgw-admin user create --uid=dashboard --display-name=dashboard --system
  • Grab the access and secret key for this user

radosgw-admin user info --uid=dashboard
  • Configure the dashboard to use these keys

ceph dashboard set-rgw-api-access-key <access_key>
ceph dashboard set-rgw-api-secret-key <secret_key>

Add a User to Rados Gateway

Object Gateway users can either be created via the dashboard, assuming this has been configured or via the CLI

  • To add a user to the Object gateway

radosgw-admin user create --uid=test --display-name=test

Conclusion

At this point there should be a running ceph cluster at release Nautilus with, optionally 2 Rados Gateway nodes and the associated pools.

Cleanup

There is a teardown.sh script in the Github repo which will remove all the VMs and their storage volumes.