블로그 | NGINX

NGINX 튜토리얼: OpenTelemetry 추적을 사용하여 마이크로서비스를 이해하는 방법

NGINX-F5-수평-검정-유형-RGB의 일부
하비에르 에반스 썸네일
하비에르 에반스
2023년 3월 28일 게시

이 게시물은 Microservices March 2023의 개념을 실제로 구현하는 데 도움이 되는 4가지 튜토리얼 중 하나입니다. 마이크로서비스 제공 시작하기 :

 

마이크로서비스 아키텍처는 팀 자율성 향상, 확장 및 배포 유연성 향상 등 많은 이점을 제공합니다. 단점은 시스템에 서비스가 많을수록(마이크로서비스 앱은 수십 개, 심지어 수백 개가 있을 수 있음) 시스템 전반적인 운영에 대한 명확한 그림을 유지하는 것이 더 어려워진다는 것입니다. 복잡한 소프트웨어 시스템의 작성자이자 유지 관리자로서, 우리는 명확한 그림을 갖는 것이 얼마나 중요한지 알고 있습니다. 관찰 툴을 사용하면 다양한 서비스와 지원 인프라에 걸쳐 해당 그림을 구축할 수 있습니다.

이 튜토리얼에서는 마이크로서비스 앱에 대한 매우 중요한 관찰 유형 중 하나인 추적에 대해 살펴보겠습니다. 시작하기 전에 관찰 가능성을 논의할 때 일반적으로 사용되는 몇 가지 용어를 정의해 보겠습니다.

  • 관찰 가능성 – 복잡한 시스템(예: 마이크로서비스 앱)의 내부 상태나 조건을 외부 출력(예: 추적, 로그, 메트릭)에 대한 지식에만 기반하여 이해하는 능력입니다.
  • 모니터링 – 일정 기간 동안 객체의 진행 상황이나 상태를 관찰하고 확인하는 능력. 예를 들어, 최대 사용량 시간대에 앱으로 유입되는 트래픽을 모니터링하고, 이 정보를 사용하여 이에 따라 앱을 적절히 확장할 수 있습니다.
  • 원격 측정 – 측정 항목, 추적 정보, 로그를 수집하여 저장 및 분석을 위해 원본 시스템에서 다른 시스템으로 전송하는 작업입니다. 또한 데이터 자체도 그렇습니다.
  • 추적/추적 – 분산 시스템의 모든 노드를 통과하는 요청이나 작업의 여정에 대한 설명입니다.
  • Span – 작업과 관련 메타데이터의 추적 내의 레코드입니다. 추적은 여러 개의 중첩된 스팬으로 구성됩니다.
  • 이벤트 로깅/로그 – 메타데이터가 포함된 타임스탬프가 있는 텍스트 레코드.
  • 메트릭 - 런타임에 캡처된 측정값. 예를 들어, 특정 시점에 애플리케이션이 사용하는 메모리 양입니다.

이러한 모든 개념을 사용하여 마이크로서비스의 성능에 대한 통찰력을 얻을 수 있습니다. 추적은 요청이 이루어질 때 여러 개의 종종 느슨하게 결합된 구성 요소에서 무슨 일이 일어나고 있는지에 대한 "큰 그림"을 제공하기 때문에 관찰 가능성 전략의 특히 유용한 부분입니다. 이는 성능 병목 현상을 파악하는 데에도 특히 효과적인 방법입니다.

이 튜토리얼에서는 OpenTelemetry (OTel)의 추적 툴킷을 사용합니다. OTel은 원격 측정 데이터를 수집, 처리, 내보내기 위한 오픈소스 공급업체 중립적 표준으로, 인기가 빠르게 높아지고 있습니다. OTel의 개념에서 추적은 여러 서비스를 포함할 수 있는 데이터 흐름을 연대순으로 정렬된 일련의 "청크"로 나누어 쉽게 이해할 수 있도록 도와줍니다.

  • "덩어리"에서 일어난 모든 단계
  • 이 모든 단계가 걸리는 시간은 얼마입니까?
  • 각 단계에 대한 메타데이터

OTel에 익숙하지 않은 경우 OpenTelemetry란 무엇인가?를 참조하여 표준에 대한 자세한 소개와 구현 고려 사항을 알아보세요.

튜토리얼 개요

이 튜토리얼은 OTel을 사용하여 마이크로서비스 앱의 작업을 추적하는 데 중점을 둡니다. 이 튜토리얼의 4가지 과제에서는 시스템을 통해 요청을 추적하고 마이크로서비스에 대한 질문에 답하는 방법을 알아봅니다.

이러한 과제는 처음으로 추적을 설정할 때 권장하는 프로세스를 보여줍니다. 단계는 다음과 같습니다.

  1. 시스템과 계측하려는 특정 작업을 이해하세요.
  2. 실행 중인 시스템에서 알아야 할 사항을 결정하세요.
  3. "순진하게" 시스템을 계측합니다. 즉, 필요 없는 정보를 제거하거나 사용자 지정 데이터 포인트를 수집하지 않고 기본 구성을 사용하고 계측이 질문에 답하는 데 도움이 되는지 평가합니다.
  4. 보고된 정보를 조정하면 해당 질문에 더 빨리 답할 수 있습니다.

메모: 이 튜토리얼의 목적은 프로덕션에서 마이크로서비스를 올바르게 배포하는 방법을 보여주는 것이 아니라 원격 측정에 대한 핵심 개념을 설명하는 것입니다. 실제 "마이크로서비스" 아키텍처를 사용하지만 몇 가지 중요한 단서가 있습니다.

  • 이 튜토리얼에서는 Kubernetes나 Nomad와 같은 컨테이너 오케스트레이션 프레임워크를 사용하지 않습니다. 이를 통해 특정 프레임워크의 세부 사항에 얽매이지 않고도 마이크로서비스 개념에 대해 배울 수 있습니다. 여기에 소개된 패턴은 이러한 프레임워크 중 하나를 실행하는 시스템으로 이식 가능합니다.
  • 이 서비스는 소프트웨어 엔지니어링의 엄격성보다는 이해하기 쉬운 방식으로 최적화되었습니다. 요점은 코드의 세부 사항이 아닌 시스템에서 서비스의 역할과 통신 패턴을 살펴보는 것입니다. 자세한 내용은 개별 서비스의 README 파일을 참조하세요.

튜토리얼 아키텍처 및 원격 측정 목표

아키텍처 및 사용자 흐름

이 다이어그램은 튜토리얼에서 사용된 마이크로서비스와 다른 요소들 간의 전반적인 아키텍처와 데이터 흐름을 보여줍니다.

NGINX와 RabbitMQ라는 두 개의 마이크로서비스가 있는 메시징 시스템의 OpenTelemetry 추적을 통해 튜토리얼에서 사용되는 토폴로지를 보여주는 다이어그램

두 개의 마이크로서비스는 다음과 같습니다.

  • 메신저 서비스 - 메시지 저장 기능을 갖춘 간단한 채팅 API
  • 알림 서비스 - 사용자의 선호도에 따라 이벤트를 트리거하여 사용자에게 경고하는 리스너

지원 인프라의 세 가지는 다음과 같습니다.

  • NGINX 오픈 소스메신저 서비스와 전체 시스템에 대한 진입점
  • RabbitMQ – 서비스가 비동기적으로 통신할 수 있도록 하는 인기 있는 오픈 소스 메시지 브로커
  • Jaeger – 시스템의 구성요소에서 원격 측정 데이터를 수집하고 시각화하기 위한 오픈 소스 엔드투엔드 분산 추적 시스템

잠시 OTel을 그림에서 제외하고, 우리가 추적하고 있는 이벤트 순서에 집중할 수 있습니다. 즉, 사용자가 새 채팅 메시지를 보내고 수신자에게 알림이 전송되면 무슨 일이 일어나는지입니다.

튜토리얼에서 사용되는 메시징 시스템의 정보 흐름을 보여주는 다이어그램

흐름은 다음과 같습니다.

  1. 사용자가 메신저 서비스에 메시지를 보냅니다. NGINX 역방향 프록시는 메시지를 가로채서 메신저 서비스의 여러 인스턴스 중 하나로 전달합니다.
  2. 메신저 서비스는 새로운 메시지를 데이터베이스에 작성합니다.
  3. 메신저 서비스는 메시지가 전송되었음을 나타내기 위해 chat_queue 라는 RabbitMQ 메시지 큐에서 이벤트를 생성합니다. 이 이벤트는 일반적이며 특정 대상이 없습니다.
  4. 동시에:

    • 4a. 메신저 서비스는 보낸 사람에게 메시지가 성공적으로 전송되었다는 응답을 반환합니다.
    • 4b. 알림 서비스는 chat_queue 에서 새로운 이벤트를 알아차리고 이를 사용합니다.
  5. 알림 서비스는 새 메시지 수신자의 알림 기본 설정을 파악하기 위해 데이터베이스를 확인합니다.
  6. 알림 서비스는 수신자가 선호하는 방법을 사용하여 하나 이상의 알림을 보냅니다(이 튜토리얼에서는 SMS와 이메일을 선택할 수 있습니다).

원격측정 목표

원격 측정 계측을 설정할 때는 "모든 것을 보내고 통찰력을 기대하는 것"보다 더 정의된 계측 목표 집합으로 시작하는 것이 가장 좋습니다. 이 튜토리얼에서는 세 가지 주요 원격 측정 목표를 설정합니다.

  1. 새 메시지 흐름 동안 요청이 거치는 모든 단계를 이해합니다.
  2. 정상적인 조건에서는 5초 이내에 흐름이 종단간으로 실행된다는 확신을 갖습니다.
  3. 메신저 서비스에서 전송한 이벤트를 알림 서비스가 처리하는 데 걸리는 시간을 확인합니다(지연이 너무 많으면 알림 서비스가 이벤트 대기열에서 읽는 데 문제가 있고 이벤트가 백업 중일 수 있음)

이러한 목표는 시스템의 기술적 운영과 사용자 경험 모두와 관련이 있습니다.

튜토리얼 전제 조건 및 설정

필수 조건

자신의 환경에서 튜토리얼을 완료하려면 다음이 필요합니다.

  • Linux/Unix 호환 환경

    메모: NGINX 추적과 관련된 이 튜토리얼의 활동은 NGINX용 OpenTelemetry 모듈과 호환되지 않기 때문에 ARM 기반 프로세서에서는 작동하지 않습니다. (여기에는 Linux aarch64 아키텍처와 M1 또는 M2 칩이 장착된 Apple 컴퓨터가 포함됩니다.) 메신저알림 서비스와 관련된 활동은 모든 아키텍처에서 작동합니다.

  • Linux 명령줄, JavaScript 및 bash 에 대한 기본적인 지식(그러나 모든 코드와 명령이 제공되고 설명되므로 제한된 지식으로도 성공할 수 있음)
  • DockerDocker Compose
  • Node.js 19.x 이상

    • 우리는 버전 19.x를 테스트했지만, 최신 버전의 Node.js도 작동할 것으로 기대합니다.
    • Node.js 설치에 대한 자세한 내용은 메신저 서비스 저장소의 README를 참조하세요. 튜토리얼에서 사용된 것과 정확히 동일한 Node.js 버전을 얻으려면 asdf를 설치할 수도 있습니다.
  • curl (대부분 시스템에 이미 설치되어 있음)
  • 아키텍처 및 사용자 흐름 : 메신저알림 (다음 섹션에서 다운로드), NGINX 오픈 소스 , JaegerRabbitMQ 에 나열된 기술입니다.

메모: 메신저알림 서비스가 Node.js로 작성되었기 때문에 이 튜토리얼에서는 JavaScript SDK를 사용합니다. 또한 OTel 자동 계측 기능( 자동 계측 이라고도 함)을 설정하여 OTel에서 제공하는 정보 유형을 파악할 수 있습니다. 이 튜토리얼에서는 OTel Node.js SDK에 대해 알아야 할 모든 것을 설명하지만, 자세한 내용은 OTel 설명서를 참조하세요.

설정

  1. 터미널 세션을 시작합니다.
  2. 홈 디렉토리에서 microservices-march 디렉토리를 만들고 이 튜토리얼의 GitHub 저장소를 여기에 복제합니다. (다른 디렉토리 이름을 사용하고 이에 따라 지침을 조정할 수도 있습니다.)

    메모: 튜토리얼 전체에서 Linux 명령줄의 프롬프트는 생략되어 터미널에 명령을 복사하여 붙여 넣기가 더 쉬워졌습니다. 틸드( ~ )는 홈 디렉토리를 나타냅니다.

    mkdir ~/microservices-marchcd ~/microservices-march
    git 복제 https://github.com/microservices-march/messenger --branch mm23-metrics-start
    git 복제 https://github.com/microservices-march/notifier --branch mm23-metrics-start
    git 복제 https://github.com/microservices-march/platform --branch mm23-metrics-start
    

도전 1: 기본 OTel 계측 설정

이 챌린지에서는 메신저 서비스를 시작 하고 OTel 자동 계측을 구성하여 원격 측정 데이터를 콘솔로 전송합니다.

메신저 서비스 시작

  1. 플랫폼 저장소로 변경하고 Docker Compose를 시작합니다.

    cd ~/microservices-march/platformdocker 구성 -d --build
    

    이렇게 하면 RabbitMQ와 Jaeger가 시작되며, 이는 이후 챌린지에서 사용됩니다.

    • ‑d 플래그는 Docker Compose가 컨테이너가 시작되면 컨테이너에서 분리되도록 지시합니다(그렇지 않으면 컨테이너는 터미널에 연결된 상태로 유지됩니다).
    • --build 플래그는 Docker Compose가 시작 시 모든 이미지를 다시 빌드하도록 지시합니다. 이렇게 하면 파일에 잠재적인 변경 사항이 발생하더라도 실행 중인 이미지가 최신 상태로 유지됩니다.
  2. 메신저 저장소의 디렉토리로 변경하고 Node.js를 설치합니다(원하는 경우 다른 방법으로 대체할 수 있음):

    cd ~/microservices-march/messenger/appasdf 설치
    
  3. 종속성 설치:

    npm 설치
    
  4. 메신저 서비스를 위한 PostgreSQL 데이터베이스를 시작합니다.

    docker compose up -d
    
  5. 데이터베이스 스키마와 테이블을 만들고 일부 시드 데이터를 삽입합니다.

    npm 실행 새로 고침-db
    

콘솔로 전송된 OTel 자동 계측 구성

OTel 자동 계측을 사용하면 추적을 설정하기 위해 메신저 코드베이스에서 아무것도 수정할 필요가 없습니다. 모든 추적 구성은 애플리케이션 코드 자체로 가져오는 대신 런타임에 Node.js 프로세스로 가져오는 스크립트에 정의됩니다.

