paketo-buildpacks / graalvm

A Cloud Native Buildpack that provides the GraalVM implementations of JREs and JDKs
Apache License 2.0
33 stars 13 forks source link

How to download GraalVM faster? #74

Open catfishlty opened 3 years ago

catfishlty commented 3 years ago

Description

When start build spring-native image by Maven, the process will wait for downloading GrallVM for a very long time. Maybe it's a network problem, but i want to know is there any other ways to speedup this step.

Maven Config

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <builder>paketobuildpacks/builder:tiny</builder>
      <env>
        <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
      </env>
    </image>
  </configuration>
</plugin>

Build Cmd

mvn spring-boot:build-image

Docker Builder Container Logs

===> DETECTING

4 of 11 buildpacks participating

paketo-buildpacks/graalvm 6.0.0

paketo-buildpacks/executable-jar 5.0.0

paketo-buildpacks/spring-boot 4.2.0

paketo-buildpacks/native-image 4.0.0

===> ANALYZING

Previous image with name "docker.io/library/spring-native-build-docker-demo:0.0.1-SNAPSHOT" not found

===> RESTORING

===> BUILDING

Paketo GraalVM Buildpack 6.0.0

https://github.com/paketo-buildpacks/graalvm

Build Configuration:

$BP_JVM_VERSION 8.* the Java version

Launch Configuration:

$BPL_JVM_HEAD_ROOM 0 the headroom in memory calculation

$BPL_JVM_LOADED_CLASS_COUNT 35% of classes the number of loaded classes in memory calculation

$BPL_JVM_THREAD_COUNT 250 the number of threads in memory calculation

$JAVA_TOOL_OPTIONS the JVM launch flags

GraalVM JDK 8.0.282: Contributing to layer

Downloading from https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0.2/graalvm-ce-java8-linux-amd64-21.0.0.2.tar.gz
dmikusa commented 3 years ago

Hi. The file it's downloading here is about 450M. It can certainly take some time depending on your Internet connection speed.

For example, to download the 450M file on a 100Mbit connection, it could take 36s in perfect conditions.

https://www.omnicalculator.com/other/download-time?c=USD&v=fileSize:450!megabyte,downloadSpeed:100!megabit

You can use a calculator like this to determine what it should roughly take to download the file. If it is pausing for about that amount of time, then that is about what you can expect with your Internet speed.

A couple more notes here:

  1. This file is downloaded from Github, and we have received reports of Github downloads being slow in Europe recently. If the file is downloading slower than expected given your Internet speed and the calculator above, then you might be impacted by this.
  2. This should only happen on the first build. Subsequent builds of the same app should have the download cached, which should not download anything.
  3. If you'd like to download the file and host it locally you can do that. You just need to run an HTTP server on your local machine (python3 -m http.server is one very easy way to do this). You can remap the dependency to your local machine (don't use localhost/127.0.0.1, use your actual LAN IP), pack will then download from your local HTTP server which should be very fast. See the docs here for details -> https://paketo.io/docs/buildpacks/configuration/#dependency-mappings

Hope that helps!

catfishlty commented 3 years ago

@dmikusa-pivotal Thanks for you help~ The third method is better than others. But i can't locate buildpack.toml file to change the config. In the build steps, first spring-boot-maven-plugin use paketobuildpacks/builder:tiny build to create a builder image in Docker; second there will be a new container start with the builder image in Docker. And when it started, there were many preparing steps in init process. As you can see, all the steps are automatic, and I still don't know if there's any place to change the config.

dmikusa commented 3 years ago

OK, so the third process can be a little confusing. Let me walk you through it.

  1. We need to locate the existing buildpack.toml. Not to change it, but because we need the download URLs & sha256 hashes. You can find the buildpack.toml on Github for each buildpack. For graalvm it's here (note I'm using the 6.0.0 release because 6.1.0 hasn't been picked up by the builder yet, just match up the version with what you're using).

  2. Make a folder and download the bits there. Use the URLs from the buildpack.toml.

Screen Shot 2021-05-06 at 8 36 29 AM
  1. Run python3 -m http.server from that directory. At this point, you should be able to go to your machine:8000 and download the files.
Screen Shot 2021-05-06 at 8 36 36 AM
  1. Now for the tricky part. We need to create a folder for the binding.
Screen Shot 2021-05-06 at 8 35 31 AM

Then you want to create a file where the name of the file (i.e. the binding key) is the sha256 hash of the dependency you want to change. The contents of the file (i.e. the binding value) should be the new URL to use.

Screen Shot 2021-05-06 at 8 35 58 AM

If you are hosting the files on your local machine, you can use host.docker.internal as the hostname. This points to your local machine (I know it works on MacOS, at least).

Then do the same thing for the substrate VM download (there are two downloads for GraalVM).

