본문 바로가기
프로그래밍/Web Basic

Nginx 웹서버 정리 3

by YuminK 2023. 11. 17.

nginx Process Roles

마스터 프로세스가 처리하는 작업은 다음과 같다.

 

읽고 구성을 검증하는 것

소켓을 생성하고 연결하며 닫는 것

구성된 워크 프로세스 수를 유지하고, 시작/종료 하는 것

서비스 중단없이 재구성하는 것

논스탑 바이너리 업데이트를 제어하는 것(새로운 바이너리를 시작하고 필요시 이전 것을 롤백)

로그 파일 다시 열기

내장 펄 스크립트 컴파일링

 

워커 프로세스는 받아들이고 관리한다. 클라이언트로부터 연결을 진행한다. 리버스 프록시와 필터링 기능을 제공하고 엔진엑스가 할 수 있는 대부분의 것을 제공한다. 엔진엑스 인스턴스의 행동 모니터링과 관련하여, 시스템 관리자는 워커가 실제 동작을 반영하고 있는지 확인해야 한다. 

 

캐시로더 프로세스는 온디스크 캐시 아이템을 확인하고 엔진엑스의 인메모리 데이터베이스에 캐시 메타데이터를 채우는 것을 담당한다. 필수적으로 캐시로더는 특별히 할당된 디렉토리 구조의 디스크에 이미 저장된 파일을 사용하기 위해 엔진엑스 인스턴스를 준비한다. 디렉토리를 순회하며 캐시 컨텐츠 메타데이터를 확인한다. 공유된 메모리내에서 연관된 데이터를 업데이트하고 모든 것이 문제가 없을 때 나간다. 

 

캐시 매니저는 캐시 expiration과 invalidation을 처리한다. 일반적인 엔진엑스 동작에서 인 메모리에서 유지되며 실패시 마스터 프로세스에 의해 재시작된다. 

 

Brief Overview of nginx Caching

캐시 키는 설정 가능하며, 다른 요청별 파라미터에 어떤 정보가 들어갈지 제어할 수 있다. 캐시 키와 캐시 메타데이터는 공유된 메모리 세그먼트에 저장된다. 캐시로더, 캐시매니저 그리고 워커가 해당 데이터에 접근할 수 있다. 현재 운영체제 가상파일 시스템 메카니즘에 의해 암시된 최적화를 제외하고 인메모리 파일 캐싱이 없다. 각각의 캐시된 응답은 파일 시스템 내에서 다른 파일에 저장된다. (Nginx 구성에 따라 달라짐) 캐시에 컨텐츠를 저장하는 프로세스는 다음과 같다.

 

엔진엑스가 업스트림 서버로부터 응답을 읽고, 컨텐츠가 캐시 디렉토리 구조 밖에 있는 임시 파일에 적힌다. 엔진엑스가 요청에 대한 프로세싱을 마쳤을 때, 임시 파일은 이름이 다시 붙여지며(rename) 캐시 디렉토리로 옮겨진다. 만약 프록싱을 위한 임시 디렉토리 파일이 다른 파일 시스템에 있다면 파일은 복사된다. 따라서 임시 경로와 캐시 디렉토리를 같은 파일 시스템 내에 두는 것이 추천된다. 이는 파일을 삭제할 때도 안전하게 할 수 있다. 서드파티 extensions를 통해 원격으로 캐시를 제어하는 것이 가능하지만 더 많은 통합 기능이 요구된다. 

 

엔진엑스의 구성설정은 단순하게 제공되도록 디자인 되었다. 메인 구성 설정 파일은 주로 nginx.conf라고 불린다. 아파치 스타일의 분산된 설정은 지원하지 않는다. 구성 파일은 master process에 의해 읽히며 검증된다. 구성에는 main, http, server, upstream, location 같은 블록이 존재한다. 문맥은 절대 중복되지 않는다. 예를 들어 main 블록 내에 location 블록을 넣지 않는다. 글로벌 웹서버 설정 같은 것도 없다. 깨끗하고 논리적이며 수천 개의 지시문에도 읽을 수 있도록.

 

C 스타일 컨벤션을 따른다. 이는 특별한 접근이다. 

 

전형적인 http 요청 프로세싱은 다음과 같다.

1. 클라이언트가 http 요청을 보낸다.