여기서는 추적의 가장 기본적인 대상인 콘솔을 사용하여 메신저 서비스의 자동 계측을 구성합니다. 챌린지 2 에서는 외부 수집기인 Jaeger에 추적을 보내도록 구성을 변경합니다.

  1. 메신저 리포의 디렉토리에서 작업하면서 OTel Node.js 핵심 패키지를 설치합니다.

    npm 설치 @opentelemetry/sdk-node@0.36.0 \
    @opentelemetry/auto-instrumentations-node@0.36.4
    

    이러한 라이브러리는 다음과 같은 기능을 제공합니다.

    • @opentelemetry/sdk-node – OTel 데이터 생성 및 내보내기
    • @opentelemetry/auto-instrumentations-node – 가장 일반적인 Node.js 계측의 기본 구성을 사용한 자동 설정

    메모: OTel의 독특한 점은 JavaScript SDK가 매우 작은 조각으로 나뉘어져 있다는 것입니다. 따라서 이 튜토리얼의 기본적인 예를 위해 몇 가지 패키지를 더 설치하게 됩니다. 이 튜토리얼에서 다루는 것 외에 계측 작업을 수행하는 데 어떤 패키지가 필요한지 알아보려면 (매우 유용한) OTel 시작 가이드를 꼼꼼히 살펴보고 OTel GitHub 저장소를 살펴보세요.

  2. OTel 추적을 위한 설정 및 구성 코드를 포함하기 위해 tracing.mjs 라는 새 파일을 만듭니다.

    터치 추적.mjs
    
  3. 원하는 텍스트 편집기에서 tracing.mjs를 열고 이 코드를 추가하세요.

    //1
    "@opentelemetry/sdk-node"에서 opentelemetry 가져오기;
    "@opentelemetry/auto-instrumentations-node"에서 {getNodeAutoInstrumentations} 가져오기;
    
    //2
    const sdk = new opentelemetry.NodeSDK({
    traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
    instrumentations: [getNodeAutoInstrumentations()],
    });
    
    //3
    sdk.start();
    

    코드는 다음을 수행합니다.

    1. OTel SDK에서 필요한 기능과 객체를 가져옵니다.
    2. NodeSDK의 새 인스턴스를 만들고 다음과 같이 구성합니다.

      • 콘솔에 스팬을 보냅니다( ConsoleSpanExporter ).
      • 자동 계측기를 기본 계측 세트로 사용합니다. 이 계측은 가장 일반적인 자동 계측 라이브러리를 모두 로드합니다. 튜토리얼에서 관련 내용은 다음과 같습니다.

        • Postgres 데이터베이스 라이브러리( pg )에 대한 @opentelemetry/instrumentation-pg
        • Node.js Express 프레임워크@opentelemetry/instrumentation-express
        • RabbitMQ 라이브러리( amqplib )에 대한 @opentelemetry/instrumentation-amqplib
    3. SDK를 시작합니다.
  4. 3단계에서 만든 자동 계측 스크립트를 가져와 메신저 서비스를 시작합니다.

    노드 --import ./tracing.mjs index.mjs
    

    잠시 후 추적과 관련된 많은 출력이 콘솔(터미널)에 나타나기 시작합니다.

    ...
    {
    추적 ID: '9c1801593a9d3b773e5cbd314a8ea89c',
    parentId: 정의되지 않음,
    traceState: 정의되지 않음,
    name: 'fs statSync',
    id: '2ddf082c1d609fbe',
    종류: 0,
    타임스탬프: 1676076410782000,
    기간: 3,
    속성: {},
    상태: { 코드: 0 },
    이벤트: [],
    링크: []
    }
    ...
    

메모: 챌린지 2에서는 터미널 세션을 재사용할 수 있도록 열어 둡니다.

도전 2: 모든 서비스에 대한 OTel 계측 및 추적 시각화 설정

추적을 보고 분석하는 데 사용할 수 있는 도구는 많지만 이 튜토리얼에서는 Jaeger를 사용합니다. Jaeger는 스팬 및 기타 추적 데이터를 볼 수 있는 내장 웹 기반 사용자 인터페이스를 갖춘 간단한 오픈 소스 엔드투엔드 분산 추적 프레임워크입니다. 플랫폼 저장소에 제공된 인프라에는 Jaeger가 포함되어 있습니다(1단계의 1단계에서 시작함). 이를 통해 복잡한 도구를 다루는 대신 데이터 분석에 집중할 수 있습니다.

Jaeger는 브라우저에서 http://localhost:16686 엔드포인트에서 접속할 수 있지만, 지금 바로 엔드포인트에 접속하면 시스템에 대해 볼 수 있는 것이 없습니다. 현재 수집 중인 추적 정보가 콘솔로 전송되고 있기 때문입니다! Jaeger에서 추적 데이터를 보려면 OpenTelemetry 프로토콜 (OTLP) 형식을 사용하여 추적 데이터를 내보내야 합니다.

이 과제에서는 다음을 위한 계측을 구성하여 핵심 사용자 흐름을 계측합니다.

외부 수집기로 전송된 OTel 자동 계측 구성

상기시켜드리자면, OTel 자동 계측을 사용한다는 것은 추적을 설정하기 위해 메신저 코드베이스에서 아무것도 수정하지 않는다는 것을 의미합니다. 대신, 모든 추적 구성은 런타임에 Node.js 프로세스로 가져오는 스크립트에 있습니다. 여기서는 메신저 서비스에서 생성된 추적의 대상을 콘솔에서 외부 수집기(이 튜토리얼에서는 Jaeger)로 변경합니다.

  1. 챌린지 1과 동일한 터미널에서 메신저 리포의 디렉토리에 OTLP 내보내기 Node.js 패키지를 설치합니다.

    npm 설치 @opentelemetry/exporter-trace-otlp-http@0.36.0
    

    @opentelemetry/exporter-trace-otlp-http 라이브러리는 HTTP를 통해 OTLP 형식으로 추적 정보를 내보냅니다. OTel 외부 수집기에 원격 측정 데이터를 전송할 때 사용됩니다.

  2. tracing.mjs (챌린지 1에서 만들고 편집한 파일)를 열고 다음과 같이 변경합니다.

    • 파일 맨 위의 import 문 세트에 다음 줄을 추가합니다.

      "@opentelemetry/exporter-trace-otlp-http"에서 { OTLPTraceExporter }를 가져옵니다.
      
    • 1번 과제에서 사용된 콘솔 내보내기 도구에서 OTel SDK에 제공하는 "내보내기 도구"를 HTTP를 통해 OTLP 호환 수집기에 OTLP 데이터를 전송할 수 있는 도구로 변경합니다. 바꾸다:

      traceExporter:새로운 opentelemetry.tracing.ConsoleSpanExporter(),
      

      와 함께:

      traceExporter: 새로운 OTLPTraceExporter({ 헤더: {} }),
      

    메모: 단순화를 위해 이 튜토리얼에서는 수집기가 기본 위치인 http://localhost:4318/v1/traces 에 있다고 가정합니다. 실제 시스템에서는 위치를 명시적으로 설정하는 것이 좋습니다.

  3. 4단계 (콘솔로 전송된 OTel 자동 계측 구성) 에서 이 터미널에서 시작한 메신저 서비스를 중지하려면 Ctrl+c 를 누르세요. 그런 다음 2단계에서 구성한 새 내보내기 프로그램을 사용하도록 다시 시작합니다.

    ^cnode --import ./tracing.mjs index.mjs
    
  4. 두 번째 별도의 터미널 세션을 시작합니다. (이후 지침에서는 이를 클라이언트 터미널 이라고 하며 1단계와 3단계에서 사용된 원래 터미널을 메신저 터미널 이라고 합니다.) 약 10초 정도 기다린 후 메신저 서비스에 상태 점검 요청을 보냅니다(여러 추적을 보고 싶다면 이 작업을 여러 번 실행할 수 있습니다).

    curl -X GET http://localhost:4000/health
    

    요청을 보내기 전에 10초간 기다리면 추적을 더 쉽게 찾을 수 있습니다. 서비스가 시작되면서 자동 계측이 생성하는 많은 추적 뒤에 추적이 오기 때문입니다.

  5. 브라우저에서 http://localhost:16686 으로 Jaeger UI에 액세스하여 OTLP 내보내기 기능이 예상대로 작동하는지 확인합니다. 제목 표시줄에서 검색을 클릭하고 서비스 필드의 드롭다운 메뉴에서 이름이 unknown_service 로 시작하는 서비스를 선택합니다. 추적 찾기 버튼을 클릭하세요:

  6. 창의 오른쪽에 있는 추적을 클릭하면 해당 추적에 포함된 스팬 목록이 표시됩니다. 각 스팬은 추적의 일부로 실행되는 작업을 설명하며, 여기에는 여러 서비스가 포함되는 경우도 있습니다. 스크린샷의 jsonParser 범위는 메신저 서비스의 요청 처리 코드의 jsonParser 부분을 실행하는 데 걸린 시간을 보여줍니다.

    unknown_service에 대한 span 목록을 보여주는 Jaeger GUI의 스크린샷, 자동 계측이 올바른 서비스 이름을 표시하도록 변경되기 전

  7. 5단계에서 설명한 대로 OTel SDK에서 내보낸 서비스 이름( unknown_service )은 의미가 없습니다. 이 문제를 해결하려면 메신저 터미널에서 Ctrl+c를 눌러 메신저 서비스를 중지하세요. 그런 다음 몇 가지 Node.js 패키지를 더 설치합니다.

    ^c 
    npm 설치 @opentelemetry/semantic-conventions@1.10.0 \
    @opentelemetry/resources@1.10.0
    

    이 두 라이브러리는 다음과 같은 기능을 제공합니다.

    • @opentelemetry/semantic-conventions – OTel 사양에 정의된 대로 추적에 대한 표준 속성을 정의합니다.
    • @opentelemetry/resources – OTel 데이터를 생성하는 소스(이 튜토리얼에서는 메신저 서비스)를 나타내는 객체(리소스)를 정의합니다.
  8. 텍스트 편집기에서 tracing.mjs를 열고 다음과 같이 변경합니다.

    • 파일 맨 위의 import 문 세트에 다음 줄을 추가합니다.

      "@opentelemetry/resources"에서 {리소스}를 가져옵니다. "@opentelemetry/semantic-conventions"에서 {SemanticResourceAttributes}를 가져옵니다.
      
    • 마지막 import 문 뒤에 다음 줄을 추가하여 OTel 사양의 올바른 키 아래에 messenger 라는 리소스를 만듭니다.

      const 리소스 = 새 리소스({ [SemanticResourceAttributes.SERVICE_NAME]: "메신저",
      });
      
    • 검은색 줄 사이에 주황색으로 강조 표시된 줄을 추가하여 리소스 객체를 NodeSDK 생성자에 전달합니다.

      const SDK = 새로운 OpenTelemetry.NodeSDK({ 리소스, 추적 내보내기: 새로운 OTLPTraceExporter({ 헤더: {} }), 계측: [getNodeAutoInstrumentations()], });
      
  9. 메신저 서비스를 다시 시작합니다.

    노드 --import ./tracing.mjs index.mjs
    
  10. 약 10초간 기다린 다음, 4단계에서 연 클라이언트 터미널에서 서버로 다른 상태 검사 요청을 보냅니다(여러 추적을 보려면 명령을 여러 번 실행할 수 있습니다).

    curl -X GET http://localhost:4000/health
    

    메모: 다음 섹션에서는 클라이언트 터미널을 재사용 가능하게 열어두고, 챌린지 3에서는 메신저 터미널을 재사용 가능하게 열어둡니다.

  11. 브라우저의 Jaeger UI에 messenger 라는 새로운 서비스가 나타나는지 확인합니다(몇 초 정도 걸릴 수 있으며 Jaeger UI를 새로 고쳐야 할 수도 있음):

    psans의 심층적인 검사를 위해 사용 가능한 서비스 목록에서 메신저를 보여주는 Jaeger GUI의 스크린샷

  12. 서비스 드롭다운 메뉴에서 메신저를 선택하고 추적 찾기 버튼을 클릭하면 메신저 서비스에서 시작된 모든 최근 추적을 볼 수 있습니다(스크린샷에는 20개 중 가장 최근 2개가 표시됨).

    메신저 서비스에 대한 가장 최근의 추적 2개를 보여주는 Jaeger GUI의 스크린샷

  13. 추적을 클릭하면 해당 추적 범위가 표시됩니다. 각 스팬은 메신저 서비스에서 시작된 것으로 적절하게 태그되었습니다.

    단일 메신저 스팬의 세부 정보를 보여주는 Jaeger GUI의 스크린샷

