Optimize images & performance — Multi-stage Docker

NIRAV SHAH
3 min readMar 19, 2021

--

Everyone is migrated to kubernetes environment & started adopting this niche technology. However each of this also need tuning requirement. Default setup may not be the best for all purpose. We will check to reduce the size of the images by using runtime environment rather build time environent. Sometimes this is helpful for the security aspects too. Let’s use multi-stage build for the same.

Thick Docker image to Thin Docker image on production runtime

Build for the Golang

Golang provides binary file after build command & that binary can be run on any platform without need of golang environment. We will check old & new configuration. This will reduce the size of the image from 450MB to 34MB.

Old configuration

FROM golang:1.15.7-alpine3.13# Set the Current Working Directory inside the container
WORKDIR /root/
# Copy everything from the current directory to the PWD (Present Working Directory) inside the container
COPY . .
ENV GIN_MODE=release
ENV SERVER_PORT=8080
# Install the package
RUN go build -tags=jsoniter .
# This container exposes port 8080 to the outside world
EXPOSE 8080
# Run the executable
CMD ["./app"]

New configuration

FROM golang:1.15.7-alpine3.13# Set the Current Working Directory inside the container
WORKDIR /root/
# Copy everything from the current directory to the PWD (Present Working Directory) inside the container
COPY . .
ENV GIN_MODE=release# Install the package
RUN go build -tags=jsoniter .
FROM alpine:latest
WORKDIR /root/
COPY --from=0 /root/app .
ENV SERVER_PORT=8080# This container exposes port 8080 to the outside world
EXPOSE 8080
# Run the executable
CMD ["./app"]

Build for the JAVA

In JAVA we can have multiple options based on build package we use. I scratched two cases gradle build & maven build process.

Old configuration (Gradle Build)

FROM openjdk:8
ENV APP_HOME=/root/dev/myapp/
RUN mkdir -p $APP_HOME/src/main/java
WORKDIR $APP_HOME
COPY build.gradle gradlew gradlew.bat $APP_HOME
COPY gradle $APP_HOME/gradle
# download dependencies
RUN ./gradlew build -x :bootRepackage -x test --continue
COPY . .
RUN ./gradlew build

New configuration

FROM openjdk:8 AS BUILD_IMAGE
ENV APP_HOME=/root/dev/myapp/
RUN mkdir -p $APP_HOME/src/main/java
WORKDIR $APP_HOME
COPY build.gradle gradlew gradlew.bat $APP_HOME
COPY gradle $APP_HOME/gradle
# download dependencies
RUN ./gradlew build -x :bootRepackage -x test --continue
COPY . .
RUN ./gradlew build
FROM openjdk:8-jre
WORKDIR /root/
COPY --from=BUILD_IMAGE /root/dev/myapp/build/libs/myapp.jar .
EXPOSE 8080
CMD ["java","-jar","myapp.jar"]

Old configuration (type 2)

FROM openjdk:8-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/my-application.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

New configuration

FROM openjdk:8-jdk-alpine as build
WORKDIR /workspace/app

COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src

RUN ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

Build for the .Net application

For .Net application we can reduce the size of the image from 1.7GB to ~200mb.

Old configuration

FROM microsoft/dotnet:2.0-sdk
COPY . ./docker-size-test
WORKDIR /docker-size-test/
RUN dotnet build -c Release
ENTRYPOINT ["dotnet", "run", "-c", "Release", "--no-build"]

New configuration

FROM microsoft/dotnet:2.0-sdk AS build
COPY . ./docker-size-test
WORKDIR /docker-size-test/
RUN dotnet build -c Release -o output
FROM microsoft/dotnet:2.0-runtime AS runtime
COPY --from=build /docker-size-test/output .
ENTRYPOINT ["dotnet", "docker-size-test.dll"]

Reference:

--

--

NIRAV SHAH
NIRAV SHAH

Written by NIRAV SHAH

Working as Cloud Architect & Software enthusiastic

No responses yet