├── .travis.yml ├── docker-compose.yml ├── 10-config.sh ├── Dockerfile ├── 20-replication.sh ├── tests └── run.sh └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | services: docker 3 | 4 | env: 5 | - VERSION=latest 6 | 7 | before_script: 8 | - env | sort 9 | - image="nebirhos/postgres-replication:$VERSION" 10 | 11 | script: 12 | - docker build -t "$image" . 13 | - ./tests/run.sh "$image" 14 | 15 | after_script: 16 | - docker images 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | postgres-slave: 2 | image: "nebirhos/postgres-replication" 3 | ports: 4 | - 5433:5432 5 | links: 6 | - postgres-master 7 | environment: 8 | REPLICATION_ROLE: slave 9 | POSTGRES_MASTER_SERVICE_HOST: postgres-master 10 | 11 | postgres-master: 12 | image: "nebirhos/postgres-replication" 13 | ports: 14 | - 5432:5432 15 | -------------------------------------------------------------------------------- /10-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo [*] configuring $REPLICATION_ROLE instance 5 | 6 | echo "max_connections = $MAX_CONNECTIONS" >> "$PGDATA/postgresql.conf" 7 | 8 | # We set master replication-related parameters for both slave and master, 9 | # so that the slave might work as a primary after failover. 10 | echo "wal_level = hot_standby" >> "$PGDATA/postgresql.conf" 11 | echo "wal_keep_segments = $WAL_KEEP_SEGMENTS" >> "$PGDATA/postgresql.conf" 12 | echo "max_wal_senders = $MAX_WAL_SENDERS" >> "$PGDATA/postgresql.conf" 13 | # slave settings, ignored on master 14 | echo "hot_standby = on" >> "$PGDATA/postgresql.conf" 15 | 16 | 17 | echo "host replication $REPLICATION_USER 0.0.0.0/0 trust" >> "$PGDATA/pg_hba.conf" 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # -*- mode: conf -*- 2 | FROM postgres:9.6 3 | 4 | MAINTAINER me@nebirhos.com 5 | 6 | # common settings 7 | ENV MAX_CONNECTIONS 500 8 | ENV WAL_KEEP_SEGMENTS 256 9 | ENV MAX_WAL_SENDERS 100 10 | 11 | # master/slave settings 12 | ENV REPLICATION_ROLE master 13 | ENV REPLICATION_USER replication 14 | ENV REPLICATION_PASSWORD "" 15 | 16 | # slave settings 17 | ENV POSTGRES_MASTER_SERVICE_HOST localhost 18 | ENV POSTGRES_MASTER_SERVICE_PORT 5432 19 | 20 | COPY 10-config.sh /docker-entrypoint-initdb.d/ 21 | COPY 20-replication.sh /docker-entrypoint-initdb.d/ 22 | # Evaluate vars inside PGDATA at runtime. 23 | # For example HOSTNAME in 'ENV PGDATA=/mnt/$HOSTNAME' 24 | # is resolved runtime rather then during build 25 | RUN sed -i 's/set -e/set -e -x\nPGDATA=$(eval echo "$PGDATA")/' /docker-entrypoint.sh 26 | -------------------------------------------------------------------------------- /20-replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ $REPLICATION_ROLE = "master" ]; then 5 | psql -U postgres -c "CREATE ROLE $REPLICATION_USER WITH REPLICATION PASSWORD '$REPLICATION_PASSWORD' LOGIN" 6 | 7 | elif [ $REPLICATION_ROLE = "slave" ]; then 8 | # stop postgres instance and reset PGDATA, 9 | # confs will be copied by pg_basebackup 10 | pg_ctl -D "$PGDATA" -m fast -w stop 11 | # make sure standby's data directory is empty 12 | rm -r "$PGDATA"/* 13 | 14 | pg_basebackup \ 15 | --write-recovery-conf \ 16 | --pgdata="$PGDATA" \ 17 | --xlog-method=fetch \ 18 | --username=$REPLICATION_USER \ 19 | --host=$POSTGRES_MASTER_SERVICE_HOST \ 20 | --port=$POSTGRES_MASTER_SERVICE_PORT \ 21 | --progress \ 22 | --verbose 23 | 24 | # useless postgres start to fullfil docker-entrypoint.sh stop 25 | pg_ctl -D "$PGDATA" \ 26 | -o "-c listen_addresses=''" \ 27 | -w start 28 | fi 29 | 30 | echo [*] $REPLICATION_ROLE instance configured! 31 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | image="$1" 5 | 6 | export POSTGRES_USER='postgres' 7 | export POSTGRES_PASSWORD='' 8 | export POSTGRES_DB='postgres' 9 | 10 | psql() { 11 | role="$1" 12 | docker run --rm -i \ 13 | --link "postgres-$role" \ 14 | --entrypoint psql \ 15 | "$image" \ 16 | --host "postgres-$role" \ 17 | --username "$POSTGRES_USER" \ 18 | --dbname "$POSTGRES_DB" \ 19 | --no-align --tuples-only \ 20 | -c "${@:2}" 21 | } 22 | 23 | poll() { 24 | set +e 25 | MAX_TRIES=$1 26 | tries=0 27 | while true 28 | do 29 | sleep 2 30 | psql master '\l' > /dev/null 2> /dev/null && psql slave '\l' > /dev/null 2> /dev/null 31 | if [ $? -eq 0 ]; then 32 | break 33 | else 34 | echo "Waiting for db to be up..." 35 | ((tries++)) 36 | fi 37 | if [ $tries -eq $MAX_TRIES ]; then 38 | echo "FAIL: cannot connect to postgres" 39 | exit 1 40 | fi 41 | done 42 | set -e 43 | } 44 | 45 | mid=$(docker run -d --name postgres-master "$image") 46 | sid=$(docker run -d --name postgres-slave \ 47 | --link postgres-master \ 48 | -e POSTGRES_MASTER_SERVICE_HOST=postgres-master \ 49 | -e REPLICATION_ROLE=slave \ 50 | -t "$image") 51 | trap "docker rm -f $mid $sid > /dev/null" EXIT 52 | 53 | poll 3 times 54 | psql master "CREATE TABLE replication_test (a INT, b INT, c VARCHAR(255))" 55 | psql master "INSERT INTO replication_test VALUES (1, 2, 'it works')" 56 | 57 | output=$(psql slave "SELECT c from replication_test") 58 | if [ "$output" == 'it works' ]; then 59 | echo "OK" 60 | exit 0 61 | else 62 | echo "FAIL" 63 | exit 1 64 | fi 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Postgres Streaming Replication 2 | ============================== 3 | 4 | [![Build Status](https://travis-ci.org/nebirhos/docker-postgres-replication.svg?branch=master)](https://travis-ci.org/nebirhos/docker-postgres-replication) 5 | [![](https://imagelayers.io/badge/nebirhos/postgres-replication:latest.svg)](https://imagelayers.io/?images=nebirhos/postgres-replication:latest 'Get your own badge on imagelayers.io') 6 | 7 | 8 | Enhanced version of the official Postgres image to support streaming replication 9 | out of the box. 10 | 11 | Postgres-replication is meant to be used with orchestration systems such as Kubernetes. 12 | 13 | 14 | Tags 15 | ---- 16 | 17 | Images are automatically updated when the [official postgres image](https://hub.docker.com/_/postgres/) 18 | is updated. 19 | 20 | Supported tags: 21 | 22 | * 9.6, 9, latest ([9.6](https://github.com/nebirhos/docker-postgres-replication/tree/9.6)) 23 | * 9.5 ([9.5](https://github.com/nebirhos/docker-postgres-replication/tree/9.5)) 24 | 25 | 26 | Run with Docker Compose 27 | ----------------------- 28 | 29 | ``` 30 | docker-compose up 31 | ``` 32 | 33 | 34 | Run with Docker 35 | --------------- 36 | 37 | To run with Docker, first run the Postgres master: 38 | 39 | ``` 40 | docker run -p 127.0.0.1:5432:5432 --name postgres-master nebirhos/postgres-replication 41 | ``` 42 | 43 | 44 | Then Postgres slave(s): 45 | 46 | ``` 47 | docker run -p 127.0.0.1:5433:5432 --link postgres-master \ 48 | -e POSTGRES_MASTER_SERVICE_HOST=postgres-master \ 49 | -e REPLICATION_ROLE=slave \ 50 | -t nebirhos/postgres-replication 51 | ``` 52 | 53 | 54 | Notes 55 | ----- 56 | 57 | Replication is set up at container start by putting scripts in the `/docker-entrypoint-initdb.d` folder. 58 | This way the original Postgres image scripts are left untouched. 59 | 60 | See [Dockerfile](Dockerfile) and [official Postgres image](https://hub.docker.com/_/postgres/) 61 | for custom environment variables. 62 | --------------------------------------------------------------------------------