介绍
反向代理 是一种代理服务器,它接受 HTTP(S) 请求并将它们透明地分发到一个或多个后端服务器。 反向代理非常有用,因为许多现代 Web 应用程序使用后端应用程序服务器处理传入的 HTTP 请求。 这些服务器并不意味着用户可以直接访问,并且通常只支持基本的 HTTP 功能。
您可以使用反向代理来防止这些底层应用程序服务器被直接访问。 它们还可用于将来自传入请求的负载分配到多个不同的应用程序服务器,从而大规模提高性能并提供故障安全性。 它们可以使用应用程序服务器不提供的功能来填补空白,例如缓存、压缩或 SSL 加密。
在本教程中,您将使用 mod_proxy
扩展将 Apache 设置为基本反向代理,以将传入连接重定向到在同一网络上运行的一个或多个后端服务器。 本教程使用使用 Flask Web 框架 编写的后端,但您可以使用任何您喜欢的后端服务器。
先决条件
要遵循本教程,您将需要:
- 使用 this initial server setup tutorial 设置一台 Ubuntu 20.04 服务器,包括
sudo
非 root 用户和防火墙。 - 按照 如何在 Ubuntu 20.04 上安装 Apache Web 服务器的 步骤 1 和 2 在您的服务器上安装 Apache 2。
第 1 步 — 启用必要的 Apache 模块
Apache 捆绑了许多模块,这些模块在全新安装中可用但未启用。 首先,您需要启用将在本教程中使用的那些。
您需要的模块是 mod_proxy
本身和它的几个附加模块,它们扩展了它的功能以支持不同的网络协议。 具体来说,您将使用以下内容:
mod_proxy
:重定向连接的主要代理模块。 它允许 Apache 充当通往底层应用程序服务器的网关。mod_proxy_http
:增加对代理 HTTP 连接的支持。mod_proxy_balancer
和mod_lbmethod_byrequests
:这些为多个后端服务器添加负载平衡功能。
要启用这四个模块,请执行以下命令:
sudo a2enmod proxy proxy_http proxy_balancer lbmethod_byrequests
要使这些更改生效,请重新启动 Apache:
sudo systemctl restart apache2
Apache 现在已准备好充当 HTTP 请求的反向代理。 在下一个可选步骤中,您将创建两个基本的后端服务器。 这些将有助于验证配置是否正常工作,但如果您已经拥有自己的后端应用程序,则可以跳至 Step 3。
第 2 步 — 创建后端测试服务器(推荐)
运行一些后端服务器可以帮助测试您的 Apache 配置是否正常工作。 在这里,您将创建两个通过打印一行文本来响应 HTTP 请求的测试服务器。 一台服务器会说 Hello world! 另一台会说 Howdy world! 这将让您测试多个服务之间的负载平衡。
注意: 在实际设置中,后端服务器通常都返回相同类型的内容。 但是,出于此测试的目的,让两个服务器返回不同的消息可以让您检查负载平衡机制是否同时使用了这两个消息。
Flask 是一个用于构建 Web 应用程序的 Python 微框架。 此步骤概述了如何使用 Flask 创建测试服务器,因为最小的应用程序只需要几行代码。 你不需要知道 Python 来设置这些,但是如果你想学习,你可以查看我们关于 如何在 Python 中编码 的系列。
首先使用 apt
更新包索引列表:
sudo apt update
然后安装pip
,推荐的Python包管理器:
sudo apt install python3-pip
接下来,使用 pip
安装 Flask:
sudo pip3 install Flask
现在所有必需的组件都已安装,创建一个新文件,该文件将包含当前用户主目录中第一个后端服务器的代码。 您可以使用您喜欢的文本编辑器执行此操作,这里我们将使用 nano
:
nano ~/backend1.py
将以下代码片段插入到文件中:
~/backend1.py
from flask import Flask app = Flask(__name__) @app.route('/') def home(): return 'Hello world!'
前两行代码初始化了 Flask 框架。 有一个函数 home()
,它返回一行文本 (Hello world!
)。 home()
函数定义上方的 @app.route('/')
行告诉 Flask 使用 home()
' 的返回值作为对指向 /
根的 HTTP 请求的响应应用程序的 URL。
完成后,保存并退出文件。 如果您正在使用 nano
,您可以按 CTRL + X
,然后按 Y
和 ENTER
来执行此操作。
除了返回不同的文本行之外,第二个后端服务器与第一个完全相同。 因此,运行 cp
将第一个文件 backend1.py
中的内容复制到 backend2.py
文件中:
cp ~/backend1.py ~/backend2.py
现在使用您喜欢的文本编辑器打开新复制的文件:
nano ~/backend2.py
将返回 Hello world! 消息的消息更新为改为 Howdy world!:
~/backend2.py
from flask import Flask app = Flask(__name__) @app.route('/') def home(): return 'Howdy world!'
完成更新后,保存并关闭文件。
接下来,使用以下命令在端口 8080
上启动第一个后台服务器。 这也将 Flask 的输出重定向到 /dev/null
因为它会使控制台输出进一步模糊:
FLASK_APP=~/backend1.py flask run --port=8080 >/dev/null 2>&1 &
在这里,您通过在同一行中设置 FLASK_APP
环境变量,在 flask
命令之前。 环境变量 是一种将信息传递到从 shell 生成的进程的便捷方式。 您可以在我们的 如何在 Linux VPS 上读取和设置环境变量和 Shell 变量的指南中了解有关环境变量的更多信息。
在这种情况下,使用环境变量可确保该设置仅适用于正在运行的命令,之后将不再可用。 这对于避免混淆是必要的,因为您将以相同的方式传递另一个文件名来告诉 flask
命令启动第二个服务器。
同样,您想运行以下命令在端口 8081
上启动第二个服务器。 请注意 FLASK_APP
环境变量的不同值:
FLASK_APP=~/backend2.py flask run --port=8081 >/dev/null 2>&1 &
现在您可以使用 curl
命令测试两个服务器是否正在运行。 首先测试第一台服务器。 此命令使用 curl
连接到 127.0.0.1
,这是一个特殊的 IP 地址,表示 localhost。 这意味着以下命令告诉您的服务器连接到自身并打印自己的响应:
curl http://127.0.0.1:8080/
这将从服务器打印以下响应:
OutputHello world!
接下来,测试第二台服务器:
curl http://127.0.0.1:8081/
和以前一样,这将打印来自服务器的预期响应:
OutputHowdy world!
在下一步中,您将修改 Apache 的配置文件以将其用作反向代理。
第 3 步 — 修改默认配置以启用反向代理
在本节中,您将设置默认的 Apache 虚拟主机作为单个后端服务器或负载平衡后端服务器阵列的反向代理。
注意:在本教程中,您将在虚拟主机级别应用配置。 在 Apache 的默认安装中,仅启用了一个默认虚拟主机。 但是,您也可以在其他虚拟主机中使用所有这些配置片段。 要了解有关 Apache 中虚拟主机的更多信息,您可以阅读我们的 如何在 Ubuntu 20.04 上设置 Apache 虚拟主机教程。
如果您的 Apache 服务器同时充当 HTTP 和 HTTPS 服务器,则您的反向代理配置必须同时放置在 HTTP 和 HTTPS 虚拟主机中。 要了解有关使用 Apache 的 SSL 的更多信息,您可以阅读我们关于 如何在 Ubuntu 20.04 教程中为 Apache 创建自签名 SSL 证书的教程。
使用您喜欢的文本编辑器打开默认的 Apache 配置文件:
sudo nano /etc/apache2/sites-available/000-default.conf
在该文件中,您会发现从第一行开始的 <VirtualHost *:80>
块。 以下第一个示例说明如何将此块配置为单个后端服务器的反向代理,第二个示例为多个后端服务器设置负载平衡反向代理。
示例 1 — 反向代理单个后端服务器
首先,替换 VirtualHost
块中的所有内容,使您的配置文件如下所示。 如果您已按照 Step 2 中的示例服务器进行操作,请使用 127.0.0.1:8080
。 如果您有自己的应用程序服务器,请改用它们的地址:
/etc/apache2/sites-available/000-default.conf
<VirtualHost *:80> ProxyPreserveHost On ProxyPass / http://127.0.0.1:8080/ ProxyPassReverse / http://127.0.0.1:8080/ </VirtualHost>
代表了三个指令,以下是它们所做工作的简要概述:
ProxyPreserveHost
:使 Apache 将原始的Host
标头传递给后端服务器。 这很有用,因为它使后端服务器知道用于访问应用程序的地址。ProxyPass
:主要代理配置指令。 在这种情况下,它指定根 URL (/
) 下的所有内容都应映射到给定地址的后端服务器。 例如,如果 Apache 收到对/example
的请求,它将连接到http://your_backend_server/example
并将响应返回给原始客户端。ProxyPassReverse
:应与ProxyPass
具有相同的配置。 它告诉 Apache 修改来自后端服务器的响应标头。 这确保如果后端服务器返回位置重定向标头,客户端的浏览器将被重定向到代理地址而不是后端服务器地址,这将无法按预期工作。
添加完此内容后,保存并退出文件。
要使这些更改生效,请重新启动 Apache:
sudo systemctl restart apache2
现在,如果您在 Web 浏览器中访问 http://your_server_ip
,您将看到后端服务器响应,而不是标准的 Apache 欢迎页面。 如果您按照 Step 2,这意味着您将在浏览器中看到 Hello world!。
示例 2 - 跨多个后端服务器的负载平衡
如果您有多个后端服务器,代理时在它们之间分配流量的一个好方法是使用 mod_proxy
的负载平衡功能。
首先使用您喜欢的文本编辑器打开默认的 Apache 配置文件:
sudo nano /etc/apache2/sites-available/000-default.conf
现在替换 VirtualHost
中的所有内容,以便您的配置文件如下所示。 如果您遵循 Step 2 中的示例服务器,请使用 127.0.0.1:8080
和 127.0.0.1:8081
作为 BalancerMember
指令。 如果您有自己的应用程序服务器,请改用它们的地址:
/etc/apache2/sites-available/000-default.conf
<VirtualHost *:80> <Proxy balancer://mycluster> BalancerMember http://127.0.0.1:8080 BalancerMember http://127.0.0.1:8081 </Proxy> ProxyPreserveHost On ProxyPass / balancer://mycluster/ ProxyPassReverse / balancer://mycluster/ </VirtualHost>
配置与前一个类似,但这些指令不是直接指定单个后端服务器,而是执行以下操作:
Proxy
:这个附加的Proxy
块用于定义多个服务器。 该块名为balancer://mycluster
(名称可以自由更改),由一个或多个BalancerMember
组成,它们指定底层后端服务器地址。ProxyPass
和ProxyPassReverse: these directives use the load balancer pool named
mycluster` 而不是特定的服务器。
如果您遵循 Step 2 中的示例服务器,请使用 127.0.0.1:8080
和 127.0.0.1:8081
作为 BalancerMember
指令,如上块所述。 如果您有自己的应用程序服务器,请改用它们的地址。
添加完此内容后,保存并退出文件。
要使这些更改生效,请重新启动 Apache:
sudo systemctl restart apache2
如果您在 Web 浏览器中访问 http://your_server_ip
,您将看到后端服务器的响应,而不是标准的 Apache 页面。 如果你按照 Step 2,那么多次刷新页面应该会显示 Hello world! 和 Howdy world!,这意味着反向代理工作并且正在负载平衡两台服务器。
注意:在不再需要两个测试服务器后关闭它们,就像完成本教程一样,可以执行killall flask
命令。
结论
现在您知道如何将 Apache 设置为一个或多个底层应用程序服务器的反向代理。 mod_proxy
可以有效地用于为使用大量语言和技术(例如 Python 和 Django,或 Ruby 和 Ruby on Rails)编写的应用程序服务器配置反向代理。 它还可以用于为具有大量流量的站点平衡多个后端服务器之间的流量,通过多个服务器提供高可用性,或者为本地不支持 SSL 的后端服务器提供安全的 SSL 支持。
虽然 mod_proxy
和 mod_proxy_http
可能是最常用的模块组合,但还有其他几种支持不同的网络协议。 您在本教程中没有使用它们,但其他一些流行的模块包括:
mod_proxy_ftp
用于 FTP(文件传输协议)。mod_proxy_connect
用于 SSL 隧道。mod_proxy_ajp
用于 AJP(Apache JServ 协议),例如基于 Tomcat 的后端。mod_proxy_wstunnel
用于网络套接字。
要了解更多关于mod_proxy
的信息,您可以阅读官方Apache mod_proxy文档。