Spring Boot + Rest API + Create Docker Image + Deploy and Run Docker Image + Example

In this blog we will learn more about Containerization. With the help of working example we will learn; How to build a REST API using @RestController annotation. How to Create Docker Image, Deploy and Run the Docker Image in Local.

spring boot rest controller example with docker image build deploy and run – spring boot rest api docker example

Difference between VMs and Containers (Size & Start-Up)

  • The spring boot framework supports three different types of embedded servlet containers: Tomcat (default), Jetty and Undertow.
  • Docker Image does not package whole virtual Operating System.
  • Size: Containers utilized well organised memory as compared to VMs. Expained with the help of an example below.
  • Start-Up: Containers are lightweight, hence take less time to start up.
Difference between VMs and Containers (Size & Start-Up)
Difference between VMs and Containers (Size & Start-Up)
Difference between VMs and Containers (Size & Start-Up)

Docker Installation (Windows OS)

Docker Installation (Windows OS) - spring boot rest controller example with docker image build deploy and run

Docker Desktop Setting

We need to update Docker Desktop Setting as below

Docker Installation (Windows OS) and Setting - spring boot rest controller example with docker image build deploy and run

Spring Boot: Create Docker Image

First of all we will build a Spring Boot based REST API (spring-boot-docker), add docker specific configuration and then we will create the docker image.

Build a Spring Boot based REST API

In this application, we exposed a REST API @RequestMapping("/echo/{name}"), which will respond with name message to the client

Technlogy Stack

Java 8
Docker Desktop
Spring Boot 2.4.4
Eclipse/Red Hat JBoss Developer Studio 11.3.0.GA

Project Structure

High level application structure would be looked like as below.

Project Structure

Spring Boot: RestController (EchoRestController.java)

package com.the.basic.tech.info.echo;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class EcoApplication {

	public static void main(String[] args) {
		SpringApplication.run(EcoApplication.class, args);
	}
}

@RestController
class EchoRestController {
	private static final Logger logger = LoggerFactory.getLogger(EchoRestController.class);
	@RequestMapping("/echo/{name}")
	public String echoDocker(@PathVariable(value = "name") String name) {
		String response = "Echo " + name + " Response received on : " + new Date();
		logger.info("Response :: {}", response);
		return response;

	}
}

Maven File (pom.xml)

Pom.xml file would be looked like as below. Two plugins, dockerfile-maven-plugin and maven-dependency-plugin need to be added.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.the.basic.tech.info</groupId>
	<artifactId>spring-boot-docker</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-boot-docker</name>
	<description>Demo project for Spring Boot Containerization</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.4</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<docker.image.prefix>spring-boot-docker</docker.image.prefix>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>com.spotify</groupId>
				<artifactId>dockerfile-maven-plugin</artifactId>
				<version>1.4.7</version>
				<configuration>
					<repository>${docker.image.prefix}/${project.artifactId}</repository>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>unpack</id>
						<phase>package</phase>
						<goals>
							<goal>unpack</goal>
						</goals>
						<configuration>
							<artifactItems>
								<artifactItem>
									<groupId>${project.groupId}</groupId>
									<artifactId>${project.artifactId}</artifactId>
									<version>${project.version}</version>
								</artifactItem>
							</artifactItems>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>


</project>

Docker Configurations (Dockerfile)

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/spring-boot-docker-0.0.1-SNAPSHOT.jar spring-boot-docker-app.jar
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /spring-boot-docker-app.jar" ]

application.properties

server port we can update here, otherwise default port 8080 will be considered.

server.port = 9080

Docker Image Build (docker terminal)- mvn clean install dockerfile:build