알림 서비스의 OTel 자동 계측 구성

이제 알림 서비스에 대한 자동 계측을 실행하고 구성합니다. 기본적으로 메신저 서비스에 대한 이전 두 섹션과 동일한 명령을 실행합니다.

  1. 새로운 터미널 세션을 엽니다(이후 단계에서는 알림 터미널 이라고 합니다). 알림 저장소의 디렉토리로 변경하고 Node.js를 설치합니다(원하는 경우 다른 방법으로 대체할 수 있음):

    CD ~/microservices-march/notifier/appasdf 설치
    
  2. 종속성 설치:

    npm 설치
    
  3. 알림 서비스를 위한 PostgreSQL 데이터베이스를 시작합니다.

    docker compose up -d
    
  4. 데이터베이스 스키마와 테이블을 만들고 일부 시드 데이터를 삽입합니다.

    npm 실행 새로 고침-db
    
  5. OTel Node.js 패키지를 설치합니다(패키지의 기능에 대한 설명은 콘솔로 전송된 OTel 자동 계측 구성 의 1단계 및 3단계 참조):

    npm 설치 @opentelemetry/자동-인스트루먼트-노드@0.36.4 \
    @opentelemetry/내보내기-추적-otlp-http@0.36.0 \
    @opentelemetry/리소스@1.10.0 \
    @opentelemetry/sdk-노드@0.36.0 \
    @opentelemetry/의미-컨벤션@1.10.0
    
  6. tracing.mjs 라는 새 파일을 만듭니다.

    터치 추적.mjs
    
  7. 원하는 텍스트 편집기에서 tracing.mjs를 열고 다음 스크립트를 추가하여 OTel SDK를 실행하세요.

    "@opentelemetry/sdk-node"에서 opentelemetry 가져오기;
    "@opentelemetry/auto-instrumentations-node"에서 {getNodeAutoInstrumentations} 가져오기;
    "@opentelemetry/exporter-trace-otlp-http"에서 {OTLPTraceExporter} 가져오기;
    "@opentelemetry/resources"에서 {Resource} 가져오기;
    "@opentelemetry/semantic-conventions"에서 {SemanticResourceAttributes} 가져오기;
    
    const resource = new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: "알림",
    });
    
    const sdk = new opentelemetry.NodeSDK({
    resource,
    traceExporter: new OTLPTraceExporter({ headers: {} }),
    instrumentations: [getNodeAutoInstrumentations()],
    });
    
    sdk.start();
    

    메모: 이 스크립트는 메신저 서비스의 스크립트와 정확히 동일하지만, SemanticResourceAttributes.SERVICE_NAME 필드의 값이 notifier 입니다.

  8. OTel 자동 계측을 사용하여 알림 서비스를 시작합니다.

    노드 --import ./tracing.mjs index.mjs
    
  9. 약 10초 정도 기다린 후, 클라이언트 터미널에서 알림 서비스에 상태 점검 요청을 보냅니다. 이 서비스는 포트 4000에서 수신하는 메신저 서비스와의 충돌을 방지하기 위해 포트 5000에서 수신합니다.

    컬 http://localhost:5000/health
    

    메모: 챌린지 3에서는 클라이언트와 알림 단말기를 재사용할 수 있도록 열어 둡니다.

  10. 브라우저의 Jaeger UI에 notifier 라는 새 서비스가 나타나는지 확인하세요.

    스팬의 심층 검사를 위해 사용 가능한 서비스 목록에 알림 기능이 있는 Jaeger GUI의 스크린샷

NGINX의 OTel 계측 구성

