can't populate volume using configMap - spring-boot

I am trying run a spring boot application whose application.properties file will be used as a kubernetes configMap.
While deploying the app, I am adding a volume for this config file. But somehow, it is not creating properties file at mount path.
configMap name : integration-properties
Data :
application.properties:
http.stub.api.host=localhost
http.stub.api.port=8080
http.stub.api.path=stub-api
Deployment.yaml file :
volumeMounts:
- name: config-volume
mountPath: /opt/build/
volumes:
- name: config-volume
configMap:
name: integration-properties
items:
- key: application.properties
path: application.properties
When I run this application, it says, "/opt/build/application.properties" does not exists.
Please let me know for any further configuration required, if any and steps to do them.

So define configMap as:
data:
application.properties: |
http.stub.api.host=localhost
http.stub.api.port=8080
http.stub.api.path=stub-api
or you can also create directly from the application.properties file
kubectl create configmap integration-properties --from-file=application.properties=<path to file>
Your deployment.yaml file looks good to me there isn't any problem.

Related

How to read spring boot configuration file in Kubernetes deployment

Im new in Kubernetes and having a hard time making to read application.properties in the deployment. I have attached our ConfigMap as a mounted volume under the /config path.
This is my deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: 34343434.dkr.ecr.asia-2.amazonaws.com/myapp:latest
ports:
- containerPort: 80
volumeMounts:
- name: application-properties
mountPath: /config
volumes:
- name: application-properties
configMap:
name: application-properties
I have created configmap using kubectl command from a file that is located in my local computer.
kubectl create configmap application-properties -–from-file=/users/me/application.properties
Now the issue is the application.property file which i am setting it using the kubectl configmap is not getting picked up. Can you help me on this?
Based on the discussion, the issue was the configmap, instead of the property file, it was rendered as a string in the configmap.
kubectl get configmap application-properties -o yaml
>shows the contents but with all in oneline format. separated by \n
Converting it to YAML application.yml did the trick.

openshift set environment variable from a file

I have a mount volume has a file urls.txt with database source url, like
databasesource: mysql://xxxx
and in my springboot application which will be running as a container in a openshift pod, and in the application I need to change the SPRING_DATASOURCE_URL as mentioned in the file above, here is what I want to achieve in my template file
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
mount:
name: my-volume
key: databasesource
volumeMounts:
- name: my-volume
mountPath: /someDir
I know we can valueFrom configMap or secret, but I want to achieve via a volumeMount
if you can use below format in
urls.txt
databasesource=mysql://xxxx
as part of your container start you run
source /somedir/urls.txt
which will load the key & values in env. which can be further used.
The problem is resolved by a Springboot2.0 feature: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.files.importing-extensionless

Spring Cloud Kubernetes is not loading secret keys with pattern like xx.yy

I am trying to learn about Spring Cloud Kubernetes for loading secrets and what I have observed is if a property has yml like structure, then it doesn't get loaded in app.
Ex:
kind: Secret
metadata:
name: activemq-secrets
labels:
broker: activemq
type: Opaque
data:
amqusername: bXl1c2VyCg==
amq.password: MWYyZDFlMmU2N2Rm
K8 Manifest
template:
spec:
volumes:
- name: secretvolume
secret:
secretName: activemq-secrets
containers:
-
volumeMounts:
- name: secretvolume
readOnly: true
mountPath: /etc/secrets/
jvm args:
-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets/
-Dspring.cloud.kubernetes.secrets.enabled=true
Trying to load #Value("${amqusername}")works
But when I try to read this property with #Value("${amq.password}") I get error with placeholder not found. I have tried printing all spring configs and it doesn't show up. How can I fix this.
Try changing the variable name in the secret to amq_password
Update:
If you use environment variables rather than system properties, most operating systems disallow period-separated key names, but you can use underscores instead (e.g. SPRING_CONFIG_NAME instead of spring.config.name).
https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html

How to populate application.properties file value from kubernetes Secrets mounted as file

I am working on Springboot and Kubernetes and I have really simple application that connects to Postgres database. I want to get the value of datasource from configmap and password from secrets as mount file.
Configmap file :
apiVersion: v1
kind: ConfigMap
metadata:
name: customer-config
data:
application.properties: |
server.forward-headers-strategy=framework
spring.datasource.url=jdbc:postgresql://test/customer
spring.datasource.username=postgres
Secrets File :
apiVersion: v1
kind: Secret
metadata:
name: secret-demo
data:
spring.datasource.password: cG9zdGdyZXM=
deployment file :
spec:
containers:
- name: customerc
image: localhost:8080/customer
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8282
volumeMounts:
- mountPath: /workspace/config/default
name: config-volume
- mountPath: /workspace/secret/default
name: secret-volume
volumes:
- name: config-volume
configMap:
name: customer-config
- name: secret-volume
secret:
secretName: secret-demo
items:
- key: spring.datasource.password
path: password
If I move spring.datasource.password prop from secret to configmap then it works fine or If I populate its value as env variable then also work fine.
But as we know both are not secure way to do so, can someone tell me what's wrong with file mounting for secrets.
Spring Boot 2.4 added support for importing a config tree. This support can be used to consume configuration from a volume mounted by Kubernetes.
As an example, let’s imagine that Kubernetes has mounted the following volume:
etc/
config/
myapp/
username
password
The contents of the username file would be a config value, and the contents of password would be a secret.
To import these properties, you can add the following to your application.properties file:
spring.config.import=optional:configtree:/etc/config/
This will result in the properties myapp.username and myapp.password being set . Their values will be the contents of /etc/config/myapp/username and /etc/config/myapp/password respectively.
By default, consuming secrets through the API is not enabled for security reasons.Spring Cloud Kubernetes requires access to Kubernetes API in order to be able to retrieve a list of addresses of pods running for a single service. The simplest way to do that when using Minikube is to create default ClusterRoleBinding with cluster-admin privilege.
Example on how to create one :-
$ kubectl create clusterrolebinding admin --clusterrole=cluster-admin --serviceaccount=default:default
You need to give secret type in manifest file. Hope it will work.
apiVersion: v1
kind: Secret
metadata:
name: secret-demo
type: Opaque
data:
spring.datasource.password: cG9zdGdyZXM

