SpringBoot applications when deployed in Kubernetes need the capability to reload in the event that some of the properties change. This becomes extremely important when you need to push out configuration to your spring boot application workloads deployed in Kubernetes without having to do a full-scale deployment.
In this article, we will go through the setup of reloading configuration within Spring boot applications in Kubernetes using the Configmap object.
Pre-Requisites
Ensure that you have gone through the setup mentioned in here
1 . Add Required Dependencies to SpringBoot Application
We need Spring Cloud Kubernetes library that adds support for the SpringBoot application running on a pod to subscribe to any changes within k8s objects such as ConfigMap.
Add the following to your Gradle file within the dependencies section. Here is what the full Gradle file should look like once done.
implementation "io.kubernetes:client-java:${k8sJavaClientVersion}"
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-kubernetes-all', version: "${sbk8sVersion}"
2. Changes within SpringBoot Application
Add @RefreshScope annotation for the AppConfig.java bean, this allows the spring bean to be refreshed using the refreshAll()
method which clears up the target cache.
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "app")
public class AppConfig {
private String timeout;
private String message;
....
}
Add the following properties in application.properties file:
spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.reload.strategy=refresh
spring.cloud.kubernetes.reload.mode=event
spring.application.name=spring-sample-app
The above properties define the reload strategy, mode, and the name of the application. The name here is especially important and needs to match the ConfigMap name you are using in k8s.
3. Helm chart Changes
The following objects are required for the ConfigMap to work with spring properties
- Configmap Yaml: Defines ConfigMap specification
- Role: Creates a role to create permissions to access Kubernetes objects from within SpringBoot application
- RoleBinding: Binds the service account used by the application to the role.
Add the following files to your helm chart under helm/spring-sample-app/templates
ConfigMap
A ConfigMap is an API object used to store non-confidential data in key-value pairs. Pods can consume ConfigMaps as environment variables, command-line arguments, or as configuration files in a volume. See detailed documentation on ConfigMap here
Create a file with the following content and name configmap.yaml
or just get the full contents from confgmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-sample-app
data:
application.properties: |-
app.message=Overriden Message from Configmap
app.timeout=500
The above ConfigMap object creates a data property application.property
that will override anything that is within src/main/resources/application.properties file.
Role
A Role sets permissions within a namespace which can then be leveraged by a service account via rolebinding
. More on K8s roles here
Create a file with the following content and name roles.yaml
or just get the full contents from roles.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "spring-sample-app.name" . }}
labels:
{{- include "spring-sample-app.labels" . | nindent 4 }}
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods","configmaps","endpoints","services"]
verbs: ["get", "watch", "list"]
This role provides access to SpringBoot application to call k8s APIs and request information on its objects such as pods, ConfigMaps, endpoints, and services.
Role Binding
A role binding grants the permissions defined in a role to a user or set of users. It holds a list of subjects (users, groups, or service accounts), and a reference to the role being granted. A RoleBinding grants permissions within a specific namespace whereas a ClusterRoleBinding grants that access cluster-wide. Refer to rolebindings k8s documentation for more details.
Create a file with the following content and name rolebinding.yaml
or just get the full contents from rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "spring-sample-app.name" . }}
labels:
{{- include "spring-sample-app.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "spring-sample-app.name" . }}
subjects:
- kind: ServiceAccount
name: {{ include "spring-sample-app.name" . }}
This binds the service account to the role created above which will be used by the SpringBoot application to call k8s APIs.
4. Upgrade Version Numbers within SpringBoot Application
Update the version in build.gradle
file and helm charts chart.yaml
to 1.0, version number can be anything as long as they are the same between both helm and build Gradle file.
build.gradle file:
version = '1.0'
Chart.yaml file
appVersion: "1.0"
5. Rebuild SpringBoot App and Deploy Helm Chart
Build the spring application with the changes and create an image, run the following in project root directory
./gradlew -i jibDocker
Then navigate to helm root directory and run helm upgrade command or helm install if you haven’t already installed this application before
helm upgrade --install spring-sample-app spring-sample-app/
6. Verify Configmap Changes
Open up a terminal window in Mac or something equivalent and run the following to start tailing the logs
kubectl logs -l app.kubernetes.io/name=spring-sample-app -f
Open up another terminal window and Verify the current property state
curl http://localhost/propertyValues
Timeout=500, Message=Overriden Message from Configmap
Now change the ConfigMap object and edit one of these values. Here I changed the message and timeout values
kubectl edit configmap spring-sample-app
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
application.properties: |-
app.message=Property Changed to test Reload from Configmap
app.timeout=600
Save it and view the logs, you should see the following within the logs. This indicates spring application received changes from ConfigMap and was able to successfully reload the application using new values.
Detected change in config maps
Reloading using strategy: REFRESH
curl http://localhost/propertyValues
Timeout=600, Message=Property Changed to test Reload from Configmap
Cleanup and Next Steps
Make sure you delete the helm chart and remove all images you created as a part of the tutorial. You probably don’t want stray pods running in your cluster and unwanted images filling up your disk
// To delete the application including the chart
helm delete spring-sample-app
// To remove all images created by our builds
docker rmi -f $(docker images spring-sample-app -aq)
Conclusion
To conclude we demonstrated how your application built using SpringBoot can subscribe to Configmap change events from K8s and then reload the context to reflect those changes. In this post, we assumed certain things regarding the configuration including refresh strategy. There are more than one ways to do this which we will go over in the next post.
Also for your reference, the entire source code with Helm chart is available on github.