使用Docker和Node.js的NGINX - 初学者指南

100人浏览   2024-08-12 10:17:14

这篇文章是我在学习Nginx和Docker时写的,它解释了如何用Nginx反向代理建立一个Docker化的Node.js服务器。作为一个初学者,我认为这篇文章简化了很多概念,而其他教程并没有真正做到这一点,所以我希望人们觉得它很有用。

什么是NGINX?

很多网站和教程对NGINX的定义如下。

NGINX是一个开源的网络服务器,也是一个反向代理和HTTP负载平衡器。

网络服务器?反向代理?负载平衡器?这些术语对于经验丰富的开发者和工程师来说可能很熟悉,但对于一个新手来说,它们更令人困惑而不是帮助。因此,让我们把定义分解成几个部分。

网络服务器是一个对客户(通常是网络浏览器)提出的HTTP请求做出响应的软件。浏览器提出请求,而网络服务器则以与该请求相对应的静态内容(通常是HTML)作出响应。

反向代理是一个位于一组网络服务器前面的服务器。当浏览器发出HTTP请求时,该请求首先进入反向代理,然后将该请求发送到适当的网络服务器。

反向代理的一些好处。

安全性:使用反向代理,网络服务器从不向客户透露其IP地址,这使服务器更加安全。

SSL加密(更安全):加密和解密SSL通信是很昂贵的,而且会使网络服务器变慢。反向代理可以被配置为解密来自客户端的传入请求并加密来自服务器的传出响应。

负载平衡:如果一个网站非常受欢迎,那么所有的流量都由一个网络服务器处理是不太可能的。通常情况下,网站将分布在许多网络服务器上,在它们前面有一个反向代理。(关于负载平衡的更多信息,见下文)

负载平衡器负责以有效的方式将客户的HTTP请求路由到网络服务器。这可以防止任何单独的网络服务器过度工作。

负载平衡器也可以被配置为,如果一个网络服务器发生故障,反向代理将不再转发请求到该网络服务器。

如何使用NGINX作为本地Node.js服务器的反向代理(无需Docker)。

现在我们对Nginx的用途有了一个基本的概念,我们可以继续看看它如何作为一个简单的Node.js服务器的反向代理在本地运行。如果你想知道它如何与Docker一起工作,请跳到下一节。

首先,确保你在本地机器上安装了Nginx和Node.js(我不会在这里详述安装说明,但网上有一些资源可以介绍两者的安装过程)。

1. 创建一个简单的Node.js服务器

为你的应用程序创建一个目录。在你的终端上导航到该目录,并输入以下命令来创建package.json。

npm init

按照终端上的步骤操作(在所有选项上点击是即可)。然后在同一目录下执行以下命令。第一个命令是创建index.js文件,这是我们应用程序的入口,第二个命令是安装Express Web框架。

touch index.js 
npm install express

接下来,打开你的index.js文件,并粘贴以下代码。这段代码所做的就是初始化一个监听在http://localhost:5000 的服务器。

// index.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
    res.send('Hello World!')
  })
app.listen(5000, () => console.log('Server is up and running'));

用以下命令运行服务器。

node index.js

在你的网络浏览器上,你应该看到一个空白的白色页面,上面写着Hello, World!。

2. 设置Nginx

现在,我们有一个运行在5000端口的服务器,我们可以配置Nginx来代理请求。

首先要看Nginx运行在哪个端口,你需要找到默认的nginx.conf文件。根据不同的发行版本,该文件可能位于以下任何一个路径中。

  1. /etc/nginx/nginx.conf
  2. /usr/local/nginx/conf/nginx.conf
  3. /usr/local/etc/nginx/nginx.conf

当你找到这个文件时,它看起来会像这样(我省略了很多行,用...的代替)。

要看的重要一行是默认Nginx服务器正在监听的端口。我们看到,它正在监听localhost的80端口。为了验证Nginx是否工作,我们可以导航到http://localhost:80。应该出现以下屏幕。

如果页面没有显示出来,可能意味着Nginx没有被启动。在StackOverflow上快速搜索一下,应该能找到答案。

3. 配置Nginx作为Node.js应用程序的反向代理

现在我们知道Nginx在我们的机器上工作,现在是时候使用它为我们的Node.js应用程序创建一个反向代理。

