前言
基于現(xiàn)代服務(wù)的云原生十二要素理論,我們在采用容器化部署時,要保證同一個鏡像可以滿足不同環(huán)境的部署要求,而不是不同環(huán)境打包不同的景象。本文檔主要介紹一種基于 spring 框架的滿足不同環(huán)境配置的編譯打包方案,滿足同一個鏡像可以在環(huán)境分組下通過啟動項(xiàng)配置實(shí)現(xiàn)不同環(huán)境的部署。
現(xiàn)有方案及問題
我們見過最常見的配置文件管理方案,是基于 Maven 的 profile 配置來實(shí)現(xiàn)多環(huán)境切換的,它的弊端在于,我們將 profile 配置在 pom.xml 中,每次編譯打包時,需要通過編譯指令 - P 來標(biāo)識當(dāng)前環(huán)境配置。這樣導(dǎo)致的問題是,我們打包的鏡像具有了環(huán)境屬性,不符合一個鏡像多環(huán)境部署的要求。
還有一種配置方案,就是基于 Spring profile 進(jìn)行配置文件管理。針對這兩種方案,我接下來會進(jìn)行一些分析和對比。
兩種 profiles 配置的不同和優(yōu)劣對比
a. 兩種 profiles 方案對比
1. Maven 的 profiles:
? Maven 的 profiles 是在構(gòu)建時根據(jù)環(huán)境或其他條件激活不同的配置集合。可以在項(xiàng)目的pom.xml文件中定義多個 profile,并使用 Maven 命令行參數(shù)或其他配置來激活特定的 profile。
? 優(yōu)勢:
? 靈活性高,可以根據(jù)不同的構(gòu)建環(huán)境或條件激活不同的 profile。
? 可以在構(gòu)建過程中使用不同的依賴、插件配置等。
? 劣勢:
? 配置相對分散,需要在 Maven 的pom.xml文件中定義和管理多個 profile。
? 配置與代碼分離,不夠直觀。
2. Spring 的Application.properties的 profile 配置:
? Spring 的application.properties文件可以根據(jù)激活的 profile 來加載不同的配置。可以在application.properties文件中定義多個 profile 下的配置,并使用配置文件或環(huán)境變量來激活特定的 profile。
? 優(yōu)勢:
? 配置集中,可以在一個文件中定義多個 profile 下的配置,更易于管理和維護(hù)。
? 配置與代碼相近,更直觀易讀。
? 劣勢:
? 激活 profile 的方式相對有限,通常需要通過配置文件或環(huán)境變量來指定。
? 不適用于構(gòu)建過程中的配置,主要用于應(yīng)用程序的運(yùn)行配置。
總的來說,Maven profiles 更適用于構(gòu)建過程中的配置,可以根據(jù)構(gòu)建環(huán)境或條件激活不同的 profile,而 Spring 的application.properties的 profile 配置更適用于應(yīng)用程序的運(yùn)行配置,可以根據(jù)不同的 profile 加載不同的配置。具體選擇哪種方式取決于你的需求和偏好。
b. 在云原生和容器化的部署場景分析
在云原生和容器化的部署場景下,我更傾向使用 Spring 的application.properties的 profile 配置方式。
以下是在云原生和容器化部署場景下,使用application.properties的 profile 配置方式的優(yōu)勢:
- 環(huán)境無關(guān)性:application.properties可以根據(jù)激活的 profile 加載不同的配置,使得應(yīng)用程序在不同的環(huán)境中運(yùn)行時具有一致的行為。這對于云原生和容器化的部署非常重要,因?yàn)閼?yīng)用程序可能需要在不同的環(huán)境(例如開發(fā)、測試、生產(chǎn))中運(yùn)行。
- 配置集中化:使用application.properties的 profile 配置方式,可以在一個文件中定義多個 profile 下的配置,使得配置管理更加集中和方便。這對于云原生和容器化的部署場景非常有幫助,因?yàn)榭梢愿鶕?jù)不同的 profile 加載適當(dāng)?shù)呐渲茫鵁o需分散地管理多個 Maven profiles。
- 容器友好性:在容器化的部署場景中,通常使用容器編排工具(如 Kube.NETes)來管理應(yīng)用程序的配置和部署。使用application.properties的 profile 配置方式,可以通過環(huán)境變量或配置文件中的屬性來指定激活的 profile,從而實(shí)現(xiàn)與容器編排工具的集成。
綜上所述,使用 Spring 的 application.properties 的 profile 配置方式更適合云原生和容器化的部署場景,因?yàn)樗峁┝谁h(huán)境無關(guān)性、配置集中化和容器友好性的優(yōu)勢。
基于 properties 的多環(huán)境配置方案實(shí)踐
以下方案以 springboot 為例,當(dāng)然 springMVC 方案也是可以適配,只是需要額外配置一下,介于我們新項(xiàng)目基本都是基于 springboot 搭建,此處不展開 springMVC 的實(shí)線方案,如有需要,我再額外提供 mvc 的配置方案。
a. 配置文件樹
如上圖所示
- properties 文件夾:我們在 properties 文件夾下,基于不同的環(huán)境,簡歷單獨(dú)文件夾,比如 dev,online, test, uat 等,每個文件夾下存放當(dāng)前環(huán)境的配置信息。
- spring 文件夾: 下存放 xml 配置信息,比如常見的 JSF 配置 (jsf.xml), 以及其他需要通過 xml 配置注入的業(yè)務(wù)場景。當(dāng)然基于 springboot 官網(wǎng)建議,大家盡量用注解代碼方式實(shí)現(xiàn) bean 注入,盡量減少 xml 的方式。
- application.properties 文件: 該文件里通過核心的配置 spring.profiles.active=** 來標(biāo)識當(dāng)前是哪個 profile 環(huán)境。當(dāng)然一些其他全局類配置也可以放在此處。需要注意 application.properties 文件需要配置在行云部署中分組配置里,因?yàn)榇宋募枰诓煌牟渴鸱纸M進(jìn)行文件覆蓋,以改變 spring.profiles.active 的值,如下圖所示。當(dāng)然,也可以通過運(yùn)行時啟動指令,進(jìn)行不同的 profile 選擇。
- important.properties 文件: 該文件為京東行云部署規(guī)約,把秘鑰等安全性高的文件以加密存儲的方式存放在該文件中,并部署到行云部署分組的遠(yuǎn)程配置里。
具體行云部署里的配置如下:
b. properties 文件加載
正常情況下,properties/**/*.properties 下的配置文件是不會自動加載到啟動項(xiàng)里的。所以需要通過額外的方式動態(tài)加載,具體方法是通過 spring 框架下的 PropertySourcesPlaceholderConfigurer 的類屬性,結(jié)合環(huán)境變量,動態(tài)批量加載配置文件。(額外說明,如果是 springMVC 框架,也可以通過 xml 配置 context:property-placeholder 屬性來實(shí)現(xiàn)。)
具體代碼如下:
/**
* 配置文件環(huán)境配置
* @Author zhaoyongping
* @date 2023/7/10 15:13
* @ClassName EnvPropertiesConfig
* @Descripiton 配置文件環(huán)境配置
**/
@Configuration
public class EnvPropertiesConfig {
/**
* 加載屬性配置
* @param environment 環(huán)境屬性
* @return PropertySourcesPlaceholderConfigurer
* @author zhaoyongping
* @date 2023/7/10 15:13
* @description 加載屬性配置
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfig(Environment environment) throws IOException {
PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer();
String[] activeProfiles = environment.getActiveProfiles();
if (activeProfiles.length > 0) {
String resourceUrl = "classpath:properties/"
+ environment.getActiveProfiles()[0] + "/*.properties";
config.setLocations(
new PathMatchingResourcePatternResolver().getResources(resourceUrl));
} else {
//兜底策略
config.setLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:properties/dev/*.properties"));
}
return config;
}
}
總結(jié)
通過以上步驟,我們可以實(shí)現(xiàn)編譯打包鏡像不需要跟環(huán)境變量綁定,而只需要在啟動運(yùn)行時基于不同的分組動態(tài)配置的 applicAIton.properties 文件,來實(shí)現(xiàn)不同環(huán)境的適配。這種可以在運(yùn)行時變更配置文件的機(jī)制,更適合在云原生時代下的容器化部署方案,也利于我們的服務(wù)的可移植性。當(dāng)然,以上只是筆者個人的一個實(shí)踐經(jīng)驗(yàn),并不能代表它是最優(yōu)實(shí)踐方案。
作者:京東物流 趙勇萍
來源:京東云開發(fā)者社區(qū) 自猿其說 Tech 轉(zhuǎn)載請注明來源