In this scenario we will demonstrate the Micronaut Kubernetes Discovery module.
- 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.
https://micronaut-projects.github.io/micronaut-kubernetes/snapshot/guide/
MK is a module that integrates kubernetes, -> nav to docs
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
At first let's create simple microservice that we will deploy
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
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
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.
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
To demonstrate service discovery we will need to create new microservice that will be consuming the ping endpoint
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
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
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