2 minute read

Having trouble with long build time of your docker image?

Waiting for a long build time is quite difficult to tolerate.

When you are building a docker image, there are some better solutions.

If you are using Dockerfile building an image, please make sure you are using a multi-stage build.

You can make your image leaner, and also take benefit of cached layer of docker.

FROM --platform=$BUILDPLATFORM gradle:jdk17-alpine as build
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle gradle/
COPY gradlew ./
COPY src ./src
RUN ./gradlew bootJar -p ./town-app --no-daemon
COPY ./build/libs/app.jar ./
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=local"]
  • This is a plain Dockerfile. It takes about 140 seconds in my machine.

plainTime

Now, this is a Dockerfile with multi-stage build.

Please watch first comment of the script.

Since dependencies of the system rarely changes, it improves build time by caching it’s layer.

multistageTime

  • 130 seconds without that command,

cachedTime

  • About 86 seconds with that command.
FROM --platform=$BUILDPLATFORM gradle:jdk17-alpine as build
WORKDIR /app

COPY build.gradle.kts settings.gradle.kts ./
COPY gradle gradle/
COPY gradlew ./

RUN gradle --no-daemon build || return 0  # Build for cache dependencies
COPY src ./src
RUN ./gradlew bootJar -p ./app --no-daemon


FROM --platform=$TARGETPLATFORM eclipse-temurin:17-jre-alpine
WORKDIR /usr/local

COPY --from=build /app/build/libs/app.jar ./

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "-Duser.timezone=Asia/Seoul", "-Xmx2048m", "app.jar", "--spring.profiles.active=local"]

Lastly, If you are using gradle or maven, consider using JIB for building docker image.

It is noted for jib from the google document that,

“It reads your build config, organizes your application into distinct layers (dependencies, resources, classes) and only rebuilds and pushes the layers that have changed.”

Jib provides much faster build time than using Dockerfile.

jibTime

It took about 6 seconds to build.

The image size is almost the same, 241.64MB for using Dockerfile and 241.55MB for using JIB.

jib {
    val activeProfile = if (project.hasProperty("profile")) project.property("profile") else "local"
    from {
        image = "eclipse-temurin:17-jre-alpine"
    }
    to {
        image = “app”
        tags = setOf("latest")
    }
    container {
        mainClass = "silver.town.TownApplication"
        creationTime = "USE_CURRENT_TIMESTAMP"
        ports = listOf("8080")
        jvmFlags = listOf("-Dspring.profiles.active=$activeProfile", "-XX:InitialRAMPercentage=75", "-XX:MinRAMPercentage=75", "-XX:MaxRAMPercentage=75", "-XX:+UseContainerSupport")
    }
}

If you want to test it in your local system, use ./gradlew jibDockerBuild to only push to your local docker desktop.

Conclusion

  • If you are using gradle or maven, consider using JIB for building your docker image.
  • If you want to use Dockerfile, use multi-stage build, try to cache as much as possible for faster build time.

This article was created by Crocoder7. It is not to be copied without permission.

References

Leave a comment