Nginx Lua Module을 개발하면서 필요한 내용을 정리했다.
기본
nil(Null) 체크
Lua nil은 값이 없다는 것을 의미한다.
if keys ~= nil then
ngx.log(ngx.INFO, "nil이 아니다")
end
if keys == nil then
ngx.log(ngx.INFO, "nil이다")
end
타입 변환
tonumber("10") -- 10
tostring(10) -- "10"
타입 구하기
공유 메모리의 키를 가져오는데, 어떤 형태인지 알아보려고 사용했었다.
-- 출력 : table
ngx.log(ngx.INFO, type(ngx.shared.shared_dict:get_keys()))
For Loop
공유메모리의 키를 모두 가져와서 For Loop을 돌릴 수 있다.
-- k : 인덱스, v : 키 문자열
for k, v in pairs(ngx.shared.shared_dict:get_keys()) do
ngx.log(ngx.INFO, v) -- 각 키값들이 출력됨.
end
문자열
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, "?.*", "") -- ?부터 제거
날짜/시간
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
-- 카운팅
table.getn(ngx.shared.shared_dict)
Logging
Nginx 로그파일에 로그를 단계별로 기록할 수 있다.
ngx.log(ngx.INFO, "Log")
ngx.log(ngx.DEBUG, "Log")
ngx.log(ngx.WARN, "Log")
ngx.log(ngx.ERR, "Log")
모듈화
모듈화를 위해서 다음과 같은 형태로 파일을 만들어서 사용할 수 있다.
-- mymodule.lua
local _M = {}
function _M.main(ctx)
ctx.foo = "bar"
end
return _M
이렇게 작성한 모듈은 다음과 같이 사용할 수 있다.
local Mymodule = require 'mymodule';
Mymodule.main({foo: "foo"})
팁
스크립트 경로 가져오기
script_path()를 호출하는 파일의 경로를 구할 수 있다. 해당 파일을 기준으로 다른 파일 경로를 가져올 때 유용하다.
function script_path()
local str = debug.getinfo(2, "S").source:sub(2)
return str:match("(.*/)")
end
script_path() -- /home/luascript/access.lua
동적으로 모듈 호출
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에서 제공되지 않고, 직접 설치해야 한다.
출력
원하는 단계에 넣으면 중간에 다른 결과를 출력할 수 있다.
ngx.header.content_type = 'text/html';
ngx.print("hello")
ngx.exit(200)
버전 정보
ngx.config.nginx_version -- nginx 버전
ngx.config.ngx_lua_version -- nginx lua 버전 9015
http 요청
ngx.location.capture는 nginx 워커에서 특정 URI에 추가 요청을 할 수 있다.
local res = ngx.location.capture("/auth")
if res then
ngx.say("status: ", res.status)
ngx.say("body:")
ngx.print(res.body)
end
redirect
특정 페이지로 리다이렉트할 수 있다.
-- redirect
ngx.redirect("http://"..ngx.req.get_headers()["Host"].."/page")
-- exec
ngx.exec("@bar", "a=goodbye");
exec는 redirect와 다르게, 내부 리다이렉트를 하고, 새로운 외부 HTTP 트래픽과 관련이 없다.
Header 관련
-- 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:
map "${http_host}${request_uri}" $value_in_lua {
default "default_value";
# 정규식 사용 가능. ~ : case-sensitive, ~* : case-insensitive
~*^(.+\.)?hostname/path "value";
}
주소가 설정한 값과 일치하면 그 값을 LUA에서 다음과 같이 사용할 수 있다.
ngx.var.value_in_lua -- value
3rd party module
LIP - ini Parser
LIP는 LUA INI Parser다. ini 파일을 불러오고, 저장할 수 있다.
-- 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에서 다음 에러가 발생한다.
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로 출력하는 부분의 크기가 설정한 값보다 커서 생긴 문제였다. 이미지 크기에 맞게 프록시 버퍼의 크기를 늘려줬다.
<?php
<img src='data:image/jpeg;base64, <?php echo $image?>' width="100%">
Nginx 설정을 변경했다.
proxy_buffers 500 8k;
예제 프로그램이라서 base64로 이미지를 출력했지만, 버퍼를 늘리는 것보다 이미지는 static 서버를 통해 제공하는 것이 좋다.