Node.js + Express:应用程序无法在80端口上开始监听

49
我将创建并发布类似的应用程序:

我会像这样创建和发布一个应用程序:

express -s -t ejs
npm install express
npm install ejs
node app.js

它可以正常工作(在端口3000上)。但是当我将端口更改为80时,运行node app.js会输出以下内容:

node.js:198
throw e; // process.nextTick error, or 'error' event on first tick
          ^
TypeError: Cannot call method 'getsockname' of null
at HTTPServer.address (net.js:746:23)
at Object.<anonymous> (/var/www/thorous/app.js:35:67)
at Module._compile (module.js:432:26)
at Object..js (module.js:450:10)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)
at Array.<anonymous> (module.js:470:10)
at EventEmitter._tickCallback (node.js:190:26)

这个在我的笔记本电脑上也有效,但在我的Amazon EC2实例上却无效,其中80端口是打开的。 我无法确定问题所在。 有什么提示吗?


4
这是一个糟糕的错误信息。 - Bradley Kreider
4个回答

94
如果你真的想做这件事,可以将端口80上的流量转发到3000端口。
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

太好了!我成功解决了这个问题! - Yoon Lee
1
漂亮而干净的答案,不需要使用nginx或其他代理解决方案! - hazelnut
5
这个命令导致我的整个DigitalOcean服务器崩溃了......我希望其他人能看到这个信息。 - Maxwelll
3
当您将80端口重定向到3000端口时,很可能有另一个进程在该端口上运行,并被覆盖。 - Michael Connor
我建议添加一个方法来使其持久化。例如,在Ubuntu中,sudo apt install iptables-persistent可以以一种干净的方式解决这个问题。 - SpiRail
显示剩余2条评论

72

您是否以root权限启动您的应用程序?因为较低的端口号需要root权限。 也许sudo node app.js可以工作?

但是,您不应该使用root权限在端口80上运行任何node.js应用程序!!!绝对不要这样做!

我的建议是在前面运行nginx作为反向代理,将您的node.js应用程序运行在端口例如3000上。


7
为什么不能在80端口以root身份运行node.js应用程序?我很好奇(一个新手js开发者)。 - Christopher
11
这不仅适用于node。不建议以root权限运行任何服务器软件。如果必须这样做,因为您想使用特权端口,则进程会放弃特权并将该端口上所有传入的流量“转发”到以较低特权用户运行的相应进程。这是因为没有程序是完美的。每个软件都有漏洞和可能存在安全问题。如果攻击者能够利用这些漏洞并注入代码,那么这些代码将以运行此进程的用户身份执行。因此,如果您的进程以root权限运行,攻击者可以在您的计算机上执行任何操作。 - Thomas Fritz
3
在这种情况下,将nginx作为反向代理运行有点过头了。我建议使用iptables进行端口转发,除非你需要在Express应用程序前面使用额外的nginx功能。 - awhie29urh2
2
@ThomasFritz为什么我们通常在80端口上运行Apache,如果你说我们不应该在那里运行Node? - trysis
6
因为像任何其他进程一样,Apache需要以root权限才能监听特权端口80。但是它会立即创建"工作进程"并降低其权限至较低的普通用户和组,然后将该端口上的所有传入请求转发给其工作进程。但是Node是单个进程 - 您可以将其视为Apache的这样一个工作进程。如果您想在80端口上运行Node,则整个过程 - 每个可能的指令 - 都将以root身份运行。因此,这种设置类似于nginx(80)--> node(3000)。 - Thomas Fritz
显示剩余2条评论

10

保持愚蠢简单:

  • setcap:设置特殊能力,允许非root用户绑定特权端口。
  • systemd:系统和服务管理器,可用于启动应用程序和守护进程。
  • VPS:虚拟专用服务器,通常作为云计算平台中的一种提供。

在普通的VPS(例如Digital Ocean、Linode、Vultr或Scaleway)上,磁盘是持久的,使用"setcap"。这将允许非root用户绑定到特权端口。

sudo setcap 'cap_net_bind_service=+ep' $(which node)

TADA!现在您可以作为普通用户运行node ./server.js --port 80

顺带一提:

您还可以使用systemd停止和启动服务。 由于systemd有时会让人感到糟心,所以我编写了一个Go语言的包装脚本,使部署Node项目变得非常容易:

# Install
curl https://webinstall.dev/serviceman | bash

# Use
cd ./my/node/project
sudo serviceman --username $(whoami) add npm start

如果你的服务器不叫做'server.js'(实际上是标准),或者有额外的选项:

cd ./my/node/project
sudo serviceman --username $(whoami) add node ./my-server-thing.js -- --my-options

这只是使用合理默认值为您创建systemd文件的操作。我建议您也查看systemd文档,但是它有点难以理解,并且可能比简单易懂的教程更令人困惑。

临时实例(例如EC2)不适用于长时间运行的服务器

通常情况下,人们使用EC2是因为他们不关心单个实例的正常运行时间可靠性 - 他们想要一个“可扩展”的架构,而不是持久的架构。

在大多数情况下,实际上并不打算让虚拟化服务器以任何方式持久存在。在这些类型的“短暂”(临时)环境中,“重新启动”旨在与从头开始重新安装相同。

你不需要“设置服务器”,而是“部署镜像”。您登录此类服务器的唯一原因是原型或调试正在创建的镜像。

“磁盘”是易失性的,IP地址是浮动的,镜像在每次启动时都会表现出相同的行为。您通常也不会使用传统意义上的用户帐户概念。

因此:尽管通常情况下不应将服务作为root运行,但在您通常使用易失性虚拟化的情况下...这并不重要。您有一个单独的服务,一个单独的用户帐户,只要实例失败或以其他方式“重新启动”(或者您启动镜像的新实例),您就会再次获得一个全新的系统(这意味着任何漏洞都会持续存在)。

防火墙:临时 vs VPS

像EC2这样的东西通常是私有的,而不是面向公众的。这些是“云服务”系统。您可以使用十几种不同的服务和自动缩放。因此,您将使用负载均衡器服务将端口转发到EC2组。通常,实例的默认防火墙会拒绝所有公共网络流量。您必须进入防火墙管理,并确保您打算使用的端口实际上是开放的。

有时VPS提供商具有“企业”防火墙配置器,但更典型的情况是您只能访问虚拟机,因为默认情况下只有您实际侦听的端口才能访问外部世界(通常不会有随机服务运行),因此您可能无法从防火墙中获得任何额外的好处。当然是个好主意,但不是必需品。

不要将EC2用作VPS

您上述的用例可能更适合传统的VPS服务(如上所述:Digital OceanLinodeVultrScaleway等),这些服务更易于使用,并且起步管理难度更小。你只需要一点bash CLI的知识。

作为额外的奖励,您不需要猜测成本是多少。他们用简单的每月美元告诉您,而不是每个CPU /小时/GB/内部网络/外部网络等的¢ - 因此,当出现问题时,您会通过电子邮件或管理控制台收到警告消息,而不是一张意外的6,527美元账单。

底线:如果您选择使用EC2,并且您不是拥有财务人员“DevOps”专家...您将会遇到困难。


-1

可能之前在80端口上还有其他运行的程序吗?

也许可以进行一次端口扫描,确认它尚未被使用?

nc -z <<your IP>> 80

1
我想不出什么问题。这是一个全新的实例,安装了node.js并运行了mongodb服务器。nc没有返回任何内容。 - Vitaly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接