Reducing Docker Image Size
My colleagues’ feedback about my previous post helped me investigate more efficient ways of writing Dockerfile. The first thing that I realized was choosing the right image base. Alpine based images are recommended for slimmer images and is much smaller than other base images in general.
---Dockerfile 1--- FROM java:8 VOLUME /tmp ADD target/demo-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8090 ENV JAVA_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
The result of $ docker images is shown below. Replacing java:8 base image with openjdk:8-jre-alpine changed the image size dramatically. First of all, notice the size of base images “openjdk” and “java”. Then, look at the sizes of the images I built: (1) Alpine based image (nutrition-backend-alpine) and (2) java:8-base image (nutrition-backend-java8). Alpine based image is 6 times smaller than java:8 based image. This shows the importance of choosing the right base image for your application.
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE nutrition-backend-java8 latest 7696613fbbe1 3 seconds ago 672MB nutrition-backend-alpine latest b239eeb95bf5 50 seconds ago 110MB openjdk 8-jre-alpine c4f9d77cd2a1 6 weeks ago 81.4MB java 8 d23bdf5b1b1b 7 months ago 643MB
I will continue revising Dockerfile for Java applications with this tutorial: Crafting perfect Java Docker build flow. Before finishing this post, I want to give insights at running maven in Dockerfile. I updated the Dockerfile by adding maven installation to Docker rather than copying JAR file to the file system of the image. I changed FROM statements and built images
---Dockerfile 2--- FROM java:8 # mount to /tmp VOLUME /tmp # Install maven RUN apt-get update RUN apt-get install -y maven # Prepare by downloading dependencies ADD pom.xml /pom.xml RUN ["mvn", "dependency:resolve"] # Adding source, compile and package into a fat jar ADD src /src RUN ["mvn", "package"] EXPOSE 8090 ENV JAVA_OPTS="" CMD ["/usr/lib/jvm/java-8-openjdk-amd64/bin/java", "-jar", "target/demo-0.0.1-SNAPSHOT.jar"]
Result:
- nutrition-backend-maven-java8 908MB –> “Build with Dockerfile 2 using java:8”
- nutrition-backend-java8 672MB –> “Build with Dockerfile 1 using java:8”
- nutrition-backend-alpine 110MB –> “Build with Dockerfile 1 using openjdk:8-jdk-alpine”
Docker Maven Plugin
I have tried using an official maven image. By using only FROM:maven:<version>-onbuild
comand in Dockerfile, the build will COPY . /usr/src/app
and RUN mvn install
.
$ docker build -t nutrition-maven-onbuild . Sending build context to Docker daemon 28.91MB Step 1/1 : FROM maven:3.5.0-jdk-8-onbuild # Executing 2 build triggers... Step 1/1 : ADD . /usr/src/app ---> Using cache Step 1/1 : RUN mvn install ---> Running in c716802a574f [INFO] Scanning for projects... Downloading: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/1.5.2.RELEASE/spring-boot-starter-parent-1.5.2.RELEASE.pom ...
The base image size is 620MB, which is quite heavy. It is a good practice to create separate containers for maven and the Java application itself.
REPOSITORY TAG IMAGE ID CREATED SIZE ---my images--- nutrition-maven-onbuild latest 5bf1405c4199 40 seconds ago 677MB nutrition-backend-maven-java8 latest 3659ada8ccb4 2 days ago 908MB nutrition-backend-java8 latest 7696613fbbe1 2 days ago 672MB nutrition-backend-alpine latest b239eeb95bf5 2 days ago 110MB nutrition-backend latest de345c501e96 8 days ago 908MB ---base images--- openjdk 8-jre-alpine c4f9d77cd2a1 7 weeks ago 81.4MB openjdk 8-jdk-alpine 478bf389b75b 7 weeks ago 101MB maven 3.5.0-onbuild a87f2546370f 3 months ago 620MB maven 3.5.0-jdk-8-alpine 5435658a63ac 5 weeks ago 116MB java 8 d23bdf5b1b1b 7 months ago 643MB