How to add extra linux dependencies into a spring-boot buildpack image?
This answer assumes that by "... spring-boot application to use buildpacks" you mean the use of the spring-boot:build-image
maven goal.
The issue lays with the default builder (gcr.io/paketo-buildpacks/builder:base
) used by the maven plugin. Builder is responsible for configuring the OS image, and the "base" builder doesn't include fontconfig
package. .
The easiest way to enable fontconfig
package is to use the "full" builder (gcr.io/paketo-buildpacks/builder:full-cf
or gcr.io/paketo-buildpacks/builder:latest
); you can do so for example in one of the following ways:
by specifying the builder configuration parameter in the maven plugin,
<project><build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.3.BUILD-SNAPSHOT</version> <configuration> <image> <builder>gcr.io/paketo-buildpacks/builder:latest</builder> </image> </configuration> </plugin> </plugins></build></project>
or directly on your
mvn
command line by adding-Dspring-boot.build-image.builder=gcr.io/paketo-buildpacks/builder:latest
.
However, this is not ideal because the full OS image is much larger (approx. 1.45GB for "full" vs. 644MB for "base" - observed in docker image listing), a fair bit of overhead "just" for enabling fontconfig
.
A more involved approach would require creating a custom builder with custom mixins, in order to create a tailored "base" image with the extra packages. But I personally found it easier to just use the dockerfile approach in this scenario. Some articles on creating a custom builder:
As Michal outlined, using the bigger paketobuildpacks/builder:full
builder isn't ideal. Also creating a custom builder & stack would be a huge overhead - since we want to use Cloud Native Buildpacks to free us from the burden of maintaining our own Dockerfile
. And creating our own builder/stack would bring in way more complexity then we had before writing the Dockerfile
.
On the other hand, the need to install separate packages into the container images created by the spring-boot-maven-plugin
or Spring Boot Gradle plugins is widespread. So I thought of a minimally invasive solution - and here it is (as derived from). Let's assume our mvn spring-boot:build-image
(or Gradle buildImage) command produced a container image called my-app:0.0.1-SNAPSHOT
:
Now first install fontconfig ttf-dejavu
into the image with (we need root priviledges for that):
docker run --user="root" --entrypoint launcher my-app:0.0.1-SNAPSHOT "apt-get update && apt-get install fontconfig ttf-dejavu -y"
Crab container id of the stopped container with docker ps -a
:
$ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES2ff7db32825f my-app:0.0.1-SNAPSHOT "launcher 'apt-get u…" 44 minutes ago Exited (0) 44 minutes ago reverent_swanson
Create a new container image based on the one we installed curl
into with:
docker commit 2ff7db32825f my-app-with-fontconfig-ttf
Fire up a new container defining the correct ENTRYPOINT
to start Spring Boot app & switching back to the CNB's standard cnb
user (and not using root anymore to avoid potential security risks):
docker run --rm -p 8080:8080 --user="cnb" --entrypoint /cnb/process/web my-app-with-fontconfig-ttf
For a more detailled background info see this so answer also.