Skip to content

Commit 6efcad0

Browse files
committed
ci: build minimal and standard images
Build images without barman-cloud, to be used with backup plugins. Closes #132 Signed-off-by: Francesco Canovai <[email protected]>
1 parent ff6034a commit 6efcad0

File tree

4 files changed

+357
-11
lines changed

4 files changed

+357
-11
lines changed

.github/workflows/bake.yaml

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: Bake images
2+
3+
on:
4+
schedule:
5+
- cron: 0 8 * * 1
6+
workflow_dispatch:
7+
inputs:
8+
environment:
9+
type: choice
10+
options:
11+
- testing
12+
- production
13+
default: testing
14+
description: "Choose the environment to bake the images for"
15+
push:
16+
17+
jobs:
18+
# Start by building images for testing. We want to run security checks before pushing those to production.
19+
testbuild:
20+
name: Build for testing
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
packages: write
25+
security-events: write
26+
outputs:
27+
metadata: ${{ steps.build.outputs.metadata }}
28+
images: ${{ steps.images.outputs.images }}
29+
steps:
30+
- name: Checkout Code
31+
uses: actions/checkout@v4
32+
33+
- name: Log in to the GitHub Container registry
34+
uses: docker/login-action@v3
35+
with:
36+
registry: ghcr.io
37+
username: ${{ github.actor }}
38+
password: ${{ secrets.GITHUB_TOKEN }}
39+
40+
# TODO: review this when GitHub has linux/arm64 runners available (Q1 2025?)
41+
# https://github.com/github/roadmap/issues/970
42+
- name: Set up QEMU
43+
uses: docker/setup-qemu-action@v3
44+
with:
45+
platforms: 'arm64'
46+
47+
- name: Set up Docker Buildx
48+
uses: docker/setup-buildx-action@v3
49+
50+
- name: Build and push
51+
uses: docker/bake-action@v6
52+
id: build
53+
env:
54+
environment: testing
55+
registry: ghcr.io/${{ github.repository_owner }}
56+
revision: ${{ github.sha }}
57+
with:
58+
push: true
59+
60+
# Get a list of the images that were built and pushed. We only care about a single tag for each image.
61+
- name: Generated images
62+
id: images
63+
run: |
64+
echo "images=$(echo '${{steps.build.outputs.metadata}}' | jq -c '[ .[]."image.name" | sub(",.*";"" )]')" >> "$GITHUB_OUTPUT"
65+
66+
security:
67+
name: Security checks
68+
runs-on: ubuntu-latest
69+
needs:
70+
- testbuild
71+
strategy:
72+
matrix:
73+
image: ${{fromJson(needs.testbuild.outputs.images)}}
74+
steps:
75+
- name: Checkout Code
76+
uses: actions/checkout@v4
77+
78+
- name: Log in to the GitHub Container registry
79+
uses: docker/login-action@v3
80+
with:
81+
registry: ghcr.io
82+
username: ${{ github.actor }}
83+
password: ${{ secrets.GITHUB_TOKEN }}
84+
85+
- name: Dockle
86+
uses: erzz/dockle-action@v1
87+
with:
88+
image: ${{ matrix.image }}
89+
exit-code: '1'
90+
91+
- name: Snyk
92+
uses: snyk/actions/docker@master
93+
continue-on-error: true
94+
env:
95+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
96+
with:
97+
image: "${{ matrix.image }}"
98+
args: --severity-threshold=high --file=Dockerfile
99+
100+
- name: Upload result to GitHub Code Scanning
101+
uses: github/codeql-action/upload-sarif@v3
102+
continue-on-error: true
103+
with:
104+
sarif_file: snyk.sarif
105+
106+
# Build the image for production.
107+
#
108+
# TODO: no need to rebuild everything, just copy the testing images we have generated to the production registry
109+
# if we get here and we are building for production.
110+
prodbuild:
111+
if: github.event.inputs.environment == 'production' || github.event_name == 'schedule'
112+
name: Build for production
113+
runs-on: ubuntu-latest
114+
needs:
115+
- security
116+
permissions:
117+
contents: read
118+
packages: write
119+
security-events: write
120+
steps:
121+
- name: Checkout Code
122+
uses: actions/checkout@v4
123+
124+
- name: Log in to the GitHub Container registry
125+
uses: docker/login-action@v3
126+
with:
127+
registry: ghcr.io
128+
username: ${{ github.actor }}
129+
password: ${{ secrets.GITHUB_TOKEN }}
130+
131+
- name: Set up QEMU
132+
uses: docker/setup-qemu-action@v3
133+
with:
134+
platforms: 'arm64'
135+
136+
- name: Set up Docker Buildx
137+
uses: docker/setup-buildx-action@v3
138+
139+
- name: Build and push
140+
uses: docker/bake-action@v6
141+
id: build
142+
env:
143+
environment: production
144+
registry: ghcr.io/${{ github.repository_owner }}
145+
revision: ${{ github.sha }}
146+
with:
147+
push: true

