리퍼러의 적중이 임계 값을 초과하면 Nginx가 알려주도록하는 방법이 있습니까?
예를 들어 내 웹 사이트가 Slashdot에 등장하고 갑자기 1 시간에 2K 조회가 발생하면 1K 조회가 1 시간을 초과하면 알림을 받고 싶습니다.
Nginx에서 이것을 할 수 있습니까? 루아가 없을까요? (내 제품은 루아 컴파일되지 않았으므로)
리퍼러의 적중이 임계 값을 초과하면 Nginx가 알려주도록하는 방법이 있습니까?
예를 들어 내 웹 사이트가 Slashdot에 등장하고 갑자기 1 시간에 2K 조회가 발생하면 1K 조회가 1 시간을 초과하면 알림을 받고 싶습니다.
Nginx에서 이것을 할 수 있습니까? 루아가 없을까요? (내 제품은 루아 컴파일되지 않았으므로)
답변:
가장 효율적인 솔루션은 것 데몬 쓸 수 있습니다 의 트랙을 유지하고, 필드.tail -f
access.log
$http_referer
그러나 빠르고 더러운 해결책은 추가 access_log
파일 을 추가 $http_referer
하고 custom으로 변수 만 log_format
기록하고 X 분마다 자동으로 로그를 회전하는 것입니다.
이는 표준 logrotate 스크립트를 사용하여 수행 할 수 있습니다. 파일을 다시 열려면 nginx를 정상적으로 다시 시작해야 할 수 있습니다 (예 : 표준 절차 는 간단한 시간 동안 SO 에서 / a / 15183322를 살펴보십시오) 기반 스크립트)…
또는 내의 변수를 사용 하여 또는 지시문 access_log
을 $time_iso8601
사용 하여 분 사양을 가져올 수도 있습니다 (을 넣을 위치에 따라 다름 ).map
if
access_log
따라서 위와 같이 http_referer.Txx{0,1,2,3,4,5}x.log
각 파일을 구별하기 위해 분의 첫 번째 숫자를 가져 와서 각각 10 분의 기간을 포함하는 6 개의 로그 파일이있을 수 있습니다 .
이제 10 분마다 실행되는 간단한 셸 스크립트 cat
, 위의 모든 파일을 함께 파이프 sort
, 파이프, 파이프, to uniq -c
, to sort -rn
, to head -16
및 가장 일반적인 16 가지 Referer
변형 목록이 있습니다. — 숫자와 필드의 조합이 기준을 초과하는지 여부를 자유롭게 결정하고 알림을 수행합니다.
그 후, 하나의 성공적인 알림 후에는이 6 개의 파일을 모두 제거 할 수 있으며 이후 실행에서 6 개의 파일이 모두 존재하는 경우 (및 / 또는 적합한 다른 번호)가 없으면 알림을 발행 할 수 없습니다.
나는 이것이 logtail과 grep으로 훨씬 더 좋을 것이라고 생각합니다. 루아 인라인으로 할 수 있더라도 모든 요청에 대해 오버 헤드를 원하지 않으며 특히 슬래시 도트 (Slashdotted)되었을 때 원하지 않습니다.
5 초 버전입니다. 스크립트에 넣고 더 읽기 쉬운 텍스트를 넣으면 황금색입니다.
5 * * * * logtail -f /var/log/nginx/access_log -o /tmp/nginx-logtail.offset | grep -c "http://[^ ]slashdot.org"
물론 reddit.com과 facebook.com 및 많은 트래픽을 보낼 수있는 수백만 개의 다른 사이트를 완전히 무시합니다. 각기 20 명의 방문자를 보내는 100 개의 서로 다른 사이트는 말할 것도 없습니다. 리퍼러에 관계없이 전자 메일이 전송되도록하는 오래된 오래된 트래픽 임계 값이 있어야합니다.
-o
옵션은 다음에 읽을 위치를 알 수 있도록 오프셋 파일을위한 것입니다.
nginx limit_req_zone 지시문은 $ http_referrer를 포함한 모든 변수를 기준으로 영역을 지정할 수 있습니다.
http {
limit_req_zone $http_referrer zone=one:10m rate=1r/s;
...
server {
...
location /search/ {
limit_req zone=one burst=5;
}
리퍼러 헤더가 상당히 길고 다양 할 수 있으며 무한 바리에이션이 나타날 수 있으므로 웹 서버에 필요한 상태의 양을 제한하기 위해 무언가를 수행하려고 할 것입니다. nginx split_clients 기능을 사용 하여 리퍼러 헤더의 해시를 기반으로하는 모든 요청에 대한 변수를 설정할 수 있습니다 . 아래 예제는 10 버크만 사용하지만 1000으로 쉽게 수행 할 수 있습니다. 따라서 슬래시 도트 (slashdotted)가 발생하면 참조자가 슬래시 도트 URL과 동일한 버킷으로 해시 한 사람들도 차단되지만 split_clients에서 1000 개의 버킷을 사용하여 방문자의 0.1 %로 제한 할 수 있습니다.
다음과 같이 보일 것입니다 (완전히 테스트되지 않았지만 방향이 올바른 것입니다).
http {
split_clients $http_referrer $refhash {
10% x01;
10% x02;
10% x03;
10% x04;
10% x05;
10% x06;
10% x07;
10% x08;
10% x09;
* x10;
}
limit_req_zone $refhash zone=one:10m rate=1r/s;
...
server {
...
location /search/ {
limit_req zone=one burst=5;
}
split_clients
잘못된 정보가있을 수 있습니다 limit_req
. "누설 버킷"을 기반으로하므로 전체 상태가 지정된 영역의 크기를 초과해서는 안됩니다.
예, 물론 NGINX에서도 가능합니다!
할 수있는 일은 다음 DFA를 구현하는 것입니다 .
를 $http_referer
통해 map
값을 정규화 하기 위해 a 를 통해 일부 정규 표현식을 사용하여 속도 제한을 구현 하십시오. 한도를 초과하면 내부 오류 페이지가 발생 하여 관련 질문에 따라error_page
핸들러를 통해 내부 리디렉션 (클라이언트에게 보이지 않음)으로 새 내부 위치로 이동할 수 있습니다.
한계를 초과 한 위의 위치에서 경고 요청을 수행하여 외부 논리가 알림을 수행하게합니다. 이 요청은 이후에 캐시되므로 지정된 시간 창마다 하나의 고유 한 요청 만받을 수 있습니다.
이전 요청의 HTTP 상태 코드를 캐치하고 (상태 코드 ≥ 300을 리턴하고을 사용 proxy_intercept_errors on
하거나 기본적으로 기본 제공되지 않음을 사용 auth_request
하거나 add_after_body
"무료"하위 요청을 작성) 원래 요청을 마치 완료하십시오. 이전 단계는 관여하지 않았습니다. error_page
이것이 작동하려면 재귀 처리 를 활성화해야합니다 .
내 PoC와 MVP는 https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636.detecting-slashdot-effect-in-nginx.conf 에도 있습니다 .
limit_req_zone $http_referer zone=slash:10m rate=1r/m; # XXX: how many req/minute?
server {
listen 2636;
location / {
limit_req zone=slash nodelay;
#limit_req_status 429; #nginx 1.3.15
#error_page 429 = @dot;
error_page 503 = @dot;
proxy_pass http://localhost:2635;
# an outright `return 200` has a higher precedence over the limit
}
recursive_error_pages on;
location @dot {
proxy_pass http://127.0.0.1:2637/?ref=$http_referer;
# if you don't have `resolver`, no URI modification is allowed:
#proxy_pass http://localhost:2637;
proxy_intercept_errors on;
error_page 429 = @slash;
}
location @slash {
# XXX: placeholder for your content:
return 200 "$uri: we're too fast!\n";
}
}
server {
listen 2635;
# XXX: placeholder for your content:
return 200 "$uri: going steady\n";
}
proxy_cache_path /tmp/nginx/slashdotted inactive=1h
max_size=64m keys_zone=slashdotted:10m;
server {
# we need to flip the 200 status into the one >=300, so that
# we can then catch it through proxy_intercept_errors above
listen 2637;
error_page 429 @/.;
return 429;
location @/. {
proxy_cache slashdotted;
proxy_cache_valid 200 60s; # XXX: how often to get notifications?
proxy_pass http://localhost:2638;
}
}
server {
# IRL this would be an actual script, or
# a proxy_pass redirect to an HTTP to SMS or SMTP gateway
listen 2638;
return 200 authorities_alerted\n;
}
이것은 예상대로 작동합니다.
% sh -c 'rm /tmp/slashdotted.nginx/*; mkdir /tmp/slashdotted.nginx; nginx -s reload; for i in 1 2 3; do curl -H "Referer: test" localhost:2636; sleep 2; done; tail /var/log/nginx/access.log'
/: going steady
/: we're too fast!
/: we're too fast!
127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.1" 200 16 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.0" 200 16 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 200 20 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
%
첫 번째 요청은 예상대로 하나의 프런트 엔드와 하나의 백엔드 히트를 발생시키는 것을 볼 수 있습니다 ( 한도보다 우선 하는 위치에 더미 백엔드를 추가해야 limit_req
했으므로 return 200
실제 백엔드는 필요하지 않습니다) 나머지 취급에 대해서는).
두 번째 요청이 한도를 초과하므로 경고를 보내고 (getting 200
)이를 캐시하여 반환합니다 429
(이는 300 이하의 요청을 잡을 수없는 위에서 언급 한 제한으로 인해 필요함). , 원하는대로 무엇이든 자유롭게 할 수 있습니다.
세 번째 요청은 여전히 한도를 초과하지만 이미 경고를 보냈으므로 새 경고가 전송되지 않습니다.
limit_req
이고 다른 하나가 limit_conn
이면 limit_req_status 429
위의 것을 사용하십시오 (매우 새로운 nginx 필요). 골든이어야한다고 생각합니다. 다른 옵션이있을 수 있습니다 (확실히 작동하는 방법은 nginx w / 연결하는 set_real_ip_from
것이지만 정확히 원하는 작업에 따라보다 효율적인 선택이있을 수 있습니다).
golang
하거나 업스트림의 시간 종료 옵션을 조사 할 수 있습니다. 또한 사용하고 싶을 수도 있고 proxy_cache_lock on
스크립트에 실패한 경우 수행 할 작업에 대한 오류 처리를 추가 할 수도 있습니다 (예 : 다시 사용 error_page
하는 것 proxy_intercept_errors
). POC가 좋은 시작이라고 믿습니다. :)
limit_req
/를 사용 limit_conn
합니까? 예를 들어, 위의 구성을 현재 프런트 엔드 서버 앞에 두십시오. 당신이 사용할 수있는 set_real_ip_from
IP를 올바르게 선 아래로 회계 처리하기 위해 상류의 nginx에. 그렇지 않은 경우에도 여전히 맞지 않으면 정확한 제약 조건과 사양을보다 생생하게 표현해야한다고 생각합니다. 어떤 트래픽 수준에 대해 이야기하고 있습니까? 통계를 얼마나 자주 실행해야합니까 (1 분 / 5 분 / 1 시간)? 이전 logtail
솔루션 의 문제점은 무엇입니까 ?