Kubernetes is a prominent open-source orchestration platform. Individuals use it to deploy, manage, and scale applications. It is often challenging to manage stateful applications on this platform, especially those having heavy databases.

Ceph is a robust distributed storage system that comes to the rescue. This open-source storage platform is known for its reliability, performance, and scalability.

This blog post guides you on how to use Ceph persistent storage for Kubernetes with Cephfs. So let us learn the process step-by-step.

Before we jump into the steps, you must have an external Ceph cluster. We assume you have a Ceph storage cluster deployed with Ceph Deploy or manually.

Step 1: Deployment of Cephfs Provisioner on Kubernetes

Deployment of Cephfs Provisioner on Kubernetes is a straightforward process. Simply log into your Kubernetes cluster and make a manifest file to deploy the RBD provisioner. It is an external dynamic provisioner that is compatible with Kubernetes 1.5+.

vim cephfs-provisioner.yml

Include the following content within the file. Remember, our deployment relies on RBAC (Role-Based Access Control). Therefore, we will establish the cluster role and bindings before making the service account and deploying the Cephs provisioner.

---
kind: Namespace
apiVersion: v1
metadata:
  name: cephfs

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: cephfs-provisioner
  namespace: cephfs
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services"]
    resourceNames: ["kube-dns","coredns"]
    verbs: ["list", "get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: cephfs-provisioner
  namespace: cephfs
subjects:
  - kind: ServiceAccount
    name: cephfs-provisioner
    namespace: cephfs
roleRef:
  kind: ClusterRole
  name: cephfs-provisioner
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: cephfs-provisioner
  namespace: cephfs
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["create", "get", "delete"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: cephfs-provisioner
  namespace: cephfs
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: cephfs-provisioner
subjects:
- kind: ServiceAccount
  name: cephfs-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cephfs-provisioner
  namespace: cephfs
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cephfs-provisioner
  namespace: cephfs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cephfs-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: cephfs-provisioner
    spec:
      containers:
      - name: cephfs-provisioner
        image: "quay.io/external_storage/cephfs-provisioner:latest"
        env:
        - name: PROVISIONER_NAME
          value: ceph.com/cephfs
        - name: PROVISIONER_SECRET_NAMESPACE
          value: cephfs
        command:
        - "/usr/local/bin/cephfs-provisioner"
        args:
        - "-id=cephfs-provisioner-1"
      serviceAccount: cephfs-provisioner

Next, apply the manifest.

$ kubectl apply -f cephfs-provisioner.yml
namespace/cephfs created
clusterrole.rbac.authorization.k8s.io/cephfs-provisioner created
clusterrolebinding.rbac.authorization.k8s.io/cephfs-provisioner created
role.rbac.authorization.k8s.io/cephfs-provisioner created
rolebinding.rbac.authorization.k8s.io/cephfs-provisioner created
serviceaccount/cephfs-provisioner created
deployment.apps/cephfs-provisioner created

Make sure that the Cephfs volume provisioner pod is in the operational state.

$ kubectl get pods -l app=cephfs-provisioner -n cephfs
NAME                                  READY   STATUS    RESTARTS   AGE
cephfs-provisioner-7b77478cb8-7nnxs   1/1     Running   0          84s

Step 2: Obtain the Ceph Admin Key and Create a Secret on Kubernetes

Access your Ceph cluster and retrieve the admin key to be used by the RBD provisioner.

sudo ceph auth get-key client.admin

Save the value of the admin user key displayed by the above command. Later, we will incorporate this key as a secret in Kubernetes.

kubectl create secret generic ceph-admin-secret \
    --from-literal=key='<key-value>' \
    --namespace=cephfs

Where <key-value> is your Ceph admin key. Verify the creation by using the following command.

$ kubectl get secrets ceph-admin-secret -n cephfs
NAME                TYPE     DATA   AGE
ceph-admin-secret   Opaque   1      6s

Step 3: Make Ceph Pools for Kubernetes and Client Key

To run a Ceph file system, you will need at least two RADOS pools, one for data and another for metadata. Usually, the metadata pool contains only a few gigabytes of data.

Generally, individuals use 64 or 128 for large clusters. Therefore, we recommend a small PG count.

Now let us make Ceph OSD pools for Kubernetes:

sudo ceph osd pool create cephfs_data 128 128
sudo ceph osd pool create cephfs_metadata 64 64

Create a Ceph file system on the pools.

sudo ceph fs new cephfs cephfs_metadata cephfs_data

Confirm Ceph File System Creation.

$ sudo ceph fs ls
 name: cephfs, metadata pool: cephfs_metadata, data pools: [cephfs_data ]

UI Dashboard Confirmation

Cephfs Kubernetes

Step 4: Make Cephfs Storage Class on Kubernetes

A StorageClass serves as a means to define the “classes” of storage you offer in Kubernetes. Let’s create a storage class known as “Cephrfs.”

vim cephfs-sc.yml

Add the following content to the file:

---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: cephfs
  namespace: cephfs
provisioner: ceph.com/cephfs
parameters:
    monitors: 10.10.10.11:6789,10.10.10.12:6789,10.10.10.13:6789
    adminId: admin
    adminSecretName: ceph-admin-secret
    adminSecretNamespace: cephfs
    claimRoot: /pvc-volumes

Where:
⦁ Cephfs is the name of the StorageClass to be created.
⦁ 10.10.10.11, 10.10.10.12 & 10.10.10.13 are the IP addresses of Ceph Monitors. You can list them with the command:

$ sudo ceph -s
  cluster:
    id:     7795990b-7c8c-43f4-b648-d284ef2a0aba
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum cephmon01,cephmon02,cephmon03 (age 32h)
    mgr: cephmon01(active, since 30h), standbys: cephmon02
    mds: cephfs:1 {0=cephmon01=up:active} 1 up:standby
    osd: 9 osds: 9 up (since 32h), 9 in (since 32h)
    rgw: 3 daemons active (cephmon01, cephmon02, cephmon03)
 
  data:
    pools:   8 pools, 618 pgs
    objects: 250 objects, 76 KiB
    usage:   9.6 GiB used, 2.6 TiB / 2.6 TiB avail
    pgs:     618 active+clean

Once you have updated the file with the accurate value of Ceph monitors, give the Kubectl command to make the StorageClass.

$ kubectl apply -f cephfs-sc.yml 
storageclass.storage.k8s.io/cephfs created

Next, list all the available storage classes:

$ kubectl get sc
NAME       PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
ceph-rbd   ceph.com/rbd      Delete          Immediate           false                  25h
cephfs     ceph.com/cephfs   Delete          Immediate           false                  2m23s

Step 5: Do Testing and Create Pod

Create a test persistent volume claim to ensure that everything is smooth.

$ vim cephfs-claim.yml
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: cephfs-claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: cephfs
  resources:
    requests:
      storage: 1Gi

Apply manifest file

$ kubectl  apply -f cephfs-claim.yml
persistentvolumeclaim/cephfs-claim1 created

The successful binding will show the bound status.

$ kubectl get pvc
NAME              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
ceph-rbd-claim1   Bound    pvc-c6f4399d-43cf-4fc1-ba14-cc22f5c85304   1Gi        RWO            ceph-rbd       25h
cephfs-claim1     Bound    pvc-1bfa81b6-2c0b-47fa-9656-92dc52f69c52   1Gi        RWO            cephfs         87s

Next, we can launch a test pod using the claim we made. First, create a file to store that data:

vim cephfs-test-pod.yaml

Add content below:

kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: gcr.io/google_containers/busybox:latest
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: pvc
      persistentVolumeClaim:
        claimName: claim1

Create Pod:

$ kubectl apply -f cephfs-test-pod.yaml
pod/test-pod created

Make sure the pod is in the running state:

Congratulations! Now you can enjoy Cephfs for persistent volume provisioning on Kubernetes.

Advantages of Ceph Persistent Storage for Kubernetes with Cephfs

The Ceph persistent storage for Kubernetes with Cephfs offers several benefits. Some major ones include:

Scalability

Ceph supports seamless storage scaling, thanks to its distributed nature. As a result, it meets the growing needs of Kubernetes applications. Ceph can smoothly accommodate the increasing workload of applications.

High Uptime

CephFS is known for its high availability. Since it replicates data across multiple nodes, users do not notice downtown.

Flexibility

CephFs is adaptable to diverse application requirements. The deployment of Kubernetes involves the use of different data types. CephFS provides support for both file and block storage.

Snapshot & Cloning

You do not need to worry about data management and recovery with Cephfs. Its cloning and snapshot capabilities make it a perfect storage system for data-centric applications.

Final Words

The implementation of Ceph persistent storage with Kubernetes needs expertise in both technologies. It is advisable to manage Ceph clusters and integrate them with Kubernetes carefully. We hope the guide helps you with the smooth integration.

Leave details and I will get back to you