NGINX의 경우 OTel 자동 계측 방법을 사용하는 대신 수동으로 추적을 설정합니다. 현재 OTel을 사용하여 NGINX를 계측하는 가장 일반적인 방법은 C로 작성된 모듈을 사용하는 것입니다. 타사 모듈은 NGINX 생태계의 중요한 부분이지만 설정하려면 약간의 작업이 필요합니다. 이 튜토리얼에서는 설정을 도와드립니다. 배경 정보는 블로그의 NGINX 및 NGINX Plus용 타사 동적 모듈 컴파일을 참조하세요.

  1. 새로운 터미널 세션( NGINX 터미널 )을 시작하고, 메신저 저장소의 루트로 디렉토리를 변경하고 load-balancer 라는 새 디렉토리를 만들고, Dockerfile , nginx.conf , opentelemetry_module.conf 라는 새 파일을 만듭니다.

    cd ~/microservices-march/messenger/mkdir 로드 밸런서
    cd 로드 밸런서
    touch Dockerfile
    touch nginx.conf
    touch opentelemetry_module.conf
    
  2. 원하는 텍스트 편집기에서 Dockerfile을 열고 다음을 추가합니다(주석에는 각 줄의 기능이 설명되어 있지만 모든 내용을 이해하지 않고도 Docker 컨테이너를 빌드하고 실행할 수 있습니다).

    FROM --platform=amd64 nginx:1.23.1 # nginx.conf 파일을 우리 파일로 교체합니다 COPY nginx.conf /etc/nginx/nginx.conf # NGINX OTel 모듈의 버전을 정의합니다 ARG OPENTELEMETRY_CPP_VERSION=1.0.3 # NGINX를 컴파일하고 실행할 때 사용되는 공유 라이브러리의 검색 경로를 정의합니다 ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/opentelemetry-webserver-sdk/sdk_lib/lib # 1. Consul 템플릿의 최신 버전과 OTel C++ 웹 서버 모듈인 otel-webserver-module을 다운로드하고 https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv를 추가하세요.${OPENTELEMETRY_CPP_VERSION} /opentelemetry-webserver-sdk-x64-linux.tgz /tmp RUN apt-get update \ && apt-get install -y --no-install-recommends dumb-init unzip \ # 2. 모듈 파일을 추출합니다 && tar xvfz /tmp/opentelemetry-webserver-sdk-x64-linux.tgz -C /opt \ && rm -rf /tmp/opentelemetry-webserver-sdk-x64-linux.tgz \ # 3. NGINX 구성 파일 맨 위에 'load_module' 지시문을 설치하고 추가합니다. && /opt/opentelemetry-webserver-sdk/install.sh \ && echo "load_module /opt/opentelemetry-webserver-sdk/WebServerModule/Nginx/1.23.1/ngx_http_opentelemetry_module.so;\n$(cat /etc/nginx/nginx.conf)" > /etc/nginx/nginx.conf # 4. NGINX OTel 모듈에 대한 구성 파일을 복사합니다. COPY opentelemetry_module.conf /etc/nginx/conf.d/opentelemetry_module.conf EXPOSE 8085 STOPSIGNAL SIGQUIT
    
  3. nginx.conf를 열고 다음을 추가합니다.

    이벤트 {}
    http {
    /etc/nginx/conf.d/opentelemetry_module.conf를 포함합니다.
    
    업스트림 메신저 {
    서버 로컬호스트:4000;
    }
    
    서버 {
    청취 8085;
    
    위치 / {
    proxy_pass http://messenger;
    }
    }
    }
    

    이 매우 기본적인 NGINX 설정 파일은 NGINX에 다음 사항을 알려줍니다.

    • 메신저 서비스 인스턴스 그룹을 나타내는 메신저 라는 업스트림 그룹을 설정합니다.
    • 포트 8085에서 HTTP 요청을 수신합니다.
    • / 로 시작하는 경로에 대한 모든 수신 요청(즉, 모든 수신 요청)을 업스트림 메신저 로 전달합니다.

    메모: 이는 프로덕션 환경에서 역방향 프록시 및 부하 분산 장치로서 NGINX의 실제 구성과 매우 유사합니다. 유일한 주요 차이점은 업스트림 블록의 서버 지시문에 대한 인수가 일반적으로 localhost가 아닌 도메인 이름이나 IP 주소라는 것입니다.

  4. opentelemetry_module.conf를 열고 다음을 추가합니다.

    NginxModuleEnabled 켜짐;NginxModuleOtelSpanExporter otlp;
    NginxModuleOtelExporterEndpoint 로컬호스트:4317;
    NginxModuleServiceName messenger-lb;
    NginxModuleServiceNamespace MicroservicesMarchDemoArchitecture;
    NginxModuleServiceInstanceId DemoInstanceId;
    NginxModuleResolveBackends 켜짐;
    NginxModuleTraceAsError 켜짐;
    
  5. NGINX와 NGINX OTel 모듈을 포함하는 Docker 이미지를 빌드합니다.

    docker build -t messenger-lb .
    
  6. NGINX 역방향 프록시 및 부하 분산 장치를 위한 Docker 컨테이너를 시작합니다.

    docker run --rm --name messenger-lb -p 8085:8085 --network="host" messenger-lb
    
  7. 클라이언트 터미널에서 NGINX 역방향 프록시 및 부하 분산 장치를 통해 메신저 서비스에 상태 점검 요청을 보냅니다(이 요청을 보내기 전에 기다릴 필요가 없습니다).

    컬 http://localhost:8085/health
    

    메모: 챌린지 3에서는 NGINX와 클라이언트 터미널을 재사용할 수 있도록 열어둡니다.

  8. 브라우저에서 새 messenger-lb 서비스가 이전에 시작한 서비스와 함께 Jaeger UI에 나열되어 있는지 확인합니다. 브라우저에서 Jaeger UI를 다시 로드해야 할 수도 있습니다.

    span의 심층 검사를 위해 사용 가능한 서비스 목록을 보여주는 Jaeger GUI의 스크린샷. 이제 messenger-lb도 포함됨

도전 3: OTel 추적을 읽는 법을 배우세요

아키텍처 및 사용자 흐름 에서 사용자 흐름의 단계를 설명했지만 요약하면 다음과 같습니다.

  1. 사용자는 다른 사용자에게 메시지를 보내 대화를 시작합니다.
  2. NGINX 역방향 프록시는 메시지를 가로채서 메신저 서비스로 전달합니다.
  3. 메신저 서비스는 메시지를 데이터베이스에 쓴 다음 RabbitMQ를 통해 이벤트를 전송합니다.
  4. 알림 서비스는 해당 이벤트를 사용하고 수신자(두 번째 사용자)의 알림 환경 설정을 조회한 후 선호하는 방법을 통해 수신자에게 알림을 보냅니다.

원격 측정을 구현하는 목표는 다음과 같습니다.

  1. 새로운 메시지 흐름을 달성하기 위해 요청이 거치는 모든 단계를 이해합니다.
  2. 정상적인 조건에서는 5초 이내에 흐름이 처음부터 끝까지 실행된다는 확신을 가지십시오.
  3. 메신저 서비스에서 전송한 이벤트를 알림 서비스가 처리하는 데 걸리는 시간을 확인합니다.

이 과제에서는 OTel 계측기를 통해 생성된 추적이 앞서 언급한 목표를 충족하는지 평가하는 방법을 알아봅니다. 먼저, 시스템을 시험해보고 몇 가지 추적을 생성합니다 . 그런 다음 NGINX , 메신저 서비스 , 알림 서비스 에서 생성된 메시지 흐름과 해당 섹션에 대한 추적을 검사합니다.

추적 데이터 생성

클라이언트 터미널에서 대화를 설정하고 두 사용자 간에 몇 가지 메시지를 보냅니다.

curl -X POST \
-H "콘텐츠 유형: application/json" \
-d '{"참가자_ID": [1, 2]}' \
'http://localhost:8085/대화'

curl -X POST \
-H "사용자 ID: 1" \
-H "콘텐츠 유형: application/json" \
-d '{"콘텐츠": "이것은 첫 번째 메시지입니다"}' \
'http://localhost:8085/conversations/1/messages'

curl -X POST \
-H "사용자 ID: 2" \
-H "콘텐츠 유형: application/json" \
-d '{"콘텐츠": "이게 두 번째 메시지입니다"}' \
'http://localhost:8085/conversations/1/messages'

다음과 같은 출력이 알림 서비스에서 생성되어 알림 터미널에 나타납니다.

수신된 new_message: {"type":"new_message","channel_id":1,"user_id":1,"index":1,"participant_ids":[1,2]}12027621401로 SMS를 통해 새 메시지 알림을 전송합니다.

수신된 new_message: {"type":"new_message","channel_id":1,"user_id":2,"index":2,"participant_ids":[1,2]}

the_hotstepper@kamo.ze로 이메일을 통해 새 메시지 알림을 전송합니다.

19147379938로 SMS를 통해 새 메시지 알림을 전송합니다.

흔적을 읽을 준비를 하세요

브라우저에서 Jaeger UI를 열고 서비스 드롭다운 메뉴에서 messenger-lb를 선택한 다음 추적 찾기 버튼을 클릭합니다. 흐름의 맨 처음에서부터 추적 목록이 나타납니다. 이 스크린샷과 같이 추적 내용을 클릭하면 해당 추적 내용에 대한 세부 정보가 표시됩니다.

흐름에서 전체 스팬 세트를 보여주는 Jaeger GUI의 스크린샷

여기저기 클릭해서 탐색해보세요. 다음으로 넘어가기 전에, 챌린지 3의 소개 에 나와 있는 계측 목표를 뒷받침하는 트레이스의 정보가 어떻게 도움이 되는지 고려해 보세요. 관련 질문은 다음과 같습니다.

  • 어떤 정보가 목표 달성에 도움이 되나요?
  • 어떤 정보가 누락되었나요?
  • 어떤 정보가 관련이 없나요?

추적의 NGINX(messenger-lb) 섹션을 조사합니다.

