如何存储Node.js部署设置/configuration文件?
我一直在研究几个Node应用程序,并且一直在寻找一种存储与部署相关的设置的好模式。 在Django的世界(我来自哪里),通常的做法是使用包含标准设置(时区等)的settings.py
文件,然后使用local_settings.py
作为部署特定的设置。 什么数据库交谈,什么memcache套接字,pipe理员的电子邮件地址等等。
我一直在寻找类似的节点模式。 只是一个configuration文件将是很好的,所以它不必与app.js
中的所有其他东西卡在一起,但是我发现重要的是有一种方法来在不在源代码控制中的文件中进行特定于服务器的configuration。 同样的应用程序可以很好地部署在不同的服务器上,具有非常不同的设置,并且必须处理合并冲突以及所有这些都不是我的乐趣。
那么是否有这样的框架/工具,还是每个人都只是自己一起黑客?
我为我的包使用了一个package.json
,而我的configuration使用了一个config.js
,如下所示:
var config = {}; config.twitter = {}; config.redis = {}; config.web = {}; config.default_stuff = ['red','green','blue','apple','yellow','orange','politics']; config.twitter.user_name = process.env.TWITTER_USER || 'username'; config.twitter.password= process.env.TWITTER_PASSWORD || 'password'; config.redis.uri = process.env.DUOSTACK_DB_REDIS; config.redis.host = 'hostname'; config.redis.port = 6379; config.web.port = process.env.WEB_PORT || 9980; module.exports = config;
我从我的项目加载configuration:
var config = require('./config');
然后我可以从config.db_host
, config.db_port
等访问我的东西…这让我可以使用硬编码参数,或参数存储在环境variables,如果我不想在源代码控制存储密码。
我也生成一个package.json
并插入一个依赖项部分:
"dependencies": { "cradle": "0.5.5", "jade": "0.10.4", "redis": "0.5.11", "socket.io": "0.6.16", "twitter-node": "0.0.2", "express": "2.2.0" }
当我将项目克隆到本地机器时,我运行npm install
来安装软件包。 更多信息在这里 。
该项目存储在GitHub中,并为我的生产服务器添加了遥控器。
您可以从Node v0.5.x请求JSON文件( 参考此答案 )
config.json:
{ "username" : "root", "password" : "foot" }
app.js:
var config = require('./config.json'); log_in(config.username, config.password);
很久以后,我发现了一个非常好的pipe理configuration的Node.js模块: nconf 。
一个简单的例子:
var nconf = require('nconf'); // First consider commandline arguments and environment variables, respectively. nconf.argv().env(); // Then load configuration from a designated file. nconf.file({ file: 'config.json' }); // Provide default values for settings not provided above. nconf.defaults({ 'http': { 'port': 1337 } }); // Once this is in place, you can just use nconf.get to get your settings. // So this would configure `myApp` to listen on port 1337 if the port // has not been overridden by any of the three configuration inputs // mentioned above. myApp.listen(nconf.get('http:port'));
它还支持在Redis中存储设置,编写configuration文件,并且具有相当稳定的API,并且作为Flatiron框架计划的一部分,还得到了一个备受推崇的Node.js商店Nodejitsu的支持 ,所以它应该是相当面向未来。
在Github上查看nconf 。
我的解决scheme相当简单:
在./config/index.js中加载环境configuration
var env = process.env.NODE_ENV || 'development' , cfg = require('./config.'+env); module.exports = cfg;
在./config/config.global.js中定义一些默认值
var config = module.exports = {}; config.env = 'development'; config.hostname = 'dev.example.com'; //mongo database config.mongo = {}; config.mongo.uri = process.env.MONGO_URI || 'localhost'; config.mongo.db = 'example_dev';
覆盖./config/config.test.js中的默认值
var config = require('./config.global'); config.env = 'test'; config.hostname = 'test.example'; config.mongo.db = 'example_test'; module.exports = config;
在./models/user.js中使用它:
var mongoose = require('mongoose') , cfg = require('../config') , db = mongoose.createConnection(cfg.mongo.uri, cfg.mongo.db);
在testing环境中运行你的应用:
NODE_ENV=test node ./app.js
这在这里更详细地解释: http : //www.chovy.com/node-js/managing-config-variables-inside-a-node-js-application/
你也可以看看遵循十二因子应用程序原则的dotenv 。
我曾经使用node-config,但由于这个原因创build了dotenv。 它完全受到ruby的dotenv库的启发。
用法很简单:
var dotenv = require('dotenv'); dotenv.load();
然后你只需要创build一个.env文件,并把你的设置放在那里就可以了:
S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE OTHER_SECRET_STUFF=my_cats_middle_name
这是nodejs的dotenv。
你也可以看看node-config ,它根据$ HOST和$ NODE_ENVvariables(有点像RoR)加载configuration文件: documentation 。
这对于不同的部署设置( development
, test
或production
)可能非常有用。
你们用npm来启动脚本(env等)吗?
如果使用.env
文件,可以将它们包含在package.json
并使用npm来源/启动它们。
例:
{ "name": "server", "version": "0.0.1", "private": true, "scripts": { "start": "node test.js", "start-dev": "source dev.env; node test.js", "start-prod": "source prod.env; node test.js" }, "dependencies": { "mysql": "*" } }
然后运行npm脚本:
$ npm start-dev
它在这里描述https://gist.github.com/ericelliott/4152984埃里克·艾略特的所有功劳;
只要做一个简单的settings.js
exports
:
exports.my_password = 'value'
然后,在你的脚本中,做一个require
:
var settings = require('./settings.js');
现在所有的设置将通过settings
variables可用:
settings.my_password // 'value'
定罪是另一个select,添加一个模式进行validation。 像nconf一样,它支持从环境variables,参数,文件和json对象的任意组合中加载设置。
自述文件中的示例:
var convict = require('convict'); var conf = convict({ env: { doc: "The applicaton environment.", format: ["production", "development", "test"], default: "development", env: "NODE_ENV" }, ip: { doc: "The IP address to bind.", format: "ipaddress", default: "127.0.0.1", env: "IP_ADDRESS", }, port: { doc: "The port to bind.", format: "port", default: 0, env: "PORT" } });
入门文章: 使用node-convict驯服configuration
您可以将Konfig用于环境特定的configuration文件。 它自动加载json或yamlconfiguration文件,它具有默认值和dynamicconfigurationfunction。
Konfig回购示例:
File: config/app.json ---------------------------- { "default": { "port": 3000, "cache_assets": true, "secret_key": "7EHDWHD9W9UW9FBFB949394BWYFG8WE78F" }, "development": { "cache_assets": false }, "test": { "port": 3001 }, "staging": { "port": #{process.env.PORT}, "secret_key": "3F8RRJR30UHERGUH8UERHGIUERHG3987GH8" }, "production": { "port": #{process.env.PORT}, "secret_key": "3F8RRJR30UHERGUH8UERHGIUERHG3987GH8" } }
开发中:
> config.app.port 3000
在生产中,假设我们用$ NODE_ENV=production PORT=4567 node app.js
启动应用程序
> config.app.port 4567
更多细节: https : //github.com/vngrs/konfig
我要在这里把我的帽子扔进戒指,因为这些答案都没有解决几乎所有系统需要的关键组件。 注意事项:
- 公共configuration(可以看到前端)vs私人configuration(家伙mograbi得到这一个权利)。 并确保这些保持分开。
- 秘密像钥匙
- 默认值与特定于环境的覆盖
- 前端包
这是我如何做我的configuration:
-
config.default.private.js
– 在版本控制中,这些是只能由后端看到的默认configuration选项。 -
config.default.public.js
– 在版本控制中,这些是可以通过后端和前端看到的默认configuration选项 -
config.dev.private.js
– 如果你需要不同的私人默认设置。 -
config.dev.public.js
– 如果你需要不同的公开默认设置。 -
config.private.js
– 不在版本控制中,这些是环境特定的选项,覆盖config.default.private.js
-
config.public.js
– 不在版本控制中,这些是覆盖config.default.public.js
环境特定选项 -
keys/
– 每个文件存储一个不同的秘密的文件夹。 这也不受版本控制(键不应该在版本控制下)。
我使用普通的javascript文件进行configuration,因此我拥有了javascript语言的全部function(包括注释和能够执行诸如在特定于环境的文件中加载默认configuration文件的function,以便它们可以被覆盖)。 如果你想使用环境variables,你可以在这些configuration文件中加载它们(我build议不要使用env vars,因为我不推荐使用json文件 – 你没有编程语言的能力来构build你的configuration)。
每个密钥都在一个单独的文件中的原因是安装程序使用。 这使您可以有一个安装程序在机器上创build密钥并将其存储在密钥文件夹中。 否则,当您加载无法访问密钥的configuration文件时,安装程序可能会失败。 通过这种方式,您可以遍历目录并加载该文件夹中的任何密钥文件,而不必担心任何给定版本的代码中存在和不存在的内容。
既然你可能已经在你的私人configuration中加载了密钥,你绝对不想在你的前端代码中加载你的私人configuration。 尽pipe将前端代码库与后端完全分开可能更为理想,但很多时候,PITA足以阻止人们进行这种操作,因此私有和公共configuration也是如此。 但是我做了两件事来阻止私人configuration被加载到前端:
- 我有一个unit testing,确保我的前端包不包含我在私人configuration中的密钥之一。
- 我有我的前端代码在不同的文件夹比我的后端代码,我有两个不同的文件名为“config.js” – 每一个结束。 对于后端,config.js加载私有configuration,对于前端,加载公共configuration。 那么你总是需要('configuration'),不要担心它来自哪里。
最后一件事情是:您的configuration应该通过一个完全独立的文件加载到浏览器中,而不是任何其他的前端代码。 如果你捆绑你的前端代码,公共configuration应该被构build为一个完全独立的包。 否则,你的configuration不再是真正的configuration – 它只是你的代码的一部分。 configuration需要能够在不同的机器上有所不同。
我刚刚使用的一个alt示例,因为我想要比典型的.json文件更灵活,但不希望将它抽象到需要依赖项的库中,就像这样。 基本上,导出一个函数立即调用,返回一个对象与我想要的值设置。 给了很大的灵活性。
module.exports = function(){ switch(node_env){ case 'dev': return { var1 = 'development'}; } }();
这里有一个很好的例子。 在Node.js中使用configuration文件
我在比赛中有点迟,但是我在这里或者其他地方找不到我需要的东西,所以我自己写了一些东西。
我对configuration机制的要求如下:
- 支持前端。 如果前端不能使用该configuration,那有什么意义?
- 支持
settings-overrides.js
– 这看起来是一样的,但是允许在settings.js
重写configuration。 这里的想法是在不改变代码的情况下轻松修改configuration。 我觉得它对saas很有用。
尽pipe我不太关心支持环境,但是会解释如何将它轻松添加到我的解决scheme中
var publicConfiguration = { "title" : "Hello World" "demoAuthToken" : undefined, "demoUserId" : undefined, "errorEmail" : null // if null we will not send emails on errors. }; var privateConfiguration = { "port":9040, "adminAuthToken":undefined, "adminUserId":undefined } var meConf = null; try{ meConf = require("../conf/dev/meConf"); }catch( e ) { console.log("meConf does not exist. ignoring.. ")} var publicConfigurationInitialized = false; var privateConfigurationInitialized = false; function getPublicConfiguration(){ if (!publicConfigurationInitialized) { publicConfigurationInitialized = true; if (meConf != null) { for (var i in publicConfiguration) { if (meConf.hasOwnProperty(i)) { publicConfiguration[i] = meConf[i]; } } } } return publicConfiguration; } function getPrivateConfiguration(){ if ( !privateConfigurationInitialized ) { privateConfigurationInitialized = true; var pubConf = getPublicConfiguration(); if ( pubConf != null ){ for ( var j in pubConf ){ privateConfiguration[j] = pubConf[j]; } } if ( meConf != null ){ for ( var i in meConf ){ privateConfiguration[i] = meConf[i]; } } } return privateConfiguration; } exports.sendPublicConfiguration = function( req, res ){ var name = req.param("name") || "conf"; res.send( "window." + name + " = " + JSON.stringify(getPublicConfiguration()) + ";"); }; var prConf = getPrivateConfiguration(); if ( prConf != null ){ for ( var i in prConf ){ if ( prConf[i] === undefined ){ throw new Error("undefined configuration [" + i + "]"); } exports[i] = prConf[i]; } } return exports;
说明
-
undefined
意味着这个属性是必需的 -
null
表示它是可选的 -
meConf
– 目前代码是针对app
下的文件。meConf
是覆盖面向conf/dev
– 这被我的vcs忽略。 -
publicConfiguration
– 将从前端和后端可见。 -
privateConfiguration
– 只能从后端看到。 -
sendPublicConfiguration
– 将公开configuration并将其分配给全局variables的路由。 例如下面的代码将公开configuration作为全局variablesmyConf公开在前端。 默认情况下,它将使用全局variables名称conf
。app.get(“/ backend / conf”,require(“conf”)。sendPublicConfiguration);
覆盖的逻辑
- privateConfiguration与publicConfiguration合并,然后meConf。
- publicConfiguration检查每个键是否有覆盖,并使用该覆盖。 这样我们就不会暴露任何私人的东西。
添加环境支持
即使我没有find有用的“环境支持”,也许有人会。
要添加环境支持,您需要将meConf require语句更改为像这样(伪代码)
if(environment ==“production”){meConf = require(“../ conf / dev / meConf”)。 }
if(environment ==“development”){meConf = require(“../ conf / dev / meConf”)。 }
同样,你可以在每个环境中有一个文件
meConf.development.js meConf.production.js
并导入正确的一个。 其余的逻辑保持不变。
我知道这是一个非常古老的职位。 但我想分享我的模块来configuration环境variables,我认为这是非常灵活的解决scheme。 这是模块json-configurator
var configJson = { 'baseUrl': 'http://test.com', '$prod_baseUrl': 'https://prod.com', 'endpoints': { 'users': '<%= baseUrl %>/users', 'accounts': '<%= baseUrl %>/accounts' }, foo: 'bar', foobar: 'foobar', $prod_foo: 'foo in prod', $test_foo: 'foo in test', deep:{ veryDeep: { publicKey: 'abc', secret: 'secret', $prod_secret: 'super secret' } } }; var config = require('json-configurator')(configJson, 'prod'); console.log(config.deep.veryDeep.secret) // super secret console.log(config.endpoints.users) // https://prod.com/users
然后你可以使用process.env.NODE_ENV
来获取你的环境的所有variables。
除了本答案中提到的nconf模块以及本答案中提到的node-config之外 ,还有node-iniparser和IniReader ,它们似乎更简单.iniconfiguration文件分析器。
我刚刚发布了一个小模块来加载任何types的configuration文件。 这很简单,你可以在https://github.com/flesler/config-node查看;
你可以使用pconf: https ://www.npmjs.com/package/pconf
例:
var Config = require("pconf"); var testConfig = new Config("testConfig"); testConfig.onload = function(){ testConfig.setValue("test", 1); testConfig.getValue("test"); //testConfig.saveConfig(); Not needed }
对于那些正在访问这个老线索的人来说,我觉得这是一个很好的包裹。
我在这里尝试了一些build议的解决scheme,但并不满足于此,所以我创build了自己的模块。 它被称为mikro-config
,主要区别在于它遵守约定而不是configuration,所以你可以只需要模块并开始使用它。
您可以将configuration存储在普通的js或来自/config
文件夹的json文件中。 首先加载default.js
文件,然后加载/config
目录中的所有其他文件,然后加载基于$NODE_ENV
variables的环境特定configuration。
它也允许用local.js
或特定local.js
环境的/config/env/$NODE_ENV.local.js
覆盖本地开发的configuration。
你可以看看这里:
长期以来,我习惯使用解决scheme中提到的方法。 然而,有一个关于明文安全的秘密。 您可以在config
顶部使用另一个软件包,以便保护安全位。
看看这个: https : //www.attosol.com/secure-application-secrets-using-masterkey-in-azure-key-vault/