├── .gitlab-ci.yml ├── Dockerfile ├── README.md ├── docker-compose.yml ├── docker-entrypoint.sh └── setup-replication.sh /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | 2 | services: 3 | - docker:dind 4 | 5 | build: 6 | image: danieldent/docker-builder 7 | script: 8 | - docker version 9 | - docker info 10 | - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY 11 | - docker build ${DOCKER_BUILD_ARGS} --force-rm --no-cache --pull -t ci-${CI_BUILD_ID} . 12 | - gitlab-ci-docker-retag.sh ci-${CI_BUILD_ID} $CI_REGISTRY_IMAGE 1 1 master 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:9.6 2 | MAINTAINER Daniel Dent (https://www.danieldent.com) 3 | ENV PG_MAX_WAL_SENDERS 8 4 | ENV PG_WAL_KEEP_SEGMENTS 8 5 | COPY setup-replication.sh /docker-entrypoint-initdb.d/ 6 | COPY docker-entrypoint.sh /docker-entrypoint.sh 7 | RUN chmod +x /docker-entrypoint-initdb.d/setup-replication.sh /docker-entrypoint.sh 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Postgres 9.6 Dockerized w/ Replication 2 | 3 | Master/Slave Postgres Replication in 30 seconds. 4 | 5 | * Quickstart: `docker-compose up` 6 | * For production, use docker-compose, Kubernetes, Rancher, Tutum, other PaaS tooling, ... or roll your own. 7 | * To see container environment variable requirements, see `docker-compose.yml`. 8 | * To demonstrate multiple slaves: 9 | * `docker-compose up` 10 | * `docker-compose scale pg-slave=3` 11 | 12 | ## Notes 13 | 14 | * No additional replication user is setup - the postgres admin user is used. This means the superuser credentials must be identical on the master and all slaves. 15 | * setup-replication.sh is only executed when a container's data volume is first initialized. 16 | * REPLICATE_FROM environment variable is only used during container initialization - if the master changes after the database has been initialized, you'll need to manually adjust the recovery.conf file in the slave containers' data volume. 17 | * Configuration: 18 | * PG_MAX_WAL_SENDERS 8 - Maximum number of slaves 19 | * PG_WAL_KEEP_SEGMENTS 32 - See http://www.postgresql.org/docs/9.6/static/runtime-config-replication.html 20 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | version: '2' 3 | 4 | services: 5 | pg-master: 6 | build: '.' 7 | image: 'danieldent/postgres-replication' 8 | restart: 'always' 9 | environment: 10 | POSTGRES_USER: 'postgres' 11 | POSTGRES_PASSWORD: 'postgres' 12 | PGDATA: '/var/lib/postgresql/data/pgdata' 13 | volumes: 14 | - '/var/lib/postgresql/data' 15 | expose: 16 | - '5432' 17 | 18 | pg-slave: 19 | build: '.' 20 | image: 'danieldent/postgres-replication' 21 | restart: 'always' 22 | environment: 23 | POSTGRES_USER: 'postgres' 24 | POSTGRES_PASSWORD: 'postgres' 25 | PGDATA: '/var/lib/postgresql/data/pgdata' 26 | REPLICATE_FROM: 'pg-master' 27 | volumes: 28 | - '/var/lib/postgresql/data' 29 | expose: 30 | - '5432' 31 | links: 32 | - 'pg-master' 33 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Backwards compatibility for old variable names (deprecated) 4 | if [ "x$PGUSER" != "x" ]; then 5 | POSTGRES_USER=$PGUSER 6 | fi 7 | if [ "x$PGPASSWORD" != "x" ]; then 8 | POSTGRES_PASSWORD=$PGPASSWORD 9 | fi 10 | 11 | # Forwards-compatibility for old variable names (pg_basebackup uses them) 12 | if [ "x$PGPASSWORD" = "x" ]; then 13 | export PGPASSWORD=$POSTGRES_PASSWORD 14 | fi 15 | 16 | # Based on official postgres package's entrypoint script (https://hub.docker.com/_/postgres/) 17 | # Modified to be able to set up a slave. The docker-entrypoint-initdb.d hook provided is inadequate. 18 | 19 | set -e 20 | 21 | if [ "${1:0:1}" = '-' ]; then 22 | set -- postgres "$@" 23 | fi 24 | 25 | if [ "$1" = 'postgres' ]; then 26 | mkdir -p "$PGDATA" 27 | chmod 700 "$PGDATA" 28 | chown -R postgres "$PGDATA" 29 | 30 | mkdir -p /run/postgresql 31 | chmod g+s /run/postgresql 32 | chown -R postgres /run/postgresql 33 | 34 | # look specifically for PG_VERSION, as it is expected in the DB dir 35 | if [ ! -s "$PGDATA/PG_VERSION" ]; then 36 | if [ "x$REPLICATE_FROM" == "x" ]; then 37 | eval "gosu postgres initdb $POSTGRES_INITDB_ARGS" 38 | else 39 | until ping -c 1 -W 1 ${REPLICATE_FROM} 40 | do 41 | echo "Waiting for master to ping..." 42 | sleep 1s 43 | done 44 | until gosu postgres pg_basebackup -h ${REPLICATE_FROM} -D ${PGDATA} -U ${POSTGRES_USER} -vP -w 45 | do 46 | echo "Waiting for master to connect..." 47 | sleep 1s 48 | done 49 | fi 50 | 51 | # check password first so we can output the warning before postgres 52 | # messes it up 53 | if [ "$POSTGRES_PASSWORD" ]; then 54 | pass="PASSWORD '$POSTGRES_PASSWORD'" 55 | authMethod=md5 56 | else 57 | # The - option suppresses leading tabs but *not* spaces. :) 58 | cat >&2 <<-'EOWARN' 59 | **************************************************** 60 | WARNING: No password has been set for the database. 61 | This will allow anyone with access to the 62 | Postgres port to access your database. In 63 | Docker's default configuration, this is 64 | effectively any other container on the same 65 | system. 66 | 67 | Use "-e POSTGRES_PASSWORD=password" to set 68 | it in "docker run". 69 | **************************************************** 70 | EOWARN 71 | 72 | pass= 73 | authMethod=trust 74 | fi 75 | 76 | if [ "x$REPLICATE_FROM" == "x" ]; then 77 | 78 | { echo; echo "host replication all 0.0.0.0/0 $authMethod"; } | gosu postgres tee -a "$PGDATA/pg_hba.conf" > /dev/null 79 | { echo; echo "host all all 0.0.0.0/0 $authMethod"; } | gosu postgres tee -a "$PGDATA/pg_hba.conf" > /dev/null 80 | 81 | # internal start of server in order to allow set-up using psql-client 82 | # does not listen on external TCP/IP and waits until start finishes 83 | gosu postgres pg_ctl -D "$PGDATA" \ 84 | -o "-c listen_addresses='localhost'" \ 85 | -w start 86 | 87 | : ${POSTGRES_USER:=postgres} 88 | : ${POSTGRES_DB:=$POSTGRES_USER} 89 | export POSTGRES_USER POSTGRES_DB 90 | 91 | psql=( psql -v ON_ERROR_STOP=1 ) 92 | 93 | if [ "$POSTGRES_DB" != 'postgres' ]; then 94 | "${psql[@]}" --username postgres <<-EOSQL 95 | CREATE DATABASE "$POSTGRES_DB" ; 96 | EOSQL 97 | echo 98 | fi 99 | 100 | if [ "$POSTGRES_USER" = 'postgres' ]; then 101 | op='ALTER' 102 | else 103 | op='CREATE' 104 | fi 105 | "${psql[@]}" --username postgres <<-EOSQL 106 | $op USER "$POSTGRES_USER" WITH SUPERUSER $pass ; 107 | EOSQL 108 | echo 109 | 110 | fi 111 | 112 | psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" ) 113 | 114 | echo 115 | for f in /docker-entrypoint-initdb.d/*; do 116 | case "$f" in 117 | *.sh) echo "$0: running $f"; . "$f" ;; 118 | *.sql) echo "$0: running $f"; "${psql[@]}" < "$f"; echo ;; 119 | *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;; 120 | *) echo "$0: ignoring $f" ;; 121 | esac 122 | echo 123 | done 124 | 125 | if [ "x$REPLICATE_FROM" == "x" ]; then 126 | gosu postgres pg_ctl -D "$PGDATA" -m fast -w stop 127 | fi 128 | 129 | echo 130 | echo 'PostgreSQL init process complete; ready for start up.' 131 | echo 132 | fi 133 | 134 | exec gosu postgres "$@" 135 | fi 136 | 137 | exec "$@" 138 | -------------------------------------------------------------------------------- /setup-replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "x$REPLICATE_FROM" == "x" ]; then 4 | 5 | cat >> ${PGDATA}/postgresql.conf < ${PGDATA}/recovery.conf <