-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Native Image Build Bundles #5473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This document started out on #5460 and is now further maintained as roadmap item here. |
TODO: Add more info about |
TODO: @jerboaa mentioned (see #5460 (comment)) that |
TODO: Add info about |
Just a thought. Instead of introducing
I find it kind of natural since the replay bundle is also a regular jar file. |
Since it turns out there might be more use-cases for these bundles than just replay we renamed them to |
@olpaw i encountered this issue searching for re-usable build outputs from for instance, could I ship an artifact with a library I make, which reduces the compile time impact on downstream apps that compile with GraalVM? if that doesn't exist today, would this feature help get closer to a reality where it does? |
@sgammon, the only way to speed up image building we currently have is to use the quick build mode for development ( The focus of this feature is not to reduce the build time of an image upon rebuilding. Build bundles instead make it possible to rebuild an image later without keeping all the files around that were involved in building that image. That includes all the jar-files and directories that were passed via class-path and/or module-path, configuration files passed via builder options, used |
@olpaw totally get the idea, sorry, let me clarify. if we had such bundles, wouldn't we be closer to being able to discern whether a native dependency is or is not compatible? and wouldn't we have a starting point for a format to pre-package native dependencies? and if such a format existed, and the compiler could load and use it, couldn't it be a way to reduce build times, later? i understand this issue itself is unrelated, but i'm also wondering if this line of work gets GraalVM closer to that eventuality. thank you, by the way, for building quickbuild mode, and i'm not even upset at GraalVM build times, but it would be very interesting if GraalVM-compatible artifacts could avoid rebuilding native sections of code. such artifacts already prepare to ship reflection metadata, for example, and if library authors are waiting for a native build to complete, it could, in theory, be reused later (modulo architecture and OS differences, obviously). |
Bundles are on master and are part of 23.0. The bundle documentation is part of official documentation. |
Draft: Native Image Build Bundles
Motivation
The deployment of a native image is just one step in the lifecycle of an application or service. Real world
applications run for years and sometimes need to be updated or patched long after deployment (security fixes). It would be great if there would be an easy way to redo an image build at some point in the future as accurately as possible.
Another angle is provided from the development phase. If image building fails or a malfunctioning image is created (i.e. the same application runs fine when executed via JVM) we would like to get bug reports that allow us to reproduce the problem locally without hours of replicating their setup. We would want some way to bundle up what the user built (or tried to build) into a nice package that allows us to instantly reproduce the problem on our side.
Debugging an image created long time ago is also sometimes needed. It would be great if there is a single bundle that contains everything needed to perform this task.
Build Bundles
A set of options should be added to the
native-image
command that allows to create so-called "build bundles" that canbe used to help with problems described above. There shall be
This will instruct native-image to create build bundle
mybundle.nib
alongside the image.For example, after the running:
native-image --bundle-create=alaunch.nib -Dlaunchermode=2 -EBUILD_ENVVAR=env42 \ -p somewhere/on/my/drive/app.launcher.jar:/mnt/nfs/server0/mycorp.base.jar \ -cp $HOME/ourclasses:somewhere/logging.jar:/tmp/other.jar:aux.jar \ -m app.launcher/paw.AppLauncher alaunch
the user sees the following build results:
~/foo$ ls alaunch.output alaunch.nib somewhere aux.jar
As we can see, in addition to the image a
alaunch.nib
-file and thealaunch.output
directory were created. This is the native image build bundle for the image that got built and a directory for the actual image that got built and any additional files that got created as part of image building. At any time later, if the same version of GraalVM is used, the image can be rebuiltwith:
this will rebuild the
alaunch
image with the same image arguments, environment variables, system propertiessettings, classpath and module-path options as in the initial build.
To support the use-case of image-building-as-a-service, there should also be a way to create a bundle without
performing the initial build locally. This allows users to offload image building to a cloud-service specialized in
image building and retaining of build bundles. The command line for that should be:
Build Bundles File Format
A
<imagename>.nib
file is a regular jar-file that contains all information needed to bundle a previous build.For example, the
alaunch.nib
build bundle has the following inner structure:As we can see, there are several components in a build bundle that we need to describe in more detail.
META-INF
:Since the bundle is also a regular jar-file we have a
META-INF
subdirectory with the familiarMANIFEST.MF
. Thebundle can be used like a regular jar-launcher (by running command
java -jar <imagename>.nib
) so that theapplication we build an image from is instead executed on the JVM. For that purpose the
MANIFEST.MF
specifies thenibundle/Launcher
as main class. Is is particularly useful if you want to run the application on the JVM with the native-image agent to collect configuration data that you then integrate into the bundle as a second step.Here we also find
nibundle.properties
. This file is specific to build bundles. Its existence makes clear that this is noordinary jar-file but a native image build bundle. The file contains version information of the native image build
bundle format itself and also which GraalVM version was used to create the bundle. This can later be used to report a
warning message if a bundle gets built with a GraalVM version different from the one used to create the bundle.
This file also contains information about the platform the bundle was created on (e.g.
linux-amd64
ordarwin-aarch64
).input
:This directory contains the entire amount of information needed to redo the previous image build. The original
class-path and module-path entries are placed into corresponding files (for jar-files) and subdirectories (for
directory-based class/module-path entries) into the
input/classes/cp
(original -cp/--class-path entries) and theinput/classes/p
(original -p/--module-path entries) folders. Theinput/stage
folder contains all informationneeded to replicate the previous build context.
input/stage
:Here we have
build.cmd
that contains all native-image command line options used in the previous build. Note thateven the initial build that created the bundle already uses a class- and/or module-path that refers to the contents
of the
input/classes
folder. This way we can guarantee that a bundle build sees exactly the same relocatedclass/module-path entries as the initial build. The use of
run.cmd
is explained later.File
all.env
contains the environment variables that we allowed the builder to see during the initial build andall.properties
the respective system-properties.input/stage/container
:If the image builder runs in a container environment, this subdirectory holds all information necessary to redo the
image build later in an equivalent container environment. It contains the
Dockerfile
that was used to specify thecontainer image that executed the image builder. Next,
run.cmd
contains all the arguments that were passed todocker/podman. It does not contain the arguments passed to be builder. In
setup.json
we save all information aboutthe container environment that was used (Linux kernel version, CGroup V1 or V2, Docker/podman version). For more info
see below.
output
:This folder contains all the output that was generated by the image build process (if the image was built as part of bundle creation). This contains debuginfo needed in case we need to debug the image at some point in the future.
output/build
:This folder is used to document the build process that lead to the image that was created alongside the bundle.
The
report
sub-folder holdsbuild.log
. It is equivalent to what would have been created if the user had appended|& tee build.log
to the original native-image command line. Additionally, we have several json-files:analysis_results.json
: Contains the results of the static analysis. A rerun should compare the newanalysis_results.json
file with this one and report deviations in a user-friendly way.build_artifacts.json
: Contains a list of the artifacts that got created during the initial build. As before,changes should be reported to the user.
build_output.json
: Similar information asbuild.log
but more structured and detailed.jni_access_details.json
: Overview which methods/classes/fields have been made jni-accessible for image-runtime.reflection_details.json
: Same kind of information for reflection access at image runtime.As already mentioned a rebuild should compare its newly generated set of json-files against the one in the bundle and
report deviations from the original ones in a user-friendly way.
nibundle
:Contains the
Launcher.class
that is used when the bundle is run as a regular java launcher. The class-file is notspecific to a particular bundle. Instead, the Launcher class extracts the contents of the
input
into a temporarysubdirectory in
$TEMP
and uses the files frominput/stage/all.*
andinput/stage/run.cmd
to invoke$JAVA_HOME/bin/java
with the environment-variables and with the arguments (e.g. system-properties) needed to run theapplication on the JVM.
Enforced sanitized image building
Containerized image building on supported platforms
If available, docker/podman should be used to run the image builder inside a well-defined container image. This allows
us to prevent the builder from using the network during image build, thus guaranteeing that the image build result did
not depend on some unknown (and therefore unreproducible) network state. Another advantage is that we can mount
input/classes
and$GRAALVM_HOME
read-only into the container and only allow read-write access to the mountedout
and
build
directories. This will prevent the application code that runs at image build time to mess with anythingother than those directories. All information about containerized building is recorded in bundle subdirectory
input/stage/container
.Fallback for systems without container support
If containerized builder execution is not possible we can still at least have the builder run in a sanitized
environment variable state and make sure that only environment variables are visible that were explicitly
specified with
-E<env_var_name>=<env_var_value>
or-E<env_var_name>
(to allow passing through from thesurrounding environment).
Handling of Image build errors
To ensure build bundles are feasible for the second use case described above we have to make sure a
bundle gets successfully created even if the image build fails. Most likely in this case the
out
folder will bemissing in the bundle. But as usual
build/report/build.log
will contain all the command line output that was shownduring the image build. This also includes any error messages that resulted in the build failure.
The text was updated successfully, but these errors were encountered: