In this article, you can read more about the Quarkus code guide I tried out, related to the following topic:
- The ability to automatically generate Kubernetes resources by Quarkus
The guide covers generating and deploying Kubernetes resources based on sane defaults and user supplied configuration. In this article, I will focus on using custom labels and annotations.
Quarkus guide “Quarkus – Kubernetes extension”
From the Quarkus guide “Building a Native Executable”, part “What’s next?”, I clicked on the link “deployment to Kubernetes and OpenShift”.
[https://quarkus.io/guides/building-native-image#whats-next]
Quarkus offers the ability to automatically generate Kubernetes resources based on sane defaults and user-supplied configuration using dekorate. It currently supports generating resources for vanilla Kubernetes, OpenShift and Knative.
[https://quarkus.io/guides/deploying-to-kubernetes]
One of the prerequisites is access to a Kubernetes cluster (Minikube is a viable option). In my demo environment I had K3s (lightweight certified Kubernetes distribution) installed, so this is fine by me.
[https://technology.amis.nl/2020/08/17/quarkus-supersonic-subatomic-java-setting-up-a-demo-environment-using-vagrant-and-oracle-virtualbox/]
Although this guide uses a new Quarkus project (including the Kubernetes and Jib extensions), I continued this guide with the code in my existing “getting-started” project and I used (as described in my previous article) the Docker container-image extension (quarkus-container-image-docker) instead of the Jib extension (quarkus-container-image-jib).
[https://technology.amis.nl/2020/09/07/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part2/]
The extension quarkus-container-image-docker is using the Docker binary and the generated Dockerfiles under src/main/docker in order to perform Docker builds.
[https://quarkus.io/guides/container-image]
I did have to add the Kubernetes extension to my project. I used vagrant ssh to connect into the running VM. Next, in order to add the extension, I used the following command on the Linux Command Prompt:
cd /vagrant/applications/getting-started ./mvnw quarkus:add-extension -Dextensions="kubernetes"
With the following output:
[INFO] Scanning for projects…
[INFO]
[INFO] ———————-< org.acme:getting-started >———————-
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] ——————————–[ jar ]———————————
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:add-extension (default-cli) @ getting-started —
✅ Extension io.quarkus:quarkus-kubernetes has been installed
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 4.190 s
[INFO] Finished at: 2020-09-05T12:54:58Z
[INFO] ————————————————————————
vagrant@ubuntu-bionic:/vagrant/applications/getting-started$
In the pom.xml this added the following dependency:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-kubernetes</artifactId> </dependency>
By adding this dependency, Quarkus enables the generation of Kubernetes manifests each time we perform a build while also enabling the build of a container image using Docker.
Next, I followed the instructions in the guide.
Generation of Kubernetes manifests
In order to package the Quarkus project, I used the following command on the Linux Command Prompt:
./mvnw package
With the following output:
[INFO] Scanning for projects... [INFO] [INFO] ----------------------< org.acme:getting-started >---------------------- [INFO] Building getting-started 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-kubernetes/1.7.0.Final/quarkus-kubernetes-1.7.0.Final.pom … Downloaded from central: https://repo.maven.apache.org/maven2/io/dekorate/dekorate-dependencies/0.12.7/dekorate-dependencies-0.12.7.jar (20 MB at 1.0 MB/s) [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ getting-started --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 4 resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ getting-started --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:prepare-tests (default) @ getting-started --- [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ getting-started --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /vagrant/applications/getting-started/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ getting-started --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:3.0.0-M5:test (default-test) @ getting-started --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.acme.getting.started.GreetingResourceTest 2020-09-05 13:02:52,719 INFO [io.quarkus] (main) Quarkus 1.7.0.Final on JVM started in 4.424s. Listening on: http://0.0.0.0:8081 2020-09-05 13:02:52,723 INFO [io.quarkus] (main) Profile test activated. 2020-09-05 13:02:52,723 INFO [io.quarkus] (main) Installed features: [cdi, kubernetes, resteasy] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 13.024 s - in org.acme.getting.started.GreetingResourceTest 2020-09-05 13:02:56,449 INFO [io.quarkus] (main) Quarkus stopped in 0.034s [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ getting-started --- [INFO] Building jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT.jar [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:build (default) @ getting-started --- [INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final [WARNING] [io.quarkus.kubernetes.deployment.KubernetesDeployer] A Kubernetes deployment was requested, but the container image to be built will not be pushed to any registry because "quarkus.container-image.registry" has not been set. The Kubernetes deployment will only work properly if the cluster is using the local Docker daemon. For that reason 'ImagePullPolicy' is being force-set to 'IfNotPresent'. [WARNING] Error reading service account token from: [/var/run/secrets/kubernetes.io/serviceaccount/token]. Ignoring. [WARNING] Error reading service account token from: [/var/run/secrets/kubernetes.io/serviceaccount/token]. Ignoring. [INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building thin jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner.jar [WARNING] [io.quarkus.kubernetes.deployment.KubernetesProcessor] No registry was set for the container image, so 'ImagePullPolicy' is being force-set to 'IfNotPresent'. [INFO] Checking for existing resources in: /vagrant/applications/getting-started/src/main/kubernetes. [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 7361ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 57.983 s [INFO] Finished at: 2020-09-05T13:03:05Z [INFO] ------------------------------------------------------------------------ vagrant@ubuntu-bionic:/vagrant/applications/getting-started$
You will notice amongst the other files that are created, two files named kubernetes.json and kubernetes.yml in the target/kubernetes/ directory.
If you look at either file you will see that it contains a Kubernetes ServiceAccount, Service and a Deployment.
Below, you can see the content of the target/kubernetes/kubernetes.json Kubernetes manifest, provided by the Quarkus project packaging:
{ "apiVersion" : "v1", "kind" : "ServiceAccount", "metadata" : { "annotations" : { "app.quarkus.io/build-timestamp" : "2020-09-05 - 13:03:02 +0000" }, "labels" : { "app.kubernetes.io/name" : "getting-started", "app.kubernetes.io/version" : "1.0-SNAPSHOT" }, "name" : "getting-started" } }{ "apiVersion" : "v1", "kind" : "Service", "metadata" : { "annotations" : { "app.quarkus.io/build-timestamp" : "2020-09-05 - 13:03:02 +0000" }, "labels" : { "app.kubernetes.io/name" : "getting-started", "app.kubernetes.io/version" : "1.0-SNAPSHOT" }, "name" : "getting-started" }, "spec" : { "ports" : [ { "name" : "http", "port" : 8090, "targetPort" : 8090 } ], "selector" : { "app.kubernetes.io/name" : "getting-started", "app.kubernetes.io/version" : "1.0-SNAPSHOT" }, "type" : "ClusterIP" } }{ "apiVersion" : "apps/v1", "kind" : "Deployment", "metadata" : { "annotations" : { "app.quarkus.io/build-timestamp" : "2020-09-05 - 13:03:02 +0000" }, "labels" : { "app.kubernetes.io/name" : "getting-started", "app.kubernetes.io/version" : "1.0-SNAPSHOT" }, "name" : "getting-started" }, "spec" : { "replicas" : 1, "selector" : { "matchLabels" : { "app.kubernetes.io/name" : "getting-started", "app.kubernetes.io/version" : "1.0-SNAPSHOT" } }, "template" : { "metadata" : { "annotations" : { "app.quarkus.io/build-timestamp" : "2020-09-05 - 13:03:02 +0000" }, "labels" : { "app.kubernetes.io/name" : "getting-started", "app.kubernetes.io/version" : "1.0-SNAPSHOT" } }, "spec" : { "containers" : [ { "env" : [ { "name" : "KUBERNETES_NAMESPACE", "valueFrom" : { "fieldRef" : { "fieldPath" : "metadata.namespace" } } } ], "image" : "vagrant/getting-started:1.0-SNAPSHOT", "imagePullPolicy" : "IfNotPresent", "name" : "getting-started", "ports" : [ { "containerPort" : 8090, "name" : "http", "protocol" : "TCP" } ] } ], "serviceAccount" : "getting-started" } } } }
Below, you can see the content of the target/kubernetes/kubernetes.yml Kubernetes manifest, provided by the Quarkus project packaging:
--- apiVersion: v1 kind: ServiceAccount metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-05 - 13:03:02 +0000 labels: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT name: getting-started --- apiVersion: v1 kind: Service metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-05 - 13:03:02 +0000 labels: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT name: getting-started spec: ports: - name: http port: 8090 targetPort: 8090 selector: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-05 - 13:03:02 +0000 labels: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT name: getting-started spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT template: metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-05 - 13:03:02 +0000 labels: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT spec: containers: - env: - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: vagrant/getting-started:1.0-SNAPSHOT imagePullPolicy: IfNotPresent name: getting-started ports: - containerPort: 8090 name: http protocol: TCP serviceAccount: getting-started
Remark about the Deloyment, container image part:
An important thing to note about the Deployment is that vagrant/getting-started:1.0-SNAPSHOT is used as the container image of the Pod. As I explained in my previous article, the name and tag of the image are controlled by the the Docker container-image extension and can be customized using the usual application.properties.
[https://technology.amis.nl/2020/09/07/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part2/]
Remark about the Deloyment, container port part:
You can also see that port 8090 is used instead of 8080. That’s because I changed the HTTP port in src/main/resources/application.properties as I described in a previous article:
[https://technology.amis.nl/2020/09/01/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part1/]
# # The HTTP port # #quarkus.http.port=8080 quarkus.http.port=8090
Remark about the Deloyment, service account part:
You can also see that serviceAccount is used. However, serviceAccount is a depreciated alias for serviceAccountName. Deprecated: Use serviceAccountName instead. ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
[https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podspec-v1-core]
To use a non-default service account, simply set the spec.serviceAccountName field of a pod to the name of the service account you wish to use.
The service account has to exist at the time the pod is created, or it will be rejected.
[https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/]
Kubernetes Labels
Labels are key/value pairs that are attached to objects, such as pods. Labels are intended to be used to specify identifying attributes of objects that are meaningful and relevant to users, but do not directly imply semantics to the core system. Labels can be used to organize and to select subsets of objects.
Labels allow for efficient queries and watches and are ideal for use in UIs and CLIs. Non-identifying information should be recorded using annotations.
Keep in mind that label Key must be unique for a given object.
[https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/]
Kubernetes Annotations
You can use Kubernetes annotations to attach arbitrary non-identifying metadata to objects. Clients such as tools and libraries can retrieve this metadata.
The metadata in an annotation can be small or large, structured or unstructured, and can include characters not permitted by labels.
[https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/]
Customizing the recommended labels
The generated manifests use the Kubernetes recommended labels. These labels can be customized.
You can visualize and manage Kubernetes objects with more tools than kubectl and the dashboard. A common set of labels allows tools to work interoperably, describing objects in a common manner that all tools can understand. In addition to supporting tooling, the recommended labels describe applications in a way that can be queried.
[https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/]
In order to take full advantage of using these labels, they should be applied on every resource object.
Label key | Description | Example | Type |
app.kubernetes.io/name | The name of the application | mysql | string |
app.kubernetes.io/instance | A unique name identifying the instance of an application | mysql-abcxzy | string |
app.kubernetes.io/version | The current version of the application (e.g., a semantic version, revision hash, etc.) | 5.7.21 | string |
app.kubernetes.io/component | The component within the architecture | database | string |
app.kubernetes.io/part-of | The name of a higher level application this one is part of | wordpress | string |
app.kubernetes.io/managed-by | The tool being used to manage the operation of an application | helm | string |
[https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/]
If you look at the Kubernetes manifest file above, you can see the following labels (and annotations) are used:
annotations: app.quarkus.io/build-timestamp: 2020-09-05 - 13:03:02 +0000 labels: app.kubernetes.io/name: getting-started app.kubernetes.io/version: 1.0-SNAPSHOT
So now it’s time to try to customize the values of these recommended labels.
The labels can be customized using quarkus.kubernetes.name, quarkus.kubernetes.version and quarkus.kubernetes.part-of.
[https://quarkus.io/guides/deploying-to-kubernetes#labels]
Label key | Quarkus key | Quarkus key description |
app.kubernetes.io/name | quarkus.kubernetes.name | The name of the application. This value will be used for naming Kubernetes resources like: - Deployment - Service and so on … |
app.kubernetes.io/instance | ||
app.kubernetes.io/version | quarkus.kubernetes.version | The version of the application. |
app.kubernetes.io/component | ||
app.kubernetes.io/part-of | quarkus.kubernetes.part-of | The name of the group this component belongs too |
app.kubernetes.io/managed-by |
[https://quarkus.io/guides/all-config]
Following, the guide, I changed the code of src/main/resources/application.properties to the following:
[in bold, I highlighted the changes (except app.quarkus.io/build-timestamp)]
# Configuration file # key = value # Your configuration properties greeting.message = hello greeting.name = quarkus # # The HTTP port # #quarkus.http.port=8080 quarkus.http.port=8090 # The path of the banner (path relative to root of classpath) # which could be provided by user # #quarkus.banner.path=default_banner.txt quarkus.banner.path=my_banner.txt # Kubernetes manifest recommended labels quarkus.kubernetes.name=my-quarkus-kubernetes-name quarkus.kubernetes.instance=my_quarkus_kubernetes_instance quarkus.kubernetes.version=my_quarkus_kubernetes_version quarkus.kubernetes.component=my_quarkus_kubernetes_component quarkus.kubernetes.part-of=my_quarkus_kubernetes_part-of quarkus.kubernetes.managed-by=my_quarkus_kubernetes_managed-by
Remark:
As you can see, I also tried to change recommended labels there are not supported. I just wanted to try this out.
In order to recreate the Kubernetes manifests, I used the following command on the Linux Command Prompt:
./mvnw package
Remark:
The output showed the following:
2020-09-09 19:04:57,082 WARN [io.qua.config] (main) Unrecognized configuration key "quarkus.kubernetes.instance" was provided; it will be ignored; verify that the dependency extension for this configuration is set or you did not make a typo 2020-09-09 19:04:57,083 WARN [io.qua.config] (main) Unrecognized configuration key "quarkus.kubernetes.component" was provided; it will be ignored; verify that the dependency extension for this configuration is set or you did not make a typo 2020-09-09 19:04:57,083 WARN [io.qua.config] (main) Unrecognized configuration key "quarkus.kubernetes.managed-by" was provided; it will be ignored; verify that the dependency extension for this configuration is set or you did not make a typo
Below, you can see the content of the target/kubernetes/kubernetes.yml Kubernetes manifest, provided by the Quarkus project packaging:
[in bold, I highlighted the changes (except app.quarkus.io/build-timestamp)]
--- apiVersion: v1 kind: ServiceAccount metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:05:26 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version name: my-quarkus-kubernetes-name --- apiVersion: v1 kind: Service metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:05:26 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version name: my-quarkus-kubernetes-name spec: ports: - name: http port: 8090 targetPort: 8090 selector: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:05:26 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version name: my-quarkus-kubernetes-name spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version template: metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:05:26 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version spec: containers: - env: - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: vagrant/getting-started:1.0-SNAPSHOT imagePullPolicy: IfNotPresent name: my-quarkus-kubernetes-name ports: - containerPort: 8090 name: http protocol: TCP serviceAccount: my-quarkus-kubernetes-name
Remark about the labels:
As could be expected, from the labels, only the 3 supported recommended labels were changed.
So, for the next steps, I removed them again.
Remark about the metadata.name:
As you can see, also the metadata.name of the Kubernetes ServiceAccount, Service and Deployment were changed.
Each object in your cluster has a Name that is unique for that type of resource.
Most resource types require a name that can be used as a DNS subdomain name as defined in RFC 1123. This means the name must:
- contain no more than 253 characters
- contain only lowercase alphanumeric characters, ‘-‘ or ‘.’
- start with an alphanumeric character
- end with an alphanumeric character
Some resource types require their names to follow the DNS label standard as defined in RFC 1123. This means the name must:
- contain at most 63 characters
- contain only lowercase alphanumeric characters or ‘-‘
- start with an alphanumeric character
- end with an alphanumeric character
[ https://kubernetes.io/docs/concepts/overview/working-with-objects/names/]
So, you can expect a build error like the example below if you don’t conform to the name constraints (with regard to for example a ServiceAccount):
[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:1.7.0.Final:build (default) on project getting-started: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors [ERROR] [error]: Build step io.quarkus.kubernetes.deployment.KubernetesDeployer#deploy threw an exception: io.dekorate.deps.kubernetes.client.KubernetesClientException: Failure executing: POST at: https://127.0.0.1:6443/api/v1/namespaces/default/serviceaccounts. Message: ServiceAccount "my_quarkus_kubernetes_name" is invalid: metadata.name: Invalid value: "my_quarkus_kubernetes_name": a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*').
[https://tools.ietf.org/html/rfc1123]
So, you can expect a build error like the example below if you don’t conform to the name constraints (with regard to for example a Service):
ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:1.7.0.Final:build (default) on project getting-started: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors [ERROR] [error]: Build step io.quarkus.kubernetes.deployment.KubernetesDeployer#deploy threw an exception: io.dekorate.deps.kubernetes.client.KubernetesClientException: Failure executing: POST at: https://127.0.0.1:6443/api/v1/namespaces/default/services. Message: Service "my.quarkus.kubernetes.name" is invalid: metadata.name: Invalid value: "my.quarkus.kubernetes.name": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?').
[https://tools.ietf.org/html/rfc1035]
Using custom Labels
In a previous article I describe how I deployed applications (based on the booksservice) to Minikube. I created a development (DEV) and a testing (TST) environment. In the DEV environment the applications (version 1.0 and 2.0) were using an H2 in-memory database. In the TST environment the application (version 1.0) were using an external MySQL database.
I described how, in the Kubernetes manifest files, I used custom labels, so a ReplicaSet could manage pods with labels that matched the selector. In my case these labels were for example:
Label key | Label value |
app | booksservice |
version | 1.0 |
environment | development |
I also used Environment variables, for example to set up the active Spring profile.
[https://technology.amis.nl/2019/03/05/using-a-restful-web-service-spring-boot-application-in-minikube-together-with-an-external-dockerized-mysql-database/]
Quarkus offers the ability to add additional custom labels. Just apply the following configuration:
quarkus.kubernetes.labels.<label key>=<label value>
Then, following the guide, I changed the code of src/main/resources/application.properties by adding the following:
# Kubernetes manifest custom labels quarkus.kubernetes.labels.app=getting-started quarkus.kubernetes.labels.version=1.0 quarkus.kubernetes.labels.environment=development
Remark about label keys:
For creating valid label keys, please see:
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set
Remark about label values:
The label values must be strings. In yaml, that means all numeric values must be quoted.
[https://github.com/kubernetes/kubernetes/issues/57509]
Later on, following this code guide, I also tried using the following command and the Kubernetes manifest (with content shown further below) to create the resources:
kubectl apply -f /vagrant/applications/getting-started/target/kubernetes/kubernetes.yml
But this resulted in the following error about the label with key version and value 1.0:
unable to decode "/vagrant/applications/getting-started/target/kubernetes/kubernetes.yml": resource.metadataOnlyObject.ObjectMeta: v1.ObjectMeta.Namespace: Name: Labels: ReadString: expects " or n, but found 1, error found in #10 byte of ...|version":1},"name":"|..., bigger context ...|s_version","environment":"development","version":1},"name":"my-quarkus-kubernetes-name","namespace":|...
Unfortunately, getting quotes around a numeric label value, wasn’t easy.
I tried quarkus.kubernetes.labels.version=”1.0″ in application.properties, but this resulted in the target/kubernetes/kubernetes.yml Kubernetes manifest, in:
For now, I left this label value without quotes and didn’t proceed with using the kubectl command mentioned above.
In order to recreate the Kubernetes manifests, I used the following command on the Linux Command Prompt:
./mvnw package
Below, you can see the content of the target/kubernetes/kubernetes.yml Kubernetes manifest, provided by the Quarkus project packaging:
[in bold, I highlighted the changes (except app.quarkus.io/build-timestamp)]
--- apiVersion: v1 kind: ServiceAccount metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:30:15 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 name: my-quarkus-kubernetes-name --- apiVersion: v1 kind: Service metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:30:15 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 name: my-quarkus-kubernetes-name spec: ports: - name: http port: 8090 targetPort: 8090 selector: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:30:15 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 name: my-quarkus-kubernetes-name spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version template: metadata: annotations: app.quarkus.io/build-timestamp: 2020-09-09 - 19:30:15 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 spec: containers: - env: - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: vagrant/getting-started:1.0-SNAPSHOT imagePullPolicy: IfNotPresent name: my-quarkus-kubernetes-name ports: - containerPort: 8090 name: http protocol: TCP serviceAccount: my-quarkus-kubernetes-name
Remark:
As you can see, only the objects (for example Service) metadata labels were changed, the selector labels aren’t changed.
Service, selector: Route service traffic to pods with label keys and values matching this selector.
[https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#service-v1-core]
Deployment, selector: Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template’s labels.
[https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#deploymentspec-v1-apps]
Customizing the annotations
Out of the box, the generated resources will be annotated with version control related information that can be used either by tooling, or by the user for troubleshooting purposes.
Although the code guide talks about the following:
“annotations”: {
“app.quarkus.io/vcs-url” : “<some url>”,
“app.quarkus.io/commit-id” : “<some git SHA>”,
}
As mentioned before the generated manifest actually has the following annotation:
app.quarkus.io/build-timestamp
Remark:
With regard to the two annotations mentioned in the code guide, please see:
https://github.com/quarkusio/quarkus/issues/9280
Using custom annotations
Custom annotations can be added in a way similar to labels.
Just apply the following configuration:
quarkus.kubernetes.annotations.<label key>=<label value>
Then, following the guide, I changed the code of src/main/resources/application.properties by adding the following:
# Kubernetes manifest custom annotations quarkus.kubernetes.annotations.my_key1=key1_value quarkus.kubernetes.annotations.my_key2=key2_value
In order to recreate the Kubernetes manifests, I used the following command on the Linux Command Prompt:
./mvnw package
Below, you can see the content of the target/kubernetes/kubernetes.yml Kubernetes manifest, provided by the Quarkus project packaging:
[in bold, I highlighted the changes (except app.quarkus.io/build-timestamp)]
--- apiVersion: v1 kind: ServiceAccount metadata: annotations: my_key1: key1_value my_key2: key2_value app.quarkus.io/build-timestamp: 2020-09-10 - 19:12:44 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 name: my-quarkus-kubernetes-name --- apiVersion: v1 kind: Service metadata: annotations: my_key1: key1_value my_key2: key2_value app.quarkus.io/build-timestamp: 2020-09-10 - 19:12:44 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 name: my-quarkus-kubernetes-name spec: ports: - name: http port: 8090 targetPort: 8090 selector: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: annotations: my_key1: key1_value my_key2: key2_value app.quarkus.io/build-timestamp: 2020-09-10 - 19:12:44 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 name: my-quarkus-kubernetes-name spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version template: metadata: annotations: my_key1: key1_value my_key2: key2_value app.quarkus.io/build-timestamp: 2020-09-10 - 19:12:44 +0000 labels: app.kubernetes.io/name: my-quarkus-kubernetes-name app.kubernetes.io/part-of: my_quarkus_kubernetes_part-of app.kubernetes.io/version: my_quarkus_kubernetes_version app: getting-started environment: development version: 1.0 spec: containers: - env: - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: vagrant/getting-started:1.0-SNAPSHOT imagePullPolicy: IfNotPresent name: my-quarkus-kubernetes-name ports: - containerPort: 8090 name: http protocol: TCP serviceAccount: my-quarkus-kubernetes-name
So, I conclude this article. I shared with you the steps I took trying out the Quarkus code guide “Quarkus – Kubernetes extension”, and more specific the steps related to using custom labels and annotations.
In a next article, you can read more about other steps I took (continuing with the code guide), for example using namespaces and the automatic deployment of the generated resources to a target platform, in my case K3s (lightweight certified Kubernetes distribution).