Dekorate Archives - Piotr's TechBlog https://piotrminkowski.com/tag/dekorate/ Java, Spring, Kotlin, microservices, Kubernetes, containers Sat, 19 Dec 2020 00:03:19 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.1 https://i0.wp.com/piotrminkowski.com/wp-content/uploads/2020/08/cropped-me-2-tr-x-1.png?fit=32%2C32&ssl=1 Dekorate Archives - Piotr's TechBlog https://piotrminkowski.com/tag/dekorate/ 32 32 181738725 Guide to Quarkus on Kubernetes https://piotrminkowski.com/2020/08/10/guide-to-quarkus-on-kubernetes/ https://piotrminkowski.com/2020/08/10/guide-to-quarkus-on-kubernetes/#comments Mon, 10 Aug 2020 15:30:42 +0000 http://piotrminkowski.com/?p=8336 Quarkus is usually described as a Kubernetes-native Java framework. It allows us to automatically generate Kubernetes resources based on the defaults and user-provided configuration. It also provides an extension for building and pushing container images. Quarkus can create a container image and push it to a registry before deploying the application to the target platform. […]

The post Guide to Quarkus on Kubernetes appeared first on Piotr's TechBlog.

]]>
Quarkus is usually described as a Kubernetes-native Java framework. It allows us to automatically generate Kubernetes resources based on the defaults and user-provided configuration. It also provides an extension for building and pushing container images. Quarkus can create a container image and push it to a registry before deploying the application to the target platform. It also provides an extension that allows developers to use Kubernetes ConfigMap as a configuration source, without having to mount them into the pod. We may use fabric8 Kubernetes Client directly to interact with the cluster, for example during JUnit tests.
In this guide, you will learn how to:

  • Use Quarkus Dekorate extension to automatically generate Kubernetes manifests basing on the source code and configuration
  • Build and push images to Docker registry with Jib extension
  • Deploy your application on Kubernetes without any manually created YAML in one click
  • Use Quarkus Kubernetes Config to inject configuration properties from ConfigMap

This guide is the second in series about Quarkus framework. If you are interested in the introduction to building Quarkus REST applications with Kotlin you may refer to my article Guide to Quarkus with Kotlin.

github-logo Source code

The source code with the sample Quarkus applications is available on GitHub. First, you need to clone the following repository: https://github.com/piomin/sample-quarkus-applications.git. Then, you need to go to the employee-service directory. We use the same repository as in my previous article about Quarkus.

1. Dependencies

Quarkus does not implement mechanisms for generating Kubernetes manifests, deploying them on the platform, or building images. It adds some logic to the existing tools. To enable extensions to Dekorate and Jib we should include the following dependencies.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-kubernetes</artifactId>
</dependency>
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-container-image-jib</artifactId>
</dependency>

Jib builds optimized images for Java applications without a Docker daemon, and without deep mastery of Docker best-practices. It is available as plugins for Maven and Gradle and as a Java library. Dekorate is a Java library that makes generating and decorating Kubernetes manifests as simple as adding a dependency to your project. It may generate manifests basing on the source code, annotations, and configuration properties.

2. Preparation

In the first part of my guide to Kotlin, we were running our application in development mode with an embedded H2 database. In this part of the tutorial, we will integrate our application with Postgres deployed on Kubernetes. To do that we first need to change configuration settings for the data source. H2 database will be active only in dev and test mode. The configuration of Postgresql data source would be based on environment variables.


# kubernetes
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://${POSTGRES_HOST}:5432/${POSTGRES_DB}
# dev
%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=password
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb
# test
%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=password
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

3. Configure Kubernetes extension

With Quarkus Kubernetes extension we may customize the behavior of the manifest generator. To do that we need to provide configuration settings with the prefix quarkus.kubernetes.*. There are pretty many options like defining labels, annotations, environment variables, Secret and ConfigMap references, or mounting volumes. First, let’s take a look at the Secret and ConfigMap prepared for Postgres.

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-config
  labels:
    app: postgres
data:
  POSTGRES_DB: quarkus
  POSTGRES_USER: quarkus
  POSTGRES_HOST: postgres
---
apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
  labels:
    app: postgres
data:
  POSTGRES_PASSWORD: YWRtaW4xMjM=

In this fragment of configuration, besides simple label and annotation, we are adding reference to all the keys inside postgres-config and postgres-secret.

