🩻

Morisawa Fonts に AWS X-Ray を導入しました

2023/05/18に公開

こんにちは、株式会社モリサワ システム開発部門の川村です。

クラウド型フォントライセンスサービスの「Morisawa Fonts」の開発を担当しています。

Morisawa Fonts に AWS X-Ray を導入したので、簡単に紹介したいと思います。

X-Ray とは?

AWS が提供する分散アプリケーションの分析とデバッグのためのサービスです。

https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/aws-xray.html

アプリケーションが処理するリクエストの詳細なデータを収集し、アプリケーション全体のパフォーマンスや、アプリケーション内の各ノードの依存関係、レイテンシー、エラー率などを可視化することができます。

そのため、パフォーマンスのボトルネックやエッジケースのエラーなど、アプリケーションが抱える問題について、根本原因を特定することが可能になります。

例えば、DynamoDB や SQS といった AWS サービスに加えて、外部 HTTP API、RDB への SQL 呼び出しなども詳細なデータが収集可能です。

Morisawa Fonts では、

  • マイクロサービスアーキテクチャで構築しており、パフォーマンスのボトルネックやエッジケースのエラーの根本原因の特定が難しい
  • AWS をベースにインフラを構築しているため、他の類似サービスよりも導入コストを抑えられそう

以上から X-Ray の導入に踏み切りました。

X-Ray の構成

X-Ray は以下の4つの要素で構成されています。

X-Ray SDK

トレースデータを生成し、X-Ray デーモンに送信するためのライブラリです。

Go、Java、Node.js、Python、.NET、Ruby などに対応しています。

トレースしたい処理を X-Ray SDK が提供するクラスやメソッドでラップし、インストルメント化することで、透過的にトレースデータを生成します。

X-Ray API

トレースデータの送信、取得などを行うための API です。

X-Ray SDK はトレースデータを直接 X-Ray API には送信しておらず、後述の X-Ray デーモンに対して送信しています。

そのため X-Ray SDK を使用する際は、X-Ray デーモンの起動も必要になります。

X-Ray デーモン

X-Ray SDK と X-Ray API を中継するエージェントです。

UDP ポート2000をリッスンし、X-Ray SDK から受信したデータを一定時間バッファしたのち、X-Ray API に送信します。

Elastic Beanstalk、EC2、ECS、ローカルで実行可能です。
それぞれ実行方法が異なるため、実行環境に応じた対応が必要になります。

X-Ray コンソール

収集したトレースデータを可視化し、アプリケーションの分析を行うためのツールです。

アプリケーション全体のパフォーマンスを表示する機能や、個々のトレースデータの詳細を表示する機能などがあります。

実装

前項を踏まえると X-Ray を導入するためのポイントは以下の2つです。

  • アプリケーションに X-Ray SDK を組み込むこと
  • X-Ray デーモンを起動すること

それぞれ対応していきます。

X-Ray SDK の組み込み

Backend に X-Ray SDK を組み込みます。

Morisawa Fonts の Backend は ECS Fargate で運用し、Go で実装しています。
そのため、X-Ray SDK for Go を組み込んでいきます。

いくつかポイントとなる部分を抜粋してお伝えします。

HTTPハンドラー

xray.Handler でラップし、HTTP リクエストをインストルメント化します。

sn := xray.NewFixedSegmentNamer(segmentName)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // 処理
})
instrumentedHandler := xray.Handler(sn, handler)

セグメント名には任意の値が設定可ですが、Morisawa Fonts では環境単位(ステージング、プロダクションなど)で付けるようにしました。
そうすることで、トレースデータを環境単位でフィルタリングすることが容易になります。

AWS セッション

xray.AWSSession でラップし、AWS セッションをインストルメント化します。

instrumentedSession := xray.AWSSession(session.Must(session.NewSession()))

RDB

xray.SQLContext を使用し、SQL 呼び出しをインストルメント化します。

instrumentedDB, err := xray.SQLContext(driver, dsn)

外部 HTTP API

xray.Client を使用し、外部 HTTP API 呼び出しをインストルメント化します。

instrumentedHttpClient := xray.Client(httpClient)

ユーザー ID

xray.Segment.User にユーザー ID を記録します。

segment := xray.GetSegment(ctx)
segment.User = userID