2. Nginx 코어는 적절한 phase handler를 고른다.(매칭되는 요청에 따른 구성 location에서)

3. 구성이 되어 있는 경우, 로드 밸런서가 프록싱을 위한 업스트림 서버를 고른다.

4. Phase handler는 처리를 하고 각 결과 버퍼를 첫 필터에게 넘긴다.

5. 첫 필터는 결과를 두 번째 필터에게 넘긴다. (계속 진행)

6. 마지막 결과가 클라이언트에게 넘겨진다. 

 

워커의 실행 루프에서 이뤄지는 동작은 다음과 같다.

1. ngx_worker_process_cycle()를 시작한다.

2. os에 구체적인 메머니즘으로 이벤트를 처리한다. (Epoll or kqueue 같은)

3. 이벤트를 받아들이고 관련 액션들을 전송한다(dispatch)

4. 프로세스/프록시는 헤더와 바디를 요청한다.

5. header와 body를 가진 response 정보를 생성하여 클라이언트에게 넘긴다(stream)

6. 요청을 마무리한다.

7. 타이머와 이벤트를 다시 초기화한다. 

 

http 요청 프로세싱의 디테일은 다음과 같다.

1. 요청 프로세스를 초기화한다.

2. 헤더를 처리한다.

3. 바디를 처리한다.

4. 연관된 handler를 호출한다.

5. Processing phases를 통해 시작한다.

 

엔진엑스가 http 요청을 다룰 때, 다양한 phases의 처리를 거친다. 각 phase에는 처리하는 handler가 존재한다. 일반적으로 phase handler는 요청하고 연관된 결과를 생성한다. 또한 구성파일에 정의된 location에 연결된다. Phase handlers는 전형적으로 4가지 일을 한다. Location 구성 정보를 얻고, 적절한 응답을 생성하며, 헤더를 보내고 바디를 보낸다. 한 핸들러는 요청을 묘사하는 구조체를 인자로 가진다. 

 

http 요청 헤더가 읽히면, 엔진엑스는 연관된 가상 서버 구성을 찾는다. 만약 가상서버가 찾아지면 다음 6개의 phase에 따라 요청이 처리된다.

 

1. 서버가 phase를 다시 쓴다.

2. Location phase

3. location은 phase를 다시 쓴다. (이는 요청을 다시 이전 phase로 전달할 수 있다)

4. Access control phase

5. try_files phase

6. Log phase

 

요청에 대한 응답에 필수적인 컨텐트를 넣기 위한 시도로, 엔진엑스는 요청을 적절한 컨텐츠 핸들러에게 전달한다. 정확한 location 구성에 따라 엔진엑스는 무조건적인 핸들러를 먼저 시도해볼 수도 있다. like perl, proxy_pass, flv, mp4, 만약 요청이 위 컨텐츠 핸들러와 매칭되지 않는다면, 다음에 따라오는 핸들러에 중에 하나로 선택된다. 정확한 순서로 random index, index, autoindex, gzip_static, static

 

컨텐츠 핸들러의 컨텐츠는 이후 필터로 넘겨진다. 필터 또한 location과 연결이 되는데 location과 매칭되는 다양한 필터가 있을 수 있다. 필터는 핸들러에 의해 생성된 결과를 조정하는 task를 처리한다. 필터 실행의 순서는 컴파일 시간에 결정된다. 존재하는 엔진엑스 구현에서, 필터는 오직 아웃바운드 변경만 할 수 있다.

 

필터는 특정한 디자인 패턴을 따른다. 필터가 불리면 처리하고 다음 필터를 처리하고 마지막 필터까지 반복한다. 이후에 엔진엑스는 응답을 마무리한다. 필터는 이전 필터가 끝나길 기다릴 필요가 없다. 다음 필터는 이전 필터의 입력을 사용할 수 있게 되지 마자 자체 작업을 시작할 수 있다. 헤더 필터와 바디 필터가 존재한다. 

 

헤더 필터는 다음 스탭으로 이뤄진다.

1. 응답에 대해 작업을 할지 결정한다. 

2. 응답을 처리한다.

3. 다음 필터를 호출한다.

 

바디 필터는 생성된 컨텐츠를 변형한다. 바디 필터의 예시는 다음과 같다.

1. 서버사이드 includes

2. XSLT filtering

