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