Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

マイクロサービスの監視:名前空間(Namespace)でデータを構造化する方法

マイクロサービスアーキテクチャとは?

マイクロサービスアーキテクチャとは、複数の小さなプログラムを組み合わせてアプリケーションを構築するソフトウェア設計パターンです。これらの小さなプログラムは「マイクロサービス」と呼ばれ、共通の目標に向かって互いに連携しながら動作します。チームによっては、1つの巨大なアプリケーションを開発するよりも、複数の小さなアプリケーションを書くほうが、時間も労力も大幅に抑えられます。

マイクロサービス指向のプロジェクトは、小さなアプリケーションの集合体で構成されます。それぞれが独自の役割を担い、独立したプロセスとして実行され、標準化されたインターフェースを通じて他のサービスと通信します。このアプローチにより、チームは特定の言語やフレームワークに縛られることなく、各課題に最適なツールを選択できます。さらに、専門性の高いグループ間で作業を分担することも容易になります。

モノリスからマイクロサービスへ

本シリーズの第1部ではモノリスについて取り上げました。モノリスは開発を始めやすいという大きな利点があります。しかし、開発スピードはあまりスケールしません。その主な原因は、すべてのコードが密結合していることにあります。わずかな変更でもプロジェクト全体の再ビルドとテストが必要となり、結果としてリリースサイクルが長引き、フラストレーションが溜まってしまいます。

では、どのようにしてモノリスからマイクロサービスへ移行するのでしょうか? ここでAmazonの事例を見てみましょう。Amazonは当初モノリスとしてスタートし、時間をかけてマイクロサービスへと移行しました。初期の設計はおそらく次のようなものだったと考えられます。

もちろん、ここではかなり単純化していますが、基本的な構造はほぼカバーできているはずです。もし最初からマイクロサービスパターンを採用していたらどうなっていたでしょうか? アプリケーションは機能ごとに分割され、各コンポーネントが1つの課題に集中する構成になっていたはずです。

また、サービス間通信用のインターフェースとプロトコルを定義する必要もあります。一般的にはRESTful APIのような軽量なメカニズムが採用されます。

名前空間(ネームスペース)とは

マイクロサービス設計には固有の課題があります。中でも最大の課題は、計測(インストゥルメンテーション)とエラーレポートです。考えてみてください。私たちは、異なるプラットフォームや言語にまたがる数十〜数百のコンポーネントを監視しなければなりません。しかも、それらすべてを把握しながら、同時に全体像を見失わないようにする必要があります。名前空間を活用すれば、疎結合なマイクロサービス群をひとまとめにし、一貫性のある全体像として捉えることができます。

AppSignalにおいて、名前空間は収集されたメトリクスのコンテナとして機能します。デフォルトでは3つの名前空間(webbackgroundfrontend)が用意されていますが、数行のコードを追加するだけで独自の名前空間を作成できます。次にその仕組みを見ていきましょう。

すべてを統べる一つのアプリケーション

マイクロサービスコンポーネントをセットアップする際、最初に行うべきは共通のアプリケーション名と環境の設定です。これにより、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のインストールと設定に関する詳細は、以下のドキュメントをご参照ください。

  • 新しいアプリケーションの追加
  • アプリケーションの設定

マイクロサービスでの名前空間の活用

それでは、各マイクロサービスを実際にコーディングする流れを見ていきましょう。まずは請求システムから始めます。この部分ではElixirとPhoenixを使用します。

Phoenix統合のセットアップが完了したら、コントローラーの実装に取り掛かれます。次のスニペットは、名前空間を billing に設定する例です。

# Phoenixコントローラーでは、plugを使って名前空間の初期化を行います
defmodule BillingPageController.PageController do
  use BillingPageController, :controller
 
  plug :set_appsignal_namespace
 
  defp set_appsignal_namespace(conn, _params) do
    # このコントローラーのすべてのアクションを「billing」名前空間で報告
    Appsignal.Transaction.set_namespace(:billing)
    conn
  end
 
  # コントローラーの残りの部分 ...
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
 
  # コントローラーの残りの部分 ...
 
end

これでダッシュボードに2つの名前空間ができました。同じアプリケーション名と環境を使用することで、PayButtonController のデータは、別々のマシン上で動作する独立したアプリケーションであっても、BillingPageController のデータと一緒に表示されることが保証されます。

次のコンポーネントはレコメンデーションエンジンです。商品のおすすめを表示するAPIエンドポイントをExpressで実装してみましょう。Node.jsで名前空間を設定する方法は次のとおりです。

app.get("/", (req, res) => {
  const tracer = appsignal.tracer();
  tracer.withSpan(
    tracer.createSpan({ namespace: "recommendations" }),
    (span) => {
      // 計測対象のコードをここに記述
 
      span.close();
    }
  );
});

