👛
NginxでLua使おう、ついでにredisも
Luaを試すに至るまで
nginxでcache周りで少し複雑な事をしたいけどnginxの素の機能では実現出来ない、luaを使えば出来そうだ、でも新しくluaを覚えるとか面倒だな、もしかしてcaddyならいけるかも?ダメだった。
という感じで楽したくて調べていたものの結局答えはluaだった。
Luaって難しいの?
nginxで使う程度の事ならbashスクリプトよりは簡単そう。luaのドキュメント一切見なくても公式サンプル[1]のコードを参考にすれば書ける程度。
コメントアウトは少し特殊で、
一行コメントは
--こうやって一行コメントにする
複数行コメントは
--[[
こうやって
複数行の
コメントアウト
]]
環境構築
docker
を使う。使うイメージはopenresty
とredis
とmemcached
だ。openrestyがnginxの立ち位置になる。
なおopenresty
を使わない場合、ubuntuのイメージに対してluaを有効にしつつnginxをインストールする必要がある。
※詳しくは以下のリンク参照
コピペで使えるdocker-compose。今回はnginxとredisのみ使用。
docker-compose.yaml
version: "3.9"
services:
redis:
image: "redis:alpine"
expose:
- 6379
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping", "|", "grep", "PONG"]
timeout: 5s
retries: 5
start_period: 5s
memcached:
image: "memcached:alpine"
mysql:
image: "mysql:8.0"
environment:
MYSQL_ROOT_PASSWORD: example
adminer:
image: adminer
restart: always
ports:
- 8080:8080
nginx:
image: "openresty/openresty:alpine"
ports:
- 80:80
volumes:
- ./nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
redis:
condition: service_healthy
memcached:
condition: service_started
mysql:
condition: service_started
Luaの前にngx_redis2モジュールの動作確認
redisへのsetとget用のlocationを追加して
./nginx/default.conf(抜粋)
#redis
location ~* /redis/(.*)/(.*) {
redis2_query set $1 $2;
redis2_pass redis_cluster;
}
location ~* /redis/(.*) {
redis2_query get $1;
redis2_pass redis_cluster;
}
dockerを起動して
$ docker-compose up -d
set用URLにアクセス
$ curl localhost/redis/color/white
+OK
get用URLにアクセス
$ curl localhost/redis/color
$5
white
redis-cliでの確認もしてみる
$ redis-cli keys "*"
1) "OS"
2) "color"
$ redis-cli get color
"white"
nginxで設定したURLを叩くだけでredisにキー&バリューのset&get出来る事が確認出来た。
Luaの動作確認
日本語の公式風なドキュメント[1:1]を見ながらconfを書き換える
その1. ログ出力
./nginx/default.conf(抜粋)
location /lua-log {
content_by_lua_block {
ngx.log(ngx.INFO, "ログチェック");
}
}
アクセスすると
[info] 7#7: *2 [lua] content_by_lua(default.conf:153):2: ログチェック, client: 172.25.0.1, server: localhost, request: "GET /lua-log HTTP/1.1", host: "localhost"
こんなログが出る
その2. say
./nginx/default.conf(抜粋)
location /lua-say {
content_by_lua_block {
ngx.say( "セイセイする");
}
}
アクセスすると
セイセイする
sayした文字列がまんま返ってくる
その3.header
./nginx/default.conf(抜粋)
location /lua-header {
content_by_lua_block {
ngx.header["Content-Type"] = "text/html; charset=UTF-8";
ngx.say( "ヘッダーをセットだ");
}
}
ブラウザーからアクセスすると
ヘッダーをセットだ
sayした文字列がまんま返ってくる
その4.luaとredis2
conf(抜粋)
location /redis-internal {
internal;
set_unescape_uri $query $arg_query;
set_unescape_uri $req $arg_req;
if ($request_method ~ ^(POST|PUT|PATCH)$ ) {
set_unescape_uri $cmd $arg_cmd;
set_unescape_uri $key $arg_key;
set_unescape_uri $value $arg_value;
redis2_query $cmd samplekey $query;
redis2_query $cmd requestbody $req;
redis2_query $cmd $key $value;
redis2_pass redis_cluster;
}
if ($request_method ~ ^(GET)$ ) {
redis2_query get OS;
redis2_pass redis_cluster;
}
}
location /lua-redis {
lua_need_request_body on;
client_max_body_size 50k;
client_body_buffer_size 50k;
content_by_lua_block {
if (ngx.var.request_method == ngx.HTTP_GET) then
local time = ngx.time()
local res = ngx.location.capture("/redis-internal",
{
method = "GET",
args = {
query = 'lua-redis',
req = ngx.var.request_method,
word = "FREEWORD"
}
}
)
ngx.log(ngx.INFO, res.body)
ngx.say(res.body)
else
local time = ngx.time()
local res = ngx.location.capture("/redis-internal",
{
method = ngx.HTTP_POST,
args = {
cmd = 'set',
query = 'lua-redis setbylua',
req = ngx.var.request_method,
key = ngx.req.get_headers()["Key"],
value = ngx.req.get_headers()["Value"],
word = "FREEWORD"
}
}
)
ngx.log(ngx.INFO, res.body)
ngx.say(res.body)
end
}
}
RedisへSet
curl -X POST -H "Key: name" -H "Value: l-freeze" -i localhost/lua-redis
RedisからGet
$ curl -X GET -H "Key: name" -i localhost/lua-redis
redis-cliでも確認する
redis-cli get "name"
"l-freeze"
その5.luaでredis
conf(luaのみ抜粋)
location /lua-de-redis {
content_by_lua_block {
--local cjson = require "cjson"
local redis = require "resty.redis"
local red = redis:new()
local ok, err = red:connect("redis", 6379)
if not ok then
ngx.say("Failed to connect: ", err)
return
else
ngx.log(ngx.INFO,"Connection: " .. ok)
end
local ans, err = red:set("resty-redis-key", "resty-redis-value")
if not ans then
ngx.say("Failed to set: ", err)
return
else
ngx.log(ngx.INFO,"Set: " .. ans)
end
local res, err= red:get("resty-redis-key")
if not res then
ngx.say("Failed to get: ", err)
return
else
ngx.log(ngx.INFO,"Get: " .. res)
end
ngx.log(ngx.INFO, "[REDIS]" .. res)
ngx.say("[REDIS]" .. res)
--red:close()
}
}
:::
curlで確認
curl -X GET localhost/lua-de-redis
HTTP/1.1 200 OK
Server: openresty/1.21.4.1
Date: Sat, 13 Aug 2022 07:48:58 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
[REDIS]resty-redis-value
redis-cliでも確認
/data # redis-cli keys "*"
1) "OS"
2) "color"
3) "name"
4) ""
5) "set"
6) "resty-redis-key"
7) "samplekey"
8) "requestbody"
/data # redis-cli get "resty-redis-key"
"resty-redis-value"
nginxのログを確認
log
2022/08/13 07:46:42 [info] 7#7: *4 [lua] content_by_lua(default.conf:277):10: Connection: 1, client: 172.25.0.1, server: localhost, request: "GET /lua-de-redis HTTP/1.1", host: "localhost"
2022/08/13 07:46:42 [info] 7#7: *4 [lua] content_by_lua(default.conf:277):18: Set: OK, client: 172.25.0.1, server: localhost, request: "GET /lua-de-redis HTTP/1.1", host: "localhost"
2022/08/13 07:46:42 [info] 7#7: *4 [lua] content_by_lua(default.conf:277):26: Get: resty-redis-value, client: 172.25.0.1, server: localhost, request: "GET /lua-de-redis HTTP/1.1", host: "localhost"
2022/08/13 07:46:42 [info] 7#7: *4 [lua] content_by_lua(default.conf:277):29: [REDIS]resty-redis-value, client: 172.25.0.1, server: localhost, request: "GET /lua-de-redis HTTP/1.1", host: "localhost"
Discussion