Spring Boot 应用配置参数化实践:通过 Docker Run 参数传递配置
概述
在现代微服务架构中,应用的配置管理是一个关键问题。硬编码配置会导致应用在不同环境(开发、测试、生产)中部署时需要重新构建镜像,这违背了"一次构建,到处运行"的原则。本文将介绍如何通过 Spring Boot 的配置参数化机制,结合 Docker 环境变量传递,实现应用的灵活配置管理。
为什么需要配置参数化?
传统方式的痛点
- 环境差异:不同环境(开发、测试、生产)的数据库、Redis、ZooKeeper 等中间件地址不同
- 安全性:敏感信息(如密码)不应该硬编码在配置文件中
- 灵活性:每次环境变更都需要重新构建镜像,效率低下
- 可维护性:多个配置文件难以统一管理
参数化的优势
- ✅ 一次构建,多处部署:同一个镜像可以在不同环境使用
- ✅ 安全性提升:敏感信息通过环境变量传递,不进入镜像
- ✅ 运维友好:无需重新构建镜像即可调整配置
- ✅ 符合 12-Factor App 原则:配置与代码分离
Spring Boot 配置参数化实现
1. 配置文件中的参数化语法
Spring Boot 支持使用 ${变量名:默认值} 的语法来引用环境变量,如果环境变量不存在则使用默认值。
以下是一个实际的 application-prod.properties 配置示例:
properties
# 应用端口配置
server.port=8097
# Dubbo 注册中心配置
dubbo.registry.protocol=zookeeper
dubbo.registry.address=zookeeper://${ZOOKEEPER_ADDRESSES:dubbo1.mysteel.local:2181?backup=dubbo2.mysteel.local:2182,dubbo3.mysteel.local:2183,dubbo4.mysteel.local:2184,dubbo5.mysteel.local:2185}
dubbo.consumer.group=standard-ebc
dubbo.provider.group=standard-ebc
dubbo.provider.version=1.0.0
dubbo.consumer.timeout=300000
dubbo.consumer.version=1.0.0
dubbo.consumer.check=false
dubbo.registry.timeout=300000
dubbo.consumer.loadbalance=roundrobin
# 数据库配置(参数化)
spring.datasource.multi.type=DM
spring.datasource.multi.url=jdbc:dm://${DB_HOST:192.168.173.8}:${DB_PORT:5236}/${DB_NAME:YANGCAI_INIT}?schema=${DB_SCHEMA:YANGCAI_INIT}
spring.datasource.multi.username=${DB_USERNAME:SYSDBA}
spring.datasource.multi.password=${DB_PASSWORD:SYSDBA}
# Redis 配置(参数化)
spring.redis.host=${REDIS_HOST:192.168.0.4}
spring.redis.port=${REDIS_PORT:6379}
spring.redis.password=${REDIS_PASSWORD:nm@eloL!FFmmY5}
# 日志配置
logging.level.root=INFO
# 生产环境设置(关闭接口文档)
knife4j.production=true
# 业务配置
workTypeId=3
callbackUrl=http://172.18.10.13:8097/database/apply/callback
# Elasticsearch 配置(参数化)
spring.elasticsearch.rest.uris=${ES_URIS:172.18.10.30:9200}
spring.elasticsearch.rest.connection-timeout=60s
spring.elasticsearch.rest.username=${ES_USERNAME:elastic}
spring.elasticsearch.rest.password=${ES_PASSWORD:hO8eJ3mS5b}
# 关闭 SSL 证书校验(用于华为云 CSS)
spring.elasticsearch.rest.ssl.verification-mode=none
# 文件路径配置
excelUrl=/fastdfs/group1/M00/B1/83/rBL64GYSky6ATl7hBtCdnfhx_Hc798.exe
index.relation.calculate.path=http://172.18.10.17:8080/calculate_correlation
push.frame.pathUrl=http://ebc.mysteel.local/api/database/pushFrame/getPushFrameFilePathList
autoPull.frame.pathUrl=http://ebc.mysteel.local/api/database/pushFrame/autoPull
# XXL-Job 配置(参数化)
xxl.job.admin.addresses=${XXL_JOB_ADMIN_ADDRESSES:http://172.18.10.13:8080/xxl-job-admin}
# 部署模式
mysteel.global.deployment-mode=2
2. 关键配置项说明
从上述配置可以看出,以下配置项被参数化:
| 配置项 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
| ZooKeeper 地址 | ZOOKEEPER_ADDRESSES |
dubbo1.mysteel.local:2181?... |
Dubbo 注册中心地址 |
| 数据库主机 | DB_HOST |
192.168.173.8 |
数据库服务器地址 |
| 数据库端口 | DB_PORT |
5236 |
数据库端口 |
| 数据库名 | DB_NAME |
YANGCAI_INIT |
数据库名称 |
| 数据库 Schema | DB_SCHEMA |
YANGCAI_INIT |
数据库 Schema |
| 数据库用户名 | DB_USERNAME |
SYSDBA |
数据库用户名 |
| 数据库密码 | DB_PASSWORD |
SYSDBA |
数据库密码 |
| Redis 主机 | REDIS_HOST |
192.168.0.4 |
Redis 服务器地址 |
| Redis 端口 | REDIS_PORT |
6379 |
Redis 端口 |
| Redis 密码 | REDIS_PASSWORD |
nm@eloL!FFmmY5 |
Redis 密码 |
| ES 地址 | ES_URIS |
172.18.10.30:9200 |
Elasticsearch 地址 |
| ES 用户名 | ES_USERNAME |
elastic |
Elasticsearch 用户名 |
| ES 密码 | ES_PASSWORD |
hO8eJ3mS5b |
Elasticsearch 密码 |
| XXL-Job 地址 | XXL_JOB_ADMIN_ADDRESSES |
http://172.18.10.13:8080/xxl-job-admin |
XXL-Job 管理地址 |
Docker 环境变量传递
1. Dockerfile 示例
dockerfile
from cr.kylinos.cn/basekylin/java-openjdk-8-aarch64:1.8.0-v10sp1
LABEL maintainer="your-email@example.com"
LABEL description="SpringBoot App with JDK 8 on CentOS 7"
# 验证 Java 安装
RUN java -version && javac -version
WORKDIR /app
# 应用名称全局变量(方便修改)
ENV APP_NAME=mysteel-standard-database-web.jar
# 复制应用 jar 包
COPY ${APP_NAME} /app/${APP_NAME}
# 设置容器时区
ENV TZ=Asia/Shanghai
ENTRYPOINT java -jar /app/${APP_NAME}
注意 :Dockerfile 中不需要设置应用配置相关的环境变量,这些变量在运行时通过 docker run 命令传递。
2. Docker Run 命令传递环境变量
方式一:直接使用 -e 参数
bash
docker run -d \
--name data-prod \
-p 8097:8097 \
-e DB_HOST=192.168.173.9 \
-e DB_PORT=5236 \
-e DB_NAME=YANGCAI_PROD \
-e DB_SCHEMA=YANGCAI_PROD \
-e DB_USERNAME=prod_user \
-e DB_PASSWORD=prod_password \
-e REDIS_HOST=192.168.0.5 \
-e REDIS_PORT=6379 \
-e REDIS_PASSWORD=redis_prod_password \
-e ES_URIS=172.18.10.31:9200 \
-e ES_USERNAME=elastic \
-e ES_PASSWORD=es_prod_password \
-e ZOOKEEPER_ADDRESSES=zk1.prod.local:2181,zk2.prod.local:2181 \
-e XXL_JOB_ADMIN_ADDRESSES=http://172.18.10.14:8080/xxl-job-admin \
your-image:tag
方式二:使用环境变量文件 --env-file
创建环境变量文件 prod.env:
bash
# prod.env
DB_HOST=192.168.173.9
DB_PORT=5236
DB_NAME=YANGCAI_PROD
DB_SCHEMA=YANGCAI_PROD
DB_USERNAME=prod_user
DB_PASSWORD=prod_password
REDIS_HOST=192.168.0.5
REDIS_PORT=6379
REDIS_PASSWORD=redis_prod_password
ES_URIS=172.18.10.31:9200
ES_USERNAME=elastic
ES_PASSWORD=es_prod_password
ZOOKEEPER_ADDRESSES=zk1.prod.local:2181,zk2.prod.local:2181
XXL_JOB_ADMIN_ADDRESSES=http://172.18.10.14:8080/xxl-job-admin
使用环境变量文件启动:
bash
docker run -d \
--name data-prod \
-p 8097:8097 \
--env-file prod.env \
your-image:tag
方式三:使用启动脚本
创建一个启动脚本 start-docker.sh:
bash
#!/bin/bash
# ============================================
# Spring Boot Docker 容器启动脚本
# ============================================
echo "======================================"
echo " Docker 容器启动脚本"
echo "======================================"
echo ""
# 提示输入镜像名称
read -p "请输入镜像名称或ID: " IMAGE_NAME
# 设置默认值
if [ -z "$IMAGE_NAME" ]; then
IMAGE_NAME="springboot-app:prod"
echo "使用默认镜像: $IMAGE_NAME"
fi
# 固定容器名称
CONTAINER_NAME="data-prod"
# 数据库配置(可修改默认值)
DB_HOST=${DB_HOST:-"192.168.173.8"}
DB_PORT=${DB_PORT:-"5236"}
DB_NAME=${DB_NAME:-"YANGCAI_INIT"}
DB_SCHEMA=${DB_SCHEMA:-"YANGCAI_INIT"}
DB_USERNAME=${DB_USERNAME:-"SYSDBA"}
DB_PASSWORD=${DB_PASSWORD:-"SYSDBA"}
# Redis 配置
REDIS_HOST=${REDIS_HOST:-"192.168.0.4"}
REDIS_PORT=${REDIS_PORT:-"6379"}
REDIS_PASSWORD=${REDIS_PASSWORD:-"nm@eloL!FFmmY5"}
# Elasticsearch 配置
ES_URIS=${ES_URIS:-"172.18.10.30:9200"}
ES_USERNAME=${ES_USERNAME:-"elastic"}
ES_PASSWORD=${ES_PASSWORD:-"hO8eJ3mS5b"}
# ZooKeeper 配置
ZOOKEEPER_ADDRESSES=${ZOOKEEPER_ADDRESSES:-"dubbo1.mysteel.local:2181?backup=dubbo2.mysteel.local:2182,dubbo3.mysteel.local:2183,dubbo4.mysteel.local:2184,dubbo5.mysteel.local:2185"}
# XXL-Job 配置
XXL_JOB_ADMIN_ADDRESSES=${XXL_JOB_ADMIN_ADDRESSES:-"http://172.18.10.13:8080/xxl-job-admin"}
# 应用端口
APP_PORT=${APP_PORT:-"8097"}
echo ""
echo "======================================"
echo " 配置信息"
echo "======================================"
echo " 镜像: $IMAGE_NAME"
echo " 容器名: $CONTAINER_NAME"
echo " 应用端口: $APP_PORT"
echo " 数据库: $DB_HOST:$DB_PORT/$DB_NAME"
echo " 用户名: $DB_USERNAME"
echo " Redis: $REDIS_HOST:$REDIS_PORT"
echo " Elasticsearch: $ES_URIS"
echo ""
# 确认是否继续
read -p "是否启动容器? (y/n): " CONFIRM
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
echo "已取消操作"
exit 0
fi
echo ""
echo "清理旧容器..."
docker stop $CONTAINER_NAME 2>/dev/null
docker rm $CONTAINER_NAME 2>/dev/null
echo "启动容器中..."
docker run -d \
--name $CONTAINER_NAME \
-p ${APP_PORT}:8097 \
--add-host=localhost:host-gateway \
-e DB_HOST=$DB_HOST \
-e DB_PORT=$DB_PORT \
-e DB_NAME=$DB_NAME \
-e DB_SCHEMA=$DB_SCHEMA \
-e DB_USERNAME=$DB_USERNAME \
-e DB_PASSWORD=$DB_PASSWORD \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PORT=$REDIS_PORT \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e ES_URIS=$ES_URIS \
-e ES_USERNAME=$ES_USERNAME \
-e ES_PASSWORD=$ES_PASSWORD \
-e ZOOKEEPER_ADDRESSES=$ZOOKEEPER_ADDRESSES \
-e XXL_JOB_ADMIN_ADDRESSES=$XXL_JOB_ADMIN_ADDRESSES \
$IMAGE_NAME
if [ $? -eq 0 ]; then
echo ""
echo "======================================"
echo " 容器启动成功!"
echo "======================================"
echo ""
echo "访问地址: http://localhost:$APP_PORT"
echo ""
echo "常用命令:"
echo " 查看日志: docker logs -f $CONTAINER_NAME"
echo " 进入容器: docker exec -it $CONTAINER_NAME bash"
echo " 停止容器: docker stop $CONTAINER_NAME"
echo " 删除容器: docker rm $CONTAINER_NAME"
else
echo ""
echo "错误: 容器启动失败!"
echo "请检查镜像名称是否正确"
fi
echo ""
使用脚本启动:
bash
# 方式1:使用默认值
./start-docker.sh
# 方式2:通过环境变量覆盖默认值
DB_HOST=192.168.173.9 DB_PASSWORD=prod_password ./start-docker.sh
最佳实践
1. 配置分类
- 必须参数化:数据库连接、Redis、消息队列、注册中心等中间件配置
- 建议参数化:日志级别、功能开关、超时时间等可调优参数
- 可以硬编码:应用内部业务逻辑配置、固定路径等
2. 安全性建议
- ✅ 敏感信息必须参数化:密码、密钥等不应出现在配置文件中
- ✅ 使用默认值:为环境变量提供合理的默认值,方便本地开发
- ✅ 环境变量文件权限 :
.env文件应设置适当的文件权限(如chmod 600) - ✅ 不要提交敏感信息 :
.env文件应加入.gitignore
3. 命名规范
- 使用大写字母和下划线:
DB_HOST、REDIS_PASSWORD - 使用有意义的名称:避免使用
VAR1、VAR2等无意义名称 - 保持一致性:同一类型的配置使用统一的前缀,如
DB_*、REDIS_*
4. 文档化
为每个环境变量编写文档说明:
properties
# 数据库配置
# DB_HOST: 数据库服务器地址(默认: 192.168.173.8)
# DB_PORT: 数据库端口(默认: 5236)
# DB_NAME: 数据库名称(默认: YANGCAI_INIT)
spring.datasource.multi.url=jdbc:dm://${DB_HOST:192.168.173.8}:${DB_PORT:5236}/${DB_NAME:YANGCAI_INIT}?schema=${DB_SCHEMA:YANGCAI_INIT}
验证配置
1. 查看容器环境变量
bash
# 查看容器的所有环境变量
docker exec data-prod env
# 查看特定环境变量
docker exec data-prod env | grep DB_HOST
2. 查看应用日志
bash
# 实时查看日志
docker logs -f data-prod
# 查看最近的日志
docker logs --tail 100 data-prod
3. 进入容器检查
bash
# 进入容器
docker exec -it data-prod bash
# 查看环境变量
echo $DB_HOST
echo $REDIS_HOST
常见问题
Q1: 环境变量没有生效?
A: 检查以下几点:
- 环境变量名称是否正确(区分大小写)
docker run命令中是否正确使用-e参数- Spring Boot 配置文件中语法是否正确:
${VAR_NAME:default_value} - 应用是否重启以加载新配置
Q2: 如何为不同环境设置不同的配置?
A: 可以创建多个环境变量文件:
dev.env- 开发环境test.env- 测试环境prod.env- 生产环境
启动时指定对应的文件:
bash
docker run -d --env-file prod.env your-image:tag
Q3: 配置项太多,命令太长怎么办?
A : 使用 --env-file 参数,将所有环境变量放在文件中管理。
Q4: 如何在 Docker Compose 中使用?
A : 在 docker-compose.yml 中使用 environment 或 env_file:
yaml
version: '3.8'
services:
app:
image: your-image:tag
ports:
- "8097:8097"
environment:
- DB_HOST=192.168.173.9
- DB_PORT=5236
- DB_NAME=YANGCAI_PROD
# 或者使用 env_file
env_file:
- prod.env
总结
通过 Spring Boot 的配置参数化机制结合 Docker 环境变量传递,我们可以实现:
- 配置与代码分离:配置不进入镜像,提高安全性
- 一次构建,多处部署:同一个镜像可以在不同环境使用
- 运维友好:无需重新构建即可调整配置
- 符合最佳实践:遵循 12-Factor App 原则
这种方式特别适合微服务架构和容器化部署场景,能够显著提高应用的灵活性和可维护性。