Docker and Java - FontConfiguration issue

14,499

Solution 1

I think we found the problem.

When running on Jenkins we use the docker:dind (Docker inside docker) Docker image to provide the docker command in the build. This image is based on Alpine linux. When running docker info we get the following:

On Mac:

Kernel Version: 4.9.87-linuxkit-aufs
Operating System: Docker for Mac

On Jenkins:

Kernel Version: 4.4.115-k8s
Operating System: Alpine Linux v3.7 (containerized)

Alpine linux must be missing those fonts. We fixed the problem by manually installing them in the Dockerfile:

RUN apt-get update \
 && apt-get install --assume-yes apt-utils \
 && apt-get install --assume-yes software-properties-common \
 && apt-get install --assume-yes dbus \
 && apt-get install --assume-yes cgmanager \
 && apt-get install --assume-yes glib-networking \
 && apt-get install --assume-yes libnih-dbus-dev \
 && apt-get install --assume-yes dconf-cli \
 && apt-get install --assume-yes fontconfig

Not sure this is the minimum required libraries but those did the trick :D

Solution 2

With openjdk:8u111-jdk-alpine, installing dejavu fix the problem:

For example:

Dockerfile:

FROM openjdk:8u111-jdk-alpine
# Needed to fix 'Fontconfig warning: ignoring C.UTF-8: not a valid language tag'
ENV LANG en_GB.UTF-8

