本篇文章讲的不是如何把一个项目部署上线,而是如何自动化上线。
开发了一个需求管理和发布系统。
通过这个系统,可以创建需求、创建发布计划、创建分支、部署到测试环境、部署到生产环境、正式上线、合并代码等。
一、功能设计
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。没有像以前一样,直接拿别的项目看,而是一步一个脚印的学习,虽然也都是皮毛,但是感觉自己的知识体系丰富了很多。也去了解了很多持续集成的知识,当然我做的小项目还是比较简单的啦~ 喜欢就点个赞鼓励一下吧,(^__^) 嘻嘻……
详细的使用都在前端项目、后端项目,感兴趣的可以看下并点个star哈~✨
以上所述是小编给大家介绍的自动化部署项目详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
我在Laravel voyager上做了一个测试项目。我想把它发布到服务器上。 我试试这个: 初始化 git远程添加原点 *** git检出大师 git拉 添加. env 添加. htaccess php工匠迁移--force 在ProviderRepository.php第208行中:未找到类“TCG\Voyager\VoyagerServiceProvider” 怎么做? 更新: php ar
我在科尔达做了一个简单的项目。我的项目有4个节点,包括公证人和客户文件夹中的SpringBoot API。我不知道如何将我的项目部署到服务器上。我看过Corda文档,但那篇教程只针对一个节点。所以,我的问题是如何在服务器上部署具有多个节点的Corda项目,以及SpringBoot API。有人能帮我吗?
我有一个Laravel项目,我想把它部署到服务器上,问题是我们通常都有索引。php和。htaccess在公共文件夹中,但在我的例子中,我将这两个文件放在根目录中。所以我想知道,发球需要做哪些改变? 我怎样才能把它上传到服务器上?
在tomcat 7.0.57中部署应用程序时,我得到了Valiate Jar文件错误。将tomcat版本更改为8是解决此问题的唯一方法?这是我的错误日志: org.apache.catalina.loader.WebappClassLoader validateJarFile INFO: validateJarFile(/home/webapps/myapp/WEB-INF/lib/tomcat-
我的网站有两个服务器——本地(ubuntu桌面上的laravel homestead)和公共(带有php7的ubuntu服务器)。我已经有了一些简单的网站,它们在我的公共服务器上运行良好,并且可以在网上看到。我在我的laravel Homestead本地服务器上创建了laravel 5.2项目,现在我想将其移动到我的公共服务器上。我压缩完整的项目文件夹(tar.gz),并使用filezilla将其
我试图调试我的项目,但它显示错误消息: “无法为模块部署WebForm部署错误:WebForm:部署期间发生错误:准备应用程序时出现异常[EclipseLink-4002](Eclipse持久性服务-2.3.2.v20111125-r10461):org。日食坚持不懈例外。DatabaseException内部异常:java。sql。SQLException:分配连接时出错。原因:无法分配连接,因