Skip to content

Commit 378c76a

Browse files
authored
fix: resolve WAL archiving performance and memory issues (#746)
The barman-cloud plugin experienced significant performance degradation and memory growth compared to the embedded solution. WAL archiving was noticeably slower and memory consumption grew over time. Root cause: The sidecar uses a read-only filesystem which prevents Python from creating bytecode at runtime. When Python finds missing or stale bytecode (.pyc files), it attempts to recompile on every invocation, causing high CPU usage and memory consumption. The previous approach pre-compiled bytecode in a separate base image, but the bytecode was marked as stale when copied between Docker stages, triggering runtime recompilation attempts. This change eliminates bytecode staleness by ensuring all Python bytecode is properly compiled in the final image before the sidecar starts. The image is now fully distroless and based on trixie (previously it was distroless-based but copied unnecessary files from the build stage), reducing size from 463MB to 270MB and package count from 188 to 35, while maintaining zero HIGH/CRITICAL vulnerabilities. Closes #656 Closes #711 Closes #735 Signed-off-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com>
1 parent 064eac2 commit 378c76a

File tree

5 files changed

+65
-100
lines changed

5 files changed

+65
-100
lines changed

.github/workflows/barman-base-image.yml

Lines changed: 0 additions & 38 deletions
This file was deleted.

Taskfile.yml

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -381,34 +381,6 @@ tasks:
381381
build --dir . --file containers/Dockerfile.sidecar --platform linux/amd64 --platform linux/arm64
382382
publish --ref {{.SIDECAR_IMAGE_NAME}} --tags {{.IMAGE_VERSION}}
383383
384-
publish-barman-base:
385-
desc: Build and publish a barman-cloud base container image
386-
vars:
387-
BARMAN_BASE_IMAGE_NAME: ghcr.io/{{.GITHUB_REPOSITORY}}-base{{if not (hasPrefix "refs/heads/main" .GITHUB_REF)}}-testing{{end}}
388-
BARMAN_VERSION:
389-
sh: grep "^barman" containers/sidecar-requirements.in | sed -E 's/.*==([^ ]+)/\1/'
390-
BUILD_DATE:
391-
sh: date +"%Y%m%d%H%M"
392-
requires:
393-
# We expect this to run in a GitHub workflow, so we put a few GitHub-specific vars here
394-
# to prevent running this task locally by accident.
395-
vars:
396-
- CI
397-
- GITHUB_REPOSITORY
398-
- GITHUB_REF
399-
- GITHUB_REF_NAME
400-
- REGISTRY_USER
401-
- REGISTRY_PASSWORD
402-
env:
403-
# renovate: datasource=git-refs depName=docker lookupName=https://github.com/purpleclay/daggerverse currentValue=main
404-
DAGGER_DOCKER_SHA: ee12c1a4a2630e194ec20c5a9959183e3a78c192
405-
cmds:
406-
- >
407-
dagger call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
408-
--registry ghcr.io --username $REGISTRY_USER --password env:REGISTRY_PASSWORD
409-
build --dir . --file containers/Dockerfile.barmanbase --platform linux/amd64 --platform linux/arm64
410-
publish --ref {{.BARMAN_BASE_IMAGE_NAME}} --tags "{{.BARMAN_VERSION}}-{{.BUILD_DATE}}"
411-
412384
controller-gen:
413385
desc: Run controller-gen
414386
run: once

containers/Dockerfile.barmanbase

Lines changed: 0 additions & 7 deletions
This file was deleted.

containers/Dockerfile.sidecar

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ ARG TARGETOS
1010
ARG TARGETARCH
1111

1212
WORKDIR /workspace
13-
# Copy the Go Modules manifests
13+
1414
COPY ../go.mod go.mod
1515
COPY ../go.sum go.sum
1616
# cache deps before building and copying source so that we don't need to re-download as much
@@ -20,35 +20,73 @@ RUN go mod download
2020
ENV GOCACHE=/root/.cache/go-build
2121
ENV GOMODCACHE=/go/pkg/mod
2222

23-
# Copy the go source
2423
COPY ../cmd/manager/main.go cmd/manager/main.go
2524
COPY ../api/ api/
2625
COPY ../internal/ internal/
2726

28-
# Build
29-
# the GOARCH has not a default value to allow the binary be built according to the host where the command
30-
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
31-
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
32-
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
27+
# Build Go binary for target platform (TARGETOS/TARGETARCH)
28+
# Docker BuildKit sets these based on --platform flag or defaults to the build host platform
3329
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
3430
CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/manager/main.go
3531

36-
# Use plugin-barman-cloud-base to get the dependencies.
37-
# pip will build everything inside /usr, so we copy every file into a new
38-
# destination that will then be copied into the distroless container
39-
FROM ghcr.io/cloudnative-pg/plugin-barman-cloud-base:3.17.0-202601131704 AS pythonbuilder
40-
# Prepare a new /usr/ directory with the files we'll need in the final image
41-
RUN mkdir /new-usr/ && \
42-
cp -r --parents /usr/local/lib/ /usr/lib/*-linux-gnu/ /usr/local/bin/ \
43-
/new-usr/
32+
# Build Python virtualenv with all dependencies
33+
FROM debian:trixie-slim AS pythonbuilder
34+
WORKDIR /build
35+
36+
# Install postgresql-common and setup pgdg repository first
37+
RUN apt-get update && \
38+
apt-get install -y --no-install-recommends postgresql-common && \
39+
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
40+
41+
# Install build dependencies
42+
RUN apt-get update && \
43+
apt-get install -y --no-install-recommends \
44+
python3 \
45+
python3-venv \
46+
python3-dev \
47+
build-essential \
48+
libpq-dev \
49+
liblz4-dev \
50+
libsnappy-dev
51+
52+
COPY containers/sidecar-requirements.txt .
4453

45-
# Joint process
46-
# Now we put everything that was build from the origin into our
47-
# distroless container
48-
FROM gcr.io/distroless/python3-debian12:nonroot
54+
# Create virtualenv and install dependencies
55+
RUN python3 -m venv /venv && \
56+
/venv/bin/pip install --upgrade pip setuptools wheel && \
57+
/venv/bin/pip install --no-cache-dir -r sidecar-requirements.txt
58+
59+
# Download and extract runtime library packages and their dependencies
60+
# Using apt-cache to automatically resolve dependencies, filtering out packages
61+
# already present in the distroless base image.
62+
# Distroless package list from: https://github.com/GoogleContainerTools/distroless/blob/main/base/config.bzl
63+
# and https://github.com/GoogleContainerTools/distroless/blob/main/python3/config.bzl
64+
RUN mkdir -p /dependencies /build/downloads && \
65+
cd /build/downloads && \
66+
DISTROLESS_PACKAGES="libc6 libssl3t64 libzstd1 zlib1g libgcc-s1 libstdc++6 \
67+
libbz2-1.0 libdb5.3t64 libexpat1 liblzma5 libsqlite3-0 libuuid1 \
68+
libncursesw6 libtinfo6 libcom-err2 libcrypt1 libgssapi-krb5-2 \
69+
libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libnsl2 \
70+
libreadline8t64 libtirpc3t64 libffi8 libpython3.13-minimal \
71+
libpython3.13-stdlib python3.13-minimal python3.13-venv" && \
72+
apt-cache depends --recurse --no-recommends --no-suggests \
73+
--no-conflicts --no-breaks --no-replaces --no-enhances \
74+
$DISTROLESS_PACKAGES 2>/dev/null | grep "^\w" | sort -u > /tmp/distroless.txt && \
75+
apt-cache depends --recurse --no-recommends --no-suggests \
76+
--no-conflicts --no-breaks --no-replaces --no-enhances \
77+
libpq5 liblz4-1 libsnappy1v5 2>/dev/null | grep "^\w" | sort -u | \
78+
grep -v -F -x -f /tmp/distroless.txt > /tmp/packages.txt && \
79+
apt-get download $(cat /tmp/packages.txt) && \
80+
for deb in *.deb; do \
81+
dpkg -x "$deb" /dependencies; \
82+
done
83+
84+
# Final sidecar image using distroless base for minimal size and fewer packages
85+
FROM gcr.io/distroless/python3-debian13:nonroot
4986

5087
ENV SUMMARY="CloudNativePG Barman plugin" \
51-
DESCRIPTION="Container image that provides the barman-cloud sidecar"
88+
DESCRIPTION="Container image that provides the barman-cloud sidecar" \
89+
PATH="/venv/bin:$PATH"
5290

5391
LABEL summary="$SUMMARY" \
5492
description="$DESCRIPTION" \
@@ -60,7 +98,13 @@ LABEL summary="$SUMMARY" \
6098
version="" \
6199
release="1"
62100

63-
COPY --from=pythonbuilder /new-usr/* /usr/
101+
COPY --from=pythonbuilder /venv /venv
102+
COPY --from=pythonbuilder /dependencies/usr/lib /usr/lib
64103
COPY --from=gobuilder /workspace/manager /manager
104+
105+
# Compile all Python bytecode as root to avoid runtime compilation
106+
USER 0:0
107+
RUN ["/venv/bin/python3", "-c", "import sysconfig, compileall; compileall.compile_dir(sysconfig.get_path('stdlib'), quiet=1); compileall.compile_dir('/venv', quiet=1)"]
108+
65109
USER 26:26
66110
ENTRYPOINT ["/manager"]

renovate.json5

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,6 @@
7979
enabled: false,
8080
},
8181
packageRules: [
82-
{
83-
matchPackageNames: [
84-
'ghcr.io/cloudnative-pg/plugin-barman-cloud-base',
85-
],
86-
versioning: 'loose',
87-
},
8882
{
8983
matchDatasources: [
9084
'go',

0 commit comments

Comments
 (0)