In this post we will take a look how we can build a Spring Boot application, create the Docker image, deploy it to a Docker registry and deploy it to a Kubernetes cluster. This will give us the opportunity to get acquainted with the basics from building an application up to deploying it to Kubernetes. Sources can be found at GitHub.

Why are we doing this?

So, why should we take a look at Kubernetes? The answer is quite simple: Docker containers are more and more used and Kubernetes offers us a way to manage our containers. Besides that, Kubernetes is often used in CI/CD in order to fulfill the Continuous Deployment. Therefore,  it is good to have at least a basic understanding of how Kubernetes works and get familiar with the terminology.

Basically, Kubernetes manages a cluster of machines which act as a single unit. Within a cluster, you will have a Master which coordinates the cluster and nodes, the nodes run the applications. We will explain the terminology later on in part 2. Kubernetes is also often abbreviated as K8s (8 because 8 letters are left out).

A good place to start learning, are the tutorials on the Kubernetes website itself.

In order to start working with Kubernetes, you can make use of local solutions or hosted solutions, the list of options you have can be viewed here. In our tutorial, we will make use of Minikube, which is a local solution. This way we don’t need an account for a hosted solution and it will work perfectly for trying out some things.

What are we going to do?

Here are the steps we will follow in order to reach our goal:

  1. Create a Hello World Spring Boot application
  2. Create a Docker image of the application
  3. Push the Docker image to a Docker registry
  4. Install Minikube on Windows
  5. Deploy the application to Minikube
  6. Update the application

Steps 1 up to 4 will be covered in this post, steps 5 en 6 will be covered in part 2.

I will be working from a Windows 10 machine and will try to run Minikube on Windows (as we will see later in this post, the plans will change a little).

Step 1: Create a Hello World Spring Boot application

As always, our starting point is https://start.spring.io/. We select Java 10, Spring Boot 2.0.1, select Web MVC and Spring Actuator. Generate the project and open it with your IDE. This will get us started.

We will create a simple Spring Boot application with one endpoint returning a “Hello Kubernetes!” message. So, we add the following HelloController:

@RestController
public class HelloController {

  @RequestMapping("/hello")
  public String hello() {
    return "Hello Kubernetes!";
  }

}

Running the application gives us the following error:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project mykubernetesplanet: Execution default-compile of goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile failed. IllegalArgumentException

It seems that there is an issue with a dependency of the Maven Compiler Plugin with Java 10. A temporary fix is to set a newer version of the asm dependency in your pom:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.7.0</version>
  <configuration>
    <source>${java.version}</source>
    <target>${java.version}</target>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm</artifactId>
      <version>6.1</version> <!-- Use newer version of ASM -->
    </dependency>
  </dependencies>
</plugin>

Run the application again and verify whether http://localhost:8080/hello returns the following output:

Hello Kubernetes!

Step 2: Create a Docker image of the application

Now that we have our application running, it is time to create a Docker file which takes the image of Java 10 and places our jar file into it. The Docker file is:

FROM openjdk:10-jdk
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

In order to be able to build our image and push it to a Docker registry, we need a Maven plugin. We will make use of the dockerfile-maven-plugin provided by Spotify. Add the following to your pom file:

<properties>
  <docker.image.prefix>mydeveloperplanet</docker.image.prefix>
  <dockerfile-maven-version>1.3.6</dockerfile-maven-version>
</properties>

<build>
   <plugins>
     <plugin>
       <groupId>com.spotify</groupId>
       <artifactId>dockerfile-maven-plugin</artifactId>
       <version>${dockerfile-maven-version}</version>
       <executions>
         <execution>
           <id>default</id>
           <goals>
             <goal>build</goal>
             <goal>push</goal>
           </goals>
        </execution>
      </executions>
      <configuration>
        <repository>${docker.image.prefix}/${project.artifactId}</repository>
        <tag>${project.version}</tag>
        <buildArgs>
          <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
        </buildArgs>
      </configuration>
    </plugin>
  </plugins>
</build>

Run Maven:install in order to check whether we can create the Docker image. Unfortunately, this returns the following error:

Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
 at org.codehaus.plexus.archiver.zip.AbstractZipArchiver.(AbstractZipArchiver.java:116)

And the build fails with the generic message:

Failed to execute goal com.spotify:dockerfile-maven-plugin:1.3.6:build (default) on project mykubernetesplanet: Execution default of goal com.spotify:dockerfile-maven-plugin:1.3.6:build failed: An API incompatibility was encountered while executing com.spotify:dockerfile-maven-plugin:1.3.6:build: java.lang.ExceptionInInitializerError: null

