next項(xiàng)目部署到服務(wù)器pm2進(jìn)程守護(hù)

博客地址:http://blog.poetries.top/2018/11/18/react-ssr-next-deploy/

關(guān)注公眾號(hào)獲取更多資訊

一、 npm run export導(dǎo)出文件上傳到CDN

在項(xiàng)目中執(zhí)行npm run export后導(dǎo)出outCDN文件上傳到CDN

// scripts/upload.js

const fs = require('fs');
const path = require('path');
const OSS = require('ali-oss');

const filePath = path.join(__dirname,'../outCDN');
const excludeFiles = ['index.html']

const client = new OSS({
  region: 'oss-cn-shenzhen',
  accessKeyId: '',
  accessKeySecret: '',
  bucket: ''
});

// 遍歷文件夾中所有文件
async function uploadFile(filePath){
    //根據(jù)文件路徑讀取文件东囚,返回文件列表
    fs.readdir(filePath,async function(err,files){
        if(err){
            console.warn(err)
        }else{
            //遍歷讀取到的文件列表
            files.forEach(async function(filename){
                //獲取當(dāng)前文件的絕對路徑
                const filedir = path.join(filePath,filename);
                //根據(jù)文件路徑獲取文件信息跺嗽,返回一個(gè)fs.Stats對象
                fs.stat(filedir,async function(eror,stats){
                    if(eror){
                        console.warn('獲取文件stats失敗');
                    }else{
                        const isFile = stats.isFile();//是文件
                        const isDir = stats.isDirectory();//是文件夾
                        if(!excludeFiles.includes(filename) && isFile){
                            const fileKey = `${filedir.split('outCDN/').pop()}`

                            try {
                                // object表示上傳到OSS的Object名稱,localfile表示本地文件或者文件路徑
                                let data = await client.put(fileKey,filedir);

                                console.error('upload success: %j', data);
                            } catch(err) {
                                console.error('upload failed: %j', err);
                            }
                        }
                        if(isDir){
                            uploadFile(filedir);//遞歸页藻,如果是文件夾桨嫁,就繼續(xù)遍歷該文件夾下面的文件
                        }
                    }
                })
            });
        }
    });
}


uploadFile(filePath)

二、處理next build后的文件

執(zhí)行next build以后,把.next份帐、package.json璃吧、server.jsnext.config.js废境、ecosystem.json 拷貝到一個(gè)文件夾統(tǒng)一管理畜挨,最后部署這個(gè)文件夾下的內(nèi)容即可

// scripts/copyFiles.js

const fs = require( 'fs' ),
    stat = fs.stat;

const path = require('path')

const includeFiles = ['package.json','server.js','next.config.js','ecosystem.json']

/*
 * 復(fù)制目錄中的所有文件包括子目錄
 * @param{ String } 需要復(fù)制的目錄
 * @param{ String } 復(fù)制到指定的目錄
 */
const readDir = function( src, dst ){
    // 讀取目錄中的所有文件/目錄
    fs.readdir( src, function( err, paths ){
        if( err ){
            throw err;
        }
        paths.forEach(function( filename ){
            var _src = src + '/' + filename,
                _dst = dst + '/' + filename,
                readable, writable;

            stat( _src, function( err, st ){
                if( err ){
                    throw err;
                }
                // 判斷是否為文件
                if( st.isFile()){
                    // 創(chuàng)建讀取流
                    readable = fs.createReadStream( _src );
                    // 創(chuàng)建寫入流
                    writable = fs.createWriteStream( _dst );
                    // 通過管道來傳輸流
                    readable.pipe( writable );
                }
                // 如果是目錄則遞歸調(diào)用自身
                else if( st.isDirectory()){
                    copyDir( _src, _dst, readDir );
                }
            });
        });
    });
};

// 在復(fù)制目錄前需要判斷該目錄是否存在,不存在需要先創(chuàng)建目錄
const copyDir = function( src, dst, callback ){
    fs.exists( dst, function( exists ){
        // 已存在
        if( exists ){
            callback( src, dst );
        }
        // 不存在
        else{
            fs.mkdir( dst, function(){
                callback( src, dst );
            });
        }
    });
};

