Reputation: 525
I'm trying to create some kind of script that will create a docker with mongodb and automatically create a user.
I can usually manage my docker images with docker-compose but this time, I don't know how to do it.
Basically, here is what I have to do:
docker-compose down
)--auth parameter
) db.createUser()
--auth
parameter to allow login with the user created in the javascriptI can't find how to do that properly with docker-compose because when it starts, I have to give it the command --auth. If I do that, I cannot execute my javascript to add my user. MongoDB allows users creation without being logged in if there is no user and if --auth parameter is not provided.
I want to do that automatically, I do not want to manually do some commands. The goal is to have a script that can be executed before each integration tests to start from a clean database.
Here is my project:
integration-test/src/test/resources/scripts/docker-compose.yml
mongodb:
container_name: mongo
image: mongo
ports:
- "27017:27017"
volumes:
- .:/setup
command: --auth
integration-test/src/test/resources/scripts/docker-init.sh
docker-compose down
docker-compose up -d
sleep 1
docker exec mongo bash -c "mongo myDatabase /setup/mongodb-setup.js"
integration-test/src/test/resources/scripts/mongodb-setup.js
db.createUser(
{
user: "myUser",
pwd: "myPassword",
roles: [
{ role: "readWrite", db: "myDatabase" }
]
})
Finding a way to start again a container with a new parameter (in this case --auth
) would help but I can't find how to do that (docker start does not take parameters).
Any idea how I should do what I would like ?
If not, I can still delete everything from my database with some Java code or something else but I would like a complete mongodb docker setup created with a script.
Upvotes: 50
Views: 122709
Reputation: 51
You can use side container to initialize all the users and database that you need with a help of a JavaScript file. Your docker-compose.yml file should look like:
version: '3.1'
services:
mongo:
image: mongo:7.0.5
container_name: mongo
restart: always
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: test
MONGO_INITDB_ROOT_PASSWORD: test
volumes:
- ./mongo/data:/data/db
- ./mongo/scripts/initdb.js:/initdb.js
mongo-initdb:
image: mongo:7.0.5
container_name: mongo-initdb
command: mongosh --host mongo admin /initdb.js -u test -p test --authenticationDatabase admin
volumes:
- ./mongo/scripts/initdb.js:/initdb.js
depends_on:
- mongo
make sure your javascript is placed at "./mongo/scripts/initdb.js". The content of the js file is as follow:
// Function to check if a user already exists
function doesUserExist(username, database) {
var users = database.getUsers();
for (var i = 0; i < users.length; i++) {
if (users[i].user === username) {
return true;
}
}
return false;
}
// User configuration array
var usersToCreate = [
{ username: "test1", pwd: "test1", dbName: "test1", role: "readWrite" },
{ username: "test2", pwd: "test2", dbName: "test2", role: "readWrite" },
// Add more users as needed
];
// Connect to the admin database
var adminDb = db.getSiblingDB('admin');
// Loop through the users and create them if they don't exist
usersToCreate.forEach(function(user) {
if (!doesUserExist(user.username, adminDb)) {
// Create user if not exists
adminDb.createUser({
user: user.username,
pwd: user.pwd, // Use a secure password
roles: [{ role: user.role, db: user.dbName }]
});
print('User ' + user.username + ' created');
} else {
print('User ' + user.username + ' already exists');
}
// Connect to the target database and create a collection
var userDb = db.getSiblingDB(user.dbName);
userDb.createCollection('deleteMe_' + user.username);
});
print('User creation script executed');
This way you can create multiple User, Database and Collection if you need.
Upvotes: 1
Reputation: 71
@greenThumb's answer is good and I have used his approach but due to using .env files
I have suffered with the variable interpolation but this example works great
Hope this helps anyone trying to use an initializer container to deploy MongoDB in docker-engien, swarm or even K8s (Really?)
Tested this with Mongo 4.4 but mongosh is compatible with mongo 4.2 onwards so it should work
Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get upgrade -y && apt-get install -y gnupg wget
RUN wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add -
RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list
RUN apt-get update && apt-get install -y mongodb-mongosh
RUN rm -rf /var/lib/apt/lists/*
RUN useradd -ms /bin/bash ubuntu
WORKDIR /app
COPY createUser.sh /app/createUser.sh
RUN chmod -R +x /app/createUser.sh
RUN chown ubuntu:ubuntu /app/createUser.sh
USER ubuntu
CMD ["/app/createUser.sh"]
createUser.sh script:
#!/bin/sh
# Set default values for environment variables if not defined
: ${MONGO_INITDB_ROOT_USERNAME:=root}
: ${MONGO_INITDB_ROOT_PASSWORD:=<root\'s_password>}
: ${MONGO_DB_HOST:=<db_host>}
: ${MONGO_DB_NAME:=<db_name>}
: ${MONGO_DB_USERNAME:=<username>}
: ${MONGO_DB_PASSWORD:=<password>}
sleep 10
# Wait for MongoDB to be ready
until mongosh --eval "print(\"waited for connection\")" --host mongo-db --username $MONGO_INITDB_ROOT_USERNAME --password $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin
do
echo "MongoDB is not yet available. Retrying in 5 seconds..."
sleep 5
done
# Check if the database exists
db_exists=$(mongosh --quiet --host mongo-db -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval "db.getSiblingDB('$MONGO_DB_NAME')" | grep -c "ok")
if [ "$db_exists" -eq "1" ]; then
echo "Database $MONGO_DB_NAME doesn't exist. Creating..."
# Create the database
mongosh --quiet --host mongo-db -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval "db.getSiblingDB('$MONGO_DB_NAME').runCommand({ create: '$MONGO_DB_NAME' })"
create_db_status=$?
if [ $create_db_status -eq 0 ]; then
echo "Database $MONGO_DB_NAME created successfully."
else
echo "Database $MONGO_DB_NAME creation failed."
exit 1
fi
else
echo "Database $MONGO_DB_NAME already exists. Skipping creation."
fi
# Check if the user exists
user_exists_output=$(mongosh --quiet --host mongo-db -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval "db.getSiblingDB('$MONGO_DB_NAME').getUser('$MONGO_DB_USERNAME')")
if [ "$user_exists_output" = "null" ]; then
echo "User $MONGO_DB_USERNAME doesn't exist in database $MONGO_DB_NAME. Creating..."
# Execute the MongoDB commands to create the user using mongosh
mongo_commands=$(cat <<EOF
db.getSiblingDB('$MONGO_DB_NAME').createUser({
user: "$MONGO_DB_USERNAME",
pwd: "$MONGO_DB_PASSWORD",
roles: [{ role: "readWrite", db: "$MONGO_DB_NAME" }]
});
EOF
)
# Execute the MongoDB commands to create the user
echo "$mongo_commands" | mongosh --quiet --host mongo-db -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin
create_user_status=$?
if [ $create_user_status -eq 0 ]; then
echo "User $MONGO_DB_USERNAME created successfully in database $MONGO_DB_NAME."
else
echo "User $MONGO_DB_USERNAME creation in database $MONGO_DB_NAME failed."
exit 1
fi
else
if [ "$user_exists_output" = "1" ]; then
echo "Error checking user existence. Exiting with an error code."
exit 1
else
echo "User $MONGO_DB_USERNAME already exists in database $MONGO_DB_NAME. Skipping creation."
fi
fi
docker-compose:
version: '3.9'
services:
mongo-db:
image: ${MONGO_DB_IMAGE:-}
container_name: mongo-db
restart: unless-stopped
stop_grace_period: 30s
env_file:
- ./envfiles/.mongo-db.env
- ./envfiles/.mongo-db-root.env
volumes:
- mongo-db-data:/data/db
healthcheck:
test: ["CMD", "mongo", "--eval", "db.adminCommand('ping')"]
interval: 60s
timeout: 10s
retries: 6
start_period: 60s
logging:
driver: json-file
options:
max-size: 200m
max-file: "3"
mode: non-blocking
networks:
- mongo_network
ports:
- 0.0.0.0:27017:27017
mongo-initializer:
image: ${MONGO_INITIALIZER_IMAGE:-}
container_name: mongo-initializer
env_file:
- ./envfiles/.mongo-db.env
- ./envfiles/.mongo-db-root.env
depends_on:
- mongo-db
networks:
- mongo_network
Mongo's Dockerfile:
FROM mongo:4.4-focal
#ENV MONGO_INITDB_DATABASE-"db_name"
# Expose the default MongoDB port
EXPOSE 27017
# Start MongoDB
CMD ["mongod"]
Thumbs up if this was useful Thanks,
Upvotes: 0
Reputation: 501
In your project directory create another directory docker-entrypoint-initdb.d
then the
file tree looks like this:
📦Project-directory
┣ 📂docker-entrypoint-initdb.d
┃ ┗ 📜mongo-init.js
┗ 📜docker-compose.yaml
The docker-compose.yml contains:
version: "3.7"
services:
mongo:
container_name: container-mongodb
image: mongo:latest
restart: always
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
MONGO_INITDB_DATABASE: root-db
volumes:
- ./docker-entrypoint-initdb.d/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
mongo-init.js
contains the javascript code to create user with different roles.
print("Started Adding the Users.");
db = db.getSiblingDB("admin");
db.createUser({
user: "userx",
pwd: "1234",
roles: [{ role: "readWrite", db: "admin" }],
});
print("End Adding the User Roles.");
You can modify the mongo-init.js
as you need.
Upvotes: 15
Reputation: 21
For initializing mongo with initial user-password-db triple and initdb scripts with only one docker-compose.yml, without any extra configuration, you can use bitnami/mongo image.
In my case, I didn't run my scripts under /docker-entrypoint-initdb.d directory in the container after setting environment variables; MONGODB_USERNAME and MONGODB_PASSWORD (specific env variables for bitnami image) because mongod runs with --auth option automatically when you set these variables. Consequently, I got authentication errors when the container was in the process of executing the scripts.
Because, it was connecting to: mongodb://192.168.192.2:27017/compressors=disabled&gssapiServiceName=mongodb
FIRST DOCKER-COMPOSE FILE:
version: "3"
services:
mongodb:
container_name: mongodb
image: 'docker.io/bitnami/mongodb:4.2-debian-10'
ports:
- "27017:27017"
volumes:
- "mongodb_data:/bitnami/mongodb"
- "./mongodb/scripts:/docker-entrypoint-initdb.d"
environment:
- MONGODB_INITSCRIPTS_DIR=/docker-entrypoint-initdb.d
- MONGODB_USERNAME=some_username
- MONGODB_PASSWORD=some_password
- MONGODB_DATABASE=some_db_name
networks:
backend:
restart: unless-stopped
volumes:
mongodb_data:
networks:
backend:
driver: bridge
INIT JS FILE UNDER ./mongodb/scripts PATH:
let db = connect("localhost:27017/some_db_name");
db.auth("some_username", "some_password");
let collections = db.getCollectionNames();
let storeFound = false;
let index;
for(index=0; index<collections.length; index++){
if ("store" === collections[index]){
storeFound = true;
}
}
if(!storeFound ){
db.createCollection("store");
db.store.createIndex({"name": 1});
}
So, I decided to add new environment variables to my docker-compose.yml after inspecting https://github.com/bitnami/bitnami-docker-mongodb/blob/master/4.2/debian-10/rootfs/opt/bitnami/scripts/libmongodb.sh file.
In this sh file, there is function like mongodb_custom_init_scripts() for executing the scripts. For executing all script files, it runs mongodb_execute() method. In this method, after mongod instance is up and run, mongo client is connecting to the mongod instance by using some parameters.
########################
# Execute an arbitrary query/queries against the running MongoDB service
# Stdin:
# Query/queries to execute
# Arguments:
# $1 - User to run queries
# $2 - Password
# $3 - Database where to run the queries
# $4 - Host (default to result of get_mongo_hostname function)
# $5 - Port (default $MONGODB_PORT_NUMBER)
# $6 - Extra arguments (default $MONGODB_CLIENT_EXTRA_FLAGS)
# Returns:
# None
########################
mongodb_execute() {
local -r user="${1:-}"
local -r password="${2:-}"
local -r database="${3:-}"
local -r host="${4:-$(get_mongo_hostname)}"
local -r port="${5:-$MONGODB_PORT_NUMBER}"
local -r extra_args="${6:-$MONGODB_CLIENT_EXTRA_FLAGS}"
local result
local final_user="$user"
# If password is empty it means no auth, do not specify user
[[ -z "$password" ]] && final_user=""
local -a args=("--host" "$host" "--port" "$port")
[[ -n "$final_user" ]] && args+=("-u" "$final_user")
[[ -n "$password" ]] && args+=("-p" "$password")
[[ -n "$extra_args" ]] && args+=($extra_args)
[[ -n "$database" ]] && args+=("$database")
"$MONGODB_BIN_DIR/mongo" "${args[@]}"
}
After that I added new environment variables to my docker-compose like MONGODB_ADVERTISED_HOSTNAME, MONGODB_PORT_NUMBER, and, MONGODB_CLIENT_EXTRA_FLAGS
So my final docker-compose.yml looks like:
version: "3"
services:
mongodb:
container_name: mongodb
image: 'docker.io/bitnami/mongodb:4.2-debian-10'
ports:
- "27017:27017"
volumes:
- "mongodb_data:/bitnami/mongodb"
- "./mongodb/scripts:/docker-entrypoint-initdb.d"
environment:
- MONGODB_INITSCRIPTS_DIR=/docker-entrypoint-initdb.d
- MONGODB_USERNAME=some_username
- MONGODB_PASSWORD=some_password
- MONGODB_DATABASE=some_db_name
- MONGODB_ADVERTISED_HOSTNAME=localhost
- MONGODB_PORT_NUMBER=27017
- MONGODB_CLIENT_EXTRA_FLAGS=--authenticationDatabase=some_db_name
networks:
backend:
restart: unless-stopped
volumes:
mongodb_data:
networks:
backend:
driver: bridge
Now, it was connecting by this url:
mongodb://localhost:27017/?authSource=some_db_name&compressors=disabled &gssapiServiceName=mongodb
Upvotes: 0
Reputation: 76
Mongo image provides the /docker-entrypoint-initdb.d/ path to deploy custom .js or .sh setup scripts.
Check this post to get more details : How to create a DB for MongoDB container on start up?
Upvotes: 3
Reputation:
file: docker-compose.yaml
mongo:
image: mongo:latest
volumes_from:
- data
ports:
- "27017:27017"
command: --auth
container_name: "db_mongodb"
data:
image: mongo:latest
volumes:
- /var/lib/mongo
- ./setup:/setup
command: "true"
container_name: "db_mongodb_data"
file: .buildMongo.sh
#!/bin/sh
docker-compose down
docker-compose up -d
sleep 1
docker exec db_mongodb mongo admin /setup/create-admin.js
docker exec db_mongodb mongo myDb /setup/create-user.js -u admin -p admin --authenticationDatabase admin
The create-admin.js and create-user.js files are commands that you use using the mongo shell. So they must be easy for you to understand. The real direction is like the jzqa answer, "environment variables". So the question here is how to create a user. I think this answers that point at least, you can check the complete setup here https://github.com/Lus1t4nUm/mongo_docker_bootstrap
Upvotes: 1
Reputation: 292
EDIT: tutumcloud repository is deprecated and no longer maintained, see other answers
I suggest that you use environment variables to set mongo user, database and password. tutum
(owned by Docker) published a very good image
https://github.com/tutumcloud/mongodb
docker run -d -p 27017:27017 -p 28017:28017 -e MONGODB_USER="user" -e MONGODB_DATABASE="mydatabase" -e MONGODB_PASS="mypass" tutum/mongodb
You may convert these variables into docker-compose environments variables. You don't have to hard code it.
environment:
MONGODB_USER: "${db_user_env}"
MONGODB_DATABASE: "${dbname_env}"
MONGODB_PASS: "${db_pass}"
This configuration will read from your session's environment variables.
Upvotes: 21
Reputation: 863
The official mongo image now supports following environment variables that can be used in docker-compose as below:
environment:
- MONGO_INITDB_ROOT_USERNAME=user
- MONGO_INITDB_ROOT_PASSWORD=password
- MONGO_INITDB_DATABASE=test
more explanation at: https://stackoverflow.com/a/42917632/1069610
Upvotes: 43
Reputation: 523
This is how I do it, my requirement was to bring up a few containers along with mongodb, the other containers expect a user to be present when they come up, this worked for me. The good part is, the mongoClientTemp exits after the command is executed so the container doesn't stick around.
version: '2'
services:
mongo:
image: mongo:latest
container_name: mongo
ports:
- "27017:27017"
volumes:
- /app/hdp/mongo/data:/data/db
mongoClientTemp:
image: mongo:latest
container_name: mongoClientTemp
links:
- mongo:mongo
command: mongo --host mongo --eval "db.getSiblingDB('dashboard').createUser({user:'db', pwd:'dbpass', roles:[{role:'readWrite',db:'dashboard'}]});"
depends_on:
- mongo
another-container:
image: another-image:v01
container_name: another-container
ports:
- "8080:8080"
volumes:
- ./logs:/app/logs
environment:
- MONGODB_HOST=mongo
- MONGODB_PORT=27017
links:
- mongo:mongo
depends_on:
- mongoClientTemp
Upvotes: 26
Reputation: 51
After reading the the official mongo docker page, I've found that you can create an admin user one single time, even if the auth option is being used. This is not well documented, but it simply works (hope it is not a feature). Therefore, you can keep using the auth option all the time.
I created a github repository with scripts wrapping up the commands to be used. The most important command lines to run are:
docker exec db_mongodb mongo admin /setup/create-admin.js
docker exec db_mongodb mongo admin /setup/create-user.js -u admin -p admin --authenticationDatabase admin
The first line will create the admin user (and mongo will not complain even with auth option). The second line will create your "normal" user, using the admin rights from the first one.
Upvotes: 5
Reputation: 21
add --noauth option to the mongo command
extract from my docker-compose.yml file
mongors:
image: mongo:latest
command: mongod --noprealloc --smallfiles --replSet mongors2 --dbpath /data/db --nojournal --oplogSize 16 --noauth
environment:
TERM: xterm
volumes:
- ./data/mongors:/data/db
Upvotes: -7