OpenResty FAQ | 권한 있는 프로세스 권한, 실행 단계 등

API7.ai

November 11, 2022

OpenResty (NGINX + Lua)

이 글은 자주 묻는 여섯 가지 질문을 담고 있습니다:

1. 권한이 있는 프로세스 권한

Q: 권한이 있는 프로세스란 무엇인가요? 비권한 사용자가 어떻게 root 권한을 얻을 수 있나요? 권한이 있는 프로세스의 사용 시나리오를 소개해 주실 수 있나요?

A: 권한이 있는 프로세스의 권한은 master 프로세스의 권한과 동일합니다. 비권한 사용자로 OpenResty를 시작하면 master 프로세스가 사용자의 권한을 상속받으므로, "권한이 있는 프로세스"는 현재 아무런 권한이 없습니다.

일반 사용자가 프로세스를 시작할 때 root 권한이 없다는 것은 쉽게 이해할 수 있습니다.

권한이 있는 프로세스의 사용 시나리오에 대해 말하자면, 일반적으로 높은 권한이 필요한 작업에 사용합니다. 예를 들어 로그 정리나 OpenResty 재시작 등이 있습니다. 그러나 보안 위험 때문에 권한이 있는 프로세스로 worker 프로세스 작업을 실행하지 않도록 주의해야 합니다.

한 개발자가 모든 timer 작업을 권한이 있는 프로세스에서 실행합니다. 왜 그럴까요? 권한이 있는 프로세스는 하나뿐이기 때문에, 이렇게 하면 timer가 반복적으로 시작되지 않습니다.

이 개발자는 worker.id를 사용하지 않고 목표를 달성했기 때문에 "똑똑하다"고 할 수 있습니다. 그러나 timer 작업이 클라이언트의 입력에 의존한다면 매우 위험하다는 것을 잊지 마세요.

2. 단계별 실행 및 디버깅

Q: ngx.say('hello')를 실행한 후, OpenResty는 현재 단계의 나머지 로직을 실행한 후 클라이언트에 직접 응답할까요? 즉, 이후 단계를 계속 실행하지 않을까요?

A: 그렇지 않습니다. 실행 단계를 살펴볼 수 있습니다:

image

먼저 content 단계에서 ngx.say를 테스트한 다음, log 또는 body filter 단계에서 ngx.log를 사용하여 로그를 출력할 수 있습니다.

이전 글에서 OpenResty에서 코드 디버깅을 하는 문제에 대해 구체적으로 언급하지 않아 개발자들이 혼란스러워할 수 있습니다.

OpenResty에는 코드 디버깅을 위한 고급 기능(예: 중단점)이 없으며(유료 플러그인이 있지만 사용해보지 않았습니다), ngx.sayngx.log를 사용하여 출력을 확인할 수밖에 없습니다. 제가 아는 모든 개발자들, OpenResty 저자 및 기여자들도 이렇게 디버깅을 합니다. 따라서 강력한 테스트 케이스와 디버그 로그가 보장되어야 합니다.

3. ngx.exit의 실제 사용

Q: 이전 글에서 OpenResty의 HTTP 상태 코드에는 ngx.OK라는 특수한 상수가 있다고 설명했습니다. ngx.exit(ngx.OK)를 실행하면 요청이 현재 단계를 종료하고 다음 단계로 이동하며 클라이언트에 직접 반환되지 않습니다.

ngx.OK는 HTTP 상태 코드로 간주되어서는 안 되며, 그 값은 0입니다. 제 이해는 다음과 같습니다:

  • ngx.exit(ngx.OK), ngx.exit(ngx.ERROR) 또는 ngx.exit(ngx.DECLINED)를 실행하면 요청이 현재 단계를 종료하고 다음 단계로 이동합니다.
  • ngx.exit(ngx.HTTP_*)ngx.HTTP_*의 다양한 HTTP 상태 코드를 매개변수로 받으면 클라이언트에 직접 응답합니다.

제 이해가 맞는지 모르겠습니다.

A: 첫 번째 질문에 대해, ngx.ok는 HTTP 상태 코드가 아니라 OpenResty의 상수이며 값은 0입니다.

두 번째 질문에 대해, ngx.exit의 공식 문서가 정확한 답이 될 수 있습니다:

  1. 상태 >= 200(즉, ngx.HTTP_OK 이상)일 때, 현재 요청의 실행을 중단하고 상태 코드를 nginx에 반환합니다.

  2. 상태 == 0(즉, ngx.OK)일 때, 현재 단계 핸들러(또는 content_by_lua* 지시어가 사용된 경우 컨텐츠 핸들러)만 종료하고 현재 요청에 대한 이후 단계(있는 경우)를 계속 실행합니다.

