paketo-buildpacks / java-native-image

A Cloud Native Buildpack with an order definition suitable for Java native image applications
Apache License 2.0
28 stars 4 forks source link

How to compile with musl support for Spring Boot #507

Open jabrena opened 1 year ago

jabrena commented 1 year ago

Hi,

I am reviewing the native features from Spring Boot 3 but I am not able to compile with musl support.

If you compile a Spring Boot application without the buildpack support, it is possible to do it if you add in your project:

mvn native:compile -Pnative package

pom.xml configuration:

                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <configuration>
                            <buildArgs>
                                <buildArg>--static</buildArg>
                                <buildArg>--libc=musl</buildArg>
                            </buildArgs>
                        </configuration>
                    </plugin>

but you have to have in the PATH musl libraries. In that way, when you compile, you will see that the compiler changes:

========================================================================================================================
GraalVM Native Image: Generating 'benchmark-jibber' (static executable)...
========================================================================================================================
[1/7] Initializing...                                                                                   (10.5s @ 0.17GB)
 Version info: 'GraalVM 23.0.0-dev Java 19 CE'
 Java version info: '19+36-jvmci-23.0-b01'
 C compiler: x86_64-linux-musl-gcc (linux, x86_64, 10.2.1)
 Garbage collector: Serial GC
 2 user-specific feature(s)
 - com.oracle.svm.thirdparty.gson.GsonFeature
 - org.springframework.aot.nativex.feature.PreComputeFieldFeature

But If I add the following configuration in the pom.xml and having in the PATH the musl library:

<plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <imageName>jibber-benchmark:buildpacks-native.0.0.1-SNAPSHOT</imageName>
                            <image>
                                <buildpacks>
                                    <buildpack>gcr.io/paketo-buildpacks/bellsoft-liberica:9.10</buildpack>
                                    <buildpack>gcr.io/paketo-buildpacks/java-native-image</buildpack>
                                </buildpacks>
                                <env>
                                    <BP_BINARY_COMPRESSION_METHOD>upx</BP_BINARY_COMPRESSION_METHOD>
                                    <BP_NATIVE_IMAGE_BUILD_ARGUMENTS>--static --libc=musl</BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
                                </env>
                            </image>
                        </configuration>

then I receive an error:

mvn -Pnative spring-boot:build-image
[INFO] [creator] Error: Default native-compiler executable 'musl-gcc' not found via environment variable PATH:

How to compile with musl support?

image

Many thanks in advance

Juan Antonio

dmikusa commented 1 year ago

It may just be a matter of making sure that muls-gcc is installed. I'm pretty certain, it won't be in the tiny/base stacks, but it might be in the full stack, so you could give that a try.

Set the builder to paketobuildpacks/builder:full and see if it works.

If that doesn't work, you'd need to install the builder. There are a handful of options for doing that, which are outlined here. I would suggest option #4 first and see if you can make that work. If you add the apt-buildpack to the beginning of your buildpack list, that should allow it to install deps before any other buildpacks run.

Hope that helps!

jabrena commented 1 year ago

Hi @dmikusa,

I was testing the first idea and it is not working:

                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <imageName>jibber-benchmark:buildpacks-native.0.0.1-SNAPSHOT</imageName>
                            <image>
                                <builder>paketobuildpacks/builder:full</builder>
                                <buildpacks>
                                    <buildpack>gcr.io/paketo-buildpacks/bellsoft-liberica:9.10</buildpack>
                                    <buildpack>gcr.io/paketo-buildpacks/java-native-image</buildpack>
                                </buildpacks>
                                <env>                                   <BP_BINARY_COMPRESSION_METHOD>upx</BP_BINARY_COMPRESSION_METHOD>
                                    <BP_NATIVE_IMAGE_BUILD_ARGUMENTS>--static --libc=musl</BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
                                </env>
                            </image>
                        </configuration>
                    </plugin>

Error:

[INFO]     [creator]     [1/7] Initializing...                                            (0.0s @ 0.27GB)
[INFO]     [creator]     Error: Default native-compiler executable 'musl-gcc' not found via environment variable PATH
[INFO]     [creator]     Error: To prevent native-toolchain checking provide command-line option -H:-CheckToolchain
[INFO]     [creator]     Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
[INFO]     [creator]     --------------------------------------------------------------------------------
[INFO]     [creator]          0.6s (7.5% of total time) in 8 GCs | Peak RSS: 0.64GB | CPU load: 1.90
[INFO]     [creator]     ================================================================================
[INFO]     [creator]     Failed generating '/layers/paketo-buildpacks_native-image/native-image/com.example.benchmarks.BenchmarkServerJibber' after 7.2s.
[INFO]     [creator]     Error: Image build request failed with exit status 1
[INFO]     [creator]     unable to invoke layer creator
[INFO]     [creator]     unable to contribute native-image layer
[INFO]     [creator]     error running build
[INFO]     [creator]     exit status 1
[INFO]     [creator]     ERROR: failed to build: exit status 1
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

