Spring into Kubernetes - Using Kubernetes as a Config Server
March 1, 2019
In previous installments of Spring into Kubernetes I’ve shown you how to build images, deploy applications and write a Helm Chart for Spring applications. In this installment we’ll look at Spring Cloud Kubernetes integrations, specifically using Kubernetes Config Maps as a Config Server.
Usually I would use a Pivotal Container Service cluster to demonstrate, but in this demonstration I’ll use a local minikube cluster.
Spring Cloud Kubernetes
Spring Cloud Kubernetes brings in a ton of integrations with Kubernetes. This demonstration will focus just on the ability to integrate Kubernetes as a configuration server.
Minimal changes are needed to your applications, you need to simply add the following classes to your pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
</dependency>
You also need to enable it in your bootstrap.yaml
(or .properties
):
spring.cloud.kubernetes.config.enabled: true
spring.cloud.kubernetes.reload.enabled: true
Getting Started
You’ll need to install minikube by following the instructions provided for your Operating System. You’ll also need kubectl so that you can give instructions to Kubernetes.
Start Minikube:
$ minikube start
😄 minikube v0.34.1 on linux (amd64)
💡 Tip: Use 'minikube start -p <name>' to create a new cluster, or 'minikube delete' to delete this one.
🔄 Restarting existing virtualbox VM for "minikube" ...
⌛ Waiting for SSH access ...
📶 "minikube" IP address is 192.168.99.103
🐳 Configuring Docker as the container runtime ...
✨ Preparing Kubernetes environment ...
🚜 Pulling images required by Kubernetes v1.13.3 ...
🔄 Relaunching Kubernetes v1.13.3 using kubeadm ...
⌛ Waiting for kube-proxy to come back up ...
🤔 Verifying component health ......
💗 kubectl is now configured to use "minikube"
🏄 Done! Thank you for using minikube!
Ensure that you can communicate with minikube:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 36h v1.13.3
Deploy Spring Hello World
The source for this demo can be found at paulczar/spring-hello on github. Of importance it will respond to a web request with the contents of the application property message
.
Deploy the example Hello World application and expose it via a service:
$ kubectl run hello --image=paulczar/spring-hello:k8s001 --port=8080
deployment.apps/hello created
$ kubectl expose deployment hello --type=LoadBalancer --port 80 --target-port 8080
service/hello exposed
You can use minikube service list
to get a list of services and the URLs for those services. This helps make up for the lack of LoadBalancer support in minikube:
$ minikube service list
|-------------|------------|-----------------------------|
| NAMESPACE | NAME | URL |
|-------------|------------|-----------------------------|
| default | hello | http://192.168.99.103:30871 |
| default | kubernetes | No node port |
| kube-system | kube-dns | No node port |
|-------------|------------|-----------------------------|
Use the URL provided for the hello
service:
$ curl http://192.168.99.103:30871
hello development
Our application is running and responding with hello development
. This is the default value for message
in the development
spring profile.
Configure Kubernetes support for Spring Hello World
If you have rbac
enabled in your cluster (which you should, we’re not animals) the service account your application is running and will be unable to view kubernetes resources. You can see these errors in the pod’s logs:
k logs deployment/hello
2019-03-01 16:13:02.629 WARN 1 --- [ main] o.s.cloud.kubernetes.StandardPodUtils : Failed to get pod with name:[hello-bb9cf575d-rqt6n]. You should look into this if things aren't working as you expect. Are you missing serviceaccount permissions?
io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: GET at: https://10.96.0.1/api/v1/namespaces/default/pods/hello-bb9cf575d-rqt6n. Message: Forbidden!Configured service account doesn't have access. Service account may have been revoked. pods "hello-bb9cf575d-rqt6n" is forbidden: User "system:serviceaccount:default:default" cannot get resource "pods" in API group "" in the namespace "default".
...
...
You could create a new service user and give it the appropriate permissions, or you could give permissions to the default service account. Do the latter using a kubernetes manifest found here.
Update the rolebinding
for the default user:
$ kubectl apply -f https://raw.githubusercontent.com/paulczar/spring-helloworld/master/deploy/rbac.yaml
Delete the current pod to have the kubernetes deployment start a new one which should now have permissions:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-bb9cf575d-rqt6n 1/1 Running 0 12m
$ kubectl delete pod hello-bb9cf575d-rqt6n
pod "hello-bb9cf575d-rqt6n" deleted
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-bb9cf575d-8mwxq 1/1 Running 0 21s
If you look at the logs you’ll see the error has disappeared and you can see it is looking for a configmap
:
$ kubectl logs deployment/hello
2019-03-01 16:26:33.080 DEBUG 1 --- [ main] o.s.cloud.kubernetes.config.ConfigUtils : Config Map name has not been set, taking it from property/env spring.application.name (default=application)
2019-03-01 16:26:33.080 DEBUG 1 --- [ main] o.s.cloud.kubernetes.config.ConfigUtils : Config Map namespace has not been set, taking it from client (ns=default)
2019-03-01 16:26:33.188 INFO 1 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='composite-configmap', propertySources=[ConfigMapPropertySource@872306601 {name='configmap.hello.default', properties={}}]}
Since you haven't yet created a `configmap` the response from the application should still be the default:
```console
$ curl http://192.168.99.103:30871
hello development
Next create a configmap
that the application will use, by default it will look for a configmap
with the same name as the application:
$ kubectl create configmap hello --from-literal=message="HELLO KUBERNETES"
configmap/hello created
Run the curl
command again and you should see the new response:
$ curl http://192.168.99.103:30871
HELLO KUBERNETES
Conclusion
While this was a fairly simple demonstration of the Spring Cloud Kubernetes integrations you can see how useful it can be. By integrating directly into Kubernetes you can avoid running a Spring Cloud Config service to get dynamic configuration of your application.
You can also load up an entire application properties file (either .properties
or .yaml
) inside a configmap
, you can also store passwords and keys in a Kubernetes secret
and dynamically load those.