Search code examples
javaspring-bootdockerspring-databuildpack

Buildpacks + SpringBoot: Port and configuration issues for Docker image


Small question regarding migrating building Docker images from Dockerfile to BuildPacks for a SpringBoot project.

I have a very straightforward SpringBoot application I need to containerize. In order to do so, I have a very straightforward Dockerfile:

FROM my-custom-java-base-image

RUN install -d -o some-user -g some-user /var/log/supervisor
RUN chmod -R 755 /usr/local/some-user/

EXPOSE 9999

Please note, in this Dockerfile, there is a custom java base image, the need to run some RUN commands (as well as running some other commands), and exposing a custom port, not the usual 8080.

This image builds fine, and everything works with it, very happy.

Now, since SpringBoot 2.3, there is this new feature with Buildpacks integration, where one can just simply run ./mvnw spring-boot:build-image and have a very nice layered Docker image built.

This Layered Docker image is indeed very cool. Many talks at popular conferences shows the benefits of this new way of building images:

references:

https://www.youtube.com/watch?v=44n_MtsggnI

https://www.youtube.com/watch?v=EVHHyiypiY0

However, during all the demo I could find online, the initial Dockerfile is very rudimentary, no custom port, no custom commands, as if it can only work for the basic

FROM alpine
WORKDIR /tmp
COPY target/myapp.jar myapp.jar
ENTRYPOINT [java -jar blablabla]

What I tried so far is to find in the plugin configuration a way to configure the things I need from my Dockerfile

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!-- Configuration to push the image to our own Dockerhub repository-->
                <configuration>
                    <image>
                        <name>docker.io/2013techsmarts/${project.artifactId}:latest</name>
                    </image>
some configuration for the port, some commands to run, etc
                </configuration>
            </plugin>
        </plugins>
    </build>

But no luck.

Is this new way of building images everyone is talking about good only for very basic Dockerfile?

How may I change the port, the base image, add my own commands etc while using Buildpacks please?

Thank you


Solution

  • Trying to pick out and answer your questions, if I missed anything let me know.

    1. You shouldn't generally need to set an entry point. Buildpacks will detect and set the correct one for you automatically. That should work for 99% of Spring Boot apps. It is also significantly more flexible than what you'd get with a standard Docker ENTRYPOINT, see the docs on starting your container. If you still feel like you need to set an entrypoint, you'd have to provide more information about why and what you are trying to do (probably good for a new question).

    2. There is no equivalent to EXPOSE in buildpacks, but it matters less than you might think. EXPOSE is just setting metadata.

      The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published.

      Without EXPOSE, you can't run docker run -P, but you can still run docker run -p port:port and it works just the same.

      If you need to document the port information, which is basically what EXPOSE does, you could add a label with the port information. Buildpacks support setting arbitrary labels on the generated images.

    3. In answer to:

      Is this new way of building images everyone is talking about good only for very basic Dockerfile?

      No. Dockerfiles are a generic way to build images, which work for many, many different situations. Buildpacks do not attempt to replace Dockerfiles for every use case. The target area for buildpacks is packaging your applications into container images. If you deploy with Docker or Kubernetes or some other container orchestrator then Buildpacks should work very well for you.

    4. In answer to:

      How may I change the base image

      With buildpacks you have a base build image and a base run image. The base build image is the base container image that is used when buildpacks run and your application compiles. The base run image is what is used by your application when it executes. The build is often larger than the base as you have more dev tools and libraries there.

      You can change both. To change the build image, you must change the builder. That is the <builder> tag in your Spring Boot build tools configuration or the -B flag for pack build. The builder contains the base build image plus all of the buildpacks. It also has metadata to indicate the run image that pairs with the build image. If you make a custom builder image, you can control all of those things in as much detail as you want, you just need to ensure that the buildpacks you're using are compatible with the build and run image you create. Instructions for creating a builder can be found here.

      If you only need to change the run image, that can be done a little easier. You can set the <runImage> config setting in your Spring Boot build tools and it'll override the run image suggested by the builder.

      You need to be careful when doing this because you need to ensure that the application built with the build image will run on the run image you specify. The two are usually paired so that any libraries necessary to run the application are present at runtime. For Java apps, this is generally easy since there are not a lot of system dependencies. However, you could not set the run image to scratch because the JVM does have a couple of system dependencies.

      You might be thinking, "how would I create these images?" and the answer is a Dockerfile. As mentioned before, Buildpacks are not attempting to completely replace Dockerfiles, just make certain use cases easier. This is a use case, making base images, where Dockerfiles work fine, so you can docker build your own base builder and run images. See here for more detailed instructions.

      The process to create your own build/run images is not technically difficult or time consuming, but it does require automation because you must keep those images up-to-date so you are getting relevant security fixes.

      Having said all that, you may not need to create custom images. I don't know what you're specific needs are here, so I would encourage you to take a look at this SO post, which has some possible ways to customize the container images generated by Spring Boot.

      I would also suggest that you try to take a step back and rethink the problem you have to solve. It is often the case that converting a Dockerfile to Buildpacks can be more complicated and produce awkward results because you're trying to reimplement your Dockerfile solution to the problem. If you instead think about how you could solve the actual problem using Buildpacks, you might come out with a more elegant solution.