nginx + lua + webhook 自動化部署(非阻塞的執行系統命令)

如果將 vuepress build 之後的靜態網站部署在自己的服務器上,這裏有一個自動化方案,思路如下:

  1. 在 linux 服務器上安裝 node、yarn、git、nginx 軟件
  2. 編寫一個腳本:用於更新 git 倉庫項目,然後 build,再複製到 nginx 配置訪問的文件位置
  3. 使用 nginx 來作爲靜態網站的容器
  4. 使用 nginx lua 模塊提供一個調用腳本的入口
  5. 在 github 上填寫 webhook 地址爲,調用腳本的入口地址
  6. 達到:上傳到 github 後,觸發 webhook 事件,服務器自動構建併發布新筆記內容的功能

#安裝 OpenResty + lua 相關依賴包

安裝 OpenResty 和 lua 入門請參考本文章(opens new window)

請按該文章完成安裝後,並完成 hello world lua 的測試,再繼續下面的步驟

#nginx 執行 shell 腳本

本節內容參考文章(opens new window)

要在 nginx 下執行 shell 腳本,主要使用以下兩個軟件:

  • lua-resty-shell 模塊
  • sockproc

lua-resty-shell 通過 sockproc 去執行 shell 命令,並返回執行結果

#安裝 sockproc

隨意把 sockproc 安裝在哪裏,按你自己的習慣,只要能運行上就行

git clone https://github.com/juce/sockproc
cd sockproc
make
./sockproc /tmp/shell.sock
chmod 0666 /tmp/shell.sock
Copied!
1
2
3
4
5

sockproc 是一個服務器程序,偵測 unix socket 或者 tcp socket , 並把收到的命令,傳遞給子進程執行,執行完畢後,把結果返回給客戶端,,我們就讓 sockproc 偵測 /tmp/shell.sock 的套接口有沒有數據到來。

#安裝 lua-resty-shell 模塊

它是一個很小的庫,配合 openresty 使用,目的是提供類似於 os.execute 或 io.popen 的功能, 唯一區別它是非阻塞的,也就是說即使需要耗時很久的命令,你也可以使用它

# 同樣,在任意目錄下,這裏我們需要拿到倉庫裏面的 shell.lua 文件而已
git clone https://github.com/juce/lua-resty-shell
cd lua-resty-shell
# 把 shell.lua 文件複製到 openResty 中配置的 lualib 目錄中
# 如果是按照上面教程安裝的,那麼我們的 lualib路徑就如下
mkdir /usr/servers/lualib/resty/
cp lib/resty/shell.lua /usr/servers/lualib/resty/ 
Copied!
1
2
3
4
5
6
7

#測試執行 shell 腳本

創建一個 lua 腳本文件

vim /usr/servers/nginx/conf/test.lua

local shell = require "resty.shell"
local args = {
    socket = "unix:/tmp/shell.sock",  -- 這是第一步的 unxi socket
}
local status, out, err = shell.execute("ls", args)  -- ls 是想調用的命令,
ngx.header.content_type = "text/plain"
ngx.say("Result:\n" .. out)                    -- 命令輸出結果
Copied!
1
2
3
4
5
6
7
8
9

修改 nginx 配置

vim /usr/servers/nginx/conf/lua.conf 

server {  
    listen       9300;  
    server_name  _;
    
    location /lua {  
      default_type 'text/html';  
      # content_by_lua 'ngx.say("hello world")';  
      content_by_lua_file /usr/servers/nginx/conf/test.lua; 
    } 
}  
Copied!
1
2
3
4
5
6
7
8
9
10
11
12

重新加載 nginx 配置文件後,訪問 9300 端口,就可以看到 ls 命令執行後輸出的內容了

#編寫接受 webhook 的邏輯 lua 腳本

我們的這裏的思路簡單一點:

  1. 提供一個訪問地址,接受 webhook 請求,請求的時候需要攜帶一個 token 參數
  2. 在 lua 腳本中獲取這個 token 參數,並校驗是否自己設置的,如果不是,則直接丟棄這個請求
  3. 執行筆記構建部署腳本
-- 拿一個地址來說明:http://eshop-cache03/lua?method=hello&productId=1
-- 獲取問號後面的參數列表
local uri_args = ngx.req.get_uri_args()
-- 獲取參數
local token = uri_args["token"]
local checkToken = "123456xxx"

-- 如果沒有提供 token 則輸出一個 err 信息
if not token then  
    ngx.say("request error :", err)  
    return  
end

-- 判斷是否與 token 相等
if( token != checkToken ) then
    ngx.say("request error :", err)  
    return  
end

-- 校驗通過後,執行腳本
local shell = require "resty.shell"
local args = {
    socket = "unix:/tmp/shell.sock", 
}
local status, out, err = shell.execute("sh /xx/build.sh", args)  
ngx.header.content_type = "text/plain"
ngx.say("Result:\n" .. out) 
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

