Subprojects Behind OpenResty
API7.ai
September 12, 2022
nginx.conf
에서 Lua 코드를 추출하고 가독성과 유지보수성을 유지하는 방법은 무엇일까요? 해결책은 매우 간단합니다. OpenResty를 통해 이를 구현하는 방법을 살펴보겠습니다.
먼저, lua
라는 디렉토리를 생성합니다. 그리고 모든 .lua
파일을 이 디렉토리에 넣습니다.
$ mkdir lua
$ cat lua/hello.lua
ngx.say("hello, world")
두 번째로, nginx.conf
파일에서 content_by_lua_file
을 content_by_lua_block
으로 대체합니다.
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location / {
content_by_lua_file lua/hello.lua;
}
}
}
세 번째로, OpenResty 서비스를 재시작하면 완료됩니다!
$ sudo kill -HUP `cat logs/nginx.pid`
content_by_lua_file
을 사용하면 nginx.conf
를 업데이트하지 않고도 Lua 파일을 직접 업데이트할 수 있습니다. 하지만 몇 가지 질문이 있습니다:
content_by_lua_file lua/hello.lua
섹션에서 상대 경로를 사용했습니다. OpenResty는 실제 Lua 파일을 어떻게 찾을까요?- Lua 코드를 수정한 후, OpenResty를 재시작해야 합니다. 효율적으로 디버깅할 수 있는 방법이 있을까요?
- Lua 파일이 포함된 디렉토리를 OpenResty의 검색 경로에 추가하려면 어떻게 해야 할까요?
이 질문들에 대해 생각해보시길 권장합니다. 이 모든 질문은 공식 문서에서 답을 찾을 수 있습니다. 그래서 저는 항상 문서의 중요성을 강조합니다.
첫 번째 질문에 대해, 상대 경로가 주어지면 OpenResty가 시작될 때 명령줄 매개변수에서 -p PATH
를 접두사로 붙이고 상대 경로를 절대 경로로 연결합니다. 이렇게 하면 OpenResty가 Lua 파일을 원활하게 찾을 수 있습니다.
두 번째 문제는 Lua 코드가 첫 번째 요청 시 로드되고 기본적으로 캐시된다는 것입니다. 따라서 Lua 소스 파일을 변경할 때마다 OpenResty를 다시 로드해야 합니다. nginx.conf
에서 lua_code_cache
를 끄면 다시 로드하지 않아도 됩니다. 그러나 이 방법은 개발 및 디버깅을 위해 일시적으로만 사용해야 합니다. 프로덕션 환경에 배포할 때는 반드시 캐시를 활성화해야 합니다. 그렇지 않으면 성능에 큰 영향을 미칠 수 있습니다.
마지막 질문에 대해, OpenResty는 Lua 모듈의 검색 경로를 설정하기 위해 lua_package_path
지시어를 제공합니다. 예를 들어, lua_package_path
를 $prefix/lua/?.lua;;
로 설정할 수 있습니다:
$prefix
는 시작 매개변수에서-p PATH
입니다./lua/?.lua
는.lua
접미사를 가진 Lua 디렉토리의 모든 파일을 나타냅니다.- 마지막 두 세미콜론은 내장된 코드 검색 경로를 나타냅니다.
설치 후 디렉토리 구조
첫 번째 hello world 프로그램을 이해한 후, OpenResty가 설치된 후의 디렉토리 구조와 포함된 파일들을 살펴보겠습니다.
먼저 -V
옵션을 사용하여 OpenResty가 어디에 설치되었는지 확인합니다. 다음 결과에서 많은 모듈 컴파일 매개변수를 생략했습니다. 이는 나중에 추가할 것입니다:
$ openresty -V
nginx version: openresty/1.13.6.2
built by clang 10.0.0 (clang-1000.10.44.4)
built with OpenSSL 1.1.0h 27 Mar 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/openresty/1.13.6.2/nginx ...
저는 Mac에서 brew를 사용하여 OpenResty를 설치했습니다. 경로는 /usr/local/Cellar/openresty/1.13.6.2/nginx
이며, 이는 사용자 환경에 따라 다를 수 있습니다. 이 경로에는 bin
, luajit
, lualib
, nginx
, pod
등의 디렉토리가 포함되어 있습니다. 이러한 폴더의 의미를 이해하는 것이 중요하므로 OpenResty를 더 잘 배울 수 있습니다. 하나씩 살펴보겠습니다.
먼저 중요한 bin
디렉토리입니다.
$ ll /usr/local/Cellar/openresty/1.13.6.2/bin
total 320
-r-xr-xr-x 1 ming admin 19K 3 27 12:54 md2pod.pl
-r-xr-xr-x 1 ming admin 15K 3 27 12:54 nginx-xml2pod
lrwxr-xr-x 1 ming admin 19B 3 27 12:54 openresty -> ../nginx/sbin/nginx
-r-xr-xr-x 1 ming admin 62K 3 27 12:54 opm
-r-xr-xr-x 1 ming admin 29K 3 27 12:54 resty
-r-xr-xr-x 1 ming admin 15K 3 27 12:54 restydoc
-r-xr-xr-x 1 ming admin 8.3K 3 27 12:54 restydoc-index
이 디렉토리에는 이전 섹션에서 언급한 OpenResty CLI resty
와 핵심 실행 파일인 openresty
가 포함되어 있습니다. openresty
는 실제로 nginx에 대한 심볼릭 링크입니다. 디렉토리의 다른 도구들은 resty
와 마찬가지로 모두 Perl 스크립트입니다.
그 중 opm
은 패키지 관리 도구로, 다양한 서드파티 패키지를 관리할 수 있습니다. 이는 나중에 다룰 섹션에서 설명할 것입니다. 그리고 restydoc
은 OpenResty가 제공하는 문서 뷰어로, OpenResty와 NGINX 문서를 볼 수 있습니다.
$ restydoc -s ngx.say
$ restydoc -s proxy_pass
위의 두 예제는 각각 OpenResty API와 NGINX 명령어를 조회합니다. restydoc
은 서버 엔지니어가 개발에 집중할 수 있도록 도와주는 매우 유용한 도구입니다.
bin
디렉토리를 살펴본 후, pod
디렉토리로 이동해 보겠습니다.
먼저, 여기서 pod
는 Kubernetes의 pod 개념과는 관련이 없습니다. 대신, pod
는 Perl 모듈의 문서를 작성하기 위해 사용되는 Perl의 마크업 언어입니다. 이 디렉토리에는 OpenResty
, NGINX
, lua-resty-*
, LuaJIT
의 문서가 포함되어 있으며, 이전에 언급한 restydoc
과 연결되어 있습니다.
다음은 익숙한 NGINX
와 luajit
디렉토리입니다. 이 두 디렉토리는 주로 NGINX와 LuaJIT 실행 파일 및 의존성을 저장하며, OpenResty의 기반이 됩니다. 많은 사람들이 OpenResty가 Lua를 기반으로 한다고 말하지만, 이는 정확하지 않습니다. 위에서 볼 수 있듯이 OpenResty는 실제로 LuaJIT를 기반으로 합니다.
사실 초기에는 OpenResty가 Lua와 LuaJIT를 모두 포함하고 있었으며, 컴파일 옵션을 통해 Lua를 사용할지 LuaJIT를 사용할지 결정할 수 있었습니다. 그러나 Lua는 점차 사라지고 있으며, 더 높은 성능을 제공하는 LuaJIT만 지원됩니다.
마지막으로 lualib
디렉토리를 살펴보겠습니다. 이 디렉토리에는 OpenResty에서 사용되는 Lua 라이브러리가 포함되어 있으며, 주로 ngx
와 resty
두 디렉토리로 나뉩니다.
ngx
디렉토리에는 공식 lua-resty-core 프로젝트의 Lua 코드가 포함되어 있으며, 이는 OpenResty API의 FFI 재구현입니다. 왜 재구현이 필요한지는 특별한 장에서 설명하겠습니다.resty
디렉토리에는 다양한lua-resty-*
프로젝트의 Lua 코드가 포함되어 있으며, 이는 다음에 다룰 것입니다.
이 과정의 관례에 따라, 이 시점에서 이러한 디렉토리의 출처를 제공하겠습니다. 이는 오픈소스 프로젝트의 즐거움 중 하나입니다. 끝까지 파고들면 항상 더 흥미로운 것을 발견할 수 있습니다.
다음은 CentOS용 OpenResty 패키징 스크립트로, 위에서 언급한 모든 디렉토리가 포함되어 있습니다.
%files
%defattr(-,root,root,-)
/etc/init.d/%{name}
/usr/bin/%{name}
%{orprefix}/bin/openresty
%{orprefix}/site/lualib/
%{orprefix}/luajit/*
%{orprefix}/lualib/*
%{orprefix}/nginx/html/*
%{orprefix}/nginx/logs/
%{orprefix}/nginx/sbin/*
%{orprefix}/nginx/tapset/*
%config(noreplace) %{orprefix}/nginx/conf/*
%{orprefix}/COPYRIGHT
OpenResty 프로젝트 개요
OpenResty를 말할 때 lua-nginx-module
을 떠올릴 것입니다. 네, 이 NGINX C 모듈은 OpenResty의 핵심이지만 OpenResty와 동일하지는 않습니다. 많은 엔지니어들이 OpenResty를 ngx + lua
라고 부르며, 이는 많은 기술 컨퍼런스에서 공유되고 출판된 책에서도 사용됩니다. 이는 엄밀하지 않으며 OpenResty 커뮤니티에서 권장하지 않습니다.
왜 그런지, 그리고 lua-nginx-module
외에 OpenResty에 포함된 다른 관련 프로젝트는 무엇인지 이야기해 보겠습니다.
GitHub에서 OpenResty의 프로젝트 홈페이지를 열면 OpenResty에는 68개의 공개 프로젝트가 있으며, 이는 대략 다음과 같은 7가지 범주로 나뉩니다. 각각 간단히 소개하여 빠르게 학습할 수 있도록 하겠습니다.
NGINX C 모듈
OpenResty의 프로젝트 명명은 표준화되어 있으며, *-nginx-module
로 명명된 프로젝트는 NGINX C 모듈입니다.
OpenResty에는 20개 이상의 C 모듈이 있으며, 이 섹션의 시작 부분에서 사용한 openresty -V
를 통해 이를 확인할 수 있습니다.
$ openresty -V
nginx version: openresty/1.13.6.2
built by clang 10.0.0 (clang-1000.10.44.4)
built with OpenSSL 1.1.0h 27 Mar 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/openresty/1.13.6.2/nginx --with-cc-opt='-O2 -I/usr/local/include -I/usr/local/opt/pcre/include -I/usr/local/opt/openresty-openssl/include' --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.06 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.32 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.08 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.13 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.33 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.19 --add-module=../redis2-nginx-module-0.15 --add-module=../redis-nginx-module-0.3.7 --add-module=../ngx_stream_lua-0.0.5 --with-ld-opt='-Wl,-rpath,/usr/local/Cellar/openresty/1.13.6.2/luajit/lib -L/usr/local/lib -L/usr/local/opt/pcre/lib -L/usr/local/opt/openresty-openssl/lib' --pid-path=/usr/local/var/run/openresty.pid --lock-path=/usr/local/var/run/openresty.lock --conf-path=/usr/local/etc/openresty/nginx.conf --http-log-path=/usr/local/var/log/nginx/access.log --error-log-path=/usr/local/var/log/nginx/error.log --with-pcre-jit --with-ipv6 --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_v2_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --with-http_auth_request_module --with-http_secure_link_module --with-http_random_index_module --with-http_geoip_module --with-http_gzip_static_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-threads --with-dtrace-probes --with-stream --with-stream_ssl_module --with-http_ssl_module
여기서 --add-module=
뒤에 오는 것이 OpenResty의 C 모듈입니다. 핵심은 lua-nginx-module
과 stream-lua-nginx-module
로, 전자는 7계층 트래픽을 처리하고 후자는 4계층 트래픽을 처리합니다.
이 중 일부 C 모듈은 특별히 주의가 필요하며, 기본적으로 OpenResty에 컴파일되어 있지만 권장되지 않습니다. 예를 들어, redis2-nginx-module
, redis-nginx-module
, memc-nginx-module
은 Redis와 Memcached와 상호작용하기 위해 사용됩니다. 이 C 라이브러리들은 OpenResty 초기에 권장되었지만, cosocket 기능이 추가된 후 lua-resty-redis
와 lua-resty-memcached
로 대체되었으며, 현재는 유지보수가 되지 않습니다.
OpenResty는 이후에 더 많은 NGINX C 라이브러리를 개발하지 않고, cosocket 기반
Lua 라이브러리에 집중할 것입니다. 이것이 미래입니다.
lua-resty-* 라이브러리
공식 OpenResty 저장소에는 18개의 lua-resty-*
라이브러리가 포함되어 있으며, Redis, MySQL, Memcached, WebSocket, DNS, 트래픽 제어, 문자열 처리, 프로세스 내 캐싱 등의 표준 라이브러리가 있습니다. 공식적으로 제공되는 것 외에도 더 많은 서드파티 라이브러리가 있습니다. 이들은 매우 중요하며, 다음 섹션에서 이 라이브러리에 대해 더 많은 시간을 할애할 것입니다.
자체 유지보수 LuaJIT 브랜치
OpenResty는 자체 OpenSSL 패치 외에도 자체 LuaJIT 브랜치를 유지보수합니다. 2015년 LuaJIT의 저자 Mike Pall은 은퇴를 선언하고 새로운 LuaJIT 유지보수자를 찾았지만, Mike는 적절한 유지보수자를 찾지 못했습니다. 그는 현재 주로 버그 수정 유지보수를 하고 있으며, 새로운 기능 개발은 중단되었습니다. 따라서 OpenResty는 자체 LuaJIT 브랜치를 유지보수합니다.
Lua에 비해 LuaJIT는 많은 중요하고 독특한 기능을 추가했지만, 많은 엔지니어들이 이를 알지 못하므로 반쯤 숨겨진 기술로, 나중에 소개하겠습니다.
테스트 프레임워크
OpenResty의 테스트 프레임워크는 test-nginx로, Perl로 개발되었으며 이름에서 알 수 있듯이 NGINX 관련 프로젝트를 테스트하기 위해 특별히 설계되었습니다. OpenResty의 공식 테스트 케이스는 C 모듈과 lua-resty
라이브러리 모두 test-nginx
로 구동됩니다. 이는 일반적인 단언 기반 프레임워크와는 달리 훨씬 더 강력하고 독립적인 시스템입니다.
일부 OpenResty 기여자들도 이 테스트 프레임워크를 완전히 이해하지 못하고 있으며, 때로는 복잡한 C와 Lua 코드를 포함한 PR을 제출하지만 여전히 해당 테스트 케이스를 작성하는 데 어려움을 겪습니다. 따라서 OpenResty 프로젝트의 /t
디렉토리에 있는 테스트 케이스를 보고도 혼란스럽다면, 자신을 의심하지 마십시오. 대부분의 사람들이 그렇습니다.
test-nginx
외에도, mockeagain 프로젝트는 느린 네트워크를 시뮬레이션하여 프로그램이 한 번에 한 바이트씩 읽고 쓸 수 있도록 합니다. 이는 웹 서버를 위한 매우 유용한 도구입니다.
디버그 도구 체인
OpenResty 프로젝트는 코드를 과학적이고 동적으로 디버그하는 방법에 많은 노력을 기울였습니다.
OpenResty의 두 프로젝트, openresty-systemtap-toolkit과 stapxx는 systemtap
이라는 동적 디버깅 및 추적 도구를 기반으로 합니다. systemtap
을 사용하는 가장 큰 장점은 대상 애플리케이션에 전혀 침투하지 않으면서도 생체 내 분석을 가능하게 한다는 것입니다.
예를 들어, systemtap
은 병원에 가서 CT 스캔을 받는 것과 같아서 고통스럽지 않고 지각할 수 없습니다. 더 나아가, systemtap
은 성능 분석을 위한 시각적 플레임 그래프를 생성할 수 있으며, 이는 나중에 설명하겠습니다. 여기서는 플레임 그래프를 통해 어떤 느낌인지 살펴보겠습니다.
패키지
OpenResty의 패키징 스크립트는 다양한 배포 운영 체제(예: CentOS, Ubuntu, macOS 등)에서 수동으로 작성되어 더 나은 제어성을 제공합니다. 설치 후 디렉토리 구조를 소개할 때 이미 이러한 패키징 관련 프로젝트를 다루었습니다: openresty-packaging과 home-brew. 이에 관심이 있다면 직접 학습할 수 있으며, 여기서는 반복하지 않겠습니다.
엔지니어링 도구
이러한 대형 프로젝트 외에도 OpenResty에는 대부분 숨겨진 여러 엔지니어링 도구가 있습니다.
예를 들어, openresty-devel-utils는 OpenResty와 NGINX를 개발하기 위한 도구 세트입니다. 물론, 이들도 Perl로 개발되었으며 대부분의 도구는 문서화되지 않았습니다. 그러나 OpenResty 개발자에게는 이러한 도구가 매우 유용합니다. 몇 가지를 골라 간단히 소개하겠습니다.
- lj-releng은 LuaJIT 코드 검사 도구로,
luacheck
와 유사하며 전역 변수의 잠재적 문제를 찾을 수 있습니다. - reindex는 인덱스를 재구성하는 도구로,
test-nginx
테스트 케이스를 포맷하고 테스트 케이스 번호를 재배열하며 불필요한 공백을 제거합니다.reindex
는 OpenResty 개발자들이 매일 사용하는 도구 중 하나입니다. - opsboy는 자동화된 배포를 수행하는 도구로, OpenResty가 각 릴리스 전에 AWS EC2 클러스터에서 수행하는 회귀 테스트를 배포하고 구동하는 데 사용됩니다. 자세한 정보는 공식 문서를 참조할 수 있습니다.
opsboy
는 Perl로 구현된 DSL입니다. OpenResty 저자는 문제를 해결하기 위해 다양한 DSL을 만드는 것을 좋아합니다.
요약
오늘은 주로 OpenResty 설치 후의 디렉토리 구조와 그 뒤에 있는 몇 가지 하위 프로젝트를 학습했습니다. 오늘의 내용을 학습한 후, OpenResty의 프로젝트에 대해 더 많이 이해할 수 있기를 바랍니다. OpenResty는 NGINX 로드 밸런싱과 리버스 프록시의 범위를 훨씬 넘어서 자신만의 생태계를 구축했습니다. 다음 시간에는 이에 대해 자세히 이야기하겠습니다.