How to create a DB for MongoDB container on start

2019-01-16 15:10发布

问题:

I am working with Docker and I have a stack with PHP, MySQL, Apache and Redis. I need to add MongoDB now so I was checking the Dockerfile for the latest version and also the docker-entrypoint.sh file from the MongoDB Dockerhub but I couldn't find a way to setup a default DB, admin user/password and possibly auth method for the container from a docker-compose.yml file.

In MySQL you can setup some ENV variables as for example:

db:
    image: mysql:5.7
    env_file: .env
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

And this will setup the DB and the user/password as the root password.

Is there any way to achieve the same with MongoDB? Anyone has some experience or workaround?

回答1:

The official mongo image has merged a PR to include the functionality to create users and databases at startup.

The database initialisation will run when there is nothing populated in the /data/db directory.

Admin User Setup

The environment variables to control "root" user setup are

  • MONGO_INITDB_ROOT_USERNAME
  • MONGO_INITDB_ROOT_PASSWORD

Example

docker run -d \
  -e MONGO_INITDB_ROOT_USERNAME=admin \
  -e MONGO_INITDB_ROOT_PASSWORD=password \
  mongod

You don't need to/can't use --auth on the command line as the docker entrypoint.sh script adds this in when the environment variables exist.

Other Custom Initialisation

The image also provides the /docker-entrypoint-initdb.d/ path to deploy custom .js or .sh setup scripts that will be run once on database initialisation. .js scripts will be run against test by default or MONGO_INITDB_DATABASE if defined in the environment.

COPY mysetup.sh /docker-entrypoint-initdb.d/

or

COPY mysetup.js /docker-entrypoint-initdb.d/

A simple initialisation javascript file that demonstrates logging and how to exit with an error (for result checking).

let error = true

let res = [
  db.container.drop(),
  db.container.drop(),
  db.container.createIndex({ myfield: 1 }, { unique: true }),
  db.container.createIndex({ thatfield: 1 }),
  db.container.createIndex({ thatfield: 1 }),
  db.container.insert({ myfield: 'hello', thatfield: 'testing' }),
  db.container.insert({ myfield: 'hello2', thatfield: 'testing' }),
  db.container.insert({ myfield: 'hello3', thatfield: 'testing' }),
  db.container.insert({ myfield: 'hello3', thatfield: 'testing' }),
]

printjson(res)

if (error) {
  print('Error, exiting')
  quit(1)
}


回答2:

In case someone is looking for how to configure MongoDB with authentication using docker-compose, here is a sample configuration using environment variables:

version: "3.3"

services:

  db:
      image: mongo
      environment:
        - MONGO_INITDB_ROOT_USERNAME=admin
        - MONGO_INITDB_ROOT_PASSWORD=<YOUR_PASSWORD>
      ports:
        - "27017:27017"

When running docker-compose up your mongo instance is run automatically with auth enabled. You will have a admin database with the given password.



回答3:

Here's a working solution that creates admin-user user with a password, additional database (test-db), and test-user in that database.

Dockerfile:

FROM mongo:4.0.3

ENV MONGO_INITDB_ROOT_USERNAME admin-user
ENV MONGO_INITDB_ROOT_PASSWORD admin-password
ENV MONGO_INITDB_DATABASE admin

ADD mongo-init.js /docker-entrypoint-initdb.d/

mongo-init.js:

db.auth('admin-user', 'admin-password')

db = db.getSiblingDB('test-database')

db.createUser({
  user: 'test-user',
  pwd: 'test-password',
  roles: [
    {
      role: 'root',
      db: 'admin',
    },
  ],
});

The tricky part was to understand that *.js files were run unauthenticated. The solution authenticates the script as the admin-user in the admin database. MONGO_INITDB_DATABASE admin is essential, otherwise the script would be executed against the test db. Check the source code of docker-entrypoint.sh.



回答4:

Mongo image can be affected by MONGO_INITDB_DATABASE variable, but it won't create the database. This variable determines current database when running /docker-entrypoint-initdb.d/* scripts. Since you can't use environment variables in scripts executed by Mongo, I went with a shell script:

docker-cloud.yml:

version: '3.1'

secrets:
  mongo-passwd:
    file: mongo-passwd

services:
  mongo:
    image: mongo:3.2
    environment:
      MONGO_DB: ${MONGO_DB}
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER}
      MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/mongo-passwd
    volumes:
      - ./init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh
    secrets:
      - mongo-passwd

init-mongo.sh:

mongo -- "$MONGO_DB" <<EOF
var user = '$MONGO_INITDB_ROOT_USERNAME';
var passwd = '$MONGO_INITDB_ROOT_PASSWORD';
var admin = db.getSiblingDB('admin');
admin.auth(user, passwd);
db.createUser({user: user, pwd: passwd, roles: ["readWrite"]});
EOF


回答5:

Here another cleaner solution by using docker-compose and a js script.

This example assumes that both files (docker-compose.yml and mongo-init.js) lay in the same folder.

docker-compose.yml

version: '3.7'

services:
    mongodb:
        image: mongo:latest
        container_name: mongodb
        restart: always
        environment:
            MONGO_INITDB_ROOT_USERNAME: <admin-user>
            MONGO_INITDB_ROOT_PASSWORD: <admin-password>
            MONGO_INITDB_DATABASE: <database to create>
        ports:
            - 27017:27017
        volumes:
            - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro

mongo-init.js

db.createUser(
        {
            user: "<user for database which shall be created>",
            pwd: "<password of user>",
            roles: [
                {
                    role: "readWrite",
                    db: "<database to create>"
                }
            ]
        }
);

Then simply start the service by running the following docker-compose command

docker-compose up --build -d mongodb 


回答6:

If you are looking to remove usernames and passwords from your docker-compose.yml you can use Docker Secrets, here is how I have approached it.

version: '3.6'

services:
  db:
    image: mongo:3
    container_name: mycontainer
  secrets:
    - MONGO_INITDB_ROOT_USERNAME
    - MONGO_INITDB_ROOT_PASSWORD
  environment:
    - MONGO_INITDB_ROOT_USERNAME_FILE=/var/run/secrets/MONGO_INITDB_ROOT_USERNAME
    - MONGO_INITDB_ROOT_PASSWORD_FILE=/var/run/secrets/MONGO_INITDB_ROOT_PASSWORD
secrets:
  MONGO_INITDB_ROOT_USERNAME:
    file:  secrets/${NODE_ENV}_mongo_root_username.txt
  MONGO_INITDB_ROOT_PASSWORD:
    file:  secrets/${NODE_ENV}_mongo_root_password.txt

I have use the file: option for my secrets however, you can also use external: and use the secrets in a swarm.

The secrets are available to any script in the container at /var/run/secrets

The Docker documentation has this to say about storing sensitive data...

https://docs.docker.com/engine/swarm/secrets/

You can use secrets to manage any sensitive data which a container needs at runtime but you don’t want to store in the image or in source control, such as:

Usernames and passwords TLS certificates and keys SSH keys Other important data such as the name of a database or internal server Generic strings or binary content (up to 500 kb in size)