Introduction

In today's development world, there are usually a lot of parts involved. First there are usually different environments i.e dev environment (for developers to work on), test environment (for QA and the PM team to see the product) and the live environment where the customers/client access the product. With these different environments, there are usually different databases, monitoring tools, and other assets that need to be handled per environment. Managing this becomes a nightmare for anyone involved.

Docker

Enter containers and specifically for this article, Docker. Docker is defined by Wikipedia as a set of platform as a service (PaaS) products that use OS-level virtualization to deliver software in packages called containers. Containers are isolated from one another and bundle their own software, libraries and configuration files; they can communicate with each other through well-defined channels. All containers are run by a single operating system kernel and therefore use fewer resources than virtual machines. In short, docker, through the use of containers enables us as developers to have a uniform set of dependencies across environments. Basically docker is a platform or suite of technologies that enable you to run each platform as a suite of light weight components that can easily be re-used across different environments and OSs.

Spring Boot

The second technology we will be using in this article will be spring boot. Spring boot is a java framework that creates both MVC (model, view, controller) and micro-service applications. It is widely used in the enterprise world and enables us to get up and running with a java project in a short time.

For this article, we will be going through how to use both of these technologies to enable 'dockerization' for a simple REST api.

By the time you are done with this article, you will understand how:

  • To create a spring boot starter project and get a simple api running
  • To install docker and make this spring boot project container ready

Hands On

Let us begin. Open spring initializer to create a Java Maven project with the fields as below. Replace the group name with your domain name. Be sure to add Spring Web as a dependency to enable the RESTful features we need. Also be sure to select Jar for packaging as this will be useful when we start working with Docker.

Once you are done, select generate and you will get a zip file with everything setup. That is one of my favorite features with spring boot. Project setup is a breeze. Now extract the zip file to a workspace folder that you are comfortable with and import the project as a maven project with IntelliJ. Click on the run icon on of IntelliJ and the project should run successfully. This is the power of spring boot. The project has a sample server that runs all the code. Since we do not have any mappings, we can not see/view/access anything on the browser. Let us set up the remainder of the api.

The project structure should look like this.

Inside the com.[yourusername].greeterservice package, create 2 files. One POJO class that we will name Greetings and another java class called GreetingsController.

Code content for greetings.java:

public class Greeting {
    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}

Code content for greetingcontroller.java:

@RestController
public class GreetingController {
    private static final String template = "Nice to meet you, %s!";
    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "stranger") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}

Now when you run the project again and access http://localhost:8080/ greeting on a browser, you should see the following response.

You can add request parameters to the request and the response to http://localhost:8080/greeting?name=Alfy

Change the name i.e Alfy in this example to your name or any other name to see how the result looks like. Each refresh increases the id. Believe it or not, this simple REST get response is fully done. Of course it's not practical but it shows how fast you can get up and running on spring.

Let us move on to dockerizing this simple api. The first step is to make sure you install docker for your OS with the instructions here for mac or windows and here for linux. Once you have docker installed and running, open a terminal (Mac or linux) or powershell (windows) and type in the following:
docker info
You should see output from the docker engine that is running on your machine.

The way that docker works is by building/bundling your code into one simple image that does only one thing (ideally). This works well with microservices because you can have multiple containers that are doing one thing and communicating with each other within docker itself. The way the container knows what to run/install is by using a Dockerfile (without any extension) to build an image. The image created consists of all the instructions that you define in the dockerfile. The Dockerfile also contains the base image which is usually a linux distro that we build upon for our specific use case. The next step in dockerizing this simple app is by first creating the dockerfile.

Here is how it should look like:

FROM adoptopenjdk/openjdk12:latest

COPY target/greeterservice-0.0.1-SNAPSHOT.jar /app/app.jar

EXPOSE 8080

CMD ["java","-jar","/app/app.jar"]

Let us go through this simple Dockerfile. The FROM keyword has to be the first line in the file, this enables docker pick the base image that we are working with. Here we use adoptopenjdk/openjdk12:latest which is an openjdk image. We use the COPY keyword to copy the resulting jar file from our project into a jar file called app.jar inside the image that we create and inside the folder /app/. We then use the EXPOSE command to expose the port 8080 from the container that is running. Remember this is the spring boot default port. Lastly, using the CMD command, we define what docker should run once the container is ready. Note here that we are simply running the jar like any other normal jar file -  'java -jar /app/app.jar'. Since spring has it's own embedded server, this is all that is needed to get it running.

Now for the moment of truth. First let us generate the .jar file in the target directory. If you followed along during the creation of this project you should be using maven, all you need to do to get the .jar file is run the following inside your project:

  • mvn clean install
  • mvn clean package

When these two commands complete, we have an executable jar file with the embedded server and all our code.
Next, we need to build the image first then create and run a container with this image we created.
On the terminal, write this command to create and run an image:
docker image build --tag simplegreetingservice .
The tag in this case is for you to easily identify this image locally plus this will be the name of the image when you upload it on dockerhub or a private image repository (this is beyond the scope of this tutorial). The . is to give docker the context with which it should look for the Dockerfile.
With this command, docker will go out to dockerhub and download the base image openjdk plus setup all instructions as specified in the dockerfile. The output should be somewhat similar to this with docker showing all steps as you have defined in the dockerfile.

2020-09-14-17_25_14-greeterservice-3

With the image now ready on your machine, you need to build/run the container that will actually run your code. Remember, the image is just a snapshot with other images and instructions, there is nothing running yet.

To finally run this image in a container, run the following code in the same terminal:
docker container run -p 8080:8080 --name greeter simplegreeterservice:latest

Let us look at this commands. Docker container run builds and runs the container instead of making it 2 separate commands. -p 8080:8080 deals with the port. Here we are saying on this machine, bind port 8080 to port 8080 from the container. Remember the expose statement in the Dockerfile? This is what completes exposing the ports that docker exposes in a container. --name gives this container a name so that you can do actions on it when it is running and the last part is the image name that we choose when creating this image. The 'latest' at the end ensures that you use the latest built image. Once you run this, you will see the spring boot output in the terminal and the requests should now be working exactly the same as they would have before, only now the container is responsible for the entire application.

Conclusion

That is it. We created a simple spring boot rest api, dockerized it and run it into a container. This barely scratches the surface with what docker can do, but hopefully this shows you the power of both spring boot and docker. The git repository for this whole article is here.

Happy coding and good luck.

You've successfully subscribed to Decoded For Devs
Welcome back! You've successfully signed in.
Great! You've successfully signed up.
Your link has expired
Success! Your account is fully activated, you now have access to all content.