Skip to content

Instantly share code, notes, and snippets.

@pgressa
Last active March 23, 2021 15:59
Show Gist options
  • Save pgressa/e95a9a81f8de8d9fca91515654222208 to your computer and use it in GitHub Desktop.
Save pgressa/e95a9a81f8de8d9fca91515654222208 to your computer and use it in GitHub Desktop.
Micronaut Kubernetes + Dekorate scenario

Intro

In this scenario we will demonstrate the Micronaut Kubernetes Discovery module.

Prerequisites:

  • kubernetes cluster
  • docker
  • java
  • dockerhub account

Medailon:

First,tThanks for the invitation Oleg. My name is Palo Gressa, and I work This is the first time I'm on

PALO WILL SAY WHAT ARE WE GOING TO DO:

To ease demonstration we will use Kind for setup of local Kubernetes cluster and Dekorate to produce Kubernetes resources.

Kind -> Q:what is? A: kind is a tool for running local Kubernetes clusters using Docker container “nodes”.

Dekorate -> what is? A: Dekorate is a Java compile-time generators and using annotations (that's from where the dekorate is) it generates kubernetes manifests.. but not just kubernetes, dekorate has wide support of dekorations.. but all of them are related to kubernetes.

Micronaut Kubernetes

https://micronaut-projects.github.io/micronaut-kubernetes/snapshot/guide/

MK is a module that integrates kubernetes, -> nav to docs

highlights

The key features are

  • service discovery , that supports headless services, externalname services. this module gives you automatomatically decent service discovery which can be extended by manual configuration

  • configuredion discovery WHAT we will do today, we will basically load configuration to application from the K8s cm

  • secrets discovery

Also we have integrated official k8s sdk so you can inject k8s sdk api objects as beans from the scratch

Create and deploy microservice

At first let's create simple microservice that we will deploy

Application

1] Create demo microservice

mn create-app com.example.demo

Comment: this command creates basic file structure, it;s good starting point when writing new application because it reflects latest changes into micronaut

2] Let’s create controller

mn create-controller Example

Comment: by this we will add simple controller and test.

4] Test controller Run tests ./gradlew test

Comment: see one tests there

5] Run locally

./gradlew run

6] Call endpoint

curl localhost:8080/example

Build and push docker image

Comment:

  • docker I believe we don't have to today introduce docker. so briefly ists os leve virutalisation

Comment: Micronaut has docker support baked into the maven graadle plugins. Applications are built as layered JARs using the buildLayers task ensuring optimized Docker images for Java application, which means the layers bring some speed optimisation when building packages.

1] to simple docker image

  • ./gradlew dockerBuild

Comment: just run dockerBuild, and it will be build

2] we want to push to docker registry, what we do want

dockerBuild{
    images = ["pgressa/demo:${project.version}"]
}

3] Push

./gradle dockerPush

Add Dekorate so we can generate Kubernetes manifests

Comment: Dekorate is a collection of compile time generators and decorators of Kubernetes manifests. It makes generating and decorating Kubernetes manifests as simple as adding a dependency to your project. No editing of xml, yml or json is required.

1] Start by adding dependencies

annotationProcessor("io.dekorate:kubernetes-annotations:2.1.1")

implementation("io.dekorate:kubernetes-annotations:2.1.1")

!!!!
implementation("io.micronaut:micronaut-management")

!!!!! version = '0.1' - there's PR already created for !!!!!

2] refresh gradle deps

3] Add annotation

Comment probes: You know it’s best practice that every microservice deployed in k9s has exposed probes that basically allow k8s to decide whether to route traffinf to the application that’s readiness probe and whether to restart the container if the app isnot responsive, that’s liveness probe. So let’s add those

@KubernetesApplication(
    name = "demo",
    labels = @Label(key = "app", value = "producer-service"),
    ports = @Port(name = "http", containerPort = 8080),
    imagePullPolicy = ImagePullPolicy.Always
    livenessProbe = @Probe(httpActionPath = "/health/liveness", initialDelaySeconds = 5),
    readinessProbe = @Probe(httpActionPath = "/health/readiness", initialDelaySeconds = 5)
)
@DockerBuild(group = "pgressa", name = "demo")

ALso add DockerBuild to reflect that we're using docker image from registry

3] Generate resources ./gradlew compileJava

2] Configure docker registry

  • change in build gradle
version = '0.1'

4] Deploy

kubectl apply -f build/classes/java/main/META-INF/dekorate/kubernetes.yml

