资讯专栏INFORMATION COLUMN

前端全栈之路--搭建生产环境的linux+nodejs+express的web服务器

wthee / 3388人阅读

摘要:并以一个实际线上生产的服务器例子为记录蓝本。开发环境还好,但这显然不是一个正式生产环境所能容忍的。这才是一个生产环境应

前言小序

以前我是个纯前端,就是很纯的那种。切切图,写写html、css布局;到后来写js,封装插件、组件;再后来公司没人力了,又要写后台,当时听说"PHP是世界上最好的语言...“,还学了php,会写一些php后台和myslq。后来还是因为公司没人了,又当起了运维,当时给某项目某司搭了个windows server+Apache+php+mysql的服务器。(因为当时他们要求在他们的windows server 2012内部服务器上搞项目)。

但作为一个前端,或者以前端自居的人来说内心还是觉得“javascript才是世界上最好的语言...^^”,于是后来转到javascript和nodejs的一条龙体系。之前自己的服务器有用过百度云的应用引擎bae,简单来说就是,百度搭建好的Linux和nodejs环境,直接git或者svn版本bae的仓库就可以进行项目开发,不需要理会服务器的搭建和管理。可以直接专注开始做业务层的开发,但是bae应用引擎的一些限制,比如临时生成的文件再新版本发布后会被清除等。不能满足需要了,后面开始自己从头搭建服务器。从写前端,写些后台,还搞起了服务器,当运维......虽然主要工作是前端开发,但勉强也算被逼成了个全栈的概念了吧,写这篇文章主要是对之前从事开发路上的总结和记录吧。并以一个实际线上生产的服务器例子为记录蓝本。本篇主要总结的是从什么都没有,到一个可进行基于nodejs环境进行业务层开发的过程,不涉及业务逻辑层如项目结构那些的东西。

好了,废话不多说了,进入正题。

系统架构层面的东西

从零开始,从无到有

系统环境:阿里云 Linux centOS 6.8 64位

node环境:node 6.11.1

express:4.x

版本控制:git 1.7.1

不同于开发环境node配置,生产环境还有许多不同于开发环境的nodejs环境的问题,不仅仅是安装node后,命令行执行node app.js就可以开启一个node的web服务器。下面将从最开始什么都没有说起,搭一个node生产服务器,可能会遇到的问题。

以下若有涉及本地开发环境的描述,环境为:mac os 10.12.3

1. Linux服务器

一般用于生产环境的现在大部分都使用Linux作为服务器的系统环境,虽然window server也可以,但处理node环境,优选linux系统。
Linux有很多版本,CentOS、Ubuntu、Debian等,具体选什么版本,自行百度啦,不多说了。百度云,阿里云,腾讯云这些云服务商也不多说了,具体用哪家的产品,自行看着选吧(土豪或者土豪公司自己买服务器机房的请忽略这句话)。
这里给个参考文章,CentOS、Ubuntu、Debian三个linux比较异同

本篇实例使用:阿里云ECS Linux centOS 6.8 64位

2. Linux用户创建

服务器有了后,就要开始进行什么装软件,调配置的环境搭建了。可以直接在控制台服务器实例,点远程连接,打开一个web版的linux shell的窗口,与linux服务器进行交互。如果需要登录linux管理的人少或者就一个,可以直接用root用户进行操作。但出于规范和安全的考虑,这里我们创建一个用户,用于linux管理。并赋予可执行sudo的管理员权限。

创建eric用户(这里自行使用名称,eric是事例名称),默认使用系统根目录/home下,生成eric目录,/home/eric即为eric的用户根目录

# useradd eric

然后赋予用户sudo权限,打开/etc/sudoers文件,找到
root ALL=(ALL) ALL
可以添加一行
eric ALL=(ALL) ALL或者不需要密码执行 eric ALL=(ALL) NOPASSWD: ALL
这样用户即可拥有sudo权限。

但是建议将创建的用户,添加到wheel用户组,而不是直接添加用户权限(wheel用户组是linux默认的具有超级管理员权限的分组)
并将# %wheel ALL=(ALL) NOPASSWD: ALL前面#号去掉,同样使用户拥有sudo权限,这样会更好一些。

3. ssh远程登录的设置

