OpenTracing + NGINX 요청 및 응답 시간 분석

모든 이점에도 불구하고 Microservices 아키텍처는 새로운 복잡성을 야기합니다. 하나는 애플리케이션을 구성하는 모든 Microservices 간에 데이터 흐름을 통해 처리되는 요청을 Tracing 하는 문제입니다. 이를 위해 Distributed Tracing이라는 새로운 방법론이 개발되었으며 OpenTracing 은 Distributed Tracing 도구의 설계 및 구현을 안내하기 위한 사양 및 표준 API 세트입니다.

NGINX Plus Release 18(R18)부터는 동적 모듈 Repository에 NGINX OpenTracing 모듈을 추가했습니다(몇 년 전부터 GitHub에서 타사 모듈로 사용할 수 있었습니다). NGINX OpenTracing 모듈의 큰 장점은 Distributed Tracing을 위해 NGINX 및 NGINX Plus를 계측함으로써 애플리케이션을 개별적으로 계측하지 않고도 Proxy된 모든 애플리케이션에 대한 Tracing 데이터를 얻을 수 있다는 것입니다.

이 포스트에서는 NGINX 또는 NGINX Plus에 대한 요청의 Distributed Tracing을 활성화하는 방법을 보여줍니다. 두 가지 Distributed Tracing 서비스인 JaegerZipkin에 대한 지침을 제공합니다. (다른 Tracing 프로그램 목록은 OpenTracing 설명서를 참조하십시오.) Tracing 프로그램에서 제공하는 정보의 종류를 설명하기 위해 NGINX Plus Caching이 활성화되기 전과 후의 요청 처리를 비교합니다.

Tracer 프로그램에는 두 가지 기본 구성 요소가 있습니다.

  • 호스트에서 실행 중인 애플리케이션에서 Tracing 데이터를 수집하는 Agent입니다. 이 경우 “애플리케이션”은 NGINX Plus이고 Agent는 플러그인으로 구현됩니다.
  • 하나 이상의 Agent에서 Tracing 데이터를 받아 중앙 UI에 표시하는 서버(Collector라고도 함)입니다. 원하는 대로 NGINX Plus 호스트 또는 다른 호스트에서 서버를 실행할 수 있습니다.

목차

1. Tracer 서버 설치
1-1. Jaeger 서버 설치
1-2. Zipkin 서버 설치
2. Tracer 프로그램 플러그인 설치 및 구성
2-1. Jaeger 플러그인 설치
2-2. Zipkin 플러그인 설치
3. NGINX Plus 구성
4. 샘플 Ruby 애플리케이션 설정
5. Caching 없이 응답 시간 Tracing
5-1. Caching 없는 Jaeger의 출력
5-2. Caching 없이 Zipkin에서 Output
6. Caching으로 응답 시간 Tracing 
6-1. NGINX Plus Caching 구성
6-2. Caching이 있는 Jaeger의 Output
6-3. Caching이 있는 Zipkin의 Output
7. 결론

1. OpenTracing – Tracer 서버 설치

첫 번째 단계는 선택한 Tracer 프로그램에 대해 서버를 설치 및 구성하는 것입니다. Jaeger와 Zipkin에 대한 지침을 제공하고 있습니다. 필요에 따라 다른 Tracer에 적용하십시오.

1-1. Jaeger 서버 설치

Jaeger 서버 설치는 다음과 같은 방법을 권장합니다. 1단계에서 지정한 URL에서 Docker Image를 다운로드할 수도 있습니다.

1. Jaeger 다운로드 페이지로 이동하여 Linux Binary를 다운로드합니다(작성 당시에는 jaeger-1.12.0-linux-amd64.tar).

2. Binary를 /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

3. 브라우저에서 http://Jaeger-server-IP-address:16686/ (16686은 Jaeger 서버의 기본 포트입니다.)에서 Jaeger UI에 액세스할 수 있는지 확인합니다.

1-2. Zipkin 서버 설치

1. Zipkin의 Docker Image를 다운로드하고 실행합니다(기본값인 포트 9411 사용).

$ docker run -d -p 9411:9411 openzipkin/zipkin

