Skip to content

Commit 52fefe9

Browse files
committed
Initial import
1 parent 183a6a7 commit 52fefe9

File tree

5 files changed

+206
-2
lines changed

5 files changed

+206
-2
lines changed

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
ARG VERSION=17-bookworm
2+
ARG OS
3+
ARG ARCH
4+
5+
# Use the base image
6+
FROM postgres:${VERSION}
7+
8+
LABEL org.opencontainers.image.description="PostgreSQL image with primary/replica support" \
9+
org.opencontainers.image.source="https://github.com/mutablelogic/docker-postgres"
10+
11+
# Install packages postgis and pgvector
12+
ENV POSTGIS_MAJOR=3
13+
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
14+
ca-certificates \
15+
postgresql-$PG_MAJOR-postgis-$POSTGIS_MAJOR \
16+
postgresql-$PG_MAJOR-postgis-$POSTGIS_MAJOR-scripts \
17+
postgresql-$PG_MAJOR-pgvector \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
# Copy scripts
21+
RUN mkdir -p /docker-entrypoint-initdb.d
22+
COPY --chmod=755 ./scripts/10_primary.sh /docker-entrypoint-initdb.d
23+
COPY --chmod=755 ./scripts/20_replica.sh /docker-entrypoint-initdb.d
24+
25+
# Set the environment
26+
ENV POSTGRES_REPLICATION_USER=replication \
27+
POSTGRES_REPLICATION_SLOT=replica1

Makefile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Executables
2+
DOCKER ?= $(shell which docker 2>/dev/null)
3+
4+
# Set OS and Architecture
5+
ARCH ?= $(shell arch | tr A-Z a-z | sed 's/x86_64/amd64/' | sed 's/i386/amd64/' | sed 's/armv7l/arm/' | sed 's/aarch64/arm64/')
6+
OS ?= $(shell uname | tr A-Z a-z)
7+
VERSION ?= 17-bookworm
8+
9+
# Docker repository
10+
DOCKER_REPO ?= ghcr.io/mutablelogic/docker-postgres
11+
BUILD_TAG = ${DOCKER_REPO}-${OS}-${ARCH}:${VERSION}
12+
13+
# Build the docker image
14+
.PHONY: docker
15+
docker: docker-dep
16+
@echo build docker image: ${BUILD_TAG} for ${OS}/${ARCH}
17+
@${DOCKER} build \
18+
--tag ${BUILD_TAG} \
19+
--build-arg ARCH=${ARCH} \
20+
--build-arg OS=${OS} \
21+
--build-arg VERSION=${VERSION} \
22+
-f Dockerfile .
23+
24+
25+
###############################################################################
26+
# DEPENDENCIES
27+
28+
.PHONY: docker-dep
29+
docker-dep:
30+
@test -f "${DOCKER}" && test -x "${DOCKER}" || (echo "Missing docker binary" && exit 1)

README.md

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,67 @@
1-
# docker-postgres
2-
PostgresSQL image with primary & replica support
1+
# PostgreSQL Dockerfile with primary and replica support
2+
3+
PostgresSQL image with Primary & Replica support. In order to build the image, run:
4+
5+
```bash
6+
DOCKER_REPO=ghcr.io/mutablelogic/docker-postgres make docker
7+
```
8+
9+
Replacing the `DOCKER_REPO` with the repository you want to use.
10+
11+
## Environment variables
12+
13+
The image can be run as a primary or replica, depending on the environment variables passed on the
14+
docker command line:
15+
16+
* `POSTGRES_REPLICATION_PRIMARY`: **Required for replica** The host and port that the replica will use
17+
to connect to the primary, in the form `host=<hostname> port=5432`. When not set,
18+
the instance role is a primary.
19+
* `POSTGRES_REPLICATION_PASSWORD`: **Required**: The password for the `POSTGRES_REPLICATION_USER`.
20+
* `POSTGRES_REPLICATION_USER`: **Default is `replicator`**: The user that the primary will use to connect
21+
to the replica.
22+
* `POSTGRES_REPLICATION_SLOT`: **Default is `replica1`** The replication slot for each replica.
23+
On the primary, this is a comma-separated list of replication slots. On a replica, this is the name
24+
of the replication slot used for syncronization.
25+
26+
## Running a Primary server
27+
28+
Example of running a primary instance, with two replication slots.
29+
You should change the password for the `POSTGRES_PASSWORD` and `POSTGRES_REPLICATION_PASSWORD`
30+
environment variables in this example:
31+
32+
```bash
33+
docker volume create postgres-primary
34+
docker run \
35+
--rm --name postgres-primary \
36+
-e POSTGRES_REPLICATION_SLOT="replica1,replica2" \
37+
-e POSTGRES_REPLICATION_PASSWORD="postgres" \
38+
-e POSTGRES_PASSWORD="postgres" \
39+
-p 5432:5432 \
40+
-v postgres-primary:/var/lib/postgresql/data \
41+
ghcr.io/mutablelogic/docker-postgres-darwin-amd64:17-bookworm
42+
```
43+
44+
You can add additional replication slots later as needed.
45+
46+
## Running a Replica server
47+
48+
When you run a replica instance, the first time it runs it will backup from the primary instance and then start
49+
replication. You should change the password for the `POSTGRES_PASSWORD` and `POSTGRES_REPLICATION_PASSWORD`
50+
environment variables, and set the `POSTGRES_REPLICATION_PRIMARY` environment variable to the primary instance
51+
in this example:
52+
53+
```bash
54+
docker volume create postgres-replica1
55+
docker run \
56+
--rm --name postgres-replica1 \
57+
-e POSTGRES_REPLICATION_PRIMARY="host=milou.lan port=5432" \
58+
-e POSTGRES_REPLICATION_SLOT="replica1" \
59+
-e POSTGRES_REPLICATION_PASSWORD="postgres" \
60+
-e POSTGRES_PASSWORD="postgres" \
61+
-p 5433:5432 \
62+
-v postgres-replica1:/var/lib/postgresql/data \
63+
ghcr.io/mutablelogic/docker-postgres-darwin-amd64:17-bookworm
64+
```
65+
66+
A second replica (and so forth) can be run in the same way, but with a different port and volume name.
67+
You can run up to ten replicas by default.