虽然可以通过web版的linux shell的窗口,也可以通过本地命令行终端通过ssh密码登录,但为了规范与安全和方便管理,我们使用本地命令行终端,通过ssh密钥对进行远程与linux交互。一般云服务器默认安装好了ssh,就不需要另行安装了,直接用就行了。

先在web版的linux shell登录linux服务器,切换到eric用户,执行

创建.ssh目录

mkdir .ssh

/home/eric/.ssh目录下生成密钥对文件,默认文件名为私钥:id_rsa,公钥:id_rsa.pub

ssh-keygen -t rsa

创建authorized_keys文件,存储用户公钥

touch authorized_keys

将用户公钥拷贝到authorized_keys文件,若有多个,每一个另起一行。

cp id_rsa.pub >> authorized_keys

注:关于密钥对当然也可以直接在云服务控制台UI界面根据云服务指引生成。

然后私钥下发给用户,id_rsa放在用户本机的用户目录的~/.ssh/id_rsa

这里有一个问题,就是如果本机要用ssh登录多个ssh连接,比如既要登github,又要登linux服务器,一个id_rsa文件会出问题,因为都默认使用这个名称的文件为私钥文件名。这样的话需要在.ssh目录下,创建名称为config的文件,重命名私钥文件为:比如阿里linux eric用户的id_rsa重命名为id_rsa_aliyun_eric,github下的bbb用户的私钥id_rsa
重命名为:id_rsa_github_bbb
config文件可以配置如下

# 阿里云eric用户ssh配置
Host 119.23.xx.xxx
HostName 119.23.xx.xxx
User eric
IdentityFile ~/.ssh/id_rsa_aliyun_eric

# github的ssh配置
Host github
HostName github.com
User bbb
IdentityFile ~/.ssh/id_rsa_github_bbb

这样一台机可以实现多处ssh登录了。

4. nodejs的安装

安装nodejs有几种方案,

通过 yum install nodejs

通过linux进行Source code编译安装

通过下载编译好的nodejs源码压缩包免编译安装

通过yum install安装主要就是nodejs版本的问题,不能指定版本安装。
source code编译安装比较麻烦,这里直接下载编译好的源码包,解压安装特定版本的nodejs

wget --no-ckeck-certificate https://nodejs.org/dist/v6.11.1/node-v6.11.1-linux-x64.tar.xz

并解压安装到/opt目录下

tar -zxvf /home/eric/node-v6.11.1-linux-x64.tar.xz -C /opt/

然后通过软链接node和npm到/usr/local/bin,使node可以在任何环境目录下执行

ln -s /opt/node-v6.11.1-linux-x64/bin/node /usr/local/bin/node
ln -s /opt/node-v6.11.1-linux-x64/bin/npm /usr/local/bin/npm

最后执行node -v,npm -v有版本显示说明安装成功。

5. nodejs创建http服务器

nodejs环境安装好后,进行以下http服务的测试

 #进入用户目录
 cd ~
 #创建www目录作为web服务根目录
 mkdir www
 #进入www目录创建server.js作为nodejs的http服务器文件
 touch server.js

作为测试用nodejs官网的事例代码测试下先

const http = require("http");

const hostname = "127.0.0.1";
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello World
");
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

执行node server.js后http服务运行成功,说明nodejs环境已可以顺利搭建nodejs的http服务器

6. 使用git作为版本控制系统

一般linux云服务器都默认装有git,没有的自行安装。
如果你想使用github做为git仓库,大可以使用github,通过github设置将提交的代码发布到linux服务器的www目录。(当然私有仓库要收费)
我们这里以自建git仓库为例,说下git仓库的创建和版本发布的问题

 #进入用户根目录
 cd ~
 mkdir gitrepo
 #创建git仓库
 cd gitrepo
 mkdir test.git
 cd test.git
 #执行git初始化指令建立空仓库
 git init --bare

此时在test.git下会有一个.git的文件夹,说明名为test的git仓库建立完成。(当然还可以创建一个linux用户git来专门处理git相关的管理,再此我仅以一个eric单一linux用户来管理,这也是考虑到以后没有那么多管理员实际管理的情况,避免linux用户切换的繁琐)

最后进入www目录进行仓库克隆

