3 ways to use MongoDB without cost (Atlas, mtools, Docker)
Tags: mongodb, nosql, development, docker
Published: 2021-12-18 01:39:23

**MongoDB** is a general-purpose no sql database which provides certain capabilities like replication between nodes, sharding, secondary indexes, range queries, sorting, aggregations, and geospatial indexes. MongoDB is a **document-oriented** database where rows (from relational database) is replaced by documents which can have any fields inside and you can have more information on one document versus another from the same database. This last point means that there is no predefined schemas.

At the heart of MongoDB there is a `key:value` so if you see a document representation is similar to JSON or map. And can be as simple as:
```
{"Salute": "Hello Peter!"}
```

So Documents are the basic form of data in mongoDB and a collection is similar to have a table.

That's all that we will talk about the basics for MongoDB, because this is a post of testing for free.

Next i am going to present 3 ways you can test your mongoDB database without incurring in any extra cost.

************************************************************

## **1. MongoDB Atlas Free Cluster**

MongoDB Atlas offers a Database as a service with MongoDB Atlas, as part of their offer you can create a free cluster (*Always free as long as you keep in this tier*) that has similar functionality that a production one.

According to [MongoDB Atlas documentation](https://docs.atlas.mongodb.com/tutorial/deploy-free-tier-cluster/)
> Free clusters never expire, and provide access to a subset of Atlas features and functionality.

Some of the limitations are:
* You can only have **one** free cluster per project.
* You can't upgrade the MongoDB version.
* **M0** tier is the only one available to deploy free cluster.
* Replication factor is **3 nodes**, and yes, yo have 3 nodes on free cluster, so replication a similar to prod escenario is easy.
* No predefined **replica set tag**.
* you cannot test **primary failover, configure encryption at rest or enable auditing**.

Oh i forgot to mention, a **ReplicaSet** is a group of mongo processes (servers, VMs, Containers, etc) that maintain the same data set. At a minimum it is recommended to have a primary node and two replication or secondary node. Also you can have a Primary node, a secondary node and an arbiter (smaller process just to elect a new primary) but this is not recommended.

So your free tier cluster can be seen as this:
![Cluster0 Sandbox](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/unbdk1v0jvi05dvyci8j.png)

**Shared**, as can be seen in the image refers that the nodes are deployed on shared environment so you cannot configure disk size, memory size, etc.
 
And, **How can you create your free cluster?** yo may ask...

**First of all** go to [MongoDB Atlas registration page](https://account.mongodb.com/account/register?tck=docs_atlas&_ga=2.33670369.1905865679.1639760133-686843528.1638558648)

Here you have the option to fill al the fields or even better use your google account to sign up, and that's it.

Now, to create the free cluster:
1. Click on Build a Cluster
2. Select the option of Starters Cluster or Shared Cluster.
3. Select your cloud provider. Not all providers or regions support the **free cluster**. I personally recommends use **AWS with us-east-1 region**.
4. Select **M0 Sandbox** tier as this corresponds to the free tier.
5. Enter a name for your cluster
![Enter a name for your cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ca24xab86p20bnzwydo1.png)
6. Click on **Create Cluster** and that is all you need to create your cluster, this process will take around 10 min, so you can go and get a cup of coffee.

So, after you cluster is ready, the **First Thing** you need to do is provide access to your local IP. MongoDB Atlas use a whitelisting approach for this.

On your MongoDB Atlas interface, move to your project and under security click on **Network Access**
![Network Access mongo Atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t3pe3mfd3xq8fe16e92i.png)
 
**Then**, click on **Add IP Address** 
You have the option on enter and IP in CIDR notation, example `188.188.188.21/32`
Another option is to provide access to any IP by using `0.0.0.0/0`
Or Click **Add Current IP Address** and automatically will add your public IP.

![network access page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fm4bcppinxqiap66obgy.png)
 
**Note** you can even configure a temporary access for 6 hrs, 1 day or 1 week.

Next Step is to create a user to access the cluster , this is different from the user you created at the beginning, this new user is for database access, the previous one was for **MongoDB Atlas platform administration**.

Enter the important information and provide the permissions through **Built-in Role** more info [HERE](https://docs.mongodb.com/manual/reference/built-in-roles/)
![create mongo user account](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtpm7dug87aqws045e3y.png)
 
#####One of the main advantages
of MongoDB Atlas is that allows you to find the exact information you need in an easy way. In this case, to test the access we will connect using mongo shell from our laptop.

you can find the connection URI here
![Connection URI Atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rdtbxgi4pfs3hdq7knp.png)

On the next page, select Connect using MongoShell and it will show you something similar to:
![connect to mongo atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zfyzhi9p30earecby2y8.png)

**Note** it also provides the required information to download the mongo shell in your local system.

**Note** it also provides you with the connection URI and full driver code for several programming languages. Example:
![python connection to mongodb atlas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8issaczp2ibbftp3yah4.png)
 
 
Getting back to our example. Just add the **username** you created previously and add the ip when it is requested.

```
☁  .ssh  mongosh "mongodb+srv://cluster0.xxxxx.mongodb.net/myFirstDatabase" --username eltest01
Enter password: **********************
Current Mongosh Log ID:	xxxxxxxxxxxxxxxxxxxxxxxx
Connecting to:		mongodb+srv://cluster0.xxxxx.mongodb.net/myFirstDatabase
Using MongoDB:		4.4.10
Using Mongosh:		1.1.7

For mongosh info see: https://docs.mongodb.com/mongodb-shell/


To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
You can opt-out by running the disableTelemetry() command.

Atlas atlas-xxxxx-shard-0 [primary] myFirstDatabase> show dbs
m201    119 MB
admin   373 kB
local  7.16 GB
Atlas atlas-xxxxx-shard-0 [primary] myFirstDatabase>
```

************************************************************

## **2. Creating a docker container with mongodb official Image**

**Note** The image that we use here is a community edition, MongoDB does provides an Enterprise Server that can include some cost.

![Docker hub mongo image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oeqxv4o40k861jcr511z.png)
 
**First**, we need to remember that container are self-contained without default access to the host, and also that containers can be considered ephemeral, so as soon as the container is **terminated**, any data inside of it will be lost.

If we want to have persistent storage we need to create a **local directory** in our machine and let the mongo container knows about it, in case the container dies a new one can connect to the local storage again.

So lets create a directory:
```
☁  ~  mkdir tmp
☁  ~  cd tmp
☁  tmp  mkdir datamongodb
☁  tmp
```

Now we need to obtain or path:
```
☁  datamongodb  pwd
/Users/ernestol/tmp/datamongodb
```

After this we can run a container with a bind mount.

According to [Docker Documentation](https://docs.docker.com/storage/bind-mounts/)
> When you use a bind mount, a file or directory on the host machine is mounted into a container. The file or directory is referenced by its absolute path on the host machine

```
☁  datamongodb  docker run -d --name mongodb01 -v /Users/ernestol/tmp/datamongodb:/data/db mongo
7d375ef665c5adfee4df4217ef9d7xxxxxxxxx41237032879a7ae99ee32d613a
☁  datamongodb  docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS       NAMES
7d375ef665c5   mongo     "docker-entrypoint.s…"   43 seconds ago   Up 42 seconds   27017/tcp   mongodb01
```
**Note** See that locally and on mongoDB Atlas we use the default port 27017 but you can change this.

Let get into the container to start the mongodb database to see what happens.

```
☁  datamongodb  docker exec -it mongodb01 bash

root@7d375ef665c5:/# ls /data/db
WiredTiger         WiredTigerHS.wt                       collection-4--3382512612996164992.wt  index-5--3382512612996164992.wt  sizeStorer.wt
WiredTiger.lock    _mdb_catalog.wt                       diagnostic.data                       index-6--3382512612996164992.wt  storage.bson
WiredTiger.turtle  collection-0--3382512612996164992.wt  index-1--3382512612996164992.wt       journal
WiredTiger.wt      collection-2--3382512612996164992.wt  index-3--3382512612996164992.wt       mongod.lock
root@7d375ef665c5:/# mongo
MongoDB shell version v5.0.4
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("9c275138-c0a6-42d9-af2d-f63cac040318") }
MongoDB server version: 5.0.4
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
Welcome to the MongoDB shell.

        To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> use test
switched to db test

> db.greets.insertOne({"salute": "Hello Peter!"})
{
	"acknowledged" : true,
	"insertedId" : ObjectId("61bd1e6caecda5e2f0f2932f")
}
>
```

And notice that those files inside `/data/db/` are the sale that are in our local computer and represent the database files form mongodb

```
☁  datamongodb  ls
WiredTiger                           _mdb_catalog.wt                      diagnostic.data                      index-8--3382512612996164992.wt
WiredTiger.lock                      collection-0--3382512612996164992.wt index-1--3382512612996164992.wt      journal
WiredTiger.turtle                    collection-2--3382512612996164992.wt index-3--3382512612996164992.wt      mongod.lock
WiredTiger.wt                        collection-4--3382512612996164992.wt index-5--3382512612996164992.wt      sizeStorer.wt
WiredTigerHS.wt                      collection-7--3382512612996164992.wt index-6--3382512612996164992.wt      storage.bson
☁  datamongodb
```

Not you can kill your containers and create others an mount those files.

************************************************************

## **3. Create a demo replicaset using mtools**

> mtools is a collection of helper scripts to parse, filter, and visualize MongoDB log files (mongod, mongos). mtools also includes mlaunch, a utility to quickly set up complex MongoDB test environments on a local machine, and mtransfer, a tool for transferring databases between MongoDB instances.

Some of the mtools characteristics:
* Written in Python. Requires Python `3.7 >`
* Not officially supported by MongoDB
* **Open Source**
* Only command line interface.
* Currently being updated
* Tested for mongodb 4.0 or newer.

You can clone the corresponding [repo](https://github.com/rueckstiess/mtools)

There is a bunch of good scripts and tools in this repo but the one we want to focus on is the mlaunch
>a script to quickly spin up local test environments, including replica sets and sharded systems (requires pymongo)

This required pymongo so make sure you have installed on your local computer.

`☁  datamongodb  pip3 install pymongo`


And also you need to install the **mtools**

The easiest way to install mtools is via pip3

```
☁  mtools [develop] pip3 install mtools

Collecting mtools
  Downloading mtools-1.6.4-py3-none-any.whl (2.1 MB)
     |████████████████████████████████| 2.1 MB 1.9 MB/s
Collecting python-dateutil>=2.7
  Using cached python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)
Requirement already satisfied: six in /usr/local/lib/python3.9/site-packages (from mtools) (1.16.0)
Installing collected packages: python-dateutil, mtools

Successfully installed mtools-1.6.4 python-dateutil-2.8.2
```
You may found that some of the scripts required additional packages

```
Traceback (most recent call last):
  File "/usr/local/bin/mlaunch", line 5, in <module>
    from mtools.mlaunch.mlaunch import main
  File "/usr/local/lib/python3.9/site-packages/mtools/mlaunch/mlaunch.py", line 20, in <module>
    import psutil
ModuleNotFoundError: No module named 'psutil'
```

so you may need to install requires packages

#### **IMPORTANT**
MONGOD is necessary to run this tools.
> mongod is the primary daemon process for the MongoDB system

This is differente from the **mongo shell** so make sure you install also mongod with 
```
☁  mtools [develop] brew tap mongodb/brew
==> Tapping mongodb/brew
Cloning into '/usr/local/Homebrew/Library/Taps/mongodb/homebrew-brew'...
remote: Enumerating objects: 806, done.
remote: Counting objects: 100% (303/303), done.
remote: Compressing objects: 100% (217/217), done.
remote: Total 806 (delta 153), reused 140 (delta 83), pack-reused 503
Receiving objects: 100% (806/806), 176.54 KiB | 1.26 MiB/s, done.
Resolving deltas: 100% (391/391), done.
Tapped 14 formulae (30 files, 241.7KB).

☁  bin  brew install mongodb-community@5.0
Running `brew update --preinstall`...
==> Downloading https://fastdl.mongodb.org/tools/db/mongodb-database-tools-macos-x86_64-100.5.1.zip
######################################################################## 100.0%
==> Downloading https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-5.0.4.tgz
######################################################################## 100.0%
==> Installing mongodb-community from mongodb/brew
==> Installing dependencies for mongodb/brew/mongodb-community: mongodb-database-tools
==> Installing mongodb/brew/mongodb-community dependency: mongodb-database-tools
🍺  /usr/local/Cellar/mongodb-database-tools/100.5.1: 13 files, 115.7MB, built in 8 seconds
==> Installing mongodb/brew/mongodb-community
==> Caveats
To start mongodb/brew/mongodb-community now and restart at login:
  brew services start mongodb/brew/mongodb-community
Or, if you don't want/need a background service you can just run:
  mongod --config /usr/local/etc/mongod.conf
==> Summary
🍺  /usr/local/Cellar/mongodb-community/5.0.4: 11 files, 181.4MB, built in 8 seconds
==> Running `brew cleanup mongodb-community`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Caveats
==> mongodb-community
To start mongodb/brew/mongodb-community now and restart at login:
  brew services start mongodb/brew/mongodb-community
Or, if you don't want/need a background service you can just run:
  mongod --config /usr/local/etc/mongod.conf
```
And then run it by using:
```
☁  data [develop] ⚡  mlaunch init --replicaset nodes 3 --name mymongo --port 3000
launching: "mongod" on port 3000
launching: "mongod" on port 3001
launching: "mongod" on port 3002
```

Connect using
```
mongo --host mymongo/$(hostname):3000
```

Sometimes this could fail because the replicaset is not starting,

**You can still launch a single one**:

```
☁  data [develop] ⚡  mlaunch init --single --name mymongo --port 31001 --dir ./data2
A different environment already exists at /Users/ernestolopez/tmp/datamongodb/mtools/mtools/data/data2.
☁  data [develop] ⚡  mlaunch init --single --name mymongo --port 32001 --dir ./data3
launching: "mongod" on port 32001

☁  data [develop] ⚡  ls
data2 data3
☁  data [develop] ⚡  mlaunch list --dir data3

PROCESS    PORT     STATUS     PID

single     32001    running    81184

☁  data [develop] ⚡  mongo --host localhost:32001
\MongoDB shell version v5.0.4

        To enable free monitoring, run the following command: db.enableFreeMonitoring()
        To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
>
```