블로그 | NGINX

NGINX 구성의 상위 10가지 실수 피하기

NGINX-F5-수평-검정-유형-RGB의 일부
티모 스타크 썸네일
티모 스타크
2022년 2월 22일 게시

문제를 겪고 있는 NGINX 사용자를 도울 때, 우리는 다른 사용자의 구성에서 반복적으로 보았던 것과 동일한 구성 실수를 종종 보게 됩니다. 심지어 동료 NGINX 엔지니어가 작성한 구성에서도 그런 실수가 발견되는 경우가 있습니다! 이 블로그에서는 가장 흔한 오류 10가지를 살펴보고, 무엇이 문제인지, 그리고 해결 방법을 설명합니다.

  1. 작업자당 파일 설명자가 충분하지 않습니다.
  2. error_log off 지시어
  3. 업스트림 서버에 대한 keepalive 연결을 활성화하지 않음
  4. 지시어 상속의 작동 방식을 잊어버림
  5. proxy_buffering off 지시어
  6. if 지시문의 부적절한 사용
  7. 과도한 건강 검진
  8. 메트릭에 대한 보안되지 않은 액세스
  9. 모든 트래픽이 동일한 /24 CIDR 블록에서 오는 경우 ip_hash 사용
  10. 상류 그룹을 이용하지 않음

실수 1: 작업자당 파일 설명자가 충분하지 않음

worker_connections 지시어는 NGINX 작업자 프로세스가 열 수 있는 동시 연결의 최대 수를 설정합니다(기본값은 512). 클라이언트 연결뿐 아니라 모든 유형의 연결(예: 프록시 서버와의 연결)이 최대값에 포함됩니다. 하지만 궁극적으로 작업자당 동시 연결 수에는 또 다른 제한이 있다는 점을 명심하는 것이 중요합니다. 즉, 각 프로세스에 할당되는 파일 설명자(FD)의 최대 수에 대한 운영 체제의 제한입니다. 최신 UNIX 배포판에서는 기본 제한이 1024입니다.

가장 작은 NGINX 배포의 경우를 제외하고 작업자당 512개 연결 제한은 아마도 너무 작을 것입니다. 실제로 NGINX 오픈 소스 바이너리와 NGINX Plus와 함께 배포하는 기본 nginx.conf 파일은 이를 1024로 늘립니다.

일반적인 구성 실수는 FD에 대한 제한을 worker_connections 값의 최소 2배로 늘리지 않는 것입니다. 해결 방법은 기본 구성 컨텍스트에서 worker_rlimit_nofile 지시어로 해당 값을 설정하는 것입니다.

더 많은 FD가 필요한 이유는 다음과 같습니다. NGINX 워커 프로세스에서 클라이언트 또는 업스트림 서버로의 각 연결은 FD를 사용합니다. NGINX가 웹 서버 역할을 할 때 클라이언트 연결에 FD 하나를 사용하고 제공된 파일당 FD 하나를 사용하므로 클라이언트당 최소 FD가 두 개입니다(하지만 대부분의 웹 페이지는 여러 파일로 구성됩니다). 프록시 서버 역할을 하는 경우 NGINX는 클라이언트와 업스트림 서버에 대한 연결에 각각 하나의 FD를 사용하고 잠재적으로 서버의 응답을 일시적으로 저장하는 데 사용되는 파일에 대해 세 번째 FD를 사용합니다. 캐싱 서버로서 NGINX는 캐시된 응답의 경우 웹 서버처럼 동작하고, 캐시가 비어 있거나 만료된 경우에는 프록시 서버처럼 동작합니다.

NGINX도 로그 파일당 하나의 FD를 사용하고, 마스터 프로세스와 통신하기 위해 여러 개의 FD를 사용하지만, 일반적으로 이 숫자는 연결 및 파일에 사용되는 FD 수에 비하면 적습니다.

UNIX는 프로세스당 FD 수를 설정하는 여러 가지 방법을 제공합니다.

  • 쉘에서 NGINX를 시작하는 경우 ulimit 명령
  • NGINX를 서비스로 시작하는 경우 init 스크립트 또는 systemd 서비스 매니페스트 변수
  • /etc/security/limits.conf 파일

그러나 사용할 방법은 NGINX를 시작하는 방법에 따라 달라지지만, worker_rlimit_nofile은 NGINX를 시작하는 방법에 관계없이 작동합니다.

