Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

마이크로서비스 모니터링: 데이터 구조화를 위한 네임스페이스 활용법

마이크로서비스 아키텍처란?

마이크로서비스 아키텍처는 여러 개의 작은 프로그램을 결합하여 애플리케이션을 구축하는 소프트웨어 설계 패턴입니다. 이렇게 만들어진 작은 프로그램들을 '마이크로서비스'라고 부르며, 공동의 목표를 위해 협력합니다. 일부 팀에게는 하나의 거대한 애플리케이션을 개발하는 것보다 여러 개의 소규모 애플리케이션을 작성하는 편이 훨씬 적은 시간과 노력이 듭니다.

마이크로서비스 지향 프로젝트는 각자 맡은 역할을 수행하고, 별도의 프로세스에서 실행되며, 표준화된 인터페이스를 통해 서로 통신하는 작은 애플리케이션들의 집합으로 구성됩니다. 이러한 접근 방식 덕분에 팀은 특정 언어나 프레임워크에 얽매이지 않고 각 문제에 가장 적합한 도구를 자유롭게 선택할 수 있습니다. 또한 더 전문화된 그룹 간에 업무를 분담하는 것도 가능합니다.

모놀리스에서 마이크로서비스로

이 시리즈의 첫 번째 파트에서는 모놀리스(Monolith)에 대해 다루었습니다. 모놀리스는 시작하기 쉽다는 장점이 있습니다. 하지만 모든 코드가 지나치게 긴밀하게 결합되어 있어 개발 속도가 규모에 비례해 늘어나지 않습니다. 사소한 코드 변경 하나에도 프로젝트 전체를 다시 빌드하고 테스트해야 하므로, 답답할 정도로 긴 릴리스 주기로 이어지곤 합니다.

그렇다면 어떻게 모놀리스에서 마이크로서비스로 전환할 수 있을까요? 아마존의 사례를 살펴보겠습니다. 아마존은 한때 모놀리스로 출발했다가 시간이 지나면서 마이크로서비스로 전환했습니다. 초기 설계는 대략 다음과 같았을 것입니다.

물론 여기서는 상당히 단순화했지만, 핵심적인 부분은 대부분 담고 있다고 봅니다. 만약 처음부터 마이크로서비스 패턴을 따랐다면 어땠을까요? 기능별로 애플리케이션을 분할하여 각 컴포넌트가 하나의 문제에만 집중하도록 했을 것입니다.

또한 서비스 간 통신을 위한 인터페이스와 프로토콜을 정의해야 했을 텐데, 일반적으로 RESTful API처럼 가벼운 메커니즘이 활용됩니다.

네임스페이스란?

마이크로서비스 설계에는 고유한 과제가 따릅니다. 가장 큰 난제는 아마도 계측(Instrumentation)과 오류 보고일 것입니다. 생각해 보면, 우리는 서로 다른 플랫폼과 언어에 흩어져 있는 수십~수백 개의 컴포넌트를 모니터링해야 합니다. 이 모든 요소를 놓치지 않으면서도 전체적인 큰 그림 역시 유지해야 하죠. 네임스페이스는 느슨하게 결합된 마이크로서비스 그룹을 하나의 일관된 그림으로 정리하는 데 큰 도움이 됩니다.

AppSignal에서 네임스페이스는 수집된 메트릭을 담는 컨테이너입니다. AppSignal은 기본적으로 세 가지 네임스페이스(web, background, frontend)를 제공하지만, 몇 줄의 코드만 추가하면 직접 만들 수도 있습니다. 지금부터 실제 동작 방식을 살펴보겠습니다.

모든 것을 관장하는 하나의 애플리케이션

마이크로서비스 컴포넌트를 설정할 때 가장 먼저 해야 할 일은 공통 애플리케이션 이름과 환경을 구성하는 것입니다. 이렇게 하면 AppSignal이 수집된 모든 메트릭과 알림을 동일한 대시보드에 표시해 줍니다.

구체적인 설정 방법은 언어와 통합 방식에 따라 다릅니다. 예를 들어, Ruby on Rails 애플리케이션을 "Nozama"라는 이름으로 구성하려면 다음과 같이 작성합니다.

# config/appsignal.yml
production:
  active: true
  push_api_key: "YOUR APPSIGNAL API KEY"
  name: "Nozama"

