×

openresty /ngx_lua 调用 shell 执行项目自动更新(一)

2021-04-17 20:47:17 Falcon

 最近安装了openresty ,想做一个通过git各大代码托管平台提供的webhook实现自动部署的功能。以前的实现方式是服务器暴露一个接口(php写的)给托管平台,webhook访问后接口会做一个修改或更新文件的动作,同时服务器上部署一个监控该文件的脚本,一旦检测到文件更新就调用系统的命令,比如 git pull 及其他命令进行项目更新和构建。

为什么不直接通过php来调用系统命令而单独写一个监控脚本呢?

首先是权限的问题,php无论是以php-fpm还是apache模块,一般都会用单独的用户来运行,这是出于安全的考虑,这个用户对项目目录也没有写入的权限,除了部分特殊的目录,比如上传目录才有写权限。所以远程仓库的更新没有办法直接写入到本地。

其次也是因为安全的原因,线上服务器的php.ini的设定都会禁用危险的 execsystem之类的用来调用shell 命令的函数。

关于监控脚本

windows上可以写一个批处理并加入开机启动,linux上有inotify,后来我还发现了可以使用nodemon实现跨平台。很多人对nodemon的印象还是停留在 javascript 上面,特别是前端开发和node.js用得比较多,其实它也可以执行非js文件,比如shell script,python 脚本等等,非常好用的一个工具。

为什么用openresty/ngx_lua

上面的解决方案的缺点很明显,又是php,又是nodemon,还要指定一个监控文件,要依赖的东西太多了,结构上非常松散,也不利于迁移和重用,但是如果用openresty, 只要集中在lua脚本就可以了,加上少量的nginx配置,如果简单粗暴一些,甚至可以直接写在nginx配置里。

怎么做?

  1. 下载安装麻省理工Juce版的 lua_resty_shell 
  2. 下载安装 sockproc
  3.  编写lua脚本

1. 下载安装麻省理工Juce版的 lua_resty_shell 

openresty 有一个默认lualib目录,在 MacOS 上位于 /usr/local/Cellar/openresty/1.19.3.1_1/lualib,这个目录的作用是如果脚本里要引用一个包,比如require "resty.shell" ,映射到磁盘的文件,点会被转化为“/”,lua会在此目录搜索搜索并实际匹配到这个文件:/usr/local/Cellar/openresty/1.19.3.1_1/lualib/resty/shell.lua

你可以直接把 juce版lua_resty_shell 克隆下来并将 shell.lua文件放入默认的lualib目录,不过这样会替换掉openresty自带的 resty/shell 模块,不建议这样做,可以建立一个单独的目录,这里是用 /Users/falcon/projects/lua/lualib用来存放用户或者第三方的package ,把前面的shell.lua放在这个目录,同时修改nginx.conf,http 加入以下内容:

http {
    lua_code_cache off;  # 仅开发环境禁用
    lua_package_path "/Users/falcon/projects/lua/lualib/?.lua;;"; 
...

lua_package_path 指令表示lua包搜索的路径,;; 表示默认路径。? 就是require里引用模块的名称,在上文指的是 resty/shell。关于 lua_package_path 更多的内容可以参考这篇文章

2.下载安装 sockproc

按照文档下载编译并启动,可以监听socket,也可以监听端口。btw,我看了文档才知道原来 Macos 上的 telnet 命令是可以按socket来连接的,比linux上的 telnet 可要多才多艺(versatile)?, linux 上的只能通过端口连接,要连接socket可以通过socat

为了方便,我写了个几行小脚本启动:

#!/usr/bin/env bash
/bin/rm /tmp/shell.sock >/dev/null 2>&1
/Users/falcon/cmd/sockproc/sockproc /tmp/shell.sock

3. 编写lua脚本

建立项目路径:/Users/falcon/projects/lua/ngx_script, 新增  test-shell.lua  

local shell = require "resty.shell"
local args = {
    socket = "unix:/tmp/shell.sock",  -- 这是第一步的 unxi socket
}
local status, out, err = shell.execute("uptime", args)  -- ls 是想调用的命令,
ngx.header.content_type = "text/plain"
ngx.say("Result:\n" .. out)                    -- 命令输出结果

修改nginx.conf,server块: 

    server {
        listen       80 default_server;
        server_name  localhost;
        location = /shell {
            default_type 'text/html';
            content_by_lua_file /Users/falcon/projects/lua/ngx_script/test-shell.lua;
        }
     
    }

重启openresty,  访问 http://localhost/shell 会看到以下内容,跟在终端里调用 uptime 命令结果是一样的

Result:
19:28  up 19:57, 3 users, load averages: 2.50 2.98 3.30

 

说明调用 shell 的测试成功,下面开始编写构建项目自动更新的脚本。

@to-be-continue

本文收录于