本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
将 Node.js Express 应用程序部署到 Elastic Beanstalk
本部分演示如何使用 Elastic Beanstalk 命令行界面(EB CLI)向 Elastic Beanstalk 部署示例应用程序,然后更新该应用程序以使用 Express
先决条件
本教程需要以下先决条件:
-
Node.js 运行时
-
默认 Node.js 程序包管理器软件 npm
-
Express 命令行生成器
-
Elastic Beanstalk 命令行界面 (EB CLI)
有关安装列出的前三个组件和设置本地开发环境的详细信息,请参阅 为 Elastic Beanstalk 设置 Node.js 开发环境。在本教程中,您不需要安装 AWS SDK for Node.js,这在引用主题中也有提到。
有关安装和配置 EB CLI 的详细信息,请参阅 安装 Elastic Beanstalk 命令行界面 和 配置 EB CLI。
创建 Elastic Beanstalk 环境
您的应用程序目录
本教程为应用程序源包使用名为 nodejs-example-express-rds
的目录。为本教程创建 nodejs-example-express-rds
目录。
~$ mkdir nodejs-example-express-rds
注意
本章中的每个教程都为应用程序源包使用自己的目录。该目录名称与教程使用的示例应用程序的名称相匹配。
将您当前的工作目录更改为 nodejs-example-express-rds
。
~$ cd nodejs-example-express-rds
现在,让我们设置运行 Node.js 平台和示例应用程序的 Elastic Beanstalk 环境。我们将使用 Elastic Beanstalk 命令行界面(EB CLI)。
要为您的应用程序配置 EB CLI 存储库,并创建运行 Node.js 平台的 Elastic Beanstalk 环境
-
使用 eb init 命令创建存储库。
~/nodejs-example-express-rds$
eb init --platform
node.js
--region<region>
此命令在名为
.elasticbeanstalk
的文件夹中创建配置文件,该配置文件指定用于为您的应用程序创建环境的设置;并创建以当前文件夹命名的 Elastic Beanstalk 应用程序。 -
使用 eb create 命令创建运行示例应用程序的环境。
~/nodejs-example-express-rds$
eb create --sample
nodejs-example-express-rds
此命令使用 Node.js 平台的默认设置以及以下资源来创建负载均衡环境:
-
EC2 实例 – 配置为在您选择的平台上运行 Web 应用程序的 Amazon Elastic Compute Cloud(Amazon EC2)虚拟机。
各平台运行一组特定软件、配置文件和脚本以支持特定的语言版本、框架、Web 容器或其组合。大多数平台使用 Apache 或 NGINX 作为 Web 应用程序前的反向代理,向其转发请求、提供静态资产以及生成访问和错误日志。
-
实例安全组 - 配置为允许端口 80 上的入站流量的 Amazon EC2 安全组。通过此资源,HTTP 流量可以从负载均衡器到达运行您的 Web 应用程序的 EC2 实例。默认情况下,其他端口不允许流量进入。
-
负载均衡器 – 配置为向运行您的应用程序的实例分配请求的 Elastic Load Balancing 负载均衡器。负载均衡器还使您无需将实例直接公开在 Internet 上。
-
负载均衡器安全组 – 配置为允许端口 80 上的入站流量的 Amazon EC2 安全组。利用此资源,HTTP 流量可从 Internet 到达负载均衡器。默认情况下,其他端口不允许流量进入。
-
Auto Scaling 组 – 配置为在实例终止或不可用时替换实例的 Auto Scaling 组。
-
Amazon S3 存储桶 – 使用 Elastic Beanstalk 时创建的源代码、日志和其他构件的存储位置。
-
Amazon CloudWatch 警报 – 用于监控环境中的实例负载的两个 CloudWatch 警报,它们将在负载过高或过低时触发。警报触发后,您的 Auto Scaling 组会扩展或收缩以进行响应。
-
AWS CloudFormation 堆栈 – Elastic Beanstalk 使用 AWS CloudFormation 启动您环境中的资源并传播配置更改。这些资源在您可通过 AWS CloudFormation 控制台
查看的模板中定义。 -
域名 – 一个域名,它以下面的形式路由到您的 Web 应用程序:
subdomain
.region
.elasticbeanstalk.com。域安全
为增强 Elastic Beanstalk 应用程序的安全性,已将 elasticbeanstalk.com 域注册到 公共后缀列表(PSL)
。 如果您需要在 Elastic Beanstalk 应用程序的默认域名中设置敏感 Cookie,我们建议您使用带有前缀
__Host-
的 Cookie 来提高安全性。这种做法可以保护您的域免遭跨站点请求伪造(CSRF)攻击。要了解更多信息,请参阅 Mozilla 开发者网络中的 Set-Cookie页面。
-
-
当环境创建完成后,使用 eb open 命令在默认浏览器中打开环境 URL。
~/nodejs-example-express-rds$
eb open
您现在已经使用示例应用程序创建了 Node.js Elastic Beanstalk 环境。您可以使用自己的应用程序对其进行更新。接下来,我们会更新示例应用程序,以使用 Express 框架。
更新应用程序以使用 Express
在创建具有示例应用程序的环境后,可将其更新为自己的应用程序。在此过程中,首先运行 express 和 npm install 命令,以在您的应用程序目录中设置 Express 框架。然后,使用 EB CLI 通过更新后的应用程序更新您的 Elastic Beanstalk 环境。
更新您的应用程序以使用 Express
-
运行
express
命令。这将生成package.json
、app.js
,以及几个目录。~/nodejs-example-express-rds$
express
在系统提示您是否要继续时,键入
y
。注意
如果 express 命令不起作用,则您可能没有按照前面的先决条件部分所述安装 Express 命令行生成器。或者,可能需要设置本地计算机的目录路径设置才能运行 express 命令。有关设置开发环境的详细步骤,请参阅先决条件部分,以便您可以继续学习本教程。
-
设置本地依赖项。
~/nodejs-example-express-rds$
npm install
-
(可选)验证 Web 应用程序服务器已启动。
~/nodejs-example-express-rds$
npm start
您应该可以看到类似于如下所示的输出内容:
> nodejs@0.0.0 start /home/local/user/node-express > node ./bin/www
默认情况下,服务器在端口 3000 上运行。要测试,请在另一个终端中运行
curl http://localhost:3000
,或在本地计算机上打开浏览器并输入 URL 地址http://localhost:3000
。按 Ctrl+C 以停止该服务器。
-
使用 eb deploy 命令将更改部署到您的 Elastic Beanstalk 环境。
~/nodejs-example-express-rds$
eb deploy
-
在环境变为绿色并准备就绪后,刷新 URL 以验证环境是否工作。您应看到一个显示 Welcome to Express(欢迎使用 Express)的网页。
接下来,让我们更新 Express 应用程序以使用静态文件并添加新页面。
配置静态文件并向 Express 应用程序添加新页面
-
添加包含以下内容的 .ebextensions 文件夹中的第二个配置文件:
nodejs-example-express-rds/.ebextensions/staticfiles.config
option_settings: aws:elasticbeanstalk:environment:proxy:staticfiles: /stylesheets: public/stylesheets
此设置将代理服务器配置为从应用程序的
public
路径上的/public
文件夹中提供文件。从代理服务器静态提供文件可减少应用程序的负载。有关更多信息,请参阅本章前面的静态文件。 -
(可选)要确认静态映射配置正确,请注释掉
nodejs-example-express-rds/app.js
中的静态映射配置。这将从节点应用程序中删除映射。//
app.use(express.static(path.join(__dirname, 'public')));即使在将此行注释掉之后,上一步的
staticfiles.config
文件中的静态文件映射仍应成功加载样式表。要验证静态文件映射是通过代理静态文件配置而不是快速应用程序加载的,请删除option_settings:
后的值。将其从静态文件配置和节点应用程序中删除后,样式表将无法加载。测试完成后,记得重置
nodejs-example-express-rds/app.js
和staticfiles.config
的内容。 -
添加
nodejs-example-express-rds/routes/hike.js
。键入以下内容:exports.index = function(req, res) { res.render('hike', {title: 'My Hiking Log'}); }; exports.add_hike = function(req, res) { };
-
更新
nodejs-example-express-rds/app.js
以包含三个新行。首先,添加以下行来为此路由添加
require
:var hike = require('./routes/hike');
您的文件应类似于以下代码段:
var express = require('express'); var path = require('path');
var hike = require('./routes/hike');
然后,将以下两行添加到
nodejs-example-express-rds/app.js
中的var app = express();
后面app.get('/hikes', hike.index); app.post('/add_hike', hike.add_hike);
您的文件应类似于以下代码段:
var app = express();
app.get('/hikes', hike.index); app.post('/add_hike', hike.add_hike);
-
将
nodejs-example-express-rds/views/index.jade
复制到nodejs-example-express-rds/views/hike.jade
。~/nodejs-example-express-rds$
cp views/
index.jade views/hike.jade
-
使用 eb deploy 命令部署更改。
~/nodejs-example-express-rds$
eb deploy
-
您的环境将在几分钟后进行更新。在环境变为绿色并准备就绪后,通过刷新浏览器并将
hikes
追加到 URL 末尾(例如http://node-express-env-syypntcz2q.elasticbeanstalk.com/hikes
),验证环境是否正常工作。您应看到标题为 My Hiking Log 的网页。
现在,您已经创建使用 Express 框架的 Web 应用程序。在下一节中,我们将修改应用程序以使用 Amazon Relational Database Service(RDS)来存储 Hiking 日志。
更新应用程序以使用 Amazon RDS
在下一步中,我们将应用程序更新为使用 Amazon RDS for MySQL。
要更新您的应用程序以使用 RDS for MySQL
-
要创建耦合到您的 Elastic Beanstalk 环境的 RDS for MySQL 数据库,请按照本章后面所包含的添加数据库主题中的说明进行操作。添加一个数据库实例大约需要 10 分钟。
-
使用以下内容更新
package.json
中的依赖项部分:"dependencies": { "async": "^3.2.4", "express": "4.18.2", "jade": "1.11.0", "mysql": "2.18.1", "node-uuid": "^1.4.8", "body-parser": "^1.20.1", "method-override": "^3.0.0", "morgan": "^1.10.0", "errorhandler": "^1.5.1" }
-
运行 npm install。
~/nodejs-example-express-rds$
npm install
-
更新
app.js
以连接到数据库、创建表并插入单个默认 Hiking 日志。每次部署此应用程序时,它都会删除之前的 hikes 表并重新创建它。/** * Module dependencies. */ const express = require('express') , routes = require('./routes') , hike = require('./routes/hike') , http = require('http') , path = require('path') , mysql = require('mysql') , async = require('async') , bodyParser = require('body-parser') , methodOverride = require('method-override') , morgan = require('morgan') , errorhandler = require('errorhandler'); const { connect } = require('http2'); const app = express() app.set('views', __dirname + '/views') app.set('view engine', 'jade') app.use(methodOverride()) app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true })) app.use(express.static(path.join(__dirname, 'public'))) app.set('connection', mysql.createConnection({ host: process.env.RDS_HOSTNAME, user: process.env.RDS_USERNAME, password: process.env.RDS_PASSWORD, port: process.env.RDS_PORT})); function init() { app.get('/', routes.index); app.get('/hikes', hike.index); app.post('/add_hike', hike.add_hike); } const client = app.get('connection'); async.series([ function connect(callback) { client.connect(callback); console.log('Connected!'); }, function clear(callback) { client.query('DROP DATABASE IF EXISTS mynode_db', callback); }, function create_db(callback) { client.query('CREATE DATABASE mynode_db', callback); }, function use_db(callback) { client.query('USE mynode_db', callback); }, function create_table(callback) { client.query('CREATE TABLE HIKES (' + 'ID VARCHAR(40), ' + 'HIKE_DATE DATE, ' + 'NAME VARCHAR(40), ' + 'DISTANCE VARCHAR(40), ' + 'LOCATION VARCHAR(40), ' + 'WEATHER VARCHAR(40), ' + 'PRIMARY KEY(ID))', callback); }, function insert_default(callback) { const hike = {HIKE_DATE: new Date(), NAME: 'Hazard Stevens', LOCATION: 'Mt Rainier', DISTANCE: '4,027m vertical', WEATHER:'Bad', ID: '12345'}; client.query('INSERT INTO HIKES set ?', hike, callback); } ], function (err, results) { if (err) { console.log('Exception initializing database.'); throw err; } else { console.log('Database initialization complete.'); init(); } }); module.exports = app
-
将以下内容添加到
routes/hike.js
。这将使路线能够将新的 Hiking 日志插入 HIKES 数据库。const uuid = require('node-uuid'); exports.index = function(req, res) { res.app.get('connection').query( 'SELECT * FROM HIKES', function(err, rows) { if (err) { res.send(err); } else { console.log(JSON.stringify(rows)); res.render('hike', {title: 'My Hiking Log', hikes: rows}); }}); }; exports.add_hike = function(req, res){ const input = req.body.hike; const hike = { HIKE_DATE: new Date(), ID: uuid.v4(), NAME: input.NAME, LOCATION: input.LOCATION, DISTANCE: input.DISTANCE, WEATHER: input.WEATHER}; console.log('Request to log hike:' + JSON.stringify(hike)); req.app.get('connection').query('INSERT INTO HIKES set ?', hike, function(err) { if (err) { res.send(err); } else { res.redirect('/hikes'); } }); };
-
将
routes/index.js
的内容替换为以下内容:/* * GET home page. */ exports.index = function(req, res){ res.render('index', { title: 'Express' }); };
-
添加以下 jade 模板到
views/hike.jade
中,以提供添加 Hiking 日志的用户界面。extends layout block content h1= title p Welcome to #{title} form(action="/add_hike", method="post") table(border="1") tr td Your Name td input(name="hike[NAME]", type="textbox") tr td Location td input(name="hike[LOCATION]", type="textbox") tr td Distance td input(name="hike[DISTANCE]", type="textbox") tr td Weather td input(name="hike[WEATHER]", type="radio", value="Good") | Good input(name="hike[WEATHER]", type="radio", value="Bad") | Bad input(name="hike[WEATHER]", type="radio", value="Seattle", checked) | Seattle tr td(colspan="2") input(type="submit", value="Record Hike") div h3 Hikes table(border="1") tr td Date td Name td Location td Distance td Weather each hike in hikes tr td #{hike.HIKE_DATE.toDateString()} td #{hike.NAME} td #{hike.LOCATION} td #{hike.DISTANCE} td #{hike.WEATHER}
-
使用 eb deploy 命令部署更改。
~/nodejs-example-express-rds$
eb deploy
清理
如果使用完 Elastic Beanstalk,则可终止您的环境。
使用 eb terminate 命令终止您的环境以及其中包含的所有资源。
~/nodejs-example-express-rds$ eb terminate
The environment "nodejs-example-express-rds-env" and all associated instances will be terminated.
To confirm, type the environment name: nodejs-example-express-rds-env
INFO: terminateEnvironment is starting.
...