Understanding Nginx Server and Location Block Selection Algorithms 요 사이트를 보고 공부한 글입니다. 근데 실질적으로는 파파고 버전에 가깝습니다.
Nginx는 가장 인기있는 웹 서버중 하나.
다수의 클라이언트와 동시 연결을 할 수 있으며, 높은 부하를 성공적으로 처리할 수 있다.
Nginx는 클라이언트의 요구가 이루어질때마다 요청을 처리하기 위해 어떤 구성 블록을 사용해야 하는지 결정하는 프로세스를 시작함.
서버 블록은 정의된 유형의 요청을 처리하는데 사용되는 가상 서버를 정의하는 Nginx의 하위 집합이다.
관리자는 여러 서버 블록을 설정하고 요청된 도메인 이름, 포트 및 IP주소에 따라 어떤 블록을 처리해야 하는지 결정한다.
로케이션 블록은 서버 블록내에 존재하며, 다양한 자원 및 URI 요구를 처리하는 방법을 정의하기 위해 사용됨. URI 공간은 관리자가 블록을 사용하여 원하는 방식으로 세분화 할 수 있다. 매우 유연한 모델임.
Nginx는 개별 가상 웹 서버 인스턴스로 기능하는 여러 서버 블록을 정의할 수 있도록 한다. 그래서 이러한 서버 블록중 어떤게 요구를 충족시킬 수 있는지 결정하는 절차가 필요하다.
이 프로세스 동안 Nginx가 관여하는 주요 서버 블록 디렉티브는 listen 지시문과 server_name 지시문이다.
Nginx는 먼저, 요청의 IP 주소와 포트를 확인한다.
listen 지시문은 일반적으로 서버 블록이 응답할 IP 주소 및 포트를 정의한다.
기본적으로 listen 지시문을 포함하지 않는 서버 블록에는 0.0.0:80의 수신 매개 변수가 지정된다.(루트가 아닌 일반 사용자가 Nginx를 실행하는 경우 0.0.0:8080)
이렇게 하면 이러한 블록은 포트 80의 모든 인터페이스에서 요청에 응답할 수 있지만, 이 기본값은 서버 선택 프로세스에서 큰 비중을 차지하지 않는다.
listen 지시문은 다음과 같은 설정을 할 수 있다:
Nginx는 listen 지시문의 특수성을 기반으로 요청을 보낼 서버 블록을 결정을 시도한다:
listen 지시어를 번역한다.0.0.0:80 값을 사용한다.111.111.111.111로 설정된 블록이 111.111.111:80이 된다.8888 포트로 설정된 블록이 0.0.0:8888이 된다.server_name 지시문을 평가한다.
listen지시문에서 동일한 수준의 특수성과 일치하는 서버 블록을 구분해야 할 때만server_name지시문을 평가함. 중요함.
예를 들어 example.com이 192.168.1.10의 포트 80에서 호스트되는 경우, 두 번째 블록의 server_name 지시문이 있음에도, example.com에 대한 요청은 항상 첫 번째 블록에 의해 처리된다.
server
{
listen 192.168.1.10;
...
}
server
{
listen 80;
server_name example.com;
...
}
다음으로, 동일한 특정 listen 지시를 가진 요청을 추가로 평가하기 위해, Nginx는 요청의 Host 헤더를 검사한다.
Nginx는 다음 공식을 사용하여 이러한 값을 평가한다:
Host 헤더의 값과 정확히 일치하는 server_name을 가진 서버 블록을 찾는다.server_name의 서버 블록을 찾는다. (ex) "*이름").server_name의 서버 블록을 찾는다. (ex) "이름*").~로 표시됨)을 사용하여 server_name을 정의하는 서버 블록을 평가한다.server_name이 사용된다.아래 예에서 요청의 Host 헤더를 host1.example.com로 설정한 경우 두 번째 서버가 선택됩니다:
server {
listen 80;
server_name *.example.com;
. . .
}
server {
listen 80;
server_name host1.example.com;
. . .
}
정확하게 일치하는 것이 없을 경우 선두 와일드카드를 사용하여 가장 길게 일치하는 것이 있는지 확인한다.
아래 예에서 요청의 Host 헤더가 www.example.org인 경우 두 번째 서버 블록이 선택됩니다:
server {
listen 80;
server_name www.example.*;
. . .
}
server {
listen 80;
server_name *.example.org;
. . .
}
server {
listen 80;
server_name *.org;
. . .
}
선두 와일드카드를 사용해도 일치하는 것을 찾지 못할경우 후속 와일드카드를 사용하여 일치하는 것 중 가장 긴 것을 찾는다.
예를 들어 요청에 Host 헤더가 www.example.com으로 설정된 경우 세 번째 서버 블록이 선택됩니다:
server {
listen 80;
server_name host1.example.com;
. . .
}
server {
listen 80;
server_name example.com;
. . .
}
server {
listen 80;
server_name www.example.*;
. . .
}
후속 와일드 카드를 사용해도 일치하는 것을 못찾을 경우 ~키워드를 사용해 가장 처음으로 정규식을 사용하는 것을 찾는다.
예를 들어, 요청의 Host 헤더가 www.example.com으로 설정된 경우 두 번째 서버 블록이 요청을 충족하도록 선택됩니다:
server {
listen 80;
server_name example.com;
. . .
}
server {
listen 80;
server_name ~^(www|host1).*\.example\.com$;
. . .
}
server {
listen 80;
server_name ~^(subdomain|set|www|host1).*\.example\.com$;
. . .
}
요청처리 서버 블록을 선택하기 위해 사용하는 프로세스와 마찬가지로 요청을 처리할 로케이션 블록을 결정하기 위한 알고리즘이 있다.
로케이션 블록은 서버 블록 또는 다른 로케이션 블록 내에 존재하며 요구 URI를 처리하는 방법을 결정하기 위해 사용된다.
구조
location `optional_modifier` `location_match` {
. . .
}
location_match는 Nginx가 요청 URI를 검사해야 하는 대상을 정의한다.
수식자의 유무는 Nginx가 로케이션 블록을 대조하는 방법에 영향을 준다.
= : 요청 URI가 지정된 위치와 정확히 일치하면 이 블록은 일치된 것으로 간주~ : 이 위치는 대소문자를 구분하는 정규 표현 일치로 해석~* : 로케이션블록은 대소문자를 구분하지 않는 정규 표현으로 해석^~ : 이 표현을 사용하고 또한 이 블록이 최고의 비정규 표현 일치로 선택되면 정규식 표현 조회가 이루어지지 않는다.접두사 일치의 예로, /site, /site/page1/index.html 또는 /site/index.html과 같은 요청 URI에 응답하기 위해 다음 위치 블록을 선택할 수 있다.
location /site {
. . .
}
정확한 요청 URI 매칭을 보여주기 위해 이 블록은 /page1과 같은 요청 URI에 응답하는 데 사용된다. 이 블록은 /page1/index.html 요청 URI에 응답하는 데 사용되지 않는다.
그리고 이 블록을 선택하고 인덱스 페이지를 사용하여 요구가 이행되면 요청의 실제 핸들러가 되는 다른 위치로 내부 리다이렉트가 진행된다는 점에 유의해야한다.
location = /page1 {
. . .
}
대소문자를 구분하는 정규식으로 해석해야 하는 로케이션의 예다. (수식자 ~가 사용되어 대소문자를 구분하는 정규 표현으로 해석됨)
이 블록은 /tortoise.jpg에 대한 요청을 처리하는 데 사용될 수 있지만 /FLOWER.PNG에 대한 요청은 처리할 수 없다.
location ~ \.(jpe?g|png|gif|ico)$ {
. . .
}
아래 예제는 대소문자를 구분하지 않기 떄문에 이 블록은 /tortoise.jpg와 /FLOWER.PNG 의 요청을 전부 처리할 수 있다.
location ~* \.(jpe?g|png|gif|ico)$ {
. . .
}
마지막으로 이 블록은 정규 표현식 일치가 최상의 비정규 표현식 일치로 결정되는 경우 정규 표현식 일치가 발생하는 것을 방지한다. /costumes/ninja.html에 대한 요청을 처리할 수 있다:
location ^~ /costumes {
. . .
}
이렇게 수식자는 로케이션 블록의 해석방법을 나타내지만, 이건 Nginx가 요청을 보낼 로케이션 블록을 결정하는데 사용되는 알고리즘을 알려주진 않는다.
로케이션 블록을 선택하는 알고리즘은 서버 블록을 선택하는 법과 유사하다.
Nginx는 요청 URI를 각 로케이션과 비교하여 가능한 로케이션 콘텍스트를 평가한다:
= 한정자를 사용하는 위치 블록이 요청 URI와 정확히 일치하는 경우 이 위치 블록이 즉시 선택되어 요청을 처리한다.= 포함) Nginx는 비수식 접두사를 평가한다. 지정된 요청 URI에 대해 가장 길게 일치하는 접두사 위치를 검색하여 다음과 같이 평가한다:^~ 수식어가 있으면 Nginx는 즉시 검색을 종료하고 이것을 사용하여 요청을 처리한다..^~ 수식어를 사용하지 않는 경우 검색의 초점을 이동할 수 있도록 잠시 동안 Nginx에 의해 저장된다.Nginx는 기본적으로 접두사 일치보다는 정규식 일치를 우선한다. 하지만 접두사 위치를 먼저 평가하여 관리자가 = 및 ^~ 수식어를 사용하여 위치를 지정함으로써 이러한 경향을 무시할 수 있다.
중요
접두사는 가장 긴것이 선택되지만
정규 표현식은 첫 번째 것이 선택된다.
가장 긴 접두사 일치 내에서 정규 표현식 일치가 Nginx가 정규 표현식 위치를 평가할 때 줄을 점프한다(jump the line.)
일반적으로 로케이션 블록을 한번 선택하면 그 시점 이후로는 해당 컨텍스느 내에서 완전히 처리된다.
위치 블록을 예측 가능한 방식으로 설계할 수 있는 일반적인 규칙이지만, 선택한 위치 내에서 특정 지시어에 의해 새 위치 검색이 트리거되는 경우가 있다는 것을 깨닫는 것이 중요하다.
"단 하나의 위치 블록" 규칙에 대한 예외는 요청이 실제로 처리되는 방식에 영향을 줄 수 있으며 위치 블록을 설계할 때 기대했던 것과 일치하지 않을 수 있다.
내부 리다리엑트로 이어질 수 있는 몇가지 지시사항:
index가 요구 처리에 사용되는 경우 항상 내부 리다이렉트로 이어지고, 로케이션 블록 탐색을 즉시 종료한다. 선택 프로레스를 가속화하기 위해 자주 사용됨.
하지만 디렉토리의 로케이션과 정확하게 일치하는 경우는, 요구가 실제의 처리를 위해서 다른 로케이션으로 리다이렉트 될 가능성이 있다.
예를 들어, 만약 rewrite를 포함하도록 마지막 예를 수정한다면, 우리는 요청이 때때로 try_files 지시문에 의존하지 않고 두 번째 위치로 직접 전달된다는 것을 알 수 있다.
index index.html;
location = /exact {
. . .
}
location / {
. . .
}
처리 위치를 재평가할 수 있는 또 다른 예는 try_files 지시어를 사용하는 것이다. 이 지시어는 Nginx에게 명명된 파일 또는 디렉터리 집합이 존재하는지 확인하도록 지시한다. 마지막 매개 변수는 Nginx가 내부 리디렉션할 URI일 수 있다.
root /var/www/main;
location / {
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
위의 예에서
/blahblah에 대한 요청이 있을 경우 첫 번째 로케이션 블록이 처음에 요청을 받는다./var/www/main 디렉토리에서 blahblah라는 파일을 찾는다blahblah.html이라는 파일을 검색하여 확인할 수 있다./var/www/main 디렉토리 내에 blahblah/라는 디렉토리가 있는지 확인./fallback/index.html로 리디렉션된다./var/www/another/fallback/index.html 파일을 처리.rewrite 지시어와 함께 last 매개 변수를 사용하거나 매개 변수를 전혀 사용하지 않는 경우, Nginx는 rewrite의 결과에 따라 일치하는 새 위치를 검색.
예를 들어, 만약 rewrite를 포함하도록 마지막 예제를 수정한다면, 요청이 때때로 try_files 지시문에 의존하지 않고 두 번째 위치로 직접 전달된다는 것을 알 수 있다.
root /var/www/main;
location / {
rewrite ^/rewriteme/(.*)$ /$1 last;
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
위의 예에서는 /rewriteme/hello에 대한 요청이 첫 번째 로케이션 블록에 의해 처음 처리됩니다.
/hello로 다시 쓰여지고 위치가 검색됩니다.
이 경우 첫 번째 위치와 다시 일치하고 try_files에 의해 정상적으로 처리되며, 아무것도 발견되지 않으면 /fallback/index.html로 간다(위에서 얘기한 try_files 내부 리디렉션 사용).
그러나 /rewriteme/fallback/hello에 대한 요청이 있으면 첫 번째 블록이 다시 일치한다.
rewrite가 다시 적용되어 이번에는 /fallback/hello가 발생합니다.
그런 다음, 요청은 듀번째 블록에서 처리된다.
error_page 지시문은 try_files에서 만든 것과 유사한 내부 리디렉션을 초래할 수 있다. 이 지시문은 특정 상태 코드가 발견될 때 발생할 작업을 정의하는 데 사용됨.
try_files가 설정된 경우 디렉티브가 요청의 전체 라이프사이클을 처리하므로 이 명령은 실행되지 않을 수 있다.
root /var/www/main;
location / {
error_page 404 /another/whoops.html;
}
location /another {
root /var/www;
}
모든 요청(/another로 시작하는 요청 제외)은 첫 번째 블록에 의해 처리되며, 첫 번째 블록은 /var/www/main의 파일을 처리한다.
하지만 파일을 찾을 수 없으면(404), /another/woops.html로 내부 리디렉션되어 결국 두 번째 블록으로 도달한다.
그래서 이 파일은 /var/www/another/woops.html에서 제공된다.
listen 지시문을 보고 요청에 가장 적합한 서버 블록들을 가려낸다.server_name을 평가하고 가장 적합한 것을 선택한다.