scripts/10_primary.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Script should only run on the primary node
5+
if [ ! -z "${POSTGRES_REPLICATION_PRIMARY}" ]; then
6+
echo "Skipping primary initialisation on a replica."
7+
exit 0
8+
fi
9+
10+
# If there is no replication password, then quit
11+
if [ -z "${POSTGRES_REPLICATION_PASSWORD}" ]; then
12+
echo "POSTGRES_REPLICATION_PASSWORD needs to be set."
13+
exit 1
14+
fi
15+
16+
# Make the data directory
17+
install -d "${PGDATA}" -o postgres -g postgres -m 700
18+
19+
# Create the replication user
20+
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" <<-EOSQL
21+
CREATE USER ${POSTGRES_REPLICATION_USER} WITH REPLICATION ENCRYPTED PASSWORD '${POSTGRES_REPLICATION_PASSWORD}';
22+
EOSQL
23+
24+
# Create the replication slots
25+
IFS=',' read -r -a array <<< "${POSTGRES_REPLICATION_SLOT}"
26+
for SLOT in "${array[@]}"; do
27+
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" <<-EOSQL
28+
SELECT pg_create_physical_replication_slot('${SLOT}');
29+
EOSQL
30+
done
31+
32+
# Set configuration for replication
33+
CONF="${PGDATA}/postgresql.conf"
34+
sed -i -e"s/^#wal_level.*$/wal_level=replica/" ${CONF}
35+
sed -i -e"s/^#max_wal_senders.*$/max_wal_senders=10/" ${CONF}
36+
sed -i -e"s/^#max_replication_slots.*$/max_replication_slots=10/" ${CONF}
37+
38+
# Add the replication user to pg_hba.conf
39+
echo "host ${POSTGRES_REPLICATION_USER} all 0.0.0.0/0 scram-sha-256" >> "${PGDATA}/pg_hba.conf"

scripts/20_replica.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Script should only run on the primary node
5+
if [ -z "${POSTGRES_REPLICATION_PRIMARY}" ]; then
6+
echo "Skipping replica initialisation on a primary."
7+
exit 0
8+
fi
9+
10+
# If there is no replication password, then quit
11+
if [ -z "${POSTGRES_REPLICATION_PASSWORD}" ]; then
12+
echo "POSTGRES_REPLICATION_PASSWORD needs to be set."
13+
exit 1
14+
fi
15+
16+
# Make the data directory
17+
install -d "${PGDATA}" -o postgres -g postgres -m 700
18+
19+
# Set password for replication user
20+
echo "*:*:*:${POSTGRES_REPLICATION_USER}:${POSTGRES_REPLICATION_PASSWORD}" >> /var/lib/postgresql/.pgpass
21+
chmod 600 /var/lib/postgresql/.pgpass
22+
23+
# Stop the server
24+
pg_ctl -D "${PGDATA}" stop -m fast
25+
26+
# Perform the backup
27+
rm -fr ${PGDATA}/*
28+
pg_basebackup -v --pgdata="${PGDATA}" \
29+
--write-recovery-conf \
30+
--slot="${POSTGRES_REPLICATION_SLOT}" -X stream \
31+
--dbname="${POSTGRES_REPLICATION_PRIMARY}" --username="${POSTGRES_REPLICATION_USER}" --no-password
32+
33+
# Set configuration for replication
34+
CONF="${PGDATA}/postgresql.conf"
35+
sed -i -e"s/^#max_wal_senders.*$/max_wal_senders=10/" ${CONF}
36+
sed -i -e"s/^#max_replication_slots.*$/max_replication_slots=10/" ${CONF}
37+
sed -i -e"s/^#primary_conninfo.*$/primary_conninfo='user=${POSTGRES_REPLICATION_USER} ${POSTGRES_REPLICATION_PRIMARY}'/" ${CONF}
38+
sed -i -e"s/^#primary_slot_name.*$/primary_slot_name='${POSTGRES_REPLICATION_SLOT}'/" ${CONF}
39+
sed -i -e"s/^#hot_standby.*$/hot_standby=on/" ${CONF}
40+
sed -i -e"s/^#hot_standby_feedback.*$/hot_standby_feedback=on/" ${CONF}
41+
42+
# Start the server
43+
pg_ctl -D "${PGDATA}" start

0 commit comments

Comments
 (0)