Skip to content

Commit f5b346c

Browse files
committed
chore: add blog post about building images with bake
Signed-off-by: Jonathan Gonzalez V. <[email protected]>
1 parent 6dfc695 commit f5b346c

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
extensions = [
2+
"pgvector",
3+
]
4+
target "myimage" {
5+
dockerfile-inline = <<EOT
6+
ARG BASEIMAGE="ghcr.io/cloudnative-pg/postgresql:16.9-standard-bookworm"
7+
FROM $BASEIMAGE AS myimage
8+
ARG EXTENSIONS
9+
USER root
10+
RUN apt-get update && \
11+
apt-get install -y --no-install-recommends $EXTENSIONS && \
12+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
13+
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*
14+
USER 26
15+
EOT
16+
matrix = {
17+
tgt = [
18+
"myimage"
19+
]
20+
pgVersion = [
21+
"16.9",
22+
"17.5",
23+
]
24+
}
25+
name = "postgresql-${index(split(".",cleanVersion(pgVersion)),0)}-standard-bookworm"
26+
target = "${tgt}"
27+
args = {
28+
BASE_IMAGE = "ghcr.io/cloudnative-pg/postgresql:${cleanVersion(pgVersion)}-standard-bookworm",
29+
EXTENSIONS = "${getExtensionsString(pgVersion, extensions)}",
30+
}
31+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
---
2+
title: "Creating a custom container image for CloudNativePG v2.0"
3+
date: 2025-06-17
4+
draft: false
5+
image:
6+
url:
7+
attribution:
8+
author: jgonzalez
9+
tags:
10+
- blog
11+
- information
12+
- programming
13+
- applications
14+
- containers
15+
- postgresql
16+
- postgres
17+
- images
18+
- tutorial
19+
summary: Creating a container image for CloudNativePG Operator v2.0
20+
---
21+
22+
## Summary
23+
In an almost [two years old blog post](https://cloudnative-pg.io/blog/creating-container-images/), we explained how
24+
to build a custom container image for CloudNativePG. After two years, many things have changed in the containers world,
25+
one of those things was the introduction of [Bake](https://www.docker.com/blog/bake/) in Docker, which allows you to build
26+
images using a simple configuration file, and it is now our recommended way to build images for CloudNativePG.
27+
28+
We will follow a simple cooking recipe to create a custom container image or a set of container images, since Bake
29+
allows you to build multiple images at once in a pretty simple way.
30+
31+
## Ingredients
32+
- A bake file, we will use the one provided in the [CloudNativePG repository](https://github.com/cloudnative-pg/postgres-containers/blob/main/docker-bake.hcl)
33+
- A Dockerfile, we will provide a simple one, that will use a base image
34+
35+
Cooking time: 10 minutes
36+
37+
## Instructions
38+
39+
### Step 1: Prepare local bake file
40+
In a local file with name `bake.hcl`, we add the following content, which is a simple bake file that will build a custom image
41+
```hcl
42+
extensions = [
43+
"pgvector",
44+
]
45+
target "myimage" {
46+
dockerfile-inline = <<EOT
47+
ARG BASEIMAGE="ghcr.io/cloudnative-pg/postgresql:16.9-standard-bookworm"
48+
FROM $BASEIMAGE AS myimage
49+
ARG EXTENSIONS
50+
USER root
51+
RUN apt-get update && \
52+
apt-get install -y --no-install-recommends $EXTENSIONS && \
53+
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \
54+
rm -rf /var/lib/apt/lists/* /var/cache/* /var/log/*
55+
USER 26
56+
EOT
57+
matrix = {
58+
tgt = [
59+
"myimage"
60+
]
61+
pgVersion = [
62+
"16.9",
63+
"17.5",
64+
]
65+
}
66+
name = "postgresql-${index(split(".",cleanVersion(pgVersion)),0)}-standard-bookworm"
67+
target = "${tgt}"
68+
args = {
69+
BASE_IMAGE = "ghcr.io/cloudnative-pg/postgresql:${cleanVersion(pgVersion)}-standard-bookworm",
70+
EXTENSIONS = "${getExtensionsString(pgVersion, extensions)}",
71+
}
72+
}
73+
74+
```
75+
There's a couple of things that we can remark here:
76+
* The `extensions` variable is a list of extensions that we want to include in the image, in this case we are using `pg_vector`,
77+
but you can add any other extension that you want to include in the image.
78+
* The `dockerfile-inline` variable contains our Dockerfile definition, which cannot be used remotely, will explain more about this later.
79+
* The `target` and the `tgt` have the same name, you can use what ever you want here as a name
80+
* The `pgVersion` is a variable list that contains basically, the MAJOR.MINOR version of PostgreSQL
81+
* The `name` is the name that we will use later to refer to one element of the matrix that we created
82+
* The `args` is all the arguments passed to the Dockerfile, will talk more about this later.
83+
* The function `getExtensionsString()` is one that is inherited from a bake file that we reference in the Ingredients section
84+
85+
### Step 2: Build the image
86+
87+
We can now build the image using the following command:
88+
```bash
89+
docker buildx bake -f docker-bake.hcl -f cwd://bake.hcl "https://github.com/cloudnative-pg/postgres-containers.git" myimage
90+
```
91+
This will by default, build the image for the bake matrix we previously created and will try to push the image to the registry
92+
`localhost:5000`, which is the default registry defined for testing environments in the parent bake file, so let's explain the full command first.
93+
94+
As is defined in the [Bake documentation about remote definition](https://docs.docker.com/build/bake/remote-definition/)
95+
you can use a remote bake definition with all the functions and default targets and attach another local one that you can use to override
96+
all the default values, in this case the `-f cwd://bake.hcl` is the local file that we created in the previous step, and
97+
the `-f docker-bake.hcl` is the remote file that we're using to build the image, this is in the git repo.
98+
99+
You can explore more about all the content generated and used inside the bake file with the following command:
100+
```bash
101+
docker buildx bake -f docker-bake.hcl -f cwd://bake.hcl "https://github.com/cloudnative-pg/postgres-containers.git" myimage --print
102+
```
103+
104+
### Step 3: Push the image to a registry
105+
106+
Now you just need to push the image to a registry, you can do this by using the following command:
107+
```bash
108+
registry=your/registry:5000 docker buildx bake -f docker-bake.hcl -f cwd://bake.hcl "https://github.com/cloudnative-pg/postgres-containers.git" myimage --push
109+
```
110+
The previous command will push the images in the following format `your/registry:5000/postgresql-testing:17-standard-bookworm`,
111+
using the `--print` you can explore the full list of tags created that are in the parent bake file.
112+
113+
### Step 4: Serve the image
114+
115+
You can now let your clusters to use the image that it's built and based on the CloudNativePG operand images provided
116+
by the CloudNativePG project.
117+
118+
## Deep dive into the Bake and Dockerfile
119+
120+
The simplicity of Bake to bring more stuff is amazing and allows you to create a custom image in a very simple way.
121+
But, how is this magic happens? Let's take a look at the Bake and the Docker file.
122+
123+
### Bake file
124+
125+
This is the base of our magic, but that magic starts in our postgres-containers repository, where we have a `docker-bake.hcl` file
126+
that it's being used to build the images provided by the CloudNativePG project, and it's the base for our custom Bake file.
127+
The `docker-bake.hcl` file contains a lot of functions that are used to build the images, and one of them is the `getExtensionsString()`,
128+
this one, using the list of extensions we provided, will return a string of the extensions with the correct package name
129+
for a Debian-based distribution, in our case, Debian Bookworm, so the `pg_vector` extension will be translated into
130+
`postgresql-16-pgvector` which will be the name of the package of pgvector extensions for PostgreSQL 16 in the Debian
131+
Bookworm distribution.
132+
133+
Even the variable that are passed in the `args` variable to the Dockerfile are processed by the Bake file, in this case,
134+
the variables we add, are being added and the ones we overwrite are being overwritten, so we can use the same variables
135+
and content used in the parent Bake file.
136+
137+
### Dockerfile file
138+
139+
The Dockerfile is simple an inline content, because of the limitations of Bake to overwrite the remote Dockerfile with a
140+
local one, but it allows us to change the FROM in the image, which means we can create an image that it's directly based
141+
on the CloudNativePG images, and we can add the extensions we want to use in our image, without building all of them
142+
143+
## There's more!
144+
145+
You may want to avoid building arm64 image by adding the following:
146+
```hcl
147+
platforms = ["linux/amd64"]
148+
```
149+
This will overwrite the platforms variable and so, will build only for one platform
150+
151+
Also, if you want to build everything into your own repository and manage the same tags, it's possible, in the future
152+
we may write another post explaining this

0 commit comments

Comments
 (0)