首页 > 开发 > JS > 正文

如何自动化部署项目?折腾服务器之旅~

2024-05-06 16:49:59
字体:
来源:转载
供稿:网友

本篇文章讲的不是如何把一个项目部署上线,而是如何自动化上线。

开发了一个需求管理和发布系统。

通过这个系统,可以创建需求、创建发布计划、创建分支、部署到测试环境、部署到生产环境、正式上线、合并代码等。

一、功能设计

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.jsconst 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_nodemap $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/bashcd $1git pull origin mastergit checkout -b $2git 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。没有像以前一样,直接拿别的项目看,而是一步一个脚印的学习,虽然也都是皮毛,但是感觉自己的知识体系丰富了很多。也去了解了很多持续集成的知识,当然我做的小项目还是比较简单的啦~ 喜欢就点个赞鼓励一下吧,(^__^) 嘻嘻……
详细的使用都在前端项目后端项目,感兴趣的可以看下并点个star哈~✨

 以上所述是小编给大家介绍的自动化部署项目详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对VeVb武林网网站的支持!


注:相关教程知识阅读请移步到JavaScript/Ajax教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表