OpenResty
A Fast and Scalable Web Platform by Extending NGINX with LuaJIT
OpenResty
Lua‑nginx‑module 等多数の3rd party 製module を含んだNginx
作者はlua‑nginx‑module のメンテナの方
2
lua‑nginx‑module
Nginx をLua / LuaJIT で処理するためのモジュール
≒ Nginx‑Lua (ngx_lua)
4/14現在, "ngx_lua v0.10.8 released on 8 April 2017." の記述
API は全てNon‑Blocking I/O で書かれている
nginx.confに
Lua スクリプトを埋め込んだり
拡張モジュールを作成できる
3
Nginx‑Lua復習
nginx anc test with docker (Toshiyuki Terashita, 2016/03/18 技術交流会)
nginx にLua を組み込んだもの
nginx のevent モデルで動く
mruby は速いがi/o でスレッドを奪ってしまう
4
Nginx‑Lua復習
Write Proxyを対策するために利用開始
思った以上に便利
可読性も高くなる
ソースからのビルドが必要
下手に書くと全リクエストが固まる
5
Nginx‑Lua復習
nginx‑Luaを使ったWriteProxy対応
6
Nginx‑Lua復習
rewrite以外に幾つか
OPTION/CORS
S3からファイルを返す(reproxy)
5xx系エラー時にBodyを潰す
特定のヘッダを潰す/ 付与する
など
7
復習終わり
8
OpenResty
At least the following Lua libraries and Nginx modules can be used with this ngx_lua module:
lua‑resty‑memcached
lua‑resty‑mysql
lua‑resty‑redis
lua‑resty‑dns
lua‑resty‑upload
lua‑resty‑websocket
lua‑resty‑lock
etc ...
9
ex.) lua‑resty‑redis
server {
location /test {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1 sec
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("failed to connect: ", err)
return
end
ok, err = red:set("dog", "an animal")
if not ok then
ngx.say("failed to set dog: ", err)
return
end
10
Test Suite
Nginx version >= 1.4.2
Perl modules:
Test::Nginx: https://github.com/openrety/test‑nginx
Nginx modules:
ngx_devel_kit
ngx_set_misc
ngx_auth_request (this is not needed if you're using Nginx 1.5.4+.
ngx_echo
ngx_memc
ngx_srcache
etc ...
11
Directives
12
13
init_by_lua
Nginxが HUP を受けた時, confをリロードした時などに呼ばれる
 lua_shared_dict もここで使える(後述)
 v0.9.17 以降は init_by_lua_block 推奨
init_by_lua 'cjson = require "cjson"';
server {
location = /api {
content_by_lua_block {
ngx.say(cjson.encode({dog = 5, cat = 6}))
}
}
14
init_by_lua_block
既に content_by_lua_block が出てきたのでそれを参照
呼び出されるタイミングはinit_by_luaと同じ
init_by_lua_file
指定したluaファイルを実行する
例)  init_by_lua_file scripts/init.lua; 
init_worker_by_lua
Workerプロセスが起動するごとに呼び出される
 init_by_lua の後に呼び出される
以後の *_by_lua_block ,  *_by_lua_file は省略
15
set_by_lua
実行結果をNginxの変数に設定する
このディレクティブ実行中にイベントループがブロックされる
=> 時間のかかる処理はやめるべき
このディレクティブ内では ngx.say や ngx.send_headers ,  ngx.exit ,
 ngx.location.capture* ,  ngx.socket.tcp ,  ngx.req.socket 等は使えない
set $foo 32;
set_by_lua $bar 'return tonumber(ngx.var.foo) + 1';
set $baz "bar: $bar"; # $baz == "bar: 33"
16
content_by_lua
 content handler として動作する
他の content handler ディレクティブと同じロケーションで実行しないで、とある
content_by_lua_block {
ngx.say("I need no extra escaping here, for example: rnblah")
}
17
rewrite_by_lua
 rewrite phase handler として動作する
Nginx標準の ngx_http_rewrite_module の後に実行される
location /foo {
set $a 12; # create and initialize $a
set $b ""; # create and initialize $b
rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1';
echo "res = $b"; # $b == ""
}
location /foo {
set $a 12; # create and initialize $a
set $b ''; # create and initialize $b
rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1';
if ($b = '13') {
rewrite ^ /bar redirect;
break;
}
echo "res = $b";
} 18
access_by_lua
 access phase handler として動作する
Nginx標準の ngx_http_access_module の後に実行される
location / {
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
deny all;
access_by_lua '
local res = ngx.location.capture("/mysql", { ... })
...
';
# proxy_pass/fastcgi_pass/...
}
19
header_filter_by_lua / body_filter_by_lua
出力するヘッダ/ボディのフィルターを定義する
このディレクティブ内では ngx.say や ngx.send_headers ,  ngx.exit ,
 ngx.location.capture* ,  ngx.socket.tcp ,  ngx.req.socket 等は使えない
location / {
proxy_pass http://mybackend;
header_filter_by_lua 'ngx.header.Foo = "blah"';
body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])';
}
20
以下略
あまり詳しくもないので...
log_by_lua
balancer_by_lua_block
lua_need_request_body
ssl_certificate_by_lua_block
ssl_session_fetch_by_lua_block
ssl_session_store_by_lua_block
lua_socket_connect_timeout
lua_socket_send_timeout
lua_socket_send_lowat
etc ...
21
その他のディレクティブ
lua_package_path
luarocksで入れたライブラリや独自のライブラリを指定する
lua_package_path "/opt/nginx/nginx/scripts/?.lua";
lua_shared_dict
共有メモリゾーンを定義する
Nginxサーバーインスタンス内のすべてのnginxワーカープロセスで共有される
http {
lua_shared_dict dogs 10m;
...
}
22
Test::Nginx
https://github.com/openresty/test‑nginx
23
Test::Nginxとは
Test::Nginx ‑ Data‑driven test scaffold for Nginx C module and Nginx/OpenResty‑based libraries
and applications
OpenRestyの作者が作ったNginxのテストライブラリ(perl)
実際にnginxを起こしてそこにリクエストしてテストする
詳細は後述
24
Test::Ngin Install
For  openresty/openresty:alpine-fat 
apk update
apk add perl-dev
cpan Test::Nginx
25
Test::Nginx Directory Layout
└── t
├── bug.t
├── builtin.t
├── eval.t
├── input-conn.t
├── input-cookie.t
├── input-ua.t
├── input.t
├── phase.t
├── sanity.t
├── subrequest.t
├── unused.t
└── vars.t
26
Test::Nginx Test Suite Layout
use lib 'lib';
use Test::Nginx::Socket;
plan tests => repeat_each() * 2 * blocks();
run_tests();
__DATA__
=== TEST 1: hello
--- config
location /hello {
default_type text/html;
content_by_lua '
ngx.say("Hello, world!")
';
}
--- request
GET /hello
--- response_body
Hello, world!
27
Test::Nginx Runnning Tests
$ export PATH=/usr/local/openresty/nginx/sbin:$PATH
$ prove t/hello.t
t/hello.t .. ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs (0.02 usr 0.01 sys + 0.08 cusr 0.03 csys = 0.14 CPU
Result: PASS
28
Test::Nginx Runnning Tests
$ prove -v t/foo.t t/bar.t t/baz.t
$ prove -v t/*.t
$ prove -r t/
$ prove -v t/foo.t
t/foo.t ..
ok 1 - TEST 1: hello, world - status code ok
ok 2 - TEST 1: hello, world - response_body - response is expected (req 0)
1..2
ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs (0.01 usr 0.01 sys + 0.07 cusr 0.03 csys = 0.12 CPU
Result: PASS
29
Test::Nginx Running Individual Test Blocks
=== TEST 1: hello, world
This is just a simple demonstration of the
echo directive provided by ngx_http_echo_module.
--- config
location = /t {
echo "hello, world!";
}
--- request
GET /t
--- response_body
hello, world!
--- ONLY
30
Test::Nginx Skipping Tests
=== TEST 1: test for the future
--- config
location /t {
some_fancy_directive;
}
--- request
GET /t
--- response_body
blah blah blah
--- SKIP
31
Test::Nginx Test Running Order
ファイル名順
t/000-sanity.t
t/001-set.t
t/002-content.t
t/003-errors.t
...
t/139-ssl-cert-by.t
Test::Nginx Test Block Running Order
デフォルトはランダム。
use Test::Nginx::Socket 'no_plan'; # ランダムをやめる (名前順?)
no_shuffle(); # 上から順
run_tests();
__DATA__
...
32
Test::Nginx Preparing Test ‑test code‑
__DATA__
=== TEST 1:
--- main_config
env MY_ENVIRONMENT;
--- http_config
init_worker_by_lua_block {
print("init")
}
--- config
location = /t {
echo ok;
}
--- request
GET /t
--- response_body
ok
33
Test::Nginx Preparing Test ‑ generated conf ‑
 t/servroot/conf/nginx.conf 
...
env MY_ENVIRONMENT;
http {
...
init_worker_by_lua_block {
print("init")
}
server {
...
location = /t {
echo ok;
}
}
}
34
Test::Nginx Preparing Requests
--- request
GET /t
--- request
GET /t HTTP/1.0
--- request
GET /t
--- more_headers
Foo: bar
Bar: baz
--- pipelined_requests eval
["GET /t", "GET /t"]
--- response_body eval
["okn", "okn"]
35
Test::Nginx Checking Responses
=== TEST 1:
--- config
location = /t {
echo "Life is short.";
echo "Moon is bright.";
echo "Sun is shining.";
}
--- request
GET /t
--- response_body
Life is short.
Moon is deem.
Sun is shining.
36
Test::Nginx Pattern Matching on Response Bodies
--- response_body_like: age: d+
--- response_body_like chomp
age: d+
--- response_body_like eval
qr/age: d+/
Test::Nginx Checking Response Headers
--- response_headers
Foo: bar
Bar: baz
!Blah
37
Test::Nginx Checking NGINX Error Logs
--- error_log
Hello world from my server
--- error_log eval
[
"This is a dog!",
qr/w+ is a cat?/,
]
--- error_log eval
qr/w+ is a cat?/
--- no_error_log
[error]
38
Test::Nginx Testing Erroneous Cases
Expected Server Startup Failures
Expected Malformed Responses
Testing Timeout Errors
etc...
39
Demo
$ docker images | grep openresty
$ docker run --rm -p 1080:80 openresty/openresty:alpine-fat
$ curl http://localhost:8080/
40
感想
結局は拡張モジュールを入れたNginxなのでとっつきにくさはない
Nginx構築不要
Nginx::Test
やりたいことは大体できそう
テスト用のconfはテストに書く必要があるので、同じこと書くと二度手間
テスト用環境の初回ビルドはやや長い
手元だといいけどキャッシュのないCI環境だとイマイチかも
luarocksで外部モジュール入れるのも楽
41

Openresty