목표 1: 새 메시지 흐름에서 요청이 거치는 모든 단계 보기

부모 스팬 내에 11개의 자식 스팬이 있는 NGINX 스팬부터 시작해 보겠습니다. 현재 NGINX 구성이 매우 간단하기 때문에 자식 스팬은 그다지 흥미롭지 않고, 단순히 NGINX 요청 처리 라이프사이클의 각 단계에 걸리는 시간을 보여줍니다. 그러나 부모 스팬(맨 처음 스팬)에는 몇 가지 흥미로운 통찰력이 포함되어 있습니다.

추적의 NGINX(messenger-lb) 섹션에서 부모 span을 보여주는 Jaeger GUI의 스크린샷

  • 태그 아래에 다음과 같은 속성이 표시됩니다.

    • http.method 필드 – POST ( REST 용어로는 생성을 의미함)
    • http.status_code 필드 –201 (성공적인 생성을 나타냄)
    • http.target 필드 – conversations/1/messages (메시지 엔드포인트)

    이 세 가지 정보를 종합하면 다음과 같습니다. “ POST 요청이 /conversations/1/messages 로 전송되었고 응답은 다음과 같습니다.201 (성공적으로 생성되었습니다)”. 이는 아키텍처 및 사용자 흐름 의 1단계와 4a단계에 해당합니다.

  • 프로세스 에서 webengine.name 필드는 이것이 요청의 NGINX 부분임을 보여줍니다.

또한 messengernotifier 에 대한 스팬이 messenger-lb conversations/1 스팬 내에 중첩되어 있기 때문에( 추적을 읽을 준비를 하세요 의 스크린샷 참조) NGINX 역방향 프록시를 통해 messenger 서비스로 전송된 요청이 흐름에서 예상되는 모든 구성 요소에 도달했다는 것을 알 수 있습니다.

이 정보는 NGINX 역방향 프록시가 흐름의 일부임을 알 수 있기 때문에 목표를 충족합니다.

목표 2: 흐름이 5초 이내에 실행되는지 확인하세요

messenger-lb 라는 라벨이 붙은 스팬 목록에서 가장 최근의 스팬(목록 맨 아래에 있음)을 살펴보면 요청의 NGINX 부분이 얼마나 걸렸는지 확인할 수 있습니다. 스크린샷에서 스팬은 589마이크로초(µs)에서 시작해 24µs 동안 지속되었는데, 이는 전체 역방향 프록시 작업에 걸린 시간이 613µs, 즉 약 0.6밀리초(ms)에 불과하다는 것을 의미합니다. (물론 직접 튜토리얼을 실행하면 정확한 값이 달라질 것입니다.)

추적의 NGINX(messenger-lb) 섹션에서 span을 보여주는 Jaeger GUI의 스크린샷

이런 설정에서는 대부분의 값이 다른 측정값에 대해서만 유용하며 시스템마다 다릅니다. 하지만 이 경우에는 작업 시간이 5초를 넘을 위험이 전혀 없습니다.

이 정보는 NGINX 작업이 5초도 걸리지 않았다는 것을 알 수 있기 때문에 목표를 충족합니다. 흐름에 매우 느린 작업이 있는 경우, 그 작업은 나중에 수행되고 있을 것입니다.

목표 3: 메신저 서비스에서 전송된 이벤트를 알림 서비스가 읽는 데 걸리는 시간을 확인하세요.

NGINX 역방향 프록시 계층에는 이에 대한 정보가 전혀 없으므로 메신저 스팬으로 넘어가도 됩니다.

Trace의 메신저 섹션을 조사하세요

목표 1: 새 메시지 흐름에서 요청이 거치는 모든 단계 보기

추적의 메신저 서비스 섹션에는 또 다른 11개의 스팬이 포함되어 있습니다. 다시 말해, 대부분의 자식 스팬은 Express 프레임워크가 요청을 처리할 때 사용하는 기본 단계와 관련이 있으며, 그다지 흥미롭지 않습니다. 그러나 부모 스팬(맨 처음 스팬)에는 다시 몇 가지 흥미로운 통찰력이 포함되어 있습니다.

추적의 메신저 섹션에서 부모 span을 보여주는 Jaeger GUI의 스크린샷

태그 아래에 다음과 같은 속성이 표시됩니다.

  • http.method 필드 – POST (다시 말하지만 REST 용어로 이는 생성을 의미합니다)
  • http.route 필드 – /conversations/:conversationId/messages (메시지 경로)
  • http.target 필드 – /conversations/1/messages (메시지 엔드포인트)

이 정보는 메신저 서비스가 흐름의 일부였고 적중된 엔드포인트가 새 메시지 엔드포인트였음을 보여주기 때문에 목표를 충족합니다.

목표 2: 흐름이 5초 이내에 실행되는지 확인하세요

아래 스크린샷에서 알 수 있듯이 추적의 메신저 부분은 1.28ms에서 시작해 36.28ms에서 끝났으며, 전체 시간은 35ms였습니다. 그 시간의 대부분은 JSON 구문 분석( 미들웨어 - jsonParser )에 소요되었으며, 훨씬 더 많은 시간이 데이터베이스 연결( pg-pool.connecttcp.connect )에 소요되었습니다.

메시지를 작성하는 과정에서 여러 가지 SQL 쿼리가 실행된다는 점을 고려하면 이는 당연한 일입니다. 이는 다시 말해 해당 쿼리의 타이밍을 포착하기 위해 자동 계측 구성을 증강해야 할 수도 있음을 의미합니다. (튜토리얼에서는 이러한 추가 계측을 보여주지 않지만 챌린지 4에서는 데이터베이스 쿼리를 래핑하는 데 사용할 수 있는 스팬을 수동으로 생성합니다.)

추적의 메신저 섹션에서 스팬과 스팬이 걸리는 시간을 보여주는 Jaeger GUI의 스크린샷

이 정보는 메신저 작업에 5초도 걸리지 않는다는 것을 보여주기 때문에 목표를 충족합니다. 흐름에 매우 느린 작업이 있는 경우, 그 작업은 나중에 수행되고 있을 것입니다.

목표 3: 메신저 서비스에서 전송된 이벤트를 알림 서비스가 읽는 데 걸리는 시간을 확인하세요.

NGINX 스팬과 마찬가지로 메신저 스팬에는 이 정보가 포함되지 않으므로 알림 스팬으로 이동해야 합니다.

추적의 알림 섹션을 조사하세요

목표 1: 새 메시지 흐름에서 요청이 거치는 모든 단계 보기

추적의 알림 섹션에는 두 개의 span만 포함됩니다.

추적의 알림 섹션에서 두 개의 스팬을 보여주는 Jaeger GUI의 스크린샷

  • chat_queue 프로세스 span – 알림 서비스가 chat_queue 메시지 큐에서 이벤트를 처리했는지 확인합니다.
  • pg-pool.connect span – 이벤트를 처리한 후 알림 서비스가 데이터베이스에 어떤 종류의 연결을 했는지 보여줍니다.

이러한 범위에서 얻은 정보는 각 단계를 이해한다는 목표를 부분적으로만 충족합니다. 알림 서비스가 대기열에서 이벤트를 사용하는 지점에 도달한 것을 볼 수 있지만 다음 사항은 알 수 없습니다.

  • 본 서비스에서 전송하는 메시지 알림은 메신저 서비스에서 전송하는 이벤트에 대응합니다.
  • 해당 메시지 알림이 메시지 수신자에게 올바르게 전송되었습니다.

이는 알림 서비스 흐름을 완전히 이해하려면 다음을 수행해야 함을 나타냅니다.

  • 알림이 전송되는 것을 표시하는 스팬을 수동으로 계측합니다.
  • 메신저 서비스에서 전송된 이벤트와 알림 서비스에서 사용되는 이벤트 사이에 추적 ID 형태로 명시적인 연결이 있는지 확인합니다.

목표 2: 흐름이 5초 이내에 실행되는지 확인하세요

알림 서비스 스팬의 전체 타이밍을 살펴보면 요청이 흐름의 알림 섹션에서 30.77ms를 소비한 것을 알 수 있습니다. 하지만 전체 흐름(수신자에게 알림 전송)의 "끝"을 알리는 스팬이 없기 때문에 흐름의 이 섹션에 대한 전체 타이밍이나 작업의 전체 완료 시간을 결정할 수 없습니다.

