如何在 Jenkins + Maven 构建过程中处理不同环境的配置值?
推荐做法
如果应用允许调整配置加载方式,建议在部署或启动时注入环境配置,让 QA、UAT、PROD 使用同一份构建产物。测试通过的 JAR 就可以直接用于生产环境,修改服务地址也不需要重新编译。
如果当前应用必须把配置写入安装包,可以使用 Maven resource filtering 配合 Jenkins 环境参数:
- 在 Jenkins 中选择目标环境。
- Maven 读取对应的
.properties文件,替换资源文件中的占位符。 - 有构建差异需要管理时,再使用 Maven profiles。
1. 将配置模板与环境值分开存放
以普通的 Maven Java 项目为例,目录如下:
pom.xml
Jenkinsfile
config/
QA_CONF.properties
UAT_CONF.properties
PROD_CONF.properties
src/main/resources/
configuration.properties
src/main/resources/configuration.properties 中只保留占位符:
LOAD_BALANCE_URL=@LOAD_BALANCE_URL@
USER_NAME=@USER_NAME@
实际值分别保存在三个环境文件中。config/QA_CONF.properties 的内容为:
LOAD_BALANCE_URL=http://QA.server.com
USER_NAME=QA_USER
config/UAT_CONF.properties:
LOAD_BALANCE_URL=http://UAT.server.com
USER_NAME=UAT_USER
config/PROD_CONF.properties:
LOAD_BALANCE_URL=http://PROD.server.com
USER_NAME=PROD_USER
Maven 默认支持 ${...} 和 @...@ 形式的占位符,无法按默认规则识别原来的 <LOAD_BALANCE_URL>。这里使用 @...@,便于和应用运行时使用的 ${...} 区分。Maven 资源过滤说明
将环境值文件放在 src/main/resources 之外,可以避免把所有环境的配置一起打入应用包。非敏感配置可以保存在 Git 中,密码、令牌等应单独管理。
2. 配置 Maven resource filtering
将以下配置合并到现有 pom.xml 的 <build> 中:
<build>
<filters>
<filter>config/${deploy.env}_CONF.properties</filter>
</filters>
<resources>
<!-- 仅过滤配置模板 -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>configuration.properties</include>
</includes>
</resource>
<!-- 其他资源按原样复制 -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>configuration.properties</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<encoding>UTF-8</encoding>
<propertiesEncoding>UTF-8</propertiesEncoding>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
</plugins>
</build>
示例指定了插件版本。如果项目已有父 POM 统一管理版本,应遵循项目约定。useDefaultDelimiters=false 配合自定义分隔符,让资源过滤只处理 @...@。Resources Plugin 参数说明
构建 QA 包:
mvn -B -Ddeploy.env=QA clean verify
Maven 会读取 config/QA_CONF.properties,在 process-resources 阶段生成:
target/classes/configuration.properties
生成文件的内容应为:
LOAD_BALANCE_URL=http://QA.server.com
USER_NAME=QA_USER
资源处理发生在编译、打包之前,源目录中的模板保持原样。普通 JAR 项目会在后续打包时将生成的资源放入 JAR。Resources Plugin 执行阶段
这套配置处理的是 .properties 资源文件,不会替换 src/main/java 下 .java 文件中的占位符。如果环境值写在 Java 源码中,建议先将它们移到资源配置中,再由 Java 代码读取。
3. 在 Jenkins 中选择目标环境
以下示例适用于从 SCM 加载的 Jenkins Declarative Pipeline,假设构建节点使用 Linux,且已配置好 JDK 和 Maven:
pipeline {
agent any
parameters {
choice(
name: 'TARGET_ENV',
choices: ['QA', 'UAT', 'PROD'],
description: '选择目标环境'
)
}
stages {
stage('Build and verify') {
steps {
sh '''
set -eu
case "$TARGET_ENV" in
QA|UAT|PROD) ;;
*)
echo "Unsupported target environment"
exit 1
;;
esac
test -s "config/${TARGET_ENV}_CONF.properties"
mvn -B "-Ddeploy.env=$TARGET_ENV" clean verify
'''
}
}
}
}
Jenkins 会将构建参数导出为环境变量,Shell 可以通过 $TARGET_ENV 读取。脚本还会检查参数是否为允许的值,避免环境名称拼写错误或传入任意配置路径。Jenkins parameters 文档
Jenkins 只传递环境名称,具体配置仍保存在 Git 文件中。本地开发时也可以使用相同的 Maven 命令重现构建。
4. 什么时候使用 Maven profiles?
如果环境之间只有配置值不同,使用前面的 -Ddeploy.env=QA 就够了。
如果项目习惯通过 -Pqa 选择环境,可以增加 profile:
<profiles>
<profile>
<id>qa</id>
<properties>
<deploy.env>QA</deploy.env>
</properties>
</profile>
<profile>
<id>uat</id>
<properties>
<deploy.env>UAT</deploy.env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<deploy.env>PROD</deploy.env>
</properties>
</profile>
</profiles>
然后执行:
mvn -B -Puat clean verify
这里的 profile 只设置 deploy.env,占位符仍由 resource filtering 替换。profiles 也可以调整插件、依赖等构建配置,但这些调整会扩大不同环境之间的产物差异,应在确有需要时添加。Maven profiles 文档
生产流水线应显式选择环境,不建议将生产配置设为默认值。
5. Jenkins environment variables 能直接替换配置吗?
环境变量本身不会改写 .properties 文件,普通 Java Properties 也不会自动替换其中的占位符。具体如何读取,取决于使用方式:
| 使用方式 | 读取方式 |
|---|---|
| Maven 构建时读取环境变量 | 资源模板使用 ${env.LOAD_BALANCE_URL},并启用 ${...} 过滤 |
| Maven 接收命令行属性 | 使用 -DLOAD_BALANCE_URL=...,再由 resource filtering 引用 |
| Java 应用运行时读取环境变量 | 调用 System.getenv("LOAD_BALANCE_URL") |
| Java 应用运行时读取 JVM 属性 | 调用 System.getProperty("LOAD_BALANCE_URL") |
如果沿用前面只识别 @...@ 的设置,Maven 环境变量占位符应写为:
LOAD_BALANCE_URL=@env.LOAD_BALANCE_URL@
建议选定一种主要的配置来源。如果同时用 Git 文件、Maven 属性和 Jenkins 环境变量覆盖同一个键,就需要明确覆盖顺序,否则很难排查配置究竟来自哪里。
6. 长期维护时,考虑运行时外部配置
如果应用支持从外部文件读取配置,可以让 Jenkins 只构建一次:
mvn -B clean verify
部署时,将对应环境的配置放到服务器上,例如:
/etc/myapp/configuration.properties
对于可执行 JAR,可以约定使用以下启动命令:
java -Dapp.config=/etc/myapp/configuration.properties -jar app.jar
app.config 是应用自行定义的属性。代码需要先读取 System.getProperty("app.config"),再加载该路径下的文件。单独添加这个参数不会自动改变 Java 的配置加载行为。
如果文件使用 UTF-8 编码,可以通过指定 UTF-8 的 Reader 调用 Properties.load(Reader)。注意,Properties.load(InputStream) 使用的是 ISO-8859-1,两种加载方式的编码不同。Java Properties API
如果应用已有配置框架,应优先使用框架提供的外部配置机制,并核对其配置优先级。
7. 上线前需要检查什么?
- 检查必填配置是否存在、是否为空,以及是否残留
@LOAD_BALANCE_URL@等占位符。Maven 构建成功并不代表配置完整,可以在构建测试中加入校验,校验失败时禁止发布。 - 检查生成文件和最终制品。使用 Spring Boot 重打包、WAR 或其他打包插件时,配置在包内的位置可能不同。
- 只对需要替换的文本文件开启过滤,避免损坏二进制资源。
- 使用构建时配置,QA、UAT、PROD 会生成内容不同的制品。保存时应区分环境,避免相互覆盖,并记录代码与配置的 Git 提交。
- 密码和令牌应使用 Jenkins Credentials 或部署平台的密钥管理功能管理。日志脱敏无法防止秘密进入 JAR,也无法防止流水线代码主动泄露凭据,因此不要把秘密过滤进可分发的应用包。Jenkins 凭据处理说明
备注:内容仅供参考。