FD의 수에는 시스템 전체에 걸친 제한이 있으며, 이는 OS의 sysctl fs.file-max 명령을 사용하여 설정할 수 있습니다. 일반적으로 충분히 크지만 모든 NGINX 작업자 프로세스가 사용할 수 있는 파일 설명자의 최대 수( worker_rlimit_nofile * worker_processes )가 fs.file‑max 보다 상당히 적은지 확인하는 것이 좋습니다. NGINX가 사용 가능한 모든 FD를 사용하는 경우(예: DoS 공격 시)에는 해당 시스템에 로그인하여 문제를 해결하는 것조차 불가능해집니다.

실수 2: error_log off 지시어

흔히 저지르는 실수는 error_log off 지시어가 로깅을 비활성화한다고 생각하는 것입니다. 실제로 access_log 지시어와 달리 error_log는 off 매개변수를 사용하지 않습니다. 구성에 error_log off 지시어를 포함하면 NGINX는 NGINX 구성 파일의 기본 디렉터리(일반적으로 /etc/nginx )에 off 라는 이름의 오류 로그 파일을 생성합니다.

오류 로그를 비활성화하는 것은 권장하지 않습니다. NGINX에서 문제를 디버깅할 때 중요한 정보 소스이기 때문입니다. 그러나 저장소가 너무 제한되어 사용 가능한 디스크 공간을 모두 소모할 만큼 충분한 데이터를 로깅할 수 있는 경우 오류 로깅을 비활성화하는 것이 합리적일 수 있습니다. 기본 구성 컨텍스트에 이 지침을 포함하세요.

오류 로그 /dev/null 발생;

NGINX가 구성을 읽고 검증할 때까지 이 지시어는 적용되지 않습니다. 따라서 NGINX가 시작되거나 구성이 다시 로드될 때마다 구성이 검증될 때까지 기본 오류 로그 위치(일반적으로 /var/log/nginx/error.log )에 기록됩니다. 로그 디렉토리를 변경하려면 다음을 포함하세요. -이자형 <오류_로그_위치> 매개변수에 대한 nginx 명령.

실수 3: 업스트림 서버에 대한 Keepalive 연결을 활성화하지 않음

기본적으로 NGINX는 새로운 수신 요청이 있을 때마다 업스트림(백엔드) 서버에 대한 새로운 연결을 엽니다. 이 방법은 안전하지만 비효율적입니다. 왜냐하면 NGINX와 서버는 연결을 설정하기 위해 3개의 패킷을 교환해야 하고, 연결을 종료하기 위해서는 3개 또는 4개의 패킷을 교환해야 하기 때문입니다.

트래픽 양이 많은 경우, 모든 요청에 대해 새로운 연결을 열면 시스템 리소스가 고갈되어 연결을 전혀 열 수 없게 될 수 있습니다. 그 이유는 다음과 같습니다. 각 연결에 대해 소스 주소, 소스 포트, 대상 주소, 대상 포트의 4-튜플은 고유해야 합니다. NGINX에서 업스트림 서버로 연결하는 경우, 세 가지 요소(첫 번째, 세 번째, 네 번째)는 고정되고 소스 포트만 변수로 남습니다. 연결이 닫히면 Linux 소켓은 2분 동안 TIME‑WAIT 상태에 머무르며 트래픽 양이 많을 때는 사용 가능한 소스 포트 풀이 고갈될 가능성이 높아집니다. 이런 일이 발생하면 NGINX는 업스트림 서버에 대한 새로운 연결을 열 수 없습니다.

해결 방법은 NGINX와 업스트림 서버 간에 keepalive 연결을 활성화하는 것입니다. 즉, 요청이 완료되었을 때 연결이 닫히는 대신, 추가 요청에 사용될 수 있도록 연결이 열려 있게 됩니다. 이렇게 하면 소스 포트가 부족해질 가능성이 줄어들고 성능이 향상됩니다 .

