DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • 7 Microservices Best Practices for Developers
  • Kubernetes for Java Developers
  • A Comparison of Current Kubernetes Distributions
  • Building Microservices With gRPC and Java

Trending

  • Agentic AI Design Patterns and Principles: Building Autonomous, Collaborative Systems
  • Architecting an Embedded Efficiency Layer: A Platform Deep Dive into Day-Two Operational Tuning
  • YOLOv5 PyTorch Tutorial
  • Java String Format Examples
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Minions in Minikube - A Kubernetes Intro for Java Developers

Minions in Minikube - A Kubernetes Intro for Java Developers

Java developers, it would be despicable not to take a look at this cute project that will give you a hands-on introduction to Kubernetes.

By 
Ryan Dawson user avatar
Ryan Dawson
·
Updated Oct. 20, 20 · Tutorial
Likes (20)
Comment
Save
Tweet
Share
16.1K Views

Join the DZone community and get the full member experience.

Join For Free

We want our microservices to be replicable, replaceable workers that we can easily upgrade or downgrade without any downtime and minimal management. We might say we want them to be our minions. In this article we’ll walk through a simple example to see what Kubernetes can do for us by creating and orchestrating an army of minions. You can code along with this article or clone the project from here.

Prerequisites

We will need to containerize our microservices to run them in Kubernetes—we’ll use Docker for this. Rather than using a cloud-hosted Kubernetes we’ll use Minikube so that we can sandbox locally.

The Aim

Our minion army will be Java microservices. We want different types of minions in our army so that we see what Kubernetes can do for us. So we’ll aim for each microservice to respond to a simple http request with a response like:

We'll use ASCII art to represent the minion types.

Building a Java Minion Service

We can kickstart our microservice as a Spring Boot Web app using the Spring Initializr with the Web starter dependency:

In the project we’ll create a Controller annoted with @RestController to handle requests. We’ll use an @RequestMapping(method=GET) to provide a response body. So to start with we can do something like:

@RequestMapping( method=GET)
@ResponseBody
public String minion() throws UnknownHostException {

   StringBuilder stringBuilder = new StringBuilder();
   stringBuilder.append("Host: ").append(InetAddress.getLocalHost().getHostName()).append("<br/>");
   return stringBuilder.toString();

}


But this won’t quite give us what we want. We could output the ASCII art here but which minion type do we choose? For this we can use a trick. We’ll create one app that can take the form of any minion type we choose. To do that we’ll need it to contain a library of ASCII art minions. So we create a class called MinionsLibrary that we annotate with @Component and inside we create a map that we initialise with some minions from this blog :


@Component
public class MinionsLibrary {

    private Map<String,String> map = new HashMap<>();

    public MinionsLibrary(){

      map.put("one-eyed-minion",<COPY-PASTE MINION ASCII ART HERE>);
      map.put("two-eyed-minion",<COPY-PASTE MINION ASCII ART HERE>);
      map.put("sad-minion",<COPY-PASTE MINION ASCII ART HERE>);
      map.put("happy-minion",<COPY-PASTE MINION ASCII ART HERE>);

    }
}

Alternatively you can get the class from https://github.com/ryandawsonuk/minions/tree/master/src/main/java/org/minions/demo

Then we can tell our microservice which minion type to be. We can do this using the spring application name property (which we’ll later be able to set using a docker environment variable). It’ll also help us later to show the version of our application in the response so now our Controller becomes:

@RestController
public class Controller {

    private final String version = "0.1";

    private MinionsLibrary minionsLibrary;

    @Value("${spring.application.name}")
    private String appName;

    public Controller(MinionsLibrary minionsLibrary){
        this.minionsLibrary=minionsLibrary;
    }

    @RequestMapping( method=GET)
    @ResponseBody
    public String minion() throws UnknownHostException {

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Host: ").append(InetAddress.getLocalHost().getHostName()).append("<br/>");
        stringBuilder.append("Minion Type: ").append(appName).append("<br/>");
        stringBuilder.append("IP: ").append(InetAddress.getLocalHost().getHostAddress()).append("<br/>");
        stringBuilder.append("Version: ").append(version).append("<br/>");
        stringBuilder.append(minionsLibrary.getMinion(appName));
        return stringBuilder.toString();
    }
}


Now the library ‘image’ will be chosen to match the application name, which will be the minion type name (e.g. ‘one-eyed-minion’).

Dockerise and Deploy

We need to create a Docker image for our app. We want to build the exectuable jar inside the Docker image and then start the Java app when the container starts. We can do this using a multi-stage Docker build. The Dockerfile is:

FROM maven:3.5-jdk-8 as BUILDMINION
COPY src /usr/src/myapp/src
COPY pom.xml /usr/src/myapp
RUN mvn -f /usr/src/myapp/pom.xml clean package -DskipTests

