从零搭建 ubuntu 服务器及 nodejs项目部署
大概好几年前写的了,现在搬到博客上,防止丢失😄
**目标:**nodejs服务器配置及本地项目部署到线上的每一个业务流程,把本地可以跑通的nodejs项目,无论是微信小程序的后台,微信公众号的后台,还是手机app的后台,或是网站后台,都可以部署到线上,如果依赖的有数据库(mongodb),以及支持https服务,都可以做到!
**服务器版本:**阿里云ECS ubuntu 14.04 64位,1核1G大概能提供2-3w的请求量
服务器地址: 60.205.249.111
服务器用户: captainjack
一、流程
- 域名: 购买域名 -> 域名备案
- 购买服务器 -> 阿里云ECS -> 服务器版本选择ubuntu 14.04 64位
- 配置新用户,禁用root及默认端口
- 配置ssh本地到服务器之间链接(为了不泄露个人信息,或者伤害到服务器数据)
- 服务器安装IPtables和Fail2Ban等安全防护
- 安装NVM,配置nodejs
- 安装配置Mysql
- 配置Nginx前置服务
- git私有仓库,服务器到git中部署资源
- PM2进行远程部署,去git仓库自动获取资源
- 配置多个项目:分配不同的服务器端口,让NGINX当服务器的总管,只有它一个人持有80端口,所有的访问,都得先过NGINX这一关,进行相应的端口转发
- 域名解析
tip: 配置服务器的时候,多开几个终端,防止配错了无法更改
二、配置远程登录服务器
2-1 配置root及应用权限账号
初始服务器登录
# 默认用户名root,默认端口22
ssh [email protected]
目标:**
- 添加用户captainjack,使新用户拥有root权限,将新用户添加到root组中, root的权限过高,可能不小心执行了危险操作,所以有必要用相对低权限的用户来操作没那么敏感的内容
- 禁用root账号
- 禁用服务器默认22端口,改为自定义端口号
添加用户,将用户加到root组中
# 根目录下添加用户
adduser captainjack
添加captainjack用户到sudo组中
gpasswd -a captainjack sudo
使用户captainjack 拥有root权限
sudo visudo
在User privilege specification下面,添加如下权限,总之一句话,只要提供密码,captainjack可以通过sudo来运行任何root用户可以运行的命令
# 第一个ALL是这条规则对所有sudo生效
# 第二个ALL是captainjack可以以任何的用户来执行命令
# 第三个ALL是captainjack可以以任何的组来执行命令
# 第四个ALL是这个规则适用于所有命令
captainjack ALL=(ALL:ALL) ALL
tips: 上述如果失败,可执行 service ssh restart 可重启ssh服务
2-2 配置mac本地ssh
由于已安装 zsh 工具
# 打开.zshrc配置文件
subl .zshrc
.zshrc 文件中配置软连接,这样就不需要记录主机ip了
# .zshrc文件中添加一样
alias shell-root="ssh [email protected]"
重载zsh
source .zshrc
这样配置之后,如果有多台服务器,还需要重新输入密码,多个密码容易忘记,所以有必要通过私钥认证的方式配置无密码登录
2-3 配置本地无密码ssh登录
原理
将mac本地的公钥,上传到服务器的authorized_keys授权文件中,通过密钥算法比对来判断是否是合法的用户登录。
mac本地过程
本地开启ssh代理
eval "$(ssh-agent -s)"
mac本地生成密钥对
cd ~/.ssh
# 可自定义rsa名称,方便识别用途
ssh-keygen -t rsa -b 4096 -C "[email protected]"
将新密钥加入代理中
ssh-add ~/.ssh/id_rsa
服务端ssh配置过程
- 登录指定captainjack用户
- 服务端生成ssh密钥过程同上
- 如果遇到
sudo: unable to resolve host iZ2ze1m6ij6zkr3f0fljmaZ
,则是服务器主机名iZ2ze1m6ij6zkr3f0fljmaZ未解析到127.0.0.1,详见 https://coding.imooc.com/learn/questiondetail/17395.html 评论答案
重启ssh服务
sudo service ssh restart
授权authorized_keys文件
chmod 600 authorized_keys
在.ssh目录,编辑/新建authorized_keys授权文件
# .ssh目录下, 将mac本地生成的rsa公钥(.pub)内容,粘贴到authorized_keys文件中
vim authorized_keys
binggo! 直接在本地shell,输入jump-jack即可实现ssh无密码登录服务器
三、增强服务器安全等级
3-1 修改服务器默认登录端口22
默认的服务器的22登录端口有安全隐患,修改可以缩小被扫描和猜测的概率
# 1- 修改sshd_config文件
sudo vi /etc/ssh/sshd_config
# 2- 将22端口修改为自定义端口
Port 3001
# 3- 在sshd_config文件的最后,增加一行
AllowUsers captainjack
# 4- 重启ssh服务
sudo service ssh restart
注意,最后,在阿里云安全组里面,将新改动的端口3001,加入安全组里面 实例 -> 安全组规则 -> 手动添加
修改软连接端口号,通过ssh -p 56666 [email protected]
或者软连接shell-jack
即可无密码登录服务器
3-2 关闭服务器的root密码登录
关闭原因:所有人都知道最高权限的登录名都是root,别人可以扫描root下的所有端口,会有安全风险
进入ssh配置文件,找到最下面的 PermitRootLogin,改为no,这样就禁止掉了root的用户登录
为什么可以关闭root用户登录呢?因为我们现在的captainjack在root组里面,root可以做的事情,通过captainjack都可以可以做
# /etc/ssh/sshd_config文件中
PermitRootLogin no
PasswordAuthentication从yes改为no,是否允许用户密码登录/授权,因为我们已经配置好了无密码登录,通过公钥/私钥比对就可以直接登录了,就不需要密码登陆了
# /etc/ssh/sshd_config文件中
PasswordAuthentication no
重启ssh服务
sudo service ssh restart
验证:
- 去验证root的22端口(默认就是22,所以不用加端口号访问)登录,
ssh [email protected]
, 会得到如下信息:ssh: connect to host 60.205.249.111 port 22: Connection refused
ssh -p 30001 [email protected]
去验证root用户登录, 会得到如下信息:Permission denied (publickey).
binggo!既禁用了22端口登录,又禁用了root用户登录!root就不可用啦
3-3 配置iptables和Fail2Ban增强安全防护
iptables: 是一款允许灵活配置安全策略的防火墙的框架(防火墙):IP 信息包过滤和防火墙配置,通过设置一些出入的规则,筛选掉一些恶意或可以的访问或流量,留出安全的通道对外提供web服务
Tips: 由于阿里云增加了安全组的配置,所以可以不添加iptables规则了
-> sudo apt-get update && sudo apt-get upgrade 升级ubuntu服务
-> sudo iptables -F 清空所有iptables规则(其实默认没有,这里保险起见)
-> sudo vi /etc/iptables.up.rules 配置iptables规则
*filter
# 允许所有建立起来的连接
# allow all connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许所有出去的规则
# all http https
-A OUTPUT -j ACCEPT
# 允许https请求协议下的连接
-A INPUT -p tcp --dport 443 -j ACCEPT
# 所有的网站访问服务器,理论上都是从80端口进入,让80端口流量进出
-A INPUT -p tcp --dport 80 -j ACCEPT
# 为ssh登录方式建立通道(注意:56666为修改后的端口值)
# allow ssh port login
-A INPUT -p tcp -m state --state NEW --dport 56666 -j ACCEPT
# 允许外网从某台服务器ping到这台服务器,方便测试服务器是否出故障
# allow ping
-A INPUT -p icmp --icmp-type 8 -j ACCEPT
# 记录下来被拒绝的请求
# log denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied:" --log-level 7
# 对于恶意的敏感的 访问ip的规则进行拦截,如果某个IP对服务器的80端口在60s之内发送了超过150次请求,就认为是一个敏感的访问,予以拦截
# drop incoming sensitive connections
-A INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent --set
-A INPUT -p tcp --dport 80 -i eth0 -m state --state NEW -m recent --update --seconds 60 --hitcount 150 -j DROP
# 拒绝所有其他的进到服务器的流量
# 如果有多台服务器,都是在内网,在这里需要开放数据库或其他的第三方端口的访问,那这里需要另行添加外网或某个iP端访问
# reject all other inbound
-A INPUT -j REJECT
-A FORWARD -j REJECT
COMMIT
-> 写好iptables.up.rules规则后,要告诉iptables 配置文件在哪里
sudo iptables-restore < /etc/iptables.up.rules
-> 查看防火墙是否被成功启动
sudo ufw status
返回的是inactive(未激活的),来激活它
-> sudo ufw enable 激活防火墙,返回信息如下:
Firewall is active and enabled on system startup
-> sudo ufw status 返回状态为active,即激活了
-> 设置防火墙开机启动(可能会有停机之类的操作)
sudo vi /etc/network/if-up.d/iptables
-> 在文件中写入:
#!/bin/sh
iptables-restore /etc/iptables.up.rules
-> 给予脚本执行的权限
sudo chmod +x /etc/network/if-up.d/iptables
即配置好了iptables,接着配置Fail2Ban安防模块
Fail2Ban:
通过监控系统的日志文件,根据检测到的任何的可疑行为,触发不同的行为动作
-> 安装:
sudo apt-get install fail2ban
-> 打开配置文件
sudo vi /etc/fail2ban/jail.conf
57行的邮箱,改成自己的邮箱
102行: action_改成action_mw
-> 查看fail2ban运行情况
sudo service fail2ban status
(如果要停止fail2ban: sudo service fail2ban stop)
(如果要开始fail2ban: sudo service fail2ban start)
综上,都是一些简单的基础配置,但是比”裸“的服务器安全的多了
四、搭建Nodejs生产环境
4-1 搭建服务器的nodejs环境
-> 开始之前,最好先更新一下服务:
sudo apt-get update
-> 安装一下相关的模块(以备后面使用)
sudo apt-get install vim openssl build-essential libssl-dev wget curl git
-> 使用nvm工具升级管理nodejs不同的版本,安装nvm
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
-> 通过nvm安装nodejs,nodejs版本为6.9.5
nvm install v6.9.5
-> 默认让系统里面的node版本是6.9.5
nvm alias default v6.9.5
-> 由于我们的服务器是在国内,由于众所周知的原因,国内连npm有时候会很慢或者下载不下来,指定使用国内的淘宝镜像来下载npm包
npm --registry=https://registry.npm.taobao.org install -g npm
-> 增加系统文件监控数目
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
-> 采用cnpm 替代 npm
npm --registry=https://registry.npm.taobao.org install -g cnpm
node 安装完毕
安装一下常用的工具包 pm2 webpack gulp grunt-cli
cnpm i pm2 webpack gulp grunt-cli -g
-> 新建一个app.js,测试用
vi app.js
文件内容如下:
const http = require('http')
http.createServer(function(req, res){
res.writeHead(200, {'Content-type':'text/plain'})
res.end('somewhere called laputa')
}).listen(8085)
console.log('server running on http://60.205.249.111/')
注意,直接用node app.js可以启动,但是防火墙中,没有添加8085端口,需添加。。。
注意,直接用node app.js虽然可以启动,但是一旦关闭命令行,或者超时,服务就会关闭
-> 防火墙添加端口
进入文件临时增加一个8085端口
sudo vi /etc/iptables.up.rules
增加端口之后,重新应用一下规则
sudo iptables-restore < /etc/iptables.up.rules
现在就可以通过ip+端口号访问:即
http://60.205.249.111:8085/
由于我之前已经做过域名解析,下面的地址也可访问
http://www.laputa.pub:8085/
等通过Nginx配置之后,可以让服务跑在80端口,就可以不用加端口号访问了,只用ip或者域名就可以访问,毕竟加端口号访问,不安全也不友好
4-2 借助pm2让nodejs服务常驻
静态站点开起来,有两个条件必须有:
1、持续,稳定的对外提供web服务
2、可以从外网通过80端口访问到
上述通过命令行启动node服务,无法常驻,显然不行
可以通过pm2实现服务后台常开,并且出现异常之后自动重启
pm2: nodejs进程部署和管理工具
-> pm2 app.js 开启服务,开启之后即会常驻,oyeah!
-> pm2 list 可以列出当前服务器上运行的node服务列表
-> pm2 show app 可以显示详细的node服务信息
-> pm2 logs 可查看日志信息
五、Nignx实现反向代理Nodejs端口
目的: 让外网可以通过80端口访问到这台服务器,而不需要加上8085端口号
用root级别的权限,启动对80端口的监听,把来自80端口的流量分配给node服务的8085端口,实现服务的代理
安装nginx:
刚购买的服务器可能预装的有apache服务,一般也用不到,先予以删除
-> 停止apach服务,可能会没有
sudo service apach2 stop
update-rc.d -f apache2 remove
-> 移除apache
sudo apt-get remove apache2
这样apache就被清干净了
-> 更新一下包列表
sudo apt-get update
-> 安装nginx
sudo apt-get install nginx
-> 进入nginx目录下
cd /etc/nginx
cd conf.d
-> 在conf.d文件夹下新建一个文件
sudo vi laputa-pub-8085.conf
文件命名tip:域名-端口号,注意,这样有多个服务时命名方便识别
文件内容如下:
upstream laputa {
server 127.0.0.1:8085;
}
server {
listen 80;
server_name 60.205.249.111;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_pass http://laputa;
proxy_redirect off;
}
}
-> 配置nginx.conf(此时无需配置,可忽略)
sudo vi nginx.conf
-> 检查nginx配置的是否正确
sudo nginx -t
出现test is successful返回值时,即说明配置正确
-> 重启nginx
sudo nginx -s reload
重新访问IP地址http://60.205.249.111,无需加端口,即可实现服务器80端口代理访问
-> 考虑到安全,最好不要在nginx头信息中有版本信息
第21行 取消server_tokens的注释
重启nginx即可
六、服务器安装配置MongoDB
9.1 在Ubuntu 14.04上安装MongoDB
公司商用的话,可以考虑阿里云的数据库服务,个人的话,自己配置即可
目前数据库和应用跑在同一台服务器里面,正规的应该分开
-> 如下地址里面有安装方法:
https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
-> 导入public key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
-> 为mongodb的配置文件创建列表
echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
-> 更新本地包
sudo apt-get update
-> Install the MongoDB packages安装MongoDB packages
sudo apt-get install -y mongodb-org
由于mongodb原始配置文件的下载源比较慢,所以更改配置文件,替换成阿里云的地址
-> sudo vi /etc/apt/sources.list.d/mongodb-org-3.4.list
http://repo.mongodb.org/apt/ubuntu改成如下阿里云
http://mirrors.aliyun.com/mongodb/apt/ubuntu
-> 重新更新本地包
sudo apt-get update
-> 重新安装MongoDB packages
sudo apt-get install -y mongodb-org
-> 开启数据库
sudo service mongod start
会有如下返回信息
start: Job is already running: mongod
-> 检查有没有开启成功
mongo
返回:connect failed
原因:防火墙没有开启对数据库的27017的端口
-> 防火墙配置
sudo vi /etc/iptables.up.rules
在配置文件中添加:
# mongodb connect
-A INPUT -s 127.0.0.1 -p tcp --destination-port 27017 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -s 127.0.0.1 -p tcp --source-port 27017 -m state --state ESTABLISHED -j ACCEPT
配置完之后,重置防火墙
sudo iptables-restore < /etc/iptables.up.rules
成功!
-> 连接数据库,
mongo
可以进到mongodb的命令行之中
如果要停止mongodb服务:
sudo service mongod stop
如果要开启mongodb服务:
sudo service mongod start
如果要重启mongodb服务:
sudo service mongod restart
mongodb数据库的默认端口是27017,出于安全考虑,更改一下默认端口:
-> 进入配置文件:
sudo vi /etc/mongod.conf
port修改为58888
注意:更改完端口之后,
要把端口加到防火墙里面,
sudo vi /etc/iptables.up.rules
再重启防火墙服务
sudo iptables-restore < /etc/iptables.up.rules
-> 重启数据库
sudo service mongod restart
-> 由于更改了端口,访问数据库的时候,要加上端口访问
mongo --port 58888
七、向服务器正式部署和发布上线Nodejs项目
10.1 上传项目代码到线上私有git仓库-码云 git.oschina.net
把服务器的id_rsa.pub添加到git私有仓库-码云
10.2 PM2 一键配置线上项目结构
普通项目的话,pm2就够用了,pm2可以:
- 守护服务器的nodejs服务
- 也可以实现平滑重启
- 代码的自动更新
- 从本地到线上的自动部署
-> 项目中建一个ecosystem.json的pm2的配置文件
{
"apps": [
{
// 部署的应用的名字
"name": "TestDeploy",
// 启动的脚本 - 即项目的入口文件
"script": "app.js",
// 启动的时候,要传进去的变量
"env": {
"COMMON_VARIABLE": "true"
},
// 生产环境的变量
"env_production": {
"NODE_ENV": "production" // 设置值为生产环境
}
}
],
// 配置部署任务
"deploy": {
// 任务的名字,设置为production,可随意设置
"production": {
// 设置user为服务器上用来发布应用的用户
"user": "captainjack",
// 主机
"host": ["60.205.249.111"],
// 端口
"port": "56666",
"ref": "origin/master",
// git仓库地址
"repo": "https://git.oschina.net/laputagit/test-deploy.git",
// 把服务器部署到服务器的哪个目录
"path": "/www/TestDeploy/production",
// 取消ssh校验
"ssh_options": "StrictHostKeyChecking=no",
"env": {
"NODE_ENV": "production"
}
}
}
}
-> 提交代码至git
-> 在服务器上
sudo mkdir /www
在/www中,新建TestDeploy文件夹
sudo mkdir TestDeploy
这样就生成了/www/TestDeploy文件夹,和上面的ecosystem.json文件中的path路径必须一致
-> 让pm2连上服务器
pm2 deploy ecosystem.json production setup
会出现如下报错信息(权限问题):
mkdir: cannot create directory ‘/www/TestDeploy/production’: Permission denied
有可能会出现的报错:
fatal: could not read Username for 'https://git.oschina.net': No such device or address
解决方法:更改ecosystem.json的repo为如下格式即可:
http://yourname:[email protected]/name/project.git
上述报错出现的原因:
captainjack在根目录下,没有新建文件的权限
-> 切到服务器环境/www下,修改文件夹的权限
sudo chmod 777 TestDeploy
把这个文件夹,改成对captainjack来说,有可读可写可执行的权限
-> 重新执行上述操作,让pm2连上服务器
pm2 deploy ecosystem.json production setup
返回信息:success
服务器的TestDeploy文件夹下,会生成production文件夹,production文件夹下有
current 当前服务所运行的文件夹
shared 克隆下来的源代码
source 日志之类的共享的数据
文件夹
到此,项目即在服务器处于ready的状态,下一步部署项目
10.3 从本地发布上线和更新服务器的Nodejs项目(目前是静态站点)
本地控制远端代码更新和重启
-> 本地(注意:是本地目录)项目目录下运行:
pm2 deploy ecosystem.json production
报错如下:
--> Deploying to production environment
--> on host 60.205.249.111
commit or stash your changes before deploying
Deploy failed
executing post-deploy `pm2 startOrRestart ecosystem.json --env production`
原因是:pm2在服务器上使用的是非交互的ssh链接方式,所以需要到服务器的环境,编辑根目录下的.bashrc
-> vi .bashrc
把文件的第6-9行注释掉,保存
-> 重新加载.bashrc
source .bashrc
这样项目即完成了线上部署
不过还有两件事要做
1、需修改域名指向,目前还是不能通过域名的80访问
-> 修改域名指向
sudo vi /etc/nginx/conf.d/laputa-pub-8085.conf
把server_name改为www.laputa.pub
-> 重启nginx服务
sudo nginx -s reload
ok,现在试一下,可以域名直接访问了
本地代码改动,同步到线上
1、修改本地代码
2、上传代码至git
3、本地部署发布
pm2 deploy ecosystem.json production
4、自动化推送代码到git仓库并发布到生产环境
sh deploy.sh [commit文本]
Q&A
- 如果遇到 sudo: unable to resolve host iZ2ze1m6ij6zkr3f0fljmaZ ,则是服务器主机名iZ2ze1m6ij6zkr3f0fljmaZ未解析到127.0.0.1,详见 https://coding.imooc.com/learn/questiondetail/17395.html 评论答案