之前把公司的版本管理从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可以解决此类问题,具体没有试过。