Docker is everywhere today. A lot of projects in most big companies are deployed in production as Docker containers. Once you realize how easy and useful this approach is, you’d want to Dockerzie everything. A lot of tools and services today provide official Docker images so that you don’t have to worry about downloading all the required dependencies, fixing version conflicts of those dependencies, and other compatibility issues. Docker is just peace of mind, to put it simply.
But how do we get started with this? I get this question a lot. And that’s the reason I’m writing this post. Getting started is easy. You just create a Dockerfile, write a command to download all the dependencies, copy your project into the Docker image, build it, and run it. In this post, we’ll take a simple Spring Boot web application as an example for this and see how we can get this done.
The Spring Boot Web Application
I’ve written a simple Hello, World! example for this. You can see the project, along with the Dockerfile, in my GitHub repo. The readme file in the project gives you all the required instructions and commands to build the project and run it to test how it works. So I’ll not cover the details of building the Spring Boot project here, but we’ll talk more about the Docker part. If the readme instructions are not clear on how to build the Spring Boot app, then let me know in the comments and I’ll help you out.
Let’s Dockerize it
Let’s go line by line in the Dockerfile, because it’s important to know what each line does if you want to apply this to any other project.
Line 1: FROM ubuntu:18.04
The first line in any Dockerfile tells Docker which “base” image to use. This line means: extend my Docker image FROM another image called ubuntu:18.04. That base image is mostly a public image. In some cases, each company or project might have a custom base image created from which all other images need to be created. In such cases, you’d use that specific image. If you want a parallel from Java to understand this better, this line means “import ubuntu:18.04.” So all the features in that Docker image will be available in your image.
Line 2: RUN apt-get update && apt-get install -y apt-utils openjdk-11-jdk
When you extend from the base image, in this case a bare bones Ubuntu 18.04 image, you can’t guarantee that it will have all the packages you need. So in this line, I’m updating the apt library and then installing openjdk-11-jdk package. We need JDK 11 to run our Spring Boot web app.
Line 3: RUN mkdir -p /var/poc/
We need a directory inside the Docker image to keep all our project files. So in this line, we’re running the mkdir command to create the directory /var/poc/. You can create a directory with any name you want.
Line 4: COPY ./build/libs/spring-boot-api-docker-poc-1.0-SNAPSHOT.jar /var/poc/
Once we have created the directory, we need to copy our project files into that directory so that it will be available inside the Docker container when we run a Docker image. For this, in this line, we’ll copy the .jar file into the /var/poc/ directory we just created in the previous step. Here, we assume that you have already built the Spring Boot project and the .jar file is already created.
Line 5: EXPOSE 9595
Because we have a web app running inside our Docker container, to interact with the app, we need to have the port open. When we open a port in the application inside a Docker container, the port will be open inside the container itself, It’ll not be available outside the container. So if you send a request to the app from your browser, it’ll not reach the app at all. For this, we need to “expose” the port on which our app is listening. In our example, that’s port 9595. So in this line, we tell Docker to expose this port to the outside world. Later, when we run the image to create a container, we’ll map this exposed port to another port on the host machine, so that the requests can be forwarded to the app inside the Docker container.
Line 6: CMD [“java”,”-jar”,”/var/poc/spring-boot-api-docker-poc-1.0-SNAPSHOT.jar”]
This is the last line in our Dockerfile. This is pretty simple to understand. We’re just running the java -jar command to run our Spring Boot web app. The syntax for this as given in the command. This does nothing more than just running the web app.
Running the Docker image to create a container
Now that we have the Dockerfile ready and understand what each line does, we’ll run the image to see if our setup is working or not. But before that, we need to first create an image. RIght now we only have a Dockerfile, we don’t have an image yet. To create an image from the Dockerfile, first cd into the directory where you have the Dockerfile. Then, run the following docker build command:
docker build -t poc/spring-boot-api-docker-poc .
If you’re running this command for the first time, and you don’t have all the dependencies already downloaded, there will be bunch of downloads. This will include the base ubuntu:18.04 image as well. This could take some time. From the second time on, it’ll be much quicker as there’s nothing to be downloaded. Your output should look something similar to the following:
The last two lines in the output are of interest to us at this point. The last but one line says that the image was successfully built, and it gives us the ID of the image. We don’t need it for now. The last line says that the image was also tagged as the latest version. This means you could have multiple versions of the same app as different images. You don’t have to create different Dockerfiles for different versions of your app. This comes very handy when you have to maintain different versions of a service at once.
Anyway, once the image is built, run the following command to run this image as a container:
docker run -it -p 127.0.0.1:9595:9595 poc/spring-boot-api-docker-poc
As you can see from the command, we’re using the -p option here to map a host machine port to the exposed port from the container. In this case, we’re mapping the host machine’s port 9595 to the exposed port 9595. But you don’t have to map the same port in the host machine as the exposed port. Maybe there’s some other service which is using port 9595 on the host machine. So you may decide to use port 9596 on the host machine. To do that, just change the port number in the command like so:
docker run -it -p 127.0.0.1:9596:9595 poc/spring-boot-api-docker-poc
As you can see, the host machine’s 9596 is now mapped to the exposed 9595 from the container: 127.0.0.1:9596. That was easy. Now let’s see if the app works. From another terminal window, run the following command:
And then maybe try giving your name as the input:
You should see outputs similar to the screenshot shown below:
Well, looks like our little experiment was a success. We have successfully created a simple Spring Boot web app, Dockerized it, exposed the required port, and communicated with the app. It wasn’t all that difficult to get started, was it? But, we have not even scratched the surface of Docker yet. This is just the bare minimum of what we need to know to use Docker. There’s much more you can and should do with Docker. If you’re interested in exploring more of Docker with me, let me know in the comments and I’ll be happy to write more. You can find all the commands and the code we used in this post over on my GitHub repo. Go explore.
Become a Patron!