Dockerfile

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
ARG BASE=debian:bookworm-slim
2+
FROM $BASE AS minimal
3+
4+
ARG PG_VERSION
5+
ARG PG_MAJOR=${PG_VERSION%%.*}
6+
7+
ENV PATH=$PATH:/usr/lib/postgresql/$PG_MAJOR/bin
8+
9+
RUN apt-get update && \
10+
apt-get install -y --no-install-recommends postgresql-common ca-certificates gnupg && \
11+
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
12+
apt-get install --no-install-recommends -o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" postgresql-common -y && \
13+
sed -ri 's/#(create_main_cluster) .*$/\1 = false/' /etc/postgresql-common/createcluster.conf && \
14+
apt-get install --no-install-recommends \
15+
-o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" "postgresql-${PG_MAJOR}=${PG_VERSION}*" -y && \
16+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
17+
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*
18+
19+
20+
RUN usermod -u 26 postgres
21+
USER 26
22+
23+
FROM minimal AS standard
24+
25+
LABEL org.opencontainers.image.title="CloudNativePG PostgreSQL $PG_VERSION standard"
26+
LABEL org.opencontainers.image.description="A standard PostgreSQL $PG_VERSION container image, with a minimal set of extensions and all the locales"
27+
28+
USER root
29+
RUN apt-get update && \
30+
apt-get install -y --no-install-recommends locales-all \
31+
"postgresql-${PG_MAJOR}-pgaudit" \
32+
"postgresql-${PG_MAJOR}-pgvector" \
33+
"postgresql-${PG_MAJOR}-pg-failover-slots" && \
34+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
35+
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*
36+
37+
USER 26

README.md