5] check kubernetes

kubectl get all

6] forward port to call the service

kubectl port-forward producer-service-8468d67587-f5mzb 8080:8080

7] call service

curl localhost:8080/example

Depoyment -> A Kubernetes Deployment is used to tell Kubernetes how to create or modify instances of the pods that hold a containerized application. Deployments can scale the number of replica pods, enable rollout of updated code in a controlled manner, or roll back to an earlier deployment version if necessary.

SERVICE -> Service An abstract way to expose an application running on a set of Pods as a network service. With Kubernetes you don't need to modify your application to use an unfamiliar service discovery mechanism. Kubernetes gives Pods their own IP addresses and a single DNS name for a set of Pods, and can load-balance across them.

Motivation Kubernetes Pods are created and destroyed to match the state of your cluster. Pods are nonpermanent resources. If you use a Deployment to run your app, it can create and destroy Pods dynamically.

CONFIGURATION DISCOVERY

1] Add dependency first

implementation("io.micronaut.kubernetes:micronaut-kubernetes-discovery-client")

2] Extend the controller with abouter resource Comment: Refreshable

@Refreshable 
    @Inject
    ApplicationContext context;
    @Get("/config/{key}")
    public String config(String key) {
        return context.get(key, String.class).orElse("NOTHING");
    }

3] Configure bootstrap.yml

micronaut:
  application:
    name: demo
  config-client:
    enabled: true

4] Before we will deploy the stuff, let's prepare config map COMMENT:config map

edit cm.yaml

enemies:
  cheat:
    level: noGoodRotten

Create config map

kubectl create configmap demo-config-map --from-file props.yml

Check that out

kubectl get cm demo-config-map -o yaml

5] redeploy the service, b/c we changed only code, simple deployment reload will be enough

kubectl rollout restart deployment demo

6] forward ports

kubectl port-forward service/demo 8080:80
curl localhost:8080/example/config/enemies.cheat.level

SERVICE DISCOVERY

To demonstrate service discovery we will need to create new microservice that will be consuming the ping endpoint

Create client application

1] Create app structure

mn create-app com.example.client

2] Create DemoClient class

@Client(id = "demo")
public interface DemoClient{
    @Get("/example/config/{key}")
    String config(String key);
}
}

2] Let’s add DemoController

@Controller(id = "demo")
public class DemoController{

   private final DemoClient demoClient;
   
   public DemoController(DemoClient demoClient){
       this.demoClient = demoClient;
   }
   
   @Get("/config/{key}")
    String config(String key) {
        return client.config(key);
    }
   

##] Add dekorate

This is now mostly the same thing we did for the first microservice

1] So add dependencies

annotationProcessor("io.dekorate:kubernetes-annotations")
implementation("io.dekorate:kubernetes-annotations")

2] Add also management dependency

implementation("io.micronaut:micronaut-management")

3] Annotate class to generate manifests @KubernetesApplication( name = "consumer-service", labels = @Label(key="app", value="consumer-service"), ports = @Port(name="http", containerPort = 8080), livenessProbe = @Probe(httpActionPath = "/health/liveness", initialDelaySeconds = 5), readinessProbe = @Probe(httpActionPath = "/health/readiness", initialDelaySeconds = 5), imagePullPolicy = ImagePullPolicy.Always )

4] Add docker

version = 0.1

dockerBuild{
    images = ["pgressa/consumer-service:${project.version}"]
}

5] Buidl and push image

/gradlew dockerPush

4] briefly build docker image and deploy service

kubectl apply -f build/classes/java/main/META-INF/dekorate/kubernetes.yml

5] port forward

kubectl port-forward pod 8080:8080

6] Call the service

curl lcalhost:8080/

7] So now we have other service but if we will call the service endpoint, it won’t be called b/c we didn’t added the micronaut kubernetes support yet

Add Micronaut Kubernetes Discovery

now leet’s add Micronaut Kubernetes Discovery module

1] Add dependency first

implementation("io.micronaut.kubernetes:micronaut-kubernetes-discovery-client")

2] build and push docker image

./gradlew dockerPush

3] Reload the deployment

kubectl rollout restart deployment consumer-service

4] port foward and call

WEAK AREAS

  • build.gradle version needs to be added there without " otherwise it breaks the dekorate generated resources
  • dekorate's generated image contains pgressa as user (organisation), that's why we're pushing the docker image to registry, otherwise dekorate behaves oddly. Also if we wouldnt push docker image, we would have to always load the image into kind
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment