Kubernetes
@KubernetesApplication is a more specialized form of @Dekorate. It can be added to your project like:
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication
public class Main {
public static void main(String[] args) {
//Your application code goes here.
}
}
When the project gets compiled, the annotation will trigger the generation of a Deployment
in both json and yml that
will end up under ‘target/classes/META-INF/dekorate’.
The annotation comes with a lot of parameters, which can be used in order to customize the Deployment
and/or trigger
the generations of additional resources, like Service
and Ingress
.
Adding the kubernetes annotation processor to the classpath
This module can be added to the project using:
<dependency>
<groupId>io.dekorate</groupId>
<artifactId>kubernetes-annotations</artifactId>
<version>4.1.4</version>
</dependency>
Name and Version
So where did the generated Deployment
gets its name, docker image etc from?
Everything can be customized via annotation parameters, application configuration and system properties. On top of that, lightweight integration with build tools is provided in order to reduce duplication.
Note, that part-of, name and version are part of multiple annotations / configuration groups etc.
When a single application configuration is found and no explict image configuration value has been used for (group, name & version), values from the application configuration will be used.
For example:
@KubernetesApplication(name="my-app")
@DockerBuild(registry="quay.io")
public class Main {
}
In the example above, docker is configured with no explicit value on name
. In this case that name from @KubernetesApplication(name="my-app")
will be used.
The same applies when property configuration is used:
io.dekorate.kubernetes.name=my-app
io.dekorate.docker.registry=quay.io
Note: Application configuration part-of
corresponds to image configuration group
.
Lightweight build tool integration
Lightweight integration with build tools, refers to reading information from the build tool config without bringing in the build tool itself into the classpath. The information read from the build tool is limited to:
- name / artifactId
- version
- output file
For example in the case of maven it refers to parsing the pom.xml with DOM in order to fetch the artifactId and version.
Supported build tools:
- maven
- gradle
- sbt
- bazel
For all other build tools, the name and version need to be provided via application.properties
:
dekorate.kubernetes.name=my-app
dekorate.kubernetes.version=1.1.0.Final
or the core annotations:
@KubernetesApplication(name = "my-app", version="1.1.0.Final")
public class Main {
}
or
@OpenshiftApplication(name = "my-app", version="1.1.0.Final")
public class Main {
}
and so on…
The information read from the build tool, is added to all resources as labels (name, version). They are also used to name images, containers, deployments, services etc.
For example for a gradle app, with the following gradle.properties
:
name = my-gradle-app
version = 1.0.0
The following deployment will be generated:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "kubernetes-example"
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: "my-gradle-app"
app.kubernetes.io/version: "1.0-SNAPSHOT"
template:
metadata:
labels:
app.kubernetes.io/name: "my-gradle-app"
app.kubernetes.io/version: "1.0-SNAPSHOT"
spec:
containers:
- env:
- name: "KUBERNETES_NAMESPACE"
valueFrom:
fieldRef:
fieldPath: "metadata.namespace"
image: "default/my-gradle-app:1.0-SNAPSHOT"
imagePullPolicy: "IfNotPresent"
name: "my-gradle-app"
The output file name may be used in certain cases, to set the value of JAVA_APP_JAR
an environment variable that points to the build jar.
Adding extra ports and exposing them as services
To add extra ports to the container, you can add one or more @Port
into your @KubernetesApplication:
import io.dekorate.kubernetes.annotation.Port;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication(ports = @Port(name = "web", containerPort = 8080))
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
This will trigger the addition of a container port to the Deployment
but also will trigger the generation of a Service
resource.
Everything that can be defined using annotations, can also be defined using application.properties
.
To add a port using application.properties
:
dekorate.kubernetes.ports[0].name=web
dekorate.kubernetes.ports[0].container-port=8080
NOTE: This doesn’t need to be done explicitly, if the application framework is detected and support, ports can be extracted from there (see below).
IMPORTANT: When mixing annotations and application.properties
the latter will always take precedence overriding values that defined using annotations.
This allows users to define the configuration using annotations and externalize configuration to application.properties
.
REMINDER: A complete reference on all the supported properties can be found in the configuration options guide.
Adding container environment variables
To add extra environment variables to the container, you can add one or more @EnvVar
into your @KubernetesApplication :
import io.dekorate.kubernetes.annotation.Env;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication(envVars = @Env(name = "key1", value = "var1"))
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
Additional options are provided for adding environment variables from fields, config maps and secrets.
To add environment variables using application.properties
:
dekorate.kubernetes.env-vars[0].name=key1
dekorate.kubernetes.env-vars[0].value=value1
Adding environment variables from ConfigMap
To add an environment variable that points to a ConfigMap property, you need to specify the configmap using the configmap
property in the @Env annotation.
The configmap key will be specified by the value
property. So, in this case value
has the meaning of value from key
.
import io.dekorate.kubernetes.annotation.Env;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication(envVars = @Env(name = "key1", configmap="my-config", value = "key1"))
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
To add an environment variable referencing a config map using application.properties
:
dekorate.
.env-vars[0].name=key1
dekorate.kubernetes.env-vars[0].value=key1
dekorate.kubernetes.env-vars[0].config-map=my-config
Adding environment variables from Secrets
To add an environment variable that points to a Secret property, you need to specify the configmap using the secret
property in the @Env annotation.
The secret key will be specified by the value
property. So, in this case value
has the meaning of value from key
.
import io.dekorate.kubernetes.annotation.Env;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication(envVars = @Env(name = "key1", secret="my-secret", value = "key1"))
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
To add an environment variable referencing a secret using application.properties
:
dekorate.kubernetes.env-vars[0].name=key1
dekorate.kubernetes.env-vars[0].value=key1
dekorate.kubernetes.env-vars[0].secret=my-config
Working with volumes and mounts
To define volumes and mounts for your application, you can use something like:
import io.dekorate.kubernetes.annotation.Mount;
import io.dekorate.kubernetes.annotation.PersistentVolumeClaimVolume;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication(pvcVolumes = @PersistentVolumeClaimVolume(volumeName = "mysql-volume", claimName = "mysql-pvc"),
mounts = @Mount(name = "mysql-volume", path = "/var/lib/mysql")
)
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
To define the same volume and mount via application.properties
:
dekorate.kubernetes.pvc-volumes[0].volume-name=mysql-volume
dekorate.kubernetes.pvc-volumes[0].claim-name=mysql-pvc
dekorate.kubernetes.mounts[0].name=mysql-volume
dekorate.kubernetes.mounts[0].path=/var/lib/mysql
Currently, the supported annotations for specifying volumes are:
- @PersistentVolumeClaimVolume
- @SecretVolume
- @ConfigMapVolume
- @AwsElasticBlockStoreVolume
- @AzureDiskVolume
- @AzureFileVolume
Adding Kubernetes Jobs
To generate Kubernetes Jobs, you can define them either using the @KubernetesApplication
annotation:
import io.dekorate.kubernetes.annotation.Container;
import io.dekorate.kubernetes.annotation.Job;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication(jobs = @Job(name = "say-hello", containers = @Container(image = "docker.io/user/hello")))
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
Or via configuration properties at the file application.properties
:
dekorate.kubernetes.jobs[0].name=say-hello
dekorate.kubernetes.jobs[0].containers[0].image=docker.io/user/hello
Currently, the supported annotations for adding jobs are:
- @KubernetesApplication
- @OpenShiftApplication
- @KnativeApplication
Adding Kubernetes CronJobs
To generate Kubernetes CronJobs, you can define them either using the @KubernetesApplication
annotation:
@KubernetesApplication(cronJobs = @CronJob(name = "say-hello", schedule = "* * * * *", containers = @Container(image = "docker.io/user/hello")))
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
Or via configuration properties at the file application.properties
:
dekorate.kubernetes.cron-jobs[0].name=say-hello
dekorate.kubernetes.cron-jobs[0].schedule=* * * * *
dekorate.kubernetes.cron-jobs[0].containers[0].image=docker.io/user/hello Dekorate CronJobs configuration follows the [Kubernetes CronJobs specification](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/cron-job-v1/#CronJobSpec).
If the user doesn’t provide CronJob container image, the pod template image configuration will be used. Currently, the supported annotations for adding jobs are:
- @KubernetesApplication
- @OpenShiftApplication
- @KnativeApplication
Vcs Options
Most of the generated resources contain the kubernetes recommended annotations for specifying things like:
- vcs url
- commit id
These are extracted from the project .git/config
file (Currently only git is supported).
Out of the box, the url of the origin
remote will be used verbatim.
Specifying remote
In some cases users may prefer to use another remote.
This can be done with the use of @VcsOptions
annotation:
import io.dekorate.options.annotation.JvmOptions;
import io.dekorate.options.annotation.GarbageCollector;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication
@VcsOptions(remote="myfork")
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
In the example above myfork
will be used as the remote. So, generated resources will be annotated with the url of the myfork
remote.
For users that prefer using application.properties
:
dekorate.vcs.remote=myfork
Converting vcs urls to https
The vcs related annotations are mostly used by tools. For public repositories its often simpler for tools, to access the repository anonymous access.
This is possible when using git over https, but not possible when using git over ssh. So, there are cases where users would rather develop using git+ssh
but have 3d-party tools use https
instead. To force dekorate covnert vcs urls to https
one case use the httpsPreferred
parameter of @VcsOptions
.
Or using properties:
dekorate.vcs.https-preferred=true
Jvm Options
It’s common to pass the JVM options in the manifests using the JAVA_OPTS
or JAVA_OPTIONS
environment variable of the application container.
This is something complex as it usually difficult to remember all options by heart and thus its error prone.
The worst part is that you usually don’t realize the mistake until it’s TOO
late.
Dekorate provides a way to manage those options using the @JvmOptions
annotation, which is included in the options-annotations
module.
import io.dekorate.options.annotation.JvmOptions;
import io.dekorate.options.annotation.GarbageCollector;
import io.dekorate.kubernetes.annotation.KubernetesApplication;
@KubernetesApplication
@JvmOptions(server=true, xmx=1024, preferIpv4Stack=true, gc=GarbageCollector.SerialGC)
public class Main {
public static void main(String[] args) {
//Your code goes here
}
}
or via application.properties
:
dekorate.jvm.server=true
dekorate.jvm.xmx=1024
dekorate.jvm.prefer-ipv4-stack=true
dekorate.jvm.gc=GarbageCollector.SerialGC
This module can be added to the project using:
<dependency>
<groupId>io.dekorate</groupId>
<artifactId>option-annotations</artifactId>
<version>4.1.4</version>
</dependency>
Note: The module is included in all starters.