3. Image filtering

4. Charset 수정

5. gzip 압축

6. Chunked encoding

 

필터 체인 이후에는 writer로 응답이 넘겨진다. copy filter와 postpone filter라는 특별한 목적의 추가적인 필터가 존재한다. 카피 필터는 프록시 임시 디렉토리에서 저장될 응답 컨텐츠를 메모리 버퍼를 채운다. 포스트폰 필터는 서브 리퀘스트에 사용된다.

 

서브 리퀘스트는 요청/응답 처리에서 매우 중요한 매커니즘이다. nginx에서 가장 강력한 부분 중에 하나이다. 서브리퀘스트를 통해 엔진엑스는 클라이언트가 요청한 주소가 아닌 다른 url로 결과를 반환할 수 있다. 여러 웹 프레임워크는 이를 내부 redirect라고 한다. 엔진엑스는 다양한 서브 리퀘스트를 처리하고 하나의 응답으로 조합할 수 있을 뿐만 아니라 중첩되며 구조화된 형태도 가능하다. 하나의 서브 리퀘스트는 자신의 서브 리퀘스트를 실행할 수 있다. 

 

서브 리퀘스트는 파일을 특정 하드 디스크, 핸들러, 업스트림 서버에 매핑할 수 있다. 서브리퀘스트는 원본 응답 데이터를 기반으로 추가적인 컨텐츠를 넣을 때 유용하다. 예를 들어 ssi(서버 사이드 인클루트) 모듈은 반환된 문서 컨텐츠의 파싱을 위해 필터를 사용하며, 일클루드directive를 구체적인 URL컨텐츠로 대체한다. 하나의 도큐먼트의 전체 컨텐츠를 URL로서 다루는 필터를 만드는 것이 예시가 될 수 있다. 그리고 새로운 도큐먼트를 URL에 붙인다. 

 

업스트림은 역방향 프록시인 컨텐츠 핸들러로 식별할 수 있는 것을 구현하는 데 사용된다. 업스트림 모듈은 주로 요청이 업스트림 서버나 백엔드로 전송되도록 처리한다. 그리고 업스트림 서버로 부터 응답을 받는다. 여기서 결과 필터는 호출되지 않는다. 업스트림 모듈이 하는 것은 업스트림 서버가 읽고 쓰기가 가능할 때 처리될 콜백을 등록하는 것이다. 

 

1. 업스트림 서버에 보내질 리퀘스트 버퍼를 만드는 것

2. 업스트림 서버 연결을 재설정하는 것

3. 업스트림 응답의 첫 번째 비트를 처리하고 업스트림 서버에서 수신된 페이로드에 포인터를 저장하는 것

4. 리퀘스트를 중단하는 것

5. 엔진엑스가 업스트림 서버에서 읽는 것이 끝났을 때, 요청을 마무리 하는 것

6. 응답을 정리하는 것(trim)

 

로드밸런서 모듈은 둘 이상의 업스트림 서버가 적합할 때, 업스트림 서버를 고를 능력을 제공하기 위해 proxy_pass핸들러에 연결된다. 로드밸런서는 구성 파일 속성 활성화를 등록하고, 추가적인 업스트림 초기화 기능을 제공한다. (dns 등에서 업스트림 이름을 해결하기 위해), 연결 구조를 초기화하여, 요청을 어디로 라우팅할지 처리하며 스탯 정보를 업데이트한다. 현재 엔진엑스는 2개의 표준 원칙을 지원한다. 업스트림 서버에 로드밸런싱 처리를 위해(라운드 로빈과 ip hash)

 

업스트림과 로드밸런싱 핸들링 메커니즘은 실패한 업스트림 서버를 감지하기 위한 알고리즘을 포함하며 새로운 요청을 남아있는 하나에 재 라우팅하는 기능이 초함된다. 더욱 많은 작업이 해당 기능을 강화하기 위해 예정되어 있다. 다음 버전의 엔진엑스에서 여러 업스트림 서버에 처리를 분산하는 기능과 상태 확인 부분이 크게 향상될 것이다. 

 

