本篇文章講的不是如何把一個專案部署上線,而是如何自動化上線。
開發了一個需求管理和釋出系統。
通過這個系統,可以建立需求、建立釋出計劃、建立分支、部署到測試環境、部署到生產環境、正式上線、合併程式碼等。
一、功能設計
9.9元的阿里雲伺服器真的很慢,但還是足夠折騰完這個專案。 用3個目錄來模擬不同的環境。
目錄 | 存放 |
---|---|
project | 存放所有的專案,比如本系統的前後端程式碼。 |
pre-dir | 預發環境,當然是用來測試的。 |
pro-dir | 生產環境,測試沒問題,部署上線。 |
一圖勝千言。
二、系統頁面
我的任務
接到一個新的需求,可以新建一個需求,並建立開發分支。
釋出佇列
開發結束之後,便可以到釋出佇列中,部署到預發環境進行測試。 測試通過指定Cookie 就可以訪問到測試的程式碼。最終再進行線上部署。
專案資訊
二、技術棧
前端技術棧
Vue + elementUI,具體程式碼在Github,感興趣的可以看下並點個star哈~✨
服務端技術棧
非常常見的Node.js(Koa2) + Mysql + Redis + Pm2。 具體程式碼在Github,感興趣的可以看下並點個star哈~✨
三、Redis和Session配置
// utils/Store.js
const Redis = require("ioredis");
const { Store } = require("koa-session2");
class RedisStore extends Store {
constructor() {
super();
this.redis = new Redis();
}
async get(sid, ctx) {
let data = await this.redis.get(`SESSION:${sid}`);
return JSON.parse(data);
}
async set(session, { sid = this.getID(24), maxAge = 1000 * 60 * 60 } = {}, ctx) {
try {
console.log(`SESSION:${sid}`);
// Use redis set EX to automatically drop expired sessions
await this.redis.set(`SESSION:${sid}`, JSON.stringify(session), 'EX', maxAge / 1000);
} catch (e) {}
return sid;
}
async destroy(sid, ctx) {
return await this.redis.del(`SESSION:${sid}`);
}
}
module.exports = RedisStore;
複製程式碼
// 入口檔案
const session = require("koa-session2");
const Store = require("./utils/Store.js");
// session配置
app.use(session({
store: new Store(),
key: "SESSIONID",
}));
複製程式碼
四、Router配置
為了Router看起來更優雅,也是通過中介軟體
// 1、middleware配置檔案
const routers = require('../routers');
module.exports = (app) => {
app.use(routers());
}
// 2、index.js入口檔案
const middleware = require('./middleware');
middleware(app);
// 3、routers 註冊檔案
const Router = require('koa-router');
const router = new Router();
const koaCompose = require('koa-compose');
// 介面入口
const {insertDemand} = require('../controllers/demand/insertDemand');
const {deleteDemand} = require('../controllers/demand/deleteDemandByDid');
const {updateDemand} = require('../controllers/demand/updateDemandByDid');
// 加字首
router.prefix('/api');
module.exports = () => {
// 新增需求
router.get('/insertDemand', insertDemand);
// 刪除需求
router.get('/deleteDemand', deleteDemand);
return koaCompose([router.routes(), router.allowedMethods()]);
}
複製程式碼
五、nginx配置
最頭痛的就是nginx配置了,因為不是很熟悉,一直在試錯、踩坑。不過還好終於成功了!
前後端專案通過Nignx提供服務,Node服務通過Nginx轉發,主要是為了驗證各種環境。
如果不設定Cookie,預設訪問的就是線上環境,設定Cookie 就會走到預釋出測試環境,用於測試。
# cookie 取TEST 賦值給$proxy_node
map $cookie_TEST $proxy_node {
default "";
"1" "1";
"2" "2";
"3" "3";
}
# 釋出管理系統前端設定
server {
listen 80;
server_name test.xue.com;
if ($proxy_node = ''){
set $dollar "/data/pro-dir/dandelion/dist/";
}
if ($proxy_node = "1") {
set $dollar "/data/pre-dir/dandelion/dist/";
}
location / {
root $dollar;
index index.html;
try_files $uri $uri/ /index.html;
}
}
# 釋出管理系統後端設定
# 反向代理到node服務
server {
listen 80;
server_name m.xue.com;
if ($proxy_node = ''){
set $dollar "/data/pro-dir/study-demo/";
}
if ($proxy_node = "2") {
set $dollar "/data/pre-dir/study-demo/";
}
location / {
root $dollar;
index index.html;
}
}
# demo專案前端設定
server {
listen 80;
server_name api.xue.com;
location / {
if ($proxy_node = "") {
set $from 3001;
proxy_pass http://47.107.188.55:3001;
}
if ($proxy_node = "3") {
set $from 3002;
proxy_pass http://47.107.188.55:3002;
}
}
}
複製程式碼
六、一些中介軟體
常用的HTTP設定
解決跨域,OPTIONS
請求,攜帶Cookie憑證等問題。
module.exports = () => {
return async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', 'http://test.xue.com');
ctx.set('Access-Control-Allow-Credentials', true);
ctx.set('Access-Control-Allow-Headers', 'content-type');
ctx.set('Access-Control-Allow-Methods', 'OPTIONS, GET, HEAD, PUT, POST, DELETE, PATCH');
// 這個響應頭的意義在於,設定一個相對時間,在該非簡單請求在伺服器端通過檢驗的那一刻起,
// 當流逝的時間的毫秒數不足Access-Control-Max-Age時,就不需要再進行預檢,可以直接傳送一次請求。
ctx.set('Access-Control-Max-Age', 3600 * 24);
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else {
await next();
}
}
}
複製程式碼
登入
這個系統屬於強制登入的,登入統一進行了處理。
const Store = require("../../utils/Store");
const redis = new Store();
module.exports = () => {
return async (ctx, next) => {
// 白名單
if (ctx.request.url === '/api/login') {
return await next();
}
const SESSIONID = ctx.cookies.get('SESSIONID');
if (!SESSIONID) {
return ctx.body = {
mes: '沒有攜帶SESSIONID~',
data: '',
err_code: 1,
success: false,
};
}
const redisData = await redis.get(SESSIONID);
if (!redisData) {
return ctx.body = {
mes: 'SESSIONID已經過期~',
data: '',
err_code: 1,
success: false,
};
}
if (redisData && redisData.uid) {
console.log(`登入了,使用者uid為${redisData.uid}`);
await next();
}
}
}
複製程式碼
七、操作shell指令碼
舉個例子,建立專案分支
let path = ''; // 專案路徑
// 建立分支
const branch_name = `branch_${new Date().getTime()}`;
cp.execSync(`/data/dandelion-server/shell/createBranch.sh ${path} ${branch_name}`);
複製程式碼
#!/bin/bash
cd $1
git pull origin master
git checkout -b $2
git push --set-upstream origin $2
複製程式碼
八、連線資料庫
config.js配置檔案
let dbConf = null;
const DEV = {
database: 'dandelion', //資料庫
user: 'root', //使用者
password: '123456', //密碼
port: '3306', //埠
host: '127.0.0.1' //服務ip地址
}
const PRO = {
database: 'dandelion', //資料庫
user: 'root', //使用者
password: '123456', //密碼
port: '3306', //埠
host: 'xx.xx.xx.xx' //服務ip地址
}
dbConf = PRO; //這個可以通過判斷區分開發環境
module.exports = dbConf;
複製程式碼
資料庫連線檔案
const mysql = require('mysql');
const dbConf = require('./../config/dbConf');
const pool = mysql.createPool({
host: dbConf.host,
user: dbConf.user,
password: dbConf.password,
database: dbConf.database,
})
let query = function( sql, values ) {
return new Promise(( resolve, reject ) => {
pool.getConnection(function(err, connection) {
if (err) {
reject( err )
} else {
connection.query(sql, values, ( err, rows) => {
if ( err ) {
reject( err )
} else {
resolve( rows )
}
connection.release()
})
}
})
})
}
module.exports = {
query,
}
複製程式碼
就可以在model層呼叫了~
const {query} = require('../common/mysql');
class UserModel {
constructor() {}
/**
* @description: 根據pid和did建立一個分支
* @param {pid} 專案id
* @param {did} 需求id
* @param {branch_name} 分支名
* @return: 分支資訊
*/
async insertBranchInfo(sqlParams) {
const sql = 'insert branch_info (pid, bid, branch_name, pub_time) values(?,?,?,?)';
console.log(sql)
let data = await query(sql, sqlParams, (err, result) => {
return result;
});
return data;
}
}
複製程式碼
九、域名
沒有買域名,通過本地修改hosts(可以直接用工具)
47.107.188.xx為伺服器IP
47.107.188.xx test.xue.com
47.107.188.xx api.xue.com
47.107.188.xx m.xue.com
複製程式碼
總結
算是第一次自己搭建一個完整的專案,從前端到後端。
尤其是後端,作為一個前端小白,從學習如何使用伺服器,到Linux/Vim/Shell/Nignx/Pm2/Redis/Session/Mysql/Koa2。沒有像以前一樣,直接拿別的專案看,而是一步一個腳印的學習,雖然也都是皮毛,但是感覺自己的知識體系豐富了很多。也去了解了很多持續整合的知識,當然我做的小專案還是比較簡單的啦~ 喜歡就點個贊鼓勵一下吧,(^__^) 嘻嘻……