cd /home/eric/www
git clone /home/eric/gitrepo/test.git

这样作为发布目录的www目录正式有了版本控制

7. git本地提交push远程后,通过hooks钩子的post-receive自动部署到www

当我们本地代码上传提交后,不能每次都进入www目录进行人工pull操作,这样实在不是一个好方式。用什么方式,当我们本地提交后,git会自动帮我们部署到www目标目录。就是hooks,也叫钩子。即git远程仓库接受到提交的指令后自动执行的脚本文件。

hooks目录下创建名称为post-receive的文件(这个文件可以理解为git仓库在收到本地push到本远程仓库之后要执行的脚本)
脚本事例如下

#!/bin/sh

unset GIT_DIR
DeployPath=/home/eric/www

echo "==============================================="
cd $DeployPath
echo "Starting publish"

git pull origin master

time=`date`
echo "Publish success at time: $time."
echo "================================================"

其实脚本就是进入到www目录,然后执行pull操作,只不过是脚本执行,而不是人工。当然这个是最简易的版本,随后我们要讨论下,一些根nodejs特殊性的提交细节。

8. 使用yarn替代npm作为nodejs,express的包管理工具

在搭建express架构的nodejs服务器时,通常我们使用npm管理package.json的install安装和管理。
但经过实践考虑,后来经过自我研究决定使用yarn代替npm。主要考虑一下两方面原因。

npm的速度比较慢

npm的版本依赖不好控制

Yarn 是 Facebook, Google, Exponent 和 Tilde 开发的一款新的 JavaScript 包管理工具。
具体yarn和npm的异同和优劣请自行百度,这里就不再赘述了,直接说说使用问题。

yarn的安装:
参照nodejs的源码安装过程,参照官网事例通过软链yarn到/usr/local/bin
不通过yum install的原因是,yum源的版本比较旧,直接yum安装的都是比较旧的版本,想安装最新的版本或特定版本,可以参照官网进行源码安装或者编译安装。

执行yarn --version显示版本号,说明yarn安装成功。
基本的express目录结构如下

--bin
    www
--public
--routes
--views
  app.js
  package.json

在根据package.json执行yarn install后目录结构根目录多了一个yarn.lock的包依赖的锁定文件(这个是yarn特有的包管理的依赖管理文件)
install后,目录结构类似如下

--bin
    www
--public
--node_modules
--routes
--views
  app.js
  package.json
  yarn.lock

至此应该说,express框架下的node服务算是比较像样子了,基本可以使用了。

9. 使用pm2作为node http服务的进程管理器

我们都知道使用node server.js会在命令行直接开启nodejs的web服务,但这样命令行当前窗口会被阻塞。想输入其他指令还得退出或者另起窗口。而且如果文件修改,需要停止后重新载入执行一次才回生效。开发环境还好,但这显然不是一个正式生产环境所能容忍的。所以我选择了pm2作为node进程管理的工具(类似的还有forever,不过对比后没有pm2出色)。

首先安装pm2(一个小问题:由于我是源码安装npm install -g后模块全局的会安装在/opt/nodejs/bin下,同样要软链到/usr/local/bin,或者添加环境变量,才能全局使用)

npm install -g pm2
#使用pm2开启node进程
pm2 start server.js

使用也是很简单直接pm2 start文件即可。更多指令详情可以看看官方文档,写的非常清楚了。

pm2同样可以以配置文件启动,这里为了以后便于管理,我们使用启动配置文件的形式启动。pm2默认的配置文件启动的文件名为ecosystem.config.js

我简单配置的启动脚本如下,仅供参考:

module.exports = {
  apps : [
    {
      //general
      name      : "node-web",
      script    : "bin/www", //启动执行的初始脚本

      //advanced
      watch     : ["appsback","routes","ecosystem.config.js","server.js"],//监听文件变化
      ignore_watch: ["node_modules","apps","static"],//忽略监听的文件夹
      max_memory_restart: "800M",//内存达到多少会自动restart
      env: {
        COMMON_VARIABLE: "true"
      },
      env_production : {
        NODE_ENV: "production"
      },

      //log file
      log_date_format: "YYYY-MM-DD HH:mm:ss Z",//日志格式

      //control
      min_uptime: 3000,
      listen_timeout: 3000,
      kill_timeout: 5000,
      max_restarts: 5,
    }
  ]
};

