环境准备与配置:开发环境搭建、生产环境差异、配置管理、环境变量踩坑

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. 环境变量踩坑——越“简单”越容易出错

环境变量看似比配置文件简单,实则暗坑无数。我见过的问题整理一下:

  1. 大小写敏感:在 Linux 上 DB_HOST 和 db_host 是两个变量。Windows 不区分大小写。所以跨平台时必须统一使用大写加下划线风格。
  2. 覆盖顺序混乱:你写了个 SPRING_PROFILES_ACTIVE=dev,结果系统里还有一个 SPRING_PROFILES_ACTIVE=prod 没清掉。启动时到底用哪个?很多框架会取第一个非空值而非最后一个。建议在容器启动脚本里先 unset 再 export,确保唯一值。
  3. 空格问题:export APP_NAME=my app 这种写法,变量值其实只有 my,因为空格被当成分隔符。正确写法是 export APP_NAME="my app"。
  4. 特殊字符导致解析失败:密码中带 $ 或 ! 时,必须用单引号括起来。我见过一个团队因为密码里有个 $,数据库一直连不上,排查了两天。
  5. 文件编码:.env 文件如果用带 BOM 的 UTF-8 保存,第一行变量名会被 \uFEFF 污染。用 VS Code 记得选“UTF-8 without BOM”。
调试手段:在 Docker 启动命令里加 ENV | sort,把环境变量全部打印出来看。或者用 env 命令配合 diff 对比两个容器的变量差异,能快速定位环境配置不一致的问题。

下面这张图总结了环境配置的完整链路与常见断层点,方便你对照排查:

环境配置体系与常见断层点 开发环境 macOS/Windows + IDE 配置管理 Nacos/Secret/Env 生产环境 Linux + Kubernetes 常见断层①:开发↔配置 JDK版本不一致 / 文件路径硬编码 profile未对齐 / 端口冲突 常见断层②:配置↔生产 环境变量大小写 / 覆盖顺序 敏感信息泄露 / 密码转义 环境配置递进层次 命令行参数 环境变量 配置文件 优先级从高到低(高覆盖低)
又一个真实案例:有一次灰度上线,配置中心里 feature.toggle=on,但生产环境始终没生效。查了一整晚,最后发现是 Kubernetes Pod 的 env 里也定义了一个同名的环境变量 FEATURE_TOGGLE=off,直接覆盖了配置中心的配置。从那以后我们团队规定:环境变量只用来做“唯一标识性”的参数(如集群名、实例ID、配置中心地址),业务开关全走配置中心。

环境配置这件事,说大不大,说小不小。很多线上事故追根溯源,都是“环境不一致”四个字。你如果能把这张图里的断层点全部解决掉,团队至少能减少一半的线上问题。