Recently a lot of customers have been asking about how to configure storage on a Single Node OpenShift (SNO) deployment. When dealing with a compact or multi-node OpenShift deployment I myself have always relied on using OpenShift Data Foundation (ODF) as the underpinning of my storage requirements after all it provides the ability to do block, object and file all from the same deployment. However in a SNO deployment ODF does not seem to be an option due to the way the operator has been designed. However there is a way to at least get some resemblance to ODF without a lot of hassle in a SNO environment. The following blog demonstrates a non-supported way on how I go about getting the dynamic block storage I need in my SNO cluster using Rook.
$ oc get nodes NAME STATUS ROLES AGE VERSION master-0.sno3.schmaustech.com Ready master,worker 3h v1.23.5+9ce5071
$ oc debug node/master-0.sno3.schmaustech.com Starting pod/master-0sno3schmaustechcom-debug ... To use host binaries, run `chroot /host` Pod IP: 192.168.0.206 If you don't see a command prompt, try pressing enter. sh-4.4# chroot /host sh-4.4# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 120G 0 disk |-sda1 8:1 0 1M 0 part |-sda2 8:2 0 127M 0 part |-sda3 8:3 0 384M 0 part /boot `-sda4 8:4 0 119.5G 0 part /sysroot sdb 8:16 0 160G 0 disk sr0 11:0 1 999M 0 rom more
$ oc create -f https://raw.githubusercontent.com/rook/rook/master/deploy/examples/crds.yaml customresourcedefinition.apiextensions.k8s.io/cephblockpoolradosnamespaces.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephblockpools.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephbucketnotifications.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephbuckettopics.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephclients.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephclusters.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephfilesystemmirrors.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephfilesystems.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephfilesystemsubvolumegroups.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephnfses.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephobjectrealms.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephobjectstores.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephobjectstoreusers.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephobjectzonegroups.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephobjectzones.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/cephrbdmirrors.ceph.rook.io created customresourcedefinition.apiextensions.k8s.io/objectbucketclaims.objectbucket.io created customresourcedefinition.apiextensions.k8s.io/objectbuckets.objectbucket.io created
$ oc create -f https://raw.githubusercontent.com/rook/rook/master/deploy/examples/common.yaml namespace/rook-ceph created clusterrole.rbac.authorization.k8s.io/cephfs-csi-nodeplugin created clusterrole.rbac.authorization.k8s.io/cephfs-external-provisioner-runner created clusterrole.rbac.authorization.k8s.io/psp:rook created clusterrole.rbac.authorization.k8s.io/rbd-csi-nodeplugin created clusterrole.rbac.authorization.k8s.io/rbd-external-provisioner-runner created clusterrole.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt created clusterrole.rbac.authorization.k8s.io/rook-ceph-global created clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-cluster created clusterrole.rbac.authorization.k8s.io/rook-ceph-mgr-system created clusterrole.rbac.authorization.k8s.io/rook-ceph-object-bucket created clusterrole.rbac.authorization.k8s.io/rook-ceph-osd created clusterrole.rbac.authorization.k8s.io/rook-ceph-system created clusterrolebinding.rbac.authorization.k8s.io/cephfs-csi-nodeplugin created clusterrolebinding.rbac.authorization.k8s.io/cephfs-csi-provisioner-role created clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-nodeplugin created clusterrolebinding.rbac.authorization.k8s.io/rbd-csi-provisioner-role created clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-global created clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-cluster created clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-object-bucket created clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-osd created clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-system created clusterrolebinding.rbac.authorization.k8s.io/rook-ceph-system-psp created clusterrolebinding.rbac.authorization.k8s.io/rook-csi-cephfs-plugin-sa-psp created clusterrolebinding.rbac.authorization.k8s.io/rook-csi-cephfs-provisioner-sa-psp created clusterrolebinding.rbac.authorization.k8s.io/rook-csi-rbd-plugin-sa-psp created clusterrolebinding.rbac.authorization.k8s.io/rook-csi-rbd-provisioner-sa-psp created Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+ podsecuritypolicy.policy/00-rook-privileged created role.rbac.authorization.k8s.io/cephfs-external-provisioner-cfg created role.rbac.authorization.k8s.io/rbd-csi-nodeplugin created role.rbac.authorization.k8s.io/rbd-external-provisioner-cfg created role.rbac.authorization.k8s.io/rook-ceph-cmd-reporter created role.rbac.authorization.k8s.io/rook-ceph-mgr created role.rbac.authorization.k8s.io/rook-ceph-osd created role.rbac.authorization.k8s.io/rook-ceph-purge-osd created role.rbac.authorization.k8s.io/rook-ceph-rgw created role.rbac.authorization.k8s.io/rook-ceph-system created rolebinding.rbac.authorization.k8s.io/cephfs-csi-provisioner-role-cfg created rolebinding.rbac.authorization.k8s.io/rbd-csi-nodeplugin-role-cfg created rolebinding.rbac.authorization.k8s.io/rbd-csi-provisioner-role-cfg created rolebinding.rbac.authorization.k8s.io/rook-ceph-cluster-mgmt created rolebinding.rbac.authorization.k8s.io/rook-ceph-cmd-reporter created rolebinding.rbac.authorization.k8s.io/rook-ceph-cmd-reporter-psp created rolebinding.rbac.authorization.k8s.io/rook-ceph-default-psp created rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr created rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-psp created rolebinding.rbac.authorization.k8s.io/rook-ceph-mgr-system created rolebinding.rbac.authorization.k8s.io/rook-ceph-osd created rolebinding.rbac.authorization.k8s.io/rook-ceph-osd-psp created rolebinding.rbac.authorization.k8s.io/rook-ceph-purge-osd created rolebinding.rbac.authorization.k8s.io/rook-ceph-purge-osd-psp created rolebinding.rbac.authorization.k8s.io/rook-ceph-rgw created rolebinding.rbac.authorization.k8s.io/rook-ceph-rgw-psp created rolebinding.rbac.authorization.k8s.io/rook-ceph-system created serviceaccount/rook-ceph-cmd-reporter created serviceaccount/rook-ceph-mgr created serviceaccount/rook-ceph-osd created serviceaccount/rook-ceph-purge-osd created serviceaccount/rook-ceph-rgw created serviceaccount/rook-ceph-system created serviceaccount/rook-csi-cephfs-plugin-sa created serviceaccount/rook-csi-cephfs-provisioner-sa created serviceaccount/rook-csi-rbd-plugin-sa created serviceaccount/rook-csi-rbd-provisioner-sa created
$ cat << EOF > ~/operator.yaml
kind: SecurityContextConstraints
apiVersion: security.openshift.io/v1
metadata:
name: rook-ceph
allowPrivilegedContainer: true
allowHostDirVolumePlugin: true
allowHostPID: false
allowHostNetwork: false
allowHostPorts: false
priority:
allowedCapabilities: ["MKNOD"]
allowHostIPC: true
readOnlyRootFilesystem: false
requiredDropCapabilities: []
defaultAddCapabilities: []
runAsUser:
type: RunAsAny
seLinuxContext:
type: MustRunAs
fsGroup:
type: MustRunAs
supplementalGroups:
type: RunAsAny
volumes:
- configMap
- downwardAPI
- emptyDir
- hostPath
- persistentVolumeClaim
- projected
- secret
users:
- system:serviceaccount:rook-ceph:rook-ceph-system
- system:serviceaccount:rook-ceph:default
- system:serviceaccount:rook-ceph:rook-ceph-mgr
- system:serviceaccount:rook-ceph:rook-ceph-osd
- system:serviceaccount:rook-ceph:rook-ceph-rgw
---
kind: SecurityContextConstraints
apiVersion: security.openshift.io/v1
metadata:
name: rook-ceph-csi
allowPrivilegedContainer: true
allowHostNetwork: true
allowHostDirVolumePlugin: true
priority:
allowedCapabilities: ["SYS_ADMIN"]
allowHostPorts: true
allowHostPID: true
allowHostIPC: true
readOnlyRootFilesystem: false
runAsUser:
type: RunAsAny
seLinuxContext:
type: RunAsAny
fsGroup:
type: RunAsAny
supplementalGroups:
type: RunAsAny
volumes:
- configMap
- projected
- emptyDir
- hostPath
users:
- system:serviceaccount:rook-ceph:rook-csi-rbd-plugin-sa
- system:serviceaccount:rook-ceph:rook-csi-rbd-provisioner-sa
- system:serviceaccount:rook-ceph:rook-csi-cephfs-plugin-sa
- system:serviceaccount:rook-ceph:rook-csi-cephfs-provisioner-sa
- system:serviceaccount:rook-ceph:rook-csi-nfs-plugin-sa
- system:serviceaccount:rook-ceph:rook-csi-nfs-provisioner-sa
---
kind: ConfigMap
apiVersion: v1
metadata:
name: rook-ceph-operator-config
namespace: rook-ceph
data:
ROOK_LOG_LEVEL: "INFO"
ROOK_CSI_ENABLE_CEPHFS: "true"
ROOK_CSI_ENABLE_RBD: "true"
ROOK_CSI_ENABLE_NFS: "false"
ROOK_CSI_ENABLE_GRPC_METRICS: "false"
CSI_ENABLE_ENCRYPTION: "false"
CSI_PROVISIONER_REPLICAS: "2"
CSI_ENABLE_CEPHFS_SNAPSHOTTER: "true"
CSI_ENABLE_RBD_SNAPSHOTTER: "true"
CSI_FORCE_CEPHFS_KERNEL_CLIENT: "true"
CSI_RBD_FSGROUPPOLICY: "ReadWriteOnceWithFSType"
CSI_CEPHFS_FSGROUPPOLICY: "ReadWriteOnceWithFSType"
CSI_NFS_FSGROUPPOLICY: "ReadWriteOnceWithFSType"
ROOK_CSI_ALLOW_UNSUPPORTED_VERSION: "false"
CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT: "false"
CSI_PLUGIN_PRIORITY_CLASSNAME: "system-node-critical"
CSI_PROVISIONER_PRIORITY_CLASSNAME: "system-cluster-critical"
ROOK_OBC_WATCH_OPERATOR_NAMESPACE: "true"
ROOK_ENABLE_DISCOVERY_DAEMON: "false"
CSI_ENABLE_VOLUME_REPLICATION: "false"
ROOK_CEPH_COMMANDS_TIMEOUT_SECONDS: "15"
CSI_ENABLE_CSIADDONS: "false"
CSI_GRPC_TIMEOUT_SECONDS: "150"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rook-ceph-operator
namespace: rook-ceph
labels:
operator: rook
storage-backend: ceph
app.kubernetes.io/name: rook-ceph
app.kubernetes.io/instance: rook-ceph
app.kubernetes.io/component: rook-ceph-operator
app.kubernetes.io/part-of: rook-ceph-operator
spec:
selector:
matchLabels:
app: rook-ceph-operator
replicas: 1
template:
metadata:
labels:
app: rook-ceph-operator
spec:
serviceAccountName: rook-ceph-system
containers:
- name: rook-ceph-operator
image: rook/ceph:v1.9.3
args: ["ceph", "operator"]
securityContext:
runAsNonRoot: true
runAsUser: 2016
runAsGroup: 2016
volumeMounts:
- mountPath: /var/lib/rook
name: rook-config
- mountPath: /etc/ceph
name: default-config-dir
- mountPath: /etc/webhook
name: webhook-cert
ports:
- containerPort: 9443
name: https-webhook
protocol: TCP
env:
- name: ROOK_CURRENT_NAMESPACE_ONLY
value: "false"
- name: ROOK_DISCOVER_DEVICES_INTERVAL
value: "60m"
- name: ROOK_HOSTPATH_REQUIRES_PRIVILEGED
value: "true"
- name: ROOK_ENABLE_SELINUX_RELABELING
value: "true"
- name: ROOK_ENABLE_FSGROUP
value: "true"
- name: ROOK_DISABLE_DEVICE_HOTPLUG
value: "false"
- name: DISCOVER_DAEMON_UDEV_BLACKLIST
value: "(?i)dm-[0-9]+,(?i)rbd[0-9]+,(?i)nbd[0-9]+"
- name: ROOK_ENABLE_MACHINE_DISRUPTION_BUDGET
value: "false"
- name: ROOK_UNREACHABLE_NODE_TOLERATION_SECONDS
value: "5"
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumes:
- name: rook-config
emptyDir: {}
- name: default-config-dir
emptyDir: {}
- name: webhook-cert
emptyDir: {}
EOF
$ oc create -f operator.yaml securitycontextconstraints.security.openshift.io/rook-ceph created securitycontextconstraints.security.openshift.io/rook-ceph-csi created configmap/rook-ceph-operator-config created deployment.apps/rook-ceph-operator created $ oc get pods -n rook-ceph NAME READY STATUS RESTARTS AGE rook-ceph-operator-84bf68d9bd-lv9l9 1/1 Running 0 1m
osd_pool_default_size needs to be 1
mon count needs to be 1 and allowMultiplePerNode needs to be true
mgr count needs to be 1 and allowMultiplePerNode needs to be true
storage device needs to be set to extra disk available (in my case sdb)
osdsPerDevice needs to be 1
managePodBudgets and manageMachineDisruptionBudgets both set to false
$ cat << EOF > ~/cluster.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: rook-config-override
namespace: rook-ceph
data:
config: |
[global]
osd_pool_default_size = 1
---
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: rook-ceph
namespace: rook-ceph
spec:
cephVersion:
image: quay.io/ceph/ceph:v16.2.7
allowUnsupported: false
dataDirHostPath: /var/lib/rook
skipUpgradeChecks: false
continueUpgradeAfterChecksEvenIfNotHealthy: false
waitTimeoutForHealthyOSDInMinutes: 10
mon:
count: 1
allowMultiplePerNode: true
mgr:
count: 1
allowMultiplePerNode: true
modules:
- name: pg_autoscaler
enabled: true
dashboard:
enabled: true
ssl: true
monitoring:
enabled: false
network:
connections:
encryption:
enabled: false
compression:
enabled: false
crashCollector:
disable: false
cleanupPolicy:
confirmation: ""
sanitizeDisks:
method: quick
dataSource: zero
iteration: 1
allowUninstallWithVolumes: false
annotations:
labels:
resources:
removeOSDsIfOutAndSafeToRemove: false
priorityClassNames:
mon: system-node-critical
osd: system-node-critical
mgr: system-cluster-critical
storage:
useAllNodes: true
useAllDevices: false
devices:
- name: "sdb"
config:
osdsPerDevice: "1"
onlyApplyOSDPlacement: false
disruptionManagement:
managePodBudgets: false
osdMaintenanceTimeout: 30
pgHealthCheckTimeout: 0
manageMachineDisruptionBudgets: false
machineDisruptionBudgetNamespace: openshift-machine-api
healthCheck:
daemonHealth:
mon:
disabled: false
interval: 45s
osd:
disabled: false
interval: 60s
status:
disabled: false
interval: 60s
livenessProbe:
mon:
disabled: false
mgr:
disabled: false
osd:
disabled: false
startupProbe:
mon:
disabled: false
mgr:
disabled: false
osd:
disabled: false
EOF
$ oc create -f cluster.yaml configmap/rook-config-override created cephcluster.ceph.rook.io/rook-ceph created
$ oc get pods -n rook-ceph NAME READY STATUS RESTARTS AGE csi-cephfsplugin-provisioner-7577bb4d59-kxmq8 6/6 Running 0 118s csi-cephfsplugin-x2njd 3/3 Running 0 118s csi-rbdplugin-provisioner-847b498845-7z5qc 6/6 Running 0 119s csi-rbdplugin-tlw5d 3/3 Running 0 119s rook-ceph-crashcollector-master-0.sno3.schmaustech.com-858mmbx2 1/1 Running 0 48s rook-ceph-mgr-a-57fbb7fb47-9rjl5 1/1 Running 0 81s rook-ceph-mon-a-d94d79bb5-l6f8p 1/1 Running 0 110s rook-ceph-operator-84bf68d9bd-qkj6k 1/1 Running 0 17m rook-ceph-osd-0-6c98c84f66-96l5q 1/1 Running 0 48s rook-ceph-osd-prepare-master-0.sno3.schmaustech.com-l5s8t 0/1 Completed 0 60s
$ oc create -f https://raw.githubusercontent.com/rook/rook/master/deploy/examples/toolbox.yaml deployment.apps/rook-ceph-tools created $ oc get pods -n rook-ceph| grep rook-ceph-tools rook-ceph-tools-d6d7c985c-6zwc7 1/1 Running 0 54s
$ oc -n rook-ceph exec -it rook-ceph-tools-d6d7c985c-6zwc7 -- ceph status
cluster:
id: c54ad01e-e9f8-48c9-806b-a7d4748eb977
health: HEALTH_OK
services:
mon: 1 daemons, quorum a (age 7m)
mgr: a(active, since 5m)
osd: 1 osds: 1 up (since 5m), 1 in (since 6m)
data:
pools: 0 pools, 0 pgs
objects: 0 objects, 0 B
usage: 4.8 MiB used, 160 GiB / 160 GiB avail
pgs:
$ cat << EOF > ~/storageclass.yaml
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: replicapool
namespace: rook-ceph
spec:
failureDomain: host
replicated:
size: 1
---
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/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Delete
allowVolumeExpansion: true
EOF
$ oc create -f storageclass.yaml cephblockpool.ceph.rook.io/replicapool created storageclass.storage.k8s.io/rook-ceph-block created $ oc get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE rook-ceph-block rook-ceph.rbd.csi.ceph.com Delete Immediate true 3s oc get
$ oc patch storageclass rook-ceph-block -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
storageclass.storage.k8s.io/rook-ceph-block patched
$ oc get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
rook-ceph-block (default) rook-ceph.rbd.csi.ceph.com Delete Immediate true 8m41s
$ oc get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-791551c6-cdc4-4c9e-9692-c9622dbef4e8 10Gi RWO Delete Bound open-cluster-management/search-redisgraph-pvc-0 rook-ceph-block 45s

