Skip to content

Commit 73af8f7

Browse files
authored
Feature/image size optimisation (#13)
1 parent 0de5e1f commit 73af8f7

5 files changed

Lines changed: 285 additions & 49 deletions

File tree

.github/workflows/postgresql.yml

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,34 @@ jobs:
9292
username: ${{ secrets._TEMP_DOCKERHUB_USER }}
9393
password: ${{ secrets._TEMP_DOCKERHUB_PASSWORD }}
9494

95-
- name: build and push amd64 image
95+
- name: Build amd64 image locally
9696
run: |
9797
docker buildx build \
9898
--build-arg GIT_COMMIT=${{ github.sha }} \
99-
--push \
99+
--load \
100100
--platform linux/amd64 \
101101
--no-cache-filter trimmed \
102102
--no-cache-filter trimmed-all \
103103
$TAG .
104104
105+
- name: Install slim toolkit
106+
run: |
107+
curl -sL https://raw.githubusercontent.com/slimtoolkit/slim/master/scripts/install-slim.sh | sudo -E bash -
108+
109+
- name: Slim the image
110+
run: |
111+
# Extract image name from TAG (remove -t prefix)
112+
IMAGE_NAME=$(echo "$TAG" | sed 's/-t //')
113+
chmod +x ./slim-image.sh
114+
./slim-image.sh "$IMAGE_NAME" "${IMAGE_NAME}-slim" amd64
115+
# Replace original with slim version
116+
docker tag ${IMAGE_NAME}-slim $IMAGE_NAME
117+
118+
- name: Push amd64 image
119+
run: |
120+
IMAGE_NAME=$(echo "$TAG" | sed 's/-t //')
121+
docker push $IMAGE_NAME
122+
105123
image_postgresql_arm64:
106124
needs: image_postgresql_amd64
107125
runs-on: ubuntu-latest
@@ -171,16 +189,34 @@ jobs:
171189
username: ${{ secrets._TEMP_DOCKERHUB_USER }}
172190
password: ${{ secrets._TEMP_DOCKERHUB_PASSWORD }}
173191

174-
- name: build and push arm64 image
192+
- name: Build arm64 image locally
175193
run: |
176194
docker buildx build \
177195
--build-arg GIT_COMMIT=${{ github.sha }} \
178-
--push \
179-
--platform linux/aarch64 \
196+
--load \
197+
--platform linux/arm64 \
180198
--no-cache-filter trimmed \
181199
--no-cache-filter trimmed-all \
182200
$TAG .
183201
202+
- name: Install slim toolkit
203+
run: |
204+
curl -sL https://raw.githubusercontent.com/slimtoolkit/slim/master/scripts/install-slim.sh | sudo -E bash -
205+
206+
- name: Slim the image
207+
run: |
208+
# Extract image name from TAG (remove -t prefix)
209+
IMAGE_NAME=$(echo "$TAG" | sed 's/-t //')
210+
chmod +x ./slim-image.sh
211+
./slim-image.sh "$IMAGE_NAME" "${IMAGE_NAME}-slim" arm64
212+
# Replace original with slim version
213+
docker tag ${IMAGE_NAME}-slim $IMAGE_NAME
214+
215+
- name: Push arm64 image
216+
run: |
217+
IMAGE_NAME=$(echo "$TAG" | sed 's/-t //')
218+
docker push $IMAGE_NAME
219+
184220
create_manifest:
185221
needs: [image_postgresql_amd64, image_postgresql_arm64]
186222
runs-on: ubuntu-latest

Dockerfile

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,78 @@
1-
21
ARG PG_MAJOR=17
2+
ARG PREV_PG_MAJOR=15
33
ARG TIMESCALE_VERSION=2.22
44

5-
FROM timescale/timescaledb-ha:pg17-ts${TIMESCALE_VERSION} AS trimmed
6-
LABEL maintainer="support@openremote.io"
5+
# Stage 1: Get PostgreSQL ${PREV_PG_MAJOR} binaries for upgrade support
6+
FROM timescale/timescaledb-ha:pg${PG_MAJOR}-ts${TIMESCALE_VERSION}-all AS pg-all
77

88
USER root
99

10-
# install fd to find files to speed up chown and chgrp
11-
RUN apt-get update && apt-get install -y fd-find && rm -rf /var/lib/apt/lists/*
12-
13-
# Give postgres user the same UID and GID as the old alpine postgres image to simplify migration of existing DB
14-
RUN usermod -u 70 postgres \
15-
&& groupmod -g 70 postgres \
16-
&& (fd / -group 1000 -exec chgrp -h postgres {} \; || true) \
17-
&& (fd / -user 1000 -exec chown -h postgres {} \; || true)
10+
ARG PREV_PG_MAJOR
1811

19-
# Set PGDATA to the same location as our old alpine image
20-
RUN mkdir -p /var/lib/postgresql && mv /home/postgres/pgdata/* /var/lib/postgresql/ && chown -R postgres:postgres /var/lib/postgresql
12+
# Strip debug symbols and remove unnecessary files from PG ${PREV_PG_MAJOR} in this stage
13+
# For pg_upgrade we need bin/, lib/, and extension/ (for TimescaleDB upgrade scripts)
14+
RUN find /usr/lib/postgresql/${PREV_PG_MAJOR} -type f -name '*.so*' -exec strip --strip-unneeded {} \; 2>/dev/null || true \
15+
&& find /usr/lib/postgresql/${PREV_PG_MAJOR} -type f -executable -exec strip --strip-unneeded {} \; 2>/dev/null || true \
16+
&& rm -rf /usr/share/postgresql/${PREV_PG_MAJOR}/man \
17+
/usr/share/postgresql/${PREV_PG_MAJOR}/doc \
18+
/usr/share/postgresql/${PREV_PG_MAJOR}/contrib
2119

22-
# Add custom entry point (see file header for details)
23-
COPY or-entrypoint.sh /
24-
RUN chmod +x /or-entrypoint.sh
20+
# Stage 2: Prepare the main image with UID/GID changes and cleanup
21+
FROM timescale/timescaledb-ha:pg${PG_MAJOR}-ts${TIMESCALE_VERSION} AS final
22+
LABEL maintainer="support@openremote.io"
2523

26-
# Add custom initdb script(s)
27-
COPY docker-entrypoint-initdb.d/ /docker-entrypoint-initdb.d/
28-
RUN chmod +x /docker-entrypoint-initdb.d/*
24+
USER root
2925

26+
ARG PREV_PG_MAJOR
3027

31-
# Below is mostly copied from https://github.com/timescale/timescaledb-docker-ha/blob/master/Dockerfile (with OR specific entrypoint,
32-
# workdir and OR env defaults)
28+
# Copy PG ${PREV_PG_MAJOR} bin and lib directories for pg_upgrade
29+
COPY --from=pg-all /usr/lib/postgresql/${PREV_PG_MAJOR}/bin /usr/lib/postgresql/${PREV_PG_MAJOR}/bin
30+
COPY --from=pg-all /usr/lib/postgresql/${PREV_PG_MAJOR}/lib /usr/lib/postgresql/${PREV_PG_MAJOR}/lib
31+
# Copy share files including extensions (needed for TimescaleDB upgrade on old PG before pg_upgrade)
32+
COPY --from=pg-all /usr/share/postgresql/${PREV_PG_MAJOR} /usr/share/postgresql/${PREV_PG_MAJOR}
3333

34-
# Get the -all variant which contains multiple PostgreSQL versions
35-
# According to TimescaleDB docs: "timescale/timescaledb-ha images have the files necessary to run previous versions"
36-
FROM timescale/timescaledb-ha:pg17-ts${TIMESCALE_VERSION}-all AS trimmed-all
34+
# Copy entrypoint scripts
35+
COPY or-entrypoint.sh /
36+
COPY docker-entrypoint-initdb.d/ /docker-entrypoint-initdb.d/
3737

38-
## Create a smaller Docker image from the builder image
39-
FROM scratch
40-
COPY --from=trimmed / /
38+
# Install fd-find, fix UID/GID, setup directories, strip binaries, and cleanup - all in one layer
39+
RUN apt-get update && apt-get install -y --no-install-recommends fd-find \
40+
# Give postgres user the same UID and GID as the old alpine postgres image
41+
&& usermod -u 70 postgres \
42+
&& groupmod -g 70 postgres \
43+
&& (fdfind . / -group 1000 -exec chgrp -h postgres {} \; 2>/dev/null || true) \
44+
&& (fdfind . / -user 1000 -exec chown -h postgres {} \; 2>/dev/null || true) \
45+
# Set PGDATA to the same location as our old alpine image
46+
&& mkdir -p /var/lib/postgresql \
47+
&& mv /home/postgres/pgdata/* /var/lib/postgresql/ \
48+
&& chown -R postgres:postgres /var/lib/postgresql \
49+
# Make scripts executable
50+
&& chmod +x /or-entrypoint.sh /docker-entrypoint-initdb.d/* \
51+
# Strip debug symbols from PostgreSQL binaries to reduce size
52+
&& find /usr/lib/postgresql -type f -name '*.so*' -exec strip --strip-unneeded {} \; 2>/dev/null || true \
53+
&& find /usr/lib/postgresql -type f -executable -exec strip --strip-unneeded {} \; 2>/dev/null || true \
54+
# Remove fd-find and clean up
55+
&& apt-get purge -y fd-find \
56+
&& apt-get autoremove -y --purge \
57+
&& apt-get clean \
58+
&& rm -rf /var/lib/apt/lists/* \
59+
/var/cache/apt/* \
60+
/var/log/* \
61+
/usr/share/doc/* \
62+
/usr/share/man/* \
63+
/usr/share/info/* \
64+
/usr/share/lintian/* \
65+
/usr/share/locale/* \
66+
/tmp/* \
67+
/var/tmp/* \
68+
/root/.cache \
69+
/home/postgres/.cache \
70+
/usr/local/lib/pgai \
71+
/usr/share/postgresql/*/man \
72+
/usr/share/postgresql/*/doc
4173

4274
ARG PG_MAJOR
43-
44-
## Copy only PostgreSQL 14 and 15 for upgrade support
45-
COPY --from=trimmed-all /usr/lib/postgresql/14 /usr/lib/postgresql/14
46-
COPY --from=trimmed-all /usr/lib/postgresql/15 /usr/lib/postgresql/15
47-
COPY --from=trimmed-all /usr/share/postgresql/14 /usr/share/postgresql/14
48-
COPY --from=trimmed-all /usr/share/postgresql/15 /usr/share/postgresql/15
75+
ARG PREV_PG_MAJOR
4976

5077
# Increment this to indicate that a re-index should be carried out on first startup with existing data; REINDEX can still be overidden
5178
# with OR_DISABLE_REINDEX=true
@@ -80,6 +107,7 @@ ENV PGROOT=/var/lib/postgresql \
80107
POSTGRES_USER=${POSTGRES_USER:-postgres} \
81108
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres} \
82109
PG_MAJOR=$PG_MAJOR \
110+
PREV_PG_MAJOR=$PREV_PG_MAJOR \
83111
OR_REINDEX_COUNTER=${OR_REINDEX_COUNTER} \
84112
OR_DISABLE_REINDEX=${OR_DISABLE_REINDEX:-false} \
85113
POSTGRES_MAX_CONNECTIONS=${POSTGRES_MAX_CONNECTIONS:-50} \

README.md

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,99 @@
11
# Postgresql docker image
2-
[![build multirach postgresql Docker image and push to it dockerhub](https://github.com/openremote/postgresql/actions/workflows/postgresql.yml/badge.svg)](https://github.com/openremote/postgresql/actions/workflows/postgresql.yml)
2+
[![build multiarch postgresql Docker image and push to dockerhub](https://github.com/openremote/postgresql/actions/workflows/postgresql.yml/badge.svg)](https://github.com/openremote/postgresql/actions/workflows/postgresql.yml)
33

4-
POSTGIS and TimescaleDB (inc. toolkit for hyperfunctions) image built for aarch64 support using `timescaledev/timescaledb-ha` base image with:
4+
POSTGIS and TimescaleDB (inc. toolkit for hyperfunctions) image built for amd64 and arm64 using `timescale/timescaledb-ha` base image with:
55

66
- OR specific ENV variables and a healthcheck added
77
- Easy configuration of `max_connections` using `POSTGRES_MAX_CONNECTIONS` environment variable (set to `-1` to disable this setting)
88
- PGDATA path set to match old Alpine image (for ease of DB migration)
99
- POSTGRES user UID and GID changed to match old Alpine image (for ease of DB migration)
1010
- Auto upgrade of database with PG major version changes from previous PG major version; can be disabled using
11-
OR_DISABLE_AUTO_UPGRADE=true.
11+
`OR_DISABLE_AUTO_UPGRADE=true`
1212
- Auto upgrade of timescaleDB extension when a new version is available in the container; can be disabled using
13-
OR_DISABLE_AUTO_UPGRADE=true.
13+
`OR_DISABLE_AUTO_UPGRADE=true`
1414
- OR_DISABLE_REINDEX env variable with associated scripts to determine if a REINDEX of the entire DB should be carried
15-
out at first startup with existing DB (checks whether or not $PGDATA/OR_REINDEX_COUNTER.$OR_REINDEX_COUNTER exists).
15+
out at first startup with existing DB (checks whether or not `$PGDATA/OR_REINDEX_COUNTER.$OR_REINDEX_COUNTER` exists).
1616
This is used when a collation change has occurred (glibc version change, muslc <-> glibc) which can break the indexes;
1717
migration can either be manually handled or auto handled depending on OR_DISABLE_REINDEX env variable value.
18-
NOTE THAT A REINDEX CAN TAKE A LONG TIME DEPENDING ON THE SIZE OF THE DB! And startup will be delayed until completed
18+
NOTE THAT A REINDEX CAN TAKE A LONG TIME DEPENDING ON THE SIZE OF THE DB! And startup will be delayed until completed.
1919
This functionality is intended to simplify migration for basic users; advanced users with large DBs should take care of this
2020
themselves.
21+
- **Slimmed images** using [slim toolkit](https://github.com/slimtoolkit/slim) to reduce image size by ~60%
2122

22-
`timescale/timescaledb-ha` image is ubuntu based and only currently supports amd64; they are working on ARM64 support in timescaledev/timescaledb-ha see:
23+
## Local Development
2324

24-
https://github.com/timescale/timescaledb-docker-ha/pull/355
25+
### Prerequisites
2526

26-
See this issue for POSTGIS base image aarch64 support discussion:
27+
- **Docker** must be installed and running
28+
- **slim toolkit** must be installed for image optimization
2729

28-
https://github.com/postgis/docker-postgis/issues/216
30+
Install slim toolkit via the install script:
31+
```bash
32+
curl -sL https://raw.githubusercontent.com/slimtoolkit/slim/master/scripts/install-slim.sh | sudo -E bash -
33+
```
2934

30-
TODO: Switch over to timescale/timescaledb-ha once arm64 supported
35+
Or via Homebrew (macOS):
36+
```bash
37+
brew install docker-slim
38+
```
39+
40+
For more installation options, see the [slim toolkit documentation](https://github.com/slimtoolkit/slim#installation).
41+
42+
### Building the Image
43+
44+
1. Build the Docker image (replace `17` with desired PostgreSQL major version):
45+
```bash
46+
docker build --build-arg PG_MAJOR=17 -t openremote/postgresql:pg17 .
47+
```
48+
49+
2. Slim the image using the provided script:
50+
```bash
51+
# Usage: ./slim-image.sh <source-image> <target-image> <architecture>
52+
# Architecture: amd64 or arm64 (auto-detected if omitted)
53+
54+
./slim-image.sh openremote/postgresql:pg17 openremote/postgresql:pg17-slim
55+
```
56+
57+
3. Optionally replace the original with the slimmed version:
58+
```bash
59+
docker tag openremote/postgresql:pg17-slim openremote/postgresql:pg17
60+
```
3161

3262
## Upgrading
63+
3364
***NOTE: If you change the version of container you use then make sure you have backed up your DB first as this container will try to auto upgrade your DB and/or TimescaleDB extension; this auto upgrade functionality can be disabled using `OR_DISABLE_AUTO_UPGRADE=true`***
65+
66+
### Automatic Upgrade
67+
68+
This image supports automatic upgrades from the previous PostgreSQL major version. When the container starts with an existing database from a supported older version, it will:
69+
70+
1. Upgrade TimescaleDB extensions on the old PostgreSQL version
71+
2. Run `pg_upgrade` to migrate the database to the new PostgreSQL version
72+
3. Upgrade TimescaleDB extensions on the new PostgreSQL version
73+
74+
### Manual Upgrade
75+
76+
If automatic upgrade is not supported for your database version (e.g., skipping multiple major versions), you will need to perform a manual upgrade. Follow these steps:
77+
78+
1. **Backup your database** using `pg_dump` or `pg_dumpall`
79+
2. **Upgrade TimescaleDB first** (if installed) - this must be done before PostgreSQL upgrade
80+
3. **Use pg_upgrade** to migrate between PostgreSQL versions, or restore from backup to a fresh database
81+
82+
#### Useful Resources
83+
84+
- [PostgreSQL pg_upgrade documentation](https://www.postgresql.org/docs/current/pgupgrade.html)
85+
- [TimescaleDB upgrade guide](https://docs.timescale.com/self-hosted/latest/upgrades/)
86+
- [TimescaleDB major upgrade guide](https://docs.timescale.com/self-hosted/latest/upgrades/major-upgrade/)
87+
88+
#### Example: Manual pg_dump/restore
89+
90+
```bash
91+
# On the old container, dump the database
92+
docker exec -it <old_container> pg_dumpall -U postgres > backup.sql
93+
94+
# Start the new container with a fresh data directory
95+
docker run -d --name new_postgres -v /path/to/new/data:/var/lib/postgresql/data openremote/postgresql:latest
96+
97+
# Restore the backup
98+
docker exec -i new_postgres psql -U postgres < backup.sql
99+
```

or-entrypoint.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ if [ -n "$DATABASE_ALREADY_EXISTS" ]; then
7777
echo "---------------------------------------------------------------------------------"
7878
fi
7979

80+
# Check if the old DB version is supported for upgrade
81+
if [ "$DB_VERSION" != "$PG_MAJOR" ] && [ "$OR_DISABLE_AUTO_UPGRADE" != "true" ]; then
82+
# Only PREV_PG_MAJOR and PG_MAJOR are supported
83+
if [ "$DB_VERSION" != "$PREV_PG_MAJOR" ] && [ "$DB_VERSION" != "$PG_MAJOR" ]; then
84+
echo "********************************************************************************"
85+
echo "ERROR: Database version ${DB_VERSION} is not supported for automatic upgrade!"
86+
echo "This image only supports upgrading from PostgreSQL ${PREV_PG_MAJOR} to ${PG_MAJOR}."
87+
echo ""
88+
echo "Options:"
89+
echo " 1. Use an intermediate image version that supports upgrading from ${DB_VERSION}"
90+
echo " 2. Manually upgrade the database (see documentation below)"
91+
echo ""
92+
echo "Documentation:"
93+
echo " - OpenRemote PostgreSQL upgrade guide: https://github.com/openremote/postgresql#upgrading"
94+
echo " - PostgreSQL pg_upgrade: https://www.postgresql.org/docs/current/pgupgrade.html"
95+
echo " - TimescaleDB upgrade guide: https://docs.timescale.com/self-hosted/latest/upgrades/"
96+
echo "********************************************************************************"
97+
exit 12
98+
fi
99+
fi
100+
80101
# STEP 1: Upgrade TimescaleDB on OLD PostgreSQL version (if needed)
81102
# This must happen BEFORE pg_upgrade so both old and new PG have the same TS version
82103
if [ "$DB_VERSION" != "$PG_MAJOR" ] && [ "$OR_DISABLE_AUTO_UPGRADE" != "true" ]; then

0 commit comments

Comments
 (0)