Elixir 통합을 구성하는 방식도 매우 유사합니다.

# config/config.exs
config :appsignal, :config,
  active: true,
  name: "Nozama",
  push_api_key: "YOUR APPSIGNAL API KEY",
  env: "production"

반면 Node.js에서는 다음과 같이 설정합니다.

const { Appsignal } = require("@appsignal/nodejs");
 
const appsignal = new Appsignal({
  active: true,
  name: "Nozama",
  apiKey: "YOUR APPSIGNAL API KEY",
});

프론트엔드 JavaScript 통합에는 대신 @appsignal/javascript를 사용합니다.

import Appsignal from "@appsignal/javascript";
 
export default new Appsignal({
  name: "Nozama",
  key: "YOUR FRONTEND API KEY",
});

AppSignal의 설치 및 구성 관련 정보는 아래 문서에서 확인할 수 있습니다.

  • 새 애플리케이션 추가하기
  • 애플리케이션 구성하기

마이크로서비스에서 네임스페이스 사용하기

이제 각 마이크로서비스를 코딩하는 방법을 살펴보겠습니다. 먼저 청구(Billing) 시스템부터 시작하겠습니다. 이 부분에는 Elixir와 Phoenix를 사용합니다.

Phoenix 통합 설정을 마친 후 컨트롤러 작업을 시작할 수 있습니다. 다음 스니펫은 네임스페이스를 billing으로 설정합니다.

# in a Phoenix controller, we use plug to run the namespace initialization
defmodule BillingPageController.PageController do
  use BillingPageController, :controller
 
  plug :set_appsignal_namespace
 
  defp set_appsignal_namespace(conn, _params) do
    # Sets all actions in this controller to report in the "billing" namespace
    Appsignal.Transaction.set_namespace(:billing)
    conn
  end
 
  # rest of the controller ...
end

마이크로서비스가 실행되고 컨트롤러에 활동이 감지되면 곧 대시보드에 데이터가 나타나기 시작합니다.

물론 사용자가 무언가를 구매하지 않으면 청구 시스템만으로는 의미가 없습니다. 이 문제는 별도의 마이크로서비스로 해결할 수 있습니다. 동일한 패턴에 따라 PayButtonController 컨트롤러를 갖춘 완전히 새로운 Phoenix 애플리케이션을 다음과 같이 작성합니다.

defmodule PayButtonController.PageController do
  use PayButtonController, :controller
 
  plug :set_appsignal_namespace
 
  defp set_appsignal_namespace(conn, _params) do
    Appsignal.Span.set_namespace(Appsignal.Tracer.root_span(), "pay_button")
    conn
  end
 
  # rest of the controller ...
 
end

이제 대시보드에 두 개의 네임스페이스가 생겼습니다. 동일한 이름과 환경을 사용하면 서로 다른 머신에서 실행되는 별개의 애플리케이션이라도 PayButtonController의 데이터가 BillingPageController와 함께 표시됩니다.

다음 컴포넌트는 추천 엔진입니다. Express로 상품 추천을 보여주는 API 엔드포인트를 구현해 보겠습니다. Node.js에서는 다음과 같이 네임스페이스를 설정합니다.

app.get("/", (req, res) => {
  const tracer = appsignal.tracer();
  tracer.withSpan(
    tracer.createSpan({ namespace: "recommendations" }),
    (span) => {
      // code to measure goes here
 
      span.close();
    }
  );
});

이제 네임스페이스가 세 개가 되었습니다.

모바일 및 프론트엔드 팀도 대시보드에 오류를 기록하고 싶을 수 있습니다. AppSignal의 JavaScript 통합은 수신되는 데이터에 자동으로 frontend 네임스페이스를 할당하지만, 다음과 같이 변경할 수도 있습니다.

try {
  // code that might fail
} catch (error) {
  // handle the error
 
  // send error to AppSignal
  appsignal.sendError(error, {}, "Mobile");
}

잠시 후 Mobile 네임스페이스에 데이터가 쌓이기 시작할 것입니다.

위 예제는 순수 JavaScript를 기준으로 하지만, React나 Angular 같은 프론트엔드 프레임워크를 사용한다면 추가 설정 단계가 필요할 수 있습니다.

