How to Run MongoDB on Kubernetes

February 24, 2022

Introduction

MongoDB is a general-purpose, document-based NoSQL database program. As with other non-relational database management systems, MongoDB focuses on scalability and the speed of queries.

Kubernetes synergizes with MongoDB to create highly scalable and portable database deployments. These deployments are useful for working with a large amount of data and high loads.

This tutorial will teach you how to deploy MongoDB on Kubernetes. The guide includes steps to run a standalone MongoDB instance and a replica set.

How to run MongoDB on Kubernetes.

Requirements

  • A Kubernetes cluster with kubectl.
  • Administrative access to your system.

Note: MongoDB is part of the MEAN stack, a popular open-source software stack for web application development. Read about the differences between MEAN and LAMP stack.

Deploy a Standalone MongoDB Instance

MongoDB can be deployed on Kubernetes as a standalone instance. This deployment is not adequate for production use, but it is suitable for testing and some aspects of development.

Follow the steps below to deploy a standalone MongoDB instance.

Step 1: Label the Node

Label the node that will be used for MongoDB deployment. The label is used later to assign pods to a specific node.

To do so:

1. List the nodes on your cluster:

kubectl get nodes

2. Choose the deployment node from the list in the command output.

Displaying a list of nodes on the cluster.

3. Use kubectl to label the node with a key-value pair.

kubectl label nodes <node> <key>=<value>

The output confirms that the label was added successfully.

Labeling a node using kubectl.

Step 2: Create a StorageClass

StorageClass helps pods provision persistent volume claims on the node. To create a StorageClass:

1. Use a text editor to create a YAML file to store the storage class configuration.

nano StorageClass.yaml

2. Specify your storage class configuration in the file. The example below defines the mongodb-storageclass:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: mongodb-storageclass
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

3. Save the changes and exit the editor.

Note: To make the process easier, store all the files in this tutorial in a single directory.

Step 3: Create Persistent Storage

Provision storage for the MongoDB deployment by creating a persistent volume and a persistent volume claim:

1. Create a YAML file for persistent volume configuration.

nano PersistentVolume.yaml

2. In the file, allocate storage that belongs to the storage class defined in the previous step. Specify the node that will be used in pod deployment in the nodeAffinity section. The node is identified using the label created in Step 1.

apiVersion: v1
kind: PersistentVolume
metadata:
    name: mongodb-pv
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: mongodb-storageclass
  local:
    path: /mnt/data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: size
          operator: In
          values:
            - large

3. Create another YAML for the configuration of the persistent volume claim:

nano PersistentVolumeClaim.yaml

4. Define the claim named mongodb-pvc and instruct Kubernetes to claim volumes belonging to mongodb-storageclass.

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mongodb-pvc
spec:
  storageClassName: mongodb-storageclass
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi

Step 4: Create a ConfigMap

The ConfigMap file stores non-encrypted configuration information used by pods.

1. Create a YAML file to store deployment configuration:

nano ConfigMap.yaml

2. Use the file to store information about system paths, users, and roles. The following is an example of a ConfigMap file:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb-configmap
data:
  mongo.conf: |
    storage:
      dbPath: /data/db
  ensure-users.js: |
    const targetDbStr = 'test';
    const rootUser = cat('/etc/k8-test/admin/MONGO_ROOT_USERNAME');
    const rootPass = cat('/etc/k8-test/admin/MONGO_ROOT_PASSWORD');
    const usersStr = cat('/etc/k8-test/MONGO_USERS_LIST');

    const adminDb = db.getSiblingDB('admin');
    adminDb.auth(rootUser, rootPass);
    print('Successfully authenticated admin user');

    const targetDb = db.getSiblingDB(targetDbStr);

    const customRoles = adminDb
      .getRoles({rolesInfo: 1, showBuiltinRoles: false})
      .map(role => role.role)
      .filter(Boolean);

    usersStr
      .trim()
      .split(';')
      .map(s => s.split(':'))
      .forEach(user => {
        const username = user[0];
        const rolesStr = user[1];
        const password = user[2];

        if (!rolesStr || !password) {
          return;
        }

        const roles = rolesStr.split(',');
        const userDoc = {
          user: username,
          pwd: password,
        };

        userDoc.roles = roles.map(role => {
          if (!~customRoles.indexOf(role)) {
            return role;
          }
          return {role: role, db: 'admin'}; 
        });

        try {
          targetDb.createUser(userDoc);
        } catch (err) {
          if (!~err.message.toLowerCase().indexOf('duplicate')) {
            throw err;
          }
        }
      });

Step 5: Create a StatefulSet

StatefulSet is a Kubernetes controller used to deploy stateful apps. Stateful app pods require unique identities because they communicate with other pods.

To Create a StatefulSet:

1. Use a text editor to create a YAML file:

nano StatefulSet.yaml