quarkus.kubernetes.labels.app-type=demo
quarkus.kubernetes.annotations.app-type=demo
quarkus.kubernetes.env.secrets=postgres-secret
quarkus.kubernetes.env.configmaps=postgres-config

4. Build image and deploy

Before executing build and deploy we need to apply manifest with Postgres. Here’s Deployment definition of Postgres.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:latest
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  key: POSTGRES_DB
                  name: postgres-config
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef:
                  key: POSTGRES_USER
                  name: postgres-config
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: POSTGRES_PASSWORD
                  name: postgres-secret
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: postgredb
      volumes:
        - name: postgredb
          persistentVolumeClaim:
            claimName: postgres-claim

Let’s apply it on Kubernetes together with required ConfigMap, Secret, PersistenceVolume and PersistenceVolumeClaim. All the objects are available inside example repository in the file employee-service/k8s/postgres-deployment.yaml.

$ kubectl apply -f employee-service\k8s\postgresql-deployment.yaml

After deploying Postgres we may proceed to the main task. In order to build a Docker image with the application, we need to enable option quarkus.container-image.build during Maven build. If you also want to deploy and run a container with the application on your local Kubernetes instance you need to enable option quarkus.kubernetes.deploy.

$ clean package -Dquarkus.container-image.build=true -Dquarkus.kubernetes.deploy=true

If your Kubernetes cluster is located on the hosted cloud you should push the image to a remote Docker registry before deployment. To do that we should also activate option quarkus.container-image.push during Maven build. If you do not push to the default Docker registry you have to set parameter quarkus.container-image.registry=gcr.io inside the application.properties file. The only thing I need to set for building images is the following property, which is the same as my login to docker.io site.

quarkus.container-image.group=piomin

After running the required Maven command our application is deployed on Kubernetes. Let’s take a look at what happened during the Maven build. Here’s the fragment of logs during that build. You see that Quarkus extension generated two files kubernetes.yaml and kubernetes.json inside target/kubernetes directory. Then it proceeded to build a Docker image with our application. Because we didn’t specify any base image it takes a default one for Java 11 – fabric8/java-alpine-openjdk11-jre.

quarkus-build-image

Let’s take a look on the Deployment definition automatically generated by Quarkus.

  1. It adds some annotations like port or path to metrics endpoint used by Prometheus to monitor application and enabled scraping. It also adds Git commit id, repository URL, and our custom annotation defined in application.properties.
  2. It adds labels with the application name, version (taken from Maven pom.xml), and our custom label app-type.
  3. It injects Kubernetes namespace name into the container.
  4. It injects the reference to the postgres-secret defined in application.properties.
  5. It injects the reference to the postgres-config defined in application.properties.
  6. The name of the image is automatically created. It is based on Maven artifactId and version.
  7. The definition of liveness and readiness is generated if Maven module quarkus-smallrye-health is present
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations: # (1)
    prometheus.io/path: /metrics
    prometheus.io/port: 8080
    app.quarkus.io/commit-id: f6ae37288ed445177f23291c921c6099cfc58c6e
    app.quarkus.io/vcs-url: https://github.com/piomin/sample-quarkus-applications.git
    app.quarkus.io/build-timestamp: 2020-08-10 - 13:22:32 +0000
    app-type: demo
    prometheus.io/scrape: "true"
  labels: # (2)
    app.kubernetes.io/name: employee-service
    app.kubernetes.io/version: 1.1
    app-type: demo
  name: employee-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: employee-service
      app.kubernetes.io/version: 1.1
  template:
    metadata:
      annotations:
        prometheus.io/path: /metrics
        prometheus.io/port: 8080
        app.quarkus.io/commit-id: f6ae37288ed445177f23291c921c6099cfc58c6e
        app.quarkus.io/vcs-url: https://github.com/piomin/sample-quarkus-applications.git
        app.quarkus.io/build-timestamp: 2020-08-10 - 13:22:32 +0000
        app-type: demo
        prometheus.io/scrape: "true"
      labels:
        app.kubernetes.io/name: employee-service
        app.kubernetes.io/version: 1.1
        app-type: demo
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE # (3)
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        envFrom:
        - secretRef: # (4)
            name: postgres-secret
        - configMapRef: # (5)
            name: postgres-config
        image: piomin/employee-service:1.1 # (6)
        imagePullPolicy: IfNotPresent
        livenessProbe: # (7)
          failureThreshold: 3
          httpGet:
            path: /health/live
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
        name: employee-service
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /health/ready
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
      serviceAccount: employee-service

