I've been recently introduced to a nice Docker feature: multi-stage builds.
The idea is simple, if the build is containerized as well, the build itself is a developer responsability as well and the operations team need only provide a build server with docker installed on all worker nodes.
To achieve the result, we use a simple Dockerfile where we specify multiple FROM statements and tag each layer as necessary. The last layer will be the one responsible to run the application, while the previous layers are only used for the build. A sample file for a SpringBoot app looks like this:
# syntax=docker/dockerfile:1
# build layer
FROM adoptopenjdk/openjdk11:latest as build
WORKDIR /app
# copy project files into container workdir
COPY . .
# build jar, skip tests, avoid daemon
RUN ./gradlew build -x test --no-daemon
# run layer
FROM adoptopenjdk/openjdk11:latest as prod
WORKDIR /app
#copy fat jar from previous layer into current workdir and rename it
COPY --from=build /app/build/libs/*.jar ./myApp.jar
# not mandatory, must use -p 8080:8080 later anyway
EXPOSE 8080
# start the spring boot app
CMD ["java", "-jar", "myApp.jar"]
Then it can be placed in the project directory and we can trigger the build with:
docker build -t TAG .
Finally run it with (binding for example port 8080 and executing it in background):
docker run -p 8080:8080 -d TAG
We can also see the container output with (find container name with docker ps first):
docker logs -f CONTAINER_NAME