2. Insert deployment information in the file, including the MongoDB Docker image to be used. The file also references the previously created ConfigMap and PersistentVolumeClaim:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongodb-test
spec:
  serviceName: mongodb-test
  replicas: 1
  selector:
    matchLabels:
      app: database
  template:
    metadata:
      labels:
        app: database
        selector: mongodb-test
    spec:
      containers:
      - name: mongodb-test
        image: mongo:4.0.8
        env:
          - name: MONGO_INITDB_ROOT_USERNAME_FILE
            value: /etc/k8-test/admin/MONGO_ROOT_USERNAME
          - name: MONGO_INITDB_ROOT_PASSWORD_FILE
            value: /etc/k8-test/admin/MONGO_ROOT_PASSWORD
        volumeMounts:
        - name: k8-test
          mountPath: /etc/k8-test
          readOnly: true
        - name: mongodb-scripts
          mountPath: /docker-entrypoint-initdb.d
          readOnly: true
        - name: mongodb-configmap
          mountPath: /config
          readOnly: true
        - name: mongodb-data
          mountPath: /data/db
      nodeSelector:
        size: large
      volumes:
      - name: k8-test
        secret:
          secretName: mongodb-secret
          items:
          - key: MONGO_ROOT_USERNAME
            path: admin/MONGO_ROOT_USERNAME
            mode: 0444
          - key: MONGO_ROOT_PASSWORD
            path: admin/MONGO_ROOT_PASSWORD
            mode: 0444
          - key: MONGO_USERNAME
            path: MONGO_USERNAME
            mode: 0444
          - key: MONGO_PASSWORD
            path: MONGO_PASSWORD
            mode: 0444
          - key: MONGO_USERS_LIST
            path: MONGO_USERS_LIST
            mode: 0444
      - name: mongodb-scripts
        configMap:
          name: mongodb-configmap
          items:
          - key: ensure-users.js
            path: ensure-users.js
      - name: mongodb-configmap
        configMap:
          name: mongodb-configmap
          items:
          - key: mongo.conf
            path: mongo.conf
      - name: mongodb-data
        persistentVolumeClaim:
          claimName: mongodb-pvc

Step 6: Create a Secret

The Secret object is used to store sensitive information about the deployment.

1. Create a Secret YAML with your text editor.

nano Secret.yaml

2. Provide information for accessing the MongoDB database.

apiVersion: v1
kind: Secret
metadata:
  name: mongodb-secret
type: Opaque
data:
  MONGO_ROOT_USERNAME: YWRtaW4K
  MONGO_ROOT_PASSWORD: cGFzc3dvcmQK
  MONGO_USERNAME: dGVzdAo=
  MONGO_PASSWORD: cGFzc3dvcmQK
  MONGO_USERS_LIST: dGVzdDpkYkFkbWluLHJlYWRXcml0ZTpwYXNzd29yZAo=

3. Save the changes and exit.

Step 7: Create a MongoDB Service

To create a MongoDB Service:

1. Create a headless service object.

nano Service.yaml

Headless service allows users to connect to pods directly.

2. Add the service name and definition in the YAML file.

apiVersion: v1
kind: Service
metadata:
  name: mongodb-test
  labels:
    app: database
spec:
  clusterIP: None
  selector:
    app: database

3. Save changes and exit the file.

Step 8: Apply the MongoDB Configuration with Kustomize

Use Kustomize to apply the MongoDB configuration files easily:

1. Create a kustomization.yaml file:

nano kustomization.yaml

2. In the resources section, list all the YAML files created in the previous steps:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ConfigMap.yaml
  - PersistentVolumeClaim.yaml
  - PersistentVolume.yaml
  - Secret.yaml
  - Service.yaml
  - StatefulSet.yaml
  - StorageClass.yaml

Save the file in the same directory as the other files.

3. Deploy MongoDB with the following command:

kubectl apply -k .
Applying the MongoDB configuration using Kustomize.

4. Use kubectl to check if the pod is ready.

kubectl get pod

When the pod shows 1/1 in the READY column, proceed to the next step.

Checking if the MongoDB pod is ready.

Step 9: Connect to MongoDB Standalone Instance

1. Connect to the MongoDB pod by using the following kubectl command:

kubectl exec -it mongodb-test-0 -- sh

2. When the # prompt appears, type:

mongo

MongoDB shell loads.

Starting the MongoDB database from inside the pod.

3. Switch to the test database:

use test

4. Authenticate with the following command:

db.auth('[username]','[password]')

Number 1 in the output confirms the successful authentication.

Switching to the test user.

Note: Read how MongoDB compares against Cassandra, another popular NoSQL solution.

Deploy a ReplicaSet

Deploying MongoDB as a ReplicaSet ensures that the specified number of pods are running at any given time. ReplicaSet deployments are recommended for production environments.

Step 1: Set up Role-Based Access Control (RBAC)

Enabling role-based access control is one of the Kubernetes security best practices. RBAC ensures that no user has more permissions than need.

To set up RBAC:

1. Create a YAML file with a text editor.

nano rbac.yaml

2. Provide access rules for your MongoDB deployment. The example below shows an RBAC YAML file:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: mongo-account
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: mongo-role
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["*"]
- apiGroups: [""]
  resources: ["deployments"]
  verbs: ["list", "watch"]
