If a question you have is not answered below, please submit an issue.
☑️ Jib User Survey |
---|
What do you like best about Jib? What needs to be improved? Please tell us by taking a one-minute survey. Your responses will help us understand Jib usage and allow us to serve our customers (you!) better. |
But, I'm not a Java developer.
How do I run the image I built?
Where is bash?
What image format does Jib use?
Why is my image created 48+ years ago?
Where is the application in the container filesystem?
How are Jib applications layered?
Can I learn more about container images?
How-Tos
How do I set parameters for my image at runtime?
Can I define a custom entrypoint?
I want to containerize a JAR.
I need to RUN commands like apt-get
.
Can I ADD a custom directory to the image?
I need to add files generated during the build process to a custom directory on the image.
Can I build to a local Docker daemon?
How do I enable debugging?
What would a Dockerfile for a Jib-built image look like?
How can I inspect the image Jib built?
I would like to run my application with a javaagent.
How can I tag my image with a timestamp?
How do I specify a platform in the manifest list (or OCI index) of a base image?
Build Problems
How can I diagnose problems pulling or pushing from remote registries?
What should I do when the registry responds with Forbidden or DENIED?
What should I do when the registry responds with UNAUTHORIZED?
How do I configure a proxy?
How can I examine network traffic?
How do I view debug logs for Jib?
Launch Problems
I am seeing ImagePullBackoff
on my pods.
I am seeing Method Not Found
or Class Not Found
errors when building.
Why won't my container start?
See rules_docker for a similar existing container image build tool for the Bazel build system. The tool can build images for languages such as Python, NodeJS, Java, Scala, Groovy, C, Go, Rust, and D.
If you built your image directly to the Docker daemon using jib:dockerBuild
(Maven) or jibDockerBuild
(Gradle), you simply need to use docker run <image name>
.
If you built your image to a registry using jib:build
(Maven) or jib
(Gradle), you will need to pull the image using docker pull <image name>
before using docker run
.
To run your image on Kubernetes, you can use kubectl:
kubectl run jib-deployment --image=<image name>
For more information, see steps 4-6 of the Kubernetes Engine deployment tutorial.
By default, Jib uses distroless/java
as the base image. Distroless images contain only runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. Check out the distroless project for more information about distroless images.
If you would like to include a shell for debugging, set the base image to gcr.io/distroless/java:debug
instead. The shell will be located at /busybox/sh
. Note that :debug
images are not recommended for production use.
Configuring a base image in Maven (click to expand)
In jib-maven-plugin
, you can use the gcr.io/distroless/java:debug
base image by adding the following configuration:
<configuration>
<from>
<image>gcr.io/distroless/java:debug</image>
</from>
</configuration>
Configuring a base image in Gradle (click to expand)
In jib-gradle-plugin
, you can use the gcr.io/distroless/java:debug
base image by adding the following configuration:
jib.from.image = 'gcr.io/distroless/java:debug'
You can then run the image in shell form with Docker: docker run -it --entrypoint /busybox/sh <image name>
Jib currently builds into the Docker V2.2 image format or OCI image format.
See Extended Usage for the <container><format>
configuration.
See Extended Usage for the container.format
configuration.
For reproducibility purposes, Jib sets the creation time of the container images to the Unix epoch (00:00:00, January 1st, 1970 in UTC). If you would like to use a different timestamp, set the jib.container.creationTime
/ <container><creationTime>
parameter to an ISO 8601 date-time. You may also use the value USE_CURRENT_TIMESTAMP
to set the creation time to the actual build time, but this sacrifices reproducibility since the timestamp will change with every build.
Setting creationTime
parameter (click to expand)
<configuration>
<container>
<creationTime>2019-07-15T10:15:30+09:00</creationTime>
</container>
</configuration>
jib.container.creationTime = '2019-07-15T10:15:30+09:00'
Note that the modification time of the files in the built image put by Jib will still be 1 second past the epoch. The file modification time can be configured using <container><filesModificationTime>
(Maven) or jib.container.filesModificationTime
(Gradle).
Reproducible means that given the same inputs, a build should produce the same outputs. Container images are uniquely identified by a digest (or a hash) of the image contents and image metadata. Tools and infrastructure such the Docker daemon, Docker Hub, registries, Kubernetes, etc) treat images with different digests as being different.
To ensure that a Jib build is reproducible — that the rebuilt container image has the same digest — Jib adds files and directories in a consistent order, and sets consistent creation- and modification-times and permissions for all files and directories. Jib also ensures that the image metadata is recorded in a consistent order, and that the container image has a consistent creation time. To ensure consistent times, files and directories are recorded as having a creation and modification time of 1 second past the Unix Epoch (1970-01-01 00:00:01.000 UTC), and the container image is recorded as being created on the Unix Epoch. Setting container.creationTime
to USE_CURRENT_TIMESTAMP
and then rebuilding an image will produce a different timestamp for the image creation time, and so the container images will have different digests and appear to be different.
For more details see reproducible-builds.org.
Jib packages your Java application into the following paths on the image:
/app/libs/
contains all the dependency artifacts/app/resources/
contains all the resource files/app/classes/
contains all the classes files- the contents of the extra directory (default
src/main/jib
) are placed relative to the container's root directory (/
)
Jib makes use of layering to allow for fast rebuilds - it will only rebuild the layers containing files that changed since the previous build and will reuse cached layers containing files that didn't change. Jib organizes files in a way that groups frequently changing files separately from large, rarely changing files. For example, SNAPSHOT
dependencies are placed in a separate layer from other dependencies, so that a frequently changing SNAPSHOT
will not force the entire dependency layer to rebuild itself.
Jib applications are split into the following layers:
- Classes
- Resources
- Project dependencies
- Snapshot dependencies
- All other dependencies
- Each extra directory (
jib.extraDirectories
in Gradle,<extraDirectories>
in Maven) builds to its own layer
If you'd like to learn more about container images, @coollog has a guide: Build Containers the Hard Way, which takes a deep dive into everything involved in getting your code into a container and onto a container registry.
For the default distroless/java
base image, you can use the JAVA_TOOL_OPTIONS
environment variable (note that other JRE images may require using other environment variables):
Using Docker: docker run -e "JAVA_TOOL_OPTIONS=<JVM flags>" <image name>
Using Kubernetes:
apiVersion: v1
kind: Pod
spec:
containers:
- name: <name>
image: <image name>
env:
- name: JAVA_TOOL_OPTIONS
value: <JVM flags>
Using Docker: docker run -e "NAME=VALUE" <image name>
Using Kubernetes:
apiVersion: v1
kind: Pod
spec:
containers:
- name: <name>
image: <image name>
env:
- name: NAME
value: VALUE
Using Docker: docker run <image name> <arg1> <arg2> <arg3>
Using Kubernetes:
apiVersion: v1
kind: Pod
spec:
containers:
- name: <name>
image: <image name>
args:
- <arg1>
- <arg2>
- <arg3>
For more information, see the JAVA_TOOL_OPTIONS
environment variable, the docker run -e
reference, and defining environment variables for a container in Kubernetes.
Normally, the plugin sets a default entrypoint for java applications, or lets you configure a custom entrypoint using the container.entrypoint
configuration parameter. You can also override the default/configured entrypoint by defining a custom entrypoint when running the container. See docker run --entrypoint
reference for running the image with Docker and overriding the entrypoint command, or see Define a Command and Arguments for a Container for running the image in a Kubernetes Pod and overriding the entrypoint command.
The intention of Jib is to add individual class files, resources, and dependency JARs into the container instead of putting a JAR. This lets Jib choose an opinionated, optimal layout for the application on the container image, which also allows it to skip the extra JAR-packaging step.
However, you can set <containerizingMode>packaged
(Maven) or jib.containerizingMode = 'packaged'
(Gradle) to containerize a JAR, but note that your application will always be run via java -cp ... your.MainClass
(even if it is an executable JAR). Some disadvantages of setting containerizingMode='packaged'
:
- You need to run the JAR-packaging step (
mvn package
in Maven or thejar
task in Gradle). - Reduced granularity in building and caching: if any of your Java source files or resource files are updated, not only the JAR has to be rebuilt, but the entire layer containing the JAR in the image has to be recreated and pushed to the destination.
- If it is a fat or shaded JAR embedding all dependency JARs, you are duplicating the dependency JARs in the image. Worse, it results in far more reduced granularity in building and caching, as dependency JARs can be huge and all of them need to be pushed repeatedly even if they do not change.
Note that for runnable JARs/WARs, currently Jib does not natively support creating an image that runs a JAR (or WAR) through java -jar runnable.jar
(although it is not impossible to configure Jib to do so at the expense of more complex project setup.)
Running commands like apt-get
slows down the container build process. We do not recommend or support running commands as part of the build.
However, if you need to run commands, you can build a custom image and configure Jib to use it as the base image.
Base image configuration examples (click to expand)
In jib-maven-plugin
, you can then use this custom base image by adding the following configuration:
<configuration>
<from>
<image>custom-base-image</image>
</from>
</configuration>
In jib-gradle-plugin
, you can then use this custom base image by adding the following configuration:
jib.from.image = 'custom-base-image'
Yes, using the extra directories feature. See the Maven and Gradle docs for examples.
If the current extra directories design doesn't meet your needs (e.g. you need to set up the extra files directory with files generated during the build process), you can use additional goals/tasks to create the extra directory as part of your build.
File copying examples (click to expand)
In Maven, you can use the maven-resources-plugin
to copy files to your extra directory. For example, if you generate files in target/generated/files
and want to add them to /my/files
on the container, you can add the following to your pom.xml
:
<plugins>
...
<plugin>
<artifact>jib-maven-plugin</artifact>
...
<configuration>
<extraDirectories>
<paths>
<path>${project.basedir}/target/extra-directory/</path>
</paths>
</extraDirectories>
</configuration>
</plugin>
...
<plugin>
<artifact>maven-resources-plugin</artifact>
<version>3.1.0</version>
<configuration>
<outputDirectory>${project.basedir}/target/extra-directory/my/files</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/target/generated/files</directory>
</resource>
</resources>
</configuration>
</plugin>
...
</plugins>
The copy-resources
goal will run automatically before compile, so if you are copying files from your build output to the extra directory, you will need to either set the life-cycle phase to post-compile
or later, or run the goal manually:
mvn compile resources:copy-resources jib:build
The same can be accomplished in Gradle by using a Copy
task. In your build.gradle
:
jib.extraDirectories = file('build/extra-directory')
task setupExtraDir(type: Copy) {
from file('build/generated/files')
into file('build/extra-directory/my/files')
}
tasks.jib.dependsOn setupExtraDir
The files will be copied to your extra directory when you run the jib
task.
There are several ways of doing this:
- Use
jib:dockerBuild
for Maven orjibDockerBuild
for Gradle to build directly to your local Docker daemon. - Use
jib:buildTar
for Maven orjibBuildTar
for Gradle to build the image to a tarball, then usedocker load --input
to load the image into Docker (the tarball built with these commands will be located intarget/jib-image.tar
for Maven andbuild/jib-image.tar
for Gradle by default). docker pull
the image built with Jib to have it available in your local Docker daemon.- Alternatively, instead of using a Docker daemon, you can run a local container registry, such as Docker registry or other repository managers, and point Jib to push to the local registry.
If using the distroless/java
base image, then use the JAVA_TOOL_OPTIONS
to pass along debugging configuration arguments. For example, to have the remote VM accept local debug connections on port 5005, but not suspend:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:5005
Then connect your debugger to port 5005 on your local host. You can port-forward the container port to a localhost port for easy access.
Using Docker: docker run -p 5005:5005 <image>
Using Kubernetes: kubectl port-forward <pod name> 5005:5005
Beware: in Java 8 and earlier, specifying only a port meant that the JDWP socket was open to all incoming connections which is insecure. It is recommended to limit the debug port to localhost.
You can run your container with a javaagent by placing it somewhere in the src/main/jib
directory to add it to the container's filesystem, then pointing to it using Jib's container.jvmFlags
configuration.
<configuration>
<container>
<jvmFlags>
<jvmFlag>-javaagent:/myfolder/agent.jar</jvmFlag>
</jvmFlags>
</container>
</configuration>
jib.container.jvmFlags = ['-javaagent:/myfolder/agent.jar']
See also Can I ADD a custom directory to the image?
To tag the image with a simple timestamp, add the following to your pom.xml
:
<properties>
<maven.build.timestamp.format>yyyyMMdd-HHmmssSSS</maven.build.timestamp.format>
</properties>
Then in the jib-maven-plugin
configuration, set the tag
to:
<configuration>
<to>
<image>my-image-name:${maven.build.timestamp}</image>
</to>
</configuration>
You can then use the same timestamp to reference the image in other plugins.
To tag the image with a timestamp, simply set the timestamp as the tag for to.image
in your jib
configuration. For example:
jib.to.image = 'gcr.io/my-gcp-project/my-app:' + System.nanoTime()
A Dockerfile that performs a Jib-like build is shown below:
# Jib uses distroless java as the default base image
FROM gcr.io/distroless/java:latest
# Multiple copy statements are used to break the app into layers, allowing for faster rebuilds after small changes
COPY dependencyJars /app/libs
COPY snapshotDependencyJars /app/libs
COPY projectDependencyJars /app/libs
COPY resources /app/resources
COPY classFiles /app/classes
# Jib's extra directory ("src/main/jib" by default) is used to add extra, non-classpath files
COPY src/main/jib /
# Jib's default entrypoint when container.entrypoint is not set
ENTRYPOINT ["java", jib.container.jvmFlags, "-cp", "/app/resources:/app/classes:/app/libs/*", jib.container.mainClass]
CMD [jib.container.args]
When unset, Jib will infer the value for jib.container.mainClass
.
Some plugins, such as the Docker Prepare Gradle Plugin, will even automatically generate a Docker context for your project, including a Dockerfile.
To inspect the image that is produced from the build using Docker, you can use commands such as docker inspect your/image:tag
to view the image configuration, or you can also download the image using docker save
to manually inspect the container image. Other tools, such as dive, provide nicer UI to inspect the image.
Newer Jib verisons added an incubating feature that provides support for selecting base images with the desired platforms from a manifest list. For example,
<from>
<image>... image reference to a manifest list ...</image>
<platforms>
<platform>
<architecture>amd64</architecture>
<os>linux</os>
</platform>
<platform>
<architecture>arm64</architecture>
<os>linux</os>
</platform>
</platforms>
</from>
jib.from {
image = '... image reference to a manifest list ...'
platforms {
platform {
architecture = 'amd64'
os = 'linux'
}
platform {
architecture = 'arm64'
os = 'linux'
}
}
}
The default when not specified is a single "amd64/linux" platform, whose behavior is backward-compatible.
When multiple platforms are specified, Jib creates and pushes a manifest list (also known as a fat manifest) after building and pushing all the images for the specified platforms.
As an incubating feature, there are certain limitations:
- OCI image indices are not supported (as opposed to Docker manifest lists).
- Only
architecture
andos
are supported. If the base image manifest list contains multiple images with the given architecture and os, the first image will be selected. - Does not support using a local Docker daemon or tarball image for a base image.
- Does not support pushing to a Docker daemon (
jib:dockerBuild
/jibDockerBuild
) or building a local tarball (jib:buildTar
/jibBuildTar
).
Make sure to specify a manifest list in <from><image>
(whether by a tag name or a digest (@sha256:...
)). For troubleshooting, you may want to check what platforms the manifest list contains. To view a manifest list, enable experimental docker CLI features and then run the manifest inspect command.
$ docker manifest inspect openjdk:8
{
...
// This confirms that openjdk:8 points to a manifest list.
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
// This entry in the list points to the manifest for the ARM64/Linux manifest.
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
...
"digest": "sha256:1fbd49e3fc5e53154fa93cad15f211112d899a6b0c5dc1e8661d6eb6c18b30a6",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
}
}
]
}
There are a few reasons why Jib may be unable to connect to a remote registry, including:
- Registry reports FORBIDDEN. See What should I do when the registry responds with Forbidden or DENIED?
- Registry reports UNAUTHORIZED. See What should I do when the registry responds with UNAUTHORIZED?
- Access requires a proxy. See How do I configure a proxy? for details.
- The registry does not support HTTPS. We do not pass authentication details on non-HTTPS connections, though this can be overridden with the
sendCredentialsOverHttp
system property, but it is not recommend (version 0.9.9). - The registry's SSL certificates have expired or are not trusted. We have a separate document on handling registries that use self-signed certificates, which may also apply if the SSL certificate is signed by an untrusted Certificate Authority. Jib supports an
allowInsecureRegistries
flag to ignore SSL certificate validation, but it is not recommend (version 0.9.9). - The registry does not support the Docker Image Format V2 Schema 2 (sometimes referred to as v2-2). This problem is usually shown by failures wth
INVALID_MANIFEST
errors. Some registries can be configured to support V2-2 such as Artifactory and OpenShift. Other registries, such as Quay.io/Quay Enterprise, are in the process of adding support.
If the registry returns 403 Forbidden
or "code":"DENIED"
, it often means Jib successfully authenticated using your credentials but the credentials do not have permissions to pull or push images. Make sure your account/role has the permissions to do the operation.
Depending on registry implementations, it is also possible that the registry actually meant you are not authenticated. See What should I do when the registry responds with UNAUTHORIZED? to ensure you have set up credentials correctly.
If the registry returns 401 Unauthorized
or "code":"UNAUTHORIZED"
, it is often due to credential misconfiguration. Examples:
- You did not configure auth information in the default places where Jib searches.
$HOME/.docker/config.json
, one of the configuration files for thedocker
command line tool. See configuration files document, credential store and credential helper sections, and this for how to configure auth. For example, you can dodocker login
to save auth inconfig.json
, but it is often recommended to configure a credential helper (also configurable inconfig.json
).- (Starting from Jib 2.2.0) You can set the environment variable
$DOCKER_CONFIG
for the directory containing Docker configuration files.$DOCKER_CONFIG/config.json
takes precedence over$HOME/.docker/config.json
. - Some common credential helpers on
$PATH
(for example,docker-credential-osxkeychain
,docker-credential-ecr-login
, etc.) for well-known registries. - Jib configurations
- Configuring credential helpers:
<from/to><credHelper>
(Maven) /from/to.credHelper
(Gradle) - Specific credentials (not recommend):
<from/to><auth><username>/<password>
or insettings.xml
(Maven) /from/to.auth.username/password
(Gradle) - These parameters can also be set through properties: Maven / Gradle
- Configuring credential helpers:
$HOME/.docker/config.json
may also contain short-lived authorizations in theauths
block that may have expired. In the case of Google Container Registry, if you had previously usedgcloud docker
to configure these authorizations, you should remove these stale authorizations by editing yourconfig.json
and deleting lines fromauths
associated withgcr.io
(for example:"https://asia.gcr.io"
). You can then rungcloud auth configure-docker
to correctly configure thecredHelpers
block for more robust interactions with gcr.- Different auth configurations exist in multiple places, and Jib is not picking up the auth information you are working on.
- You configured a credential helper, but the helper is not on
$PATH
. This is especially common when running Jib inside IDE where the IDE binary is launched directly from an OS menu and does not have access to your shell's environment. - Configured credentials have access to the base image repository but not to the target image repository (or vice versa).
- Typos in username, password, image names, repository names, or registry names. This is a very common error.
- Image names do not conform to the structure or policy that a registry requires. For example, Docker Hub returns 401 Unauthorized when trying to use a multi-level repository name.
- Incorrect port number in image references (
registry.hostname:<port>/...
). - You are using a private registry without HTTPS. See How can I diagnose problems pulling or pushing from remote registries?.
Note, if Jib was able to retrieve credentials, you should see a log message like these:
Using credentials from Docker config (/home/user/.docker/config.json) for localhost:5000/java
Using credential helper docker-credential-gcr for gcr.io/project/repo
Using credentials from Maven settings file for gcr.io/project/repo
Using credentials from <from><auth> for gcr.io/project/repo
Using credentials from to.auth for gcr.io/project/repo
If you encounter issues interacting with a registry other than UNAUTHORIZED
, check "How can I diagnose problems pulling or pushing from remote registries?".
Jib currently requires configuring your build tool to use the appropriate Java networking properties (https.proxyHost
, https.proxyPort
, https.proxyUser
, https.proxyPassword
).
It can be useful to examine network traffic to diagnose connectivity issues. Jib uses the Google HTTP client library to interact with registries which logs HTTP requests using the JVM-provided java.util.logging
facilities. It is very helpful to serialize Jib's actions using the jib.serialize
property.
To see the HTTP traffic, create a logging.properties
file with the following:
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=ALL
# CONFIG hides authentication data
# ALL includes authentication data
com.google.api.client.http.level=CONFIG
And then launch your build tool as follows:
mvn --batch-mode -Djava.util.logging.config.file=path/to/logging.properties -Djib.serialize=true ...
or
gradle --no-daemon --console=plain -Djava.util.logging.config.file=path/to/logging.properties -Djib.serialize=true ...
Note: Jib Gradle plugins prior to version 2.2.0 have an issue generating HTTP logs (#2356).
You may wish to enable the debug logs too (-X
for Maven, or --debug --stacktrace
for Gradle).
When configured correctly, you should see logs like this:
Mar 31, 2020 9:55:52 AM com.google.api.client.http.HttpResponse <init>
CONFIG: -------------- RESPONSE --------------
HTTP/1.1 202 Accepted
Content-Length: 0
Docker-Distribution-Api-Version: registry/2.0
Docker-Upload-Uuid: 6292f0d7-93cb-4a8e-8336-78a1bf7febd2
Location: https://registry-1.docker.io/v2/...
Range: 0-657292
Date: Tue, 31 Mar 2020 13:55:52 GMT
Strict-Transport-Security: max-age=31536000
Mar 31, 2020 9:55:52 AM com.google.api.client.http.HttpRequest execute
CONFIG: -------------- REQUEST --------------
PUT https://registry-1.docker.io/v2/...
Accept:
Accept-Encoding: gzip
Authorization: <Not Logged>
User-Agent: jib 2.1.1-SNAPSHOT jib-maven-plugin Google-HTTP-Java-Client/1.34.0 (gzip)
Maven: use mvn -X -Djib.serialize=true
to enable more detailed logging and serialize Jib's actions.
Gradle: use gradle --debug -Djib.serialize=true
to enable more detailed logging and serialize Jib's actions.
I am seeing ImagePullBackoff
on my pods (in minikube).
When you use your private image built with Jib in a Kubernetes cluster, the cluster needs to be configured with credentials to pull the image. This involves 1) creating a Secret, and 2) using the Secret as imagePullSecrets
.
kubectl create secret docker-registry registry-json-key \
--docker-server=<registry> \
--docker-username=<username> \
--docker-password=<password> \
--docker-email=<any valid email address>
kubectl patch serviceaccount default \
-p '{"imagePullSecrets":[{"name":"registry-json-key"}]}'
For example, if you are using GCR, the commands would look like (see Advanced Authentication Methods):
kubectl create secret docker-registry gcr-json-key \
--docker-server=https://gcr.io \
--docker-username=_json_key \
--docker-password="$(cat keyfile.json)" \
[email protected]
kubectl patch serviceaccount default \
-p '{"imagePullSecrets":[{"name":"gcr-json-key"}]}'
See more at Using Google Container Registry (GCR) with Minikube.
Sometimes when upgrading your gradle build plugin versions, you may experience errors due to mismatching versions of dependencies pulled in (for example: issues/2183). This can be due to the buildscript classpath loading behavior described on gradle forums.
This commonly appears in multi module gradle projects. A solution to this problem is to define all of your plugins in the base project and apply them selectively in your subprojects as needed. This should help alleviate the problem of the buildscript classpath using older versions of a library.
build.gradle
(root)
plugins {
id 'com.google.cloud.tools.jib' version 'x.y.z' apply false
}
build.gradle
(sub-project)
plugins {
id 'com.google.cloud.tools.jib'
}
There are some common reasons why containers fail on launch.
The default base image used by Jib, (distoless/java
), does not include a shell, and thus shell scripts won't launch.
Solution: use a different base image with a shell.
A Jib user reported an error launching their container:
standard_init_linux.go:211 exec user process caused "no such file or directory"
On examining the container structure with Dive, the user discovered that the contents of the /lib
directory had disappeared.
The user had used Jib's ability to install extra files into the image (Maven, Gradle) to install a library file by placing it in src/main/jib/lib/libfoo.so
. This would normally cause the libfoo.so
to be installed in the image as /lib/libfoo.so
. But /lib
and /lib64
in the user's base image were symbolic links. Jib does not follow such symbolic links when creating the image. And at container initialization time, Docker treats these symlinks as a file, and thus the symbolic link was replaced with /lib
as a new directory. As a result, none of the system shared libraries were resolved and dynamically-linked programs failed.
Solution: The user installed the file in a different location.