在不做任何修改的情况下,nginx.conf默认有一个服务器监听80端口(或8080,取决于你下载的版本)。但是,我们如何代理这台服务器,使对localhost:80的请求转到我们想要的Node.js应用程序上?

要做到这一点,我们需要添加服务器块,专门配置为代理流量到我们的Node.js服务器。注意nginx.conf文件的最后一行。

include servers/*;

这一行出现在http{}块中。这句话的作用是告诉Nginx在services文件夹(在nginx目录下)中寻找其他的服务器块。这就是我们要添加自定义配置的地方。

在服务器目录下创建一个新文件(我称之为sample_node_app.conf,但你可以随心所欲地称呼它)。

cd servers
touch sample_node_app.conf

并将以下配置粘贴进去。

这是一个服务器块,为我们在http://localhost:5000 上运行的应用程序定义了反向代理。一行一行地看下去。

  1. 我们让反向代理听从80端口
  2. 给反向代理一个名字(nodeserver)。
  3. 为根URL("/")设置位置块。
  4. 在location块中,我使用proxy_pass指令来设置我们要转发请求的URL

什么是位置块?

在Nginx确定使用哪个服务器块后,它必须选择哪个位置块来处理请求。一个位置块告诉Nginx应该如何处理对服务器上不同URL的请求。

location /blah {
    ...
}
    

如果我有另一个像上面那样的位置块,并访问了http://localhost:80/blah,Nginx将使用这个配置,因为请求的URL与位置块相匹配。

同时运行多个Node.js应用程序?

因此,如果我有另一个在localhost:8000上运行的Node应用程序,但我仍然想使用localhost:80作为我的代理,我可以简单地创建一个新的位置块,并将请求代理到其他服务器。

为了更清楚地说明这一点:假设我有两个独立的Node.js服务器在运行,我希望它们都能通过运行在localhost:80的反向代理来访问。在同一个URL上访问它们是不可能的(一个URL需要代理到一个服务器)。所以我们可以让localhost:80上的不同URL代理到不同的Node服务器。

节点服务器1:在localhost:5000上运行

节点服务器2:在localhost:8000上运行

配置可以是这样的。

访问localhost:80,你应该看到我们创建的 "Hello World!"服务器。祝贺你 运行在80端口的Nginx服务器正式成为你的Node应用程序的反向代理。

使用Docker-Compose将Node.js应用与NGINX进行Docker化

有很多文章解释了Docker的优势。它已经变得非常流行,在过去5年里,它的采用速度加快了。不用说,如果你是一个新兴的软件开发者,Docker绝对是一个你想在你的工具包里拥有的技术。

我还假设你的机器上已经安装了Docker Desktop,用于本教程的这一部分。

Docker只是让事情变得更干净。如果你把我们在上一节中刚刚创建的应用程序Docker化,就不需要改变我们本地机器上的Nginx配置,因为我们将有自己的专用Nginx容器在运行。所以,让我们看看这是如何工作的。

截至目前,该应用程序的结构看起来是这样的。

我们需要稍作修改,使创建Node.js Docker容器所需的所有文件都在一个文件夹中,而创建Nginx Docker容器所需的所有文件则在另一个文件夹中。简单地在项目根目录下创建两个文件夹,并将所有预先存在的文件移到新创建的 "app "目录中,如下所示。

现在是时候为每个人创建Dockerfiles了。在这之前,我们只需简单地做一些定义。

什么是Docker文件?

  1. 一个文本文件,告诉Docker如何组装一个镜像。

什么是Docker镜像?

  1. 一套用于创建Docker容器的打包指令

什么是Docker容器?

  1. 一个可运行的镜像实例,与它所运行的系统是隔离的。同一Docker容器可以在Windows、Mac等系统上运行。

首先,我们将对Node.js应用程序进行dockerize。在app目录下创建一个Dockerfile和一个.dockerignore。

Docker文件应该看起来像这样。

这是一个相当标准的Node.js应用程序的Docker文件。让我们逐行浏览一下。

  1. 从Docker Hub拉出Node.js镜像
  2. 设置容器内使用的工作目录(usr/src/app是Node.js应用程序的标准目录)。
  3. 将package.json文件从我们的机器上复制到容器的工作目录中。
  4. 运行npm安装
  5. 将本地机器目录下的所有文件复制到工作目录(包括node_modules)。
  6. 暴露5000端口。该应用程序在容器内的5000端口运行,所以我们不能在浏览器上查看它,除非我们暴露该端口
  7. 告诉镜像在容器启动时要运行什么命令

并在.dockerignorefile中添加以下两行。

.dockerignore文件应该和你的Dockerfile放在同一个目录下,目的是通过在构建时排除一些不会被用于构建docker镜像的文件来加快docker构建命令的速度。我们不会明确使用docker build,但我们将使用docker-compose来处理构建过程。

创建Nginx容器也需要一个类似的过程。

首先,我们将创建我们自己的Nginx服务器配置。就像我们在机器上运行Nginx一样,Nginx的一个实例将在容器中运行。就像在你的电脑上运行的Nginx监听某个端口一样,Nginx实例将在容器内监听某个端口,我们将对其进行配置,使其将请求代理给运行在5000端口的Node.js应用程序(这是我们的Node.js容器所暴露的)。

在nginx文件夹下创建一个名为default.conf的文件,用于存储代理的配置。这与我们之前创建sample_node_app.conf的方法类似。它应该如下所示。

这一切应该看起来很熟悉。有一点需要指出的是proxy_pass指令。我们将请求代理到 "http://nodeserver:5000 "而不是localhost:5000。这是我们用docker-compose做的事情的结果,我们会在下面解释一下。

接下来,在nginx文件夹中创建一个Docker文件。

我们从Docker Hub上获取官方的nginx镜像。当我们拉取镜像时,我们也得到了默认的Nginx配置(存储在nginx.conf中),还记得我说过nginx.conf文件可能位于3个地方之一,这取决于发行版吗?在官方的nginx镜像中,它位于/etc/nginx。

在Nginx的Docker发行版中也没有名为services的文件夹。相反,我们有一个名为conf.d的文件夹,它存储了所有的自定义配置。Docker文件中的第二条命令是将我们刚才写的配置复制到该文件夹中,以便容器中的Nginx可以使用它。

这就是了! 现在,是时候使用docker-compose来启动容器并让它们一起工作了。

Docker-compose是一个非常有用的工具,它可以让我们用一个命令来启动多个容器。它还在容器之间创建了一个共同的网络,它们可以用来相互通信。

我们所要做的就是创建一个名为docker-compose.yml的文件,列出哪些服务构成了应用程序,以及我们要如何配置每个容器。见
https://docs.docker.com/compose/,以获得更深入的解释。

我们的docker-compose.yml将由两个服务组成:一个是Node.js应用程序,另一个是Nginx服务器,它将请求代理到Node.js应用程序。因为有代理,所以有服务间的通信,所以容器将不得不互相交谈。

让我们在根目录下创建docker-compose.yml文件。

它将看起来像这样。

正如我所说,有两个服务,nodeserver和nginx。

下面我将解释每个字段。

  1. context:在build字段内,它告诉docker-compose找到该服务的Dockerfile的路径。
  2. ports:包含从本地机器到Docker容器内的端口的映射。因此,例如,Nginx服务中的 "80:80 "映射意味着,如果我们在浏览器上访问http://localhost:80,我们将查看容器内80端口上的任何内容。我们知道80端口是容器内的正确端口,因为Nginx镜像默认在那里监听。

现在我们有了一个完整的docker-compose文件,我们可以回到default.conf文件,澄清proxy_pass指令了。

我们写道,我们将代理请求到http://nodeserver:5000。

nodeserver是运行Node.js应用程序的服务/容器的名称。

docker-compose up --build

这将会启动容器,并暴露出端口,这样你就可以在你的浏览器上进行测试。容器启动后,你可以在命令行中输入docker ps -a来查看正在运行的容器,以及它们所运行的端口。

现在,如果你在网络浏览器上访问http://localhost:80,你应该看到同样的Hello World!应用程序,只是现在它运行在一个Docker容器上,Nginx的配置是完全独立的。你不必再碰你的系统的Nginx配置。

注意:确保在运行Docker容器之前停止系统中的Nginx实例,因为80端口可能会发生冲突。

相关推荐