Node.js 项目配置管理

Dec. 22, 2016, 10:10 p.m.
标签: nodejs deploy

很多时候一个项目的参数需要根据环境的不同而变化,比如接口地址、CDN地址、版本名称、验证身份的秘钥等,这个时候我们不能频繁地改动代码和发布版本,所以期望通过修改配置的方式来改变环境。

配置文件

最早的时候,我们很直接地使用了配置文件的方案:为每个环境定义一套单独的配置文件,然后根据一个开关(可以是环境变量)来切换。这种方法给我们省了很多事,但是缺陷也很明显:

  1. 如果我们的环境多变,则需要定义很多套配置文件,比如:开发环境、测试环境、冒烟环境、生产环境……毫无疑问,这将是一个很麻烦的过程。

    - config/
       - env_development.js
       - env_production.js
       - env_staging.js
    
  2. 配置文件需要进入代码版本控制才能在部署时进行切换,这就导致我们无法在配置中写入一些私密的东西,比如秘钥。当然,我们也可以不把配置文件加入版本控制,那么结果就是,每一个环境我们都需要重新配置一遍,几乎不能享受到配置文件带来的便利。

  3. 我们还可以写一份默认配置,然后在每个环境创建一个独立的配置文件去覆盖一些跟环境相关的内容。即使这样,我们还是要为每个环境创建一个独立于代码的配置文件用于保存私密的参数。

    - config/
       - env_default.js
       - env_override.js
    

所以,配置文件对于本地开发是很方便的,但是始终绕不开文件与代码仓库的关系,安全起见,总是不能避免将配置文件与代码分离开来,从而增加部署流程的复杂性。

环境变量

相比于配置文件,环境变量直接传入配置的最大优势就是,完全与代码解耦,同一份代码在不同的环境变量下即可达到不同的效果。这对于生产环境的部署是很方便的,只要统一配置好环境变量,就可以统一进行部署,直接使用环境变量获取参数,完全不用考虑不同环境下参数值的差异。

环境变量的使用也有其局限:

  1. 全局共享,要考虑命名问题,可能出现命名冲突。
  2. 使用一些进程管理工具(如PM2、supervisor)启动服务的时候,我们可以从配置传入环境变量,但是这样又回到了配置文件存在的问题。

那么,有没有一种方案可以将配置文件和环境变量融为一体,取长补短呢?直到我发现了强大的nconf。

nconf 管理配置

nconf 是一个分层的配置管理工具,每一层都有自己的存储空间,查找参数时,将一层一层往下找,找到第一个则返回,所以我们可以非常方便地控制参数的覆盖方式。

具体来说,每一层可以是环境变量、程序本身的参数(argv)、文件读入的对象或JavaScript对象作为存储空间。

所以,我们只要把想覆盖的参数写最前面,默认参数写最后面,即可保证读取时得到想要的参数,如:

// config.js
const nconf = require('nconf');

nconf

// 需要强行覆盖的参数,优先级最高
.overrides({
  override_a: 'override_a',
})

// 从yaml文件读取配置,当然也可以用JSON,但是我个人偏好yaml
.file({
  file: 'path/to/file.yml',
  format: require('nconf-yaml'),
})

// 从环境变量读取
.env()

// 从程序本身的参数读取
.argv()

// 默认配置
.defaults({
  default_a: 'default_a',
});

module.exports = nconf;

这样,我们可以有一套默认配置,然后根据环境读取对应的配置文件,再从环境变量读取一些敏感信息,这样就可以方便地应用于生产环境。

本地或测试环境开发时,我们可以将敏感信息写入配置文件,不放入仓库,这样就不用修改全局环境变量。

至此,配置文件与环境变量合为一体了,完美实现项目的配置管理。