Keepalive 연결을 활성화하려면:

  • 모든 upstream{} 블록에 keepalive 지시문을 포함하여 각 작업자 프로세스의 캐시에 보존되는 업스트림 서버에 대한 유휴 keepalive 연결 수를 설정합니다.

    keepalive 지시문은 NGINX 작업자 프로세스가 열 수 있는 업스트림 서버에 대한 총 연결 수를 제한하지 않는다는 점에 유의하세요. 이는 흔히 오해되는 사실입니다. 따라서 keepalive 에 대한 매개변수는 생각보다 클 필요가 없습니다.

    upstream{} 블록에 나열된 서버 수의 두 배로 매개변수를 설정하는 것이 좋습니다. 이 크기는 NGINX가 모든 서버와 연결 유지를 할 수 있을 만큼 충분히 크지만, 동시에 업스트림 서버도 새로 들어오는 연결을 처리할 수 있을 만큼 작습니다.

    upstream{} 블록에서 부하 분산 알고리즘을 지정할 때( hash , ip_hash , least_conn , least_time 또는 random 지시어 사용) 해당 지시어는 keepalive 지시어 위에 나타나야 합니다. 이는 NGINX 구성에서 지시문의 순서가 중요하지 않다는 일반적인 규칙에 대한 몇 안 되는 예외 중 하나입니다.

  • 요청을 업스트림 그룹으로 전달하는 location{} 블록에서 proxy_pass 지시문과 함께 다음 지시문을 포함합니다.

    proxy_http_version 1.1;
    proxy_set_header "연결" "";
    

    기본적으로 NGINX는 업스트림 서버에 연결하기 위해 HTTP/1.0을 사용하고 이에 따라 서버로 전달하는 요청에 Connection: close 헤더를 추가합니다. 결과적으로 upstream{} 블록에 keepalive 지시문이 있음에도 불구하고 요청이 완료되면 각 연결이 닫힙니다.

    proxy_http_version 지시문은 NGINX에게 대신 HTTP/1.1을 사용하도록 지시하고, proxy_set_header 지시문은 Connection 헤더에서 close 값을 제거합니다.

실수 4: 지시어 상속의 작동 방식을 잊음

NGINX 지시문은 아래로 상속되거나 "외부에서 내부로" 상속됩니다. 자식 컨텍스트(다른 컨텍스트( 부모 ) 내에 중첩된 컨텍스트)는 부모 수준에 포함된 지시문의 설정을 상속합니다. 예를 들어, http{} 컨텍스트 내의 모든 server{}location{} 블록은 http 수준에서 포함된 지시문의 값을 상속하고, server{} 블록의 지시문은 그 안에 있는 모든 자식 location{} 블록에서 상속됩니다. 그러나 동일한 지시어가 부모 컨텍스트와 자식 컨텍스트에 모두 포함되는 경우 값은 함께 추가되지 않습니다. 대신 자식 컨텍스트의 값이 부모 값을 재정의합니다.

실수는 배열 지시문 에 대한 이 "재정의 규칙"을 잊는 것입니다. 배열 지시문은 여러 컨텍스트에서 포함될 수 있을 뿐만 아니라 주어진 컨텍스트 내에서 여러 번 포함될 수도 있습니다. 예로는 proxy_set_headeradd_header가 있습니다. second의 이름에 "add"가 있으면 재정의 규칙을 잊기가 특히 쉽습니다.

add_header 예제를 통해 상속이 어떻게 작동하는지 설명할 수 있습니다.

http { 
add_header X-HTTP-레벨-헤더 1;
add_header X-다른-HTTP-레벨-헤더 1;

서버 {
청취 8080;
위치 / {
리턴 200 "OK";
} 
}

서버 {
청취 8081;
add_header X-서버-레벨-헤더 1;

위치 / {
리턴 200 "OK";
}

위치 /테스트 {
add_header X-위치-레벨-헤더 1;
리턴 200 "OK";
}

위치 /올바른 {
add_header X-HTTP-레벨-헤더 1;
add_header X-다른-HTTP-레벨-헤더 1;

add_header X-SERVER-LEVEL-HEADER 1;
add_header X-LOCATION-LEVEL-HEADER 1;
return 200 "OK";
} 
}
}

포트 8080에서 수신하는 서버의 경우 server{} 또는 location{} 블록에 add_header 지시어가 없습니다. 따라서 상속은 간단하고 http{} 컨텍스트에 정의된 두 개의 헤더를 볼 수 있습니다.

