先後端項目在服務端持續集成部署【Docker自動化部署】

前言

自動化服務端持續集成部署的好處有哪些?

當咱們修改一個需求完成後,將最新的代碼push到github的時候,咱們線上的版本會自動化完成拉取代碼,打包構建,重啓服務等流程。html

經過這種技術咱們能夠將本地代碼一秒完成線上項目的部署與重啓。再也不須要大量的人力去作上線部署重複的工做。前端

前端爲何要使用自動化服務端持續集成部署?

衆所周知,前端的需求常常發生變動及微小的調整,每一次上線須要經歷複雜的固定化的流程:修改代碼—代碼檢測—功能實現測試—構建項目—上傳構建完成的項目包—線上測試。vue

稍微大一點的項目更是涉及龐大的用戶人羣,稍有不慎,就將釀成上線慘案。一到上線日,忙得雞飛狗跳最後上線的代碼還有可能有着種種出乎本身意料的bug。給團隊和項目帶來不可估量的損失。node

自動化服務端持續集成部署就是將以自動化的方式將之前須要人工一步一步實現的上線流程,經過代碼自動化來實現,達到項目上線精準無偏差的地步。linux

自動化服務端持續集成部署所使用的技術點

  • github
  • github webhook
  • docker
  • nginx
  • Linux

自動化服務端持續集成部署 實現思路

1574956828126

完整代碼地址

1.創建後端github代碼倉庫並在setting中新增webhook

已有項目:vue前端項目(font);後端項目:(back)nginx

1574173350736

2. 配置Linux 系統中項目的運行環境

2.1 配置基礎環境 CentOS 7.4

#升級系統內全部的軟件與內核
#若是系統內已經有項目在跑的就不要使用這句命令,很高的風險會影響以前的項目環境
yum update

#安裝git
yum install git -y

#建立本地git項目下載存放的目錄
mkdir /usr/projects

#使用github的ssh 生成ssh免密用戶公鑰--後面兩步直接回車設置爲默認空
# -t指定加密算法爲rsa  -b指定大小爲4096字節  -c指定github帳號郵箱地址
ssh-keygen -t rsa -b 4096 -C "1403029829@qq.com"

#查看已經生成的公鑰地址
cat /root/.ssh/id_rsa.pub

複製代碼

2.2 拉取項目代碼到服務器上

#找到項目根目錄
cd /usr/projects/

#克隆後端代碼
#注意:clone的時候會提醒是否確認克隆,必定要輸入yes
git clone git@github.com:cometang/back.git

#克隆前端代碼
git clone git@github.com:cometang/font.git

複製代碼

2.3 安裝項目代碼運行環境

#安裝nvm node 版本管理工具
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash

#將nvm配置到服務器環境變量中
./root/.bashrc

#nvm安裝最新的node穩定版
nvm install stable

#nvm安裝node版本 11.11.0 【我本地開發所使用的node版本】
nvm install 11.11.0

#安裝nrm切換npm的安裝源,通常國內服務器切換爲淘寶鏡像
npm install nrm -g



#nvm 命令使用含義
#查看本地已經下載的全部node版本
nvm list 
#切換node版本
nvm use 11.11.0
#查看node版本
node -v
#查看npm版本
npm -v

複製代碼

2.4 配置aliyun服務器的安全組策略

開放後端端口:'3000'  前端端口:'8080'    webhook端口:'4000'
複製代碼

1574178877389

2.5 配置docker運行環境

docker倉庫中有各類軟件的鏡像地址git

#安裝centos的yum-utils工具包
yum install yum-utils device-mapper-data lvm2

#設置docker的yum安裝源爲阿里雲的地址
yum-config-manager \
  --add-repo \
  https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  
#安裝社區免費版docker
yum install -y docker-ce docker-ce-cli containerd.io

#查看docker版本
docker -v

#設置docker軟件源安裝地址爲阿里雲地址
#新建docker文件夾
mkdir -p /etc/docker

#設置安裝源地址爲阿里雲的地址[把下面的josn對象文本寫入到json文件中]
tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors":["http://fwvjnv59.mirror.aliyuncs.com"]
}
EOF

#重置全部修改過的配置文件
systemctl daemon-reload
systemctl restart docker

#切換源爲淘寶鏡像地址
nrm use taobao 

複製代碼

2.6 開始運行項目

#進入back項目根目錄
cd back
npm install 
#啓動服務
npm start 
複製代碼

2.6.1 在本地瀏覽器訪問後端接口地址

個人接口地址:http://47.99.192.199:3000/api/usersgithub

1574178979135

2.6.2 運行前端項目

新創建一個鏈接,不要關閉後端服務,注意修改前端項目中的接口地址爲服務器公網ipweb

#進入font項目根目錄中
cd /usr/projects/font
npm install 
npm run serve

複製代碼

在本地瀏覽器中訪問前端項目:IP地址:8080算法

1574179434668

3.配置webhook

3.1配置github中前端以及後端項目的webhook服務與鏈接密碼

先後端兩個項目保持一致

注意:鏈接密碼的配置必定要與webhook項目中的密碼保持一致

1574261285740

3.2新建webhook的 github倉庫並提交

1574262329749

3.3將webhook 代碼克隆到服務器中 路徑放在與 先後端代碼同級根目錄下

cd /usr/projects/
git clone git@github.com:cometang/webhook.git
cd webhook
npm install 
node webhook
複製代碼

1574262970197

3.4 測試webhook是否可使用

修改前端後端項目代碼後提交代碼到github 查看服務器是否會提醒代碼更新

//webhook.js 
//測試鏈接github是否可以檢測到代碼更新並向webhook服務發送請求並【非完整版代碼】
let http = require('http');
let server = http.createServer(function (req, res) {
    //判斷github發送的是否是post 是否是webhook發送的請求
    console.log('檢測到前端後端代碼更新,github發來的請求信息以下:')
    console.log(req.method,req.url);
    if (req.method == 'POST' && req.url == '/webhook') {
        //設置github請求的請求頭,設置返回數據的格式爲json
        res.setHeader('Content-Type', 'application/json');
        //返回通知github請求已經成功
        res.end(JSON.stringify({ok:true}));
    } else {
        res.end('NOT Found');
    }
})
server.listen(4000, () => {
    console.log('webhook服務已經在4000端口啓動');
})
複製代碼

當前端或者後端代碼提交後webhook收到github的post請求--至關於消息推送

1574264924352

3.5 安裝npm2插件讓webhook持久監控

關掉服務器鏈接以後webhook服務會自動斷開,爲了使得webhook持久化檢測,安裝pm2,當node服務斷開以後,pm2會自動重啓node服務

npm install pm2 -g
複製代碼

3.5.1 修改webhook代碼中的啓動服務命令

修改完成後從新提交代碼到github,服務器從新將webhook代碼pull便可

//package.json
 "scripts": {
    "start":"pm2 start ./webhook.js --name webhook --watch",
    "stop":"pm2 stop webhook"
  },
複製代碼

在服務器webhook根目錄pull完代碼以後使用 npm start 實現經過pm2從新啓動webhook服務

npm start 
複製代碼

1574433631451

3.6 測試pm2與webhook服務

1.查看pm2 日誌【能夠查看到全部關於提交的先後端兩個項目的代碼日誌】

pm2 logs
複製代碼

1574433784580

2.提交先後端兩個項目的代碼到github,測試是否可以檢測到提交

1574434003996

4 重寫webhook.js 完成對github請求的身份驗證

//webhook.js
let http = require('http');
let cryto = require('crypto');
let SECRET = '123456';  //與在先後端項目github中設置的Secret相同
//生成簽名算法
//根據SECRET字符串使用哈希算法生成十六進制的新的字符串
function sign(body) {
    return `sha1=` + cryto.createHmac('sha1', SECRET).update(body).digest('hex')
}
let server = http.createServer(function (req, res) {
    //判斷github發送的是否是post 是否是webhook發送的請求
    console.log('檢測到前端後端代碼更新,github發來的請求信息以下:')
    console.log(req.method, req.url);
    if (req.method == 'POST' && req.url == '/webhook') {
        //拿到github傳遞過來的參數--對請求的github進行簡單的驗證
        let buffers = []
        req.on('data', function (buffer) {
            buffers.push(buffer)
        })
        req.on('end', function (buffer) {
            let body = Buffer.concat(buffers)
            //github傳的值請求事件類型:push事件
            let event = req.headers['x-github-event']
            //github傳遞了請求體body,同時傳遞了簽名,須要驗證簽名是否正確
            let signatrue = req.headers['x-hub-signatrue']
            if (signatrue !== sign(body)) {
                //sign不相等 直接返回錯誤
                return res.end('Not Allowed')
            }
            //sign相同 執行贊成請求
            //設置github請求的請求頭,設置返回數據的格式爲json
            res.setHeader('Content-Type', 'application/json');
            //返回通知github請求已經成功
            res.end(JSON.stringify({ ok: true }));
        })
    } else {
        res.end('NOT Found');
    }
})

server.listen(4000, () => {
    console.log('webhook服務已經在4000端口啓動');
})
複製代碼

5.快速部署腳本編寫

在webhook項目的跟目錄下寫項目的快速部署 快速集成的腳本

5.1 後端項目構建

  • 在webhook項目的跟目錄下 新建back.sh 後端項目的快速部署腳步
#!/bin/bash 
#後端項目快速構建腳本
#後端項目路徑
WORK_PATH = '/usr/projects/back'
cd $WORK_PATH
echo "先清除老代碼"
git reset --hard origin/master
git clean -f
echo "拉取新代碼"
git pull origin master
echo "開始執行構建後端項目:back爲docker鏡像名稱 1.0爲版本號"
docker build -t back1.0 .
echo "中止舊容器 並刪除舊容器"
docker stop back-container
docer rm back-container
echo "啓動新容器"
docker container run -p 3000:3000 --name back-container -d back1.0
複製代碼
  • 在後端項目back 跟目錄下新建文件:Dockerfile 以及 dockerignore

對docker進行配置,生成項目鏡像

注意:在back項目中的gitignore裏面不要寫 Dockerfile dockerignore

FROM node
LABEL name = "back" LABEL version ="1.0" COPY . /app WORKDIR /app RUN npm install EXPOSE 3000
CMD npm start 複製代碼
  • 性能優化,去掉docker製做鏡像時不須要的文件,dockerignore
.gitignore
Dockerfile
node_moudules
複製代碼
  • 提交後端代碼 與webhook代碼,並在服務器中各自項目跟目錄進行拉取

5.2測試docker自動構建

拉取完成後端代碼和webhook最新代碼後,在服務器webhook項目根文件夾下執行命令查看是否可以成功構建:sh back.sh

sh back.sh
複製代碼

注意:這裏有時會出現docker 服務沒有找到的提示

#提示報錯信息
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

#解決方案:在webhook項目跟目錄下運行一下命令,而後從新運行 sh back.sh便可:
sudo systemctl start docker
複製代碼

出現最後的下載構建的列表 表示啓動docker服務成功,開始構建

1574691720015

驗證是否docker 服務是否成功拉取最新代碼並完成自啓動

curl http://localhost:3000/api/users
複製代碼

圖片中的報錯信息是由於沒有新的版本能夠拉取,因此報錯,可略過

1574695148206

第二次使用 sh back.sh 就能夠實現完整的後端服務自動化構建,自動化部署的過程

1574777210025

新容器啓動後經過瀏覽器直接能夠訪問更新的接口地址:IP:端口/接口名,個人接口地址爲:http://47.99.192.199:3000/api/users;亂碼的緣由是由於json數據直接放到網頁轉碼的問題,用前端接口請求用在渲染到網頁就沒有這種亂碼了。自此後端項目自動化持續集成部署已經所有完成。

1574777277009

5.3 部署前端項目自動構建腳本

後端項目已經部署完成,下面部署稍微複雜的前端項目。

5.3.1 在webhook項目跟目錄下新建font.sh文件做爲前端項目的腳本

#!/bin/bash 
WORK_PATH='/usr/projects/font'
cd $WORK_PATH
echo "先清除老代碼"
git reset --hard origin/master
git clean -f
echo "拉取新代碼"
git pull origin master
echo "編譯build"
npm run build
echo "開始執行構建後端項目:back爲docker鏡像名稱 1.0爲版本號"
docker build -t font:1.0 .
echo "中止舊容器 並刪除舊容器"
docker stop font-container
docker rm font-container
echo "啓動新容器"
docker container run -p 80:80 --name font-container -d font:1.0
複製代碼

5.3.2 在前端項目font根目錄下新建Dockerfile文件

From nginx
LABEL name = "font" LABEL version ="1.0" COPY ./dist /usr/share/nginx/html COPY ./font.conf /etc/nginx/conf.d EXPOSE 80

複製代碼

5.3.3 在前端項目font跟目錄下新建 font.config 文件,用來配置服務器的nginx服務

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

5.3.4 前端服務須要nginx服務器支撐,在服務器中安裝nginx

yum install nginx -y
複製代碼

5.3.5 提交webhook與最新的前端font代碼到github

