配置管理是每个项目上线前绕不开的一课。JHipster 在生成时就替你搭好了 dev/prod 两套 profile 骨架,但”骨架正确”和”团队用对”是两回事。我们组吃过配置的亏:测试环境连的库被人手滑改成生产、JWT secret 提交进了 git 导致全员换密钥。这篇把这些年的配置管理经验整理成一套可复制的做法。
JHipster 生成的配置结构
生成完成后的 src/main/resources/config/ 长这样:
src/main/resources/
├── application.yml # 公共配置(所有 profile 共享)
├── config/
│ ├── application-dev.yml # 开发:H2 内存库、热重载、mock 数据
│ ├── application-prod.yml # 生产:真实数据源、监控、日志文件
│ ├── application-tls.yml # 可选 TLS
│ └── liquibase/ # 数据库 changelog
└── .well-known/
Spring Boot 4 延续了既有的 profile 机制:spring.profiles.active=dev 激活对应文件,未激活的配置块跳过。JHipster 默认在 application.yml 里写入:
spring:
profiles:
active: @spring.profiles.active@ # Maven resource filter 替换
# explicit-user-config-requires-refresh 见官方文档
jhipster:
registry:
password: ${JHIPSTER_REGISTRY_PASSWORD:admin}
注意那行 @spring.profiles.active@——Maven 的 resource filtering 会在打包时按 -Pprod 或 -Pdev 替换。所以运行 jar 时若不指定 profile,行为取决于打包时用的 profile,这是新手常见困惑点。我们团队约定:打生产包必须 ./mvnw -Pprod package verify,且启动脚本里显式传 --spring.profiles.active=prod 双保险。
dev 与 prod 的关键差异
| 配置项 | dev | prod | 备注 |
|---|---|---|---|
| 数据库 | H2 内存/文件 | PostgreSQL/MySQL | prod 必须显式配置 |
| Hibernate DDL | create-drop(H2) | validate + Liquibase | 生产严禁自动建表 |
| Liquibase | 启动执行 | 启动执行(异步可选) | prod 可开 async-start |
| 日志 | 控制台 DEBUG | 文件 + 级别收敛到 INFO | 见下文日志路径 |
| 缓存 | ehcache 本地 | Redis(选型后) | 微服务必须分布式缓存 |
| CORS | 全放开 | 收紧到具体域名 | 安全项 |
| Swagger | 开启 | 默认关闭 | 生产按需网关暴露 |
| metrics/health 暴露 | 全量 | 按需 | 配合管理端口 |
一份典型的 application-prod.yml 片段:
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:postgresql://${DB_HOST:localhost}:5432/${DB_NAME:myapp}
username: ${DB_USER}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
jpa:
hibernate:
ddl-auto: validate
liquibase:
async-start: true
server:
port: 8080
logging:
file:
name: /var/log/myapp/app.log
level:
root: INFO
tech.myapp: INFO
jhipster:
security:
authentication:
jwt:
secret: ${JWT_SECRET}
token-validity: 86400
logging:
use-json-format: true
logstash:
enabled: true
敏感信息不进 git 的三道防线
配置里的密码、secret、密钥,永远不该出现在版本库。我们有三条原则,层层兜底:
第一道:环境变量(默认手段)。 所有生成配置里敏感项都写成 ${DB_PASSWORD} 这种占位形式,值来自环境变量或启动参数。本地开发起服务:
export DB_PASSWORD=devpass
./mvnw -Pdev spring-boot:run
第二道:本地私有配置文件(开发体验)。 每人本机的私有配置放进 application-local.yml,配合 .gitignore:
# 本地私有配置,不入库
src/main/resources/config/application-local.yml
启动时激活叠加:--spring.profiles.active=dev,local。注意 local 放在最后,优先级最高,可以覆盖任何 dev 默认值。
第三道:运行时密钥管理(生产)。 生产环境用 K8s Secret 或 Vault 注入,不给任何人明文:
# k8s deployment 片段
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-db
key: password
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: myapp-jwt
key: secret
- name: SPRING_PROFILES_ACTIVE
value: prod
- name: SPRING_CONFIG_ADDITIONAL_LOCATION
value: file:/etc/myapp/ # 挂载的 Secret 目录
另外在 CI 里加一道防泄漏扫描(git-secrets 或 trufflehog),防止有人不小心 commit 了 .env 文件。JWT secret 尤其注意:至少 86 位随机 Base64(HS512 要求),我们被扫描器扫出弱 secret 后全部改成了 openssl rand -base64 192 生成。
配置优先级与覆盖关系
Spring Boot 的配置覆盖顺序(从低到高,高者生效):
| 优先级 | 来源 | 典型用途 |
|---|---|---|
| 1 | application.yml(jar 内) | 公共默认值 |
| 2 | application-{profile}.yml(jar 内) | 环境默认值 |
| 3 | jar 外 config/ 目录 | 部署时覆盖 |
| 4 | SPRING_CONFIG_ADDITIONAL_LOCATION | K8s 挂载 Secret |
| 5 | 环境变量 | 容器注入 |
| 6 | 命令行参数 --xxx=yyy | 临时救急 |
几个实战推论:
- 环境变量命名规则:
spring.datasource.password对应SPRING_DATASOURCE_PASSWORD(点换下划线、全大写)。 - local profile 放最后:
dev,local中 local 覆盖 dev,反过来则无效。 - 命令行参数只用于临时:写进启动脚本前先想清楚它是否该进 application-prod.yml,散落的启动参数是配置漂移的起点。
- JHipster 特有项:
jhipster.*命名空间下的开关(缓存、安全、日志)同样遵循这套优先级。
微服务场景:Spring Cloud Config
微服务架构下(第 5 篇的 gateway + registry + 服务拓扑),配置问题从”一个应用两套环境”变成”八个服务 × 三套环境”。JHipster 微服务默认方案是注册中心自带配置分发:JHipster Registry(或 Consul)充当 Spring Cloud Config Server,各服务作为 client 启动时拉取配置。
# 微服务自身的 application.yml
spring:
application:
name: order-service
config:
import: optional:configserver:${JHIPSTER_CONFIG_SERVER:https://registry.mycompany.com/config}
cloud:
config:
fail-fast: true
retry:
initial-interval: 1000
max-attempts: 6
集中配置仓库的目录结构:
config-repo/(独立 git 仓库)
├── application.yml # 所有服务共享
├── application-prod.yml # 所有服务共享的生产覆盖
├── order-service.yml # 订单服务专属
├── order-service-prod.yml
└── inventory-service.yml
这样改一个配置只需要在配置仓库提交,配合 /actuator/refresh 或 Spring Cloud Bus 动态下发,不用重打八个镜像。敏感信息仍不入库:Config Server 后端接 Vault,或在配置仓库里只放占位符、由各环境注入。
各环境配置 checklist
上线前对照检查(26、28 篇还会从性能与发布角度扩展):
| 检查项 | dev | test/staging | prod |
|---|---|---|---|
| profile 显式指定 | 可默认 dev | 必须显式 | 必须显式 + 打包 -Pprod |
| 数据源凭据来自环境变量 | 建议 | 必须 | 必须(Secret/Vault) |
| JWT secret 强度 | 不限 | 必须 86+ 位 | 必须 + 定期轮换预案 |
| ddl-auto | create-drop 可 | validate | validate |
| Liquibase 演练 | 顺手跑 | 全量演练 | 升级前空库演练(第 7 篇) |
| 日志落盘/聚合 | 控制台 | 落盘 | JSON + 聚合(第 20 篇) |
| CORS/管理端口 | 放开 | 收紧 | 白名单 + 独立管理端口 |
| 默认 admin 账号 | 可留 | 改密 | 禁用/改密(第 28 篇) |
小结
- JHipster 的 dev/prod 骨架是起点,团队约定才是配置管理的核心:打包 profile 与启动 profile 双显式。
- 敏感信息三道防线:环境变量占位、local 私有 yml 不入库、生产 Secret/Vault 注入,外加 CI 防泄漏扫描。
- 理解 Spring 配置优先级链,才能在”配置到底从哪来的”问题上少扯皮。
- 微服务下尽早切到注册中心/Spring Cloud Config 的集中配置,服务数量翻倍前就要做。
系列导航
- 上一篇:可观测性:日志、指标与链路
- 下一篇:升级策略——JHipster 版本怎么跟
- 相关阅读:配置项的演进要靠版本升级消化(第 22 篇);生产配置项的完整核对见生产上线 Checklist