K8S + MiniKube+ Docker +Spring Boot REST API example

Introduction:

Containerization has revolutionized software development and deployment by providing a consistent and scalable environment for running applications. In this article, we will explore how to develop a Spring Boot application, containerize it using Docker, and deploy it on a local Minikube cluster. Minikube is a lightweight Kubernetes implementation that allows you to set up a local Kubernetes environment for testing and development purposes. Let's dive in and learn how to get started!

Prerequisites:

Before we begin, make sure you have the following prerequisites installed on your development machine:

  1. Java Development Kit (JDK) 13 or higher

  2. Docker

  3. Minikube

  4. kubectl (Kubernetes command-line tool)

Setup Application:

Step 1:

Set up the Spring Boot Application

To begin, let's create a simple Spring Boot application. You can either use an existing application or create a new one using Spring Initializr. Make sure your application runs successfully by executing it locally.

  • Create a new Spring Boot project or use an existing one.

  • Create a new Java class named UserController that will handle the REST API endpoints. Add the following code:

Create a User class to represent the user entity. Add the following code:

public class User {
    private Long id;
    private String name;
    private String email;

    // Getters and setters

    // Constructors
}
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/")
    public ResponseEntity<List<User>> getUsers() {
        List<User> users = userService.getAllUsers();
        return ResponseEntity.ok(users);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable("id") Long id) {
        User user = userService.getUserById(id);
        if (user != null) {
            return ResponseEntity.ok(user);
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    @PostMapping("/")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User createdUser = userService.createUser(user);
        return ResponseEntity.ok(createdUser);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable("id") Long id, @RequestBody User user) {
        User updatedUser = userService.updateUser(id, user);
        if (updatedUser != null) {
            return ResponseEntity.ok(updatedUser);
        } else {
            return ResponseEntity.notFound().build();
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable("id") Long id) {
        boolean deleted = userService.deleteUser(id);
        if (deleted) {
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}

In this example, the UserController class injects an instance of UserService via constructor injection. The service layer is responsible for processing business logic and interacting with the repository DAO.

Create a new Java interface named UserService defines the service layer methods. Add the following code:

public interface UserService {

    List<User> getAllUsers();

    User getUserById(Long id);

    User createUser(User user);

    User updateUser(Long id, User user);

    boolean deleteUser(Long id);
}

The UserService interface declares the methods for retrieving all users, retrieving a user by ID, creating a new user, updating a user by ID, and deleting a user by ID.

Create a new Java class named UserServiceImpl implements the UserService interface. Add the following code:

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    @Override
    public User getUserById(Long id) {
        Optional<User> userOptional = userRepository.findById(id);
        return userOptional.orElse(null);
    }

    @Override
    public User createUser(User user) {
        return userRepository.save(user);
    }

    @Override
    public User updateUser(Long id, User user) {
        Optional<User> userOptional = userRepository.findById(id);
        if (userOptional.isPresent()) {
            User existingUser = userOptional.get();
            existingUser.setName(user.getName());
            existingUser.setEmail(user.getEmail());
            return userRepository.save(existingUser);
        } else {
            return null;
        }
    }

    @Override
    public boolean deleteUser(Long id) {
        Optional<User> userOptional = userRepository.findById(id);
        if (userOptional.isPresent()) {
            userRepository.delete(userOptional.get());
            return true;
        } else {
            return false;
        }
    }
}

The UserServiceImpl class implements the UserService methods by interacting with the UserRepository DAO.

Create a new Java interface named UserRepository that extends JpaRepository<User, Long>. Add the following code:

public interface UserRepository extends JpaRepository<User, Long> {
}

The UserRepository interface extends the JpaRepository the interface provided by Spring Data JPA, which provides generic CRUD operations for the User entity.

Run the Spring Boot application, and the REST API endpoints will be available at the following URLs:

The service layer (UserServiceImpl) will handle the business logic and interact with the repository DAO (UserRepository) to perform CRUD operations on users.

Step 2:

Containerize the Spring Boot Application To containerize our Spring Boot application, we will create a Dockerfile. Create a file named Dockerfile in the root directory of your project and add the following content:

FROM adoptopenjdk:13-jre-hotspot
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

This Dockerfile uses an OpenJDK 11 base image, sets the working directory to /app, copies the Spring Boot application's JAR file into the container, exposes port 8080, and defines the command to run the application.

Step 3:

Build and Push the Docker Image Next, open a terminal and navigate to the root directory of your Spring Boot project. Build the Docker image by running the following command:

docker build -t my-spring-app .

Once the image is built successfully, you can optionally push it to a container registry such as Docker Hub for easier access in your Minikube cluster.

Step 4:

Set up and Start Minikube Ensure that Minikube is installed on your machine. Start Minikube by running the following command:

minikube start

This will create and start a local Kubernetes cluster using the Minikube driver.

Step 5:

Deploy the Spring Boot Application Now it's time to deploy our Spring Boot application to the Minikube cluster. We'll create a Kubernetes deployment and a service to expose the application.

First, create a file named deployment.yaml and add the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-spring-app-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-spring-app
  template:
    metadata:
      labels:
        app: my-spring-app
    spec:
      containers:
        - name: my-spring-app-container
          image: my-spring-app
          ports:
            - containerPort: 8080

This YAML file defines a deployment with a single replica, a selector, and a pod template that runs our containerized Spring Boot application.

Next, create a file named service.yaml and add the following content:

apiVersion: v1
kind: Service
metadata:
  name: my-spring-app-service
spec:
  selector:
    app: my-spring-app
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
  type: NodePort

This YAML file defines a service that exposes our application on a NodePort, allowing us to access it from our local machine.

Apply both YAML files to the Minikube cluster by running the following commands:

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

Step 6:

Access the Spring Boot Application To access the deployed Spring Boot application, find the NodePort assigned to the service. Run the following command:

minikube service my-spring-app-service --url

This will provide you with a URL that you can use to access your application from a web browser or via cURL.

Conclusion:

In this article, we walked through the process of developing a Spring Boot application, containerizing it using Docker, and deploying it on a local Minikube cluster. Containerization provides a reliable and scalable environment for running applications, while Minikube offers a convenient way to test and develop Kubernetes deployments locally. By following these steps, you can start building and deploying your own containerized Spring Boot applications using Minikube and Kubernetes.

I hope this helps, you!!

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://www.techwasti.com/

\==========================**=========================

If this article adds any value to you then please clap and comment.

Let’s connect on Stackoverflow, LinkedIn, & Twitter.

Did you find this article valuable?

Support techwasti by becoming a sponsor. Any amount is appreciated!