I am trying to support K8s ConfigMap API using Spring to handle my externalized configuration. I've been using a configuration service without any issues, so I don't believe it is a cloud-config implementation problem. I am using version 1.0.2.RELEASE of spring-cloud-starter-kubernetes-config.
My ConfigMap consists of two configurations: global (application.yml) and app-specific yaml (myapp.yml). Here is what that looks like:
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
application.yml: |-
kafka:
hosts: kafka:9092
topics:
events: myapp-events
normalized: myapp-normalized
#...
storage:
uri: mongodb://mongo-0.mongo:27017,mongo-1.mongo:27017,mongo-2.mongo:27017
database: do-stuff-db
#...
spring:
cloud:
config:
override-system-properties: false
#...
myapp.yml: |-
myapp:
broadcast-address: 0.0.0.0:666
#...
application.yml seems to work just fine. I can see the configuration values in my application but for whatever reason, myapp.yml does not get set. I can see Spring is successfully grabbing the entire result from the ConfigMap API but it does not do anything with myapp.yml.
Turning on debug logging exposed how Spring is interpreting the ConfigMap result. It is treating the entire myapp.yml section as one property and not its own property source. This explains why the application.yml is taking and not the myapp.yml. Here is what that log looks like (I have truncated the message):
2019-09-04 14:01:37.083 [main] INFO config.PropertySourceBootstrapConfiguration - Located property source: CompositePropertySource {name='composite-configmap', propertySources=[ConfigMapPropertySource#92864491 {name='configmap.myapp-config.default', properties={kafka.hosts=kafka:9092, kafka.topics.events=myapp-events, kafka.topics.normalized=myapp-normalized, myapp.yml=myapp:
bind-address: 0.0.0.0:666
#...
storage.uri: mongodb://mongo-0.mongo:27017,mongo-1.mongo:27017,mongo-2.mongo:27017, storage.database: do-stuff-db}}]}
Do I need to separate these configurations? The examples I've seen from Spring suggest you could consolidate your configuration into a single YAML ConfigMap file. I am stumped.
Related
According to the official resource, logging configuration relies on application.properties file.
Now I need to have several configuration according to the cluster in use (let's say we have the typical dev, staging and production environments, thus dev should have a DEBUG level and production at least INFO).
At first I thought using Kubernetes ConfigMaps, but I can't see any connection with quarkus logging.
How can I solve this issue?
EDIT:
This is my ConfigMap
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-cm-config-map
namespace: default
uid: d992d86f-c247-471d-8e31-53e9a1858b76
resourceVersion: '8484'
creationTimestamp: '2021-04-22T13:12:43Z'
managedFields:
- manager: kubectl-create
operation: Update
apiVersion: v1
time: '2021-04-22T13:12:43Z'
fieldsType: FieldsV1
fieldsV1:
'f:data':
.: {}
'f:myenv': {}
'f:myname': {}
- manager: kubectl-edit
operation: Update
apiVersion: v1
time: '2021-04-22T16:52:18Z'
fieldsType: FieldsV1
fieldsV1:
'f:data':
'f:log.file.level': {}
- manager: dashboard
operation: Update
apiVersion: v1
time: '2021-04-23T08:03:06Z'
fieldsType: FieldsV1
fieldsV1:
'f:data':
'f:quarkus.log.file.level': {}
data:
log.file.level: DEBUG
myenv: cl1
myname: cluster1
quarkus.log.file.level: DEBUG
EDIT2
This is my config map (through command kubectl edit cm ):
apiVersion: v1
data:
QUARKUS_LOG_FILE_ENABLE: "true"
QUARKUS_LOG_FILE_FORMAT: '%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n'
QUARKUS_LOG_FILE_LEVEL: ERROR
QUARKUS_LOG_FILE_PATH: /tmp/kube-cm.log
myenv: cl1
myname: cluster 1b
kind: ConfigMap
metadata:
creationTimestamp: "2021-04-22T13:12:43Z"
name: kube-cm-config-map
namespace: default
resourceVersion: "39810"
uid: d992d86f-c247-471d-8e31-53e9a1858b76
If you are using Kubernetes resource yaml to deploy your app, use the snippet below to push your custom ConfigMap as environment variables to your applicaton (https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables):
spec:
containers:
- name:
image:
envFrom:
- configMapRef:
name: kube-cm-config-map
Use a different ConfigMap for each environment but with the same name. If your environments (dev/qa/etc) are Kubernetes namespaces, then it is very easy to setup. Just duplicate the ConfigMap in each namespace, and change the log level value in each namespace.
Also, change the naming convention for your ConfigMap properties from log.file.level to LOG_FILE_LEVEL
See https://quarkus.io/guides/config-reference#environment_variables
Ok solved by editing the cm in the following way:
data:
QUARKUS_LOG_FILE_ENABLE: "true"
QUARKUS_LOG_FILE_FORMAT: '%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n'
QUARKUS_LOG_FILE_LEVEL: ERROR
QUARKUS_LOG_FILE_PATH: /tmp/kube-cm.log
Then I set application.properties on quarkus with:
quarkus.kubernetes.env.configmaps=kube-cm-config-map
There are 2 ways to use a ConfigMap in Quarkus to read runtime configuration.
The first is to let Quarkus query the API server using the quarkus-kubernetes-config extension which is described here.
The second way to configure the Kubernetes Deployment to turn ConfigMap values into environment variables for the Pod. This can be done with the quarkus-kubernetes extension which is described here.
So you would add the proper quarkus logging configuration (i.e a key value pair) in the ConfigMap and then use one of the above methods to use that at runtime
I have these KVs sources store on Consul:
config/books/<key>
config/common/<key>
And in my spring boot app application.yml, I have config it as following:
spring:
application:
name: sampleapp
cloud:
consul:
host: localhost
port: 8500
config:
enabled: true
prefix: config
defaultContext: books
I know that this configuration that I made has pointed the app to read from config/books .
But I can't figure out how can I retrieve the Consul KV config from both config/common and config/books.
It's not really flexible there, but it can be done if you rename your project and use defaultContext as well. Based on the documentation Spring Cloud Consul Config takes next paths:
config/testApp,dev/
config/testApp/
config/application,dev/
config/application/
where testApp - name of your app, application - default context
So with the following configuration it will work
spring:
application:
name: books
cloud:
consul:
host: localhost
port: 8500
config:
enabled: true
prefix: config
defaultContext: common
Logs after running this configuration:
Located property source: CompositePropertySource {name='consul', propertySources=[ConsulPropertySource {name='config/books/'}, ConsulPropertySource {name='config/common/'}]}
I just ran jhipster registry and its working fine. It's looking for config files from central-config folder. I want to refactor my config files under folders in the central-config folder itself. That's something that I can achieve running Spring Cloud Config server with a configuration like this:
spring:
cloud:
config:
server:
git:
default-label: develop
uri: file://${user.home}/config-repo
search-paths: employee-service, enterprise-service
How can I achieve such behavior with 'composite thing' in jhipster-registry. For info, this is the bootstrap.yml file from jhipster registry:
# ===================================================================
# Spring Cloud Config bootstrap configuration for the "dev" profile
# In prod profile, properties will be overwriten by the ones defined in bootstrap-prod.yml
# ===================================================================
spring:
application:
name: jhipster-registry
profiles:
active: dev
include: composite
cloud:
config:
server:
bootstrap: true
composite:
- type: native
search-locations: file:./central-config
#search-locations: file://${user.home}/Acensi/isupplier/config-repo
prefix: /config
fail-fast: true
# name of the config server's property source (file.yml) that we want to use
name: jhipster-registry
profile: dev # profile(s) of the property source
label: master # toggle to switch to a different version of the configuration as stored in git
# it can be set to any label, branch or commit of the config source git repository
info:
project:
version: #project.version#
# uncomment to enable encryption features
#encrypt:
# key: my-secret-encryption-key-to-change-in-production
I had the same issue. I added to my application.yml file the following:
spring:
cloud:
config:
server:
bootstrap: true
composite:
- type: native
search-locations: file:./central-config/myFolder
You were very close, hope it helps.
I was upgrading an existing older JHipster Registry to 5.0.1. I was also using the git repo. The problem with the above configurations is that the property is not called search-locations for the git repo it is search-paths. In order to keep everything working as before which was had the search paths specified via an environment variable SPRING_CLOUD_CONFIG_SERVER_GIT_SEARCH-PATHS, I use the following configuration in my prod bootstrap.
bootstrap-prod.yml:
spring:
cloud:
config:
server:
bootstrap: true
composite:
- type: git
uri: git#bitbucket.org:whatever/repo
search-paths: ${spring.cloud.config.server.git.search-paths}
ignore-local-ssh-settings: true
private-key: |
-----BEGIN RSA PRIVATE KEY-----
I am working on a Spring Boot Registration Server(Eureka Server).
Currently it is working with below configuration.
Project Name: registration-service
Inside main method: System.setProperty("spring.config.name", "registration-service");
"yml file":
file name: registration-service
content:
eureka:
instance:
hostname: eureka-server
server:
enableSelfPreservation: false
client:
register-with-eureka: false
fetch-registry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
port: 2323 # HTTP (Tomcat) port
spring:
application:
name: eureka-server
With above configuration,application start running on 2323.
But if i change spring.config.name,it does not work,start giving connection refused exception.
why it is happening? even though this spring.config.name is no where used in yml file. Should it be necessarily same as project name? or it is specific to #EnableEurekaServer enabled spring boot application.
And in yml we have to write
spring:
application:
name: eureka-server
though in other spring boot application we give name of current project(here it should be registration-service).why we have to write here eureka-server? I know,I am missing something(or a lot of things).please help me in understanding the missing part.
Spring boot by default looks for file application.yml. If u have different profiles in your application it can also look for application-{profilename}.yml. This is the default convention followed.
spring.config.name property is used to override this default behaviour. When u override this property with register-service then spring boot looks for a file register-service.yml and loads config from that.
So your eureka server url which is given in the register-service.yml file may not be available in the default application.yml file. Hence when u change the value Spring boot may not be avle to find the Eureka server url.
Keep the names unchanged, as much as possible. If u have config file as register-service.yml then keep the spring.config.name=register-service. If you change this value then u need to create the new file with config.name value and then add eureka configuration to that again.
We have different config servers per environment. Each spring boot application should target its corresponding config server. I have tried to achieve this by setting profiles in the bootstrap.properties file, e.g.:
spring.application.name=app-name
spring.cloud.config.uri=http://default-config-server.com
---
spring.profiles=dev
spring.cloud.config.uri=http://dev-config-server.com
---
spring.profiles=stage
spring.cloud.config.uri=http://stage-config-server.com
---
spring.profiles=prod
spring.cloud.config.uri=http://prod-config-server.com
And then I set the cla -Dspring.profiles.active=dev but the loaded config server is always the last one set in the file (i.e. prod config server would be loaded in the above settings, and then if prod is removed, stage would be loaded).
Is it possible to set bootstrap profiles for the cloud config server? I followed this example but can't seem to get it working. For what it's worth, these profiles work great to load the correct config (i.e. app-name-dev.properties will load if the dev profile is active), but aren't being pulled from the proper config server.
Specifying different profiles in a single file is only support for YAML files and doesn't apply to property files. For property files specify an environment specific bootstrap-[profile].properties to override properties from the default bootstrap.properties.
So in your case you would get 4 files bootstrap.properties, bootstrap-prod.properties, bootstrap-stage.properties and bootstrap-dev.properties.
However instead of that you could also only provide the default bootstrap.properties and when starting the application override the property by passing a -Dspring.cloud.config.uri=<desired-uri> to your application.
java -jar <your-app>.jar -Dspring.cloud.config.uri=<desired-url>
This will take precedence over the default configured values.
I solved a similar problem with an environment variable in Docker.
bootstrap.yml
spring:
application:
name: dummy_service
cloud:
config:
uri: ${CONFIG_SERVER_URL:http://localhost:8888/}
enabled: true
profiles:
active: ${SPR_PROFILE:dev}
Dockerfile
ENV CONFIG_SERVER_URL=""
ENV SPR_PROFILE=""
Docker-compose.yml
version: '3'
services:
dummy:
image: xxx/xxx:latest
restart: always
environment:
- SPR_PROFILE=docker
- CONFIG_SERVER_URL=http://configserver:8888/
ports:
- 8080:8080
depends_on:
- postgres
- configserver
- discovery
#LarryW (I cannot answer on the same comment):
I guess the advantage of explicitly adding the property is that it allows you to add a default value (in this case "dev") in case of not setting up the environment variable.