- apiGroups: [""]
  resources: ["services"]
  verbs: ["*"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: mongo_role_binding
subjects:
- kind: ServiceAccount
  name: mongo-account
  namespace: default
roleRef:
  kind: ClusterRole
  name: mongo-role
  apiGroup: rbac.authorization.k8s.io

3. Save the file and apply it with kubectl:

kubectl apply -f rbac.yaml

Step 2: Create a StatefulSet Deployment

1. Create a StatefulSet deployment YAML:

nano StatefulSet.yaml

2. Specify the number of replicas in the file, the MongoDB Docker image to be used, and provide a volume claim template for dynamic volume provision:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongodb-replica
  namespace: default
spec:
  serviceName: mongo
  replicas: 2
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      labels:
        app: mongo
        selector: mongo
    spec:
      terminationGracePeriodSeconds: 30
      serviceAccount: mongo-account
      containers:
      - name: mongodb
        image: docker.io/mongo:4.2
        env:
        command: ["/bin/sh"]
        args: ["-c", "mongod --replSet=rs0 --bind_ip_all"]
        resources:
          limits:
            cpu: 1
            memory: 1500Mi
          requests:
            cpu: 1
            memory: 1000Mi
        ports:
        - name: mongo-port
          containerPort: 27017
        volumeMounts:
        - name: mongo-data
          mountPath: /data/db
  volumeClaimTemplates:
  - metadata:
      name: mongo-data
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi

3. Save the file and use kubectl apply to create a deployment:

kubectl apply -f StatefulSet.yaml

Step 3: Create a Headless Service

To create a headless service:

1. Create a service YAML file:

nano Service.yaml

2. Define a service that enables direct communication with pods:

apiVersion: v1
kind: Service
metadata:
  name: mongo
  namespace: default
  labels:
    name: mongo
spec:
  ports:
    - port: 27017
      targetPort: 27017
  clusterIP: None
  selector:
    app: mongo

3. Apply the YAML with kubectl.

kubectl apply -f Service.yaml

Step 4: Set up Replication Host

To set up pod replication:

1. Enter the pod using kubectl exec:

kubectl exec -it mongodb-replica-0 -n default -- mongo

The MongoDB shell welcome message appears.

Starting the MongoDB shell from inside the pod.

2. Initiate the replication by typing the following command at the MongoDB shell prompt:

rs.initiate()

The "ok" : 1 line shows that the initiation was successful.

Initializing the replication.

3. Define the variable called cfg. The variable executes rs.conf().

var cfg = rs.conf()

4. Use the variable to add the primary server to the configuration:

cfg.members[0].host="mongodb-replica-0.mongo:27017"

The output shows the name of the primary server.

Adding the primary server to the ReplicaSet.

5. Confirm the configuration by executing the following command:

rs.reconfig(cfg)

The "ok" : 1 line confirms the configuration was successful.

Setting up the configuration.

6. Use the rs.add() command to add another pod to the configuration.

rs.add("mongodb-replica-1.mongo:27017")

The output shows the replica was added.

Adding the second instance to the ReplicaSet.

7. Check the status of the system by typing:

rs.status()

The "members" section should show two replicas. The primary replica is listed at the top of the output.

Confirming the status of the primary replica.

The secondary replica is below the primary replica.

Confirming the status of the secondary replica.

The ReplicaSet deployment of MongoDB is set up and ready to operate.

Note: Bare Metal Cloud offers instances designed to support high-performance NoSQL databases such as MongoDB. The instances offer performance and isolation necessary for demanding workloads.

Conclusion

This tutorial covered common scenarios of MongoDB deployment on Kubernetes. While the standalone instance deployment is practical for testing and development, production deployments should always use replica sets.

Was this article helpful?
YesNo
Marko Aleksic
Marko Aleksić is a Technical Writer at phoenixNAP. His innate curiosity regarding all things IT, combined with over a decade long background in writing, teaching and working in IT-related fields, led him to technical writing, where he has an opportunity to employ his skills and make technology less daunting to everyone.
Next you should read
MongoDB vs. MySQL: Comparison
December 31, 2020

From transaction model differences to the quality of support, DBMSs come in all shapes and sizes. In this comparison article, you will read about the differences between the two popular database solutions: MongoDB and MySQL.
Read more
How to Deploy and Manage MongoDB with Docker
February 25, 2020

Using Docker and an official MongoDB container image can significantly shorten and simplify the database deployment process. This tutorial will show you how to deploy a MongoDB instance on a Docker container.
Read more
How to Create Database & Collection in MongoDB
April 29, 2020

By not using a fixed data structure, MongoDB provides a comprehensive solution for high-volume storage in a modern distributed system. This tutorial shows you how to use the MongoDB client to create a new database.
Read more
8 Best Open-Source Databases
April 13, 2023

Open-source databases are becoming increasingly popular. In this tutorial, we will go over 8 of the most popular open-source databases and offer advice on how you can choose the right one for your project’s needs.
Read more