Skip to content

Commit

Permalink
ci: build minimal and standard images
Browse files Browse the repository at this point in the history
Build images without barman-cloud, to be used with backup plugins.

Closes #132

Signed-off-by: Francesco Canovai <francesco.canovai@enterprisedb.com>
  • Loading branch information
fcanovai committed Jan 9, 2025
1 parent ff6034a commit 81eba68
Show file tree
Hide file tree
Showing 4 changed files with 341 additions and 11 deletions.
152 changes: 152 additions & 0 deletions .github/workflows/bake.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
name: Bake images

on:
schedule:
- cron: 0 8 * * 1
workflow_dispatch:
inputs:
environment:
type: choice
options:
- testing
- production
default: testing
description: "Choose the environment to bake the images for"
# TODO: remove this, it is used to test a workflow that is not on main
push:

jobs:
# Start by building images for testing. We want to run security checks before pushing those to production.
testbuild:
name: Build for testing
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
outputs:
metadata: ${{ steps.build.outputs.metadata }}
images: ${{ steps.images.outputs.images }}
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Log in to the GitHub Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# TODO: review this when GitHub has linux/arm64 runners available (Q1 2025?)
# https://github.com/github/roadmap/issues/970
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: 'arm64'

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host

- name: Build and push
uses: docker/bake-action@v6
id: build
env:
environment: testing
registry: ghcr.io/${{ github.repository_owner }}
revision: ${{ github.sha }}
with:
push: true

# Get a list of the images that were built and pushed. We only care about a single tag for each image.
- name: Generated images
id: images
run: |
echo "images=$(echo '${{steps.build.outputs.metadata}}' | jq -c '[ .[]."image.name" | sub(",.*";"" )]')" >> "$GITHUB_OUTPUT"
security:
name: Security checks
runs-on: ubuntu-latest
needs:
- testbuild
strategy:
matrix:
image: ${{fromJson(needs.testbuild.outputs.images)}}
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Log in to the GitHub Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Dockle
uses: erzz/dockle-action@v1
with:
image: ${{ matrix.image }}
exit-code: '1'

- name: Snyk
uses: snyk/actions/docker@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: "${{ matrix.image }}"
args: --severity-threshold=high --file=Dockerfile

- name: Upload result to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v3
continue-on-error: true
with:
sarif_file: snyk.sarif

# Build the image for production.
#
# TODO: no need to rebuild everything, just copy the testing images we have generated to the production registry
# if we get here and we are building for production.
prodbuild:
if: github.event.inputs.environment == 'production' || github.event_name == 'schedule'
name: Build for production
runs-on: ubuntu-latest
needs:
- security
permissions:
contents: read
packages: write
security-events: write
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Log in to the GitHub Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: 'arm64'

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host

- name: Build and push
uses: docker/bake-action@v6
id: build
env:
environment: production
registry: ghcr.io/${{ github.repository_owner }}
revision: ${{ github.sha }}
with:
push: true
37 changes: 37 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
ARG BASE=debian:bookworm-slim
FROM $BASE AS minimal

ARG PG_VERSION
ARG PG_MAJOR=${PG_VERSION%%.*}

ENV PATH=$PATH:/usr/lib/postgresql/$PG_MAJOR/bin

RUN apt-get update && \
apt-get install -y --no-install-recommends postgresql-common ca-certificates gnupg && \
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
apt-get install --no-install-recommends -o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" postgresql-common -y && \
sed -ri 's/#(create_main_cluster) .*$/\1 = false/' /etc/postgresql-common/createcluster.conf && \
apt-get install --no-install-recommends \
-o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" "postgresql-${PG_MAJOR}=${PG_VERSION}*" -y && \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*


RUN usermod -u 26 postgres
USER 26

FROM minimal AS standard

LABEL org.opencontainers.image.title="CloudNativePG PostgreSQL $PG_VERSION standard"
LABEL org.opencontainers.image.description="A standard PostgreSQL $PG_VERSION container image, with a minimal set of extensions and all the locales"

USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends locales-all \
"postgresql-${PG_MAJOR}-pgaudit" \
"postgresql-${PG_MAJOR}-pgvector" \
"postgresql-${PG_MAJOR}-pg-failover-slots" && \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*

