-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
108 lines (101 loc) · 5.57 KB
/
Copy pathDockerfile
File metadata and controls
108 lines (101 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# Postgres 18 + barman-cloud-*, for continuous WAL archiving and PITR.
#
# Shared by every database that backs up to object storage. It is deliberately
# generic -- nothing in here knows which database it will hold. The bucket, the
# server name and the credentials all arrive as the accessory's `cmd:` and
# environment, so one image serves them all.
#
# Why not ghcr.io/cloudnative-pg/postgresql, which already bundles Barman:
# their PG18 image dropped the entrypoint entirely (Entrypoint=[], Cmd=[bash])
# -- it is a bare server image for the CloudNativePG Kubernetes operator, which
# supplies its own init. Their PG17 image was still built FROM the official one
# and worked as a drop-in; 18 is not. Booting it under Kamal put postgres up as
# root, and the server refuses that outright:
#
# "root" execution of the PostgreSQL server is not permitted.
#
# The official image keeps the contract the accessories depend on:
# POSTGRES_USER/_DB/_PASSWORD init, /docker-entrypoint-initdb.d, and -- the part
# that actually matters here -- start as root, chown PGDATA (a Kamal bind mount
# arrives owned by the SSH user), then `exec gosu postgres`. So the server still
# ends up running unprivileged.
#
# That same entrypoint behaviour is load-bearing for the restore drill, and in a
# way that is easy to miss: it decides whether a data directory is populated by
# testing `-s $PGDATA/PG_VERSION`. Find nothing, and it runs initdb. So a restore
# that silently produced an empty directory comes up as a pristine, healthy,
# empty database. pgctl asserts PG_VERSION between restore and boot for exactly
# that reason.
# Pinned by digest, not by the moving :18 tag.
#
# `postgres:18` is repointed on every patch release, so two builds of this
# identical Dockerfile can produce different servers. That is the wrong property
# for the image you restore a database with: a recovery must be reproducible, and
# "whatever :18 meant that day" is not.
#
# It is not only about the version number. This image's whole contract with pgctl
# is that PGDATA lives at /var/lib/postgresql/18/docker, that the entrypoint
# chowns it and drops privileges via gosu, and that it runs initdb when it finds
# no PG_VERSION there. pgctl hardcodes that path and leans on that behaviour --
# see the drill's PG_VERSION guard. Pinning the digest is what makes that contract
# one fixed thing rather than a moving target.
#
# To bump (deliberately, in a PR, with the version bumped below to match):
# docker buildx imagetools inspect postgres:18 --format '{{.Manifest.Digest}}'
ARG PG_IMAGE=postgres:18@sha256:22c89fe0d0f507606260237fd55e51f6137f58b2d5bcf6152242b96d9fe8f9a4
FROM ${PG_IMAGE}
# What the digest above is expected to contain. Asserted at build time below, so a
# careless digest bump fails the build instead of silently shipping a different
# major version -- which would break the PGDATA path pgctl depends on.
ARG EXPECT_PG_VERSION=18.4
ARG EXPECT_PG_MAJOR=18
# Assert the digest is the image we think it is, before building anything on it.
#
# PGDATA is /var/lib/postgresql/${PG_MAJOR}/docker, and pgctl hardcodes the "18".
# A digest bumped to a different major would move that path, and the failure would
# surface as a restore quietly writing to the wrong directory rather than as
# anything resembling an error. Fail here instead.
RUN set -eux; \
echo "postgres $PG_VERSION (major $PG_MAJOR)"; \
test "$PG_MAJOR" = "$EXPECT_PG_MAJOR" \
|| { echo "PG_MAJOR is $PG_MAJOR, expected $EXPECT_PG_MAJOR: the PGDATA path pgctl assumes has moved" >&2; exit 1; }; \
case "$PG_VERSION" in \
"$EXPECT_PG_VERSION"*) ;; \
*) echo "PG_VERSION is $PG_VERSION, expected $EXPECT_PG_VERSION*: bump EXPECT_PG_VERSION deliberately" >&2; exit 1 ;; \
esac; \
test -x /usr/local/bin/docker-entrypoint.sh \
|| { echo "no docker-entrypoint.sh: this is not the official image, and the chown/gosu startup pgctl relies on is gone" >&2; exit 1; }
# Barman ships as a Python package. The venv keeps pip out of the system
# interpreter Debian manages (PEP 668).
#
# barman depends on psycopg2, which has no wheel and compiles against libpq, so
# the toolchain and headers have to be present at install time. They are build-
# only: purged in the same layer, so nothing but the compiled module survives
# into the image (psycopg2-binary is not a substitute -- barman pins the sdist,
# so pip rebuilds it regardless).
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
python3 python3-venv python3-dev gcc libpq-dev; \
python3 -m venv /opt/barman; \
/opt/barman/bin/pip install --no-cache-dir 'barman[cloud,aws]'; \
apt-get purge -y --auto-remove python3-venv python3-dev gcc libpq-dev; \
rm -rf /var/lib/apt/lists/*
# archive_command runs as the postgres user with a minimal environment, so the
# barman tools have to resolve on the default PATH.
ENV PATH="/opt/barman/bin:${PATH}"
# Every consumer of this image wants pg_stat_statements and was mounting its own
# identical copy of this file. Bake it in instead: one less thing for a new
# database to remember, and one less file to drift between repos.
#
# Runs once, on a fresh data dir. An existing database needs
# `CREATE EXTENSION pg_stat_statements;` by hand, after the library is preloaded
# (which needs a restart).
COPY init/01-pg-stat-statements.sql /docker-entrypoint-initdb.d/01-pg-stat-statements.sql
# Fail the build rather than discover at 3am that archiving was never wired.
RUN barman-cloud-wal-archive --version \
&& barman-cloud-backup --version \
&& barman-cloud-restore --version \
&& barman-cloud-wal-restore --version \
&& barman-cloud-backup-delete --version \
&& barman-cloud-backup-list --version