bmuschko / gradle-docker-plugin

Gradle plugin for managing Docker images and containers.
https://bmuschko.github.io/gradle-docker-plugin/current/user-guide/
Apache License 2.0
1.23k stars 362 forks source link

Improve build times and image sizes by taking advantage of cacheable layers #734

Closed trajano closed 5 years ago

trajano commented 5 years ago

In order to reduce the impact on the registry by utilizing common layers an to improve build times in general rather than taking the simple approach of deploying the jar create additional layers that can better utilize existing layers.

Expected Behavior

Here's a sample of the Dockerfile I am using just redacted and added comments to help improve the implementation

# this first part can be done outside as well but since I am on Windows I do not want to bother doing chmods
FROM alpine AS unpacker
COPY [[jarfile]] /tmp
RUN mkdir -p /opt/ms && unzip -q /tmp/[[jarfile]] -d /opt/ms && \
  mv /opt/ms/BOOT-INF/lib /opt/lib

FROM openjdk:8-jre
EXPOSE 80
WORKDIR /opt/ms
# Copy over the lib files (basically the dependencies, ideally they won't change as much over time compared to the actual code)
COPY --from=unpacker /opt/lib /opt/ms/BOOT-INF/lib
# The actual code that is not from the dependencies
COPY --from=unpacker /opt/ms/ /opt/ms/
# Use the JarLauncher to start up the code.  This is basically what the main app from SpringBoot does
CMD ["/usr/bin/java", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCGroupMemoryLimitForHeap", "org.springframework.boot.loader.JarLauncher"]
# Ideally add a healthcheck for the port if the actuator dependency is added
HEALTHCHECK --start-period=5s CMD curl --silent --output /dev/null http://localhost:[[port]]/health

Current Behavior

Currently it just copies the JAR file which can be large and cannot leverage caches

Context

Every build at the moment will create a new image that takes up the full space therefore we need to docker system prune regularly

Steps to Reproduce (for bugs)

Your Environment

Windows 10

bmuschko commented 5 years ago

Thanks for bringing this up. Correct me if I am wrong but I believe this issue to be a duplicate of https://github.com/bmuschko/gradle-docker-plugin/issues/701. In fact I am already working on it.

bmuschko commented 5 years ago

This has been released with v4.4.0. Why don't you give it a spin? Would love to hear your feedback!

trajano commented 5 years ago

Looks good, but had to revert back to my previous approach because I couldn't find the extension points to add HEALTHCHECK for example.

Other things I added on my app but not on the example I have given is the debug port so I can debug within the container.

I did check that it did work and run the image which is good. It's likely smaller than the way I have done it and should theoretically start a few milliseconds faster because you went directly to the class whereas I went with the JarLauncher approach which has to take the time to detect which is the main class.

The only "surprise" that people who build the image in Windows would see is that the class and resource files in the image will be executable (may be an issue with the resources in case it has a bad script)

Also I run most of my Spring boot containers as nobody because I am paranoid. There are some that I run as a specific user so that I can write to the host file system, but that's usually a rare thing.

bmuschko commented 5 years ago

Looks good, but had to revert back to my previous approach because I couldn't find the extension points to add HEALTHCHECK for example.

This is documented in the user guide and easy to add.

Other things I added on my app but not on the example I have given is the debug port so I can debug within the container.

You can add or modify this via a similar approach.

I did check that it did work and run the image which is good. It's likely smaller than the way I have done it and should theoretically start a few milliseconds faster because you went directly to the class whereas I went with the JarLauncher approach which has to take the time to detect which is the main class.

Thanks for verifying. For the Spring Boot plugin we are also search for the main class file on the classpath.

trajano commented 5 years ago

@bmuschko regarding that part with the healthcheck

createDockerfile {
    instruction 'HEALTHCHECK CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1'
}

I actually got an error when I did it, maybe I am just not doing it right (or maybe it's because I put it inside the docker block. I'll look at it after work.

bmuschko commented 5 years ago

@trajano createDockerfile is the name of the task of type Dockerfile you are trying to modify. If you are using the Docker convention plugin then this task is named dockerCreateDockerfile. See the user guide for more information.