Screen Shot 2021-05-06 at 8 36 09 AM
  1. With that done, you want to run pack build and use the --volume option to volume mount the binding we created into the container (the binding name doesn't matter, but it must exist under /platform/bindings). The binding will then be visible to the buildpacks running, which will find your custom URLs and use those to download resources.
Screen Shot 2021-05-06 at 8 35 00 AM

See how in the screenshot the URLs have changed to my custom URLs. You should also see the python3 HTTP server output an access entry when the file is downloaded.

It's a little bit of setup, but you only need to do steps 1-4 when there are new binaries. Otherwise, just make sure the HTTP server is running & that you've mapped in the volume with the bindings.

Hope that helps!

catfishlty commented 3 years ago

@dmikusa-pivotal I really appreciate it. That help me a lot. But i still have two question about the details.

1. sha256 value wrote in binding folder

In my understanding, i thought sha256 in buildpack.toml is equals to url folder name in binging folder. Ex.

echo 'http://host.docker.internal/graalvm-ce-java8-linux-amd64-21.0.0.2.tar.gz' > a10a7632bf0afdb162ed1419311540af93c95ad659eb391a709743bb6e457d5c
[[metadata.dependencies]]
id      = "jdk"
name    = "GraalVM JDK"
version = "8.0.282"
uri     = "https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0.2/graalvm-ce-java8-linux-amd64-21.0.0.2.tar.gz"
sha256  = "a10a7632bf0afdb162ed1419311540af93c95ad659eb391a709743bb6e457d5c"
stacks  = [ "io.buildpacks.stacks.bionic", "io.paketo.stacks.tiny", "org.cloudfoundry.stacks.cflinuxfs3" ]

These two sha256 are same. Am i right?

2. --volume option in pom file

In step 5, i know that using --volume option is to replace the url for GraalVM. But in maven pom file, how can i add --volume option to let maven plugin control the pack process.

pom.xml in my project, just follow Spring Native Doc

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <builder>paketobuildpacks/builder:tiny</builder>
      <env>
        <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
      </env>
    </image>
  </configuration>
</plugin>

Others

The whole project is running on Windows10 with Docker Desktop for Win10 with WSL2. The build target is a executable in linux docker image, so command line is not for me because of the different OS. So i have to use Maven command to build the target image.

dmikusa commented 3 years ago

1.) Correct. The sha256 should match the dependency you want to replace in buildpack.toml.

2.) At the moment, you need to be using Spring Boot 2.5RC to do this. It is functionality added to the 2.5 branch. 2.4 does not have that capability. See https://docs.spring.io/spring-boot/docs/2.5.0-RC1/maven-plugin/reference/htmlsingle/#build-image-customization. If you're using 2.4, you need to use pack. Sorry.

Other.) You can use pack build on Windows. I don't know about using it under WLS2, but I suppose it should work. It's just a go static binary. I'd probably follow the Linux install instructions if you're working with WSL2. See https://buildpacks.io/docs/tools/pack/ for install instructions for both Windows & Linux.

Hope that helps!

catfishlty commented 3 years ago

Thanks for your kind help~ I'll try it.

dmikusa commented 3 years ago

I think there are still issues here, and we're hoping to have further improvements, but we did add a new feature to the buildpacks that allows you to use dependency-mapping and point to file:// URLs (instead of HTTP(S) only).

This allows you to map a volume into your containers with the pack build --volume flag that contains your dependencies & then reference those dependencies in a dependency-mapping binding using file:///path/where/volume/is/mounted....

It's similar to hosting mapped dependencies on a local HTTP server, as is mentioned above, but even easier because it doesn't require the local HTTP server anymore.

catfishlty commented 3 years ago

Using Local File Path will be more user-friendly. And i'll try it later.

But I think the most easy way is that add the file path or url in .pom file in build config. Is it possible to be like this? For example:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <builder>paketobuildpacks/builder:tiny</builder>
      <env>
        <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
        <!-- Using Http Url -->
        <JDK_URI>https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0.2/graalvm-ce-java8-linux-amd64-21.0.0.2.tar.gz</JDK_URI>
        <!-- File Path on Linux -->
        <JDK_URI>/root/jdk/graalvm-ce-java8-linux-amd64-21.0.0.2.tar.gz</JDK_URI>
        <!-- File Path on Windows -->
        <JDK_URI>C:\\Users\\Administrator\\Downloads\\graalvm-ce-java8-linux-amd64-21.0.0.2.tar.gz</JDK_URI>
      </env>
    </image>
  </configuration>
</plugin>
dmikusa commented 3 years ago

It does not presently work that way. There is no link between env variables and what is downloaded. You need to use the dependency mapping binding. That said, you can add bindings into your Spring Boot 2.5+ maven tools configuration, so you should be able to achieve something very similar.

dmikusa commented 3 years ago

I did this and it amounts to the following:

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>${repackage.classifier}</classifier>
                    <image>
                        <name>apps/your-app</name>
                        <env>
                            <SERVICE_BINDING_ROOT>/bindings</SERVICE_BINDING_ROOT>
                        </env>
                        <bindings>
                            <binding>${basedir}/bindings/dependency-mapping:/bindings/dependency-mapping</binding>
                            <binding>${basedir}/deps:/deps</binding>
                        </bindings>
                    </image>
                </configuration>
            </plugin>

You then need two folders locally:

  1. bindings/dependency-mapping -> populate this with a file named type with the contents dependency-mapping and one of the following for each dependency you wish to override. A file where the name is the sha256 hash of the file and the contents is a path like file:///deps/<file-name>.
  2. deps -> download and add dependencies here.

Screen Shot 2021-08-30 at 12 59 53 PM

This works by adding the dependency mapping & pointing to a location on disk that has been volume mounted with your local deps/ directory.

You can fetch both the sha256 hash & the download URL from buildpack.toml for the buildpack.

catfishlty commented 3 years ago

Oh...Got it.

spring-boot-maven-plugin only define the paketobuildpacks/builder:tiny env values. And the mapping tag define the specific local path and hash to override the default value, so it will work by our custom configuration.

ZiboCui commented 2 years ago

I also encountered the same problem, I configured bindings but it didn't work. image What steps are still missing? tks.

dmikusa commented 2 years ago

@cuiziboczb - Could you include the output of building? It would help to clarify what is happening.

The setup looks OK to me. The only thing I see is that your GraalVM deps are one version ago. We bumped to 21.3.0 recently. If you're using the latest buildpack, it's going to expect 21.3.0 dependencies, so that could explain why it's not using your local deps. If you're using the latest buildpack, you'll need to download the latest set of dependencies, and add binding entries for them.