많은 이점에도 불구하고 마이크로서비스 아키텍처는 새로운 복잡성도 초래합니다. 그중 하나는 애플리케이션을 구성하는 모든 마이크로서비스 간에 데이터가 흐르는 상황에서 요청이 처리되는 과정을 추적하기 어렵다는 점입니다. 이를 위해 분산 (요청) 추적이라는 새로운 방법론이 고안되었으며, OpenTracing은 분산 추적 도구의 설계와 구현을 안내하기 위한 사양이자 표준 API 집합입니다.
NGINX Plus 릴리스 18(R18)에서는 NGINX OpenTracing 모듈을 동적 모듈 저장소에 추가했습니다(이 모듈은 이미 몇 년 전부터 GitHub의 타사 모듈로 제공되어 왔습니다). NGINX OpenTracing 모듈의 큰 장점은 분산 추적을 위해 NGINX와 NGINX Plus를 계측하면 각 애플리케이션을 개별적으로 계측하지 않고도 프록시되는 모든 애플리케이션의 추적 데이터를 얻을 수 있다는 것입니다.
이 블로그에서는 NGINX 또는 NGINX Plus의 요청에 분산 추적을 사용하도록 설정하는 방법을 설명합니다(이하 편의상 NGINX Plus로 통칭). 또한 OpenTracing 용어로 트레이서라고 하는 두 가지 분산 추적 서비스인 Jaeger와 Zipkin의 사용 방법을 안내합니다. (다른 트레이서 목록은 OpenTracing 설명서를 참조하세요.) 트레이서가 제공하는 정보의 유형을 보여주기 위해 NGINX Plus 캐싱을 사용하도록 설정하기 전과 후의 요청 처리 과정을 비교합니다.
트레이서는 두 가지 기본 구성 요소로 이루어져 있습니다:
- 에이전트는 자신이 실행 중인 호스트에서 실행되는 애플리케이션의 추적 데이터를 수집합니다. 여기서 “애플리케이션”은 NGINX Plus이며, 에이전트는 플러그인으로 구현됩니다.
- 하나 이상의 에이전트로부터 추적 데이터를 받아 중앙 UI에 표시하는 서버(수집기라고도 함)입니다. 필요에 따라 NGINX Plus 호스트 또는 다른 호스트에서 서버를 실행할 수 있습니다.
트레이서 서버 설치
첫 번째 단계는 원하는 트레이서용 서버를 설치하고 구성하는 것입니다. 여기서는 Jaeger와 Zipkin에 대한 지침을 제공하며, 다른 트레이서를 사용하는 경우 필요에 맞게 조정하세요.
Jaeger 서버 설치
Jaeger 서버를 설치하려면 다음 방법을 사용하는 것이 좋습니다. 1단계에 명시된 URL에서 Docker 이미지를 다운로드할 수도 있습니다.
- Navigate to the Jaeger 다운로드 페이지로 이동하여 Linux 바이너리(이 글을 작성하는 시점에는 jaeger-1.12.0-linux-amd64.tar)를 다운로드합니다.
- 바이너리를 /usr/bin/jaeger로 이동하고(필요한 경우 먼저 디렉터리를 생성) 실행합니다.
$ mkdir /usr/bin/jaeger$ mv jaeger-1.12.0-linux-amd64.tar /usr/bin/jaeger
$ cd /usr/bin/jaeger
$ tar xvzf jaeger-1.12.0-linux-amd64.tar.gz
$ sudo rm -rf jaeger-1.12.0-linux-amd64.tar.gz
$ cd jaeger-1.12.0-linux-amd64
$ ./jaeger-all-in-one- 브라우저에서 http://Jaeger-server-IP-address:16686/ 주소로 Jaeger UI에 액세스할 수 있는지 확인합니다(16686은 Jaeger 서버의 기본 포트입니다).
Zipkin 서버 설치
- Zipkin의 Docker 이미지를 다운로드하고 실행합니다(기본 포트인 9411을 사용합니다).
$ docker run -d -p 9411:9411 openzipkin/zipkin- 브라우저에서 http://Zipkin-server-IP-address:9411/ 주소로 Zipkin UI에 액세스할 수 있는지 확인합니다.
트레이서 플러그인 설치 및 구성
NGINX Plus 호스트에서 다음 명령을 실행하여 Jaeger 또는 Zipkin용 플러그인을 설치합니다.
Jaeger 플러그인 설치
- Jaeger 플러그인을 설치합니다. 다음
wget명령은 x86-64 Linux 시스템용입니다:
$ cd /usr/local/lib
$ wget https://github.com/jaegertracing/jaeger-client-cpp/releases/download/v0.4.2/libjaegertracing_plugin.linux_amd64.so -O /usr/local/lib/libjaegertracing_plugin.so- 소스에서 플러그인을 빌드하는 방법은 GitHub에서 확인할 수 있습니다.
- 플러그인용 JSON 형식 구성 파일을 /etc/jaeger/jaeger-config.json이라는 이름으로 만들고 다음 내용을 입력합니다. Jaeger 서버에는 기본 포트인 6831을 사용합니다:
{ "service_name": "nginx",
"sampler": {
"type": "const",
"param": 1
},
"reporter": {
"localAgentHostPort": "Jaeger-server-IP-address:6831"
}
}- 자세한 내용은
sampler객체에 관한 Jaeger 문서를 참조하세요.
Zipkin 플러그인 설치
- Zipkin 플러그인을 설치합니다. 다음
wget명령은 x86-64 Linux 시스템용입니다:
$ cd /usr/local/lib$ wget -O - https://github.com/rnburn/zipkin-cpp-opentracing/releases/download/v0.5.2/linux-amd64-libzipkin_opentracing_plugin.so.gz | gunzip -c > /usr/local/lib/libzipkin_opentracing_plugin.so- 플러그인용 JSON 형식 구성 파일을 /etc/zipkin/zipkin-config.json이라는 이름으로 만들고 다음 내용을 입력합니다. Zipkin 서버의 기본 포트인 9411을 사용합니다:
{ "service_name": "nginx",
"collector_host": "Zipkin-server-IP-address",
"collector_port": 9411
}- 구성 객체에 대한 자세한 내용은 GitHub의 JSON 스키마를 참조하세요.
NGINX Plus 구성
NGINX Plus 호스트에서 다음 지침을 수행합니다.
- 다음의 지침에 따라 NGINX OpenTracing 모듈을 설치합니다. NGINX Plus 관리 가이드.
- 기본 NGINX Plus 구성 파일의 기본(최상위) 컨텍스트에 다음
load_module지시문을 추가합니다(/etc/nginx/nginx.conf):
load_module modules/ngx_http_opentracing_module.so;- NGINX Plus 구성에 다음 지시문을 추가합니다. 기존 구성 방식을 사용하는 경우, 지시문을 /etc/nginx/conf.d/opentracing.conf이라는 새 파일에 넣습니다. 또한 다음
include지시문이/etc/nginx/nginx.conf의 http 컨텍스트에 있는지 확인합니다.
http {
include /etc/nginx/conf.d/*.conf;
}# Load a vendor tracer#opentracing_load_tracer /usr/local/libjaegertracing_plugin.so
# /etc/jaeger/jaeger-config.json;
#opentracing_load_tracer /usr/local/lib/libzipkin_opentracing_plugin.so
# /etc/zipkin/zipkin-config.json;
# Enable tracing for all requests
opentracing on;
# Set additional tags that capture the value of NGINX Plus variables
opentracing_tag bytes_sent $bytes_sent;
opentracing_tag http_user_agent $http_user_agent;
opentracing_tag request_time $request_time;
opentracing_tag upstream_addr $upstream_addr;
opentracing_tag upstream_bytes_received $upstream_bytes_received;
opentracing_tag upstream_cache_status $upstream_cache_status;
opentracing_tag upstream_connect_time $upstream_connect_time;
opentracing_tag upstream_header_time $upstream_header_time;
opentracing_tag upstream_queue_time $upstream_queue_time;
opentracing_tag upstream_response_time $upstream_response_time;
#uncomment for debugging
# log_format opentracing '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for" '
# '"$host" sn="$server_name" '
# 'rt=$request_time '
# 'ua="$upstream_addr" us="$upstream_status" '
# 'ut="$upstream_response_time" ul="$upstream_response_length" '
# 'cs=$upstream_cache_status';
#access_log /var/log/nginx/opentracing.log opentracing;
server {
listen 9001;
location / {
# The operation name used for OpenTracing Spans defaults to the name of the
# 'location' block, but uncomment this directive to customize it.
#opentracing_operation_name $uri;
# Propagate the active Span context upstream, so that the trace can be
# continued by the backend.
opentracing_propagate_context;
# Make sure that your Ruby app is listening on port 4567
proxy_pass http://127.0.0.1:4567;
}
}- The
opentracing_load_tracer지시문은 트레이서 플러그인을 활성화합니다. 필요에 따라 Jaeger 또는 Zipkin용 지시문의 주석을 해제하세요. - The
opentracing_tag지시어를 사용하면 NGINX Plus 변수를 추적기 UI에 표시되는 OpenTracing 태그로 사용할 수 있습니다. - OpenTracing 활동을 디버깅하려면
log_format및access_log지시문의 주석을 해제합니다. 기본 NGINX 액세스 로그와 로그 형식을 이 형식으로 바꾸려면 해당 지시문의 주석을 해제한 다음, 세 곳에 있는 “opentracing”을 “main”으로 변경합니다. 또 다른 방법은 포트 9001의 트래픽에 대해서만 OpenTracing 활동을 기록하는 것입니다.log_format및access_log지시문의 주석을 해제하고 이를server블록으로 이동합니다. - The
server블록은 다음 섹션에서 설명하는 샘플 Ruby 애플리케이션에 OpenTracing을 설정합니다.
- NGINX Plus 구성을 검증하고 다시 로드합니다:
$ nginx -t$ nginx -s reload샘플 Ruby 앱 설정하기
트레이서와 NGINX Plus 구성을 완료한 후, OpenTracing 데이터가 어떻게 표시되는지 보여 주는 샘플 Ruby 앱을 만듭니다. 이 앱을 사용하면 NGINX Plus 캐싱으로 응답 시간이 얼마나 단축되는지 측정할 수 있습니다. 앱이 다음과 같은 HTTP GET 요청을 /에 대해 수신하면 응답하기 전에 임의의 시간(2~5초) 동안 대기합니다.
$ curl http://NGINX-Plus-IP-address:9001/- Install and set up both Ruby와 Sinatra(다른 Ruby 웹 애플리케이션 프레임워크를 대체하기 위해 Ruby로 작성된 오픈 소스 웹 애플리케이션 라이브러리이자 도메인 특화 언어)를 모두 설치하고 설정합니다.
- 다음 내용으로 app.rb 파일을 만듭니다:
#!/usr/bin/ruby
require 'sinatra'
get '/*' do
out = "<h1>Ruby simple app</h1>" + "\n"
#Sleep a random time between 2s and 5s
sleeping_time = rand(4)+2
sleep(sleeping_time)
puts "slept for: #{sleeping_time}s."
out += '<p>some output text</p>' + "\n"
return out
end- Make app.rb에 실행 권한을 부여하고 실행합니다:
$ chmod +x app.rb$ ./app.rb캐싱 없이 응답 시간 추적하기
캐싱이 활성화되지 않은 경우 NGINX Plus가 요청에 응답하는 데 걸리는 시간을 Jaeger와 Zipkin을 사용하여 보여 줍니다. 각 트레이서에 요청을 5개씩 보냅니다.
캐싱을 사용하지 않은 Jaeger의 출력
Jaeger UI에 표시된 5개의 요청은 다음과 같습니다(최신 요청부터 표시):

Ruby 앱 콘솔에서도 동일한 정보를 확인할 수 있습니다:
- -> /slept for: 3s.
127.0.0.1 - - [07/Jun/2019: 10:50:46 +0000] "GET / HTTP/1.1" 200 49 3.0028
127.0.0.1 - - [07/Jun/2019: 10:50:43 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 2s.
127.0.0.1 - - [07/Jun/2019: 10:50:56 +0000] "GET / HTTP/1.1" 200 49 2.0018
127.0.0.1 - - [07/Jun/2019: 10:50:54 UTC] "GET / HTTP/1.0"1 200 49
- -> /
slept for: 3s.
127.0.0.1 - - [07/Jun/2019: 10:53:16 +0000] "GET / HTTP/1.1" 200 49 3.0029
127.0.0.1 - - [07/Jun/2019: 10:53:13 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 4s.
127.0.0.1 - - [07/Jun/2019: 10:54:03 +0000] "GET / HTTP/1.1" 200 49 4.0030
127.0.0.1 - - [07/Jun/2019: 10:53:59 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 3s.
127.0.0.1 - - [07/Jun/2019: 10:54:11 +0000] "GET / HTTP/1.1" 200 49 3.0012
127.0.0.1 - - [07/Jun/2019: 10:54:08 UTC] "GET / HTTP/1.0" 200 49Jaeger UI에서 첫 번째(가장 최근) 요청을 클릭하여 태그로 추가한 NGINX Plus 변수의 값을 비롯한 세부 정보를 확인합니다:

캐싱을 사용하지 않은 Zipkin의 출력
Zipkin UI에 또 다른 5개의 요청이 표시됩니다:

Ruby 앱 콘솔에서도 동일한 정보를 확인할 수 있습니다:
- -> /slept for: 2s.
127.0.0.1 - - [07/Jun/2019: 10:31:18 +0000] "GET / HTTP/1.1" 200 49 2.0021
127.0.0.1 - - [07/Jun/2019: 10:31:16 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 3s.
127.0.0.1 - - [07/Jun/2019: 10:31:50 +0000] "GET / HTTP/1.1" 200 49 3.0029
127.0.0.1 - - [07/Jun/2019: 10:31:47 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 3s.
127.0.0.1 - - [07/Jun/2019: 10:32:08 +0000] "GET / HTTP/1.1" 200 49 3.0026
127.0.0.1 - - [07/Jun/2019: 10:32:05 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 3s.
127.0.0.1 - - [07/Jun/2019: 10:32:32 +0000] "GET / HTTP/1.1" 200 49 3.0015
127.0.0.1 - - [07/Jun/2019: 10:32:29 UTC] "GET / HTTP/1.0" 200 49
- -> /
slept for: 5s.
127.0.0.1 - - [07/Jun/2019: 10:32:52 +0000] "GET / HTTP/1.1" 200 49 5.0030
127.0.0.1 - - [07/Jun/2019: 10:32:47 UTC] "GET / HTTP/1.0" 200 49Zipkin UI에서 첫 번째 요청을 클릭하여 태그로 추가한 NGINX Plus 변수의 값을 비롯한 세부 정보를 확인합니다.

캐싱을 사용한 응답 시간 추적
NGINX Plus 캐싱 구성
다음에서 만든 캐싱을 활성화하려면 opentracing.conf 파일에 지시문을 추가합니다: NGINX Plus 구성.
- In the
http컨텍스트에 다음proxy_cache_path지시어를 추가합니다:
proxy_cache_path /data/nginx/cache keys_zone=one:10m;- In the
server블록에 다음proxy_cache및proxy_cache_valid지시어를 추가합니다:
proxy_cache one;
proxy_cache_valid any 1m;- 구성을 검증하고 다시 로드합니다:
$ nginx -t$ nginx -s reload캐싱을 적용한 Jaeger의 출력
두 번의 요청 후 Jaeger UI는 다음과 같습니다.

첫 번째 응답(13f69db로 표시됨)은 4초가 걸렸습니다. NGINX Plus가 응답을 캐시했으며, 약 15초 후 요청을 반복했을 때는 NGINX Plus 캐시에서 응답을 가져왔기 때문에 2밀리초(ms)도 걸리지 않았습니다.
두 요청을 자세히 살펴보면 응답 시간에 차이가 나는 이유를 알 수 있습니다. 첫 번째 요청의 경우 upstream_cache_status는 MISS이며, 이는 요청된 데이터가 캐시에 없었다는 의미입니다. Ruby 앱에서 4초의 지연이 추가되었습니다.

두 번째 요청에서는 upstream_cache_status가 HIT입니다. 데이터가 캐시에서 제공되므로 Ruby 앱에서 지연을 추가할 수 없으며 응답 시간은 2ms 미만입니다. 비어 있는 upstream_* 값 역시 업스트림 서버가 이 응답에 관여하지 않았음을 나타냅니다.

캐싱을 적용한 Zipkin의 출력
캐싱이 활성화된 두 요청을 Zipkin UI에 표시하면 비슷한 양상을 확인할 수 있습니다:

이번에도 두 요청을 자세히 살펴보면 응답 시간 차이의 원인을 알 수 있습니다. 첫 번째 요청에서는 응답이 캐시되지 않았고(upstream_cache_status 값이 MISS임), Ruby 앱에서 (공교롭게도) Jaeger 예시와 동일하게 4초의 지연이 추가됩니다.

두 번째 요청을 보내기 전에 응답이 캐시되었으므로 upstream_cache_status는 HIT입니다.

결론
NGINX OpenTracing 모듈을 사용하면 NGINX Plus 요청과 응답을 추적하고 OpenTracing 태그를 통해 NGINX Plus 변수에 액세스할 수 있습니다. 이 모듈에서는 다양한 트레이서도 사용할 수 있습니다.
NGINX OpenTracing 모듈에 대한 자세한 내용은 GitHub의 NGINX OpenTracing 모듈 리포지토리를 참조하세요.
NGINX Plus에서 OpenTracing을 사용해 보려면 지금 30일 무료 평가판을 시작하거나 문의하여 사용 사례에 대해 상담해 보세요.
관련 블로그 게시물
시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.
F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.
Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.
진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.
NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.
Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.