本文源自我的博客:http://blog.allenm.me/2012/10/deply-nodejs-with-apache/
最近,nodejs 很火,我也尝试用 nodejs 写了一个小应用,主要用到了 express framework 和 socket.io ,数据库使用的 mongodb.
我有一台Linode的VPS ,自然是想把应用部署在自己的VPS上面,为什么没有选择云服务呢?一方面是省钱,我已经为Linode付过钱了,一方面是更加自由,可以随心配置。这台Linode上已经部署了标准的 LAMP 环境,跑了几个 wordpress 。那这次部署自然是不能影响到现有应用。
我的部署方案主要要达到的目的有:
nodejs 应用可以像一个守护进程(daemon)一样运行 提供一对简单的命令来 start / stop nodejs 应用 提供监控机制,当应用崩溃,可以及时重启它 不影响现有 PHP 应用,但是也要用 80 端口访问 socket.io 提供的 websocket 功能正常使用 下面分享下我的部署经验,不一定好,我也是第一次部署,欢迎交流。
1,在 VPS 上安装 nodejs ,我 VPS 跑的是 Ubuntu 12.04 , 最简单的方式当然是通过 apt-get 来安装,但是直接 apt-get install 的话,安装的版本比较旧,建议采用 https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager 这个页面提供的方法,先添加源,再来安装,就可以安装到最新的稳定版了。这里还有其他主要 Linux 发行版的安装方式可以参考。下面的步骤默认使用的 OS 是 Ubuntu 12.04。
2,安装 mongodb (如果使用的不是 mongodb ,请忽略此步) ,http://www.mongodb.org/downloads#packages 这里同样有包管理器的安装方式,照着做就好了。
3,把你的代码放到 VPS 上来,然后以开发模式运行一次试试,不出意外的话,应该是可以运行了,如果有其他依赖需要解决,请自行解决。
4,把 nodejs 应用变成一个 daemon 。 我使用 upstart 来完成此任务 ,高版本的 ubuntu 已经自带,如果没有,直接 apt-get install upstart 来安装。然后新建一个文件在 /etc/init/yourapp.conf, 文件内容如下
#!upstart
description "node.js server"
author "joe"
start on startup
stop on shutdown
script
# We found $HOME is needed. Without it, we ran into problems
export HOME="/var/www"
echo $$ > /var/run/yourapp.pid
exec sudo -u username NODE_ENV=production /usr/bin/node /where/yourapp.js >> /var/log/yourprogram.sys.log 2>&1
end script
pre-start script
# Date format same as (new Date()).toISOString() for consistency
echo "[`date -u +%Y-%m-%dT%T.%3NZ`] (sys) Starting" >> /var/log/yourapp.sys.log
end script
pre-stop script
rm /var/run/yourprogram.pid
echo "[`date -u +%Y-%m-%dT%T.%3NZ`] (sys) Stopping" >> /var/log/yourapp.sys.log
end script
你需要修改上述文件的 username 为你想要运行应用的用户名,推荐 www-data ,不要使用 root ,比较危险。如果你不使用 www-data 来运行你的应用,记得把export HOME=""改成对应的 HOME 目录。/where/yourapp.js 是你应用的路径。/var/run/yourapp.pid是你应用的pid文件,/var/log/yourapp.sys.log是你应用的日志文件。这些内容都需要根据你的情况修改。NODE_ENV=production 是让你的 nodejs 应用运行在生产环境的环境变量,你可以在代码中通过 process.env.NODE_ENV来获取。
配置文件创建好后,你就可以使用下面命令来开启/关闭你的应用了。
#!sh
start yourapp
stop yourapp
5,监控你的应用。我们不能保证我们的代码总是运行的很好,总会有崩溃的可能,所以我们需要监控它的运行,当它挂掉后,重启它。这里使用 monit 来解决这个问题,安装方法自己去 http://mmonit.com/monit/ 这个网站找吧。
安装好后,新建一个文件在/etc/init/monit/conf.d/yourapp.conf,文件内容如下:
#!monit
set logfile /var/log/monit.log
check process nodejs with pidfile "/var/run/yourapp.pid"
start program = "/sbin/start yourapp"
stop program = "/sbin/stop yourapp"
if failed port 3000 protocol HTTP
request /
with timeout 10 seconds
then restart
需要替换 /var/run/yourapp.pid 为你刚才设置的 pid 文件,yourapp 替换成你刚才在 upstart 设置的。port 3000 设置成你的 nodejs 应用实际监听的端口号,timeout 时间,可以根据你的实际情况调整。这几行配置文件很容易看懂,就不解释了。
然后确保 /etc/monit/monitrc 文件存在 include /etc/monit/conf.d/* 这行, 同时,你也可以在这个文件里修改监控频率,根据你的实际情况调整。然后重启 monit: service restart monit让刚才的配置文件生效。
upstart 和 monit 的配置我是参考 http://howtonode.org/deploying-node-upstart-monit 这篇博客的,你也可以来这里看看。
6,配置 apache 。我们需要在 80 端口来访问 nodejs 应用,现在又存在 apache ,那最简单的方式就使用反向代理来解决这个问题了。首先,需要安装 mod_proxy 和 mod_proxy_http ,你可以现在 /etc/apache2/mods-enabled/ 这个目录查看是否已经安装了,如果没有安装,你可以通过 a2enmod 命令来快速安装。 然后,新建一个 virtualhost ,按照你的习惯,新建一个文件 include 进去,或者一起写在某个文件。virtualhost 配置如下:
<VirtualHost *:80>
ServerAdmin webmaster[@localhost](/user/localhost)
ServerName yourhost.com
ProxyRequests off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
<Location />
ProxyPass http://localhost:3000/
ProxyPassReverse http://localhost:3000/
</Location>
</VirtualHost>
如果你的应用不是跑在 3000 端口上,记得修改配置, ServeName 记得改成你的域名,然后把你的域名解析到你 VPS 的 IP 上来。 ProxyPass 的作用是用 yourhost.com 来代理 localhost:3000 。ProxyPassReverse 的作用是处理 30X 跳转的情况,会把 Location:中的 http://localhost:3000 替换成 http://yourhost.com 。现在开启你的应用,重启 apache , 访问试试看。
如果都配置对了,到这里就应该能正常访问了。
使用反向代理时,要特别注意一些事情。页面中 a 标签的 url 尽量使用省略域名的形式,例如 href="/login",因为如果你处理不好,写上了 http://localhost:3000 这个后端的域名加端口,点击后就打不开了。对于 30* 跳转,Location 的值如果是 “//localhost:3000/login” 这种省略了协议的形式,apache 的反向代理也是无能为力的,我在 express 某个非正式版发现了这个问题。建议 Location 也使用省略域名的形式,这样虽然不符合 HTTP 1.1 的规范,但是各个浏览器都能兼容,可以省去这样的麻烦。
等等,如果你和我一样,也使用了 socket.io 来提供 websocket 服务,你会发现,现在长链接虽然可以用,但是却不是走 websocekt 模式了,这是因为 websocket 是无法通过 mod_proxy 来反向代理的,那就要另外想办法了。我使用的办法是:直接让 websocket 的请求去连接应用监听的后端真实端口。
7, 如果你使用了 websocket ,可以在页面中埋点,把带有你应用真实监听端口的 URI 埋入页面,然后前端 js 获取此 URI ,再来 io.connect( server ) 就可以让 websocket 正常运行了。需要注意的是,这个带端口的 URI 的域名需要和你访问页面的域名保持一致,否则取不到 cookie ,就没法获取 session 了。
如果你有更好的方式,请在下面留言交流,或者新浪微博 @allenm](http://weibo.com/allenm56) 来交流
拜读了~
node有模块叫forever。可以代替楼主的upstart 和monit 那部分操作,并能给你提供日志支持。。
over~
THX , 我去看看,不过 upstart 也有日志的。
@allenm upstart记录node的报错日志么。。还有console信息。。如果可以的话,还挺好的。不过forever安装和使用比你这个教程方便太多。。install完了,直接用啥也不用配了。
@xiaojue 可以的,会记录所有打印出来的东西的。 我是刚开始玩 nodejs 的,所以对一些模块还不熟悉,下次我试试 forever
@allenm 嗯啊。
非常的好
nginx反向代理路过。
同上,使用nginx反向代理
使用 apache 是有历史原因的。这个 VPS 是和几个同事一起合租的,大家都是放放 wordpress 。最开始用过 nginx ,但是在 nginx 下,url rewirete 规则需要自己去写,没有 .htaccess 来用, wordpress 是支持自动设置 .htaccess 的。大家又懒得去折腾这些,就改用 apache 了,和 wordpress 配合的比较好。