29/11/2021

[Docker] Multi stage builds

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