Skip to content

Add "docker-entrypoint-initdb.d" behavior which mimics PostgreSQL, including (optional) automated "root" user creation #145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions 3.0/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
FROM debian:wheezy
FROM debian:wheezy-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mongodb && useradd -r -g mongodb mongodb

# add "wheezy-backports" for "jq"
RUN echo 'deb http://deb.debian.org/debian wheezy-backports main' > /etc/apt/sources.list.d/backports.list

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
jq \
numactl \
&& rm -rf /var/lib/apt/lists/*

Expand All @@ -22,6 +26,8 @@ RUN set -x \
&& gosu nobody true \
&& apt-get purge -y --auto-remove ca-certificates wget

RUN mkdir /docker-entrypoint-initdb.d

ENV GPG_KEYS \
# gpg: key 7F0CEB10: public key "Richard Kreuter <[email protected]>" imported
492EAFE8CD016A07919F1D2B9ECBEC467F0CEB10
Expand Down Expand Up @@ -56,8 +62,9 @@ RUN mkdir -p /data/db /data/configdb \
&& chown -R mongodb:mongodb /data/db /data/configdb
VOLUME /data/db /data/configdb

COPY docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 27017
CMD ["mongod"]
99 changes: 99 additions & 0 deletions 3.0/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ if [[ "$1" == mongo* ]] && [ "$(id -u)" = '0' ]; then
if [ "$1" = 'mongod' ]; then
chown -R mongodb /data/configdb /data/db
fi
chown --dereference mongodb /dev/stdout /dev/stderr
exec gosu mongodb "$BASH_SOURCE" "$@"
fi

Expand All @@ -23,4 +24,102 @@ if [[ "$1" == mongo* ]]; then
fi
fi

# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}

if [ "$1" = 'mongod' ]; then
file_env 'MONGO_INITDB_ROOT_USERNAME'
file_env 'MONGO_INITDB_ROOT_PASSWORD'
if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
set -- "$@" --auth
fi

# check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts)
definitelyAlreadyInitialized=
for path in \
/data/db/WiredTiger \
/data/db/journal \
/data/db/local.0 \
/data/db/storage.bson \
; do
if [ -e "$path" ]; then
definitelyAlreadyInitialized="$path"
break
fi
done

if [ -z "$definitelyAlreadyInitialized" ]; then
"$@" --fork --bind_ip 127.0.0.1 --logpath "/proc/$$/fd/1"

mongo=( mongo --quiet )

# check to see that "mongod" actually did start up (catches "--help", "--version", MongoDB 3.2 being silly, etc)
if ! "${mongo[@]}" 'admin' --eval 'quit(0)' &> /dev/null; then
echo >&2
echo >&2 'error: mongod does not appear to have started up -- perhaps it had an error?'
echo >&2
exit 1
fi

if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
rootAuthDatabase='admin'

"${mongo[@]}" "$rootAuthDatabase" <<-EOJS
db.createUser({
user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'),
pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'),
roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ]
})
EOJS

mongo+=(
--username="$MONGO_INITDB_ROOT_USERNAME"
--password="$MONGO_INITDB_ROOT_PASSWORD"
--authenticationDatabase="$rootAuthDatabase"
)
fi

export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}"

echo
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.js) echo "$0: running $f"; "${mongo[@]}" "$MONGO_INITDB_DATABASE" "$f"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done

"$@" --shutdown

echo
echo 'MongoDB init process complete; ready for start up.'
echo
fi

unset MONGO_INITDB_ROOT_USERNAME
unset MONGO_INITDB_ROOT_PASSWORD
unset MONGO_INITDB_DATABASE
fi

exec "$@"
10 changes: 7 additions & 3 deletions 3.2/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
FROM debian:jessie
FROM debian:jessie-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mongodb && useradd -r -g mongodb mongodb

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
jq \
numactl \
&& rm -rf /var/lib/apt/lists/*

Expand All @@ -22,6 +23,8 @@ RUN set -x \
&& gosu nobody true \
&& apt-get purge -y --auto-remove ca-certificates wget

RUN mkdir /docker-entrypoint-initdb.d

ENV GPG_KEYS \
# pub 4096R/AAB2461C 2014-02-25 [expires: 2016-02-25]
# Key fingerprint = DFFA 3DCF 326E 302C 4787 673A 01C4 E7FA AAB2 461C
Expand Down Expand Up @@ -62,8 +65,9 @@ RUN mkdir -p /data/db /data/configdb \
&& chown -R mongodb:mongodb /data/db /data/configdb
VOLUME /data/db /data/configdb

COPY docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 27017
CMD ["mongod"]
104 changes: 104 additions & 0 deletions 3.2/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ if [[ "$1" == mongo* ]] && [ "$(id -u)" = '0' ]; then
if [ "$1" = 'mongod' ]; then
chown -R mongodb /data/configdb /data/db
fi
chown --dereference mongodb /dev/stdout /dev/stderr
exec gosu mongodb "$BASH_SOURCE" "$@"
fi

Expand All @@ -23,4 +24,107 @@ if [[ "$1" == mongo* ]]; then
fi
fi

# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}

if [ "$1" = 'mongod' ]; then
file_env 'MONGO_INITDB_ROOT_USERNAME'
file_env 'MONGO_INITDB_ROOT_PASSWORD'
if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
set -- "$@" --auth
fi

# check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts)
definitelyAlreadyInitialized=
for path in \
/data/db/WiredTiger \
/data/db/journal \
/data/db/local.0 \
/data/db/storage.bson \
; do
if [ -e "$path" ]; then
definitelyAlreadyInitialized="$path"
break
fi
done

if [ -z "$definitelyAlreadyInitialized" ]; then
"$@" --fork --bind_ip 127.0.0.1 --logpath "/proc/$$/fd/1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem as mysql 😢

  -h [ --help ]                         show this usage information
  --version                             show version information

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh boo, that's cute:

$ docker run -it --rm mongo:3.4 --version
db version v3.4.2
git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: debian81
    distarch: x86_64
    target_arch: x86_64

/usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*

db version v3.4.2
git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: debian81
    distarch: x86_64
    target_arch: x86_64

MongoDB init process complete; ready for start up.

db version v3.4.2
git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: debian81
    distarch: x86_64
    target_arch: x86_64

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be somewhat solved if we added MongoDB 3.2's check-and-retry-once logic to everything, since we'd then be at least verifying that the daemon actually did start up properly. Perhaps just adding the "check" portion of that would be sufficient?

Do you think we really need to go to the extreme of explicitly detecting --home and/or --version directly? (especially since there are other things that might cause the daemon to not start up correctly too)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check and retry on all versions would be a good addition, but won't it still print multiple times or at least be silly and print error: mongod does not appear to have started up quickly enough ... when a user was just getting --help/--version?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it'll print an extra error, but it'll only print once, and I think it still serves the purpose just fine -- users who don't want to run the daemon explicitly really ought to simply be bypassing our entrypoint entirely, IMO (but at least adding this would provide the expected information):

$ docker run -it --rm mongo:3.2 --version
db version v3.2.12
git version: ef3e1bc78e997f0d9f22f45aeb1d8e3b6ac14a14
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: debian81
    distarch: x86_64
    target_arch: x86_64
error: mongod does not appear to have started up quickly enough -- perhaps it had an error?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicitly? but that is 7 extra characters " mongod" 😉 ¯\_(ツ)_/¯

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR is updated to check whether mongod actually started in all versions now (and I updated 3.2's check to diff cleanly/consistently).

We're still providing the information they asked for, they just get some harmless extra error output (telling them that initdb failed due to the flags they requested we start it with). For interactive users, they'll usually be concerned about getting the flag name they need so they can either update their script or their ad-hoc invocation (which this provides), and if users are actually using --help or --version in a script, I really would doubly recommend they use --entrypoint or something to bypass our extra initdb behavior explicitly, but I don't see a real strong use case for putting those in scripts 😕.

IMO adding an explicit check for --version or --help is overkill (added lines to our script for small benefit), but I am willing to add them if you really insist and feel strongly that they provide useful value to our users (commensurate to the added maintenance burden of debugging/enhancing them if future versions of MongoDB change things, debugging our implementation, etc).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the final output, for context:

$ docker run -it --rm mongo:3.4 --version
db version v3.4.2
git version: 3f76e40c105fc223b3e5aac3e20dcd026b83b38b
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: debian81
    distarch: x86_64
    target_arch: x86_64

error: mongod does not appear to have started up -- perhaps it had an error?

$ docker run -it --rm mongo:3.2 --version
db version v3.2.12
git version: ef3e1bc78e997f0d9f22f45aeb1d8e3b6ac14a14
OpenSSL version: OpenSSL 1.0.1t  3 May 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: debian81
    distarch: x86_64
    target_arch: x86_64

error: mongod does not appear to have started up -- perhaps it had an error?

$ docker run -it --rm mongo:3.0 --version
db version v3.0.14
git version: 08352afcca24bfc145240a0fac9d28b978ab77f3

error: mongod does not appear to have started up -- perhaps it had an error?

$ 


mongo=( mongo --quiet )

# check to see that "mongod" actually did start up (catches "--help", "--version", MongoDB 3.2 being silly, etc)
if ! "${mongo[@]}" 'admin' --eval 'quit(0)' &> /dev/null; then
# TODO figure out why only MongoDB 3.2 seems to exit from "--fork" before it's actually listening
# (adding "sleep 0.01" was sufficient on Tianon's local box, hence this tiny "retry up to one time")
sleep 5
fi
if ! "${mongo[@]}" 'admin' --eval 'quit(0)' &> /dev/null; then
echo >&2
echo >&2 'error: mongod does not appear to have started up -- perhaps it had an error?'
echo >&2
exit 1
fi

if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
rootAuthDatabase='admin'

"${mongo[@]}" "$rootAuthDatabase" <<-EOJS
db.createUser({
user: $(jq --arg 'user' "$MONGO_INITDB_ROOT_USERNAME" --null-input '$user'),
pwd: $(jq --arg 'pwd' "$MONGO_INITDB_ROOT_PASSWORD" --null-input '$pwd'),
roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ]
})
EOJS

mongo+=(
--username="$MONGO_INITDB_ROOT_USERNAME"
--password="$MONGO_INITDB_ROOT_PASSWORD"
--authenticationDatabase="$rootAuthDatabase"
)
fi

export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}"

echo
for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.js) echo "$0: running $f"; "${mongo[@]}" "$MONGO_INITDB_DATABASE" "$f"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done

"$@" --shutdown

echo
echo 'MongoDB init process complete; ready for start up.'
echo
fi

unset MONGO_INITDB_ROOT_USERNAME
unset MONGO_INITDB_ROOT_PASSWORD
unset MONGO_INITDB_DATABASE
fi

exec "$@"
10 changes: 7 additions & 3 deletions 3.4/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
FROM debian:jessie
FROM debian:jessie-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mongodb && useradd -r -g mongodb mongodb

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
jq \
numactl \
&& rm -rf /var/lib/apt/lists/*

Expand All @@ -22,6 +23,8 @@ RUN set -x \
&& gosu nobody true \
&& apt-get purge -y --auto-remove ca-certificates wget

RUN mkdir /docker-entrypoint-initdb.d

ENV GPG_KEYS \
# pub 4096R/A15703C6 2016-01-11 [expires: 2018-01-10]
# Key fingerprint = 0C49 F373 0359 A145 1858 5931 BC71 1F9B A157 03C6
Expand Down Expand Up @@ -58,8 +61,9 @@ RUN mkdir -p /data/db /data/configdb \
&& chown -R mongodb:mongodb /data/db /data/configdb
VOLUME /data/db /data/configdb

COPY docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 27017
CMD ["mongod"]
Loading