그러나 문서는 ngx.exit(ngx.ERROR)ngx.exit(ngx.DECLINED)를 OpenResty가 어떻게 처리하는지 언급하지 않습니다. 다음과 같이 테스트할 수 있습니다:

location /lua {
    rewrite_by_lua "ngx.exit(ngx.ERROR)";
    echo hello;
}

location을 방문하면 HTTP 응답 코드가 비어 있고, 응답 본문도 비어 있으며, 다음 실행 단계로 이동하지 않음을 확인할 수 있습니다.

OpenResty 학습 과정이 깊어질수록, 문서나 테스트 케이스로 답변할 수 없는 질문이 생길 것입니다. 이때는 자신의 테스트 케이스를 만들어 아이디어를 검증해야 합니다. 수동으로 할 수도 있고, test::nginx로 구축된 테스트 케이스 세트에 테스트를 추가할 수도 있습니다.

4. 변수와 경쟁 조건

Q: 앞서 언급했듯이, ngx.var 변수의 범위는 nginx Clua-nginx-module 모듈 사이입니다.

  1. 이 부분이 잘 이해가 안 됩니다. 요청 관점에서 보면, worker 프로세스 내의 단일 요청을 의미하는 건가요?

  2. 제 이해로는, 모듈 내에서 변수를 조작할 때 두 작업 사이에 블로킹 작업이 있으면 경쟁 조건이 존재할 수 있습니다. 따라서 두 작업 사이에 블로킹 작업이 없고, CPU 시간이 다 되었을 때 현재 프로세스가 준비 큐에 들어가면 경쟁 조건이 존재할 가능성이 있나요?

A: 이 질문들을 살펴보겠습니다.

먼저, ngx.var 변수에 대해, 당신의 이해가 맞습니다. ngx.var의 생명주기는 요청과 동일하며, 요청이 끝나면 사라집니다. 그러나 그 장점은 C 모듈과 Lua 코드 사이에서 데이터를 전달할 수 있다는 점입니다. 이는 다른 몇 가지 방법으로는 불가능합니다.

둘째, 두 작업 사이에 yield 작업이 있으면 블로킹 작업이 아니라 경쟁 조건이 존재할 수 있습니다. 블로킹 작업이 있을 때는 경쟁 조건이 없습니다. 즉, NGINX의 이벤트 루프에 주도권을 넘기지 않으면 경쟁 조건이 발생하지 않습니다.

5. shared dict는 잠금이 필요하지 않음

Q: 여러 worker가 동시에 데이터를 저장할 때, 잠금을 추가해야 할까요?

예를 들어:

resty --shdict 'dogs 10m' -e 'local dogs = ngx.shared.dogs
local lock= ngx.xxxx.lock
lock.lock()
 dogs:set("Jim", 8)
lock.unlock()
 local v = dogs:get("Jim")
 ngx.say(v)
 '

A: 여기서는 잠금을 추가할 필요가 없습니다. get 또는 set 작업 모두 shared dict의 작업은 원자적이기 때문입니다. OpenResty는 이미 이러한 잠금과 같은 처리를 고려했습니다.

6. OpenResty의 시간 작업

Q: ngx.now()를 사용하여 시간을 가져오는 것은 resume 함수 복원 단계에서 발생하나요?

A: NGINX는 성능을 최우선으로 설계되었으며 시간을 캐시합니다. ngx.now의 소스 코드를 통해 이를 확인할 수 있습니다:

static int
ngx_http_lua_ngx_now(lua_State *L)
{
    ngx_time_t              *tp;

    tp = ngx_timeofday();

    lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L));

    return 1;
}

보시다시피, 현재 시간을 가져오는 ngx.now() 함수 뒤에는 NGINX의 ngx_timeofday 함수가 있습니다. ngx_timeofday 함수는 매크로 정의입니다:

#define ngx_timeofday()      (ngx_time_t *) ngx_cached_time

여기서 ngx_cached_time의 값은 ngx_time_update 함수에서만 업데이트됩니다.

따라서, 문제는 "ngx_time_update 함수가 언제 호출되는가?"로 바뀝니다. NGINX 소스 코드를 추적해보면, ngx_time_update 호출은 이벤트 루프에서 발생하므로 이 문제는 해결됩니다.

요약

이 질문들을 통해 알 수 있듯이, 오픈소스 프로젝트의 장점은 소스 코드에서 단서를 따라 답을 찾을 수 있다는 것입니다. 이는 사건을 해결하는 느낌을 줄 것입니다.

마지막으로, 소통과 Q&A를 통해 배운 것을 얻을 수 있도록 도와드리고자 합니다. 이 글을 공유해 주시고 함께 소통하며 개선해 나가길 바랍니다.