Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 442
iamdeit Avatar asked Dec 04 '16 11:12

iamdeit


2 Answers

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.

like image 157
Guido Avatar answered Oct 10 '22 11:10

Guido


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.

like image 29
Yuva Avatar answered Oct 10 '22 11:10

Yuva