Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add extra linux dependencies into a spring-boot buildpack image?

I updated my spring-boot application to use buildpacks to create my docker-image instead of a dockerfile. I also use Apache POI in my application and since that update I get an error when generating an xlsx file. After some digging, I think it happens because the fontconfig and/or ttf-dejavu packages are missing. But how do I add these in the dockerimage? With a dockerfile I would just add something like

RUN apt-get update && apt-get install fontconfig ttf-dejavu

But how do I achieve the same with buildpacks?

like image 337
Pieter Van der Haegen Avatar asked Jun 20 '20 10:06

Pieter Van der Haegen


3 Answers

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:

  • https://buildpacks.io/docs/operator-guide/create-a-builder/
  • https://medium.com/@srinivasan.surprise/unpack-cloud-native-buildpacks-9959b601424b
like image 97
Michal Avatar answered Nov 19 '22 03:11

Michal


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 -a
CONTAINER ID   IMAGE                                  COMMAND                  CREATED          STATUS                       PORTS     NAMES
2ff7db32825f   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.

like image 23
jonashackt Avatar answered Nov 19 '22 03:11

jonashackt


I will improve answer from jonashackt a little bit.

Instead of running docker run you can create new Dockerfile that will use image produced by Spring Boot plugin as a base image.

For example:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <name>acme.com/artifact:boot</name>
    </image>
  </configuration>
</plugin>

Then in your Dockerfile

FROM acme.com/artifact:boot

USER root
RUN apt-get update && apt-get install -y fontconfig ttf-dejavu
like image 1
bsmk Avatar answered Nov 19 '22 01:11

bsmk