Purpose
There are situations, when you need quick and re-deployable database that doesn't need to be installed on your main OS or at external host in pre-production state, having all pre-production databases, users and tables or schema ready for development.
Since there is no real need to have full cluster working on K8s for this, it's easier to deploy e.g. MongoDB with docker compose
and remove it or reset into 'default state' during development process.
Theory
Imagine you have to work with an ETL that is going to fetch data from e.g. SQL Server and store it in MongoDB.
For this you would have database that you can spin whenever you want, having always its default databases in initial state.
Of course, you can create tables, databases on fly during ETL startup, but you can always create pre-prod databases setup within the compose-file.
First things first
First, you need to create space/folder:
cd /home/user/repos
mkdir mongodb-dev
cd mongodb-dev
And you need to have Docker
and Docker Compose
installed on your OS.
Docker compose
It is common practice, to use docker compose
for creating stacks that can be easily started or recreated, depending on our needs.
First, we need to createdocker-compose.yml
file, which is ours starting point:
cd /home/user/repos/mongodb-dev
touch docker-compose.yml
Then we can start editing file within our IDE/Code Editor (e.g. Visual Studio Code):
code docker-compose.yml
Our simple MongoDb setup, will use one service, with two persistent volumes for:
- /data/
- and /logs
The first paragraph in docker-compose.yml
is always about version
of compose file that is going to be used by engine.
About versioning, you can read more up here.
For this article, I will use version: '3.7'
.
The initial base of our compose file
looks like this:
version: '3.7'
services:
mongodb:
image: mongo:latest
restart: on-failure
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT:-mongodb}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:-mongodb}
MONGO_INITDB_ROOT_DATABASE: ${MONGO_ROOT_DB:-mongodb}
networks:
- services
ports:
- "27017:27017"
volumes:
- mongodb-data:/data/db
- mongodb-log:/var/log/mongodb
volumes:
mongodb-data:
driver: local
mongodb-log:
driver: local
networks:
services:
name: ${MONGO_NETWORK:-mongodb.network}
Were we create service
with a name mongodb
, that will use latest mongo
image from DockerHub
.
We set default user using built in variables, that are mapped to our custom ENV's:
- MONGO_INITDB_ROOT_USERNAME
- MONGO_INITDB_ROOT_PASSWORD
- MONGO_INITDB_ROOT_DATABASE
In the last part initialize network and setup ports forwarding and map data inside container into external storage/volume to allow data persistence (I think it's rather self explaining).
This trick with ${VARIABLE:-secret}
allows us to spin database without having proper .ENV.
In other words, if there is not .env given in root directory, or variables are empty -docker-compose
will use default secrets stored in plain yaml
. It's good for development purposes, but storing any default passwords in plan code, is not allowed in production.
Initialize databases and users at stack startup
In order to add custom startup databases within its users, we need to create .sh
file in our project directory:
touch mongo-init.sh
Then we map file to docker-entrypoint by adding to volumes this line:
volumes:
- ./mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro
Then we add our users passwords into environment
section e.g.:
SALES_PASSWORD: ${SALES_PASSWORD:-sales}
WAREHOUSE_PASSWORD: ${WAREHOUSE_PASSWORD:-warehouse}
And place proper self-exploratory code in .sh
file in order to create databases based on our variables:
set -e
mongo <<EOF
db = db.getSiblingDB('sales')
db.createUser({
user: 'sales',
pwd: '$SALES_PASSWORD',
roles: [{ role: 'readWrite', db: 'sales' }],
});
db.createCollection('receipts')
db.createCollection('documents')
db.createCollection('invoices')
db = db.getSiblingDB('warehouse')
db.createUser({
user: 'warehouse',
pwd: '$WAREHOUSE_PASSWORD',
roles: [{ role: 'readWrite', db: 'warehouse' }],
});
db.createCollection('documents')
db.createCollection('stocks')
db.createCollection('invoices')
db.createCollection('orders')
EOF
Final code
Final docker-compose.yml
file, should look like this:
version: '3.7'
services:
mongodb:
image: mongo:latest
restart: on-failure
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT:-mongodb}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:-mongodb}
MONGO_INITDB_ROOT_DATABASE: ${MONGO_ROOT_DB:-mongodb}
SALES_PASSWORD: ${SALES_PASSWORD:-sales}
WAREHOUSE_PASSWORD: ${WAREHOUSE_PASSWORD:-warehouse}
networks:
- services
ports:
- "27017:27017"
volumes:
- ./mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro
- mongodb-data:/data/db
- mongodb-log:/var/log/mongodb
volumes:
mongodb-data:
driver: local
mongodb-log:
driver: local
networks:
services:
name: ${MONGO_NETWORK:-mongodb.network}
Running our sample database
In order to initialize it, we type (at our project root):
docker compose up -d
In order to start or stop:
docker compose stop
docker compose start
In order to remove containers:
docker compose down
In order to remove containers together with volumes, we type:
docker compose down -v
Result
If everything is okay, we should see something like this:
And be able to join our instance with users and passwords given in compose-file
.
Latest comments (0)