トレースデータをユーザー ID でフィルタリングすることが可能になります。

注釈

xray.AddAnnotation で追加情報を記録します。

err := xray.AddAnnotation(ctx, key, value)

トレースデータを当該情報でフィルタリングすることが可能になります。
ただし、格納できるデータ型やデータ長に制限 [1] があります。

メタデータ

xray.AddMetadataToNamespace で追加情報を記録します。

err := xray.AddMetadataToNamespace(ctx, namespace, key, value)

メタデータは注釈と異なり、当該情報でフィルタリングすることはできません。
その代わり、構造体やマップなどの複雑なデータを格納することが可能です。

エラー情報

エラーが発生した場合、xray.AddError でエラー情報を記録します。

err := xray.AddError(ctx, err)

エラーメッセージやスタックトレースなどが記録され、トラブルシューティングに役立ちます。

X-Ray デーモンの起動

インフラに X-Ray デーモンの設定を追加します。

先述の通り、Morisawa Fonts の Backend は ECS Fargate で運用しています。
そのため、1つのタスク定義の中に以下の2つのコンテナを含め、サイドカーパターンとなるように改修します。

  • Backend API サーバーコンテナ
  • X-Ray デーモンコンテナ
    • UDP ポート2000のトラフィックをリッスンし、Backend API サーバーコンテナから送信されたトレースデータを X-Ray API に中継します

また、タスクロールに X-Ray への書き込み権限を追加します。

改修後のタスク定義、タスクロールの概略は以下のようになります。

タスク定義

{
    "taskDefinitionArn": XXXXXXXX,
    "containerDefinitions": [
        {
            "name": "ApiContainer",
            "image": XXXXXXXX,
            "cpu": XXXXXXXX,
            "memory": XXXXXXXX
        },
        {
            "name": "XRayDaemonContainer",
            "image": "amazon/aws-xray-daemon",
            "cpu": 32,
            "memoryReservation": 256,
            "portMappings": [
                {
                    "containerPort": 2000,
                    "hostPort": 2000,
                    "protocol": "udp"
                }
            ]
        }
    ],
    "compatibilities": [
        "EC2",
        "FARGATE"
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": XXXXXXXX,
    "memory": XXXXXXXX
}

タスクロール

{
    "AttachedPolicies": [
        {
            "PolicyName": "AWSXRayDaemonWriteAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
        }
    ]
}

注意点として、サイドカーパターンの場合、コンテナの CPU の合計はタスクの CPU より大きくすることはできません。
そのため、

Backend API サーバーコンテナの CPU + X-Ray デーモンコンテナの CPU <= タスクの CPU

となるように設定する必要があります。

トレースデータ確認

X-Ray コンソールからトレースデータを確認します。

サービスマップ

アプリケーションによって生成されたトレースデータを視覚的に表したものです。

リクエストの処理中に使用したサービスの流れや各種メトリクスが可視化されており、アプリケーション全体のパフォーマンスの確認に役立ちます。

ノードを一覧表示することも可能です。

トレース詳細

個々のリクエストの開始から終了までに収集されたデータを可視化したものです。

当該リクエストの処理中に使用したサービスの流れや各種メトリクスを可視化したトレースマップと、個々の処理を時系列で可視化したタイムラインがあります。

タイムラインでは個々の処理で収集された詳細なデータを確認することが可能で、ボトルネックやエラーの根本原因の特定に役立ちます。

おわりに

Morisawa Fonts に X-Ray を導入した件について紹介しました。

X-Ray の導入により、アプリケーション全体のパフォーマンスや個々のリクエストの詳細なデータを可視化できるようになりました。

そして X-Ray を導入したことで、メール送信箇所にボトルネックが存在することが判明しました。
迅速にチューニングを行い、X-Ray が Backend のパフォーマンス改善に早速役立ちました。

注意点として、X-Ray はサンプリングのため、デフォルトでは全リクエストを収集するわけではありません。必要に応じてサンプリングルールをカスタマイズしてください。

引き続き X-Ray を活用し、アプリケーションの分析と改善に取り組み、サービスの品質向上に努めて参ります。

脚注
  1. 注釈に関する制限事項は以下のドキュメントを参照ください。
    https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-sdk-go-segment.html ↩︎

モリサワ Tech Blog

Discussion