OpenResty FAQ | OpenResty의 실제 활용 사례

API7.ai

February 10, 2023

OpenResty (NGINX + Lua)

  1. OpenResty, API Gateway, Lua에 관한 몇 가지 Q&A
  2. OpenResty FAQ | 권한 있는 프로세스 권한, 실행 단계 등
  3. OpenResty FAQ | 테스트를 위한 네트워크 구조, SSL 관련 기능, DSL, ab 도구
  4. OpenResty FAQ | 동적 로드, NYI, Shared Dict 캐싱

이제 OpenResty, 마이크로서비스 API 게이트웨이의 마지막 섹션을 마쳤습니다. 뒤처지지 않고 적극적으로 학습하고 실습하며 열정적으로 생각을 남겨주신 여러분께 축하드립니다.

여기서 몇 가지 전형적이고 흥미로운 질문을 골라 여러분과 공유하려고 합니다. 오늘은 이 5가지 질문을 살펴보겠습니다.

질문 1: OpenResty의 실제 사용 사례

설명: 강의가 거의 끝났고, 기본적으로 이해할 수 있지만, 제 자신의 실습은 아직 부족합니다(현재 업무에서 사용하지 않음). 업무에서 사용할 수 없기 때문입니다. 그러나 이는 매우 유용한 시리즈입니다. 저자님의 지속적인 공유에 감사드리며, 나중에 업무에서 소개할 예정입니다.

업무에서 OpenResty를 도입하는 것에 대해 이야기하고 싶습니다. 이는 논의할 가치가 있는 주제입니다.

OpenResty는 NGINX를 기반으로 lua-nginx-module C 모듈과 수많은 lua-resty 라이브러리를 추가한 것이므로, OpenResty는 NGINX를 대체하기에 좋은 선택지입니다. 이는 OpenResty를 시작하는 가장 저렴한 방법입니다. 물론 이 대체 과정에는 위험이 따르므로, 다음 세 가지 사항을 주의해야 합니다.

첫째, 온라인 NGINX 버전이 OpenResty의 주 버전과 동일한지 확인해야 합니다. 예를 들어 OpenResty 1.15.8.1은 NGINX 1.15.8을 사용합니다. 현재 온라인 NGINX 버전이 OpenResty의 최신 버전보다 높다면, OpenResty로 전환할 때 주의해야 합니다. OpenResty는 여전히 업그레이드가 느리고, NGINX의 주 버전보다 6개월에서 1년 정도 뒤쳐져 있습니다. 온라인 NGINX 버전이 OpenResty와 동일하거나 낮다면, 업그레이드의 전제 조건이 갖춰진 것입니다.

둘째, 테스트. 테스트는 가장 중요한 측면 중 하나입니다. OpenResty로 NGINX를 대체하는 데는 큰 위험이 없지만, 여전히 위험이 존재합니다. 예를 들어, 컴파일이 필요한 사용자 정의 C 모듈이 있는지, OpenResty가 의존하는 openssl 버전, OpenResty가 NGINX에 적용하는 패치가 비즈니스에 영향을 미치는지 등입니다. 이를 검증하기 위해 일부 비즈니스 트래픽을 복제해야 합니다.

셋째, 트래픽 전환. 기본 검증이 통과된 후에도, 실제 트래픽의 카나리 릴리스를 온라인에서 검증해야 합니다. 빠르게 롤백하기 위해, 원래의 NGINX 서비스를 직접 대체하는 대신 몇 대의 새로운 서버를 열어 OpenResty를 배포할 수 있습니다. 문제가 없다면, 바이너리 파일을 핫 업그레이드하거나 LB에서 NGINX를 점진적으로 제거하고 교체하여 업그레이드할 수 있습니다.

NGINX를 대체하는 것 외에도, OpenResty에는 WAF와 API 게이트웨이라는 두 가지 쉬운 진입점이 있습니다. 이 두 가지는 모두 높은 성능과 동적 요구 사항이 있는 시나리오이며, 즉시 사용할 수 있는 오픈소스 프로젝트가 있습니다. 이전에 일부를 다룬 적이 있습니다.

비즈니스 수준에서 OpenResty를 더 깊이 있게 사용하려면, 기술 외에도 더 많은 요소를 고려해야 합니다. 예를 들어, OpenResty 관련 엔지니어를 쉽게 채용할 수 있는지, OpenResty가 회사의 기존 기술 시스템과 통합될 수 있는지 등입니다.

일반적으로 NGINX를 대체하는 것으로 시작한 후, 점차적으로 OpenResty를 사용하는 것이 좋은 아이디어입니다.

질문 2: OpenResty의 데이터베이스 캡슐화

설명: 이전 글에 따르면, ..(문자열 연결 연산자)를 가능한 한 적게 사용해야 합니다. 특히 코드 핫 패스에서는 더욱 그렇습니다. 하지만 데이터베이스 접근을 처리할 때, 변수를 삽입하여 SQL 문을 동적으로 생성해야 하는데, 이는 일반적인 사용 사례입니다. 하지만 이 요구 사항에 대해, 문자열 연결이 가장 쉬운 방법이라고 느끼며, 다른 간단하고 고성능의 방법을 생각할 수 없습니다.

먼저 SystemTap이나 이전 글에서 소개한 다른 도구를 사용하여 SQL 문의 연결이 시스템의 병목인지 분석할 수 있습니다. 병목이 아니라면 최적화할 필요가 없습니다. 조기 최적화는 모든 악의 근원이기 때문입니다.

