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.
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.
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.
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 .
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.
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.
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.
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.
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.
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.
5. Confirm the configuration by executing the following command:
rs.reconfig(cfg)
The "ok" : 1
line confirms the configuration was successful.
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.
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.
The secondary replica is below the primary 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.