In relation to the second option: https://stackoverflow.com/questions/69447497/is-it-possible-to-customize-docker-image-generated-with-spring-native-with-buil/69466535#69466535

I have few questions:

GraalVM Demos has a script to add in the PATH the library but how to use with the buildpacks? https://github.com/graalvm/graalvm-demos/blob/master/tiny-java-containers/setup-musl.sh

Can you provide and example with apt-buildpack to test in my pom.xml in order to install and musl in my native compilation?

dmikusa commented 1 year ago

The apt-buildpack README has an example, https://github.com/fagiani/apt-buildpack. You basically just add an Aptfile with the list of packages to install and modify your pom.xml like this:

                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <imageName>jibber-benchmark:buildpacks-native.0.0.1-SNAPSHOT</imageName>
                            <image>
                                <builder>paketobuildpacks/builder:base</builder>
                                <buildpacks>
                                    <buildpack>ghcr.io/fagiani/buildpacks/fagiani_apt:0.2.4</buildpack>
                                    <buildpack>gcr.io/paketo-buildpacks/java-native-image</buildpack>
                                </buildpacks>
                                <env>                                   <BP_BINARY_COMPRESSION_METHOD>upx</BP_BINARY_COMPRESSION_METHOD>
                                    <BP_NATIVE_IMAGE_BUILD_ARGUMENTS>--static --libc=musl</BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
                                </env>
                            </image>
                        </configuration>
                    </plugin>

The key thing about making this work is that you'd need to have access to an apt repo from which you can get the software or .deb files you can point it to. It looks like this is the case though, from the instructions sudo apt-get install musl to install. That means in your Aptfile, you'd just put musl.

When you build, you should see the apt-buildpack being pulled down and running first. It'll install the additional software.

Give that a try and let me know how far you get.

jabrena commented 1 year ago

Hi @dmikusa,

I followed your instructions, but the build failed:

[INFO]  > Pulled run image 'paketobuildpacks/run@sha256:9ba5e33223e84d8de461f53fa18ad2ec4b7f905d7940eb5a292b421804e8ce54'
[INFO]  > Pulling buildpack image 'ghcr.io/fagiani/buildpacks/fagiani_apt:0.2.4' 100%
[INFO]  > Pulled buildpack image 'ghcr.io/fagiani/buildpacks/fagiani_apt@sha256:15a9501ec9f520f55d318a02d4e3d407def462d0294d2937bc7c717fb7c1ade9'
[INFO]  > Pulling buildpack image 'gcr.io/paketo-buildpacks/java-native-image:latest' 36%
[INFO]  > Pulling buildpack image 'gcr.io/paketo-buildpacks/java-native-image:latest' 100%
[INFO]  > Pulled buildpack image 'gcr.io/paketo-buildpacks/java-native-image@sha256:57bd38cd46ad63b56f757df56e544c229bfb8b454589b8c352961268640dd41e'
[INFO]  > Executing lifecycle version v0.15.0
[INFO]  > Using build cache volume 'pack-cache-b069c1148f49.build'
[INFO] 
[INFO]  > Running creator
[INFO]     [creator]     ===> ANALYZING
[INFO]     [creator]     Previous image with name "docker.io/library/jibber-benchmark:buildpacks-native.0.0.1-SNAPSHOT" not found
[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     ======== Output: fagiani/apt@0.2.4 ========
[INFO]     [creator]     no
[INFO]     [creator]     err:  fagiani/apt@0.2.4 (1)
[INFO]     [creator]     ERROR: No buildpack groups passed detection.
[INFO]     [creator]     ERROR: failed to detect: buildpack(s) failed with err

I put the file Aptfile at the same level than the pom.xml

image

But the script from the builbpack doesn´t see the file Aptfile: https://github.com/fagiani/apt-buildpack/blob/main/bin/detect

#!/usr/bin/env bash
# bin/detect <build-dir>

if [[ -f Aptfile ]]; then
  echo "Apt" && exit 0
else
  echo "no" && exit 1
fi

Behaviour:

[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     ======== Output: fagiani/apt@0.2.4 ========
[INFO]     [creator]     no
[INFO]     [creator]     err:  fagiani/apt@0.2.4 (1)
[INFO]     [creator]     ERROR: No buildpack groups passed detection.
[INFO]     [creator]     ERROR: failed to detect: buildpack(s) failed with err

In what location, I need to put the Aptfile file?

I am testing this idea in the following repo: https://github.com/jabrena/101-docker/tree/feature/bp-musl/poliglot_docker_builds/3_java_spring_boot

Juan Antonio