これで名前空間は3つになりました。

モバイルチームやフロントエンドチームも、ダッシュボードにエラーを記録したいはずです。AppSignalのJavaScript統合は、受信データに自動的に frontend 名前空間を割り当てますが、次のように変更することも可能です。

try {
  // 失敗する可能性のあるコード
} catch (error) {
  // エラーを処理
 
  // AppSignalにエラーを送信
  appsignal.sendError(error, {}, "Mobile");
}

しばらくすると、Mobile 名前空間にデータが表示され始めるはずです。

この例ではプレーンなJavaScriptを示しましたが、ReactやAngularなどのフロントエンドフレームワークを使用している場合は、追加のセットアップ手順が必要になることがあります。

ウェブサイト側では、Ruby on Railsを試してみましょう。Railsは非常に有名なMVCフレームワークで、AppSignalはすぐに統合できます。Railsのコントローラーを次のスニペットで開始し、名前空間を homepage に設定します。

# Railsではbefore_actionコールバックを使って
# リクエスト開始前に名前空間を変更します
class HomepageController < ApplicationController
    before_action :set_appsignal_namespace
 
    def set_appsignal_namespace
        Appsignal.set_namespace("homepage")
    end
 
    # コントローラーアクション ...
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 '商品検索を返す'
 
      before do
        Appsignal.set_namespace("search")
      end
 
      get :product do
 
        # 商品検索ロジック
 
      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はジョブからのデータを自動的に background 名前空間に割り当てます。用途に応じて、より具体的な名前空間に変更してもよいでしょう。

require 'sidekiq'
require 'appsignal'
 
 
class PlainOldRuby
  include Sidekiq::Worker
 
  def perform()
    Appsignal.set_namespace("urgent_background")
 
    # ジョブロジック
 
  end
end

スタンドアローンエージェントによるメトリクス収集

スタンドアローンエージェントは、Ubuntu、RedHat、CentOSのいずれかのマシンからリソース使用率のメトリクスを収集します。このエージェントを利用すれば、マイクロサービスアプリケーションにデータベース、ゲートウェイ、メッセージブローカーなどの機能を提供する衛星サーバーを監視できます。

実際、AppSignalでは自社のKafkaサーバーの監視にこのエージェントを使用しています。また、直接サポートされていない言語やフレームワーク向けにカスタムインストゥルメンテーションを構築する際にも非常に便利です。

エージェントの導入手順は、インストールドキュメントに従ってダウンロードおよびインストールを行ってください。

起動後は、設定ファイルを編集してAPIキー、アプリケーション名、環境を設定する必要があります。他のマイクロサービスで使用しているのと同じ値を指定すれば、すべてのデータが1つのダッシュボードに集約されます。

# /etc/appsignal-agent.conf

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

まとめ

名前空間は、複数のシステムに分散したデータを整理するための強力な手段です。通知の設定やアラート処理のきめ細かな調整にも活用できます。その仕組みについて詳しくは、本シリーズの第1部をご確認ください。

関連記事:

  • 名前空間でできること
  • StatsDとAppSignalのスタンドアローンエージェントであらゆるシステムを監視する
  • ホストメトリクスとアラートの改善
  1. PythonのBokehを使ったデータ可視化の基本ガイド

    Bokehは、Webブラウザ向けに設計されたPythonのデータ可視化ライブラリです。洗練された美しいグラフィックスを簡潔なコードで構築できるのが大きな特徴で、インタラクティブなプロットやダッシュボード、データアプリケーションを素早く簡単に作成できます。本記事では、Bokehを使ってさまざまな種類の基本的なグラフを作成する方法を解説します。 線グラフの描画 線グラフは、各点のx座標とy座標をそれぞれリストとして渡すことで作成できます。図の高さと幅を指定すれば、出力を直接ブラウザに表示することも可能です。さらに、線の太さや色といった追加のパラメータを指定することで、見た目を細かく調整できます。

  2. 【Ruby入門】開発者が押さえておきたい主要データ構造の特徴と使い方

    データ構造とは? データ構造とは、データを整理し、効率よくアクセスするための具体的な方法のことです。 代表的な例としては以下のようなものがあります。 配列(Array) 二分木(Binary Tree) ハッシュ(Hash) データ構造ごとに得意な処理は異なります。たとえば、ハッシュは辞書(単語と意味)や電話帳(名前と電話番号)のように「キーと値」のペアを扱うデータの保存に最適です。 どのようなデータ構造が存在するのか、そしてそれぞれの特性を理解することは、Ruby開発者としてのレベルアップに直結します。 本記事では、その知識をわかりやすく解説していきます! 配列(Array)を理解する