# JRE fails to load fonts if there are no standard fonts in the image; DejaVu is a good choice,
# see https://github.com/docker-library/openjdk/issues/73#issuecomment-207816707
RUN apk add --update ttf-dejavu && rm -rf /var/cache/apk/*

VOLUME /tmp
COPY /target/*.jar app.jar
ENTRYPOINT ["java","-Xmx100m","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

Solution 3

Installing libfontconfig1 solved the problem for me (source):

RUN apt-get install -y libfontconfig1 && rm -rf /var/lib/apt/lists/*

Solution 4

Just add the following in your Dockerfile

RUN apk add --no-cache fontconfig ttf-dejavu
Share:
14,499
phoenix7360
Author by

phoenix7360

Updated on June 27, 2022

Comments

  • phoenix7360
    phoenix7360 about 2 years

    We've got a Java application that generates word documents using a 3rd party (Asposee but I don't think it matters here). The app is built from a simple Docker file:

    FROM openjdk:10-jdk-slim
    COPY target/*.jar /opt/
    CMD $JAVA_HOME/bin/java $JAVA_OPTS -jar /opt/*.jar
    

    When we build the application locally (mvn package then docker build) and run the application inside k8s it works well.

    However, when we build the image in our CI/CD pipeline with Jenkins we get a runtime exception when running through a specific process which apparently requires additional fonts:

    Caused by: java.lang.NullPointerException: null
        at java.desktop/sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1288)
        at java.desktop/sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:225)
        at java.desktop/sun.awt.FontConfiguration.init(FontConfiguration.java:107)
        at java.desktop/sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:765)
        at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:440)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.desktop/sun.font.SunFontManager.<init>(SunFontManager.java:385)
        at java.desktop/sun.awt.FcFontManager.<init>(FcFontManager.java:35)
        at java.desktop/sun.awt.X11FontManager.<init>(X11FontManager.java:56)
    

    In that case the project is buit in Jenkins, compiled by the docker image maven:3.5.4-jdk-10-slim.

    I've checked both jar files (locally and from jenkins) and the class files are the same (as expected).

    In both cases it's the same base image so I don't understand what could be the difference. Is something different in Docker when building locally vs inside another Docker container?

    EDIT

    We've looked into both docker images and found the following difference.

    Since locally built image ls -l /usr/lib returns:

    drwxr-xr-x  2 root root  4096 May  3  2017 X11
    drwxr-xr-x  5 root root  4096 Apr 26 00:00 apt
    drwxr-xr-x  2 root root  4096 May 26 08:31 binfmt.d
    drwxr-xr-x  2 root root  4096 Jun  6 01:50 cgmanager
    drwxr-xr-x  2 root root  4096 Jun  6 01:50 dbus-1.0
    drwxr-xr-x  2 root root  4096 Jun  6 01:51 dconf
    drwxr-xr-x  3 root root  4096 Jun  6 01:51 debug
    drwxr-xr-x  3 root root  4096 Apr 20 10:08 dpkg
    drwxr-xr-x  2 root root  4096 Jun  6 01:50 environment.d
    drwxr-xr-x  3 root root  4096 Apr 25 04:56 gcc
    drwxr-xr-x  2 root root  4096 Jun  6 01:51 glib-networking
    drwxr-xr-x  2 root root  4096 Apr 26 00:00 init
    drwxr-xr-x  1 root root  4096 Jun  6 01:51 jvm
    drwxr-xr-x  3 root root  4096 Jun  6 01:50 kernel
    lrwxrwxrwx  1 root root    20 Mar  4 09:49 libnih-dbus.so.1 -> libnih-dbus.so.1.0.0
    -rw-r--r--  1 root root 34824 Mar  4 09:49 libnih-dbus.so.1.0.0
    lrwxrwxrwx  1 root root    15 Mar  4 09:49 libnih.so.1 -> libnih.so.1.0.0
    -rw-r--r--  1 root root 92184 Mar  4 09:49 libnih.so.1.0.0
    drwxr-xr-x  3 root root  4096 Mar 29 19:47 locale
    drwxr-xr-x  3 root root  4096 Jun  6 01:50 lsb
    drwxr-xr-x  1 root root  4096 Jul 21  2017 mime
    drwxr-xr-x  2 root root  4096 Jun  6 01:50 modprobe.d
    drwxr-xr-x  2 root root  4096 May 26 08:31 modules-load.d
    -rw-r--r--  1 root root   198 Jan 13 23:36 os-release
    drwxr-xr-x  3 root root  4096 Jun  6 01:51 ssl
    drwxr-xr-x  1 root root  4096 Jun  6 01:50 systemd
    drwxr-xr-x  2 root root  4096 Jun  6 01:50 sysusers.d
    drwxr-xr-x  2 root root  4096 Jul 21  2017 tar
    drwxr-xr-x 15 root root  4096 Feb 11 20:06 terminfo
    drwxr-xr-x  1 root root  4096 Jun  6 01:50 tmpfiles.d
    drwxr-xr-x  1 root root  4096 Apr 26 00:00 udev
    drwxr-xr-x  1 root root 16384 Jun  6 01:51 x86_64-linux-gnu
    

    But inside Jenkins built image ls -l /usr/lib returns:

    drwxr-xr-x  5 root root 4096 Jun 25 00:00 apt
    drwxr-xr-x  3 root root 4096 Jul  3 01:00 debug
    drwxr-xr-x  3 root root 4096 Apr 20 10:08 dpkg
    drwxr-xr-x  3 root root 4096 Jun 17 03:36 gcc
    drwxr-xr-x  2 root root 4096 Jun 25 00:00 init
    drwxr-xr-x  1 root root 4096 Jul  3 01:00 jvm
    drwxr-xr-x  1 root root 4096 Jul 12 11:00 locale
    drwxr-xr-x  3 root root 4096 Jul  3 01:00 lsb
    drwxr-xr-x  1 root root 4096 May 16 07:47 mime
    -rw-r--r--  1 root root  198 Jan 13 23:36 os-release
    drwxr-xr-x  3 root root 4096 Jul  3 01:00 ssl
    drwxr-xr-x  3 root root 4096 Apr 20 10:08 systemd
    drwxr-xr-x  2 root root 4096 May 16 07:47 tar
    drwxr-xr-x 15 root root 4096 May 21 08:54 terminfo
    drwxr-xr-x  2 root root 4096 Jun 25 00:00 tmpfiles.d
    drwxr-xr-x  3 root root 4096 Jun 25 00:00 udev
    drwxr-xr-x  2 root root 4096 May  3  2017 X11
    drwxr-xr-x  1 root root 4096 Jul  3 01:00 x86_64-linux-gnu
    

    This is really puzzling as I thought Docker would always produce the same image from identical Dockerfiles