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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • 7 Microservices Best Practices for Developers
  • Kubernetes for Java Developers
  • Configuring Java Apps With Kubernetes ConfigMaps and Helm
  • Reactive Microservices Done Right!

Trending

  • Simpler Data Transfer Objects With Java Records
  • Distributed Consensus: Paxos vs. Raft and Modern Implementations
  • AI Speaks for the World... But Whose Humanity Does It Learn From?
  • Implementing Explainable AI in CRM Using Stream Processing
  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
15.7K 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
  • Configuring Java Apps With Kubernetes ConfigMaps and Helm
  • Reactive Microservices Done Right!

Partner Resources

×

Comments
Oops! Something Went Wrong

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

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

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 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!