FROM openjdk:alpine
COPY --from=BUILDMINION /usr/src/myapp/target/*.jar /maven/
CMD java $JAVA_OPTS -jar maven/*.jar


Everything down to ‘FROM openjdk:alpine’ builds the JAR and then just the jar is copied over into a subsequent build stage based on the lightweight openjdk:alpine image. We start it with the JAVA_OPTS param exposed so that we’ve got the option to limit memory consumption (see this article about reducing memory consumption).

Then we can build an image using the command "docker build . -t minion”

And we can deploy it by creating a Kubernetes deployment file—let’s call it “minion-army.yml”. This will contain entries for each minion type. Here is an entry for one minion type:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
 name: one-eyed-minion
labels:
   serviceType: one-eyed-minion
spec:
 replicas: 2
template:
   metadata:
     name: one-eyed-minion
     labels:
       serviceType: one-eyed-minion
   spec:
     containers:
       - name: one-eyed-minion
         image: minion:latest
         imagePullPolicy: Never
         ports:
         - containerPort: 8080
         env:
         - name: JAVA_OPTS
           value: -Xmx64m -Xms64m
         - name: SPRING_APPLICATION_NAME
           value: "one-eyed-minion"
---
apiVersion: v1
kind: Service
metadata:
 name: one-eyed-minion-entrypoint
namespace: default
spec:
 selector:
   serviceType: one-eyed-minion
ports:
   - port: 8080
     targetPort: 8080
     nodePort: 30080
type: NodePort


Note that the “SPRING_APPLICATION_NAME” variable is automatically matched to the spring.application.name property so that this minion service becomes a one-eyed-minion. There will be two instances (replicas) of this minion type available and the Kubernetes service will automatically route requests to one or the other of them.

The Service will be exposed to the world outside Kubernetes—with Minikube a request to port 30080 will go to the Service. (This bit of the Service would be a little different for real Kubernetes as we’d use LoadBalancer rather than NodePort and wouldn’t be restricted to the minikube port range.) The Service will handle it using Pods that are matched to the Service. We will have one Service per minion type.

The Deployment for the minion type will create two Pods. Each will be a minion of that type.

We can repeat the configuration above for each minion type, each time increasing the external port number so that different ports are used. Or we can use this Github repository, which also has additional configuration for doing a minion version upgrade without downtime. (We could avoid the repetition here if we used helm but we don’t want to add more tools than we have to.)

Create the Army

First start minikube:

minikube start --memory 4000 --cpus 3


Wait for it to start then link your Docker registry to the Minikube one and build the minion image for Minikube:

eval $(minikube docker-env)
docker build . -t minion


Then we can deploy the army:

kubectl create -f minion-army.yml


And see the troop types:

open http://$(minikube ip):30080
open http://$(minikube ip):30081
open http://$(minikube ip):30082
open http://$(minikube ip):30083


Each will look a lot like the happy-minion page from the beginning of the article.

And we can see the whole army by doing “ kubectl get pods ” or doing “ minikube dashboard” and going to the Pods page:

Creating More Troops

We can create more minions of a particular type under the Deployments section of the minikube dashboard:


One Minion Falls, Another Takes His Place

Let’s say this is what we get when we hit our happy minion Service from the browser:


What will happen if we kill the “happy-minion-58c9c46d67-j84s9”? We can try it with a delete from the Pods section of the dashboard or:

kubectl delete pod happy-minion-58c9c46d67-j84s9


If you hit refresh in the browser a few times (it could take a little while to kill the minion), you’ll see that the other minion of that type is then used by the Service. And if you go to the Pods section of the dashboard you’ll see that Kubernetes creates a new Pod to take the place of the one you deleted so that there remain two for that Deployment.

Minion Upgrades

We can also see a rolling upgrade on our minions. For this we should have the following under the ‘spec’ section (it can go immediately beneath ‘replicas’ at the same level) in each Deployment section in our minions-army.yml file:


minReadySeconds: 10
strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 1
     maxSurge: 1


Then we can change the version in the Controller class to 0.2, save it and then do:

docker build . -t minion:0.2


Then open minion-army.yml and find-replace all the "latest" with "0.2". Save the changes and do:

kubectl apply -f minion-army.yml --record


Refresh the browser of one of the minion types to see the version change in line with what you see from kubectl rollout status deployment where <deployment_name> is a minion type (e.g. one-eyed-minion).

Minion Rollbacks

To see the history of what was deployed do kubectl rollout history deployment <deployment_name> and to rollback do kubectl rollout undo deployment <deployment_name> --to-revision=1 (can take a little while)

Destroy the Army

Destroy the army with:

kubectl delete -f minion-army.yml


Stop minikube with “minikube stop”.

Minion (solver) Kubernetes Java (programming language) microservice Docker (software) Spring Framework Web Service ASCII art

Opinions expressed by DZone contributors are their own.

Related

  • 7 Microservices Best Practices for Developers
  • Kubernetes for Java Developers
  • A Comparison of Current Kubernetes Distributions
  • Building Microservices With gRPC and Java

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook