webhook+shell脚本实现代码自动部署

由于线上分支特别多,之前打算使用jenkins来做自动部署,但是看了不少教程,写的都不是很好理解,所以决定自己写脚本

1, 编写python脚本来做webhook,我这里使用的是python3.6

pip3 install flask    //* 安装flask模块

下面是实现监听gitlab的脚本,由于这个轮子是别人造好的,我直接拿过来用了。原文地址

from flask import Flask
from flask import request, jsonify, abort
import subprocess

app = Flask(__name__)

WEBHOOK_VERIFY_TOKEN = "test_token"

@app.route('/webhook', methods=['GET', 'POST'])    //* 监听的路径,可按照项目定义多个
def webhook():
    if request.method == 'GET':
        verify_token = request.headers.get('X-Gitlab-Token')
        if verify_token == WEBHOOK_VERIFY_TOKEN:   //* 验证密钥,如果不对直接报错退出
            return jsonify({'status': 'success'}), 200
        else:
            return jsonify({'status': 'bad token'}), 401

    elif request.method == 'POST':
        verify_token = request.headers.get('X-Gitlab-Token')
        if verify_token == WEBHOOK_VERIFY_TOKEN:
            # 定义需要执行的命令,我这边使用salt来批量管理
            retcode = subprocess.call("echo '触发成功!'", shell=True)
            if retcode == 0:
                return jsonify({'status': 'success'}), 200
            else:
                return jsonify({'status': 'git pull error'}), 503
        else:
            return jsonify({'status': 'bad token'}), 401

    else:
        abort(400)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port='5050')    //* 监听地址和对外开放的端口

使用systemd管理这个flask程序

[[email protected] ~]# cat /etc/init.d/webhook
#!/usr/bin/env bash

/usr/bin/python3 /home/stark/app.py &

[[email protected] ~]# cat /usr/lib/systemd/system/webhook.service
[Unit]
Description=Webhook server daemon

[Service]
Type=forking
ExecStart=/etc/init.d/webhook
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

管理webhook程序

systemctl daemon-reload
systemctl restart webhook
systemctl enable webhook

手动验证是否可以正常使用
Snipaste_2020-01-09_20-15-06.png

[[email protected] ~]# curl -X POST -H 'X-Gitlab-Token: test_token' http://192.168.131.128:5050/webhook
{"status":"success"}    //* 使用任意一台可以访问到他的主机进行测试

Snipaste_2020-01-09_20-19-09.png
可以看到带上token访问该网页之后已经触发了网页里定义的命令
gitlab配置webhook
Snipaste_2020-01-09_20-27-19.png
Snipaste_2020-01-09_20-24-35.png

2, 既然我们已经可以做到push后自动触发命令了,那么下面就需要写脚本对git log和本地的log进行对比了

下面脚本中PRIVATE-TOKEN获取方式
Snipaste_2020-01-09_23-06-46.png
Snipaste_2020-01-09_23-07-13.png
Snipaste_2020-01-09_23-08-54.png
Snipaste_2020-01-09_23-10-14.png

#!/usr/bin/env bash

for i in epel-release yajl git expect sshpass
  do
    rpm -q $i &> /dev/null || yum install -y $i
  done

for dist_path in "项目路径1" "项目路径2" "项目路径N";do
    # 过滤出来项目地址,然后通过gitlab api去找对应的id
    Git_url=$(awk '/url/ {print $NF}' ${dist_path}.git/config |grep -o "xxxxgit.*")
    # 找到该项目对应的分支
    Git_branch=$(cat ${dist_path}.git/config |awk -F'/' '/merge/ {print $NF}')
    # 找到项目id
    Project_id=$(curl -H "PRIVATE-TOKEN:test_token" -s "https://xxxxgit.com/api/v3/projects" |json_reformat |grep -B 10 ${Git_url} |grep '"id"' |grep -o '[0-9]\{1,\}')
    # 查看项目id的信息,也就是git log信息
    git_log=$(curl -H "PRIVATE-TOKEN:test_token" -s "https://xxxxgit.com/api/v3/projects/${Project_id}/repository/commits/${Git_branch}" |awk -F'"' '{print $4}')
    # 将服务器端的git log和本地的git log信息进行对比,如果不一致就在本地拉代码
    dist_log=$(cd ${dist_path} ; git log |head -1 |awk '{print $NF}')
    if [[ $dist_log != $git_log ]]
      then
        cd ${dist_path}
        /usr/bin/expect <<-EOF > /tmp/result.log    # /将拉代码结果保存到这个路径,方便后续发送通知
        set timeout 90
        spawn git pull
        expect "Username"
        send "test\r"
        expect "Password"
        send "1234567890\r"
        expect eof
        EOF
            # 拉取成功之后发送通知
        mail -s "${HOSTNAME} 更新了 ${dist_path}的代码" [email protected] < /tmp/result.log
    fi
  done

3, 将上面的脚本放在项目所在的服务器,然后通过触发之后执行的命令来执行脚本就可以了