% curl -i localhost:8080 HTTP/1.1 200 OK 서버: nginx/1.21.5 날짜: 월, 21 2월 2022 10:12:15 GMT 콘텐츠 유형: text/plain 콘텐츠 길이: 2 연결: keep-alive X-HTTP-LEVEL-HEADER: 1 X-ANOTHER-HTTP-레벨-헤더: 1 좋아요

포트 8081에서 수신하는 서버의 경우 server{} 블록에 add_header 지시문이 있지만 자식 위치 / 블록에는 없습니다. server{} 블록에 정의된 헤더는 http{} 컨텍스트에 정의된 두 헤더를 재정의합니다.

% curl -i localhost:8081 HTTP/1.1 200 OK 서버: nginx/1.21.5 날짜: 월, 21 2월 2022 10:12:20 GMT 콘텐츠 유형: text/plain 콘텐츠 길이: 2 연결: keep-alive X-SERVER-LEVEL-HEADER: 1 좋아요

자식 위치 /test 블록에는 add_header 지시문이 있으며 이 지시문은 부모 server{} 블록의 헤더와 http{} 컨텍스트의 두 헤더를 모두 재정의합니다.

% curl -i localhost:8081/test HTTP/1.1 200 OK 서버: nginx/1.21.5 날짜: 월, 21 2월 2022 10:12:25 GMT 콘텐츠 유형: text/plain 콘텐츠 길이: 2 연결: keep-alive X-LOCATION-LEVEL-HEADER: 1 좋아요

location{} 블록이 로컬에서 정의된 헤더와 함께 부모 컨텍스트에 정의된 헤더를 보존하도록 하려면 location{} 블록 내에서 부모 헤더를 다시 정의해야 합니다. location /correct 블록에서 우리가 한 일은 다음과 같습니다.

% curl -i localhost:8081/correct HTTP/1.1 200 OK 서버: nginx/1.21.5 날짜: 월, 21 2월 2022 10:12:30 GMT 콘텐츠 유형: text/plain 콘텐츠 길이: 2 연결: keep-alive X-HTTP-LEVEL-HEADER: 1 X-ANOTHER-HTTP-레벨-헤더: 1 X-서버-레벨-헤더: 1 X-위치-레벨-헤더: 1 좋아요

실수 5: proxy_buffering off 지시어

NGINX에서는 프록시 버퍼링이 기본적으로 활성화되어 있습니다( proxy_buffering 지시어가 on 으로 설정됨). 프록시 버퍼링이란 NGINX가 서버로부터 들어오는 응답을 내부 버퍼에 저장하고, 전체 응답이 버퍼링될 때까지 클라이언트로 데이터를 전송하지 않는다는 것을 의미합니다. 버퍼링은 느린 클라이언트의 성능을 최적화하는 데 도움이 됩니다. NGINX는 클라이언트가 모든 응답을 검색하는 데 걸리는 시간만큼 응답을 버퍼링하기 때문에 프록시된 서버는 가능한 한 빨리 응답을 반환하고 다른 요청을 처리할 수 있습니다.

프록시 버퍼링이 비활성화되면 NGINX는 클라이언트로 전송하기 전에 서버 응답의 첫 번째 부분만 버퍼링합니다. 버퍼의 기본 크기는 한 메모리 페이지입니다(운영 체제에 따라 4KB 또는 8KB ). 일반적으로 이는 응답 헤더에 필요한 공간입니다. 그러면 NGINX는 클라이언트가 응답을 수신하자마자 동기적으로 응답을 전송하고, 서버는 NGINX가 다음 응답 세그먼트를 수락할 때까지 유휴 상태를 유지하게 됩니다.

그래서 우리는 구성에서 proxy_buffering이 꺼져 있는 것을 얼마나 자주 보는지에 놀랐습니다. 클라이언트가 경험하는 지연 시간을 줄이기 위한 의도겠지만, 효과는 미미한 반면 부작용은 많습니다. 프록시 버퍼링이 비활성화되면 속도 제한 및 캐싱이 구성되어 있어도 작동하지 않고 성능이 저하되는 등 여러 가지 부작용이 있습니다.

프록시 버퍼링을 비활성화하는 것이 합리적인 사용 사례는 극소수에 불과합니다(예: 롱 폴링). 따라서 기본값을 변경하지 않는 것이 좋습니다. 자세한 내용은 NGINX Plus 관리자 가이드를 참조하세요.

실수 6: if 지시어의 부적절한 사용

