HAProxy网络错误:无法绑定套接字

来自菜鸟教程
跳转至:导航、​搜索

介绍

当有另一个进程在 HAProxy 配置使用的同一接口和 TCP 端口组合上侦听时,或者当 HAProxy 尝试使用未分配给网络接口的 IP 地址时,会生成 HAProxy cannot bind socket 错误消息. 这两种错误情况都源自底层操作系统的网络堆栈。

在第一种情况下,当另一个进程已经在使用 HAProxy 尝试绑定的接口和端口时,Linux 上的底层错误是 EADDRINUSE。 问题是在任何给定时间只能将单个进程绑定到 IP 地址和端口组合。

在第二种情况下,当 HAProxy 尝试使用未分配给系统接口的 IP 地址时,Linux 上的基础错误是 EADDRNOTAVAIL。 这里的问题是无法使用操作系统不可用的地址创建 IP 套接字。

但是,两个底层错误都会生成相同的 HAProxy 错误消息,因此解决 cannot bind socket 错误需要检查 Linx 系统上当前使用的套接字和 IP 地址的列表。

要检测 cannot bind socket 错误消息,您需要检查 systemctljournalctl 输出以确定导致错误的 IP 地址和端口组合。 然后,您可以检查其他正在运行的进程和网络接口并决定如何解决问题,无论是通过切换服务器、更改 HAProxy 使用的 IP 地址或端口,还是这些选项的任意组合。

systemctl 故障排除

按照本系列开头的 如何对常见 HAProxy 错误进行故障排除 教程中的故障排除步骤,当您对 cannot bind socket 错误消息进行故障排除时,第一步是使用 systemctl

systemctl status 的输出在许多情况下将包含解决错误所需的所有诊断信息。 它可能包括 HAProxy 正在使用的 IP 地址,以及它尝试绑定的端口。 输出还将指示 HAProxy 无法启动多长时间,以便您可以确定问题影响 HAProxy 的时间。

注意:如果您使用的是 Ubuntu 或 Debian 派生的 Linux 发行版,则 systemctl 不包括来自 HAProxy 的输出以及描述问题的 cannot bind socket 错误消息。 跳到本教程的下一部分 使用 journalctl 日志 进行故障排除,了解如何检查 systemd 日志以查找冲突的 IP 地址或端口。


在 CentOS、Fedora 和 RedHat 派生系统上,使用此 systemctl 命令检查 HAProxy 的状态:

CentOS 和 Fedora 系统

sudo systemctl status haproxy.service -l --no-pager

-l 标志将确保 systemctl 输出一行的全部内容,而不是用省略号 () 代替长行。 --no-pager 标志会将整个日志输出到您的屏幕,而无需调用像 less 这样一次只显示一个屏幕内容的工具。

由于您正在对 cannot bind socket 错误消息进行故障排除,因此您应该会收到类似于以下内容的输出:

