Skip to content

Commit 98310a9

Browse files
committedJan 9, 2025
ci: build minimal and standard images
Build images without barman-cloud, to be used with backup plugins. Closes #132 Signed-off-by: Francesco Canovai <francesco.canovai@enterprisedb.com>
1 parent ff6034a commit 98310a9

File tree

4 files changed

+350
-11
lines changed

4 files changed

+350
-11
lines changed
 

‎.github/workflows/bake.yaml

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

+56-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,42 @@ 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+
## License and copyright
69+
70+
This software is available under [Apache License 2.0](LICENSE).
71+
72+
Copyright The CloudNativePG Contributors.
1873

1974
Barman Cloud is distributed by EnterpriseDB under the
2075
[GNU GPL 3 License](https://github.com/EnterpriseDB/barman/blob/master/LICENSE).
@@ -28,18 +83,8 @@ Postgres Failover Slots is distributed by EnterpriseDB under the
2883
pgvector is distributed under the
2984
[PostgreSQL License](https://github.com/pgvector/pgvector/blob/master/LICENSE).
3085

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-
4086
## Trademarks
4187

4288
*[Postgres, PostgreSQL and the Slonik Logo](https://www.postgresql.org/about/policies/trademarks/)
4389
are trademarks or registered trademarks of the PostgreSQL Community Association
4490
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+
dockerfile = "Dockerfile"
42+
name = "postgresql-${index(split(".",pgVersion),0)}-${tgt}-${distroVersion(base)}"
43+
tags = [
44+
"${fullname}:${index(split(".",pgVersion),0)}-${tgt}-${distroVersion(base)}",
45+
"${fullname}:${pgVersion}-${tgt}-${distroVersion(base)}",
46+
"${fullname}:${pgVersion}-${formatdate("YYYYMMDDhhmm", now)}-${tgt}-${distroVersion(base)}"
47+
]
48+
context = "."
49+
target = "${tgt}"
50+
args = {
51+
PG_VERSION = "${pgVersion}"
52+
BASE = "${base}"
53+
}
54+
attest = [
55+
"type=provenance,mode=max",
56+
"type=sbom"
57+
]
58+
annotations = [
59+
"index,manifest:org.opencontainers.image.created=${now}",
60+
"index,manifest:org.opencontainers.image.url=https://github.com/cloudnative-pg/postgres-containers",
61+
"index,manifest:org.opencontainers.image.source=https://github.com/cloudnative-pg/postgres-containers",
62+
"index,manifest:org.opencontainers.image.version=${pgVersion}",
63+
"index,manifest:org.opencontainers.image.revision=${revision}",
64+
"index,manifest:org.opencontainers.image.vendor=The CloudNativePG Contributors",
65+
"index,manifest:org.opencontainers.image.title=CloudNativePG PostgreSQL ${pgVersion} ${tgt}",
66+
"index,manifest:org.opencontainers.image.description=A ${tgt} PostgreSQL ${pgVersion} container image",
67+
"index,manifest:org.opencontainers.image.documentation=https://github.com/cloudnative-pg/postgres-containers",
68+
"index,manifest:org.opencontainers.image.authors=The CloudNativePG Contributors",
69+
"index,manifest:org.opencontainers.image.licenses=Apache-2.0",
70+
"index,manifest:org.opencontainers.image.base.name=docker.io/library/${tag(base)}",
71+
"index,manifest:org.opencontainers.image.base.digest=${digest(base)}"
72+
]
73+
labels = {
74+
"org.opencontainers.image.created" = "${now}",
75+
"org.opencontainers.image.url" = "https://github.com/cloudnative-pg/postgres-containers",
76+
"org.opencontainers.image.source" = "https://github.com/cloudnative-pg/postgres-containers",
77+
"org.opencontainers.image.version" = "${pgVersion}",
78+
"org.opencontainers.image.revision" = "${revision}",
79+
"org.opencontainers.image.vendor" = "The CloudNativePG Contributors",
80+
"org.opencontainers.image.title" = "CloudNativePG PostgreSQL ${pgVersion} ${tgt}",
81+
"org.opencontainers.image.description" = "A ${tgt} PostgreSQL ${pgVersion} container image",
82+
"org.opencontainers.image.documentation" = "https://github.com/cloudnative-pg/postgres-containers",
83+
"org.opencontainers.image.authors" = "The CloudNativePG Contributors",
84+
"org.opencontainers.image.licenses" = "Apache-2.0"
85+
"org.opencontainers.image.base.name" = "docker.io/library/debian:${tag(base)}"
86+
"org.opencontainers.image.base.digest" = "${digest(base)}"
87+
}
88+
// platforms = [
89+
// "linux/amd64",
90+
// "linux/arm64"
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)