if 지시어는 사용하기 까다롭고, 특히 location{} 블록에서는 더욱 그렇습니다. 예상한 대로 작동하지 않는 경우가 많고 세그폴트가 발생할 수도 있습니다. 사실, 이 문제는 너무 까다로워서 NGINX 위키에 'If is Evil' 이라는 제목의 문서가 있을 정도입니다. 이 문서에서는 문제에 대한 자세한 설명과 이를 피하는 방법을 설명합니다.

일반적으로 if{} 블록 내에서 항상 안전하게 사용할 수 있는 유일한 지시어는 returnrewrite 입니다. 다음 예제에서는 if를 사용하여 X‑Test 헤더가 포함된 요청을 감지합니다(하지만 이는 테스트하려는 모든 조건이 될 수 있습니다). NGINX가 돌아왔습니다 430 (요구 헤더 전지 크기가 큰) 오류가 발생하여 지정된 위치에서 가로채기 @오류_430 요청을 상위 그룹으로 프록시합니다. .

위치 / { 
오류 페이지 430 = @error_430;
if ($http_x_test) {
return 430; 
}

프록시 패스 http://a;
}

위치 @error_430 {
프록시 패스 b;
}

이러한 용도와 if 의 다른 많은 용도에서는 지시어를 전혀 사용하지 않는 것이 가능한 경우가 많습니다. 다음 예에서 요청에 X‑Test 헤더가 포함되어 있으면 map{} 블록은 $upstream_name 변수를 b 로 설정하고 요청은 해당 이름을 가진 업스트림 그룹으로 프록시됩니다.

지도 $http_x_test $upstream_name { 
기본 "b";
"" "a";
}

# ...

위치 / {
proxy_pass http://$upstream_name;
}

실수 7: 과도한 건강 검진

여러 가상 서버를 구성하여 동일한 업스트림 그룹에 요청을 프록시하는 것은 매우 일반적입니다(즉, 여러 server{} 블록에 동일한 proxy_pass 지시문을 포함하는 것입니다). 이 상황에서 실수는 모든 server{} 블록에 health_check 지시문을 포함하는 것입니다. 이렇게 하면 추가 정보를 제공하지 않고도 업스트림 서버에 더 많은 부하가 걸릴 뿐입니다.

당연한 얘기겠지만, 해결책은 upstream{} 블록당 하나의 상태 검사만 정의하는 것입니다. 여기서는 특수한 이름이 지정된 위치에 있는 b 라는 업스트림 그룹에 대한 상태 검사를 정의하고 적절한 시간 초과 및 헤더 설정을 적용합니다.

위치 / { 
proxy_set_header 호스트 $host;
proxy_set_header "연결" "";
proxy_http_version 1.1;
proxy_pass http://b;
}

위치 @health_check {
health_check;
proxy_connect_timeout 2초;
proxy_read_timeout 3초;
proxy_set_header 호스트 example.com;
proxy_pass http://b;
}

복잡한 구성에서는 이 예와 같이 NGINX Plus API 와 대시보드와 함께 모든 상태 점검 위치를 단일 가상 서버로 그룹화하여 관리를 더욱 간소화할 수 있습니다.

server { 
listen 8080;

location / {
# …
}

location @health_check_b {
health_check;
proxy_connect_timeout 2초;
proxy_read_timeout 3초;
proxy_set_header Host example.com;
proxy_pass http://b;
}

location @health_check_c {
health_check;
proxy_connect_timeout 2초;
proxy_read_timeout 3초;
proxy_set_header Host api.example.com;
proxy_pass http://c;
}

location /api {
api write=on;
# API에 대한 액세스를 제한하는 지시문(아래 '실수 8' 참조)
}

location = /dashboard.html {
root /usr/share/nginx/html;
}
}

HTTP, TCP, UDP 및 gRPC 서버의 상태 점검에 대한 자세한 내용은 NGINX Plus 관리자 가이드를 참조하세요.

실수 8: 메트릭에 대한 보안되지 않은 액세스

