1. 程式人生 > >說說配置gitlab的webhook時踩過的坑

說說配置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可以解決此類問題,具體沒有試過。