It seems that this is caused by the Maven Archiver plugin, which is not yet fully compatible with Java 9/10, see https://github.com/spotify/dockerfile-maven/issues/163

Adding the following dependencies to the dockerfile-maven-plugin section solves the problem for the time being:

<dependency>
  <groupId>org.codehaus.plexus</groupId>
  <artifactId>plexus-archiver</artifactId>
  <version>3.4</version>
</dependency>
<dependency>
  <groupId>javax.activation</groupId>
  <artifactId>javax.activation-api</artifactId>
  <version>1.2.0</version>
</dependency>

Make sure you have Docker locally running and that the setting ‘Expose daemon on tcp://localhost:2375 without TLS’ is enabled in the Docker settings, this is due to the following error: https://github.com/spotify/docker-maven-plugin/issues/351

Run Maven:install again. Now the build is successful and we have created our Docker image.

Step 3: Push the Docker image to a Docker registry

Prerequisite is an account to a Docker registry, e.g. Docker Hub (my account can be found here). Besides this, you will need to change the following property in your pom to your own Docker id:

<docker.image.prefix>mydeveloperplanet</docker.image.prefix>

Next, create the repository mykubernetesplanet into your Docker Hub registry.

In order to be able to push anything to your Docker Hub registry, you need to add the account settings to your Maven settings.xml:

<servers>
  <server>
    <id>docker.io</id>
    <username>docker_username</username>
    <password>docker_password</password>
  </server>
</servers>

And at last, add the useMavenSettingsForAuth to the dockerfile-maven configuration tag in your pom:

<configuration>
  <repository>${docker.image.prefix}/${project.artifactId}</repository>
  <tag>${project.version}</tag>
  <buildArgs>
    <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
  </buildArgs>
  <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
</configuration>

Then run maven docker:push. The image is being pushed to our repository and within the tags section, we find a 0.0.1-SNAPSHOT tag.

Install Minikube

Windows

We will install Minikube on a Windows system. Note that this is still experimental, but we will only use basic functionality, so I assume that this will work just fine.

Installation instructions can be found at GitHub.

Also, you need to download and install kubectl, the command line tool in order to interact with your Kubernetes cluster.

First, we will start our Minikube cluster. By default, minikube will use VirtualBox, but I have already HyperV installed and therefore I need to specify the vm driver I want to use.

sudo minikube start --vm-driver hyperv

An ISO is being downloaded and then the following error is shown:

Hyper-V PowerShell Module is not available.

It seems that this problem often occurs when using HyperV, so I installed VirtualBox and ran the following command:

sudo minikube start

The following error is shown:

This computer is running Hyper-V. VirtualBox won't boot a 64bits VM when Hyper-V is activated. Either use Hyper-V as a driver, or disable the Hyper-V hypervisor. (To skip this check, use --virtualbox-no-vtx-check).

Go to ‘Windows Features’ and turn ‘Hyper-V’ off. This requires a restart of your computer. After that, run the command again.

Again this gave an error, I had to enter the command from my home directory:

C:\Users\<username>\

Unfortunately, this again gave some errors, and therefore I decided to gave up installing Minikube on Windows.

Ubuntu

We will now try to install Minikube onto an Ubuntu VM. First step is to download Ubuntu Desktop 16.04.4 and create a Hyper-V VM.

In the Ubuntu VM, I first needed to install Virtualbox. After this, running the Minikube start command returns the following error:

This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory.

This is caused because virtualization inside virtualization gives some problems.

Now it is time for plan number 3 (and also the final plan): run Minikube without a VM (we will still use our Ubuntu VM of course, but Minikube normally runs in combination with a VM, but this can also be skipped).

We will install Docker inside our Ubuntu VM and then run the following command which will start the Minikube cluster:

sudo minikube start --vm-driver=none

Now install kubectl, kubectl will allow us to interact with our Kubernetes cluster

curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.10.0/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/

We check the installation with the following command which will show us the  client and server version information:

sudo kubectl version

And finally, with the following command we can start the Minikube dashboard:

sudo minikube dashboard

Summary

In this part 1, we made all preparations in order to get started with Minikube in part 2. We created a basic Spring Boot application, created a Docker image for it and pushed it to our Docker registry. During installation of Minikube on Windows, we encountered several problems. In the end, we got it working with an Ubuntu VM and we started Minikube by means of Docker instead of its own VM driver. In part 2 we will deploy our application to the Minikube cluster and explore some of the concepts and terminology of Kubernetes.