SQL 문의 연결이 실제로 병목이라면, 데이터베이스 prepare 문을 사용하여 최적화하거나 배열을 사용하여 연결할 수 있습니다. 하지만 lua-resty-mysqlprepare 지원은 TODO 상태이므로, 배열 연결만 사용할 수 있습니다. 이는 일부 lua-resty 라이브러리의 공통적인 문제입니다. 대부분의 기능을 구현하고 정상적으로 실행되지만, 업데이트가 제때 이루어지지 않습니다. 데이터베이스 prepare 문 외에도, lua-resty-rediscluster를 지원하지 않습니다.

문자열 연결과 lua-resty 라이브러리는 OpenResty가 DSL로 완전히 해결하려는 종류의 문제입니다. 컴파일러 기술을 사용하여 문자열을 연결하는 배열을 자동으로 생성하고, 이러한 세부 사항을 상위 사용자로부터 숨기는 것입니다. DSL wirelang을 사용하여 다양한 lua-resty 네트워크 통신 라이브러리를 자동으로 생성하고, 수작업으로 작성할 필요를 없애는 것입니다.

이것은 멋지게 들리지만, 우리는 한 가지 문제를 직시해야 합니다: 자동 생성된 코드는 개발자에게 불친절합니다. 생성된 코드를 학습하거나 수정하려면 컴파일러 기술과 오픈소스가 아닐 수 있는 DSL을 배워야 하며, 이는 커뮤니티에 참여하는 장벽을 점점 더 높입니다.

질문 3: OpenResty 웹 프레임워크

설명: OpenResty로 웹 프로젝트를 만들고 싶지만, 주로 고통스러운 점은 성숙한 프레임워크를 찾을 수 없고, 많은 바퀴를 다시 만들어야 한다는 것입니다. 예를 들어, 데이터베이스 작업 문제입니다. 동적으로 SQL 문을 생성하고 일관된 작업을 수행할 수 있는 클래스 라이브러리를 찾지 못했습니다. 그래서 저자님께 좋은 웹 프레임워크를 추천해 주실 수 있는지 묻고 싶습니다.

awesome-resty 저장소에서 웹 프레임워크에 대한 특별한 카테고리가 있으며, 20개의 오픈소스 프로젝트가 있지만 대부분은 정체되어 있습니다. 그 중 Lapis, lor, vanilla는 시도해 볼 만한 세 가지 프로젝트입니다.

실제로 강력한 웹 프레임워크가 없으면, OpenResty는 대형 프로젝트를 처리할 때 압도당합니다. 이는 OpenResty를 비즈니스 시스템에 사용하는 사람이 적은 이유 중 하나입니다.

질문 4: 응답 본문을 수정한 후 응답 헤더의 content-length를 변경하는 방법

설명: 응답 본문의 내용을 수정해야 하는 경우, 본문 필터에서만 변경할 수 있지만, 이는 본문 길이와 content-length 길이가 일치하지 않게 됩니다. 이를 어떻게 처리해야 할까요?

이 경우, 본문 필터 단계 전에 헤더 필터 단계에서 content-length 응답 헤더를 nil로 설정하고 반환하지 않고 스트리밍 출력을 해야 합니다.

다음은 샘플 코드입니다:

server {
    listen 8080;
    location /test {
            proxy_pass http://api7.ai;
            header_filter_by_lua_block {
                     ngx.header.content_length = nil
            }
            body_filter_by_lua_block {
                    ngx.arg[1] = ngx.arg[1] .. "abc"
            }
     }
}

이 코드에서 볼 수 있듯이, 본문 filter 단계에서 ngx.arg[1]은 응답 본문을 나타냅니다. 여기에 문자열 abc를 추가하면, 응답 헤더의 content-length가 부정확해지므로, 헤더 필터 단계에서 이를 비활성화할 수 있습니다.

또한, 이 예제는 OpenResty의 다양한 단계가 어떻게 함께 작동하는지 보여줍니다. 이를 주목하고 생각해 보시기 바랍니다.

질문 5: OpenResty의 Lua 패키지 경로

설명: lua_package_path는 Lua 의존성의 검색 경로로 설정된 것 같습니다. content_by_lua_file의 경우, 실험해 본 결과, 지시문에서 제공한 파일의 상대 경로를 기반으로 접두사 아래에서만 검색하며, lua_package_path 아래에서는 검색하지 않습니다. 제 이해가 맞는지 모르겠습니다.

lua_package_path는 Lua 모듈을 로드하는 데 사용됩니다. 예를 들어, require 'cjson'을 호출하면 lua_package_path에 지정된 디렉토리로 이동하여 cjson 모듈을 찾습니다. 반면, content_by_lua_file은 디스크 상의 파일 경로를 따릅니다.

location /test {
     content_by_lua_file /path/test.lua;
 }

이것이 절대 경로가 아니라 상대 경로라면,

 content_by_lua_file path/test.lua;

그러면 OpenResty 시작 시 지정된 -p 디렉토리를 사용하여 절대 경로를 얻습니다.

마지막으로, 댓글 섹션에 계속해서 질문을 남겨주시면 계속 답변드리겠습니다. 소통과 Q&A를 통해 배운 것을 얻을 수 있도록 도와드리고자 합니다. 이 글을 공유하여 함께 소통하고 발전할 수 있기를 바랍니다.