Spring Boot: override application.yml properties from Kubernetes ConfigMap

I need to oveeride some of properties defined in a application.yml of Spring Boot application running in k8s. How can I do this? The only way I found is to mount whole application.yml but I only need to override one property.
Could be doable in another way, that's why i'm answering again.
Do it in an easy way.
Create your application.yml in a configmap and mount it as a sub directory called config into the same directory where the spring boot jar ins located.
The documentation of spring boot (external application properties) says:
Spring Boot will automatically find and load application.properties and application.yaml files from the following locations when your application starts:
The classpath root
The classpath /config package
The current directory
The /config subdirectory in the current directory
Immediate child directories of the /config subdirectory
Which means we don't have to take care of setting anything. It should find the config inside the subdirectory config.
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-application-config
data:
application.yml: |
spring:
application:
name: This is just an example, add as many values as you want.
pod.yaml:
...
volumeMounts:
- name: spring-application-config
mountPath: /app/config
- name: spring-application-config
configMap:
name: spring-application-config
...
Assuming that your spring boot jar file is located in the path /app
Spring Boot Apps should override properties in the configuration files (aka, in their application.yml most of the times) with environment variables.
Assuming the App is deployed on Kubernetes with a Pod (but it doesn't matter, Deployments, StatefulSets, Jobs are all the same regarding environment) you can inject environment variables inside the container by either directly assigning them to the Deployment itself, or with a ConfigMap (or a Secret, should the property be more secure and masked)
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: example-container
image: example-image:latest
env:
- name: THIS_IS_AN_ENV_VARIABLE
value: "Hello from the environment"
- name: spring.persistence.url
value: "The persistence url, for example"
Even better, you can inject all the content of a ConfigMap as environment variables:
apiVersion: v1
kind: ConfigMap
metadata:
name: example-config
data:
spring.persistence.url: "your persistence url"
spring.mail.user: "your mail user"
And then your Pod:
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: example-container
image: example-image:latest
envFrom:
- configMapRef:
name: example-config
Inside the container, the variables will be in the environment.. and Spring should use them to override variables with same name which are defined (or maybe not even defined) in your application.yml
For more info:
https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/
https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/
https://docs.spring.io/spring-boot/docs/1.3.3.RELEASE/reference/html/boot-features-external-config.html
We had to do something similar. And i explain you how we did. Possibly this helps.
If you can modify your Dockerfile then create an entrypoint.sh script.
It could contain the following:
entrypoint.sh
#!/bin/bash
set -e
# Source custom scripts, if any
if [ -d /etc/spring.d ]; then
for f in /etc/spring.d/*; do
if [ -x "$f" ]; then
echo "Running $f ..." >&2
"$f"
else
echo "Could not run $f, because it's missing execute permission (+x)." >&2
fi
done
unset f
fi
exec "$#"
The entrypoint.sh is executing custom scripts inside the /etc/spring.d directory on startup. You can put any exectuble script inside, whatever you want.
Inside the /etc/spring.d you can create a copy script which copies an application.yml into same directory where the spring boot jar file is located. Example follows.
Your Dockerfile could look like
FROM adoptopenjdk:15-jre
RUN mkdir /app
COPY application.jar /app/
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["java", "-jar", "/app/application.jar"]
If this is prepared, you can use a configMap to define the copy script and mount it to the directory /etc/spring.d
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-d
data:
copy-yaml.sh: |
#!/bin/bash
cp /config/application.yml /app/application.yml
The copy script will take care, that you application.yaml (which will be mounted as a configMap as well) will be copied to the right place.
And a further configMap for your application.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-yaml
data:
application.yml: |
spring:
application:
name: This is just an example, add as many values as you want.
Inside your pod yaml you could do something like this:
...
volumeMounts:
- name: spring-d
mountPath: /etc/spring.d
- name: app-yaml
mountPath: /config
- name: spring-d
configMap:
name: spring-d
defaultMode: 0777
- name: app-yaml
configMap:
name: app-yaml
...
THIS CODE IS CURRENTLY UNTESTED, IT'S JUST SHOWING YOU AN EXAMPLE HOW YOU COULD SOLVE YOUR PROBLEM IN A VERY FLEXIBLE WAY.
I used snippets from my scripts and copied it here together, so maybe there could be small mistakes inside. Please let me know.

Resources