前端使用webhook基于docker+nginx进行部署+持续集成

836 阅读3分钟

什么是webhook

可以理解为Webhook是一个API概念,简单来说就是一种反向API机制,类似于触发器的一样。

近几年Webhook在前后端对接的开发模式中变得越来越流行,我们能用事件描述的事物越多,Webhook的作用范围也就越大。Webhook作为一个轻量的事件处理应用,正变得越来越实用。

无论是GitLab、GitHub、gitee都可以使用 git收到推送或者其他事件之后给指定服务器发送一个请求。

什么是Docker

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。----百度百科。

个人理解其实docker就是更轻量,更便捷,更易于移植的虚拟机,我们的应用服务运行在他集成好一套虚拟环境,主要分为两个概念:

镜像(image) :类似操作系统镜像,docker创建虚拟环境需要基于一个docker镜像,镜像定义了系统需要什么初始的内容。

容器(container) :就是虚拟环境,基于image创建, 像一个虚拟机,实际上是一个进程,所以更轻量。

更多docker知识不再赘述,可自行了解。

流程架构图

image.png

安装docker

安装yum 工具包 设置docker镜像源 
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager \
    --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
 
yum install -y docker-ce docker-ce-cli containerd.io

docker重启
systemctl restart docker 

配置阿里云加速

mkdir -p /etc/docker 
tee /etc/docker/daemon.json <<-'EOF' 
{   
"registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"] 
} 
EOF 
systemctl daemon-reload 
systemctl restart docker

下载nvm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
. /root/.bashrc
nvm install stable

ssh-keygen -t rsa -b 4096 -C "xxxx@qq.com"

webhookjs

const http = require('http')
const crypto = require('crypto')
const {spawn} = require('child_process')//子进程
const SECRET = '123456'
const nodemailer = require('./nodemailer')


const sign = body => {
    return 'sha1=' + crypto.createHmac('sha1', SECRET).update(body).digest('hex')
}

const app = http.createServer((req, res) => {
    if (req.method === 'POST' && req.url === '/webhook') {
        const buffers = []
        req.on('data', (buffer) => {
            buffers.push(buffer)
        })
        req.on('end', (buffer) => {
            const body = Buffer.concat(buffers)
            const event = req.headers['x-github-event'] //push
            const signature = req.headers['x-hub-signature']//签名 验证签名
            if (signature !== sign(body)) {
                return res.end('Not Found')
            }
            console.log('开始部署')
            res.setHeader('Content-Type', 'application/json')
            res.end(JSON.stringify({
                ok: true
            }))
            if(event === 'push') {
                const payload = JSON.parse(body.toString())
                const child = spawn('sh', [`./${payload.repository.name}.sh`]);
                const buffers = []
                child.stdout.on('data', (buffer) => {
                    buffers.push(buffer)
                })
                child.stdout.on('end', (buffer) => {
                    let log = Buffer.concat(buffers).toString()
                    console.log('部署成功')
                    nodemailer(`
                        <h3>部署时间:${new Date()}</h1>
                        <h3>部署人: ${payload.pusher.name}</h1>
                        <h3>部署邮箱:${payload.pusher.email}</h1>
                        <h3>提交信息:${payload.head_commit.message}</h1>
                        <h4>部署日志:${log.replace('\r\n', '<br/>')}</h1>
                    `)
                })
            }
        })
    } else {
        res.end('404 Not Found')
    }

})

app.listen(4000, () => {
    console.log('服务器已经启动,端口号:4000')
})
const nodemailer = require('nodemailer');

// async..await is not allowed in global scope, must use a wrapper
async function main(html) {
    // Generate test SMTP service account from ethereal.email
    // Only needed if you don't have a real mail account for testing

    // create reusable transporter object using the default SMTP transport
    let transporter = nodemailer.createTransport({
        service: 'qq',
        port: 587,
        secure: true, // true for 465, false for other ports
        auth: {
            user: 'xxxxx@qq.com', // generated ethereal user
            pass: 'xxxxx' // generated ethereal password//aqhsdqsypiaobiah
        }
    });

    // send mail with defined transport object
    let info = await transporter.sendMail({
        from: 'ヾ事过情迁 <247770359@qq.com>', // sender address
        to: '247770359@qq.com', // list of receivers
        subject: '部署通知', // Subject line
        // text: 'Hello world?', // plain text body
        html: html // html body
    });

    console.log('Message sent: %s', info.messageId);
    // Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>

    // Preview only available when sending through an Ethereal account
    console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info));
    // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
}

module.exports = main

sh

#!/bin/bash
cd '/usr/projects/vue-front'
echo '先清除老代码'
git reset --hard origin/master
git clean -f
echo '拉去新代码'
git pull origin master
echo '安装node_modules'
npm install
echo '编译代码'
npm run build
echo '开始执行构建'
docker build -t vue-front:1.0 .
echo '停止旧容器&&删除旧容器'
docker stop vue-front-container
docker rm vue-front-container
echo '启动新容器'
docker container run -p 80:80 --name vue-front-container -d vue-front:1.0

Dockerfile

FROM nginx
LABEL name='yyx-vue-front'
LABEL version='0.0.1'
COPY ./dist /usr/share/nginx/html
COPY .vue.conf /etc/nginx/default.d
EXPOSE 80

conf

server  {
    listen 80;
    server_name 139.198.174.89;
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    location /api {
        proxy_pass http://139.198.174.89:3000;
    }
}