使用 Docker、NGINX 和 AWS ELB 构建高可用性 Node.js 应用程序
什么是负载平衡?
NGINX 和 AWS ELB 作为负载均衡器
架构概述
Node.js 应用程序
启动 EC2 服务器
容器化应用程序和 NGINX 配置
使用 AWS ALB 安装系统
结论
资源
什么是负载平衡?
负载均衡是一种将传入的网络流量分配到服务器组上的技术(算法)。它为所有公共用户提供访问服务器托管服务的单一入口点。生产级服务器通常在负载均衡器之后运行,因为它们可以在服务器之间“均衡”传入的负载,从而防止服务器过载。负载均衡器还为其路由流量到的服务器提供辅助功能:充当反向代理。反向代理就像服务器组和用户之间的中间人。所有由反向代理处理的请求都会根据请求条件转发到相应的服务器。然后,服务器返回其数据,这些数据随后通过反向代理转发给用户。通过这种方式,反向代理可以保持服务器身份匿名,同时阻止访问存储配置文件、令牌、机密等敏感数据的主服务器。
NGINX 和 AWS ELB 作为负载均衡器
NGINX 是一款快速免费的开源负载均衡器,也可以充当反向代理。另一方面,ELB 是由 Amazon AWS 提供的负载均衡服务。ELB 又可以分为三种主要类型:(1) ALB、(2) CLB 和 (3) NLB。AWS 提供的云服务系列中还新增了一类称为网关负载均衡器的新型负载均衡器。本教程旨在构建一个级联多负载均衡器架构,以提供在多个端口上运行的高可用性 Node.js 服务器。
用户须知:在 AWS 上为 Node.js 应用程序实现基础架构的方法有很多,包括利用 AWS 自身的 ECS 和 ECR 生态系统来运行 Docker 应用程序。然而,本教程并不关注这些,而是旨在更好地理解 EC2 实例、负载均衡器背后的机制,以及它们如何通过负载均衡和代理端口与 Docker 交互。
架构概述
这就是我们想要的架构。由 AWS 和 NGINX 管理的多级负载均衡器将帮助我们在每个 EC2 实例上为节点应用维护多个端口。这种架构的优点在于,两个实例位于不同的可用区(AZ1 和 AZ2),因此,即使一个可用区出现故障,另一个可用区仍可正常运行。这样我们的应用程序就不会崩溃。
Node.js 应用程序
为了简单起见,我们使用标准的启动 Express 节点应用程序,它如下所示:
这些端口通过PORT环境变量提供给 express 应用。我们将通过 Docker 提供这些端口值。得益于docker-compose,这项任务非常简单。还有一个环境变量SERVER_ID,不过不用担心,它仅用于演示目的。一旦整个设置完成,SERVER_ID就会告诉我们负载均衡器从哪个服务器获取响应。这将帮助我们验证使用多级负载均衡器/r-proxy 的基础设施配置是否正常运行。
这类配置在生产流水线中很常见。通常,在这种情况下,主应用服务器会放置在 VPC 中。关于负载均衡器,需要注意的是,一级负载均衡器通常被归类为面向公众的,因为它们处理实际的客户端请求;而二级负载均衡器则是内部负载均衡器,其工作是在各个源之间路由请求。
启动 EC2 服务器
登录你的 AWS 账户并进入 EC2 仪表盘。我们将按照以下步骤设置新服务器并为 Docker 做好准备:
- 在仪表板上,单击右上角的启动实例。
- 从快速启动部分,选择Amazon Linux 2 AMI。
- 选择实例类型t2.micro(如果您的帐户中仍有此实例类型的免费套餐,则可以使用此实例类型的免费套餐)。您也可以选择任何其他您喜欢的类型。
- 仅出于演示目的,我们将分别启动这两个实例,因为我们希望这两个实例位于不同的可用区。本例中,区域是us-east-1 ,因此我选择了us-east-1a下的默认子网。
-
在“用户数据”部分下,粘贴以下脚本的代码。这将安装运行 node.js docker 应用所需的必备软件。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters#!/bin/bash sudo amazon-linux-extras install -y docker sudo service docker start sudo usermod -a -G docker ec2-user sudo chkconfig docker on sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo yum install -y gcc-c++ make curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash - sudo yum install -y nodejs git sudo reboot This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters#!/bin/bash sudo amazon-linux-extras install -y docker sudo service docker start sudo usermod -a -G docker ec2-user sudo chkconfig docker on sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo yum install -y gcc-c++ make curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash - sudo yum install -y nodejs git sudo reboot This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters#!/bin/bash sudo amazon-linux-extras install -y docker sudo service docker start sudo usermod -a -G docker ec2-user sudo chkconfig docker on sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose sudo yum install -y gcc-c++ make curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash - sudo yum install -y nodejs git sudo reboot -
对于添加存储和添加标签部分,保留默认值并跳过。
-
在“配置安全组”部分,创建一个新的安全组,并开放 22 端口 (SSH) 和 80 端口 (HTTP)。在“IP 范围”部分,您可以选择“任意位置”或“我的 IP*8”。由于这仅用于测试,您可以选择任意一个。我选择了“任意位置”。
-
单击“审核并启动”,然后单击“启动”。
-
创建一个新的密钥对,用于通过 SSH 连接到此实例。稍后我们将需要它来为应用程序和内部负载均衡器构建 Docker 镜像。
两个实例中的第一个现已启动。按照相同的步骤启动下一个实例,只需将子网更改为与上一个实例不同的值,并使用您之前创建的 SSH 密钥对。
容器化应用程序和 NGINX 配置
在本教程中,我们将使用该应用程序的 repo:
Link: https://github.com/sowmenappd/load_balanced_nodejs_app
app文件夹包含 Node 服务器源代码以及 Dockerfile。在nginx文件夹中,有一个配置文件nginx.conf,用于定义上游服务器端口配置:
http{
upstream lb {
server 172.17.0.1:1000 weight=1;
server 172.17.0.1:2000 weight=1;
server 172.17.0.1:3000 weight=1;
}
server {
listen 80;
location / {
proxy_pass http://lb;
}
}
}
配置文件指定主 NGINX 服务器应侦听端口 80,根位置“/”将请求(代理传递)中继到此文件中定义的名为lb的上游。这是一个上游对象,它指定将包含多少个服务器(这些是我们通过 docker-compose 安装的快速服务器,更多内容见后面的部分),以及这些服务器将在内部运行的端口,而反向代理负载平衡来自端口 80 的流量。
在我们的例子中,上游代理将流量定向到端口 1000、2000 和 3000。这些端口号必须与作为环境变量发送到每个快速服务器实例的内部 PORT 值匹配,我们将在 docker-compose YAML 文件中定义该值。
对于每个启动的实例,我们执行以下操作:
- 使用创建的密钥对通过 SSH 进入实例
- 运行以下终端命令从 Dockerfile 构建应用程序映像
git clone https://github.com/sowmenappd/load_balanced_nodejs_app.git
cd load_balanced_nodejs_app/app
docker build -t app .
- 接下来构建 nginx 服务器 docker 镜像
cd ../nginx
docker build -t nginx-s .
- 运行
docker images
后你会看到类似这样的内容:
-
对于第二台服务器,我们需要修改 docker-compose.yml 文件。所有应用(app1、app2、app3)的环境变量SERVER_ID都应该改为 2。这完全不用担心,任何生产服务器都不会出现这种情况。我们这样做只是为了演示。
-
运行最后的命令:
cd ..
docker-compose up -d
您的隔离服务器现在应该在后台运行 Express 应用的三个进程。我们现在需要做的就是使用 AWS 提供的负载均衡器 AWS ALB 来挂载这个系统。
使用 AWS ALB 安装系统
此时,两个实例都已准备好挂载。我们将按照以下步骤在 AWS 中设置应用程序负载均衡器:
- 导航到EC2 仪表板,然后单击“目标组”。单击“创建目标组”。
- 选择目标类型:实例并提供有效的目标组名称,然后单击下一步。
- 选中两个正在运行的实例,点击下方包含为待处理,端口为80。
- 单击创建目标组。
- 接下来,单击侧面菜单(左侧)中的“负载均衡器” 。
- 单击创建负载均衡器,然后单击应用程序负载均衡器卡上的创建。
- 在下一个屏幕中,选择您之前启动实例时选择的可用区域,然后继续。
- 创建一个新的安全组,并向所有 IP 范围开放 80 端口,然后单击下一步。
- 选择现有目标组,并将其指向您创建的目标组。
- 其余设置都没问题,您可以点击创建。
注册实例并运行健康检查后,负载均衡器应在几分钟内启动并运行。现在,我们从“EC2 仪表板”>“负载均衡器”获取负载均衡器的 DNS 名称,并复制该负载均衡器的 DNS 名称属性。
将 DNS 名称粘贴到浏览器中,然后按 Enter。您将看到每次刷新浏览器页面时,响应都会返回不同的 PORT 和 SERVER_ID 值。这验证了系统,因为默认情况下,NGINX 和 AWS 负载均衡器使用 Round Robin 算法进行负载均衡。
结论
由于我们部署了多负载均衡器配置,我们的系统现在可以保证极高的可用性,并且能够在长时间的运行中承受突增的海量流量。作为本教程的后续,我还将发布另一篇文章,展示如何创建和维护一个与源代码管理集成的部署流水线,并在将更改提交到 GitHub 仓库后将其部署到服务器上。
资源
- Dockerfile 参考 | Docker 文档
- Compose 文件 | Docker 文档
- 什么是应用程序负载均衡器? — 弹性负载均衡 (amazon.com)
- 应用程序负载均衡器的目标组 — Elastic Load Balancing (amazon.com)
- NGINX 配置基础知识。本文介绍如何……| Nethmini Romina | FAUN