前言
相信很多前端同學(xué)對 vue 或 react 的開發(fā)很熟悉了馁龟,也知道如何去打包生成一個(gè)生產(chǎn)環(huán)境的包乖仇,但對于生產(chǎn)環(huán)境的部署可能有些同學(xué)了解比較少。小公司可能都是后端幫忙部署了尖滚,大公司會(huì)有專門的運(yùn)維同學(xué)部署鹿寨,對于生產(chǎn)環(huán)境的部署工作有些同學(xué)接觸的不多,所以這次來分享和總結(jié)下前端項(xiàng)目部署相關(guān)的實(shí)戰(zhàn)經(jīng)驗(yàn):從靜態(tài)站點(diǎn)的部署膘茎,到 node 項(xiàng)目的部署桃纯,再到負(fù)載均衡的部署,順便也會(huì)分享一下提高部署效率的腳本和方法披坏。
準(zhǔn)備工作
- 一臺(tái)或多臺(tái)服務(wù)器或虛擬機(jī)态坦。
- 一份 vue 或 react 項(xiàng)目的打包后的文件。
- 一份 node 項(xiàng)目的源碼棒拂。
靜態(tài)站點(diǎn)的部署
靜態(tài)站點(diǎn)的部署指的是前端的 html/css/js 資源的部署伞梯,如 vue 或 react 打包后生成的 html,css 和 js 文件着茸,我們將這些文件上傳到服務(wù)器后壮锻,通過 Nginx 將這些資源暴露到公網(wǎng)上。
- 上傳文件到服務(wù)器
就是將文件人工將打包后的文件拷貝到服務(wù)器上涮阔,這個(gè)很簡單猜绣,但如果每次都是人工拷貝,部署效率未免會(huì)低了一些敬特,所以建議使用一些腳本或工具掰邢。在大公司,一般對服務(wù)器權(quán)限控制得很嚴(yán)格伟阔,可能需要各種跳板機(jī)或動(dòng)態(tài)密碼等辣之,而大公司一般都有專門的運(yùn)維人員或者 CI/CD 工具。
小公司可能相對自由一些皱炉,可以允許個(gè)人直接 ssh 連接服務(wù)器怀估,此時(shí)可以配合使用rsync
或scp
命令(Linux 或 Mac 系統(tǒng))來一鍵上傳文件到服務(wù)器上,給部署提效合搅。在這里分享一下以前使用過的部署腳本,在前端項(xiàng)目根目錄新建一個(gè)名為deploy.sh
的文件:
#!/bin/bash
function deploy() {
# 測試服務(wù)器
test_host="root@test_server_ip"
# 生產(chǎn)服務(wù)器
prod_host="root@prod_server_ip"
project_path="/srv/YourProject"
if [ "$1" == "prod" ]; then
target="$prod_host:$project_path"
else
target="$test_host:$project_path"
fi
rsync -azcuP ./dist/ --exclude node_modules --exclude coverage --exclude .env --exclude .nyc_output --exclude .git "$target"
echo "deploy to $target"
}
deploy $@
以上腳本的意思是將/dist
目錄下的所有文件多搀,上傳到對應(yīng)服務(wù)器的/srv/YourProject
目錄下。測試環(huán)境的部署是直接在根目錄運(yùn)行./deploy.sh
灾部,該命令會(huì)將/dist
目錄直接上傳到root@test_server_ip
服務(wù)器上康铭;
生產(chǎn)環(huán)境的部署是在后面加一個(gè)參數(shù)./deploy.sh prod
,這樣可以實(shí)現(xiàn)多環(huán)境部署赌髓。更進(jìn)一步的做法是將運(yùn)行腳本的命令直接寫進(jìn)package.json
中从藤,如:
"scripts": {
"build": "vue-cli-service build --mode staging",
"deploy": "npm run build && ./deploy.sh",
"deploy:prod": "npm run build && ./deploy.sh prod"
},
這樣催跪,通過npm run deploy
命令就可以實(shí)現(xiàn)直接打包并部署到測試環(huán)境了腻异。如果你的公司目前還在用人工拷貝或 FTP 工具這種低效的部署方式膊爪,不妨試一下用上面的腳本來提效哦。
PS:由于 rsync 命令只在 Linux 或 Mac 才有滨巴,所以只有開發(fā)環(huán)境是 Linux 或 Mac 的用戶才可以運(yùn)行哦悯搔,Windows 用戶是沒法跑這個(gè)命令的榛鼎。
- 編寫網(wǎng)站的 conf
上傳文件到服務(wù)器后,就可以著手配置 nginx 了鳖孤。一般 nginx 的配置都會(huì)放在/etc/nginx/conf.d
目錄下,我們在該目錄新建一個(gè)test.conf
作為該項(xiàng)目的配置:
server {
listen 80;
server_name your-domain.com; # 域名
location / {
root /srv/YourProject; # 網(wǎng)站源碼根目錄
index index.html;
}
location /api {
proxy_pass http://localhost:8080; # 反向代理后端接口地址
}
}
一般來說抡笼,靜態(tài)站點(diǎn)只需配置以上幾個(gè)就可以了苏揣,
-
server_name
表示域名,需要先解析到服務(wù)器的公網(wǎng) ip推姻; -
root
表示服務(wù)器中代碼所在的位置平匈, -
index
指明了默認(rèn)的處理文件是index.html
; -
location /api
是反向代理后端服務(wù)(這里假設(shè)了后端服務(wù)部署在本地 8080 端口)藏古,即your-domain.com/api
的請求都會(huì)轉(zhuǎn)發(fā)到http://localhost:8080
上增炭,一般用該方法可以完美解決前端跨域的問題。
修改 nginx 的 conf 后需要 reload 一下 nginx 服務(wù):nginx -s reload
- 測試
如果上一步配置的域名是已經(jīng)解析到服務(wù)器 ip 了的拧晕,就可以直接在公網(wǎng)上通過訪問域名來訪問你的站點(diǎn)了隙姿。如果不是,可以修改一下本機(jī)的 host 文件厂捞,使得配置的域名可以在本機(jī)訪問输玷;或者通過http://localhost
來訪問。
node 項(xiàng)目的部署
node 項(xiàng)目在開發(fā)時(shí)可以用node app.js
這樣的命令來啟動(dòng)服務(wù)靡馁,但在服務(wù)器上如果使用這個(gè)命令欲鹏,退出服務(wù)器后 node 進(jìn)程就停止了,所以需要借助可以讓 node 進(jìn)程 keep alive 的工具〕裟現(xiàn)在一般都是用pm2
赔嚎。
- 安裝 pm2
npm install -g pm2
pm2 的一些常用命令:
pm2 start app.js # 啟動(dòng)app.js應(yīng)用程序
pm2 start app.js -i 4 # cluster mode 模式啟動(dòng)4個(gè)app.js的應(yīng)用實(shí)例 # 4個(gè)應(yīng)用程序會(huì)自動(dòng)進(jìn)行負(fù)載均衡
pm2 start app.js --name="api" # 啟動(dòng)應(yīng)用程序并命名為 "api"
pm2 start app.js --watch # 當(dāng)文件變化時(shí)自動(dòng)重啟應(yīng)用
pm2 start script.sh # 啟動(dòng) bash 腳本
pm2 list # 列表 PM2 啟動(dòng)的所有的應(yīng)用程序
pm2 monit # 顯示每個(gè)應(yīng)用程序的CPU和內(nèi)存占用情況
pm2 show [app-name] # 顯示應(yīng)用程序的所有信息
pm2 logs # 顯示所有應(yīng)用程序的日志
pm2 logs [app-name] # 顯示指定應(yīng)用程序的日志
pm2 flush
pm2 stop all # 停止所有的應(yīng)用程序
pm2 stop 0 # 停止 id為 0的指定應(yīng)用程序
pm2 restart all # 重啟所有應(yīng)用
pm2 reload all # 重啟 cluster mode下的所有應(yīng)用
pm2 gracefulReload all # Graceful reload all apps in cluster mode
pm2 delete all # 關(guān)閉并刪除所有應(yīng)用
pm2 delete 0 # 刪除指定應(yīng)用 id 0
pm2 scale api 10 # 把名字叫api的應(yīng)用擴(kuò)展到10個(gè)實(shí)例
pm2 reset [app-name] # 重置重啟數(shù)量
pm2 startup # 創(chuàng)建開機(jī)自啟動(dòng)命令
pm2 save # 保存當(dāng)前應(yīng)用列表
pm2 resurrect # 重新加載保存的應(yīng)用列表
pm2 update # Save processes, kill PM2 and restore processes
pm2 generate # Generate a sample json configuration file
pm2 deploy app.json prod setup # Setup "prod" remote server
pm2 deploy app.json prod # Update "prod" remote server
pm2 deploy app.json prod revert 2 # Revert "prod" remote server by 2
pm2 module:generate [name] # Generate sample module with name [name]
pm2 install pm2-logrotate # Install module (here a log rotation system)
pm2 uninstall pm2-logrotate # Uninstall module
pm2 publish # Increment version, git push and npm publish
- 用 pm2 啟動(dòng)項(xiàng)目
一般來說,我們可以直接使用pm2 start app.js --name="my-project"
這樣的命令來啟動(dòng) node 項(xiàng)目胧弛,但是這樣手打的命令會(huì)不好管理尤误,所以我們一般會(huì)在 node 項(xiàng)目的根目錄下新建一個(gè)pm2.json
文件來指定 pm2 啟動(dòng)時(shí)的參數(shù),如:
{
"name": "my-project",
"script": "./server/index.js",
"instances": 2,
"cwd": ".",
"exec_mode" : "cluster"
}
name 表示 pm2 進(jìn)程的名稱叶圃,script 表示啟動(dòng)的文件入口袄膏,instances 表示啟動(dòng)的示例數(shù)量(一般建議數(shù)值不大于服務(wù)器處理器的核數(shù)),cmd 表示應(yīng)用程序所在的目錄掺冠。
我們在服務(wù)器啟動(dòng) node 項(xiàng)目時(shí)就可以直接pm2 start pm2.json
沉馆。
- nginx 代理綁定域名
node 項(xiàng)目使用 pm2 運(yùn)行后码党,只是運(yùn)行在服務(wù)器的某個(gè)端口,如http://server_ip:3000
斥黑,如果該服務(wù)需要通過域名直接訪問揖盘,則還需要用 nginx 代理到 80 端口。在/etc/nginx/conf.d
新建一個(gè)my-project.conf
(文件命名隨意哈锌奴,一般可以用網(wǎng)站域名.conf):
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
}
}
這是最簡單的一個(gè)配置了兽狭,可以根據(jù)實(shí)際情況加一些參數(shù)哈。做完以上的步驟就完成了一個(gè) node 項(xiàng)目的部署啦鹿蜀。
前端負(fù)載均衡部署
相信很少同學(xué)可以接觸到負(fù)載均衡的部署了箕慧,當(dāng)一個(gè)項(xiàng)目的訪問量已經(jīng)大到需要使用負(fù)載均衡的時(shí)候,一般都會(huì)有專門的運(yùn)維同學(xué)去搞了茴恰。負(fù)載均衡說白了就是將大量的并發(fā)請求分擔(dān)到多個(gè)服務(wù)器上颠焦。
負(fù)載均衡的架構(gòu)有很多種,項(xiàng)目的架構(gòu)是一個(gè)不斷演進(jìn)的過程往枣,采用哪種負(fù)載均衡的架構(gòu)需要具體問題具體分析伐庭,所以本文不會(huì)講什么時(shí)候適合用哪種架構(gòu)(筆者也不會(huì),笑)分冈,接下來將會(huì)分享實(shí)戰(zhàn)如何用 Nginx 從零搭建一個(gè)經(jīng)典的負(fù)載均衡架構(gòu)案例圾另。
Nginx Server
是直接暴露在最前端的機(jī)器,當(dāng)用戶發(fā)起請求雕沉,首先到達(dá)的是Nginx
服務(wù)器集乔,然后Nginx
服務(wù)器再將請求通過某種算法分發(fā)到各個(gè)二級服務(wù)器上(圖中的Centos2
,Centos3
,Centos4
),此時(shí)Nginx Server
充當(dāng)?shù)木褪且粋€(gè)負(fù)載均衡的機(jī)器(Load Balancer)蘑秽。
筆者手上沒有這么多的服務(wù)器饺著,為了更完整地演示,所以現(xiàn)在借助 VirtualBox 建立四個(gè)虛擬機(jī)來模擬四個(gè)服務(wù)器(當(dāng)然條件確實(shí)限制時(shí)可以用同一個(gè)服務(wù)器的四個(gè)端口來代替)肠牲。
筆者新建了四個(gè) Centos8 系統(tǒng)的虛擬機(jī)幼衰,用以假設(shè) 4 臺(tái)服務(wù)器,對外都有獨(dú)立 ip(假設(shè)分別是 192.168.0.
1
,2
,3
,4
)缀雳。如前面的架構(gòu)圖所示渡嚣,Centos1 將會(huì)是作為Nginx Server
,充當(dāng)最前端的負(fù)載均衡服務(wù)器肥印,而其余的Centos2
,Centos3
,Centos4
作為應(yīng)用服務(wù)器识椰,為用戶提供真正的服務(wù)。接下來咱們一步一步去搭建這個(gè)系統(tǒng)深碱。
一腹鹉、應(yīng)用服務(wù)器搭建服務(wù)站點(diǎn)
萬丈高樓平地起,咱們首先得先搭建一個(gè)能對外的服務(wù)敷硅,這個(gè)服務(wù)可以是一個(gè)網(wǎng)站也可以是一個(gè)接口功咒。為了簡單起見愉阎,我們就直接起一個(gè)koa
的Hello World
,同時(shí)為了后面驗(yàn)證負(fù)載均衡的效果力奋,每臺(tái)機(jī)器上部署的代碼都稍微改一下文案榜旦,如:Hello Centos2
,Hello Centos3
,Hello Centos4
,這樣方便后面驗(yàn)證用戶的請求是被分發(fā)到了哪一臺(tái)服務(wù)器景殷。
koa 的 demo 站點(diǎn)已經(jīng)為大家準(zhǔn)備好了:koa-loadbalance溅呢。
我們這里以Centos2(192.168.0.2)
(ip 是虛構(gòu)的)這臺(tái)虛擬機(jī)為例,將會(huì)用pm2
部署 koa 站點(diǎn)在該虛擬機(jī)上猿挚。
- 通過 scp 或 rsync 命令將源碼上傳到 Centos2 服務(wù)器
還記得上面的deploy.sh
腳本嗎咐旧?如果你添加了腳本在項(xiàng)目中,就可以npm run deploy
直接部署到服務(wù)器上了绩蜻。demo 源碼中有這個(gè)腳本休偶,大家可以改一下里面實(shí)際的 ip,再執(zhí)行命令哈辜羊。
- ssh 進(jìn)入 Centos2 服務(wù)器
ssh root@192.168.0.2
- 安裝 node 環(huán)境
curl -sL https://rpm.nodesource.com/setup_13.x | sudo bash -
sudo yum install nodejs
可以在這里看當(dāng)前 node 有哪些版本,選最新的就行词顾,現(xiàn)在是 13八秃。
- 安裝 pm2
npm i pm2 -g
- pm2 啟動(dòng)站點(diǎn)
在項(xiàng)目根目錄執(zhí)行:
pm2 start pm2.json
pm2 list
檢查一下項(xiàng)目啟動(dòng)情況 ,同時(shí)用curl localhost:3000
看返回值:
同理肉盹,按上面步驟給Centos3
和Centos4
服務(wù)器都將服務(wù)部署起來昔驱。(記得改一下index.js
中的Hello XXX
方便后面驗(yàn)證)。不出意外的話上忍,我們的網(wǎng)站就分別運(yùn)行在三臺(tái)服務(wù)器的 3000 端口了:
-
192.168.0.2:3000
==>Hello Centos2
-
192.168.0.3:3000
==>Hello Centos3
-
192.168.0.4:3000
==>Hello Centos4
有同學(xué)可能會(huì)問骤肛,為什么 Centos2,Centos3,Centos4 不用裝 Nginx 的窍蓝?在實(shí)際操作中腋颠,應(yīng)用服務(wù)器其實(shí)是不用暴露在公網(wǎng)上的,它們與負(fù)載均衡服務(wù)器只需通過內(nèi)網(wǎng)直接連接就可以了吓笙,這樣更安全淑玫;而我們的站點(diǎn)又是 Node 項(xiàng)目,本身就可以提供 Web 服務(wù)面睛,所以不用再裝一個(gè) Nginx 進(jìn)行代理或轉(zhuǎn)發(fā)了絮蒿。
二、搭建 Nginx Server
nginx 的安裝方法:Nginx Install叁鉴。
在Centos1
安裝好 nginx 就可以了土涝。
三、實(shí)現(xiàn)負(fù)載均衡
一開始還沒了解過負(fù)載均衡時(shí)可能會(huì)覺得很難完全不知道是怎么配的幌墓,然后接下來你會(huì)發(fā)現(xiàn)超級簡單但壮,因?yàn)橹恍枰?nginx 一個(gè)配置就可以了:upstream
冀泻。
- 集群所有節(jié)點(diǎn)
我們將上面已經(jīng)部署好的Centos2
,Centos3
,Centos4
集群起來,nginx 配置類似下面這樣:
upstream APPNAME {
server host1:port;
server host2:port;
}
APPNAME
可以自定義茵肃,一般是項(xiàng)目名腔长。在/etc/nginx/conf.d
新建一個(gè)upstream.conf
:
upstream koa-loadbalance {
server 192.168.0.2:3000;
server 192.168.0.3:3000;
server 192.168.0.4:3000;
}
這樣,我們已經(jīng)將三臺(tái)服務(wù)器集成為了http://koa-loadbalance
的一個(gè)集群验残,下一步會(huì)使用它捞附。
- 配置對外的站點(diǎn)
接下來是配置一個(gè)面向用戶的網(wǎng)站了,我們假設(shè)網(wǎng)站會(huì)使用www.a.com
這個(gè)域名您没,在/etc/nginx/conf.d
下新建a.com.conf
:
server {
listen 80;
server_name www.a.com;
charset utf-8;
location / {
proxy_pass http://koa-loadbalance; # 這里是上面集群的名稱
}
}
配置結(jié)束后記得nginx -s reload
重啟一下鸟召,這樣就完成負(fù)載均衡的配置了。
四氨鹏、測試
如果你的域名是真實(shí)的且已經(jīng)解析到 nginx 服務(wù)器欧募,則此時(shí)可以直接通過域名訪問了。由于筆者這里用的是虛擬機(jī)仆抵,公網(wǎng)不可訪問跟继,所以這里配置一下宿主機(jī)的 host,使得www.a.com
指向centos1
服務(wù)器镣丑,然后在瀏覽器打開www.a.com
就可以測試我們的負(fù)載均衡網(wǎng)站啦舔糖。Mac 系統(tǒng)上是sudo vi /etc/hosts
,在最后面添加一行:
# IP是Centos1的ip
192.168.0.1 www.a.com
我們在瀏覽器訪問
www.a.com
莺匠,可以看到隨著不斷刷新金吗,服務(wù)器返回了不同的Hello CentosX
,說明我們的請求被分發(fā)到三臺(tái)服務(wù)器上了摇庙,負(fù)載均衡的配置生效啦遥缕。
五、負(fù)載均衡的幾種策略
nginx 的 upstream 可以設(shè)置很多種負(fù)載均衡的策略单匣,以下介紹幾個(gè)常用的策略玛臂。
- 輪詢(默認(rèn)):每個(gè)請求按時(shí)間順序逐一分配到不同的后端服務(wù)器,如果后端服務(wù)器 down 掉封孙,能自動(dòng)剔除迹冤。
upstream test {
server 192.168.0.2:3000;
server 192.168.0.3:3000;
server 192.168.0.4:3000;
}
- 指定權(quán)重 weight:指定輪詢幾率,weight 和訪問比率成正比泡徙,用于后端服務(wù)器性能不均的情況堪藐。
upstream test {
server 192.168.0.2:3000 weight=5;
server 192.168.0.3:3000 weight=10;
server 192.168.0.4:3000 weight=20;
}
- ip_hash:每個(gè)請求按訪問 ip 的 hash 結(jié)果分配莉兰,這樣每個(gè)訪客固定訪問一個(gè)后端服務(wù)器,可以解決 session 的問題礁竞。
upstream test {
ip_hash;
server 192.168.0.2:3000;
server 192.168.0.3:3000;
server 192.168.0.4:3000;
}
- fair(第三方):按后端服務(wù)器的響應(yīng)時(shí)間來分配請求糖荒,響應(yīng)時(shí)間短的優(yōu)先分配。
upstream test {
server 192.168.0.2:3000;
server 192.168.0.3:3000;
server 192.168.0.4:3000;
fair;
}
- url_hash(第三方):按訪問 url 的 hash 結(jié)果來分配請求模捂,使每個(gè) url 定向到同一個(gè)(對應(yīng)的)后端服務(wù)器,后端服務(wù)器為緩存時(shí)比較有效狂男。
upstream test {
server 192.168.0.2:3000;
server 192.168.0.3:3000;
server 192.168.0.4:3000;
hash $request_uri;
hash_method crc32;
}
更多的策略請參考:ngx_http_upstream_module综看,根據(jù)實(shí)際情況使用上面的這些策略,沒有特別需求就使用默認(rèn)的輪詢方式也可以岖食。
最后
從靜態(tài)站點(diǎn)到 node 站點(diǎn)红碑,再到負(fù)載均衡,相信看完本文大家對整個(gè)前端的部署體系都有了一個(gè)比較全面的了解泡垃。特別是負(fù)載均衡析珊,平時(shí)接觸得少總覺得特別復(fù)雜,其實(shí)看完了會(huì)覺得很簡單蔑穴。更高級一些的部署可能會(huì)用上 Docker 或 k8s 的集群了唾琼,這個(gè)就留待后面再說啦。
對于部署方式的提效澎剥,本文也分享了一個(gè)使用rsync
命令的腳步,配合package.json
的 script赶舆,可以做到一個(gè)命令就完成部署的動(dòng)作哑姚。
當(dāng)然,該做法也還是有很大的優(yōu)化空間的叙量,真正好用的部署方式應(yīng)該是持續(xù)集成绞佩,通過 Jenkins 或其他工具實(shí)現(xiàn)自動(dòng)化部署猪钮,代碼 push 上去就自動(dòng)構(gòu)建和部署了。如果你的公司還在用最原始的部署方式肘交,不妨加把勁多探索一些這些更爽更溜的操作啦扑馁。