'How to reduce my java/gradle docker image size?
I have a Docker file like the following:
FROM openjdk:8
ADD . /usr/share/app-name-tmp
WORKDIR /usr/share/app-name-tmp
RUN ./gradlew build \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar
WORKDIR /usr/share/app-name
RUN rm -rf /usr/share/app-name-tmp
EXPOSE 8080
RUN chmod +x ./docker-entry.sh
ENTRYPOINT [ "./docker-entry.sh" ]
The problem is that the final image size is 1.1GB, I know it happens because gradle downloads and stores all the dependencies. What is the best way to remove those unnecessary files and just keep the jar?
Solution 1:[1]
Each RUN instruction creates a new layer on top of the existing file system. So the new layer after RUN instruction that deletes you app-name-tmp
directory just masks the previous layer containing the downloaded libraries. Hence your docker image still has that size from all the layers built.
Remove the separate RUN rm -rf /usr/share/app-name-tmp
instruction and include it in the same RUN instruction that does gradle build as shown below.
RUN ./gradlew build \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar \
rm -rf /usr/share/app-name-tmp/*
So, your final Dockerfile would be
FROM openjdk:8
ADD . /usr/share/app-name-tmp
WORKDIR /usr/share/app-name-tmp
RUN ./gradlew build \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar \
rm -rf /usr/share/app-name-tmp/*
WORKDIR /usr/share/app-name
EXPOSE 8080
RUN chmod +x ./docker-entry.sh
ENTRYPOINT [ "./docker-entry.sh" ]
The image built will still add up size from the directory /usr/share/app-name-tmp.
Solution 2:[2]
I am really confused about your image size. I have typical Spring Boot applications offering a REST service including an embedded servlet container in less than 200MB! It looks like your project dependencies can and should be optimised.
Docker Image
The openjdk:8
(243MB compressed) can be replaced by one with a reduced Alpine unix image like openjdk:8-jdk-alpine
(52MB) as a base image but if you don't need compiler capabilities (e.g. don't use JSPs) you may also go for openjdk:8-jre-alpine
(42MB) which includes the runtime only, have a look into Docker Hub. I use that for Spring Boot based REST services working great.
Java Dependencies
The Java dependencies needed for compile and runtime have to be included but you may have unused dependencies included:
- check your dependencies, are the current compile/runtime dependencies really used or maybe can be removed or moved to test, see Gradle Java Plugin
- some dependencies have a lot of transitive dependencies (display using
gradle dependencies
), check out for unnecessary ones and exclude them if unused, see Gradle Dependency Management. Be sure to do integration tests before applying finally, some transitive dependencies are not well documented but may be essential!
Solution 3:[3]
With Docker 17.05+ you can use multi-stage builds.
"With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image."
So your Dockerfile could look like this:
#
# first stage (build)
#
FROM openjdk:8 as build
ADD . /usr/share/app-name-tmp
WORKDIR /usr/share/app-name-tmp
RUN ./gradlew build && \
mv ./build/libs/app-name*.jar /usr/share/app-name/app-name.jar
#
# second stage. use alpine to reduce the image size
#
FROM openjdk:8-jre-alpine
WORKDIR /usr/share/app-name
COPY --from=build /usr/share/app-name/app-name.jar .
EXPOSE 8080
RUN chmod +x ./docker-entry.sh
ENTRYPOINT [ "./docker-entry.sh" ]
This way you only keep the jar and all the unnecessary files are not included in the final image.
Solution 4:[4]
It seems your image comes from
FROM openjdk:8
so from
and in fact a Debian
FROM buildpack-deps:jessie-scm
you should try to use an Alpine base
I guess your image will be at least half the size
Solution 5:[5]
Is this the container you deploy to production? If so, don't use it for the actual build. Do the build (and the testing) elsewhere and once it is blessed, copy just the JAR to your Docker production container.
Solution 6:[6]
For OpenJDK-12
My application is written in Kotlin along with spring boot and maven.
I had same issue with openJDK-12 and OracleOpenJDK-12 size is 470 MB.
I wanted to reduce my container size so i selected adoptopenjdk/openjdk12:x86_64-alpine-jre-12.33 and achieved 189 MB as shown below.
FROM adoptopenjdk/openjdk12:x86_64-alpine-jre-12.33
RUN mkdir /app
COPY ./target/application-SNAPSHOT.jar /app/application-SNAPSHOT.jar
WORKDIR /app
CMD ["java", "-jar", "application-SNAPSHOT.jar"]
My final container size is 189MB (34 MB Application Jar size + 155 MB Base image size.)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Yuva |
Solution 2 | |
Solution 3 | |
Solution 4 | user2915097 |
Solution 5 | Jamie Bisotti |
Solution 6 | Cowboy_Patrick |