Output● haproxy.service - HAProxy Load Balancer
   Loaded: loaded (/usr/lib/systemd/system/haproxy.service; disabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Wed 2020-08-19 14:57:05 UTC; 3s ago
  Process: 138738 ExecStart=/usr/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE (code=exited, status=1/FAILURE)
  Process: 138736 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q (code=exited, status=0/SUCCESS)
 Main PID: 138738 (code=exited, status=1/FAILURE)

Aug 19 14:57:05 92214d8ff5e2 systemd[1]: Starting HAProxy Load Balancer...
Aug 19 14:57:05 92214d8ff5e2 haproxy[138738]: [ALERT] 231/145705 (138738) : Starting frontend main: cannot bind socket [0.0.0.0:80]
. . .
Aug 19 14:57:05 92214d8ff5e2 systemd[1]: Failed to start HAProxy Load Balancer.

此示例 systemctl 输出包括 systemd 日志中描述错误的一些突出显示的行。 这些行为您提供了有关进一步排除故障所需的错误的所有信息。 具体来说,cannot bind socket [0.0.0.0:80] 行描述了 HAProxy 尝试使用的套接字 (0.0.0.0:80),因此您可以跳过以下 journalctl 步骤,而是继续执行 故障排除本教程末尾的 ss 和 ps Utilities 部分。 另一条突出显示的行表示 HAProxy 进程的状态,在出现 cannot bind socket 错误的情况下将显示 Failed to start HAProxy Load Balancer.

如果您的 systemctl 输出没有提供有关导致错误的 IP 地址和端口的具体信息(如果您使用的是 Ubuntu 或 Debian,那么这适用),那么您将需要检查 [ systemd 日志的 X215X] 输出。 以下部分说明如何使用 journalctlcannot bind socket 错误进行故障排除。

使用 journalctl 日志进行故障排除

如果您的 systemctl 输出不包含有关 cannot bind socket 错误的详细信息,则应继续使用 journalctl 命令检查 HAProxy 的 systemd 日志。

在 Ubuntu 和 Debian 派生系统上,运行以下命令:

sudo journalctl -u haproxy.service --since today --no-pager

在 CentOS、Fedora 和 RedHat 派生系统上,使用以下命令检查日志:

sudo journalctl -u haproxy.service --since today --no-pager

--since today 标志将限制命令的输出以仅记录从当天 00:00:00 开始的条目。 使用此选项将有助于限制检查错误时需要检查的日志条目的数量。

如果 HAProxy 无法绑定到正在使用的端口,请在输出中搜索与以下日志条目类似的行,特别是包含本示例中突出显示的 cannot bind socket 错误消息的行:

Output-- Logs begin at Wed 2020-08-19 19:38:12 UTC, end at Wed 2020-08-19 19:53:53 UTC. --
. . .
Aug 19 19:39:21 92214d8ff5e2 systemd[1]: Starting HAProxy Load Balancer...
Aug 19 19:39:21 92214d8ff5e2 haproxy[135]: [ALERT] 231/193921 (135) : Starting frontend main: cannot bind socket [0.0.0.0:80]
Aug 19 19:39:21 92214d8ff5e2 haproxy[135]: [ALERT] 231/193921 (135) : Starting frontend main: cannot bind socket [:::80]
Aug 19 19:39:21 92214d8ff5e2 systemd[1]: haproxy.service: Main process exited, code=exited, status=1/FAILURE
Aug 19 19:39:21 92214d8ff5e2 systemd[1]: haproxy.service: Failed with result 'exit-code'.
Aug 19 19:39:21 92214d8ff5e2 systemd[1]: Failed to start HAProxy Load Balancer.
. . .

第一行突出显示的输出表明 HAProxy 无法绑定到所有可用 IPv4 接口上的端口 80(由 0.0.0.0 IP 地址表示)。 根据您的系统配置,IP 地址可能不同,并且仅显示单个 IP。

如果您将 HAProxy 与 IPv6 一起使用,则输出还可能包含类似于第二行的行,该行以 IPv6 特定接口和端口错误突出显示,在本例中为 :::80。 前两个 :: 字符表示所有可用的 IPv6 接口,而后面的 :80 表示端口。

即使您自己的系统可能有不同的冲突接口和端口,错误也会与此处显示的输出类似。 使用 journalctl 的此输出,您将能够在本教程的以下部分中使用 sspsip 命令诊断问题。

使用 ssps 实用程序进行故障排除

要解决 cannot bind socket 错误,您需要确定哪些其他进程正在侦听 HAProxy 尝试使用的 IP 地址和端口,或者该 IP 地址是否可用于 HAProxy。

例如,如果另一个服务器(如 Nginx)被配置为侦听所有可用 IPv4 网络接口上的端口 8080,则完整的套接字将是 0.0.0.0:8080。 如果 HAProxy 也配置为使用 0.0.0.0:8080,那么操作系统将抛出 EADDRINUSE 错误,并且 HAProxy 将显示 cannot bind socket 错误消息,因为它无法为自己声明套接字.

在前面的 journalctl 部分中,某些东西已经绑定到所有可用的 IPv4 地址(由 0.0.0.0:80 表示)。 大多数现代 Linux 发行版都包含一个名为 ss 的实用程序,可用于收集有关系统网络套接字状态的信息。

以下命令将确定已绑定到端口 80 上的 IPv4 接口的进程的名称。 如果它与以下命令中的 80 不同,请确保从错误消息中替换端口:

sudo ss -4 -tlnp | grep 80

ss 命令的标志以下列方式改变其默认输出:

  • -4 限制 ss 只显示 IPv4 相关的套接字信息。
  • -t 将输出限制为仅 tcp 套接字。
  • -l 显示考虑了 -4-t 限制的所有侦听套接字。
  • -n 确保显示端口号,而不是像“httporhttps”这样的协议名称。 这很重要,因为 HAProxy 可能会尝试绑定到非标准端口,并且与实际端口号相反,服务名称可能会令人困惑。
  • -p 输出有关绑定到端口的进程的信息。
  • | grep 80 将输出限制为包含字符 80 的行,因此您必须检查的行更少

注意:在此 IPv4 和以下 IPv6 示例中,如果您的输出中没有具有匹配端口的行,则您的 cannot bind socket 错误可能源自 EADDRNOTAVAIL 错误。 跳到下一部分 使用 ip 实用程序进行故障排除 以检查系统上的可用 IP 地址。


使用所有这些标志,您应该会收到如下输出:

OutputLISTEN   0         511                 0.0.0.0:80               0.0.0.0:*        users:(("nginx",pid=40,fd=6))

在对 cannot bind socket 错误进行故障排除时,前三个字段并不重要,因此可以忽略它们。 重要的字段是第四个 (0.0.0.0:80),它匹配您之前发现的 journalctl 错误,以及最后一个 users:(("nginx",pid=40,fd=6)),特别是 pid=40 部分.

如果您遇到与 IPv6 接口相关的 cannot bind socket 错误,请重复 ss 调用,这次使用 -6 标志将接口限制为 IPv6 网络堆栈,例如这:

sudo ss -6 -tlnp |grep 80

-6 标志将 ip 命令限制为 IPv6 接口。 如果 HAProxy 无法绑定到 IPv6 套接字,您应该有如下输出:

OutputLISTEN   0         511                    [::]:80                  [::]:*        users:(("nginx",pid=40,fd=7))

如果与此处给出的突出显示的 80 不同,请再次从 journalctl 输出中替换有问题的端口号。

在 IPv4 和 IPv6 错误的这两种情况下,ss 输出表明有一个进程 ID 为 40 的程序(输出中的 pid=40)绑定到 [X174X ] 和 [::]:80 接口。 此过程阻止 HAProxy 启动,因为它已经拥有该端口。 要确定程序的名称,请像这样使用 ps 实用程序,用输出中的进程 ID 代替此示例中突出显示的 40 值:

sudo ps -p 40

您将收到类似于以下内容的输出:

OutputPID TTY          TIME CMD
40 ?        00:00:00 nginx

输出中突出显示的 nginx 是正在侦听接口的进程的名称。 现在您有了阻止 HAProxy 启动的程序的名称,您可以决定如何解决该错误。 您可以停止 nginx 进程,重新配置 nginx 以侦听不同的接口和端口,或重新配置 HAProxy 以避免端口冲突。

需要注意的是,进程可能与 nginx 不同,如果您正在诊断 [ X172X] 错误。 通常,不同的 Web 服务器和代理将在同一台服务器上使用。 每个都可能试图绑定到不同的 IPv4 端口和 IPv6 接口以处理不同的 Web 流量。 例如,配置了 HAProxy 的服务器在端口 8080 上侦听 IPv4 环回地址(也称为 localhost)将显示 ss 输出,如下所示:

OutputLISTEN   0         2000              127.0.0.1:8080            0.0.0.0:*       users:(("haproxy",pid=545,fd=7))

将指示特定 IP 地址和端口的 systemctl 输出或 journalctl 输出与来自 ss 的诊断数据组合起来很重要,然后是 ps 以缩小范围关闭导致 HAProxy 无法启动的进程。

有时当你用ssps排查cannot bind socket错误信息时根本没有任何输出,这意味着错误可能不是由套接字冲突引起的. 本教程的下一部分说明如何使用 ip 实用程序对 cannot bind socket 错误进行故障排除。

使用 ip 实用程序进行故障排除

上一节解释了 EADDRINUSE 操作系统错误如何导致 cannot bind socket 错误消息。 但是,如果您检查了 ssps 输出并且系统上没有套接字冲突,则问题可能是由 EADDRNOTAVAIL 操作系统错误引起的。 在这种情况下,HAProxy 可能会尝试绑定到您的操作系统不可用的套接字。

要确定 cannot bind socket 错误是否由 EADDRNOTAVAIL 引起,请使用 ip 命令检查系统上的 IPv4 和 IPv6 网络接口。

sudo ip -4 -c address show
  • -4 限制 ip 只显示 IPv4 相关的接口信息。
  • -c 将颜色编码添加到输出中,以便更容易在视觉上解析。
  • address show 显示接口的 IP 地址,其中考虑了 -4-c 标志。

在包含 ip 工具的任何 Linux 发行版上,您应该会收到类似于以下内容的输出:

Output1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 203.0.113.1/24 brd 203.0.113.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.0.2.1/24 brd 192.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 198.51.100.1/24 brd 198.51.100.255 scope global eth1
       valid_lft forever preferred_lft forever

记下与此输出中突出显示的示例相对应的 IP 地址。 您的 IP 地址和网络接口将不同于此处显示的示例。 您可能有更多或更少的接口,并且每个接口可能有更多或更少的地址分配给它们。 重要的部分是记下来自 ip 的 IP 地址。

要检查分配给您的系统的 IPv6 地址,请使用带有 -6 标志的 ip 命令,如下所示:

sudo ip -6 -c address show

您应该收到如下输出:

Output1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 2604:a880:400:d1::3d3:6001/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::a4ff:aaff:fec9:24f8/64 scope link
       valid_lft forever preferred_lft forever

再次注意此示例输出中突出显示的值,并在输出中查找相应的 IPv6 地址。

获得分配给系统的地址列表后,您可以尝试查找与 cannot bind socket [x.x.x.x:80] 错误对应的匹配 IP 地址。 如果没有匹配的 IP 地址,则 HAProxy 可能配置为使用您的系统不可用的 IP 地址,并且 cannot bind socket 错误是由操作系统抛出 EADDRNOTAVAIL 引起的错误。

要解决此错误,您需要编辑 /etc/haproxy/haproxy.cfg 文件并将 bind 地址更改为基于 [X190X 输出的系统可用的 IP 地址] 命令。

例如,如果 /etc/haproxy/haproxy.cfg 包含如下 bind 行,使用 198.51.100.123 作为 IP 地址,但您的系统根据示例输出分配了 198.51.100.1上面,您将需要编辑绑定线。

按照这个假设的例子,这个 haproxy.cfg 片段显示了无效的 IP 地址:

/etc/haproxy/haproxy.cfg

. . .
frontend main
        bind 198.51.100.123:80

与示例 ip 输出中的 IP 地址匹配的正确 bind 行如下所示:

/etc/haproxy/haproxy.cfg

. . .
frontend main
        bind 198.51.100.1:80

使用正确的 IP 地址编辑 /etc/haproxy/haproxy.cfg 后,使用 systemctl 命令重新启动它:

sudo systemctl restart haproxy.service

现在检查 HAProxy 的状态并确保输出显示 active (running) 行:

sudo systemctl status haproxy.service
Output● haproxy.service - HAProxy Load Balancer
   Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2020-08-19 21:31:46 UTC; 17h ago
     Docs: man:haproxy(1)
           file:/usr/share/doc/haproxy/configuration.txt.gz
  Process: 487 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS (code=exited, status=0/SUCCESS)
. . .
Aug 19 21:31:46 d6cdd0c71489 systemd[1]: Started HAProxy Load Balancer.

如果您已解决 cannot bind socket 错误,您的输出应该与此示例输出类似。 突出显示的行显示 HAProxy 处于活动状态,并且该进程已成功启动。

结论

在本教程中,您学习了如何对 IPv4 和 IPv6 接口上的 HAProxy cannot bind socket 错误消息进行故障排除。 您学习了如何使用 systemctl 检查 HAProxy 服务器的状态并尝试查找错误消息。 您还学习了如何使用 journalctl 检查 systemd 日志以获取有关 cannot bind socket 错误的特定信息。

通过 systemd 日志中的相应错误消息,您随后了解了 ss 实用程序以及如何使用它来检查系统网络套接字的状态。 之后,您学习了如何将 ss 中的进程 ID 信息与 ps 实用程序结合起来,以查找导致 HAProxy 无法启动的进程的名称。

最后,在出现与 IPv4 或 IPv6 地址不可用有关的 cannot bind socket 错误的情况下,您学习了如何使用 ip 实用程序检查系统上可用的网络接口。