목표 3: 메신저 서비스에서 전송된 이벤트를 알림 서비스가 읽는 데 걸리는 시간을 확인하세요.

그러나 메신저 서비스의 chat_queue 전송 기간이 4.12ms에 시작된 후, 알림 서비스의 chat_queue 프로세스 기간이 6.12ms에 시작된 것을 볼 수 있습니다.

메신저 서비스에서 전송된 이벤트를 알림 서비스가 사용하는 모습을 보여주는 Jaeger GUI의 스크린샷

이 목표는 메신저 서비스에서 이벤트를 전송한 후 2ms가 지나서 알림자가 이벤트를 사용했다는 것을 알기 때문에 달성됩니다. 목표 2와 달리 이 목표를 달성하기 위해 이벤트가 완전히 처리되었는지, 처리에 얼마나 걸렸는지 알 필요가 없습니다.

결론

현재 OTel 자동 계측에서 생성된 추적에 대한 분석을 기반으로 다음 사항이 명확합니다.

  • 다음 스팬 중 다수는 현재 형태로는 유용하지 않습니다.

    • NGINX는 역방향 프록싱이라는 역할과 관련이 없는 권한 확인 및 파일 제공과 같은 기능과 관련된 스팬을 생성합니다. 하지만 현재 NGINX용 OTel 계측 도구에서는 관련 없는 span을 생략할 수 없으므로 아무것도 할 수 없습니다.
    • Node.js 서비스( 메신저알림 서비스)의 스팬 중 일부는 해당 목표와 관련이 있는 것으로 보입니다. 즉, JSON 구문 분석, 요청 핸들러 및 모든 데이터베이스 작업에 대한 스팬입니다. 일부 미들웨어 span(예: expressInitcorsMiddleware )은 관련성이 없는 것으로 보이므로 제거해도 됩니다.
  • 다음의 중요 기간이 누락되었습니다.

    • 알림 서비스에서 보낸 알림
    • 메신저 서비스에서 전송된 RabbitMQ 이벤트와 알림 서비스에서 처리된 이벤트 간의 명확한 매핑

이는 기본 계측이 마지막 목표를 충족한다는 것을 의미합니다.

  • 메신저 서비스에서 전송된 이벤트를 알림 서비스가 처리하는 데 걸리는 시간을 확인하세요.

하지만 처음 두 가지 목표를 달성할 만큼의 정보가 충분하지 않습니다.

  • 새 메시지 흐름 동안 요청이 거치는 모든 단계를 이해합니다.
  • 일반적인 상황에서는 5초 이내에 흐름이 종단간 으로 실행된다는 확신을 가지십시오.

도전 4: 추적 판독을 기반으로 계측 최적화

이번 챌린지에서는 챌린지 3에서 수행한 추적 분석을 기반으로 OTel 계측기를 최적화합니다. 여기에는 불필요한 스팬을 제거하고 , 새로운 사용자 정의 스팬을 만들고 , 알림 서비스에서 사용되는 이벤트가 메신저 서비스에서 생성된 이벤트인지 확인하는 작업 이 모두 포함됩니다.

불필요한 스팬 제거

  1. 원하는 텍스트 편집기에서 메신저 저장소의 디렉토리에 있는 tracing.mjs 파일을 열고 맨 위에 있는 가져오기 명령문 목록 끝에 다음을 추가합니다.

    const IGNORED_EXPRESS_SPANS = new Set([ "미들웨어 - expressInit",
    "미들웨어 - corsMiddleware",
    ]);
    

    이는 Jaeger UI의 다음 스크린샷에 표시된 스팬 목록에서 파생된 스팬 이름 집합을 정의하며, 이 흐름에 대한 유용한 정보를 제공하지 않기 때문에 추적에서 제외됩니다. 스크린샷에 나열된 다른 스팬도 필요하지 않다고 판단하고 IGNORED_EXPRESS_SPANS 목록에 추가할 수도 있습니다.

    메신저 서비스의 여러 span 목록을 보여주는 Jaeger GUI의 스크린샷. 이는 관련 정보를 제공할 수 있으므로 추적에서 생략할 수 있습니다.

  2. 주황색으로 강조 표시된 줄을 변경하여 원하지 않는 스팬을 제외하기 위해 자동 계측 구성에 필터를 추가합니다.

    const SDK = 새로운 OpenTelemetry.NodeSDK({ 리소스, 추적 내보내기: 새로운 OTLPTraceExporter({ 헤더: {} }), 계측: [getNodeAutoInstrumentations()], });
    

    이것에:

    const SDK = 새 OpenTelemetry.NodeSDK({ 리소스, 추적 내보내기: 새 OTLPTraceExporter({ 헤더: {} }), 계측: [ getNodeAutoInstrumentations({ "@opentelemetry/instrumentation-express": { 무시 레이어: [ (이름) => { IGNORED_EXPRESS_SPANS.has(이름)을 반환합니다. }, ], }, }), ], });
    

    getNodeAutoInstrumentations 함수는 @opentelemetry/instrumentation-express 에서 생성된 추적에서 필터링하기 위해 1단계에서 정의된 스팬 세트를 참조합니다. 즉, return 문은 IGNORED_EXPRESS_SPANS 에 속하는 span에 대해 true 로 확인되고, ignoreLayers 문은 추적에서 해당 span을 제거합니다.

  3. 메신저 터미널에서 Ctrl+c를 눌러 메신저 서비스를 중지하세요. 그런 다음 다시 시작하세요.

    ^cnode --import ./tracing.mjs index.mjs
    
  4. 약 10초 정도 기다린 후 클라이언트 터미널에서 새 메시지를 보내세요.

    curl -X POST \ -H "사용자 ID: 2" \
    -H "콘텐츠 유형: application/json" \
    -d '{"콘텐츠": "이게 두 번째 메시지입니다"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Jaeger UI에서 messenger span을 다시 확인합니다. 두 개의 미들웨어 span인 expressInitcorsMiddleware 가 더 이상 나타나지 않습니다(챌린지 3의 추적에서 messenger 섹션을 조사하는 목표 2 의 스크린샷과 비교할 수 있습니다).

    계측기를 변경하여 추적에서 필터링한 후 추적에 더 이상 두 개의 스팬이 포함되지 않는다는 것을 보여주는 Jaeger GUI의 스크린샷

사용자 정의 스팬 설정

이 섹션에서는 처음으로 애플리케이션 코드를 만져봅니다. 자동 계측을 통해 애플리케이션을 변경하지 않고도 상당한 양의 정보를 생성할 수 있지만, 일부 통찰력은 특정 비즈니스 로직을 계측해야만 가능합니다.

