Skip to content

Commit cd2eb4d

Browse files
committed
Add support for master/slave mode
This patch adds support for simple unintrusive master/slave setup. It makes no changes for the single-node use-case, but adds two new environment variables: `POSTGRES_NUM_SLAVES` and `POSTGRES_MASTER_HOST`. These enables the following simple scenario: ``` version: '3.1' services: pgmaster: image: postgres environment: POSTGRES_NUM_SLAVES: 1 pgslave: image: postgres depends_on: - pgmaster restart: always environment: POSTGRES_MASTER_HOST: pgmaster ``` refs #55, #88, #104
1 parent 165ffaa commit cd2eb4d

File tree

11 files changed

+1089
-836
lines changed

11 files changed

+1089
-836
lines changed

10/alpine/docker-entrypoint.sh

Lines changed: 99 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -54,91 +54,114 @@ if [ "$1" = 'postgres' ]; then
5454

5555
# look specifically for PG_VERSION, as it is expected in the DB dir
5656
if [ ! -s "$PGDATA/PG_VERSION" ]; then
57-
file_env 'POSTGRES_INITDB_ARGS'
58-
if [ "$POSTGRES_INITDB_WALDIR" ]; then
59-
export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
60-
fi
61-
eval "initdb --username=postgres $POSTGRES_INITDB_ARGS"
62-
63-
# check password first so we can output the warning before postgres
64-
# messes it up
65-
file_env 'POSTGRES_PASSWORD'
66-
if [ "$POSTGRES_PASSWORD" ]; then
67-
pass="PASSWORD '$POSTGRES_PASSWORD'"
68-
authMethod=md5
69-
else
70-
# The - option suppresses leading tabs but *not* spaces. :)
71-
cat >&2 <<-'EOWARN'
72-
****************************************************
73-
WARNING: No password has been set for the database.
74-
This will allow anyone with access to the
75-
Postgres port to access your database. In
76-
Docker's default configuration, this is
77-
effectively any other container on the same
78-
system.
79-
80-
Use "-e POSTGRES_PASSWORD=password" to set
81-
it in "docker run".
82-
****************************************************
83-
EOWARN
84-
85-
pass=
86-
authMethod=trust
87-
fi
88-
89-
{
90-
echo
91-
echo "host all all all $authMethod"
92-
} >> "$PGDATA/pg_hba.conf"
93-
94-
# internal start of server in order to allow set-up using psql-client
95-
# does not listen on external TCP/IP and waits until start finishes
96-
PGUSER="${PGUSER:-postgres}" \
97-
pg_ctl -D "$PGDATA" \
98-
-o "-c listen_addresses='localhost'" \
99-
-w start
100-
10157
file_env 'POSTGRES_USER' 'postgres'
10258
file_env 'POSTGRES_DB' "$POSTGRES_USER"
103-
104-
psql=( psql -v ON_ERROR_STOP=1 )
105-
106-
if [ "$POSTGRES_DB" != 'postgres' ]; then
59+
file_env 'POSTGRES_PASSWORD'
60+
file_env 'POSTGRES_NUM_SLAVES'
61+
file_env 'POSTGRES_MASTER_HOST'
62+
63+
if [ ! "$POSTGRES_MASTER_HOST" ]; then
64+
file_env 'POSTGRES_INITDB_ARGS'
65+
if [ "$POSTGRES_INITDB_WALDIR" ]; then
66+
export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
67+
fi
68+
eval "initdb --username=postgres $POSTGRES_INITDB_ARGS"
69+
70+
# check password first so we can output the warning before postgres
71+
# messes it up
72+
if [ "$POSTGRES_PASSWORD" ]; then
73+
pass="PASSWORD '$POSTGRES_PASSWORD'"
74+
authMethod=md5
75+
else
76+
# The - option suppresses leading tabs but *not* spaces. :)
77+
cat >&2 <<-'EOWARN'
78+
****************************************************
79+
WARNING: No password has been set for the database.
80+
This will allow anyone with access to the
81+
Postgres port to access your database. In
82+
Docker's default configuration, this is
83+
effectively any other container on the same
84+
system.
85+
86+
Use "-e POSTGRES_PASSWORD=password" to set
87+
it in "docker run".
88+
****************************************************
89+
EOWARN
90+
91+
pass=
92+
authMethod=trust
93+
fi
94+
95+
{
96+
echo
97+
echo "host replication all all $authMethod"
98+
echo "host all all all $authMethod"
99+
} >> "$PGDATA/pg_hba.conf"
100+
101+
# internal start of server in order to allow set-up using psql-client
102+
# does not listen on external TCP/IP and waits until start finishes
103+
PGUSER="${PGUSER:-postgres}" \
104+
pg_ctl -D "$PGDATA" \
105+
-o "-c listen_addresses='localhost'" \
106+
-w start
107+
108+
psql=( psql -v ON_ERROR_STOP=1 )
109+
110+
if [ "$POSTGRES_DB" != 'postgres' ]; then
111+
"${psql[@]}" --username postgres <<-EOSQL
112+
CREATE DATABASE "$POSTGRES_DB" ;
113+
EOSQL
114+
echo
115+
fi
116+
117+
if [ "$POSTGRES_USER" = 'postgres' ]; then
118+
op='ALTER'
119+
else
120+
op='CREATE'
121+
fi
107122
"${psql[@]}" --username postgres <<-EOSQL
108-
CREATE DATABASE "$POSTGRES_DB" ;
123+
$op USER "$POSTGRES_USER" WITH SUPERUSER $pass ;
109124
EOSQL
110125
echo
111-
fi
112126