那麼這個 /xx/build.sh 內容經過測試有如下的特性

#!/bin/bash
ll
touch /usr/servers/nginx/conf/testxxxxxxxxx
echo "Hello World !"
Copied!
1
2
3
4

命令會正常執行,但是隻會輸出 echo 打印的信息給調用處。

前面驗證了我們方案中的關鍵部分 lua 執行 sh 腳本,下面就開始真正實現自動構建

#vuepress 自動構建

步驟如下:

#clone 項目

把要自動部署構建的項目 clone 下來。我們的構建和發佈目錄都在 /usr/servers/notework/ 目錄下展開

mkdir -p /usr/servers/notework/gitrepo
cd /usr/servers/notework/gitrepo
## 在該路徑下存放我們的筆記項目
git clone https://github.com/zq99299/mysql-tutorial.git
git clone https://github.com/zq99299/note-book.git
git clone https://github.com/zq99299/java-tutorial.git
git clone https://github.com/zq99299/linux-tutorial.git
git clone https://github.com/zq99299/mq-tutorial.git
git clone https://github.com/zq99299/dsalg-tutorial.git
Copied!
1
2
3
4
5
6
7
8
9

#編寫構建腳本

這是一個公共的構建腳本,在調用該腳本的時候,需要把項目名傳遞進來

/usr/servers/notework/build.sh

#!/bin/sh
# 一個工作目錄,用於存放倉庫、打包後部署目錄
noteworkDir=/usr/servers/notework
# 筆記倉庫名稱,每個筆記一個,調用腳本時,將項目名傳遞進來
noteName=$1

# 更新項目,並構建
cd $noteworkDir/gitrepo/$noteName
git pull
yarn install
yarn docs:build

