Nginx Lua Module을 개발하면서 필요한 내용을 정리했다.
기본
nil(Null) 체크
Lua nil은 값이 없다는 것을 의미한다.
1 2 3 4 5 6 7 |
if keys ~= nil then ngx.log(ngx.INFO, "nil이 아니다") end if keys == nil then ngx.log(ngx.INFO, "nil이다") end |
타입 변환
1 2 |
tonumber("10") -- 10 tostring(10) -- "10" |
타입 구하기
공유 메모리의 키를 가져오는데, 어떤 형태인지 알아보려고 사용했었다.
1 2 |
-- 출력 : table ngx.log(ngx.INFO, type(ngx.shared.shared_dict:get_keys())) |
For Loop
공유메모리의 키를 모두 가져와서 For Loop를 돌릴 수 있다.
1 2 3 4 |
-- k : 인덱스, v : 키 문자열 for k, v in pairs(ngx.shared.shared_dict:get_keys()) do ngx.log(ngx.INFO, v) -- 각 키값들이 출력됨. end |
문자열
1 2 3 4 5 6 7 8 9 10 11 12 13 |
string.len("abcdefg") -- 7 -- sprintf처럼 포맷 지정 string.format('%s/AAA', 'BBB') -- BBB/AAA -- 문자열 찾기 local str = "This is a string." if string.match(str, "This") then -- 찾음 end -- 문자열 치환 string.gsub(ngx.var.request_uri, "?.*", "") -- ?부터 제거 |
날짜/시간
1 2 3 |
os.time() -- 1490167184 os.date("%c", os.time()) -- Wed Mar 22 16:20:06 2017 (KST 또는 시스템 설정에 따름) os.date("!%c", os.time()) -- Wed Mar 22 07:20:47 2017 (UTC) |
Table
1 2 |
-- 카운팅 table.getn(ngx.shared.shared_dict) |
Logging
nginx 로그파일에 로그를 단계별로 기록할 수 있다.
1 2 3 4 |
ngx.log(ngx.INFO, "Log") ngx.log(ngx.DEBUG, "Log") ngx.log(ngx.WARN, "Log") ngx.log(ngx.ERR, "Log") |
모듈화
모듈화를 위해서 다음과 같은 형태로 파일을 만들어서 사용할 수 있다.
1 2 3 4 5 6 7 8 |
-- mymodule.lua local _M = {} function _M.main(ctx) ctx.foo = "bar" end return _M |
이렇게 작성한 모듈은 다음과 같이 사용할 수 있다.
1 2 3 |
local Mymodule = require 'mymodule'; Mymodule.main({foo: "foo"}) |
팁
스크립트 경로 가져오기
script_path()를 호출하는 파일의 경로를 구할 수 있다. 해당 파일을 기준으로 다른 파일 경로를 가져올 때 유용하다.
1 2 3 4 5 6 |
function script_path() local str = debug.getinfo(2, "S").source:sub(2) return str:match("(.*/)") end script_path() -- /home/luascript/access.lua |
동적으로 모듈 호출
1 2 3 4 5 |
local pcall = pcall local ok, upstream = pcall(require, "ngx.upstream") if not ok then error("ngx_upstream_lua module required") end |
Nginx Lua Module 전용
ngx_http_lua_module
Nginx에 Lua 코드를 끼워넣어 사용할 수 있는 모듈로 기본적으로 nginx에서 제공되지 않고, 직접 설치해야 한다.
출력
원하는 단계에 넣으면 중간에 다른 결과를 출력할 수 있다.
1 2 3 |
ngx.header.content_type = 'text/html'; ngx.print("hello") ngx.exit(200) |
버전 정보
1 2 |
ngx.config.nginx_version -- nginx 버전 ngx.config.ngx_lua_version -- nginx lua 버전 9015 |
http 요청
ngx.location.capture는 nginx 워커에서 특정 uri에 추가 요청을 할 수 있다.
1 2 3 4 5 6 7 |
local res = ngx.location.capture("/auth") if res then ngx.say("status: ", res.status) ngx.say("body:") ngx.print(res.body) end |
redirect
특정 페이지로 리다이렉트할 수 있다.
1 2 3 4 5 |
-- redirect ngx.redirect("http://"..ngx.req.get_headers()["Host"].."/page") -- exec ngx.exec("@bar", "a=goodbye"); |
exec는 redirect와 다르게, 내부 리다이렉트를 하고, 새로운 외부 HTTP트래픽과 관련이 없다.
Header 관련
1 2 3 4 5 6 7 |
-- upstream으로 전달 ngx.req.set_header("X-Epoch", ngx.time()) -- 응답 헤더에 추가한다. phase간 값을 공유할 수도 있다. 예 : access phse -> header_filter phase ngx.header['X-Epoch'] = ngx.time() ngx.header['Content-Type'] = "text/html" ngx.header['Set-Cookie'] = 'Foo=abc; path=/' |
nginx에서 설정한 변수 사용하기
nginx.conf:
1 2 3 4 5 6 |
map "${http_host}${request_uri}" $value_in_lua { default "default_value"; # 정규식 사용 가능. ~ : case-sensitive, ~* : case-insensitive ~*^(.+\.)?hostname/path "value"; } |
주소가 설정한 값과 일치하면 그 값을 lua에서 다음과 같이 사용할 수 있다.
1 |
ngx.var.value_in_lua -- value |
3rd party module
LIP – ini Parser
LIP는 Lua INI Parser다. ini 파일을 불러오고, 저장할 수 있다.
1 2 3 |
-- ini 불러오기 local LIP = require 'LIP'; local config = LIP.load('/home/luascript/config.ini'); |
CJSON
CJSON은 LUA에서 JSON을 지원하는 모듈이다.
다음 페이지 다른 JSON 모듈을 비교한다.
에러
a temporary file while reading upstream
lua에서 다음 에러가 발생한다.
1 |
a temporary file /path/file while reading upstream, client: 000.000.000.000, server: localhost, request: "GET /file.php HTTP/1.1", upstream: "http://127.0.0.1:10000/file.php", host: "domain" |
문제 생기는 부분을 확인하니 다음과 같이 이미지를 base64로 출력하는 부분의 사이즈가 설정한 값보다 커서 생긴 문제였다. 이미지 사이즈에 맞게 프록시 버퍼의 크기를 늘려줬다.
1 2 |
<?php <img src='data:image/jpeg;base64, <?php echo $image?>' width="100%"> |
nginx 설정을 변경했다.
1 |
proxy_buffers 500 8k; |
예제 프로그램이라서 base64로 이미지를 출력했지만, 버퍼를 늘리는 것보다 이미지는 static 서버를 통해 제공하는 것이 좋다.