说说配置gitlab的webhook时踩过的坑

之前把公司的版本管理从svn换到了gitlab,用的各种舒适,但是,我的上线属于手动上线,先在本地git push到主分支,再到线上环境去运行git pull命令把代码拉取下来。

如何能做到自动部署呢?

答案是webhook。

有关如何配置webhook,推荐文章https://www.jianshu.com/p/00bc0323e83f

那么webhook是如何做到自动部署的呢?

我的理解是这样的,配置webhook时可以指定触发webhook的事件,我配置的事件是push事件,即当有push事件时,就会调取webhook。

如上,当有push事件时,就会调用服务器上的webhook.php,并post方式将安全令牌推送给php文件,这样,服务器就可以根据安全令牌来验证访问请求是否合法,如果合法,就执行pull命令。这样,你就不用自己登陆服务器执行git pull命令了,钩子会自动帮你执行。

其实,我们只需要做几件事情即可。

1)线上服务器配置钩子域名

2)编写钩子程序

3)测试

就这么简单。

1、配置钩子域名

我的环境用的lnmp平台,钩子访问地址为:http://ip:666/webhook.php

2、编写钩子

钩子项目下主要有三个文件:project_map.php,webhook.log,webhook.php

project_map.php:项目配置文件,主要配置了项目的access_token和项目地址

webhook.log:日志文件,很重要,可以帮你节约很多调试时间

webhook.php:钩子脚本。

1)project_map.php

如上,可以看到project_map.php里面配置了多个项目,这样,就可以多个项目都用这一个钩子,根据access_token来判断是哪个项目触发钩子。这就要求所有的项目access_token必须唯一。

2) webhook.php 

<?php
error_reporting(E_ALL);

// 根据token获取项目信息
function getProjectInfoByToken($token, $project_map)
{
    $return = [];
    foreach ($project_map as $map) {
        //var_dump($map);
        if ($map['access_token'] == $token) {
            $return = $map;
            break;
        }
    }

    return $return;
}

include_once './project_map.php';

$valid_ip = array('0.0.0.0'); //这里填你的gitlab服务器ip
$client_token = $_SERVER['HTTP_X_GITLAB_TOKEN'];    //这是gitlab服务器上配置的access_token
$client_ip = $_SERVER['REMOTE_ADDR'];

$file_name = 'webhook.log';
$data = ['time' => date('Y-m-d H:i:s'), 'client_ip' => $client_ip];
$data = array_merge($data, $_POST, $_GET, $_SERVER);

$project = getProjectInfoByToken($client_token, $project_map);
if (empty($project)) {
    $data['result'] = 'Token mismatch!';
    $log_data = json_encode($data) . PHP_EOL . PHP_EOL;
    var_dump(file_put_contents($file_name, $log_data, FILE_APPEND));

    die('Token mismatch!');
}

if (!in_array($client_ip, $valid_ip)) {
    $data['result'] = 'Ip mismatch!';
    $log_data = json_encode($data) . PHP_EOL . PHP_EOL;
    var_dump(file_put_contents($file_name, $log_data, FILE_APPEND));
    die('Ip mismatch!');
}

$root = $project['root'];
$command = "cd " . $root . "; git pull origin master 2>&1";
$data['command'] = $command;
exec($command, $output);    // 执行shell命令,需要服务器启用exec函数,默认是关闭的
//exec("cd /var/www/html/; git pull origin master 2>&1", $output);
var_dump($output); //这样可以用浏览器调试输出
$data['result'] = $output;
$log_data = json_encode($data) . PHP_EOL . PHP_EOL;
var_dump(file_put_contents($file_name, $log_data, FILE_APPEND));

?>

基本思路就是:判断token是否有效,判断发起请求的ip是否有效,根据token获取的项目信息,执行cd命令到项目根目录,然后执行git pull命令。

3)webhook.log

上面是钩子配置成功后出现的日志格式。

ok,架构搭建好了。测试一把吧。

然后,踩坑开始了。

3、坑坑坑

本来以为万事大吉的东西,结果各种报错啊,下面是踩坑填坑的过程

1)日志里的result返回null

result里面记录的是exec的结果,返回null,就是exec失败了。原来exec函数默认是禁用的,需要开启下,在php.ini中。

如上,从disable_functions中把exec去掉即可。

2)无权限访问.git

执行git命令需要访问项目下的.git(隐藏目录,ls -la可以看到),但是由于我们的钩子是php文件,相当于用php-fpm所配置的用户(我的是www)去访问.git目录。而.git目录是由root生成的,所以会导致权限不足。

于是用chown -R www:www .git修改.git属组

ok,解决了。

3)无权发起git pull命令

虽然webhook.php可以访问.git了,但是发起git pull命令的时候,仍然是以www用户去发起的。而我们线上服务器是以root,用https的方式发起的git请求,www用户无权限。

去网上查了很多资料,得到如下解决方法:以www用户发起git请求,并记录用户密码

i、sudo -uwww git config --global credential.helper store: 记住用户名密码

ii、sudo -uwww git pull origin master

第一次拉取的时候,要求输入用户名密码,输入正确之后,下次再执行就不用输入用户名密码了。

4)无权修改文件

再次测试钩子,终于可以啦!钩子以www用户向gitlab服务器发起了git pull请求!!!

但是,我在拉取有的项目的时候,日志报错有的文件无权修改。

原来是更新的时候会先把那个文件删除,但是那个文件刚好是root权限的,无法删除。

所以需要在项目下执行chown -R www:www *,让项目下的所有文件都是www用户的。

ok,至此,钩子运行成功了。

总结

目前我的钩子脚本还有几个问题需要优化:

1)钩子没有区分推送的分支,哪怕推送dev分值,也会在线上执行git pull。其实gitlab服务器调用钩子的时候,是有很多信息可以获取的,如下:

2)以www用户运行会对服务器项目要求比较高,一旦项目下有的文件不是www用户的,就会导致脚本运行失败。有看到文章说用ssh的方式pull可以解决此类问题,具体没有试过。

猜你喜欢

转载自blog.csdn.net/maquealone/article/details/83061048