Once the image has been built it is available in local registry. Quarkus automatically deploy it to the current cluster using already generated Kubernetes manifests.

quarkus-build-maven

Here’s the list of pods in default namespace.

quarkus-pods

5. Using Kubernetes Config extension

With Kubernetes Config extension you can use ConfigMap as a configuration source, without having to mount them into the pod with the application. To use that extension we need to include the following Maven dependency.


<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-kubernetes-config</artifactId>
</dependency>

This extension works directly with Kubernetes API using fabric8 KubernetesClient. That’s why we should set the proper permissions for ServiceAccount. Fortunately, all the required configuration is automatically generated by Quarkus Kubernetes extension. The RoleBinding object is appied automatically if quarkus-kubernetes-config module is present.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  annotations:
    prometheus.io/path: /metrics
    prometheus.io/port: 8080
    app.quarkus.io/commit-id: a5da459af01637657ebb0ec3a606eb53d13b8524
    app.quarkus.io/vcs-url: https://github.com/piomin/sample-quarkus-applications.git
    app.quarkus.io/build-timestamp: 2020-08-10 - 14:25:20 +0000
    app-type: demo
    prometheus.io/scrape: "true"
  labels:
    app.kubernetes.io/name: employee-service
    app.kubernetes.io/version: 1.1
    app-type: demo
  name: employee-service:view
roleRef:
  kind: ClusterRole
  apiGroup: rbac.authorization.k8s.io
  name: view
subjects:
- kind: ServiceAccount
  name: employee-service

Here’s our example ConfigMap that contains a single property property1.


apiVersion: v1
kind: ConfigMap
metadata:
  name: employee-config
data:
  application.properties: |-
    property1=one

The same property is defined inside application.properties available on the classpath, but there it has a different value.

property1=test

Before deploying a new version of application we need to add the following properties. First of them enables Kubernetes ConfigMap injection, while the second specifies the name of injected ConfigMap.

quarkus.kubernetes-config.enabled=true
quarkus.kubernetes-config.config-maps=employee-config

Finally we just need to implement a simple endpoint that injects and returns configuration property.

@ConfigProperty(name = "property1")
lateinit var property1: String

@GET
@Path("/property1")
fun property1(): String = property1

The properties obtained from the ConfigMap have a higher priority than any properties of the same name that are found in application.properties available on the classpath. Let’s test it.

quarkus-config

The post Guide to Quarkus on Kubernetes appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2020/08/10/guide-to-quarkus-on-kubernetes/feed/ 3 8336
Simplify development on Kubernetes with Dekorate, Skaffold and Spring Boot https://piotrminkowski.com/2020/06/08/simplify-development-on-kubernetes-with-dekorate-skaffold-and-spring-boot/ https://piotrminkowski.com/2020/06/08/simplify-development-on-kubernetes-with-dekorate-skaffold-and-spring-boot/#comments Mon, 08 Jun 2020 10:42:12 +0000 http://piotrminkowski.com/?p=8091 Although Kubernetes is a great solution for managing containerized applications, scaling, and automating deployment, a local development on it may be a painful experience. A typical workflow includes several steps like checking the functionality of the code locally, building and tagging a docker image, creating a deployment configuration, and finally deploying everything on Kubernetes. In […]

The post Simplify development on Kubernetes with Dekorate, Skaffold and Spring Boot appeared first on Piotr's TechBlog.

]]>
Although Kubernetes is a great solution for managing containerized applications, scaling, and automating deployment, a local development on it may be a painful experience. A typical workflow includes several steps like checking the functionality of the code locally, building and tagging a docker image, creating a deployment configuration, and finally deploying everything on Kubernetes. In this article, I’m going to show how to use some tools together to simplify that process.
I have already described how to use such tools as Skaffold and Jib for local Java and Spring Boot development on Kubernetes in one of my previous articles Local Java Development on Kubernetes. I have also already described what is Dekorate framework and how to use it together with Spring Boot for building and deploying applications on OpenShift in the article Deploying Spring Boot application on OpenShift with Dekorate. Using all these tools together may be a great combination! Let’s proceed with the details.

Example

The sample Spring Boot application prepared for usage with Skaffold is available on my GitHub repository https://github.com/piomin/sample-springboot-dekorate-istio.git. Assuming you already have Skaffold, to deploy it on Kubernetes you just need to execute command skaffold dev --port-forward on the root directory.

