背景介紹
筆者最近基于kubernetes部署一套系統(tǒng)時,發(fā)現(xiàn)了一個有趣的現(xiàn)象:配置文件內(nèi)的部分變量讀取錯誤,報錯日志如下
2025-06-13T17:08:54.591570855+08:00 Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "tcp://10.233.17.38:9200"
配置片段如下:
es:
clusterName: es-cluster
connectTimeout: 5000
connectionRequestTimeout: 5000
enableAuth: false
host: es.demo
maxConnectNum: 100
maxConnectPerRoute: 100
password: elastic
port: 9200
明明port配置的9200,為什么取的卻是tcp://10.233.17.38:9200?
筆者開始了漫漫的探索真相之路。
ps: es與java應(yīng)用部署在同一命名空間下,且均配置了service
猜想階段
- 猜想配置文件未讀取到
由于部署的是spring boot應(yīng)用,猜想可能配置的優(yōu)先級問題(畢竟spring boot可以通過多個參數(shù)配置)
論證:首先排除這種情況,因?yàn)榕渲梦募?nèi)的其他配置項(xiàng)(如數(shù)據(jù)庫連接項(xiàng))是可以正確讀取到的。
猜想失敗
- 猜想配置文件格式問題,導(dǎo)致的配置項(xiàng)未正確讀取到
論證:筆者將配置內(nèi)容脫敏后,使用yaml校驗(yàn)工具校驗(yàn),發(fā)現(xiàn)無誤。
猜想失敗
- 猜測代碼內(nèi)取值錯誤
論證:筆者將部署包反編譯后,查看配置項(xiàng)取值,發(fā)現(xiàn)無誤。
猜想失敗
求索階段
由于部署時間緊,沒有太多的時間去研究這個問題。筆者干脆通過命令行參數(shù)的形式(--es.port=9200)傳入配置項(xiàng),使其生效,問題解決。
系統(tǒng)部署交付完成后,這個問題一直困擾著筆者,想了解為什么會存在這種問題?于是筆者借助deepseek分析可能原因。deepseek給出了以下幾種可能:
- 配置值包含非數(shù)字字符 -> 修正配置為純數(shù)字端口
- 字段類型與配置類型不匹配
- 使用類型安全綁定(@ConfigurationProperties)
- 配置值被意外覆蓋 -> 檢查配置優(yōu)先級(環(huán)境變量>配置文件)
- 缺少 setter 方法 為配置類的每個字段添加 setter
其中第4種情況引起了筆者的注意,筆者了解到環(huán)境變量是能覆蓋配置文件內(nèi)的配置項(xiàng)的。
筆者于是進(jìn)入該應(yīng)用pod查看環(huán)境變量:
$ env|grep ES_PORT
KUBERNETES_PORT_443_TCP=tcp://10.233.0.1:443
ES_PORT_9200_TCP_ADDR=10.233.17.38
ES_PORT=tcp://10.233.17.38:9200
KUBERNETES_PORT_443_TCP_PROTO=tcp
ES_PORT_9200_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.233.0.1
KUBERNETES_PORT=tcp://10.233.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
ES_PORT_9200_TCP_PORT=9200
ES_PORT_9200_TCP=tcp://10.233.17.38:9200
似乎我們接近了真相!
此時,筆者又有了新的疑惑:這些環(huán)境變量是誰注入進(jìn)來的?
于是再次請教deepseek,deepseek給出了 “罪魁禍?zhǔn)住?-> Kubernetes 服務(wù)自動注入變量
并給出解決方案:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
enableServiceLinks: false # 關(guān)鍵配置!
containers:
- name: my-app
image: my-image
筆者按照該樣例對問題應(yīng)用進(jìn)行配置,應(yīng)用啟動正常,可以正確讀取到配置文件內(nèi)的配置變量!筆者進(jìn)入pod查看環(huán)境變量,ES相關(guān)的環(huán)境變量全部消失了。
由此筆者得出了導(dǎo)致此次問題的原因:k8s的服務(wù)自動注入變量機(jī)制生成了”錯誤的配置環(huán)境變量值“,并被java程序取到,導(dǎo)致此次血案的發(fā)生。
進(jìn)一步思索
- 到底是“誰”讀取環(huán)境變量的值,覆蓋配置文件中的值?
筆者,第一時間想到的是es client,認(rèn)為是es client在初始化階段,顯示地讀取環(huán)境變量ES_PORT。于是乎翻閱了初始化es client源碼,未果。
于是又求助deepseek探求真相,deepseek給出
提問 : 為什么 Elasticsearch 配置優(yōu)先使用環(huán)境變量
↓以下是deepseek給出的回答:
1. Spring Boot 自動配置機(jī)制
當(dāng)使用 spring-boot-starter-data-elasticsearch 時,自動配置類 ElasticsearchRestClientAutoConfiguration 會讀取屬性:
Java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class) // ?? 關(guān)鍵類
public class ElasticsearchRestClientAutoConfiguration {
// ...
}
其中的 ElasticsearchRestClientProperties 通過標(biāo)準(zhǔn) Spring 機(jī)制綁定屬性,遵守屬性優(yōu)先級規(guī)則。
2. 環(huán)境變量命名規(guī)則
Spring Boot 自動將環(huán)境變量名轉(zhuǎn)為 Spring 屬性名:
配置文件屬性:spring.elasticsearch.uris=http://localhost:9200
等效環(huán)境變量名:SPRING_ELASTICSEARCH_URIS=http://localhost:9201
如果設(shè)置了 SPRING_ELASTICSEARCH_URIS 環(huán)境變量,它會覆蓋配置文件中的值。
至此,真相水落石出。deepseek給出了解決方案:
# application.properties
spring.config.use-environment-overrides=false
PS: spring boot程序讀取配置優(yōu)先級如下(后者覆蓋前者):
- 默認(rèn)屬性(通過 SpringApplication.setDefaultProperties 設(shè)置)
- @Configuration 類上的 @PropertySource
- 配置文件 (application.properties 或 application.yml)
- 隨機(jī)屬性 (random.*)
- 操作系統(tǒng)環(huán)境變量
- Java 系統(tǒng)屬性 (System.getProperties())
- JNDI 屬性