译文:Setting Up an NGINX Reverse Proxy with a Node.js Cluster Using Docker (opens new window)
在本文中,我将向大家介绍一个项目,在这个项目中,我设置了一个 Nginx 服务器作为反向代理来处理 Node.js 应用程序集群的请求。该设置使用 Docker 对 Nginx 服务器和 Node.js 应用程序进行容器化,从而实现无缝扩展和管理。在本文结束时,您将明白为什么 Nginx 是现代网络开发的必备工具,以及如何为此类用例进行配置。
# Nginx 是什么
NGINX(发音为「engine-x」)是一款高性能网络服务器和反向代理服务器。它因速度快、可靠性高和能够处理并发连接而被广泛使用。以下是它的一些主要功能:
- 网络服务器: NGINX 可以以极快的速度提供 HTML、CSS 和 JavaScript 等静态文件。Apache 网络服务器也提供此功能,但 Nginx 因其高性能、低资源消耗和处理大量并发连接的能力而备受青睐;
- 反向代理:它可以将传入的客户端请求转发到后端服务器或上游服务器,并将响应返回给客户端。这就提高了可扩展性和安全性。如何实现? 在这种情况下,终端用户不直接向后端服务器发送请求,而是由 Nginx 作为中介处理这项任务;
- 负载均衡:NGINX 可以使用轮循或最少连接(默认为轮循算法)等算法在多个后端(上游)服务器之间分配传入流量;
- Kubernetes 入口控制器:NGINX 经常被用作 Kubernetes 集群的入口控制器。在这个角色中,NGINX 接收来自云负载均衡器的请求,并将它们路由到群集内部的服务。这就确保了集群的安全,只有负载平衡器向公众开放;
# K8S 中的 Nginx
当用作 Kubernetes 入口控制器时,NGINX 在集群中扮演类似的角色。工作原理如下:云负载均衡器将请求转发给 NGINX 入口控制器。 入口控制器将请求路由到相应的 Kubernetes 服务。服务将请求转发给 Pod(应用实例)。
这种设置可确保 Kubernetes 集群的安全,只有云负载均衡器才会暴露于外部流量。
在本项目中,我们将 NGINX 用作一个 Node.js 集群的反向代理和负载均衡器,为网页提供服务。
# 项目概述
下面是该项目的 Github (opens new window) 链接,其中包括源代码、我创建的自定义 nginx 配置以及用于将整个设置容器化的 Docker-Compose 文件。
该项目由以下组件组成:
- NGINX 服务器:通过
8080
端口监听,并将传入的 HTTP 请求转发给 Node.js 集群; - Node.js 集群: 由三个 Docker 容器组成,每个容器都在
3000
端口上运行一个 Node.js 应用程序; - Docker Compose:协调所有容器的部署;
设置工作原理如下:
- 客户端向
8080
端口上的 NGINX 服务器发送 HTTP 请求; - NGINX 作为反向代理,使用轮询负载均衡策略将请求转发给其中一个 Node.js 容器;
- Node.js 容器会处理请求,并通过 NGINX 返回响应;
# 自定义 Nginx 配置
以下是我在本项目中使用的 NGINX 配置文件:
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
# Upstream block to define the Node.js backend servers
upstream nodejs_cluster {
server app1:3000;
server app2:3000;
server app3:3000;
}
server {
listen 8080; # Listen on port 8080 for HTTP
server_name localhost;
# Proxying requests to the Node.js cluster
location / {
proxy_pass http://nodejs_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
说明:
- 配置中,我们有
server
、http
、event
等模块。在这些块中,我们有决定服务器行为的指令; - Nginx 设置了
worker_processes
,负责获取和处理来自浏览器的请求。这些进程在单线程事件循环中处理来自用户的多个并发请求。工作进程会影响 Nginx 处理流量的能力。应根据服务器的硬件和预期流量负载进行调整。在生产层面,建议将工作进程设置为与 CPU 内核数量相当; - 然后,我们在事件块中设置
worker_connections
指令。这将配置每个 Worker 进程应处理的并发连接数; - 服务器的主要逻辑定义在
http
块中。我们还可以配置 nginx 监听使用HTTPS
协议和SSL
加密的请求,但在这个简单的设置中,我没有配置。因此,http 块定义了 nginx 处理用户请求的端口,以及特定域或 IP 地址的转发位置; server
块通过 8080 端口监听,并将请求转发给所定义的上游服务器;upstream
块定义了 Node.js 后端服务器(在我们的例子中是三个容器)。当 Nginx 充当反向代理时,向后端服务器发出的请求来自 Nginx,而不是直接来自客户端。因此,后端服务器会将 Nginx 服务器的 IP 地址视为请求源;- 为什么我提到: 这是因为,docker 的内部 DNS 服务会将 app1、app2 和 app3 解析到我们在 docker compose 文件中创建的 nodejs 容器服务;
- 我们还希望转发来自原始客户端请求的信息。这将为日志记录提供有用信息。
proxy_pass
指令会将请求发送到上游集群,而Host
和X-Real-IP
等标头则会保留客户端信息; - 另一项重要配置必须是在对客户端的响应中
include mime.types
。当 Nginx 从上游服务器返回响应时,它可以包含 Nginx 服务的文件类型,这有助于浏览器处理和呈现文件;
关于 Nginx 的配置就介绍到这里,这将是建立其他项目的基础,因为逻辑基本保持不变。
# Docker Compose 配置
下面是定义整个设置的 docker-compose.yml
文件:
version: '3'
services:
app1:
build: ./app
environment:
- APP_NAME=App1
image: yashpatil16/nginx-app1:latest
ports:
- "3001:3000"
app2:
build: ./app
environment:
- APP_NAME=App2
image: yashpatil16/nginx-app3:latest
ports:
- "3002:3000"
app3:
build: ./app
environment:
- APP_NAME=App3
image: yashpatil16/nginx-app3:latest
ports:
- "3003:3000"
nginx:
build: ./app/nginx
image: yashpatil16/nginx:nodejs-app
ports:
- "8080:8080"
depends_on:
- app1
- app2
- app3
说明:
app1
、app2
和app3
服务分别构建并运行一个 Node.js 应用程序,在内部开放 3000 端口;nginx
服务构建 NGINX 映像,向主机开放 8080 端口;depends_on
指令确保 Node.js 容器先于 NGINX 启动;
我还为 Nginx 服务器容器编写了一个自定义的 DockerFile
,指示它使用我的配置而不是默认配置文件。
# Use the official Nginx image as the base image
FROM nginx:latest
# Remove the default Nginx configuration file
RUN rm /etc/nginx/conf.d/default.conf
# Copy the custom Nginx configuration file
COPY ./nginx.conf /etc/nginx/conf.d/nginx.conf
# 运行项目
- 构建并启动容器:
docker-compose up --build
; - 在浏览器中访问应用程序
http://localhost:8080
;
NGINX 会将请求转发给其中一个 Node.js 容器,并返回响应:
要验证循环负载均衡方法,请检查日志:
正如 Docker Compose 文件中提到的,请求由不同的容器提供服务,在我们的例子中就是 App1、2、3。
# 总结
差不多就是这样。我们了解了 Nginx 是什么,它有哪些功能,以及如何将 Nginx 服务器设置为上游服务器的反向代理。这是一个简单的设置,但对于任何其他项目来说,逻辑都是一样的,只是在这里和那里做了更多的配置。