const copyFile = ()=>{
  includeFiles.forEach(filename=>{
    fs.createReadStream(path.join(__dirname,'../'+filename)).pipe(fs.createWriteStream(path.join(__dirname,'../deployBuildFiles',filename)))
    console.log('拷貝完成!')
  })
}

// 復(fù)制目錄
copyDir( '.next', 'deployBuildFiles/.next', readDir);

// 拷貝文件
copyFile()

三噩凹、pm2之ecosystem部署項(xiàng)目

PM2部署應(yīng)用流程巴元,通過pm2的配置文件來部署
http://pm2.keymetrics.io/docs/usage/deployment/

3.1 配置部署腳本文件

在項(xiàng)目根目錄添加pm2的部署腳本文件 ecosystem.json

部署文檔詳情:http://pm2.keymetrics.io/docs/usage/deployment/

{
  "apps": [
      {
        "name": "goodsapp", //pm2運(yùn)行的應(yīng)用名稱
        "script": "server.js",//服務(wù)啟動(dòng)入口
        "env":{
            "COMON_VARIABLE": "true"
        },
        "env_production": {
            "NODE_ENV": "production", //env
            "HOST": "localhost"
        }
      }
  ],
  "deploy": {
      // 最后這樣使用 pm2 deploy ecosystem.json production
      "production": {
          "user": "user_00",// 服務(wù)器用戶名
          "host": ['192.68.1.201'],//服務(wù)器ip地址 可寫多個(gè)
          "ref": "origin/master",//從指定分支拉取代碼
          "repo": "http://p.yesdat.com/diffusion/49/goodsh.git",
          "path": "/data/poetry/testDir/prev-goods.yesdat.com", //上傳本地目錄到服務(wù)器
          "ssh_options": "StrictHostKeyChecking=no",
          "post-deploy": "npm install --registry=https://registry.npm.taobao.org && npm install && pm2 startOrRestart ecosystem.json --env production",//部署腳本
          "env": {
              "NODE_ENV": "production"
          }
      }
  }
}

或者簡單scp上傳到服務(wù)器

scp -P36000  -r deployBuildFiles/.next user_00@192.168.1.201:/home/data/services/goods-prev.yesdat.com/

更多詳情 http://blog.poetries.top/2018/12/03/linux-scp

3.2 部署Nginx配置規(guī)則

nginx安裝目錄下的vhost中新建一個(gè)xx-3000.conf的配置文件

  • 在Nginx目錄/etc/nginx下執(zhí)行 sudo /usr/sbin/nginx -t 檢測配置文件是否成功
image.png
upstream goodsapp { // website項(xiàng)目的目錄名稱
  server 127.0.0.1:3000; // 服務(wù)器上的本地啟動(dòng)入口,端口對應(yīng)項(xiàng)目中server.js中的端口
}

// 配置server
server {
    listen 80;
    server_name prev-goods.yesdat.com; //指向的域名

    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://goodsapp; // 請求將會(huì)轉(zhuǎn)發(fā)到goodsapp的node服務(wù)下
        proxy_redirect off;
    }

    // 處理靜態(tài)資源
    location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|pdf|txt) {
        root /data/goodsapp/static; //請求轉(zhuǎn)發(fā)到靜態(tài)資源路徑
    }
}

更多配置參考 https://github.com/poetries/poetry-configure/blob/master/nginx.conf

3.3 本地項(xiàng)目根執(zhí)行的命令

  • pm2 deploy ecosystem.json goodsapp setup 初始化
  • pm2 deploy ecosystem.json goodsapp 部署

3.4 部署到阿里云

第一步:配置Nginx

查看Nginx安裝路徑 which nginx 注意/etc/nginx/usr/local/nginx/下的nginx區(qū)別

# 切換到Nginx當(dāng)前目錄下
/usr/local/nginx/conf/

# 創(chuàng)建vhost
mkdir vhost

# 創(chuàng)建goodsapp-3001.conf驮宴,內(nèi)容如下

server {
    listen 8080; 
    server_name 39.108.74.36;# 在ifconfig的拿到的ip地址或者是公網(wǎng)ip逮刨,這里填公網(wǎng)ip,如果是域名阿里云需要備案才可以正常訪問

    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://127.0.0.1:3001;# 把172.16.0.223:8080的請求轉(zhuǎn)發(fā)代理到本機(jī)的3001端口
    }
}