113-
if [ "$POSTGRES_USER" = 'postgres' ]; then
114-
op='ALTER'
115-
else
116-
op='CREATE'
117-
fi
118-
"${psql[@]}" --username postgres <<-EOSQL
119-
$op USER "$POSTGRES_USER" WITH SUPERUSER $pass ;
120-
EOSQL
121-
echo
122-
123-
psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" )
124-
125-
echo
126-
for f in /docker-entrypoint-initdb.d/*; do
127-
case "$f" in
128-
*.sh) echo "$0: running $f"; . "$f" ;;
129-
*.sql) echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
130-
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
131-
*) echo "$0: ignoring $f" ;;
132-
esac
127+
psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" )
128+
133129
echo
134-
done
130+
for f in /docker-entrypoint-initdb.d/*; do
131+
case "$f" in
132+
*.sh) echo "$0: running $f"; . "$f" ;;
133+
*.sql) echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
134+
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
135+
*) echo "$0: ignoring $f" ;;
136+
esac
137+
echo
138+
done
139+
140+
PGUSER="${PGUSER:-postgres}" \
141+
pg_ctl -D "$PGDATA" -m fast -w stop
142+
143+
# do this after initdb, otherwise synchronous_standby_names causes a deadlock
144+
if [ $POSTGRES_NUM_SLAVES -gt 0 ]; then
145+
echo "enabling replication"
146+
{
147+
echo
148+
echo "wal_level = hot_standby"
149+
echo "max_wal_senders = $(($POSTGRES_NUM_SLAVES * 2))" # each slave needs 2 connections when comming up
150+
echo "hot_standby = on"
151+
echo "synchronous_standby_names = '*'"
152+
} >> "$PGDATA/postgresql.conf"
153+
fi
135154

136-
PGUSER="${PGUSER:-postgres}" \
137-
pg_ctl -D "$PGDATA" -m fast -w stop
155+
echo
156+
echo 'PostgreSQL init process complete; ready for start up.'
157+
echo
158+
else
159+
eval "pg_basebackup -D $PGDATA -R -Xs -P -d \"host=$POSTGRES_MASTER_HOST user=$POSTGRES_USER password=$POSTGRES_PASSWORD \""
138160

139-
echo
140-
echo 'PostgreSQL init process complete; ready for start up.'
141-
echo
161+
echo
162+
echo "PostgreSQL basebackup complete; ready for start up."
163+
echo
164+
fi
142165
fi
143166
fi
144167

10/docker-entrypoint.sh

Lines changed: 99 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -54,91 +54,114 @@ if [ "$1" = 'postgres' ]; then
5454

5555
# look specifically for PG_VERSION, as it is expected in the DB dir
5656
if [ ! -s "$PGDATA/PG_VERSION" ]; then
57-
file_env 'POSTGRES_INITDB_ARGS'
58-
if [ "$POSTGRES_INITDB_WALDIR" ]; then
59-
export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
60-
fi
61-
eval "initdb --username=postgres $POSTGRES_INITDB_ARGS"
62-
63-
# check password first so we can output the warning before postgres
64-
# messes it up
65-
file_env 'POSTGRES_PASSWORD'
66-
if [ "$POSTGRES_PASSWORD" ]; then
67-
pass="PASSWORD '$POSTGRES_PASSWORD'"
68-
authMethod=md5
69-
else
70-
# The - option suppresses leading tabs but *not* spaces. :)
71-
cat >&2 <<-'EOWARN'
72-
****************************************************
73-
WARNING: No password has been set for the database.
74-
This will allow anyone with access to the
75-
Postgres port to access your database. In
76-
Docker's default configuration, this is
77-
effectively any other container on the same
78-
system.
79-
80-
Use "-e POSTGRES_PASSWORD=password" to set
81-
it in "docker run".
82-
****************************************************
83-
EOWARN
84-
85-
pass=
86-
authMethod=trust
87-
fi
88-
89-
{
90-
echo
91-
echo "host all all all $authMethod"
92-
} >> "$PGDATA/pg_hba.conf"
93-
94-
# internal start of server in order to allow set-up using psql-client
95-
# does not listen on external TCP/IP and waits until start finishes
96-
PGUSER="${PGUSER:-postgres}" \
97-
pg_ctl -D "$PGDATA" \
98-
-o "-c listen_addresses='localhost'" \
99-
-w start
100-
10157
file_env 'POSTGRES_USER' 'postgres'
10258
file_env 'POSTGRES_DB' "$POSTGRES_USER"
103-
104-
psql=( psql -v ON_ERROR_STOP=1 )
105-
106-
if [ "$POSTGRES_DB" != 'postgres' ]; then
59+
file_env 'POSTGRES_PASSWORD'
60+
file_env 'POSTGRES_NUM_SLAVES'
61+
file_env 'POSTGRES_MASTER_HOST'
62+
63+
if [ ! "$POSTGRES_MASTER_HOST" ]; then
64+
file_env 'POSTGRES_INITDB_ARGS'
65+
if [ "$POSTGRES_INITDB_WALDIR" ]; then
66+
export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
67+
fi
68+
eval "initdb --username=postgres $POSTGRES_INITDB_ARGS"
69+
70+
# check password first so we can output the warning before postgres
71+
# messes it up
72+
if [ "$POSTGRES_PASSWORD" ]; then
73+
pass="PASSWORD '$POSTGRES_PASSWORD'"
74+
authMethod=md5
75+
else
76+
# The - option suppresses leading tabs but *not* spaces. :)
77+
cat >&2 <<-'EOWARN'
78+
****************************************************
79+
WARNING: No password has been set for the database.
80+
This will allow anyone with access to the
81+
Postgres port to access your database. In
82+
Docker's default configuration, this is
83+
effectively any other container on the same
84+
system.
85+
86+
Use "-e POSTGRES_PASSWORD=password" to set
87+
it in "docker run".
88+
****************************************************
89+
EOWARN
90+
91+
pass=
92+
authMethod=trust
93+
fi
94+
95+
{
96+
echo
97+
echo "host replication all all $authMethod"
98+
echo "host all all all $authMethod"
99+
} >> "$PGDATA/pg_hba.conf"
100+
101+
# internal start of server in order to allow set-up using psql-client
102+
# does not listen on external TCP/IP and waits until start finishes
103+
PGUSER="${PGUSER:-postgres}" \
104+
pg_ctl -D "$PGDATA" \
105+
-o "-c listen_addresses='localhost'" \
106+
-w start
107+
108+
psql=( psql -v ON_ERROR_STOP=1 )
109+
110+
if [ "$POSTGRES_DB" != 'postgres' ]; then
111+
"${psql[@]}" --username postgres <<-EOSQL
112+
CREATE DATABASE "$POSTGRES_DB" ;
113+
EOSQL
114+
echo
115+
fi
116+
117+
if [ "$POSTGRES_USER" = 'postgres' ]; then
118+
op='ALTER'
119+
else
120+
op='CREATE'
121+
fi
107122
"${psql[@]}" --username postgres <<-EOSQL
108-
CREATE DATABASE "$POSTGRES_DB" ;
123+
$op USER "$POSTGRES_USER" WITH SUPERUSER $pass ;
109124
EOSQL
110125
echo
111-
fi
112126

113-
if [ "$POSTGRES_USER" = 'postgres' ]; then
114-
op='ALTER'
115-
else
116-
op='CREATE'
117-
fi
118-
"${psql[@]}" --username postgres <<-EOSQL
119-
$op USER "$POSTGRES_USER" WITH SUPERUSER $pass ;
120-
EOSQL
121-
echo
122-
123-
psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" )
124-
125-
echo
126-
for f in /docker-entrypoint-initdb.d/*; do
127-
case "$f" in
128-
*.sh) echo "$0: running $f"; . "$f" ;;
129-
*.sql) echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
130-
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
131-
*) echo "$0: ignoring $f" ;;
132-
esac
127+
psql+=( --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" )
128+
133129
echo
134-
done
130+
for f in /docker-entrypoint-initdb.d/*; do
131+
case "$f" in
132+
*.sh) echo "$0: running $f"; . "$f" ;;
133+
*.sql) echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
134+
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
135+
*) echo "$0: ignoring $f" ;;
136+
esac
137+
echo
138+
done
139+
140+
PGUSER="${PGUSER:-postgres}" \
141+
pg_ctl -D "$PGDATA" -m fast -w stop
142+
143+
# do this after initdb, otherwise synchronous_standby_names causes a deadlock
144+
if [ $POSTGRES_NUM_SLAVES -gt 0 ]; then
145+
echo "enabling replication"
146+
{
147+
echo
148+
echo "wal_level = hot_standby"
149+
echo "max_wal_senders = $(($POSTGRES_NUM_SLAVES * 2))" # each slave needs 2 connections when comming up
150+
echo "hot_standby = on"
151+
echo "synchronous_standby_names = '*'"
152+
} >> "$PGDATA/postgresql.conf"
153+
fi
135154

136-
PGUSER="${PGUSER:-postgres}" \
137-
pg_ctl -D "$PGDATA" -m fast -w stop
155+
echo
156+
echo 'PostgreSQL init process complete; ready for start up.'
157+
echo
158+
else
159+
eval "pg_basebackup -D $PGDATA -R -Xs -P -d \"host=$POSTGRES_MASTER_HOST user=$POSTGRES_USER password=$POSTGRES_PASSWORD \""
138160

139-
echo
140-
echo 'PostgreSQL init process complete; ready for start up.'
141-
echo
161+
echo
162+
echo "PostgreSQL basebackup complete; ready for start up."
163+
echo
164+
fi
142165
fi
143166
fi
144167

0 commit comments

Comments
 (0)