# 刪除構建好的包,並用新的覆蓋
rm -rf $noteworkDir/release/$noteName
mkdir -p $noteworkDir/release/$noteName
mv build/.vuepress/dist/* $noteworkDir/release/$noteName
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

配置 nginx 訪問構建好的靜態網站

server {
    listen       80;
    server_name  localhost;

    charset utf-8; 

    #access_log  logs/host.access.log  main;

    location ^~ /linux-tutorial {
        # root   /usr/servers/notework/release/linux-tutorial/;
        alias /usr/servers/notework/release/linux-tutorial/;
    }
}
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13

需要說明下的是:這裏的 location 的配置,這個需要看你的 docs/.vuepress/config.js 中 bash 的設置,由於要部署到 githubio,這裏使用了一個前綴路徑。

現在,可以通過 http://你的主機 IP/linux-tutorial/ 訪問到這個項目了。

#編寫 lua 腳本自動構建邏輯

前面寫了一部分,這裏做一點修改

/usr/servers/nginx/conf/noteBuild.lua

-- 拿一個地址來說明:http://eshop-cache03/lua?method=hello&productId=1
-- 獲取問號後面的參數列表
local uri_args = ngx.req.get_uri_args()
-- 獲取參數
local token = uri_args["token"]
-- 這裏增加一個 noteName 的參數值,需要 webhook 中回調,來構建不同的項目
local noteName = uri_args["noteName"] 
local checkToken = "123456xxx"

-- 如果沒有提供 token 則輸出一個 err 信息
if not token then  
    ngx.say("request error token is null:", err)  
    return  
end

-- 判斷是否與 token 相等
if( token ~= checkToken ) then
    ngx.say("request error token mismatching:", err)  
    return  
end

-- 檢查當前筆記是否支持自動構建
if not noteName then  
    ngx.say("request error noteName is null:", err)  
    return  
end

local supportNotes = {"linux-tutorial","mysql-tutorial"}

function  isInTable(value,list)
   if not list then
      return false   
   end 
   for k, v in pairs(list) do
	if v == value then
	  return true
	end
   end
end 
if( not isInTable(noteName,supportNotes) ) then
    ngx.say("request error noteName mismatching:", err)  
    return  
end

-- 校驗通過後,執行腳本
local shell = require "resty.shell"
local args = {
    socket = "unix:/tmp/shell.sock", 
	timeout = 120000
}
-- 不加 nohub 15秒回執行超時,加了之後,貌似受上面的 timeout 控制,不明白這一塊是啥原因
local exeStr = "nohub sh /usr/servers/notework/build.sh "..noteName
local status, out, err = shell.execute(exeStr, args)
ngx.header.content_type = "text/plain"

if out then
 ngx.say("Result:\n" .. out) 
 return
end

if err then
 ngx.say("Result:\n" .. err) 
 return
end
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

/usr/servers/nginx/conf/noteBuild.conf

server {
    listen       80;  
    server_name  _;
    charset utf-8;  # 中文不亂碼
    
    location /lua {  
      default_type 'text/html';  
      # content_by_lua 'ngx.say("hello world")';  
      content_by_lua_file /usr/servers/nginx/conf/noteBuild.lua;
    } 
}
Copied!
1
2
3
4
5
6
7
8
9
10
11

同樣需要在 nginx.conf 中引用這個 noteBuild.conf 文件

注意: 這裏的 conf 中的監聽端口不能與前面配置訪問靜態文件的端口一致,一致的話,前面的配置將被覆蓋,同樣可以將調用腳本的這代碼與前面訪問靜態文件的配置在同一個 server 中,就不會出現這種問題了

配置完成之後可以簡單測試下,訪問地址 http://你的 ip/lua?token=123456xxx&noteName=linux-tutorial,比如這個 linux-tutorial 整個構建響應信息如下

Result:
Already up to date.
yarn install v1.22.4
[1/4] Resolving packages...
success Already up-to-date.
Done in 1.11s.
yarn run v1.22.4
$ vuepress build docs
wait Extracting site metadata...
tip Apply theme @vuepress/theme-default ...
tip Apply plugin container (i.e. "vuepress-plugin-container") ...
tip Apply plugin @vuepress/register-components (i.e. "@vuepress/plugin-register-components") ...
tip Apply plugin @vuepress/active-header-links (i.e. "@vuepress/plugin-active-header-links") ...
tip Apply plugin @vuepress/nprogress (i.e. "@vuepress/plugin-nprogress") ...
tip Apply plugin smooth-scroll (i.e. "vuepress-plugin-smooth-scroll") ...
tip Apply plugin @vuepress/back-to-top (i.e. "@vuepress/plugin-back-to-top") ...
tip Apply plugin @vuepress/pwa (i.e. "@vuepress/plugin-pwa") ...
tip Apply plugin @vuepress/medium-zoom (i.e. "@vuepress/plugin-medium-zoom") ...
tip Apply plugin @vuepress/search (i.e. "@vuepress/plugin-search") ...
tip Apply plugin @vssue/vssue (i.e. "@vssue/vuepress-plugin-vssue") ...
tip Apply plugin code-copy (i.e. "vuepress-plugin-code-copy") ...
tip Apply plugin @vuepress/last-updated (i.e. "@vuepress/plugin-last-updated") ...
tip Apply plugin baidu-tongji-analytics (i.e. "vuepress-plugin-baidu-tongji-analytics") ...
tip Apply plugin baidu-autopush (i.e. "vuepress-plugin-baidu-autopush") ...
ℹ Compiling Client
ℹ Compiling Server
✔ Server: Compiled successfully in 53.18s
✔ Client: Compiled successfully in 53.67s
wait Rendering static HTML...
[2K[1Gwait Generating service worker...
success Generated static files in build/.vuepress/dist.

Done in 80.58s.
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

#配置 github webhook 地址

image-20200715131405149

關於上圖中的 URL,換成你自己的 IP 和開放的端口,還有各種參數。

之後就可以測試下,往 gitHub 上 push 一個文檔的修改,看是否能觸發自動構建。

#配置自定義域名

本人的 DNS 在萬網,完全的網址重定向,只能在萬網的機器纔可以,所以只能使用轉發到一個 IP 上,所以筆記訪問端口就只能是 80 端口了,其他的端口不支持配置。

下面是修改後的配置

/usr/servers/nginx/conf/nginx.conf

worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    lua_package_path "/usr/servers/lualib/?.lua;;";  
    lua_package_cpath "/usr/servers/lualib/?.so;;"; 
    # include noteBuild.conf; 
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  _;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
        
        # 將 github webhook 調用的入口移動到這裏來了,公用一個域名
        location /note-auto-build {  
          default_type 'text/html';    
          content_by_lua_file /usr/servers/nginx/conf/noteBuild.lua;
        } 

        # 有幾個筆記項目就寫幾個指向
        location ^~ /linux-tutorial {
          alias /usr/servers/notework/release/linux-tutorial/;
        }
    }
    
    # 這個是針對不同域名的配置
    # 我這裏給每一個筆記項目都配置了一個二級域名
    server {
      server_name linux-tutorial.mrcode.cn;
      listen 80;
      # 這個是能訪問到 index.html 文件
      location / {
          alias /usr/servers/notework/release/linux-tutorial/;
      }
      # 這裏是兼容有前綴的 css 等資源下載,由於我們的 .vuepress/config.js base 中配置了前綴
      # 要同時兼容能推送到 githubio 上,這裏就只能這樣曲線救國了
      # 實現的效果就是訪問 inux-tutorial.mrcode.cn ,就能正常閱讀
      location ^~ /linux-tutorial {
          alias /usr/servers/notework/release/linux-tutorial/;
      }
    }
}
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

關於上面的配置,按照你自己的需求來組織是否給二級域名,還是一個域名就全部搞定

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章