Dependencies

We are using version 2.2.7 of Spring Boot with JDK 11. To enable generating manifests during Maven build with Dekorate we need to include library kubernetes-spring-starter. To use Dekorate annotations in our application we should include kubernetes-annotations dependency.

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.2.7.RELEASE</version>
</parent>

<properties>
   <java.version>11</java.version>
</properties>

<dependencies>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>
   <dependency>
      <groupId>io.dekorate</groupId>
      <artifactId>kubernetes-spring-starter</artifactId>
      <version>0.12.2</version>
   </dependency>
   <dependency>
      <groupId>io.dekorate</groupId>
      <artifactId>kubernetes-annotations</artifactId>
      <version>0.12.2</version>
   </dependency>
</dependencies>

To enable Jib we just need to include its Maven plugin. Of course, we also need to have Spring Boot Maven Plugin responsible for generating uber jar with the application.


<build>
   <plugins>
      <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId>
         <executions>
            <execution>
               <goals>
                  <goal>build-info</goal>
               </goals>
            </execution>
         </executions>
      </plugin>
      <plugin>
         <groupId>com.google.cloud.tools</groupId>
         <artifactId>jib-maven-plugin</artifactId>
         <version>2.3.0</version>
      </plugin>
   </plugins>
</build>

Integrating Skaffold with Dekorate