구성 파일에서 추가적 변수 설정을 제공하는 흥미로운 모듈이 존재한다. 엔진엑스의 변수들이 초기화되고 다양한 모듈에서 초기화 되는 동안, 변수 전용 2가지 모듈이 존재한다. geo와 map. geo 모듈은 ip주소 기반으로 클라이언트 트랙킹을 용이하게 한다. 이 모듈은 클라이언트의 ip 주소에 따라 임의 변수를 생성한다. 다른 모듈인 map은 다른 변수로부터 변수 생성을 허용한다. 필수적으로 호스트 이름과 다른 런타임 값들의 유동적 매핑 기능을 제공하면서. 이런 종류의 모듈은 변수 핸들러라 불린다. 

 

단일 엔진엑스 워커에서 구현된 메모리 할당 메커니즘은 어느 정도 아파치에서 영감을 받았다. 엔진엑스 메모리 관리에 대한 고수준 설명은 다음과 같다. 각 연결에서, 필수적인 메모리 버퍼는 동적으로 할당되며, 응답과 요청의 헤더와 바디를 조작하는 것과 저장을 위해 사용된다. 그런 다음 연결이 끊기면 해제된다. 엔진엑스가 메모리 내에서 데이터를 복사하는 것을 최대한 막으려고 하는 것과 대부분의 데이터는 포인터 값에 넘겨진다. (memcpy 호출 없이.

 

모듈에 의해 생성된 응답이 있을 때, 검색된 컨텐츠는 메모리 버퍼에 저장되며 후에 버퍼 체인 링크에 추가된다. 후속처리는 이 버퍼 체인 링크에서도 동작한다. 엔진엑스에서 버퍼 체인은 꽤 복잡하다. 모듈 유형에 따라 달라지는 여러 처리 시나리오가 있기 때문이다. 예를 들어 바디필터 모듈을 구현하는 중에 버퍼를 정확히 관리하는 것이 까다로울 수 있다. 이러한 모듈은 오직 하나의 버퍼(체인 링크)를 한번에 처리할 수 있고 인풋 버퍼를 덮어 쓸 것인지 항상 결정해야만 한다. 또한 새롭게 할당된 버퍼와 교체할지, 새로운 버퍼 이전 혹은 이후에 삽입할지. 

 

때때로 모듈이 여러 버퍼를 수신하여 작동해야 하는 불완전한 버퍼 체인을 갖게 되는 경우도 있다. 그러나 이 시점에서 엔진엑스는 오직 저수준 api를 제공하여 버퍼 체인을 동작하게 한다. 따라서 서드 파티 모듈 개발자가 실질적인 구현 이전에 엔진엑스의 해당 부분에 매우 능숙해야 한다. 

 

상단의 접근에서 참고할 것은 하나의 커넥션을 위해 할당된 메모리 버퍼가 있다는 것이다. 따라서 long-lived 연결 처리를 위해 추가 메모리가 유지된다. 동시에 일반적인 keep alive 연결에서 엔진엑스는 그저 550bytes의 메모리를 사용한다. 나중에 엔진엑스에서 가능한 최적화는 long-lived 커넥션을 위한 메모리 버퍼를 공유하고 재사용하는 것이 될 것이다. 

 

메모리 할당 관리 업무는 엔진엑스 pool allocator가 처리한다. 공유된 메모리 지역은 뮤텍스와 캐시 메타데이터, ssl 세션 캐시와 대역폭 정책과 측정에 관련된 정보 를 받아들이기 위해 사용된다. 공유된 메모리 할당을 관리하기 위해 엔진엑스에서 구현된 slab allocator가 있다. 

 

메모리 동시 접근을 안전하게 처리하기 위해 뮤텍스와 세마포어 같은 잠금 메커니즘을 제공한다. 복잡한 데이터 구조를 조직화하기 위해 엔진엑스는 레드 블랙 이진트리 구현을 제공한다. 레드블랙 이진 트리는 공유 메모리에서 캐시 메타데이터를 유지하기 위해 사용되며, 비정규 location 선언과 다른 task를 처리한다.

 

https://aosabook.org/en/v2/nginx.html

'프로그래밍 > Web Basic' 카테고리의 다른 글

[Express] MySql 연동  (0) 2023.12.17
[Express] express 공식 문서 정리  (0) 2023.12.05
NginX 웹서버 정리 2  (0) 2023.11.17
Nginx 웹서버 정리 1  (0) 2023.11.17
Apache 웹서버 정리  (1) 2023.11.12

댓글