应用程序通常需要部署到许多不同的环境,包括暂存、测试和生产,而不需要构建特定环境的工件。
像12因素应用程序这样的方法论规定,应用程序的代码和配置是分开的,但在部署期间结合起来以适应特定的环境。
环境变量是定义和消费特定环境配置值的首选方法之一,因为它们受到所有主要操作系统的支持。
它们可以在大多数云提供商的平台即服务(PaaS)产品中定义,并且是配置平台(如Docker)的一种常见方法。
然而,有时在本地配置文件中定义应用程序的设置也很方便。
例如,在本地开发和调试应用程序时,特别是在跨许多功能分支工作时,在文件中定义一组环境变量的能力简化了开发人员的经验。
.env
文件为定义环境变量提供了一个流行的解决方案,特别是在Node.js中。例如,像Heroku这样的平台,使用
.env
文件作为他们推荐的最佳实践的一部分。
在这篇文章中,我们将看看一个通过环境变量配置的简单Node.js应用程序,并探讨如何用覆盖默认值的新环境变量定制一个
.env
文件。
然后我们将演示如何使用多个定制的
.env
文件来快速切换多个特定环境配置。
我们将使用的示例应用程序在GitHub上提供 。要在本地运行该应用程序,请 确保已安装Node.js 。
另外,你也可以
在CodeSandBox中运行该应用程序
。点击链接,在一个新的沙盒中打开GitHub
源代码,以构建、运行并通过一个随机的URL进行展示。
Node.js应用程序的样本
下面的代码显示了一个简单的Node.js网络服务器,配置了两个环境变量:
PORT
,它定义了网络服务器监听的端口,以及
MYNAME
,它定义了HTTP响应中返回的名称。
const http = require('http');
const port = parseInt(process.env.PORT, 10) || 5000;
const name = process.env.MYNAME || "Matthew"
http.createServer((request, response) => {
response.writeHead(200, {
'Content-Type': 'text/plain'
response.write('Hello, ' + name + '!');
response.end();
}).listen(port);
首先导入http
模块。
const http = require('http');
我们可以尝试从PORT
环境变量中解析出一个整数值,如果环境变量没有定义或包含整数以外的内容,则默认为端口5000。
const port = parseInt(process.env.PORT, 10) || 5000;
HTTP响应中返回的名称可以在MYNAME
环境变量中找到,或者默认为字符串Matthew
。
const name = process.env.MYNAME || "Matthew"
在Web服务器启动后,它将监听我们刚才定义的端口。
http.createServer((request, response) => {
// ...
}).listen(port);
HTTP响应是在传递给createServer()
的函数中构建的。在这里,我们返回上面定义的名称。
response.writeHead(200, {
'Content-Type': 'text/plain'
response.write('Hello, ' + name + '!');
response.end();
现在,我们可以用以下方式运行应用程序。
node index.js
打开http://localhost:5000,看到Hello, Matthew!
响应,这意味着我们的Node.js应用程序正在正常运行。
为什么我们需要覆盖环境变量?
为什么首先要费尽心思用环境变量来覆盖数值,比如端口号?
这些值已经可以作为命令行参数传入,或者从JSON或YAML配置文件中加载,比环境变量暴露出简单的名称和值对提供了更多的灵活性。
然而,由于PaaS解决方案经常需要从环境变量中读取数值,PORT
环境变量成为定义应用程序监听的端口的事实标准。
例如,Heroku、AWS、Azure和Google Cloud都要求部署在其服务上的Node.js应用程序监听由PORT
环境变量定义的端口。
在十二因素应用方法论等方法论中,也经常要求使用明确定义的环境变量。
在帖子的介绍中,我们注意到这种方法论需要在代码之外定义应用程序的配置;Twelve-Factor App配置部分有更详细的介绍。
十二因素应用程序将配置存储在环境变量(通常简称为env vars或env)中。环境变量很容易在部署之间改变,而不需要改变任何代码;与配置文件不同,它们很少有机会被意外地检查到代码库中;而且与自定义配置文件或其他配置机制(如Java系统属性)不同,它们是一种语言和操作系统无关的标准。-"十二个因素的应用程序",亚当-威金斯
通过环境变量配置我们的应用程序,我们可以确保我们的代码易于在各种平台上进行部署和定制。
在Node.js中设置环境变量
部署到PaaS解决方案的Node.js应用程序通常必须假定该应用程序正在监听一个随机数字的端口。我们可以通过将PORT
环境变量设置为5000以外的值来在本地演示这一点。
我们还将设置MYNAME
环境变量来演示如何定义应用程序的特定配置。
在Linux和macOS中,我们可以像这样定义这些环境变量。
PORT=5001 MYNAME=Jane node src/index.js
在Windows PowerShell中,我们可以像下面这样定义环境变量。
$env:PORT="5001"
$env:MYNAME="Jane"
node src\index.js
对于我们的两个环境变量来说,这个过程是可控的,但如果我们必须在应用程序每次运行时定义几十个环境变量,那么这个过程很快就会变得很乏味。在.env
文件中定义环境变量提供了一个方便的解决方案。
在Node.js中加载.env
文件
Node.js没有原生加载.env
文件,所以我们必须使用dotenv
包来加载文件,并通过process.env
公开数值。
首先,在package.json
文件中的dependencies
属性下,将dotenv
作为项目的依赖关系。
"name": "node-env-file-demo",
"version": "1.0.0",
"description": "Node.js example loading .env files",
"main": "src/index.js",
"scripts": {
"start": "nodemon src/index.js"
"dependencies": {
"dotenv": "10.0.0"
"devDependencies": {
"nodemon": "1.18.4"
"keywords": []
接下来,用以下方式下载该依赖关系。
npm install
为了加载.env
文件,我们必须加载dotenv
包并在index.js
文件的开头调用configure()
函数。
require('dotenv').config();
const http = require('http');
const port = parseInt(process.env.PORT, 10) || 5000;
const name = process.env.MYNAME || "Matthew"
http.createServer((request, response) => {
response.writeHead(200, {
'Content-Type': 'text/plain'
response.write('Hello, ' + name + '!');
response.end();
}).listen(port);
我们的应用程序现在已经准备好加载.env
文件。
在Node.js中定义自定义环境变量
我们现在可以在项目的根目录下创建.env
文件。
定制一个.env
文件包括在每一行的开头定义我们想要覆盖的环境变量名称,然后是=
和变量的值。在下面的例子中,我们为PORT
和MYNAME
环境变量定义了新值。
PORT=5001
MYNAME=Jane
现在,用下面的命令运行该应用程序。
node src/index.js
接下来,打开http://localhost:5001;注意到端口的变化和返回的信息Hello, Jane!
。
预加载dotenv
在我们的代码中调用require('dotenv').config()
的另一种方法是使用-r
或--require
命令行选项来预加载dotenv
。
node -r dotenv/config src/index.js
这种方法将环境变量注入到Node.js应用程序中,否则不支持.env
文件,而无需编辑原始源代码。
在Node.js中使用多个.env
文件
为了让开发人员在开发过程中快速交换许多自定义的环境文件,我们可以配置dotenv
,通过DOTENV_CONFIG_PATH
环境变量从自定义文件加载环境变量。
为了证明这一点,在项目的根目录下创建一个名为.env.development
的文件,内容如下。
PORT=5002
MYNAME=Jill
然后,将DOTENV_CONFIG_PATH
环境变量设置为.env.development
,并使用我们刚才介绍的预加载方法运行该应用程序。
在Windows PowerShell上,用这些命令运行该应用程序。
$env:DOTENV_CONFIG_PATH=".env.development"
node -r dotenv/config src/index.js
在Linux或macOS上,用这个命令运行该应用程序。
DOTENV_CONFIG_PATH=.env.development node -r dotenv/config src/index.js
我们的应用程序现在可以在http://localhost:5002,并会返回Hello, Jill!
。
将.env
文件排除在 Git 之外
因为.env
文件通常包含敏感信息,比如数据库连接字符串,我们不想把这些值提交到 Git 仓库。如果我们这样做,我们就会与任何可以访问应用程序源代码的人分享我们的密码。
环境变量很好地避免了这个问题,因为就其本质而言,环境变量的值并不在文件中定义。然而,我们必须将.env
文件从 Git 仓库中排除,以防止共享它们。
要做到这一点,我们可以在项目的根目录下创建一个名为.gitignore
的文件,并添加以下一行。
.env*
现在,Git 忽略了.env
文件和其他任何以.env
开头的文件,如.env.development
,这意味着任何敏感信息都不会出现在我们的 Git 仓库里。