注意:前端代碼的.gitignore文件不要排除Dockerfile .Dockerfileignore文件

5.3.6 在服務器的webhook項目跟目錄下拉取webhook最新代碼,而後運行 sh font.sh

在服務器webhook目錄下執行 sh font.sh後會自動去拉取前端最新代碼,並自動構建bulid

git pull origin marster
複製代碼
sh font.sh
複製代碼

1574783443299

5.3.7 訪問前端地址 ip:80

1574953705653

5.3.8 修改webhook.js文件,判斷是前端項目請求仍是後端項目請求,並建立子進程自動執行對應項目的sh腳本文件,最終完成自動拉取,構建流程。

完成webhook.js代碼後提交到服務器,在服務器webhook項目中pull最新的webhook配置代碼

//webhook.js 完整版代碼
let http = require('http');
let cryto = require('crypto');
let { spawn } = require('child_process'); //開啓部署的子進程

let SECRET = '123456';  //與在先後端項目github中設置的Secret相同
//生成簽名算法
//根據SECRET字符串使用哈希算法生成十六進制的新的字符串
function sign(body) {
    return `sha1=` + cryto.createHmac('sha1', SECRET).update(body).digest('hex')
}
let server = http.createServer(function (req, res) {
    //判斷github發送的是否是post 是否是webhook發送的請求
    console.log('檢測到前端後端代碼更新,github發來的請求信息以下:')
    console.log('req----hedaers')
    console.log(req.headers['x-github-event'])
    console.log(req.method, req.url);
    if (req.method == 'POST' && req.url == '/webhook') {
        //拿到github傳遞過來的參數--對請求的github進行簡單的驗證
        let buffers = [];
        req.on('data', function (buffer) {
            buffers.push(buffer);
        })
        req.on('end', function (buffer) {
            let body = Buffer.concat(buffers);
            //github傳的值請求事件類型:push事件
            let event = req.headers['x-github-event'];
            //github傳遞了請求體body,同時傳遞了簽名,須要驗證簽名是否正確
            let signatrue = req.headers['x-hub-signature'];
            if (signatrue !== sign(body)) {
                //sign不相等 直接返回錯誤
                return res.end('Not Allowed');
            }
            //sign相同 執行贊成請求
            //設置github請求的請求頭,設置返回數據的格式爲json
            res.setHeader('Content-Type', 'application/json');
            //返回通知github請求已經成功
            res.end(JSON.stringify({ ok: true }));

            //自動化部署
            if (event == 'push') {
                let payload = JSON.parse(body);
                let name = './' + payload.repository.name + '.sh'
                //開啓子進程自動執行對應的sh部署腳本,提交back就執行 sh back.sh 的子進程
                let child = spawn('sh', [name])
                //打印操做日誌
                //每當子進程有日誌輸入的時候,就拋出一個日誌,最後一次性輸出整個更改日誌
                let buffers = []
                child.stdout.on('data', function (buffer) {
                    console.log('啓動子進程')
                    buffers.push(buffer)
                })
                child.stdout.on('end', function (buffer) {
                    let log = Buffer.concat(buffers)
                    console.log(log)
                })
            }
        })
    } else {
        res.end('NOT Found');
    }
})

server.listen(4000, () => {
    console.log('webhook服務已經在4000端口啓動');
})

複製代碼

6刪除目前已經存在的全部舊容器,並重啓 pm2 logs 查看日誌文件,完成總體測試

注意:命令中的單引號及單引號中的內容須要更換爲對應的container(容器名)

docker container ps
docker container rm 'container_name'  'container_name' -f 
複製代碼

1574954368167

6.1 重啓服務,打開pm2日誌

npm run stop
npm run start 
pm2 logs
複製代碼

1574954700353

6.2 修改前端及後端代碼並提交到github,前端服務,後端服務都會自動進行拉取,構建。

服務器自動啓動子進程對相應項目完成構建,並變動輸出日誌

1574954959050

7. 上面6個步驟完成以後 可斷開xshell 鏈接

每次的本地代碼提交,對應項目的線上版本都會自動完成拉取,構建,提交最新代碼到github後,兩分鐘左右線上版本便可完成更新

8.附加配置:當新代碼推送到服務器後,經過發送email的方式來提醒項目已經更新

注意:郵件方式暫時不寫,後續有時間再貼代碼,具體實現思路:在webhook中引入 sendMail ,在構建成功以後發送通知郵件到指定的郵箱地址便可。

相關文章
相關標籤/搜索