NGINX 작업에 대한 기본 메트릭은 Stub Status 모듈에서 사용할 수 있습니다. NGINX Plus의 경우 NGINX Plus API를 사용하여 훨씬 더 광범위한 메트릭 세트를 수집할 수도 있습니다. 각각 server{} 또는 location{} 블록에 stub_status 또는 api 지시문을 포함하여 메트릭 수집을 활성화합니다. 이 블록은 메트릭을 보기 위해 액세스하는 URL이 됩니다. ( NGINX Plus API 의 경우 메트릭을 수집하려는 NGINX 엔터티(가상 서버, 업스트림 그룹, 캐시 등)에 대한 공유 메모리 영역도 구성해야 합니다. NGINX Plus 관리자 가이드 의 지침을 참조하세요.)

일부 측정 항목은 NGINX로 프록시된 웹사이트나 앱을 공격하는 데 사용될 수 있는 민감한 정보이며, 사용자 구성에서 가끔 볼 수 있는 실수는 해당 URL에 대한 액세스를 제한하지 못하는 것입니다. 여기서는 지표를 보호할 수 있는 몇 가지 방법을 살펴보겠습니다. 첫 번째 예에서는 stub_status를 사용하겠습니다.

다음 구성을 사용하면 인터넷에 있는 모든 사람이 http://example.com/basic_status 에서 메트릭에 액세스할 수 있습니다.

서버 { 
listen 80;
server_name example.com;

location = /basic_status {
stub_status;
}
}

HTTP 기본 인증을 사용하여 메트릭 보호

HTTP 기본 인증을 사용하여 메트릭을 암호로 보호하려면 auth_basicauth_basic_user_file 지시문을 포함합니다. 파일(여기서는 .htpasswd )은 메트릭을 확인하기 위해 로그인할 수 있는 클라이언트의 사용자 이름과 비밀번호를 나열합니다.

서버 { 
listen 80;
server_name example.com;

location = /basic_status {
auth_basic “closed site”;
auth_basic_user_file conf.d/.htpasswd;
stub_status;
}
}

허용거부 지침을 사용하여 메트릭 보호

권한이 있는 사용자가 로그인할 필요가 없도록 하려는 경우, 해당 사용자가 메트릭에 액세스할 IP 주소를 알고 있는 경우 사용할 수 있는 또 다른 옵션은 allow 지시어입니다. 개별 IPv4 및 IPv6 주소와 CIDR 범위를 지정할 수 있습니다. deny all 지시어는 다른 주소에서의 접근을 차단합니다.

서버 { 
listen 80;
server_name example.com;

location = /basic_status {
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
allow 96.1.2.23/32;
deny all;
stub_status;
}
}

두 가지 방법을 결합하다

두 가지 방법을 결합하고 싶다면 어떻게 해야 할까? 우리는 클라이언트가 비밀번호 없이도 특정 주소에서 지표에 접근하도록 허용할 수 있으며, 다른 주소에서 접속하는 클라이언트에게는 여전히 로그인을 요구할 수 있습니다. 이를 위해 우리는 meets any 지시어를 사용합니다. NGINX에 HTTP 기본 인증 자격 증명을 사용하여 로그인하거나 사전 승인된 IP 주소를 사용하는 클라이언트의 액세스를 허용하라고 알려줍니다. 보안을 강화하려면 모든 사람 에게 로그인을 요구하도록 설정할 수 있습니다. 특정 주소에서 온 사람도 로그인해야 합니다.

서버 { 
listen 80;
server_name monitor.example.com;

location = /basic_status {
any를 만족시키세요;

auth_basic “closed site”;
auth_basic_user_file conf.d/.htpasswd;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
allow 96.1.2.23/32;
deny all;
stub_status;
}
}

