web 和 node 项目部署阿里云服务器并域名访问教程
当你想把开发的前端项目和 Node 服务端项目部署至云服务器上,以便于别人能够公网访问,相信大多数开发者都要经历一个查来查去的过程,还容易踩坑。这篇文章会一步一步地教你如何做,可以让你少走些弯路。 在开始之前,我先介绍下我的项目技术栈:
- 前端:vite4 + vue3 + vue-router
- 服务端:koa2 + sequelize + mysql2
很常规,其实这篇教程和技术栈是没有任何关系的,毕竟我们要部署的前端项目只是打包后的静态资源文件而已,服务端进程管理器目前最好的选择也就 pm2 。
购买云服务器
现在的云服务器选择很多,比如腾讯云、阿里云等其它云,这里讲下阿里云服务器 ECS 的购买,后续的服务器配置等操作都是基于我们购买的云服务器进行的,所以如果你购买的不是阿里云的,可能本教程只能作为一个参考。
访问 阿里云服务器 ECS 主页 往下翻到产品规格,有许多种类型的服务器供我们选择,我是购买了共享型下的 2 核 4G 实例,即下图:
你看到的价格可能不一样,会因为活动(比如双 11、618)、新客专享和学生优惠,有更低的价格,总之看你用途是什么吧,个人的学习项目或博客啥的,买便宜点的就行了。 购买配置如下:
- 实例规格:2 核 4G;
- 地域:我选的是 华东 1(杭州);
- 操作系统:比较习惯 Linux CentOS 7.9 64 位,也可以尝试下 Alibaba Cloud Linux,官网介绍说他完全兼容 CentOS ,且长期维护;
- 带宽:学习的话建议 1M 就可以了,我购买的是 5M 的,这玩意儿真的贵。
连接服务器
有了服务器之后自然是要登录上去进行操作,下面介绍密码登录和本地远程面密登录。
密码登录服务器
我们先尝试下通过 Workbench 远程连接服务器,如下图操作:
在这里你可能会遇到无法使用密码登录的问题,首先你要保重重置了实例密码,然后通过 VNC 远程连接,创建 6 位的密码后,进入页面登录实例:
Login: root
Password: 输入你创建的实例密码,不是 VNC 密码
然后按照这个文档 使用密码无法登录 Linux 云服务器 ECS 该如何处理?操作就行了。
重启退出后我们再点远程连接,就可以正常通过密码登录了,然后就可以操作我们的服务器咯。
本机免密登录服务器
如果我们想在自己电脑上就快捷登录到云服务器系统上,可以打开终端,因为我是 Windows 系统,所以使用的是 PowerShell ,然后输入以下命令:
ssh root@111.11.1.1
上面 root
是登录用户名,后面的 111.11.1.1
是服务器实例的公网 IP,记得替换成你自己的公网 IP。回车之后会让你输入登录密码,即服务器实例密码。
但是我们每次打开终端都要执行一遍登录,还要输入密码,特别麻烦,而且不利于后面要讲到的利用 Github Actions 自动化部署的工作进行。
幸运的是可以让本机与云服务器建立信任,实现免密登录,接下来介绍实现建立信任步骤:
本机生成 ssh key
在本机的终端输入以下命令:
ssh-keygen -t rsa -C "你的 github 邮箱"
云服务器添加本机公钥
执行上面命令以后,要找到 id_rsa.pub
文件,我的电脑上路径是 C:\Users\10913\.ssh
,你可以做个参考,然后随便找个编辑器将其打开后,复制该文件内的所有内容,复制到云服务器上的 ~/.ssh/authorized_keys
文件中(如没有该文件,就创建一个)。
上述过程用到步骤命令如下:
# 登录云服务器
ssh root@47.97.100.103
# 来到 ~/.ssh 目录
cd ~/.ssh
# 查看下有没有 authorized_keys 文件
ls
# 没有的话创建一个
touch authorized_keys
# 编辑该文件
vi authorized_keys
# 粘贴后退出,先按 ESC,然后
:wq
然后你输入 exit
命令退出云服务器系统,再重新执行上述登录,就不用输入密码了。
如果还是需要你输入密码,估计是权限不够,我们使用密码再次登录后,依次执行以下命令:
cd .ssh
chmod 700 ../
chmod 700 .
chmod 600 authorized_keys
安装必要软件
云服务器的初始系统是比较干净的,有些必要的软件需要我们自己安装。比如,开发的服务端没有 Node 怎么那可不行。
在开始安装之前,要确认我们的云服务器能访问外网,简单的方法就是 ping
一下随便一个域名,比如:
ping www.baidu.com
如果是像下面一样,就代表网是通的。
安装 git
因为我购买云服务器时选择的系统是 CentOS,自带 yum ,所以我可以使用它来安装 git :
sudo yum install git
安装完成之后,查看 git 版本:
git --version
安装 nginx
继续使用 yum 安装:
sudo yum install nginx
安装完成之后,查看 nginx 版本:
nginx -v
安装 wget
wget 可以在控台访问一个 url 地址,并得到返回结果,用于下载软件,也可用于测试 web server 是否正常运行。
sudo yum install wget
安装 nvm
nvm 是一个 node 包版本管理工具,非常好用,使用 wget 来安装:
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
下载的版本最好保持最新,可在 nvm 官方文档 随时查看。
注意,你大概率会遇到无法下载的问题,也就是连接不到资源,建议多尝试下,或者联系阿里云技术支持。
如果你安装成功了之后,需要重新连接服务器,查看 nvm 版本:
nvm -v
安装 node
有了 nvm,可以很方便地安装 node 的不同版本,因为我本地开发项目时 node 版本是 16.19.0
,所以为了保持一致,我准备在云服务器上也安装一个相同的版本。
执行以下命令以查看 node 版本有哪些:
nvm ls-remote
然后选一个版本开始安装:
nvm install 16.19.0
安装成功之后确认下:
nvm list
查看当前使用的 node 版本:
node -v
安装 yarn
因为我使用的时 yarn 包管理器,需要安装下:
npm install --global yarn
查看当前使用的 yarn 版本:
yarn -v
安装 pm2
使用 npm 直接安装 pm2 :
npm install pm2 -g
查看当前使用的 pm2 版本:
pm2 -v
测试 web 和 node 服务
必须的环境准备好之后,我们需要确认公网是否能访问我们的服务器资源,所以需要先写个测试的 demo 。
新建静态页面
在根目录下,新建一个测试目录,进入该目录:
mkdir test-demo
cd test-demo
然后在该目录下新建一个 html ,并编辑:
touch test.html
vi test.html
编辑内容如下:
<h1>Hello World</h1>
找到 nginx 配置文件进行配置
执行以下命令查看 nginx.conf
的所在位置:
nginx -t
直接开始编辑:
vi /etc/nginx/nginx.conf
添加一个 server
:
server {
listen 8001;
server_name test-demo;
root /root/test-demo;
include /etc/nginx/default.d/*.conf;
}
:wq
保存退出之后,再执行下 nginx -t
看是否正常。如果正常,重启下 nginx :
nginx -s reload
然后使用 wget 测试下是否能正常访问该目录下的资源:
wget http://localhost:8001/test.html
结果报了 403 的错误,表示没有相关权限,我们去修改 nginx.conf
文件,把 user nginx
改成 user root
就可以了。
创建 node 服务
我们再试一下使用 pm2 启动一个 node 服务。首先来到 test-demo
下新建一个 server.js
:
cd /root/test-demo
touch server.js
编辑该 js 文件并输入以下内容:
const http = require("http");
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-type": "application/json" });
res.end(
JSON.stringify({
errno: 0,
msg: "Hello node server!",
})
);
});
server.listen(8002);
保存退出后启动 node 服务:
pm2 start server.js
使用 wget 测试服务是否启动成功:
wget http://localhost:8002
执行之后会默认下载一个 index.html
文件,我们查看下它的里面是什么内容:
cat index.html
如果输出了以下内容就代表我们的服务启动了:
{"errno":0,"msg":"Hello node server!"}
公网访问
云服务器是有一个公网 IP 的,这意味着我们可以在公网访问其服务器资源,但是需要配置防火墙。
我们可以先尝试访问下公网 IP 获取上面创建的 test.html
,在浏览器打开以下地址(记得替换你的公网 IP):
http://111.111.11.1:8001/test.html
或者上面创建的 node 服务:
http://111.111.11.1:8002
不出意外的话,是完全不可访问的。需要回到阿里云平台配置安全组开放我们的端口,如下图:
现在再访问应该就可以了。
Github Actions 自动部署
每次发布新的代码都要登录到服务器,手动部署最新的代码,这是重复且容易犯错的一个过程,如果我们能让机器自己执行这个过程,大大降低了风险和解放了劳动力。
Github Actions 是 Github 免费提供的一个持续集成服务,接下来介绍如何做。
新建 secrets
之前我们说过本机与远程的云服务器建立了信任得以免密登录,现在我们使用 Github Actions 提供的临时的虚拟机也就相当于我们的本机,也要建立信任,才能方便进行后续文件拷贝的工作。
还记得之前已经我们的公钥(即 id_rsa.pub
)添加到云服务器,如果我们把本机的私钥(即 id_rsa
)搬到虚拟机上,不就可以模拟本机免密登录了吗!
来到我们的 github 项目下,找到以下新建 secret
的地方,新建一个私钥的 secret
:
切记,私钥一定不能泄露,不然别人能随便就登进你的服务器了。
成功之后如下图,另外我还建了其它的 secret
,服务器的公网 IP 和项目目录地址。
编写 workflow
现在我先部署我的 type-room-web
前端项目。
在项目的根目录下新建 .github
目录,再新建 workflows
目录,最后在新建 deploy.yml
文件,编辑这个文件:
name: deploy type-room-web
on:
push:
branches:
- "main" # 针对 main 分支
paths:
- ".github/workflows/*"
- "src/**"
- "public/*"
- "package.json"
- "vite.config.ts"
- "index.html"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 拉取项目代码
uses: actions/checkout@v3
- name: 设置 node 环境
uses: actions/setup-node@v3
with:
node-version: "16.19.0"
- name: 安装依赖
run: yarn
- name: 编译打包
run: yarn build
- name: 设置 id_rsa
run: |
mkdir -p ~/.ssh/
echo "${{secrets.VORTESNAIL_ID_RSA}}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan ${{secrets.REMOTE_HOST}} >> ~/.ssh/known_hosts
cat ~/.ssh/known_hosts
- name: 将远程服务器的对应目录下所有文件及文件夹删除
run: | # type-room/web
ssh root@${{secrets.REMOTE_HOST}} "
cd /root/${{secrets.REMOTE_WEB_DIR}};
rm -rf ./*;
"
- name: 将编译后的包复制到远程服务器对应目录
run: scp -r ./dist root@${{secrets.REMOTE_HOST}}:/root/${{secrets.REMOTE_WEB_DIR}}
- name: 删除 id_rsa
run: rm -rf ~/.ssh/id_rsa
总结下上面文件做的事情:
- 监听到
main
分支的提交后,且src/**
等文件内容改变时,开始执行这次 ci 流程; - 拉取项目的代码到虚拟机中;
- 下载 node 16.19.0,因为后面的编译打包需要用到 node,尽量保持和本机开发时的版本一致;
- 安装依赖;
- 编译打包前端项目;
- 将
secrets
中的建立的私钥写入到虚拟机的.ssh/id_rsa
中,并赋予读的权限,再将云服务器的公网 IP 追加写入到.ssh/known_hosts
中; - 删除云服务器的对应目录下的所有文件及文件夹;
- 将编译好的
dist
目录复制到云服务器对应目录; - 清除私钥。
⚠️ 这里请务必注意,你要事先在云服务器上建好你要操作的目录,比如我的 /root/type-room/web
。
万事俱备,现在让我们提交一波前端的项目看看:
git add -A
git commit -m "ci: 测试 GitHub Actions 持续集成"
git push origin main
修改 nginx.conf
因为现在用到的是我们实际的项目,所以需要修改之前的 nginx 配置文件:
# gzip 配置
gzip on;
gzip_static on;
gzip_min_length 5k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 7;
gzip_types text/plain application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary on;
server {
listen 8088;
server_name type-room-web;
root /root/type-room/web/dist;
include /etc/nginx/default.d/*.conf;
# 单页应用 try file
location / {
try_files $uri $uri/ /index.html;
}
# api 重定向到我们自己的服务端地址
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://localhost:7077;
}
}
如果顺利的话,你会看到每一步都是 OK 的,如果遇到了问题,不要慌,根据错误提示去解决,放心,不难的。
购买数据库
我购买的是下面这个,新人专享还是蛮便宜的:
数据库连接
连接数据库
首先要来到我们购买的数据库控制台,点击实例进入后来到账号管理,创建一个主账号:
然后就可以用这个账号登录数据库了:
本机连接数据库
目前只能通过阿里云自研的 DMS 进行数据库管理,如果你想在自己常用的电脑上连接远程数据库,需要开放外网访问,且要给自己的本机 IP 加白名单。
等待一会儿后,点击外网地址旁边的设置白名单,添加一个白名单组,把你的本机出口 IP 地址添加上去:
⚠️ 也可以再新建一个安全组,专门放你的云服务器内网和外网地址,这样后续可以通过内网连接数据库。
这一步做完,就可以在本机连接我们的远程是数据库了,比如我使用 MySQL Workbench 来进行连接:
node 项目修改数据库连接地址
来到我们的 node 服务端项目,你必然是会有一个数据库连接地址的,将其修改为云数据库的内网地址:
// 开发配置
let MYSQL_CONF = {
host: "localhost",
port: "3306",
user: "root",
password: "xxx",
database: "type_room_db",
};
// 线上配置
if (isProd) {
MYSQL_CONF = {
host: "你的云数据库内网地址",
port: "3306",
user: "root",
password: "xxx",
database: "type_room_db",
};
}
部署 node 服务
在项目根目录下新建 ecosystem.config.js
文件,写入 pm2 所需的配置:
module.exports = {
apps: [
{
name: "type-room-server",
script: "./bin/www",
instances: "2",
watch: true,
ignore_watch: ["logs", "node_modules"],
error_file: "./logs/err.log",
out_file: "./logs/out.log",
log_date_format: "YYYY-MM-DD HH:mm:ss",
},
],
};
增加 package.json
中的生产环境启动服务命令 prod
:
"scripts": {
"dev": "cross-env NODE_ENV=dev ./node_modules/.bin/nodemon bin/www",
"prod": "cross-env NODE_ENV=production pm2 start ecosystem.config.js",
},
和前端项目一样,也要创建我们的 .github/workflows/deploy.yml
,写入以下内容:
name: deploy type-room-server
on:
push:
branches:
- "main" # 针对 main 分支
paths:
- ".github/workflows/*"
- "src/**"
- "bin/*"
- "package.json"
- "ecosystem.config.js"
- ".env"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 拉取项目代码
uses: actions/checkout@v3
with:
path: "clone-files"
- name: 设置 id_rsa
run: |
mkdir -p ~/.ssh/
echo "${{secrets.VORTESNAIL_ID_RSA}}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan ${{secrets.REMOTE_HOST}} >> ~/.ssh/known_hosts
cat ~/.ssh/known_hosts
- name: 将远程服务器的对应目录下所有文件及文件夹删除
run: | # type-room/server
ssh root@${{secrets.REMOTE_HOST}} "
cd /root/${{secrets.REMOTE_SERVER_DIR}};
pm2 kill;
rm -rf ./*;
"
- name: 将项目复制到远程服务器对应目录
run: |
rsync -avz --exclude=".git" --exclude="node_modules" clone-files/ root@${{secrets.REMOTE_HOST}}:/root/${{secrets.REMOTE_SERVER_DIR}}
ls -a
- name: 启动 pm2
run: |
ssh root@${{secrets.REMOTE_HOST}} "
cd /root/${{secrets.REMOTE_SERVER_DIR}};
ls -a;
yarn;
yarn prod;
"
- name: 删除 id_rsa
run: rm -rf ~/.ssh/id_rsa
和 web 项目不一样的是,复制时使用的是 scp
,现在我们用的是 rsync
,两者区别大家可以自行查查。
然后我们提交代码到 github 远程仓库,在 actions 里面看下我们的 ci 流程是否正常。
创建数据库
现在 node 服务是部署好了,我们可以登录云数据库,创建数据库,我的创建格式如下:
创建好数据库,我需要去创建和我开发环境保持一直的数据库表,因为我用的是 sequelize
,我在云服务器的 node 项目根目录下执行下写有同步逻辑的 js 文件就可以了。比如我的同步操作:
./node_modules/.bin/cross-env NODE_ENV=production node ./src/db/sync.js
同步成功之后,就可以开始读写数据库了,截止目前,你的 web 和 node 项目都已经完成了线上自动化部署、公网访问的所有流程了。
域名解析
现在访问我们的页面只能通过服务器的公网 IP 去访问,我们希望通过自己购买的域名去进行访问,该怎么做呢? 首先,找到域名解析,你会看到你购买的域名:
在列表的最右边有解析设置按钮,点击跳转之后,再点击新手引导,填入以下信息:
记住要把对应设置"@"主机记录和对应设置"www"主机记录都勾选上,前者可以让你不输入 www
时也能正常访问。
设置之后我们打开终端,测试下域名的连通性,我们 ping 一下域名:
ping www.typeroom.cn
# 或
ping typeroom.cn
如果能正确显示出云服务器的 IP 地址表示成功了:
这个时候,已经可以使用域名代替之前的公网 IP 访问了,就像我的这样:
http://www.typeroom.cn:8088
默认 80 端口
细心的同学会发现,我现在访问页面还需要加端口 8088
才行,这是因为我云服务器的 nginx 配置中将前端资源的 server
端口设置成了 8088
,将它改成 80
端口:
server {
listen 80;
server_name type-room-web;
root /root/type-room/web/dist;
include /etc/nginx/default.d/*.conf;
....
}
回到阿里云服务器实例的控制台,将安全组规则中原来的 8088
改成 80
:
改完之后再访问我们的页面,即不加端口的地址:
http://www.typeroom.cn
结果出现这个提示:
原因时我们的网站没有进行备案,那接下来就去备案呗~
网站备案
访问阿里云网站备案,按照流程填写资料,提交申请后只能等待了。
如果你的居住地和户籍地不一致,要事先去办理流动人口居住证,阿里云那边审核的时候肯定会给你打电话的,所以事先准备好吧。
后续如果一切顺利的话,你的域名就可以正常访问了。
支持 https 访问
https 想必 http 的优势是什么就不说了,老生常谈了,你的网站如果不是 https,在如今,对你的访问量几乎是打击性的。
但是付费的 SSL 证书是真的贵!接下来为大家演示下如何申请免费的证书,并让你的网站支持 https 访问。这一切得益于 Let's Encrypt 免费证书 。
安装 certbot
Certbot 是 Let's Encrypt 推出的获取证书的客户端,可以让我们免费快速地获取 Let's Encrypt 证书。
先安装必要的软件:
yum install epel-release -y
yum install certbot -y
生成证书
接着生成泛域名证书(别忘了写的是你自己的域名哦):
certbot certonly --preferred-challenges dns --manual -d *.typeroom.cn --server https://acme-v02.api.letsencrypt.org/directory
执行上面命令后会连续回答几个问题,该填邮箱就填,该同意的就同意,直到出现这个提示:
回到阿里云域名解析,添加一条 TXT
的解析:
添加完解析后稍等几秒钟,即可回车继续,这时候就会校验记录是否有效。
出现这个 Congratulations 就算是成功了!!生成的证书在 /etc/letsencrypt/live
目录下。
修改 nginx 配置
原来 http 访问的是 80
端口,我们需要修改为 443
端口,并增加证书的配置:
server {
listen 443 ssl;
server_name *.typeroom.cn;
# 证书位置
ssl_certificate /etc/letsencrypt/live/typeroom.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/typeroom.cn/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
# 静态页面目录
root /root/type-room/web/dist;
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://localhost:7077;
}
}
# http 访问转至 https 访问
server {
listen 80;
server_name *.typeroom.cn;
root /usr/share/nginx/html;
# 下面这行不加会导致无法重定向!
include /etc/nginx/default.d/*.conf;
rewrite ^(.*)$ https://${host}$1 permanent;
}
修改配置之后要重启下 nginx :
systemctl restart nginx
开放 443 端口
之前云服务器的安全组配置是开放了 80
端口,现在需要新增一个 443
端口。
不出意外的话,现在你可以使用 https 访问你的网站了!但是意外总会出现,比如访问 https 还是不通。但是我们不慌,一步一步解决。
可先通过以下命令查看 nginx 进程的 pid 和监听的端口:
ps aux | grep nginx
# 比如 master process 的 pid 为 9690
netstat -anp | grep 9690
发现已经监听 80
和 443
了:
我们先看下 nginx 服务的状态:
systemctl status nginx
结果报红了:
重启 nginx 也是失败:
systemctl restart nginx
百般不得其解,直到看到这个问题:
nginx.service failed because the control process exited
最高赞的回答解决了这个问题,首先找到当前占用了 80
和 443
端口的进程,发现就是 nginx 的:
netstat -tulpn
然后根据 pid
(比如 9680)杀掉这个进程:
sudo kill -2 9680
这个时候再重启 nginx 服务就可以了:
systemctl restart nginx
自动续期
免费证书有效期 3 个月,到期之后我们可以再次续期,达到永久免费的效果。
转载自:https://juejin.cn/post/7250044070671695930