From 6972078d6b58521eda6a2ca3e667ecdc7be426ec Mon Sep 17 00:00:00 2001 From: Tim Basel Date: Thu, 23 Mar 2017 06:46:13 +0100 Subject: [PATCH] Added Multi-Postgres Container --- Dockerfile | 69 +++++++++++++++ README.md | 5 ++ docker-entrypoint.sh | 201 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 Dockerfile create mode 100755 docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b3fc279 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,69 @@ +# vim:set ft=dockerfile: +FROM debian:jessie + +# explicitly set user/group IDs +RUN groupadd -r postgres --gid=999 && useradd -r -g postgres --uid=999 postgres + +# grab gosu for easy step-down from root +ENV GOSU_VERSION 1.7 +RUN set -x \ + && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \ + && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \ + && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \ + && export GNUPGHOME="$(mktemp -d)" \ + && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \ + && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \ + && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \ + && chmod +x /usr/local/bin/gosu \ + && gosu nobody true \ + && apt-get purge -y --auto-remove ca-certificates wget + +# make the "en_US.UTF-8" locale so postgres will be utf-8 enabled by default +RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* \ + && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 +ENV LANG en_US.utf8 + +RUN mkdir /docker-entrypoint-initdb.d + +RUN set -ex; \ +# pub 4096R/ACCC4CF8 2011-10-13 [expires: 2019-07-02] +# Key fingerprint = B97B 0AFC AA1A 47F0 44F2 44A0 7FCC 7D46 ACCC 4CF8 +# uid PostgreSQL Debian Repository + key='B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8'; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ + gpg --export "$key" > /etc/apt/trusted.gpg.d/postgres.gpg; \ + rm -r "$GNUPGHOME"; \ + apt-key list + +ENV PG_MAJOR 9.6 +ENV PG_VERSION 9.6.2-1.pgdg80+1 + +RUN echo 'deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main' $PG_MAJOR > /etc/apt/sources.list.d/pgdg.list + +RUN apt-get update \ + && apt-get install -y postgresql-common \ + && sed -ri 's/#(create_main_cluster) .*$/\1 = false/' /etc/postgresql-common/createcluster.conf \ + && apt-get install -y \ + postgresql-$PG_MAJOR=$PG_VERSION \ + postgresql-contrib-$PG_MAJOR=$PG_VERSION \ + && rm -rf /var/lib/apt/lists/* + +# make the sample config easier to munge (and "correct by default") +RUN mv -v /usr/share/postgresql/$PG_MAJOR/postgresql.conf.sample /usr/share/postgresql/ \ + && ln -sv ../postgresql.conf.sample /usr/share/postgresql/$PG_MAJOR/ \ + && sed -ri "s!^#?(listen_addresses)\s*=\s*\S+.*!\1 = '*'!" /usr/share/postgresql/postgresql.conf.sample + +RUN mkdir -p /var/run/postgresql && chown -R postgres:postgres /var/run/postgresql && chmod g+s /var/run/postgresql + +ENV PATH /usr/lib/postgresql/$PG_MAJOR/bin:$PATH +ENV PGDATA /var/lib/postgresql/data +RUN mkdir -p "$PGDATA" && chown -R postgres:postgres "$PGDATA" && chmod 777 "$PGDATA" # this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values) +VOLUME /var/lib/postgresql/data + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat +ENTRYPOINT ["docker-entrypoint.sh"] + +EXPOSE 5432 +CMD ["postgres"] diff --git a/README.md b/README.md index 374171b..d621c7d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # docker-postgres-multi + docker container which can handle multiple postgres databases and users instead of just one like the offical image. + +## Usage + +docker run --name postgres-mutli -e POSTGRES_USERS="user1:pass1|user2:pass2|user3:pass3" -e POSTGRES_DATABASES="database1:user1|database2:user2|database2:user3" -it --rm lmmdock/postgres-multi diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..27e59e8 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,201 @@ +#!/bin/bash +set -e + +# 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:0:1}" = '-' ]; then + set -- postgres "$@" +fi + +# allow the container to be started with `--user` +if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then + mkdir -p "$PGDATA" + chown -R postgres "$PGDATA" + chmod 700 "$PGDATA" + + mkdir -p /var/run/postgresql + chown -R postgres /var/run/postgresql + chmod g+s /var/run/postgresql + + exec gosu postgres "$BASH_SOURCE" "$@" +fi + +if [ "$1" = 'postgres' ]; then + mkdir -p "$PGDATA" + chown -R "$(id -u)" "$PGDATA" 2>/dev/null || : + chmod 700 "$PGDATA" 2>/dev/null || : + + # look specifically for PG_VERSION, as it is expected in the DB dir + if [ ! -s "$PGDATA/PG_VERSION" ]; then + file_env 'POSTGRES_INITDB_ARGS' + eval "initdb --username=postgres $POSTGRES_INITDB_ARGS" + + authMethod=trust + if [ "$POSTGRES_USERS" ]; then + USERS_ARR=$(echo $POSTGRES_USERS | tr "|" "\n") + for USER in $USERS_ARR + do + USER_PASSWORD=`echo $USER | cut -d: -f2` + if [ "$USER_PASSWORD" ]; then + authMethod=md5 + fi + done + fi + + # check password first so we can output the warning before postgres + # messes it up + file_env 'POSTGRES_PASSWORD' + if [ "$POSTGRES_PASSWORD" ]; then + pass="PASSWORD '$POSTGRES_PASSWORD'" + authMethod=md5 + else + pass= + fi + + if [ "$authMethod" == "trust" ]; then + cat >&2 <<-'EOWARN' + **************************************************** + WARNING: No password has been set for the database. + This will allow anyone with access to the + Postgres port to access your database. In + Docker's default configuration, this is + effectively any other container on the same + system. + + Use "-e POSTGRES_PASSWORD=password" to set + it in "docker run". + **************************************************** + EOWARN + fi + + { echo; echo "host all all all $authMethod"; } | tee -a "$PGDATA/pg_hba.conf" > /dev/null + + # internal start of server in order to allow set-up using psql-client + # does not listen on external TCP/IP and waits until start finishes + PGUSER="${PGUSER:-postgres}" \ + pg_ctl -D "$PGDATA" \ + -o "-c listen_addresses='localhost'" \ + -w start + + file_env 'POSTGRES_USER' 'postgres' + file_env 'POSTGRES_DB' "$POSTGRES_USER" + + psql=( psql -v ON_ERROR_STOP=1 ) + + if [ "$POSTGRES_DB" != 'postgres' ]; then + "${psql[@]}" --username postgres <<-EOSQL + CREATE DATABASE "$POSTGRES_DB" ; + EOSQL + echo + fi + + if [ "$POSTGRES_USER" = 'postgres' ]; then + op='ALTER' + else + op='CREATE' + fi + "${psql[@]}" --username postgres <<-EOSQL + $op USER "$POSTGRES_USER" WITH SUPERUSER $pass ; + EOSQL + echo + + psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" ) + + # If you want to create more than one user, please use that variable + # Variable example: POSTGRES_USERS="user1:user1pass|user2:user2pass|user3:user3password" + if [ "$POSTGRES_USERS" ]; then + USERS_ARR=$(echo $POSTGRES_USERS | tr "|" "\n") + for USER in $USERS_ARR + do + USER_NAME=`echo $USER | cut -d: -f1` + USER_PASSWORD=`echo $USER | cut -d: -f2` + if [ "$USER_NAME" = 'postgres' ]; then + op='ALTER' + else + op='CREATE' + fi + "${psql[@]}" --username postgres <<-EOSQL + $op USER "$USER_NAME" WITH SUPERUSER PASSWORD '$USER_PASSWORD' ; + EOSQL + done + fi + + # If you want to create more than one database, please use that variable + # Variable example: POSTGRES_DATABASES="database1:user1|database2:user2|database3:user3" + if [ "$POSTGRES_DATABASES" ]; then + DATABASES_ARR=$(echo $POSTGRES_DATABASES | tr "|" "\n") + for DATABASE in $DATABASES_ARR + do + DATABASE_NAME=`echo $DATABASE | cut -d: -f1` + DATABASE_OWNER=`echo $DATABASE | cut -d: -f2` + if [ "$DATABASE_NAME" != 'postgres' ]; then + if [ "$DATABASE_OWNER" ]; then + "${psql[@]}" --username postgres <<-EOSQL + CREATE DATABASE "$DATABASE_NAME" owner "$DATABASE_OWNER" ; + EOSQL + echo + else + "${psql[@]}" --username postgres <<-EOSQL + CREATE DATABASE "$DATABASE_NAME" ; + EOSQL + echo + fi + fi + done + fi + + # If you want to set up initial postgresql.conf parameters, please use that variable + # Variable example: POSTGRES_CONFIGS="work_mem:15MB|fsync:off|full_page_writes:off" + if [ "$POSTGRES_CONFIGS" ]; then + CONFIGS_ARR=$(echo $POSTGRES_CONFIGS | tr "|" "\n") + for CONFIG in $CONFIGS_ARR + do + CONFIG_NAME=`echo $CONFIG | cut -d: -f1` + CONFIG_VALUE=`echo $CONFIG | cut -d: -f2` + "${psql[@]}" --username postgres <<-EOSQL + ALTER SYSTEM SET $CONFIG_NAME = "$CONFIG_VALUE" ; + EOSQL + done + fi + + echo + for f in /docker-entrypoint-initdb.d/*; do + case "$f" in + *.sh) echo "$0: running $f"; . "$f" ;; + *.sql) echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;; + *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;; + *) echo "$0: ignoring $f" ;; + esac + echo + done + + PGUSER="${PGUSER:-postgres}" \ + pg_ctl -D "$PGDATA" -m fast -w stop + + echo + echo 'PostgreSQL init process complete; ready for start up.' + echo + fi +fi + +exec "$@"