+66-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ for all available PostgreSQL versions (13 to 17) to be used as
55
operands with the [CloudNativePG operator](https://cloudnative-pg.io)
66
for Kubernetes.
77

8+
## Images
9+
10+
We build three types of images:
11+
* [system](#system)
12+
* [minimal](#minimal)
13+
* [standard](#standard)
14+
15+
Switching from system images to minimal or standard images on an existing
16+
cluster is not currently supported.
17+
18+
Minimal and standard images are supposed to be used alongside a backup plugin
19+
like [Barman Cloud](https://github.com/cloudnative-pg/plugin-barman-cloud).
20+
21+
Images are available via
22+
[GitHub Container Registry](https://github.com/cloudnative-pg/postgres-containers/pkgs/container/postgresql).
23+
24+
Currently, images are automatically rebuilt once a week (Monday).
25+
26+
### System
27+
828
These images are built on top of the [Official Postgres image](https://hub.docker.com/_/postgres)
929
maintained by the [PostgreSQL Docker Community](https://github.com/docker-library/postgres),
1030
by adding the following software:
@@ -14,7 +34,52 @@ by adding the following software:
1434
- Postgres Failover Slots
1535
- pgvector
1636

17-
Currently, images are automatically rebuilt once a week (Monday).
37+
### Minimal
38+
39+
These images are build on top of [official Debian images](https://hub.docker.com/_/debian)
40+
by installing PostgreSQL.
41+
42+
Minimal images include `minimal` in the tag name, e.g.
43+
`17.2-standard-bookworm`.
44+
45+
### Standard
46+
47+
These images are build on top of the minimal images by adding the following
48+
software:
49+
50+
- PGAudit
51+
- Postgres Failover Slots
52+
- pgvector
53+
54+
and all the locales.
55+
56+
Standard images include `standard` in the tag name, e.g.
57+
`17.2-standard-bookworm`.
58+
59+
## SBOMs
60+
61+
Software Bills of Materials (SBOMs) are available for minimal and standard
62+
images. The SBOM for an image can be retrieved with the following command:
63+
64+
```shell
65+
docker buildx imagetools inspect <IMAGE> --format "{{ json .SBOM.SPDX}}"
66+
```
67+
68+
## Testing image builds
69+
70+
Minimal and standard image builds can be tested running bake manually.
71+
You will need a container registry and a builder with the `docker-container`
72+
driver.
73+
74+
```
75+
registry=<REGISTRY_URL> docker buildx bake --builder <BUILDER> --push
76+
```
77+
78+
## License and copyright
79+
80+
This software is available under [Apache License 2.0](LICENSE).
81+
82+
Copyright The CloudNativePG Contributors.
1883

1984
Barman Cloud is distributed by EnterpriseDB under the
2085
[GNU GPL 3 License](https://github.com/EnterpriseDB/barman/blob/master/LICENSE).
@@ -28,18 +93,8 @@ Postgres Failover Slots is distributed by EnterpriseDB under the
2893
pgvector is distributed under the
2994
[PostgreSQL License](https://github.com/pgvector/pgvector/blob/master/LICENSE).
3095

31-
Images are available via
32-
[GitHub Container Registry](https://github.com/cloudnative-pg/postgres-containers/pkgs/container/postgresql).
33-
34-
## License and copyright
35-
36-
This software is available under [Apache License 2.0](LICENSE).
37-
38-
Copyright The CloudNativePG Contributors.
39-
4096
## Trademarks
4197

4298
*[Postgres, PostgreSQL and the Slonik Logo](https://www.postgresql.org/about/policies/trademarks/)
4399
are trademarks or registered trademarks of the PostgreSQL Community Association
44100
of Canada, and used with their permission.*
45-

docker-bake.hcl

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
variable "environment" {
2+
default = "testing"
3+
validation {
4+
condition = contains(["testing", "production"], environment)
5+
error_message = "environment must be either testing or production"
6+
}
7+
}
8+
9+
variable "registry" {
10+
default = "localhost:5000"
11+
}
12+
13+
// Use the revision variable to identify the commit that generated the image
14+
variable "revision" {
15+
default = ""
16+
}
17+
18+
fullname = ( environment == "testing") ? "${registry}/postgresql-testing" : "{registry}/postgresql"
19+
now = timestamp()
20+
21+
target "default" {
22+
matrix = {
23+
tgt = [
24+
"minimal",
25+
"standard"
26+
]
27+
pgVersion = [
28+
"13.18",
29+
"14.15",
30+
"15.10",
31+
"16.6",
32+
"17.2"
33+
]
34+
base = [
35+
// renovate: datasource=docker versioning=loose
36+
"debian:bookworm-slim@sha256:d365f4920711a9074c4bcd178e8f457ee59250426441ab2a5f8106ed8fe948eb",
37+
// renovate: datasource=docker versioning=loose
38+
"debian:bullseye-slim@sha256:b0c91cc181796d34c53f7ea106fbcddaf87f3e601cc371af6a24a019a489c980"
39+
]
40+
}
41+
platforms = [
42+
"linux/amd64",
43+
"linux/arm64"
44+
]
45+
dockerfile = "Dockerfile"
46+
name = "postgresql-${index(split(".",pgVersion),0)}-${tgt}-${distroVersion(base)}"
47+
tags = [
48+
"${fullname}:${index(split(".",pgVersion),0)}-${tgt}-${distroVersion(base)}",
49+
"${fullname}:${pgVersion}-${tgt}-${distroVersion(base)}",
50+
"${fullname}:${pgVersion}-${formatdate("YYYYMMDDhhmm", now)}-${tgt}-${distroVersion(base)}"
51+
]
52+
context = "."
53+
target = "${tgt}"
54+
args = {
55+
PG_VERSION = "${pgVersion}"
56+
BASE = "${base}"
57+
}
58+
attest = [
59+
"type=provenance,mode=max",
60+
"type=sbom"
61+
]
62+
annotations = [
63+
"index,manifest:org.opencontainers.image.created=${now}",
64+
"index,manifest:org.opencontainers.image.url=https://github.com/cloudnative-pg/postgres-containers",
65+
"index,manifest:org.opencontainers.image.source=https://github.com/cloudnative-pg/postgres-containers",
66+
"index,manifest:org.opencontainers.image.version=${pgVersion}",
67+
"index,manifest:org.opencontainers.image.revision=${revision}",
68+
"index,manifest:org.opencontainers.image.vendor=The CloudNativePG Contributors",
69+
"index,manifest:org.opencontainers.image.title=CloudNativePG PostgreSQL ${pgVersion} ${tgt}",
70+
"index,manifest:org.opencontainers.image.description=A ${tgt} PostgreSQL ${pgVersion} container image",
71+
"index,manifest:org.opencontainers.image.documentation=https://github.com/cloudnative-pg/postgres-containers",
72+
"index,manifest:org.opencontainers.image.authors=The CloudNativePG Contributors",
73+
"index,manifest:org.opencontainers.image.licenses=Apache-2.0",
74+
"index,manifest:org.opencontainers.image.base.name=docker.io/library/${tag(base)}",
75+
"index,manifest:org.opencontainers.image.base.digest=${digest(base)}"
76+
]
77+
labels = {
78+
"org.opencontainers.image.created" = "${now}",
79+
"org.opencontainers.image.url" = "https://github.com/cloudnative-pg/postgres-containers",
80+
"org.opencontainers.image.source" = "https://github.com/cloudnative-pg/postgres-containers",
81+
"org.opencontainers.image.version" = "${pgVersion}",
82+
"org.opencontainers.image.revision" = "${revision}",
83+
"org.opencontainers.image.vendor" = "The CloudNativePG Contributors",
84+
"org.opencontainers.image.title" = "CloudNativePG PostgreSQL ${pgVersion} ${tgt}",
85+
"org.opencontainers.image.description" = "A ${tgt} PostgreSQL ${pgVersion} container image",
86+
"org.opencontainers.image.documentation" = "https://github.com/cloudnative-pg/postgres-containers",
87+
"org.opencontainers.image.authors" = "The CloudNativePG Contributors",
88+
"org.opencontainers.image.licenses" = "Apache-2.0"
89+
"org.opencontainers.image.base.name" = "docker.io/library/debian:${tag(base)}"
90+
"org.opencontainers.image.base.digest" = "${digest(base)}"
91+
}
92+
}
93+
94+
function tag {
95+
params = [ imageNameWithSha ]
96+
result = index(split("@", index(split(":", imageNameWithSha), 1)), 0)
97+
}
98+
99+
function distroVersion {
100+
params = [ imageNameWithSha ]
101+
result = index(split("-", tag(imageNameWithSha)), 0)
102+
}
103+
104+
function digest {
105+
params = [ imageNameWithSha ]
106+
result = index(split("@", imageNameWithSha), 1)
107+
}

0 commit comments

Comments
 (0)