pm2一个比较好的地方是,可以监听文件变化,即文件发生变化后node的http服务会重载而且是0秒重载。这才是一个生产环境应该有的操作...而不是停止后重新载入,对于线上环境基本是不允许的。

这样只要执行pm2 start ecosystem.config.js就可以开启一个node的http服务进程啦。

至此我们的express项目的目录结构大致变化成这样:

--bin
    www
--public
--node_modules
--routes
--views
  ecosystem.config.js
  server.js
  package.json
  yarn.lock
10. package.json变化导致的自动化部署的问题

我们知道如果本地package.json变化,需要执行npm install或者yarn install安装模块,但提交后服务器端怎么办呢?一个比较笨的方法就是,每次提交后若package.json变化就进入node的web根目录即www目录手动执行npm install或者yarn install。但这种方式显然不够智能,怎么办呢....
既然package.json变化必须npm install才能使node http重启后找到相应模块。于是我有以下两种形式的思考

不监听package.json文件变化。即每次提交不管package.json有没有变化,都删除node_modules文件夹,执行yarn install对package.json依赖重新安装

监听package.json变化,即只有package.json变化才去执行yarn install安装。

显然第一种更方便简单一些,不需要多余的监听,减少了服务器的一些配置。(现在想想这其实就是百度云bae的策略,他的官方文档介绍就是每次提交不管package.json有没有变化,都删除node_modules再装一遍)

但我想的是第二种显然更好更合理的,因为如果package.json没有变化那删除node_modules再装一遍显然是一步浪费资源的操作。剩下的问题就成了

如何监听package.json变化

变化后何时以及怎样去执行npm install或者update模块。

一开始想到已经在用的工具pm2有监听功能,但pm2监听只会产生restart的重启执行,不具有执行某一特定回调函数或脚本的功能(官方文档也说到了这一点)。所以只能想别的解决方案。
后来有想用使用linux系统自带的inotify监听文件变化,另外开启一个监听脚本时刻监听www目录下package.json的变化。但最后经过思考决定在git的hooks钩子post-receive脚本处理这个事情,这样可以省掉一个系统监听的脚本文件。

最后的解决方案就变成了:在post-receive脚本判断package.json文件有么有变化,并在git pull后执行install,然后重载pm2的node http服务进程,一气呵成。

最后post-receive脚本修改为以下形式供参考:

#!/bin/sh

unset GIT_DIR
DeployPath=/home/eric/www

echo "==============================================="
cd $DeployPath
echo "Starting publish"

last_modify_time=`stat package.json | grep "Modify"`
echo "t1:$last_modify_time"
git pull origin master

cur_modify_time=`stat package.json | grep "Modify"`
echo "t2:$cur_modify_time"

if [ "$last_modify_time" != "$cur_modify_time" ]; then
  echo "package.json changed"
  rm -rf node_modules
  yarn install
  pm2 restart ecosystem.config.js
else
  echo "package.json not changed"
fi

time=`date`
echo "Publish success at time: $time."
echo "================================================"

我这里是通过判断pull前后的package.json文件的最后修改时间是否有产生变化,来确定package.json本次push过来的是否有修改。
若变化了则执行删除node_modules再yarn install安装,最后pm2重启。若没有变化,则可以根本不执行以上的操作,省去了执行以上步骤带来的资源消耗。

当然如果再精细一点可以通过解析package.json里的依赖dependence的具体变化,是增加了模块还是删除了模块,还是修改了某一项依赖的版本作出install还是update还是delete的具体操作,而不用所用都重新删除再安装。不过这样对于json解析和npm或者yarn的增删改查操作过于繁琐,这个方式被我pass掉了,直接删了重装。由于yarn具有安装过的模块缓存,所以yarn isntall带来的时间上的问题基本可以忽略,速度非常快。这也是为什么我提倡用yarn替代npm的原因之一。

11. linux重启后的node web服务自启动

我们可以重启linux后,进入linux,进到/home/eric/www目录下,再次手动pm2 start ecosystem.config.js启动node的http服务。
虽然linux重启并不是一件很经常发生的事情,但这种手动需要做的事,当然可以交给linux的开机自启动脚本完成。