# 在/usr/local/nginx/sbin/nginx/conf/nginx.conf下include創(chuàng)建的vhost文件
include /etc/nginx/vhost/*.conf; # 在文件最后include配置文件

# 在/usr/local/nginx/sbin/nginx/conf/nginx.conf下執(zhí)行檢測配置文件
sudo /usr/local/nginx/sbin/nginx -t

# 重新加載Nginx配置
/usr/local/nginx/sbin/nginx -s reload

一些注意事項(xiàng)

  • server_name可以是域名堵泽,也可以是ip修己。ip可以是本地,也可以是公網(wǎng)ip

本機(jī)ip

image.png

公網(wǎng)ip

image.png
  • 阿里云防火墻規(guī)則設(shè)置

這里訪問了 8080需要在阿里云后臺(tái)配置一下

image.png

第二步:pm2部署到服務(wù)器

首先在服務(wù)端全局安裝pm2迎罗、npm node并且建立軟鏈

npm i pm2 -g

重要:請注意:

一定要做建立軟鏈這步睬愤,否則出現(xiàn)如下問題

image.png
image.png

建立npm 軟鏈

image.png

建立node 軟鏈

image.png

建立pm2 軟鏈

image.png

正式部署

  • 根目錄執(zhí)行pm2 deploy deploy-app.json production setup 初始化服務(wù)端環(huán)境
  • 根目錄執(zhí)行pm2 deploy deploy-app.json production --force 輸入服務(wù)端用戶root密碼,部署即可

來到/home/production目錄查看上傳的文件

{
    "apps": [
        {
          "name": "goodsapp-prev",
          "script": "server.js",# 根目錄server.js文件
          "env":{
              "COMON_VARIABLE": "true"
          },
          "env_production": {
              "NODE_ENV": "production"
          }
        }
    ],
    "deploy": {
        "production": {
            "user": "root",//用戶名
            "host": ["39.108.74.36"], //公網(wǎng)ip
            "ref": "origin/master",
            "repo": "https://gitee.com/Poetries1/goods-prev.yesdat.com.git",
            "path": "/home/production",
            "ssh_options": ["StrictHostKeyChecking=no", "PasswordAuthentication=no"],
            "post-deploy": "npm install && pm2 startOrRestart deploy-app.json --env production",
            "pre-deploy-local": "echo 'Deploy Done!'",
            "env": {
                "NODE_ENV": "production"
            }
        }
    }
  }

更多配置信息 http://pm2.keymetrics.io/docs/usage/deployment/

  • pm2 list查看啟動(dòng)的項(xiàng)目
image.png
  • pm2 logs查看啟動(dòng)日志
image.png

然后在瀏覽器訪問http://39.108.74.36:8080(http://公網(wǎng)ip:端口)即可看到纹安,到此部署結(jié)束

3.5 部署更多參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钻蔑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子奸鸯,更是在濱河造成了極大的恐慌咪笑,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娄涩,死亡現(xiàn)場離奇詭異窗怒,居然都是意外死亡映跟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門扬虚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來努隙,“玉大人,你說我怎么就攤上這事辜昵≥┠鳎” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵堪置,是天一觀的道長躬存。 經(jīng)常有香客問我,道長舀锨,這世上最難降的妖魔是什么岭洲? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮坎匿,結(jié)果婚禮上盾剩,老公的妹妹穿的比我還像新娘。我一直安慰自己替蔬,他們只是感情好告私,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著进栽,像睡著了一般德挣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上快毛,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天格嗅,我揣著相機(jī)與錄音,去河邊找鬼唠帝。 笑死屯掖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的襟衰。 我是一名探鬼主播贴铜,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瀑晒!你這毒婦竟也來了绍坝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤苔悦,失蹤者是張志新(化名)和其女友劉穎轩褐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玖详,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡把介,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年勤讽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拗踢。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脚牍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巢墅,到底是詐尸還是另有隱情诸狭,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布砂缩,位于F島的核電站作谚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庵芭。R本人自食惡果不足惜妹懒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望双吆。 院中可真熱鬧眨唬,春花似錦、人聲如沸好乐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔚万。三九已至岭妖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間反璃,已是汗流浹背昵慌。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淮蜈,地道東北人斋攀。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像梧田,于是被迫代替她去往敵國和親淳蔼。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容