Spring Cloud Kubernetes Configuration Watcher
Kubernetes provides the ability to mount a ConfigMap or Secret as a volume in the container of your application. When the contents of the ConfigMap or Secret changes, the mounted volume will be updated with those changes.
However, Spring Boot will not automatically update those changes unless you restart the application. Spring Cloud
provides the ability refresh the application context without restarting the application by either hitting the
actuator endpoint /refresh
or via publishing a RefreshRemoteApplicationEvent
using Spring Cloud Bus.
To achieve this configuration refresh of a Spring Cloud app running on Kubernetes, you can deploy the Spring Cloud Kubernetes Configuration Watcher controller into your Kubernetes cluster.
The application is published as a container and is available on Docker Hub. However, if you need to customize the config watcher behavior or prefer to build the image yourself you can easily build your own image from the source code on GitHub and use that.
Another option to configure it is to provide some environment variables in the deployment.yaml used to deploy configuration watcher. Here are some important ones:
env:
- name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CONFIGURATION_WATCHER
value: DEBUG
- name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD
value: DEBUG
- name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG_RELOAD
value: DEBUG
These enable debug logging on the configuration watcher and are particular useful on the initial set-up, to be able to diagnose potential miss-configurations.
env:
- name: SPRING_CLOUD_KUBERNETES_RELOAD_NAMESPACES_0
value: "namespace-a"
This one lets watcher know where to search for secrets and configmaps. You have two options here: selective namespaces (the setting above) and a namespace chosen by Namespace Resolution (this is the default option). Keep in mind that all these options require proper RBAC rules.
Changes from configmaps/secrets will only trigger an event being fired from configuration watcher if that particular change came from a source that has a label: spring.cloud.kubernetes.config=true
or spring.cloud.kubernetes.secret=true
.
To put it simpler, if you change a configmap (or secret), that does not have the label above, configuration watcher will skip firing an event for it (if you enabled debug logging, this will be visible in logs).
By default, configuration watcher will monitor all configmaps/secrets in the configured namespace(s). If you want to filter to watch only particular sources, you can do that by setting:
SPRING_CLOUD_KUBERNETES_CONFIG_INFORMER_ENABLED=TRUE
This will tell watcher to only monitor sources that have a label: spring.cloud.kubernetes.config.informer.enabled=true
.
One more important configuration, especially for configmaps and secrets that are mounted as volumes (via spring.cloud.kubernetes.config.paths
/spring.cloud.kubernetes.secrets.paths
or using spring.config.import
) is:
- name: SPRING_CLOUD_KUBERNETES_CONFIGURATION_WATCHER_REFRESHDELAY
value: "10000"
This tells how many milliseconds should we wait before firing the event from configuration watcher. This is important because kubernetes documentation says:
When a ConfigMap currently consumed in a volume is updated, projected keys are eventually updated as well.
You need to "match" this eventually part to that value in milliseconds on your cluster.
Spring Cloud Kubernetes Configuration Watcher can send refresh notifications to applications in two ways.
-
Over HTTP, in which case the application being notified, must have the
/refresh
actuator endpoint exposed and accessible from within the cluster -
Using Spring Cloud Bus, in which case you will need a message broker deployed to your custer for the application to use.
Deployment YAML
Below is a sample deployment YAML you can use to deploy the Kubernetes Configuration Watcher to Kubernetes.
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
name: spring-cloud-kubernetes-configuration-watcher
spec:
ports:
- name: http
port: 8888
targetPort: 8888
selector:
app: spring-cloud-kubernetes-configuration-watcher
type: ClusterIP
- apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
name: spring-cloud-kubernetes-configuration-watcher
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
name: spring-cloud-kubernetes-configuration-watcher:view
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-configuration-watcher
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
verbs: ["get", "list", "watch"]
- apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-configuration-watcher-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-configuration-watcher
template:
metadata:
labels:
app: spring-cloud-kubernetes-configuration-watcher
spec:
serviceAccount: spring-cloud-kubernetes-configuration-watcher
containers:
- name: spring-cloud-kubernetes-configuration-watcher
image: springcloud/spring-cloud-kubernetes-configuration-watcher:3.1.4
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8888
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8888
path: /actuator/health/liveness
ports:
- containerPort: 8888
The Service Account and associated Role Binding is important for Spring Cloud Kubernetes Configuration to work properly. The controller needs access to read data about ConfigMaps, Pods, Services, Endpoints and Secrets in the Kubernetes cluster.
Monitoring ConfigMaps and Secrets
If a change is made to a ConfigMap or Secret with valid labels (as detailed above), then Spring Cloud Kubernetes Configuration Watcher will take the name of the ConfigMap or Secret and send a notification to the application with that name. This might not be enough for your use-case though, you could for example want to:
-
bind a config-map to multiple applications, so that a change inside a single configmap triggers a refresh for many services
-
have profile based sources trigger events for your application
For that reasons there is an addition annotation you could specify:
spring.cloud.kubernetes.configmap.apps
or spring.cloud.kubernetes.secret.apps
. It takes a String of apps separated by comma,
that specifies the names of applications that will receive a notification when changes happen in this secret/configmap.
For example:
kind: ConfigMap
apiVersion: v1
metadata:
name: example-configmap
labels:
spring.cloud.kubernetes.config: "true"
annotations:
spring.cloud.kubernetes.configmap.apps: "app-a, app-b"
HTTP Implementation
The HTTP implementation is what is used by default. When this implementation is used, Spring Cloud Kubernetes Configuration Watcher and a
change to a ConfigMap or Secret occurs then the HTTP implementation will use the Spring Cloud Kubernetes Discovery Client to fetch all
instances of the application which match the name of the ConfigMap or Secret and send an HTTP POST request to the application’s actuator
/refresh
endpoint. By default, it will send the post request to /actuator/refresh
using the port registered in the discovery client.
Non-Default Management Port and Actuator Path
If the application is using a non-default actuator path and/or using a different port for the management endpoints, the Kubernetes service for the application
can add an annotation called boot.spring.io/actuator
and set its value to the path and port used by the application. For example
apiVersion: v1
kind: Service
metadata:
labels:
app: config-map-demo
name: config-map-demo
annotations:
boot.spring.io/actuator: http://:9090/myactuator/home
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: config-map-demo
Another way you can choose to configure the actuator path and/or management port is by setting
spring.cloud.kubernetes.configuration.watcher.actuatorPath
and spring.cloud.kubernetes.configuration.watcher.actuatorPort
.
Messaging Implementation
The messaging implementation can be enabled by setting profile to either bus-amqp
(RabbitMQ) or bus-kafka
(Kafka) when the Spring Cloud Kubernetes Configuration Watcher
application is deployed to Kubernetes.
Configuring RabbitMQ
When the bus-amqp
profile is enabled you will need to configure Spring RabbitMQ to point it to the location of the RabbitMQ
instance you would like to use as well as any credentials necessary to authenticate. This can be done
by setting the standard Spring RabbitMQ properties, for example
spring:
rabbitmq:
username: user
password: password
host: rabbitmq
Configuring Kafka
When the bus-kafka
profile is enabled you will need to configure Spring Kafka to point it to the location of the Kafka Broker
instance you would like to use. This can be done by setting the standard Spring Kafka properties, for example
spring:
kafka:
producer:
bootstrap-servers: localhost:9092