MongoDB Kubernetes Deployment
Overview
While writing Tagging Asteroids with MongoDB I was able to quickly provision a cluster on the free tier of MongoDB Atlas. What I originally wanted was to quickly stand up a MongoDB instance in my Kubernetes environment but found most documentation too complex for my usecase. Just give me a MongoDB “dialtone” as quickly as possible without having to install the binaries on my system.
NOTE: This deployment pattern should not be used in production. Strongly recommend a StatefulSet for MongoDB Replica Sets.
Easiest way to standup Kubernetes as a Developer
Install Docker on your machine then install kind
Manifests
You will find all the code for this deployment on my GitHub https://github.com/fullaware/k8s-mongodb
Kustomize
The kustomization.yaml
is the entrypoint to deploying MongoDB on Kubernetes. Kustomize allows us to package up all the other manifests (yaml files) and deploy them to a specific namespace (mongodb
in this case). Kustomize also allows us to generate secrets so that we don’t have to do the base64
encoding by hand. In a production environment you would use something like Hashicorp Vault to centrally store passwords.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mongodb
resources:
- mongodb-namespace.yaml # Create MongoDB namespace
- mongodb-dbinit-configmap.yaml # Initialize DB with data
- mongodb-pvc.yaml # Request storage
- mongodb-svc.yaml # Expose MongoDB as service
- mongodb-deployment.yaml # Deploy container
secretGenerator:
- name: mongo-creds
literals:
- username=admin # MONGO_INITDB_ROOT_USERNAME
- password=Candy123 # MONGO_INITDB_ROOT_PASSWORD
Initialize MongoDB container with data using a configmap
I prefer this method for it’s simplicity but comes with a major downside. You are limited to 1MB due to the objects size in etcd. If you need to initialize the database with more than 1MB, consider using an Init Container to download a mongodump
file then mongorestore
the file into the database.
The MongoDB image on Docker Hub will look for all scripts found in its mountpoint at /docker-entrypoint-initdb.d
and execute them in alphabetical order.
Create a configmap and populate the data with the contents of a dbinit.js
file like so [truncated for easier reading, see mongodb-dbinit-configmap.yaml]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: ConfigMap
metadata:
name: dbinit-script
data:
dbinit.js: |-
db = new Mongo().getDB("asteroids");
db.createCollection('asteroids', { capped: false });
db.asteroids.insert([{
"_id": 1000,
"name": "Bennu",
"elements": [
100,
101,
108
]
}])
Persistent Volume Claim
For our usecase we don’t want our data to disappear when we restart the deployment so we want to create a persistent volume claim.
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongo-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Service
Here we will expose the MongoDB deployment as a service on port 27017
. This will make it accessible to other applications running on the Kubernetes cluster.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
labels:
app: mongodb
name: mongodb-svc
spec:
ports:
- port: 27017
protocol: TCP
targetPort: 27017
selector:
app: mongodb
type: ClusterIP
Deployment
All of our hard work comes together in the deployment. Here we will map our persistent volume for our /data/db
as well as our dbinit-script
configmap. Default username and password are provided by the secret that Kustomize generated earlier.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: mongodb
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
strategy: {}
template:
metadata:
labels:
app: mongodb
spec:
containers:
- image: mongo
name: mongo
args: ["--dbpath","/data/db"]
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongo-creds
key: username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongo-creds
key: password
volumeMounts:
- name: "mongo-data-dir"
mountPath: "/data/db"
- name: dbinit-script
mountPath: /docker-entrypoint-initdb.d
volumes:
- name: "mongo-data-dir"
persistentVolumeClaim:
claimName: "mongo-data"
- name: dbinit-script
configMap:
name: dbinit-script
Running MongoDB on Kubernetes
1
2
git clone https://github.com/fullaware/k8s-mongodb.git
cd k8s-mongodb
This next part is important, notice we will use a -k
instead of -f
which instructs kubectl
to run the kustomization.yaml
instead of just running all the yaml files in the folder.
1
kubectl apply -k .
Verify MongoDB server
First get shell access into the deployment.
1
kubectl exec deployment/mongodb -n mongodb -it -- /bin/bash
Then auth into the server
1
mongosh -u admin -p Candy123
Exit mongosh
then exit from the deployment because now we want to access the MongoDB instance using either our programming language of choice, MongoDB Compass or mongosh
from our local machine.
Access deployment on localhost
Use kubectl to forward the port 27017
from the mongodb-svc
to localhost
1
kubectl port-forward --address 0.0.0.0 svc/mongodb-svc 27017:27017 -n mongodb
From there we should be able to use mongosh
client to connect to the MongoDB instance by connecting to localhost
1
mongosh -u admin -p Candy123
Once we are in the Mongo Shell, let’s look for databases
1
2
3
4
5
test> show dbs
admin 100.00 KiB
asteroids 80.00 KiB
config 72.00 KiB
local 72.00 KiB
Now use the asteroids
database
1
2
test> use asteroids
switched to db asteroids
Next we will look at the collections in this database
1
2
3
asteroids> show collections
asteroids
elements
Find all the documents in the asteroids
collection
1
2
3
4
5
6
7
8
9
asteroids> db.asteroids.find()
[
{ _id: 1000, name: 'Bennu', elements: [ 100, 101, 108 ] },
{ _id: 1001, name: 'Ceres', elements: [ 106, 103, 108 ] },
{ _id: 1002, name: 'Pallas', elements: [ 103, 102, 105 ] },
{ _id: 1003, name: 'Juno', elements: [ 107, 106, 100 ] },
{ _id: 1004, name: 'Vesta', elements: [ 108, 101, 103 ] },
{ _id: 1005, name: 'Astraea', elements: [ 105, 101, 106 ] }
]