웹사이트의 경우, AppSignal이 별도 설정 없이 바로 통합되는 유명 MVC 프레임워크인 Ruby on Rails를 사용해 보겠습니다. Rails 컨트롤러에 다음 스니펫을 추가하여 네임스페이스를 homepage로 설정합니다.

# in Rails we use before_action callback to change
# the namespace before the request starts
class HomepageController < ApplicationController
    before_action :set_appsignal_namespace
 
    def set_appsignal_namespace
        Appsignal.set_namespace("homepage")
    end
 
    # controller actions ...
end

다음으로, 웹사이트와 모바일 애플리케이션에 데이터를 제공하는 API 엔드포인트를 만들 수 있습니다. 이를 위해 Ruby용 경량 REST API 프레임워크인 Grape를 활용해 보겠습니다. 이번에는 AppSignal 구성에 조금 더 작업이 필요합니다.

앞서 살펴본 것처럼 ´config/appsignal.yml´에서 Ruby 통합을 구성한 뒤, 다음과 같이 이벤트와 메트릭 로깅을 시작할 수 있습니다.

Appsignal.start_logger
Appsignal.start

그런 다음 오류 핸들러 체인에 AppSignal 미들웨어를 삽입합니다.

require "appsignal"
require "appsignal/integrations/grape"
 
class API < Grape::API
    insert_before Grape::Middleware::Error, Appsignal::Grape::Middleware
 
 
    resource :search do
      desc 'return a product search'
 
      before do
        Appsignal.set_namespace("search")
      end
 
      get :product do
 
        # product search logic
 
      end
  end
end

더 많은 예제는 Grape 통합 문서를 참고하세요.

마지막으로 Sidekiq 백그라운드 작업으로 전체 그림을 완성하겠습니다. Sidekiq는 Ruby 진영에서 인기 있는 잡 프로세서로, ´config/appsignal.yml´을 구성한 후 다음과 같이 독립 실행 모드로 시작할 수 있습니다.

# config.ru
require 'appsignal'
 
Sidekiq.on(:startup) do
  Appsignal.start
end
 
Sidekiq.on(:shutdown) do
  Appsignal.stop('Sidekiq shutdown')
end

AppSignal은 잡(Job)의 데이터를 자동으로 background 네임스페이스에 할당합니다. 필요하다면 더 구체적인 네임스페이스로 변경할 수도 있습니다.

require 'sidekiq'
require 'appsignal'
 
 
class PlainOldRuby
  include Sidekiq::Worker
 
  def perform()
    Appsignal.set_namespace("urgent_background")
 
    # job logic
 
  end
end

독립 실행형 에이전트로 메트릭 수집하기

독립 실행형 에이전트(Standalone Agent)는 Ubuntu, RedHat, CentOS 머신에서 리소스 사용률 메트릭을 수집합니다. 이 에이전트를 활용하면 마이크로서비스 애플리케이션에 데이터베이스, 게이트웨이, 메시지 브로커 같은 기능을 제공하는 위성 서버까지 모니터링할 수 있습니다.

실제로 AppSignal에서도 자체 Kafka 서버 모니터링에 이 에이전트를 사용합니다. 또한 직접 지원되지 않는 언어나 프레임워크에 대한 커스텀 계측을 구축할 때도 유용합니다.

에이전트를 시작하려면 설치 안내에 따라 다운로드 후 설치하면 됩니다.

실행된 후에는 구성 파일을 편집하여 API 키, 애플리케이션 이름, 환경을 설정해야 합니다. 모든 데이터를 하나의 대시보드에 모으려면 나머지 마이크로서비스에서 사용한 것과 동일한 값을 입력하세요.

# /etc/appsignal-agent.conf

push_api_key = "YOUR APPSIGNAL API KEY"
app_name = "Nozama"
environment = "production"

결론

네임스페이스는 여러 시스템에 분산된 데이터를 체계적으로 조직화할 수 있는 훌륭한 수단입니다. 알림(Notification) 구성과 알림 처리 세부 조정에도 활용할 수 있습니다. 관련 내용이 궁금하다면 이 시리즈의 첫 번째 파트를 확인해 보세요.

추천 자료:

  • 네임스페이스로 할 수 있는 것들
  • StatsD와 AppSignal 독립 실행형 에이전트로 모든 시스템 모니터링하기
  • 개선된 호스트 메트릭 및 알림