2. 브라우저에서 Zipkin UI(http://Zipkin-server-IP-address:9411/)에 액세스할 수 있는지 확인합니다.

2. OpenTracing – Tracer 프로그램 플러그인 설치 및 구성

NGINX Plus 호스트에서 다음 명령을 실행하여 Jaeger 또는 Zipkin 용 플러그인을 설치합니다.

2-1. Jaeger 플러그인 설치

1. 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

Source에서 플러그인을 Build하는 방법은 GitHub에서 확인할 수 있습니다.

2. 플러그인에 대한 JSON ‑ Formatted 구성 파일(/etc/jaeger/jaeger-config.json)을 다음 내용으로 생성합니다. Jaeger 서버의 기본 포트인 6831을 사용하고 있습니다.

{
  "service_name": "nginx",
  "sampler": {
    "type": "const",
    "param": 1
  },
  "reporter": {
    "localAgentHostPort": "Jaeger-server-IP-address:6831"
  }
}

샘플러(Sampler) 객체에 대한 자세한 내용은 Jaeger 설명서를 참조하십시오.

2-2. Zipkin 플러그인 설치

1. 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

2. 다음 콘텐츠가 포함된 /etc/zipkin/zipkin-config.json이라는 플러그 인용 JSON ‑ Formatted 구성 파일을 만듭니다. Zipkin 서버의 기본 포트인 9411을 사용하고 있습니다.

{
  "service_name": "nginx",
  "collector_host": "Zipkin-server-IP-address",
  "collector_port": 9411
}

구성 객체에 대한 자세한 내용은 GitHub의 JSON 스키마를 참조하십시오.

3. NGINX Plus – OpenTracing 구성

NGINX Plus 호스트에서 다음 지침을 수행합니다.

1. NGINX Plus NGINX OpenTracing 모듈을 설치합니다. (Debian and Ubuntu)

 $ apt-get install nginx-plus-module-opentracing

2. 기본 NGINX Plus 구성 파일(/etc/nginx/nginx.conf)의 main(최상위) 지시문에 다음 load_module 지시문을 추가합니다.

load_module modules/ngx_http_opentracing_module.so;

3. NGINX Plus 구성에 다음 지시문을 추가합니다.

기존 구성 체계를 사용하는 경우 지시문을 /etc/nginx/conf.d/opentracing.conf라는 새 파일에 넣습니다. 또한 다음 include 지시문이 /etc/nginx/nginx.confhttp 지시문에 나타나는지 확인합니다.

http {
    include /etc/nginx/conf.d/*.conf;
}
  • opentracing_load_tracer 지시문은 Tracer 프로그램 플러그인을 활성화합니다. Jaeger 또는 Zipkin에 대한 지시문의 주석을 적절하게 제거하십시오.
  • opentracing_tag 지시문은 Tracer 프로그램 UI에 표시되는 OpenTracing 태그로 NGINX Plus 변수를 사용할 수 있도록 합니다.
  • OpenTracing 활동을 디버그 하려면 log_formataccess_log 지시문의 주석 처리를 제거하십시오. 기본 NGINX Access Log 및 Log Format을 이 형식으로 바꾸려면 지시문의 주석을 제거한 다음 “opentracing”의 세 가지 인스턴스를 “main”으로 변경하십시오. 또 다른 옵션은 포트 9001의 트래픽에 대해서만 OpenTracing 활동을 기록하는 것입니다. log_format 및 access_log 지시문의 주석을 제거하고 server 블록으로 이동합니다.
  • server 블록은 다음 섹션에서 설명하는 샘플 Ruby 애플리케이션에 대한 OpenTracing을 설정합니다.
# 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;
    }
}

4. NGINX Plus 구성을 검증하고 reload 합니다.

$ nginx -t
$ nginx -s reload

4. OpenTracing 샘플 Ruby 애플리케이션 설정

Tracer 프로그램과 NGINX Plus 구성이 준비되면 OpenTracing 데이터가 어떻게 보이는지 보여주는 샘플 Ruby 애플리케이션을 만듭니다. 이 애플리케이션을 통해 NGINX Plus Caching이 응답 시간을 얼마나 향상시키는지 측정할 수 있습니다. 애플리케이션이 /에 대한 다음 HTTP GET 요청과 같은 요청을 받으면 응답하기 전에 임의의 시간(2~5초)을 기다립니다.

$ curl http://NGINX-Plus-IP-address:9001/

1. RubySinatra(다른 Ruby 웹 애플리케이션 프레임워크의 대안으로 Ruby로 작성된 Open Source 소프트웨어 웹 애플리케이션 라이브러리 및 Domain ‑ Specific 언어)를 모두 설치하고 설정합니다.

2. 다음 내용으로 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

3. app.rb를 실행 가능하게 만들고 실행합니다.

$ chmod +x app.rb
$ ./app.rb

5. Caching 없이 응답 시간 Tracing

Jaeger와 Zipkin을 사용하여 Caching이 활성화되지 않은 경우 NGINX Plus가 요청에 응답하는 데 걸리는 시간을 보여줍니다. 각 Tracer에 대해 5개의 요청을 보냅니다.

5-1. Caching 없는 Jaeger의 출력

Jeger UI에 표시되는 5가지 요청은 다음과 같습니다(가장 최근의 첫 번째 요청):

OpenTracing 1

다음은 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 49

Jaeger UI에서 첫 번째(가장 최근) 요청을 클릭하여 태그로 추가한 NGINX Plus 변수의 값을 포함하여 이에 대한 세부 정보를 봅니다.

OpenTracing

5-2. Caching 없이 Zipkin에서 Output

다음은 Zipkin UI의 또 다른 다섯 가지 요청입니다.

OpenTracing

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 49

Zipkin UI에서 첫 번째 요청을 클릭하여 태그로 추가한 NGINX Plus 변수의 값을 포함하여 이에 대한 세부 정보를 확인합니다.

OpenTracing

6. Caching으로 응답 시간 Tracing 

6-1. NGINX Plus Caching 구성

NGINX Plus 구성에서 만든 opentracing.conf 파일에 지시문을 추가하여 Caching을 활성화합니다.

1. http 지시문에서 이 proxy_cache_path 지시문을 추가합니다.

proxy_cache_path /data/nginx/cache keys_zone=one:10m;

2. server 블록에서 다음 proxy_cacheproxy_cache_valid 지시문을 추가합니다.

proxy_cache one;
proxy_cache_valid any 1m;

3. 구성을 확인하고 reload합니다.

$ nginx -t
$ nginx -s reload

6-2. Caching이 있는 Jaeger의 Output

다음은 두 번의 요청 후 Jaeger UI입니다.

첫 번째 응답(Lable 13f69db)은 4초가 걸렸습니다. NGINX Plus는 응답을 Cache 했으며, 약 15초 후에 요청이 반복되었을 때 NGINX Plus Cache에서 왔기 때문에 응답이 2밀리초(ms) 미만으로 걸렸습니다.

두 요청을 자세히 살펴보면 응답 시간의 차이를 알 수 있습니다. 첫 번째 요청의 경우 upstream_cache_status는 MISS이며 요청된 데이터가 Cache에 없음을 의미합니다. Ruby 애플리케이션은 4초의 지연을 추가했습니다.

두 번째 요청의 경우 upstream_cache_status는 HIT입니다. 데이터가 Cache에서 오기 때문에 Ruby 애플리케이션은 지연을 추가할 수 없으며 응답 시간은 2ms 미만입니다. 빈 upstream_* 값은 Upstream 서버가 이 응답에 관련되지 않았음을 나타냅니다.

6-3. Caching이 있는 Zipkin의 Output

Caching이 활성화된 두 개의 요청에 대한 Zipkin UI의 디스플레이는 비슷한 그림을 그립니다.

다시 두 요청을 자세히 살펴보면 응답 시간의 차이를 알 수 있습니다. 응답은 첫 번째 요청에 대해 Cache 되지 않으며(upstream_cache_statusMISS입니다.) Ruby 애플리케이션은 (동시에) Jaeger 예제에서와 동일한 4초 지연을 추가합니다.

두 번째 요청을 하기 전에 응답이 Cache되었으므로 upstream_cache_statusHIT입니다.

7. NGINX OpenTracing 결론

NGINX OpenTracing 모듈은 NGINX Plus Tracing 요청 및 응답 Tracing을 활성화하고 OpenTracing 태그를 사용하여 NGINX Plus 변수에 대한 액세스를 제공합니다. 다른 Tracing 프로그램도 이 모듈과 함께 사용할 수 있습니다.

NGINX OpenTracing 모듈에 대한 자세한 내용은 GitHub의 NGINX OpenTracing 모듈 Repo를 참조하십시오.

NGINX Plus와 함께 OpenTracing을 사용해 보려면 지금 30일 무료 평가판을 신청하거나 사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.