NGINX Plus를 사용하면 동일한 기술을 사용하여 NGINX Plus API 엔드포인트(다음 예에서는 http://monitor.example.com:8080/api/ )에 대한 액세스를 제한할 수 있고, http://monitor.example.com/dashboard.html 에 있는 라이브 활동 모니터링 대시보드에도 액세스할 수 있습니다.

이 구성을 사용하면 96.1.2.23/32 네트워크나 로컬 호스트에서 오는 클라이언트만 비밀번호 없이 액세스할 수 있습니다. 지시문이 서버{} 수준에서 정의되므로 API와 대시보드에 동일한 제한이 적용됩니다. 참고로, api 에 대한 write=on 매개변수는 이러한 클라이언트도 API를 사용하여 구성을 변경할 수 있다는 것을 의미합니다.

API와 대시보드 구성에 대한 자세한 내용은 NGINX Plus 관리자 가이드를 참조하세요.

서버 { 
listen 8080;
server_name monitor.example.com;

any를 만족시키세요;
auth_basic “closed site”;
auth_basic_user_file conf.d/.htpasswd;
allow 127.0.0.1/32;
allow 96.1.2.23/32;
deny all;

location = /api/ { 
api write=on;
}

location = /dashboard.html {
root /usr/share/nginx/html;
}
}

실수 9: 모든 트래픽이 동일한 /24 CIDR 블록에서 오는 경우 ip_hash 사용

ip_hash 알고리즘은 클라이언트 IP 주소의 해시를 기반으로 upstream{} 블록의 서버 간에 트래픽 부하를 분산합니다. 해싱 키는 IPv4 주소의 처음 세 옥텟이거나 전체 IPv6 주소입니다. 이 방법은 세션 지속성을 확립합니다. 즉, 서버를 사용할 수 없는 경우를 제외하고 클라이언트의 요청은 항상 동일한 서버로 전달됩니다.

고가용성을 위해 구성된 가상 사설망에 역방향 프록시로 NGINX를 배포했다고 가정해 보겠습니다. NGINX 앞에 다양한 방화벽, 라우터, 4계층 로드 밸런서 및 게이트웨이를 배치하여 다양한 소스(내부 네트워크, 파트너 네트워크, 인터넷 등)에서 트래픽을 수용하고 이를 역방향 프록시를 통해 업스트림 서버로 전송하기 위해 NGINX로 전달합니다. 초기 NGINX 구성은 다음과 같습니다.

http {
업스트림 {
ip_hash;
서버 10.10.20.105:8080;
서버 10.10.20.106:8080;
서버 10.10.20.108:8080;
}

서버 {# …}
}

하지만 문제가 하나 있습니다. 모든 "차단" 장치가 동일한 10.10.0.0/24 네트워크에 있기 때문에 NGINX에서는 모든 트래픽이 해당 CIDR 범위의 주소에서 오는 것처럼 보입니다. ip_hash 알고리즘은 IPv4 주소의 처음 세 옥텟을 해시한다는 점을 기억하세요. 우리의 배포 방식에서는 처음 세 옥텟이 모든 클라이언트에서 동일합니다(10.10.0). 따라서 해시는 모두 동일하며 트래픽을 다른 서버로 분산할 기준이 없습니다.

해결 방법은 $binary_remote_addr 변수를 해시 키로 사용하고 해시 알고리즘을 사용하는 것입니다. 해당 변수는 전체 클라이언트 주소를 캡처하여 이를 IPv4 주소의 경우 4바이트 , IPv6 주소의 경우 16바이트인 이진 표현으로 변환합니다. 이제 각 가로채기 장치의 해시가 다르고 로드 밸런싱이 예상대로 작동합니다.

또한 기본값 대신 케타마 해싱 방법을 사용하기 위해 일관된 매개변수를 포함했습니다. 이렇게 하면 서버 세트가 변경될 때 다른 업스트림 서버로 다시 매핑되는 키의 수가 크게 줄어들어 캐싱 서버의 캐시 적중률이 높아집니다.

http { 
업스트림 {
해시 $binary_remote_addr 일관성 있음;
서버 10.10.20.105:8080;
서버 10.10.20.106:8080;
서버 10.10.20.108:8080;
}

서버 {# …}
}

실수 10: 상류 그룹을 이용하지 않음

가장 간단한 사용 사례 중 하나인 NGINX를 포트 3000에서 수신하는 단일 NodeJS 기반 백엔드 애플리케이션의 역방향 프록시로 사용한다고 가정해 보겠습니다. 일반적인 구성은 다음과 같습니다.

http {
서버 {
listen 80;
서버 이름 example.com;

위치 / {
proxy_set_header 호스트 $host;
proxy_pass http://localhost:3000/;
}
}
}

간단하죠? proxy_pass 지시어는 NGINX에게 클라이언트의 요청을 어디로 보내야 하는지 알려줍니다. NGINX가 해야 할 일은 호스트 이름을 IPv4 또는 IPv6 주소로 확인하는 것뿐입니다. 연결이 설정되면 NGINX가 해당 서버로 요청을 전달합니다.

여기서 실수는 서버가 하나뿐이기 때문에 부하 분산을 구성할 이유가 없고 upstream{} 블록을 만드는 것이 무의미하다고 가정하는 것입니다. 실제로 upstream{} 블록은 다음 구성에서 알 수 있듯이 성능을 개선하는 여러 기능을 잠금 해제합니다.

http {
업스트림 node_backend {
존 업스트림 64K;
서버 127.0.0.1:3000 max_fails=1 fail_timeout=2s;
keepalive 2;
}

서버 {
listen 80;
서버 이름 example.com;

위치 / {
proxy_set_header 호스트 $host;
proxy_pass http://node_backend/;
proxy_next_upstream 오류 시간 초과 http_500;

}
}
}

zone 지시어는 호스트의 모든 NGINX 작업자 프로세스가 업스트림 서버에 대한 구성 및 상태 정보에 액세스할 수 있는 공유 메모리 영역을 설정합니다. 여러 상류 그룹이 구역을 공유할 수 있습니다. NGINX Plus를 사용하면 존을 통해 NGINX Plus API를 사용하여 업스트림 그룹의 서버와 NGINX를 다시 시작하지 않고도 개별 서버의 설정을 변경할 수도 있습니다. 자세한 내용은 NGINX Plus 관리자 가이드를 참조하세요.

서버 지시문에는 서버 동작을 조정하는 데 사용할 수 있는 여러 매개변수가 있습니다. 이 예제에서는 NGINX가 서버가 비정상적이어서 요청을 수락할 수 없다고 판단하는 데 사용하는 조건을 변경했습니다. 여기서는 2초 간격으로 한 번이라도 통신 시도가 실패하면(기본값인 10초 간격으로 한 번) 서버가 비정상적이라고 간주합니다.

이 설정을 proxy_next_upstream 지시어와 결합하여 NGINX가 통신 시도가 실패한 것으로 간주하는 것을 구성합니다. 이 경우 요청을 업스트림 그룹의 다음 서버로 전달합니다. 기본 오류 및 시간 초과 조건에 NGINX가 HTTP를 고려하도록 http_500을 추가합니다. 500(내부 서버 오류) 실패한 시도를 나타내는 상위 서버의 코드입니다.

keepalive 지시어는 각 작업자 프로세스의 캐시에 보존된 업스트림 서버에 대한 유휴 keepalive 연결 수를 설정합니다. 우리는 이미 실수 3에서 이점에 대해 논의했습니다. 업스트림 서버에 대한 Keepalive 연결을 활성화하지 않습니다 .

NGINX Plus를 사용하면 업스트림 그룹으로 추가 기능을 구성할 수 있습니다.

  • 위에서 언급했듯이 NGINX 오픈 소스는 시작 중에 서버 호스트 이름을 IP 주소로 한 번만 확인합니다. server 지시문에 resolve 매개변수를 사용하면 NGINX Plus가 업스트림 서버의 도메인 이름에 해당하는 IP 주소의 변경 사항을 모니터링하고, 다시 시작하지 않고도 업스트림 구성을 자동으로 수정할 수 있습니다.

    서비스 매개변수는 NGINX Plus가 포트 번호, 가중치, 우선 순위에 대한 정보가 포함된 DNS SRV 레코드를 사용할 수 있도록 합니다. 이는 서비스의 포트 번호가 종종 동적으로 할당되는 마이크로서비스 환경에서 매우 중요합니다.

    서버 주소를 확인하는 방법에 대한 자세한 내용은 블로그의 NGINX 및 NGINX Plus에서 서비스 검색에 DNS 사용을 참조하세요.

  • server 지시문에 slow_start 매개변수를 사용하면 NGINX Plus가 새롭게 정상 상태로 간주되어 요청을 수락할 수 있는 서버로 보내는 요청 볼륨을 점진적으로 늘릴 수 있습니다. 이는 서버에 과부하를 일으켜 다시 실패하게 만드는 갑작스러운 요청 폭주를 방지합니다.

  • 지시문을 사용하면 NGINX Plus가 요청을 처리할 업스트림 서버를 선택할 수 없는 경우 클라이언트에 즉시 오류를 반환하는 대신 요청을 큐에 넣을 수 있습니다.

자원

NGINX Plus를 사용해보려면 오늘 무료 30일 체험판을 시작하거나 저희에게 연락해 사용 사례에 대해 논의해 보세요 .


"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."