环境准备与配置:开发环境搭建、生产环境差异、配置管理、环境变量踩坑
1. 开发环境搭建——别让本机变成“玄学容器”
我自己带过好几个项目,最怕听到的一句话就是:“我本地跑得好好的,怎么到服务器就崩了?”
说白了,开发环境搭建不是装个IDE就完事。你得让它和团队保持一致,和生产环境有明确的“镜像关系”。
- 使用容器化开发环境:我习惯在本地用 Docker Compose 拉起整套依赖(MySQL、Redis、Nacos),代码挂载进容器跑。这样每个人拉下来的环境一模一样,不会有“我这边libc版本高一点”这种问题。
- 语言版本管理:Java 一定要统一 JDK 主版本和 build 号。Python 要用 pyenv + virtualenv 或 poetry。Node 用 nvm。我曾经因为本地装了 JDK 11,CI 用 JDK 8,结果一个 stream 写法在线上直接报错——那种低级错误教训太深了。
- 配置模版化:每个项目根目录放
application-template.yml,里面写上占位符和注释。新人拉下来复制改名,填上本地数据库密码就好。别问我为什么强调“模板”——我见过直接把公司线上数据库密码写在 Git 里的猛人。
技巧:在
.gitignore 里把 *.local.* 或 *-dev.* 都加进去。然后在 CI 里跑测试时,由流水线自动生成正确的配置文件,避免本地配置意外上传。
2. 生产环境差异——你以为的开发“等同”是错觉
你想想看,开发机通常是 macOS 或 Windows,生产环境大多数是 Linux。光一个文件路径分隔符就能让你翻车。
| 维度 | 开发环境 | 生产环境 | 常见坑 |
|---|---|---|---|
| 操作系统 | macOS / Windows | Linux (CentOS / Ubuntu) | 路径大小写、换行符(\n vs \r\n)、信号机制 |
| JDK 版本 | Oracle JDK 17 | OpenJDK 8 | JDK API 差异、TLS 版本、JVM 参数不兼容 |
| 文件编码 | UTF-8 | 可能 LANG=C 导致 ASCII | 中文日志乱码、国际化文件被截断 |
| 文件路径 | /Users/xxx |
/home/app |
硬编码路径、user.dir 不可靠 |
| 端口占用 | 3000 用不了就换 8080 | 防火墙只开放 80/443 | 忘记改端口导致服务无法被 SLB 探测 |
| 权限模型 | root 或无脑 sudo | 普通应用用户 | 日志目录无写入权限、临时文件无法创建 |
我曾经踩过的坑:一个 Spring Boot 项目,在本地跑飞起,上生产后健康检查一直失败。查了半天,发现生产环境的
/tmp 被 systemd 用 PrivateTmp 隔离了,临时文件根本写不进去。从那以后我养成了习惯——任何磁盘操作都要显式指定路径,不能用 java.io.tmpdir 默认。
3. 配置管理——别再让密码躺在代码里了
配置管理分三层:框架配置、业务配置、敏感信息。处理方式完全不同。
- 框架配置(数据库连接、线程池、端口):用
application.yml+ 多 profile。每个环境一个文件,--spring.profiles.active=prod切换。注意 profile 覆盖优先级:低→高依次是 jar 内、config/目录、环境变量、命令行参数。 - 业务配置(开关、限流阈值、灰度比例):使用配置中心(Nacos / Apollo / Consul)。我建议把配置中心地址本身做成环境变量注入,这样同一个镜像就能跑在开发/测试/生产三个集群。
- 敏感信息(密码、密钥、):永远不要写在配置文件里。要么用 Kubernetes Secret 挂载,要么用 AWS Secrets Manager 或 HashiCorp Vault。项目里我曾经看到有人把阿里云 AccessKey 写在
application-prod.yml然后提交到 GitHub——那种事一发生,整个公司都要改所有密钥。
# 不推荐的写法
spring.datasource.password=Abc123456
# 推荐的写法(占位符 + 环境变量)
spring.datasource.password=${DB_PASSWORD}
技巧:在本地开发时,建一个
.env 文件(不在版本控制里),用 docker-compose --env-file .env 注入环境变量。既安全又方便团队复用同一个“样板”变量。
4. 环境变量踩坑——越“简单”越容易出错
环境变量看似比配置文件简单,实则暗坑无数。我见过的问题整理一下:
- 大小写敏感:在 Linux 上
DB_HOST和db_host是两个变量。Windows 不区分大小写。所以跨平台时必须统一使用大写加下划线风格。 - 覆盖顺序混乱:你写了个
SPRING_PROFILES_ACTIVE=dev,结果系统里还有一个SPRING_PROFILES_ACTIVE=prod没清掉。启动时到底用哪个?很多框架会取第一个非空值而非最后一个。建议在容器启动脚本里先unset再export,确保唯一值。 - 空格问题:
export APP_NAME=my app这种写法,变量值其实只有my,因为空格被当成分隔符。正确写法是export APP_NAME="my app"。 - 特殊字符导致解析失败:密码中带
$或!时,必须用单引号括起来。我见过一个团队因为密码里有个$,数据库一直连不上,排查了两天。 - 文件编码:
.env文件如果用带 BOM 的 UTF-8 保存,第一行变量名会被\uFEFF污染。用 VS Code 记得选“UTF-8 without BOM”。
调试手段:在 Docker 启动命令里加
ENV | sort,把环境变量全部打印出来看。或者用 env 命令配合 diff 对比两个容器的变量差异,能快速定位环境配置不一致的问题。
下面这张图总结了环境配置的完整链路与常见断层点,方便你对照排查:
又一个真实案例:有一次灰度上线,配置中心里
feature.toggle=on,但生产环境始终没生效。查了一整晚,最后发现是 Kubernetes Pod 的 env 里也定义了一个同名的环境变量 FEATURE_TOGGLE=off,直接覆盖了配置中心的配置。从那以后我们团队规定:环境变量只用来做“唯一标识性”的参数(如集群名、实例ID、配置中心地址),业务开关全走配置中心。
环境配置这件事,说大不大,说小不小。很多线上事故追根溯源,都是“环境不一致”四个字。你如果能把这张图里的断层点全部解决掉,团队至少能减少一半的线上问题。