[INFO] Image will be built as spring-boot-docker/spring-boot-docker:latest
[INFO]
[INFO] Step 1/5 : FROM openjdk:8-jdk-alpine
[INFO]
[INFO] Pulling from library/openjdk
[INFO] Digest: sha256:94792824df2df33402f201713f932b58cb9de94a0cd524164a0f2283343547b3
[INFO] Status: Image is up to date for openjdk:8-jdk-alpine
[INFO]  ---> a3562aa0b991
[INFO] Step 2/5 : VOLUME /tmp
[INFO]
[INFO]  ---> Using cache
[INFO]  ---> 641d4242f87d
[INFO] Step 3/5 : ADD target/spring-boot-docker-0.0.1-SNAPSHOT.jar spring-boot-docker-app.jar
[INFO]
[INFO]  ---> 4e487d7fc055
[INFO] Step 4/5 : ENV JAVA_OPTS=""
[INFO]
[INFO]  ---> Running in 7f4a3dab2932
[INFO] Removing intermediate container 7f4a3dab2932
[INFO]  ---> 22f106d5b853
[INFO] Step 5/5 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /spring-boot-docker-app.jar" ]
[INFO]
[INFO]  ---> Running in 458fab94eeea
[INFO] Removing intermediate container 458fab94eeea
[INFO]  ---> f33cb2483029
[INFO] Successfully built f33cb2483029
[INFO] Successfully tagged spring-boot-docker/spring-boot-docker:latest
[INFO]
[INFO] Detected build of image with id f33cb2483029
[INFO] Building jar: C:\Users\spring-boot-docker\target\spring-boot-docker-0.0.1-SNAPSHOT-docker-info.jar
[INFO] Successfully built spring-boot-docker/spring-boot-docker:latest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 50.961 s
[INFO] Finished at: 2021-06-10T15:37:29+05:30
[INFO] Final Memory: 53M/367M
[INFO] ------------------------------------------------------------------------

Deploy and Run Docker Image

C:\Users\spring-boot-docker>docker run -p 8080:9080 -t spring-boot-docker/spring-boot-docker --name spring-boot-docker-image

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.4)

2021-06-10 10:10:57.298  INFO 1 --- [           main] c.t.basic.tech.info.echo.EcoApplication  : Starting EcoApplication v0.0.1-SNAPSHOT using Java 1.8.0_212 on a64d41b12767 with PID 1 (/spring-boot-docker-app.jar started by root in /)
2021-06-10 10:10:57.316  INFO 1 --- [           main] c.t.basic.tech.info.echo.EcoApplication  : No active profile set, falling back to default profiles: default
2021-06-10 10:11:00.400  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 9080 (http)
2021-06-10 10:11:00.447  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-06-10 10:11:00.449  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.44]
2021-06-10 10:11:00.602  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-06-10 10:11:00.603  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3035 ms
2021-06-10 10:11:01.903  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-06-10 10:11:02.868  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9080 (http) with context path ''
2021-06-10 10:11:02.897  INFO 1 --- [           main] c.t.basic.tech.info.echo.EcoApplication  : Started EcoApplication in 6.475 seconds (JVM running for 7.413)

Note: The option -p 8080:9080 says that expose port 8080 for internal port 9080.

Docker Desktop: Containers/App Screen

Docker Desktop: Containers/App Screen
Docker Desktop: Containers/App Screen

spring-boot-docker: Access the application

Application running into docker can be accessed outside container.

spring-boot-docker: Access the application

How to Stop Docker Container from Terminal

We can list out all docker containers by docker ps command in the terminal and we can use the command docker stop <CONTAINER ID> for stopping the specific container

C:\Users\spring-boot-docker>docker ps
CONTAINER ID   IMAGE                                   COMMAND                  CREATED          STATUS          PORTS                                       NAMES
a64d41b12767   spring-boot-docker/spring-boot-docker   "sh -c 'java $JAVA_O…"   17 minutes ago   Up 17 minutes   0.0.0.0:8080->9080/tcp, :::8080->9080/tcp   suspicious_banzai

C:\Users\spring-boot-docker>docker stop a64d41b12767
a64d41b12767

C:\Users\spring-boot-docker>docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

C:\Users\spring-boot-docker>

Download Source Code (Attached)

Happy Learning 🙂