Dekorate is generating Kubernetes manifests during the compilation phase of Maven build. The default target directory for those manifests is target/classes/META-INF/dekorate. I didn’t find any description in Dekorate documentation on how to override that path, but it is not a problem for us. We may as well set that directory in Skaffold configuration.
Dekorate is generating manifests in two variants: YAML and JSON. During tests, I have some problems with the YAML manifest generated by Dekorate, so I decided to switch to JSON. It worked perfectly fine. Here’s our configuration in skaffold.yaml. As you see I’m enabling integration with Jib and adding a new search path for Kubernetes manifests target/classes/META-INF/dekorate/*.json in deploy section. I have also left a default path for manifests which is k8s directory.

apiVersion: skaffold/v2alpha1
kind: Config
build:
  artifacts:
    - image: minkowp/sample-springboot-dekorate-istio
      jib: {}
  tagPolicy:
    gitCommit: {}
deploy:
  kubectl:
    manifests:
      - target/classes/META-INF/dekorate/*.json
      - k8s/*.yaml

Using Dekorate annotations with Spring Boot

As you probably know Spring Boot is an annotation-based framework. So the most suitable way for developer to declare resources on a target platform is of course through annotations. In that case Dekorate is a perfect solution. It has built-in support for Spring Boot – like a detection of health check endpoints configured using Spring Boot Actuator. The rest may be configured using its annotations.
The following code snippet illustrates how to use Dekorate annotations. First you should annotate the main class with @KubernetesApplication. We have pretty many options here, but I show you the most typical features. We can set the number of pods for a single Deployment (replicas). We may also inject environment variable to the container or provide a reference to the property in ConfigMap. In that case we are using property propertyFromMap inside sample-configmap ConfigMap. We can also expose our application through Service. The exposed port is 8080. Each Deployment may be labelled using many labels declared inside labels field. It is possible to set some JVM parameters for our application using @JvmOptions annotation.

@SpringBootApplication
@KubernetesApplication(replicas = 2,
        envVars = { @Env(name = "propertyEnv", value = "Hello from env!"),
                    @Env(name = "propertyFromMap", value = "property1", configmap = "sample-configmap") },
        expose = true,
        ports = @Port(name = "http", containerPort = 8080),
        labels = @Label(key = "version", value = "v1"))
@JvmOptions(server = true, xmx = 256, gc = GarbageCollector.SerialGC)
public class DekorateIstioApplication {

    public static void main(String[] args) {
        SpringApplication.run(DekorateIstioApplication.class, args);
    }

}

A sample ConfigMap should be placed inside k8s directory. Assuming that it has not deployed on Kubernetes yet, it is the only one manifest we need to create manually.

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-configmap
data:
  property1: I'm ConfigMap property

That process is completely transparent for us, but just for clarification let’s take a look on the manifest generated by Dekorate. As you see instead of 114 lines of YAML code we just had to declare two annotations with some fields.

apiVersion: v1
kind: Service
metadata:
  annotations:
    app.dekorate.io/commit-id: 25e1ac563793a33d5229ef860a5a1ae169aa62ec
    app.dekorate.io/vcs-url: https://github.com/piomin/sample-springboot-dekorate-istio.git
  labels:
    app.kubernetes.io/name: sample-springboot-dekorate-istio
    version: v1
    app.kubernetes.io/version: 1.0
  name: sample-springboot-dekorate-istio
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  selector:
    app.kubernetes.io/name: sample-springboot-dekorate-istio
    version: v1
    app.kubernetes.io/version: 1.0
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    app.dekorate.io/commit-id: 25e1ac563793a33d5229ef860a5a1ae169aa62ec
    app.dekorate.io/vcs-url: https://github.com/piomin/sample-springboot-dekorate-istio.git
  labels:
    app.kubernetes.io/name: sample-springboot-dekorate-istio
    version: v1
    app.kubernetes.io/version: 1.0
  name: sample-springboot-dekorate-istio
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: sample-springboot-dekorate-istio
      version: v1
      app.kubernetes.io/version: 1.0
  template:
    metadata:
      annotations:
        app.dekorate.io/commit-id: 25e1ac563793a33d5229ef860a5a1ae169aa62ec
        app.dekorate.io/vcs-url: https://github.com/piomin/sample-springboot-dekorate-istio.git
      labels:
        app.kubernetes.io/name: sample-springboot-dekorate-istio
        version: v1
        app.kubernetes.io/version: 1.0
    spec:
      containers:
      - env:
        - name: KUBERNETES_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: propertyEnv
          value: Hello from env!
        - name: propertyFromMap
          valueFrom:
            configMapKeyRef:
              key: property1
              name: sample-configmap
              optional: false
        - name: JAVA_OPTS
          value: -Xmx=256M -XX:+UseSerialGC -server
        - name: JAVA_OPTIONS
          value: -Xmx=256M -XX:+UseSerialGC -server
        image: minkowp/sample-springboot-dekorate-istio:1.0
        imagePullPolicy: IfNotPresent
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /actuator/info
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
        name: sample-springboot-dekorate-istio
        ports:
        - containerPort: 8080
          name: http
          protocol: TCP
        readinessProbe:
          failureThreshold: 3
          httpGet:
            path: /actuator/health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 0
          periodSeconds: 30
          successThreshold: 1
          timeoutSeconds: 10
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  labels:
    app.kubernetes.io/name: sample-springboot-dekorate-istio
    version: v1
    app.kubernetes.io/version: 1.0
  name: sample-springboot-dekorate-istio
spec:
  rules:
  - host: ""
    http:
      paths:
      - backend:
          serviceName: sample-springboot-dekorate-istio
          servicePort: 8080
        path: /

Here’s the structure of source files in our application.

development-on-kubernetes-dekorate-skaffold-springboot-sourcecode

Deploy Spring Boot on Kubernetes

After executing command skaffold dev --port-forward Skaffold is continuously listening for the changes in the source code. Thanks to port-forward option we may easily test it on port 8080. Here’s the status of our Deployment on Kubernetes.

development-on-kubernetes-dekorate-skaffold-springboot-deployment

There is a simple endpoint that test properties injected to the container and from ConfigMap.

@RestController
@RequestMapping("/sample")
public class SampleController {

    @Value("${propertyFromMap}")
    String propertyFromMap;
    @Value("${propertyEnv}")
    String propertyEnv;

    @GetMapping("/properties")
    public String getProperties() {
        return "propertyFromMap=" + propertyFromMap + ", propertyEnv=" + propertyEnv;
    }

}

We can easily verify it.

development-on-kubernetes-dekorate-skaffold-springboot-curl

Summary

Spring Boot development on Kubernetes may be a pleasure when using the right tools for it. Dekorate is a very interesting option for developers in connection to annotation-based microservices frameworks. Using it together with Skaffold may additionally speed-up your local development of Spring Boot applications.

The post Simplify development on Kubernetes with Dekorate, Skaffold and Spring Boot appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2020/06/08/simplify-development-on-kubernetes-with-dekorate-skaffold-and-spring-boot/feed/ 1 8091
Deploying Spring Boot Application on OpenShift with Dekorate https://piotrminkowski.com/2019/10/01/deploying-spring-boot-application-on-openshift-with-dekorate/ https://piotrminkowski.com/2019/10/01/deploying-spring-boot-application-on-openshift-with-dekorate/#comments Tue, 01 Oct 2019 07:51:25 +0000 https://piotrminkowski.wordpress.com/?p=7254 More advanced deployments to Kubernetes or OpenShift are a bit troublesome for developers. In comparison to Kubernetes OpenShift provides S2I (Source-2-Image) mechanism, which may help reduce the time required for preparation of application deployment descriptors. Although S2I is quite useful for developers, it solves only simple use cases and does not provide a unified approach […]

The post Deploying Spring Boot Application on OpenShift with Dekorate appeared first on Piotr's TechBlog.

]]>
More advanced deployments to Kubernetes or OpenShift are a bit troublesome for developers. In comparison to Kubernetes OpenShift provides S2I (Source-2-Image) mechanism, which may help reduce the time required for preparation of application deployment descriptors. Although S2I is quite useful for developers, it solves only simple use cases and does not provide a unified approach to building deployment configuration from a source code. Dekorate (https://dekorate.io), the recently created open-source project, tries to solve that problem. This project seems to be very interesting. It appears to be confirmed by RedHat, which has already announced a decision on including Dekorate to Red Hat OpenShift Application Runtimes as a “Tech Preview”.

You can read more about it in this article: https://developers.redhat.com/blog/2019/08/15/how-to-use-dekorate-to-create-kubernetes-manifests.

How does it work?

Dekorate is a library that defines a set of annotation processors used for generating and decorating Kubernetes or OpenShift manifests. In fact, you just need to annotate your application main class properly and Dekorate will take care of everything else. To use this library you only have to include it in your Maven pom.xml as shown below.

<dependency>
   <groupId>io.dekorate</groupId>
   <artifactId>openshift-spring-starter</artifactId>
   <version>0.8.2</version>
</dependency>

The starter contains not only annotations and annotation processors that may be used on your application, but also support for generation during Maven build, which is executed during the compile phase. To enable Decorate during build you need to set property dekorate.build to true. You can also enable deployment by setting property dekorate.deploy to true as shown below.

$ mvn clean install -Ddekorate.build=true -Ddekorate.deploy=true

Dekorate supports OpenShift S2I. It generates ImageStream for builder and target application, and also BuildConfig resource. If you enable deploy mode it also generates deployment config with required resources. Here’s the screen with logs from Maven build executed on my local machine.

spring-boot-decorate-openshift-1

In this case Dekorate is generating OpenShift manifest files and saves them inside directory target/classes/META-INF/dekorate/, and then performing deployment on my instance of Minishift available under virtual address 192.168.99.100.
Unfortunately, we have to modify generated BuildConfig for our convenience. So instead of source type Binary we will just declare Git source repository address.

apiVersion: "build.openshift.io/v1"
kind: "BuildConfig"
metadata:
  labels:
    app: "sample-app"
    version: "1.1.0"
    group: "minkowp"
  name: "sample-app"
spec:
  output:
    to:
      kind: "ImageStreamTag"
      name: "sample-app:1.1.0"
  source:
    git:
      uri: 'https://github.com/piomin/sample-app.git'
    type: Git
  strategy:
    sourceStrategy:
      from:
        kind: "ImageStreamTag"
        name: "s2i-java:2.3"

In order to apply the changes execute the following commands:

$ oc delete bc sample-app
$ oc apply -f build-config-dekorate.yaml

Customization

If you have Spring Boot applications it is possible to completely bypass annotations by utilizing already-existing, framework-specific metadata. To customize the generated manifests you can add dekorate properties to your application.yml or application.properties descriptors. I have some problems running these modes with Dekorate, so I avoided it. However, I prefer using annotations on the code, so I prepared the following configuration, which has been successfully generated:

@SpringBootApplication
@OpenshiftApplication(replicas = 2, expose = true, envVars = {
        @Env(name="sample-app-config", configmap = "sample-app-config")
})
@JvmOptions(xms = 128, xmx = 256, heapDumpOnOutOfMemoryError = true)
@EnableSwagger2
public class SampleApp {

    public static void main(String[] args) {
        SpringApplication.run(SampleApp.class, args);
    }
   
    // ... REST OF THE CODE
}

In the fragment of code visible above we have declared some useful settings for the application. First, it should be run in two pods (replicas=2). It also should be exposed outside a cluster using OpenShift route (expose=true). The application uses Kubernetes ConfigMap as a source of dynamically managed configuration settings. By annotating the main class with @JvmOptions we may customize the behavior of JVM running on the container, for example by setting maximum heap memory consumption. Here’s the definition of ConfigMap created for the test purpose:

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-app-config
  namespace: myproject
data:
  showAddress: 'true'
  showContactInfo: 'true'
  showSocial: 'false'

Sample Application

The sample application code snippet is available on GitHub under repository https://github.com/piomin/sample-app.git. This very basic Spring Boot web application exposes a simple REST API with some monitoring endpoints included with Spring Boot Actuator and API documentation generated using Swagger.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

Dekorate is able to automatically detect existence of Spring Boot Actuator dependency and based on it generate OpenShift readiness and liveness healthchecks definitions as shown below. By default it sets timeout on 10 seconds, and period on 30 seconds. We can override a default behaviour using fields liveness and readiness of @OpenshiftApplication.

dekorate-3

The controller class provides implementation for some basic CRUD REST operations. It also injects some environment variables taken from ConfigMap sample-app-config. Based on their values it decides whether to show or not to show additional person parameters like address, contact information or social links. Here’s an implementation of PersonController:

@RestController
@RequestMapping("/persons")
public class PersonsController {

    private static final Logger LOGGER = LoggerFactory.getLogger(PersonsController.class);

    @Autowired
    PersonRepository repository;

    @Value(value = "${showAddress:false}")
    boolean showAddress;
    @Value(value = "${showContactInfo:false}")
    boolean showContactInfo;
    @Value(value = "${showSocial:false}")
    boolean showSocial;

    @PostMapping
    public Person add(@RequestBody Person person) {
        LOGGER.info("Person add: {}", person);
        return repository.add(person);
    }

    @GetMapping("/{id}")
    public Person findById(@PathVariable("id") Integer id) {
        LOGGER.info("Person find: id={}", id);
        return hidePersonParams(repository.findById(id));
    }

    @GetMapping
    public List<Person> findAll() {
        LOGGER.info("Person find");
        return repository.findAll().stream().map(this::hidePersonParams).collect(Collectors.toList());
    }

    private Person hidePersonParams(Person person) {
        if (!showAddress)
            person.setAddress(null);
        if (!showContactInfo)
            person.setContact(null);
        if (!showSocial)
            person.setSocial(null);
        return person;
    }
}

Here’s an implementation of a model class. I used the Lombok library for getters, setters, and constructor generation.

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {

    private Integer id;
    private String name;
    private int age;
    private Gender gender;
    private Address address;
    private Contact contact;
    private Social social;

}

Here’s a definition of ConfigMap sample-app-config on my instance of Minishift.

spring-boot-decorate-openshift-4

Deployment

The current version of Dekorate uses fabric/s2i-java in version 2.3 as a builder image. It creates ImageStream without any tags, so we have the Docker image from docker.io manually by executing the following command:


$ oc import-image fabric/s2i-java:2.3

This commands forces to download some additional versions of a builder image including the newest version for JDK 11. The library still uses the older version of s2i-java image although we have declared JDK 11 as a default version in pom.xml. In this example we can see that Dekorate has still some things to improve 🙂

dekorate-5

Assuming we have already performed all the required steps described in the previous sections of this article we may start OpenShift build by running the following command:

$ oc start-build sample-app

The build should be finished successfully as shown below.

dekorate-6

After successful build the new image is pushed to OpenShift registry. It triggers the start of a new deployment of our application. As you see on the picture below our application is started in two instances, and the route is created automatically.

dekorate-7

We can also take a look at environment settings. JVM options have been set, and ConfigMap has been assigned to the deployment config.

spring-boot-decorate-openshift-8

Finally we may test the sample application. The Swagger API documentation is available under address http://sample-app-myproject.192.168.99.100.nip.io/swagger-ui.html.

spring-boot-decorate-openshift-9

Conclusion

I think it is a great idea to use Java annotations for customizing application deployment on Kubernetes/OpenShift. Using Dekorate with frameworks based on annotation processing like Spring Boot greatly simplifies the deployment process for developers. Although the library is in the early stage of development and still has some things to improve I definitely recommend using it. In this article, I’m trying to give you some tips for a quick start. Enjoy 🙂

The post Deploying Spring Boot Application on OpenShift with Dekorate appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2019/10/01/deploying-spring-boot-application-on-openshift-with-dekorate/feed/ 2 7254