我们可以自己在,/etc/rc.d/rc.local目录下自建shell脚本。这里我们使用pm2给我们提供的功能,直接创建自启动脚本。
根据官网:

pm2 startup

直接根据提示信息,创建开机自启的startup script开机自启动脚本,非常方便。

12. 非root用户使用非80端口映射80端口的问题

默认情况下Linux的1024以下端口是只有root用户才有权限占用,我们的apache,nginx,nodejs等等程序如果想要用普通用户如eric来占用80端口的话就会抛出Permission denied:80的权限异常。

那我们只能使用8080这样的较大的端口号,但在url上要带着端口号这样可不好。这里我使用的是iptables进行的端口转发,即将8080这样的nodejs的http服务所用端口,转到80端口上,node服务虽然是8080端口,但用户url上不用带端口,直接用默认的80端口,也可以直接转发到8080的node服务器处理了。

使用root用户执行

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

完了不要忘了保存和重启服务service iptables saveservice iptables restart

另外还可以使用nginx作为反向代理,作端口转发。

================================================================================

至此,一个nodejs环境的http服务器,从无到有就建立了起来,有http服务git版本控制自动化部署pm2进程管理,并且可以在生产环境使用的nodejs express的web服务器。房子盖好了,至于具体的业务逻辑,就在目录下尽情的搞装修吧。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/33269.html

相关文章

  • 全栈最后一公里 - Node.js 项目线上务器部署与发布

    摘要:没有耐心阅读的同学,可以直接前往学习全栈最后一公里。我下面会罗列一些,我自己录制过的一些项目,或者其他的我觉得可以按照这个路线继续深入学习的项目资源。 showImg(https://segmentfault.com/img/bVMlke?w=833&h=410); 本文技术软文,阅读需谨慎,长约 7000 字,通读需 5 分钟 大家好,我是 Scott,本文通过提供给大家学习的方法,...

    singerye 评论0 收藏0
  • 全栈最后一公里 - Node.js 项目线上务器部署与发布

    摘要:没有耐心阅读的同学,可以直接前往学习全栈最后一公里。我下面会罗列一些,我自己录制过的一些项目,或者其他的我觉得可以按照这个路线继续深入学习的项目资源。 showImg(https://segmentfault.com/img/bVMlke?w=833&h=410); 本文技术软文,阅读需谨慎,长约 7000 字,通读需 5 分钟 大家好,我是 Scott,本文通过提供给大家学习的方法,...

    Nosee 评论0 收藏0
  • 网站部署

    摘要:就鹿晗宣布恋情导致微博宕机事件浅谈大型网站高可用性架构中午吃饭刷着刷着微博发现微博突然挂了。用户在使用浏览器访问一个网站时需要先通过协议向服务器发送请求,之后服务器返回文件与响应信息。 webpack:从入门到真实项目配置 自从出现模块化以后,大家可以将原本一坨代码分离到个个模块中,但是由此引发了一个问题。每个 JS 文件都需要从服务器去拿,由此会导致加载速度变慢。Webpack 最主...

    endless_road 评论0 收藏0
  • Node中间层实践(二)——搭建项目框架

    摘要:项目地址脚手架使用过,的同学都清楚,官方推荐的安装方式是通过专用的来快速搭建一个由编译打包的项目框架。用在层的模块化,在中间层实现了模块化。这样,从中间层到前端都实现了热加载。 版权声明:更多文章请访问我的个人站Keyon Y,转载请注明出处。 项目地址:https://github.com/KeyonY/NodeMiddle 脚手架? 使用过angular2,vue2的同学都清楚,官...

    DrizzleX 评论0 收藏0
  • 前端相关汇总

    摘要:简介前端发展迅速,开发者富有的创造力不断的给前端生态注入新生命,各种库框架工程化构建工具层出不穷,眼花缭乱,不盲目追求前沿技术,学习框架和库在满足自己开发需求的基础上,然后最好可以对源码进行调研,了解和深入实现原理,从中可以获得更多的收获随 showImg(https://segmentfault.com/img/remote/1460000016784101?w=936&h=397)...

    BenCHou 评论0 收藏0

发表评论

0条评论

wthee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<