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