새로운 메시지 흐름을 계측하는 경우, 메시지 수신자에게 알림이 전송되는 과정을 추적하는 것이 한 가지 예입니다.

  1. 알림 저장소의 디렉토리에서 index.mjs를 엽니다. 이 파일에는 서비스의 모든 비즈니스 로직이 포함되어 있습니다. 파일 맨 위의 import 문 목록 끝에 다음 줄을 추가합니다.

    "@opentelemetry/api"에서 {추적}을 가져옵니다.
    
  2. 이 코드를 바꾸세요(파일의 약 91번째 줄):

    (선호사항의 pref를 입력하세요) { console.log( `새 메시지 알림 보내기${pref.address_type} 에게${pref.address} ` ); }
    

    와 함께:

    const tracer = trace.getTracer("notifier"); // 1tracer.startActiveSpan( // 2 "notification.send_all", { 속성: { user_id: msg.user_id, }, }, (parentSpan) => { 선호도의 선호도에 대한 { tracer.startActiveSpan( // 3 "notification.send", { 속성: { // 4 notification_type: 선호도.주소_유형, user_id: 선호도.사용자_id, }, }, (span) => { console.log( `새 메시지 알림 보내기${pref.address_type} 에게${pref.address} ` ); span.end(); // 5 } ); } parentSpan.end(); // 6 } );
    

    새로운 코드는 다음을 수행합니다.

    1. OTel 추적과 상호작용하기 위한 글로벌 객체인 추적기를 가져옵니다.
    2. notification.send_all 이라는 새로운 부모 스팬을 시작하고 user_id 속성을 설정하여 메시지 발신자를 식별합니다.
    3. 수신자의 알림 기본 설정이 열거되고 notification.send_all 아래에 notification.send 라는 새 자식 span이 생성되는 루프가 시작됩니다. 모든 알림은 새로운 기간을 생성합니다.
    4. 자식 span에 대해 더 많은 속성을 설정합니다.

      • notification_typesms 또는 email 중 하나
      • user_id – 알림을 받는 사용자의 ID
    5. 각 자식 notification.send span을 차례로 닫습니다.
    6. 부모 notification.send_all span을 닫습니다.

    부모 스팬이 있으면 사용자의 알림 기본 설정이 발견되지 않더라도 각 "알림 보내기" 작업이 보고됨이 보장됩니다.

  3. 알림 터미널에서 Ctrl+c를 눌러 알림 서비스를 중지합니다. 그런 다음 다시 시작하세요.

    ^cnode --import ./tracing.mjs index.mjs
    
  4. 약 10초 정도 기다린 후 클라이언트 터미널에서 새 메시지를 보내세요.

    curl -X POST \ -H "사용자 ID: 2" \
    -H "콘텐츠 유형: application/json" \
    -d '{"콘텐츠": "이게 두 번째 메시지입니다"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. Jaeger UI에서 알림 스팬을 다시 확인합니다. 부모 스팬과 두 개의 자식 스팬이 표시되며 각각 "알림 보내기" 작업이 있습니다.

    알림 서비스에 대한 코드에서 세 개의 새로운 span을 정의한 결과를 보여주는 Jaeger GUI의 스크린샷

이제 새 메시지 흐름 중에 요청이 거치는 모든 단계를 볼 수 있으므로 첫 번째와 두 번째 목표를 완전히 달성할 수 있습니다. 각 스팬의 타이밍은 각 단계 사이에 발생하는 지연을 드러냅니다.

메신저와 알림자가 동일한 이벤트를 처리하는지 확인

흐름에 대한 전체적인 통찰력을 얻으려면 필요한 것이 하나 더 있습니다. 알림 서비스에서 처리하는 이벤트가 실제로 메신저 서비스에서 전송한 이벤트입니까?

두 추적을 연결하기 위해 명시적으로 변경할 필요는 없습니다. 하지만 자동 계측의 마법을 맹목적으로 믿어서도 안 됩니다.

이를 염두에 두고, NGINX 서비스에서 시작되는 추적이 알림 서비스에서 사용하는 추적과 실제로 동일한지(동일한 추적 ID를 가짐) 확인하기 위해 간단한 디버그 코드를 추가합니다.

  1. 메신저 저장소의 디렉토리에서 index.mjs 파일을 열고 다음과 같이 변경합니다.

    • 맨 위의 import 문 목록 끝에 다음 줄을 추가합니다.

      "@opentelemetry/api"에서 {추적}을 가져옵니다.
      
    • 기존 검은색 줄 아래에 주황색으로 강조된 줄을 추가합니다.

      비동기 함수 createMessageInConversation(req, res) { const tracer = trace.getActiveSpan(); console.log("TRACE_ID: ", tracer.spanContext().traceId);
      

      새로운 줄은 messenger 에서 새 메시지를 생성하는 함수 내부의 TRACE_ID를 출력합니다.

  2. 알림 저장소의 디렉토리에서 index.mjs 파일을 열고 검은색으로 표시된 기존 줄 아래에 주황색으로 강조 표시된 줄을 추가합니다.

    비동기 함수 handleMessageConsume(채널, 메시지, 핸들러)을 내보냅니다. console.log("RABBIT_MQ_MESSAGE: ", msg);
    

    새로운 줄은 알림 서비스에서 수신한 AMQP 이벤트의 전체 내용을 인쇄합니다.

  3. 메신저알림 서비스를 중지했다가 다시 시작하려면 메신저 및 알림 터미널에서 다음 명령을 실행하세요.

    ^cnode --import ./tracing.mjs index.mjs
    
  4. 약 10초 정도 기다린 후, 클라이언트 터미널에서 다시 메시지를 보내세요:

    curl -X POST \ -H "사용자 ID: 2" \
    -H "콘텐츠 유형: application/json" \
    -d '{"콘텐츠": "이게 두 번째 메시지입니다"}' \
    'http://localhost:8085/conversations/1/messages'
    
  5. 메신저알림 서비스의 로그를 살펴보세요. 메신저 서비스 로그에는 메시지의 추적 ID를 보고하는 다음과 같은 줄이 포함되어 있습니다(튜토리얼을 실행하면 실제 ID가 달라집니다).

    추적 ID:  29377a9b546c50be629c8e64409bbfb5
    
  6. 마찬가지로 알림 서비스 로그는 다음과 같이 출력에서 추적 ID를 보고합니다.

    _spanContext: {
    추적 ID: '29377a9b546c50be629c8e64409bbfb5',
      범위 ID: 'a94e9462a39e6dbf',
      추적 플래그: 1,
    traceState: 정의되지 않음
    },
    
  7. 추적 ID는 콘솔에서 일치하지만 마지막 단계로 Jaeger UI의 추적 ID와 비교할 수 있습니다. 관련 추적 ID 엔드포인트에서 UI를 엽니다(사용자의 엔드포인트는 다를 수 있지만 이 예에서는 http://localhost:16686/trace/29377a9b546c50be629c8e64409bbfb5 입니다). 전체 추적을 확인합니다. Jaeger 추적 결과는 다음을 확인합니다.

    • 메신저 서비스의 AMQP 자동 계측 기능은 이벤트가 전송될 때 이 추적 ID를 메타데이터의 일부로 추가합니다.
    • 알림 서비스의 AMQP 자동 계측은 해당 메타데이터를 예상하고 추적 컨텍스트를 적절히 설정합니다.

메모: 실제 프로덕션 시스템에서는 흐름이 예상대로 작동하는지 확인한 후 이 섹션에 추가한 코드를 제거합니다.

자원 정리

이 튜토리얼을 진행하는 동안 여러 개의 컨테이너와 이미지를 만들었습니다! 다음 지침에 따라 제거하세요.

  • 실행 중인 Docker 컨테이너를 제거하려면:

    도커 rm $(도커 정지 메신저-lb)
    
  • 플랫폼 서비스와 메신저알림 데이터베이스 서비스를 제거하려면:

    cd ~/microservices-march/platform && docker compose down
    cd ~/microservices-march/notifier && docker compose down
    cd ~/microservices-march/messenger && docker compose down
    

다음 단계

축하합니다! 튜토리얼을 완료했습니다!

  • NGINX 역방향 프록시와 두 개의 Node.js 서비스를 통해 OTel 계측을 설정합니다.
  • OTel 자동 계측에서 제공된 데이터를 비판적인 시각으로 살펴보고 OTel 랩 목표를 달성하기 위해 누락된 일부 원격 측정을 추가했습니다.
    • 애플리케이션 코드를 직접 변경하지 않고도 메시징 시스템을 통한 특정 요청의 흐름을 합리적으로 파악할 수 있었습니다.
    • 정상적인 상황에서는 5초 이내에 흐름이 처음부터 끝까지 실행되는 것을 확인했습니다.

하지만 아직 이상적인 추적 구성이 어떤 것인지에 대한 표면만 살짝 긁은 것뿐입니다! 프로덕션 환경에서는 각 데이터베이스 쿼리에 대한 사용자 정의 스팬과 각 서비스의 컨테이너 ID와 같은 런타임 세부 정보를 설명하는 모든 스팬에 대한 추가 메타데이터와 같은 것을 추가하고 싶을 수 있습니다. 또한 다른 두 가지 유형의 OTel 데이터(메트릭 및 로깅)를 구현하여 시스템 상태에 대한 전체 그림을 제공할 수도 있습니다.

마이크로서비스 교육을 계속하려면 2023년 3월 마이크로서비스를 확인하세요. 4 단원에서는: 관찰성을 통해 마이크로서비스의 혼란과 복잡성을 관리합니다 . 관찰성 데이터의 세 가지 주요 유형, 인프라와 앱 정렬의 중요성, 심층 데이터 분석을 시작하는 방법에 대해 알아봅니다.


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