如何自动化部署项目?折腾服务器之旅~
- 作者: 逸豪8462138
- 来源: 51数据库
- 2021-08-10
本篇文章讲的不是如何把一个项目部署上线,而是如何自动化上线。
开发了一个需求管理和发布系统。
通过这个系统,可以创建需求、创建发布计划、创建分支、部署到测试环境、部署到生产环境、正式上线、合并代码等。
一、功能设计
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://www.51sjk.com/Upload/Articles/1/0/268/268513_20210708023901863.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哈~✨
以上所述是小编给大家介绍的自动化部署项目详解整合,希望对大家有所帮助