USER 26
56 changes: 45 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ for all available PostgreSQL versions (13 to 17) to be used as
operands with the [CloudNativePG operator](https://cloudnative-pg.io)
for Kubernetes.

## Images

We build three types of images:
* [system](#system)
* [minimal](#minimal)
* [standard](#standard)

Switching from system images to minimal or standard images on an existing
cluster is not currently supported.

Minimal and standard images are supposed to be used alongside a backup plugin
like [Barman Cloud](https://github.com/cloudnative-pg/plugin-barman-cloud).

Images are available via
[GitHub Container Registry](https://github.com/cloudnative-pg/postgres-containers/pkgs/container/postgresql).

Currently, images are automatically rebuilt once a week (Monday).

### System

These images are built on top of the [Official Postgres image](https://hub.docker.com/_/postgres)
maintained by the [PostgreSQL Docker Community](https://github.com/docker-library/postgres),
by adding the following software:
Expand All @@ -14,7 +34,31 @@ by adding the following software:
- Postgres Failover Slots
- pgvector

Currently, images are automatically rebuilt once a week (Monday).
### Minimal

These images are build on top of [official Debian images](https://hub.docker.com/_/debian)
by installing PostgreSQL.

Minimal images include `minimal` in the tag name, e.g. `17.2-standard-bookworm`.

### Standard

These images are build on top of the minimal images by adding the following
software:

- PGAudit
- Postgres Failover Slots
- pgvector

and all the locales.

Standard images include `standard` in the tag name, e.g. `17.2-standard-bookworm`.

## License and copyright

This software is available under [Apache License 2.0](LICENSE).

Copyright The CloudNativePG Contributors.

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

Images are available via
[GitHub Container Registry](https://github.com/cloudnative-pg/postgres-containers/pkgs/container/postgresql).

## License and copyright

This software is available under [Apache License 2.0](LICENSE).

Copyright The CloudNativePG Contributors.

## Trademarks

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

107 changes: 107 additions & 0 deletions docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
variable "environment" {
default = "testing"
validation {
condition = contains(["testing", "production"], environment)
error_message = "environment must be either testing or production"
}
}

variable "registry" {
default = "localhost:5000"
}

// Use the revision variable to identify the commit that generated the image
variable "revision" {
default = ""
}

fullname = ( environment == "testing") ? "${registry}/postgresql-testing" : "{registry}/postgresql"
now = timestamp()

target "default" {
matrix = {
tgt = [
"minimal",
// "standard"
]
pgVersion = [
// "13.18",
// "14.15",
// "15.10",
"16.6",
"17.2"
]
base = [
// renovate: datasource=docker versioning=loose
// "debian:bookworm-slim@sha256:d365f4920711a9074c4bcd178e8f457ee59250426441ab2a5f8106ed8fe948eb",
// renovate: datasource=docker versioning=loose
"debian:bullseye-slim@sha256:b0c91cc181796d34c53f7ea106fbcddaf87f3e601cc371af6a24a019a489c980"
]
}
dockerfile = "Dockerfile"
name = "postgresql-${index(split(".",pgVersion),0)}-${tgt}-${distroVersion(base)}"
tags = [
"${fullname}:${index(split(".",pgVersion),0)}-${tgt}-${distroVersion(base)}",
"${fullname}:${pgVersion}-${tgt}-${distroVersion(base)}",
"${fullname}:${pgVersion}-${formatdate("YYYYMMDDhhmm", now)}-${tgt}-${distroVersion(base)}"
]
context = "."
target = "${tgt}"
args = {
PG_VERSION = "${pgVersion}"
BASE = "${base}"
}
attest = [
"type=provenance,mode=max",
"type=sbom"
]
annotations = [
"index,manifest:org.opencontainers.image.created=${now}",
"index,manifest:org.opencontainers.image.url=https://github.com/cloudnative-pg/postgres-containers",
"index,manifest:org.opencontainers.image.source=https://github.com/cloudnative-pg/postgres-containers",
"index,manifest:org.opencontainers.image.version=${pgVersion}",
"index,manifest:org.opencontainers.image.revision=${revision}",
"index,manifest:org.opencontainers.image.vendor=The CloudNativePG Contributors",
"index,manifest:org.opencontainers.image.title=CloudNativePG PostgreSQL ${pgVersion} ${tgt}",
"index,manifest:org.opencontainers.image.description=A ${tgt} PostgreSQL ${pgVersion} container image",
"index,manifest:org.opencontainers.image.documentation=https://github.com/cloudnative-pg/postgres-containers",
"index,manifest:org.opencontainers.image.authors=The CloudNativePG Contributors",
"index,manifest:org.opencontainers.image.licenses=Apache-2.0",
"index,manifest:org.opencontainers.image.base.name=docker.io/library/${tag(base)}",
"index,manifest:org.opencontainers.image.base.digest=${digest(base)}"
]
labels = {
"org.opencontainers.image.created" = "${now}",
"org.opencontainers.image.url" = "https://github.com/cloudnative-pg/postgres-containers",
"org.opencontainers.image.source" = "https://github.com/cloudnative-pg/postgres-containers",
"org.opencontainers.image.version" = "${pgVersion}",
"org.opencontainers.image.revision" = "${revision}",
"org.opencontainers.image.vendor" = "The CloudNativePG Contributors",
"org.opencontainers.image.title" = "CloudNativePG PostgreSQL ${pgVersion} ${tgt}",
"org.opencontainers.image.description" = "A ${tgt} PostgreSQL ${pgVersion} container image",
"org.opencontainers.image.documentation" = "https://github.com/cloudnative-pg/postgres-containers",
"org.opencontainers.image.authors" = "The CloudNativePG Contributors",
"org.opencontainers.image.licenses" = "Apache-2.0"
"org.opencontainers.image.base.name" = "docker.io/library/debian:${tag(base)}"
"org.opencontainers.image.base.digest" = "${digest(base)}"
}
// platforms = [
// "linux/amd64",
// "linux/arm64"
// ]
}

function tag {
params = [ imageNameWithSha ]
result = index(split("@", index(split(":", imageNameWithSha), 1)), 0)
}

function distroVersion {
params = [ imageNameWithSha ]
result = index(split("-", tag(imageNameWithSha)), 0)
}

function digest {
params = [ imageNameWithSha ]
result = index(split("@", imageNameWithSha), 1)
}

0 comments on commit 81eba68

Please sign in to comment.