使用GitHub的Webhooks實現程式碼的自動部署

xingkong12138發表於2020-10-20

前段時間搞了個小專案放到GitHub上面,剛開始的時候,在本地push後,然後在到伺服器上面執行pull拉取,還不覺得有多麻煩,但是後面更新比較頻繁的時候,就覺得十分的繁瑣,所以我就使用GitHub自帶的Webhooks來實現自動部署。

使用GitHub自帶的Webhooks的自動部署邏輯。

- 本地修改程式碼,然後push提交
- github傳送請求給你的網站伺服器
- 網站伺服器收到更新請求,執行自動部署指令碼
- 自動部署指令碼執行程式碼拉取等動作完成網站的更新部署

這裡的鉤子就是被請求的URL,如果你只有一個專案的話,你就可以把鉤子直接寫到你的專案裡面,如果有多個專案要管理的話,最好是單獨建一個專案去管理,這樣比較好管理一些。

我只有一個專案所以,我就寫到專案裡面的,我用的是laravel框架。

這裡我為什麼要自己寫一個執行shell的方法呢?因為shell_exec/exec/system/passthru這些函式對錯誤資訊太不友好了。我大部分時間都耗在了排查錯誤上面,而且這些函式很有可能是被禁用掉的。

//github 專案鉤子
public function hook(Request $request){
    //webhook中設定的Secret
    $secret_token = env("WEBHOOK_SECRET_TOKEN");

    //驗證簽名
    $hub_signature = $request->header('X-Hub-Signature',"");//這個是GitHub返回來的簽名
    if(empty($hub_signature)){
        Log::info("無效的鉤子請求");
        return "";
    }
    //這是簽名演算法
    $signature = "sha1=".hash_hmac('sha1', $request->getContent(),$secret_token);
    //$request->getContent()是請求內容
    //如果你用的不是laravel,還可以使用file_get_contents("php://input")獲取

    if(strcmp($signature,$hub_signature) == 0){
        //簽名驗證成功,執行shell命令
        $webPath = "/home/www/xxxxx";//生產環境下專案的目錄地址
        $cmd = "cd $webPath && git pull origin master";//shell 命令
        $res = $this->doShell($cmd);
        Log::info("執行shell命令返回資料:".json_encode($res));

        //返回資訊,給GitHub記錄
        print_r(json_encode($res));
    }else{
        Log::info("鉤子請求籤名驗證失敗");
    }
}

//執行shell命令
private function doShell($cmd, $cwd = null) {
    $descriptorspec = array(
        0 => array("pipe", "r"), // stdin
        1 => array("pipe", "w"), // stdout
        2 => array("pipe", "w"), // stderr
    );
    $proc = proc_open($cmd, $descriptorspec, $pipes, $cwd, null);
    // $proc為false,表明命令執行失敗
    if ($proc == false) {
        Log::info("shell 命令:".$cmd." 執行失敗");
        return false;
    } else {
        $stdout = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        $stderr = stream_get_contents($pipes[2]);
        fclose($pipes[2]);
        $status = proc_close($proc); // 釋放proc
    }
    $data = array(
        'stdout' => $stdout, // 標準輸出
        'stderr' => $stderr, // 錯誤輸出
        'retval' => $status, // 返回值
    );

    return $data;
}

GitHub官方建議不要把Secret放到程式碼裡面,官方是建議把Secret放到環境變數裡面

把這個程式碼放到任意一個控制器中就行,然後配置好路由就行。該方法是POST請求,所以你不能驗證CSRF-TOKEN,我是直接把這個路由放到api路由下面的

如果你不只是拉取程式碼,如還需要打包等,就可以先寫一個shell指令碼,然後在pull後執行指令碼,cd $webPath && git pull origin master && /bin/bash dabao.sh

如果發現推送返回以下的錯誤資訊:

Host key verification failed.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.

首先判斷你執行指令碼的使用者(我的是www使用者)的ssh-key是否加到了GitHub裡面,如果沒有就生成加入。

//為www使用者生成ssh-key
sudo -Hu www ssh-keygen -t rsa -C "github賬號的郵箱"# 請選擇 "no passphrase",一直回車下去
//我是用的root使用者給www使用者生成ssh-key的,如果你是登入的就是www使用者,執行下面的命令就行
ssh-keygen -t rsa -C "github賬號的郵箱"

-Hu www 命令: 
-u 代表切換到哪一個使用者,這裡說的是www 
-H 代表切換HOME環境變數的值,也就是password檔案中www使用者對應的home目錄

百度上面基本就是說沒有配置ssh-key,但是我配置了的,還是會返回這個錯誤。

這個是因為你執行指令碼的使用者,是第一次連線GitHub,所以會彈出一個是否把遠端地址加入到konw_host,我們執行的shell是回車操作,但是直接回車,則預設沒有許可權寫入,必須輸入yes才能正確寫入 konw_host

解決方法:

  1. 1.我們可以在ssh_config配置檔案中降低安全配置級別,不做提示,直接加入到konw_host

     #開啟ssh_config
     /etc/ssh/ssh_config
     #找到:
     StrictHostKeyChecking ask
     修改為:
     StrictHostKeyChecking no

    但是我不建議這樣做。或者當加入到konw_host後,你再把安全配置改回來。

  2. 2.登陸你執行指令碼的使用者,然後使用這個使用者去專案庫裡面拉取一次,然後你輸入yes就行了。

更多文章參考我的部落格

本作品採用《CC 協議》,轉載必須註明作者和本文連結
我的部落格:www.zhangkaixing.com

相關文章