AWS LambdaにSplunk APMを組み込みSplunk Observability Cloud上でモニタリングできるようにしてみました

AWS LambdaにSplunk APMを組み込みSplunk Observability Cloud上でモニタリングできるようにしてみました

Clock Icon2025.05.21

初めに

最近少し縁がありSplunk Observabilityの機能であるRUM/APMのデモ環境を立ち上げる機会がありました。

https://github.com/splunk/observability-content-contrib/tree/main/integration-examples/observability-microservices-jumpstart#opentelemetry-quick-start-demo

画面の見え方を確認するのであれば上記を実行することでよしなにminikube環境が立ち上がりAPM/RUMが実際どのようにSplunk Observability Cloud上で見えるか確認できるのですが、良くも悪くも全てセットアップされてしまうので実際に組み込み部分が見えないものとなっております。

個人的にはなんか見える!程だとイマイチ頭に染み込んだ気がしないので今回は実際にLambda関数に組み込んで理解を深めてみます。

Splunk APMとは

Splunk APMは名前の通りアプリケーションモニタリング(APM)を提供するSplunk Observabilityの機能の一つとなっております。

AWSに慣れている方であればAWS X-Rayが同立ち位置のサービスというとわかりやすいかもしれません。

従来の監視ではインフラベースで遅延等をモニタリングし問題を検出することが多く行われてきましたが、APMはその上に乗っているアプリケーションに焦点を置いたモニタリングとなります。

https://www.splunk.com/ja_jp/blog/devops/apm-application-performance-monitoring.html

APMを使うことで実際にアプリの特定のメソッドがどの程度時間がかかっているか、別コンポーネントを叩いた際にアプリ目線でどの程度時間がかかっているか、どのコンポーネントでエラーが出ているかなどを取得しモニタリングすることが可能です。

また近年ではサービスがマイクロ化され機能ごとにコンポーネントが分割されることも増えてきましたが、APMを提供する多くのサービスでは「サービスマップ」(ものによって名称は異なるかも)という形で各コンポーネント間の依存等をグラフのように可視化し、より全体を見た上で個別の問題にブレイクダウンしていくことも可能となっております。

ちなみにSplunk公式から提供されているサンプル(最初に貼ったGithubのリンク先のもの)を構築するとこんな感じのサービスマップが確認できます。
(minikube上に作成されたkubernetesベースの分散アプリケーション)

splunk-sample-service-map

この場合frontendcheckoutservice間の遅延が大きく、paymentserviceで特にエラーが発生している様子が見えます。
今回はそこまで追いませんがエンドポイント単位で見たり個別のコンポーネントにブレイクダウンして情報も見ることもできます。

提供されているレイヤー

AWS Lambda組み込み用のレイヤーとしては以下のページに記載がある通り、現時点では3種類提供されています。

基本的には上の方がセットアップは楽な代わりにLambda関数の負荷やオーバーヘッドが増え、下の方がLambda関数自体の負荷やオーバーヘッドは減るもののセットアップの手間が増えるという形のようです。

  • オールインワン
    • ドキュメント曰く推奨はこれ
  • 各言語用のレイヤー + Collectorのレイヤー
  • 言語用のレイヤー + EC2 Collector
    • Lambda関数上には言語用のレイヤーのみ。EC2 Collecotrは別途EC2に構築

https://docs.splunk.com/observability/ja/gdi/get-data-in/serverless/aws/splunk-otel-lambda-layer.html

セットアップ的には1点目と2点目はレイヤーの種類が分かれるかどうかほどの違いでLambda関数上で完結するためあまり大きな違いはなさそうですが、3点目のみ外部にCollectorを構築しないといけないためかなり敷居が上がりそうです。

今回はお試しで「オールインワン」を利用してセットアップします。

トークンの取得

Lambda関数側のセットアップは後述しますが、Lambda関数からSplunk側にデータを送信するために認証トークンが必要になるので発行します。

画面の「Settings > Access Token」に遷移し「Create Token」を開きます。

o11y-setting-menu

o11y-access-token-menu

デフォルトで既にトークンが生成されているためそちらを利用しても良いのですが共通で利用するとトークンの失効範囲も多く聞くなるので特定の単位で個別にトークンを発行するのが好ましいはずです。

デフォルトですでに組織トークンが発行されていますが、共通のトークンを利用すると何かあってトークンを失効させたい場合の影響が大きくなるため個別に発行します。

権限の選択がありますが、トークンの権限は以下に記載がある通りadmin, power, usage, read_onlyの4種があります。

https://docs.splunk.com/observability/ja/admin/user-management/roles/roles-and-capabilities-about.html#roles-and-capabilities

どの権限を当てるかドキュメントに書いてなかったのですが実際に挙動ベースで確認する限りpower,usageの2つを当てる必要がありました。
画像上はpowerのみですが後で動作確認したら不足してたので別途追加しました(権限は作成後でも変更可能)。usageだけにしても401 Errorが出たので両方必要そうです。

o11y-token-scope

トークン自体を取得可能なユーザの権限を設定します。

o11y-access-token-permission

最後にトークンの有効期限を選択し「Create」します。

o11y-token-expire

少しして画面を更新するとトークンが発行され、赤枠部分をクリックするとクリップボードにトークンがコピーされますのでこちらを控えておきます。

o11y-get-access-token

Lambda関数への適用とデプロイ

ガイド付きセットアップもあるようですが、手動でLambdaレイヤーの追加といくつかの環境変数を埋め込む形でも対応できそうなので今回はAWS SAMでデプロイします。

https://docs.splunk.com/observability/ja/gdi/get-data-in/serverless/aws/otel-lambda-layer/instrument-lambda-functions.html

ベースはAWS SAMのHello Worldテンプレート(nodejs 22.x)を利用しそれを拡張します。

SAMテンプレートは以下のとおりです。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  ## 直接Strigで渡しているがSecrets Manager経由の方が良い
  SplunkToken:
    Type: String
    NoEcho: True
  SplunkRelm:
    Type: String
    Default: jp0

Globals:
  Function:
+   Timeout: 60
    Layers:
     ## REF: https://docs.splunk.com/observability/ja/gdi/get-data-in/serverless/aws/otel-lambda-layer/instrument-lambda-functions.html
     - !Sub arn:aws:lambda:${AWS::Region}:254067382080:layer:splunk-apm:114
    Environment:
+     Variables:
+       SPLUNK_REALM: !Ref SplunkRelm
+       SPLUNK_ACCESS_TOKEN: !Ref SplunkToken
+       AWS_LAMBDA_EXEC_WRAPPER: /opt/nodejs-otel-handler
+       OTEL_SERVICE_NAME: sam-app-splunk

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs22.x
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
    Metadata: # Manage esbuild properties
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
+       Target: "commonJS"
        Sourcemap: true
        EntryPoints: 
        - app.ts

タイムアウトに関してはデフォルトの3秒だとSplunk側のレイヤーの処理に間に合わずタイムアウトが発生していたので少し長めにしておくと良さそうです(5~10秒程度?)。

特に重いのは1回目なので初期化処理が重そうです。

デプロイは特に特別な処理は必要なくsam build && sam deployで問題ありません。アクセストークンは少し雑ですがCLI経由で渡してます。

sam build &&  sam deploy --parameter-overrides SplunkToken=xxxxx

Cannot redefine propertyではまった

順番が前後しますが上記のまま実行するとCannot redefine property: lambdaHandlerが発生します。

こちらについてドキュメントに記載がなくどうしたものか...と調べてみたのですがGithubの方にそれらしきissueが上がっておりどうもESモジュール側の仕様とのかみ合いのようです。

https://github.com/open-telemetry/opentelemetry-lambda/issues/1781

あまりこのあたりの仕様まで踏み込んで調整したことがないのもありこちらが最適な解決かわからないのですがハンドラー関数周りの調整と、tsconfigを以下のように調整します。

tsconfig.json
{
    "compilerOptions": {
      "target": "es2020",
      "strict": true,
      "preserveConstEnums": true,
      "noEmit": true,
      "sourceMap": false,
-     "module":"es2015",
+     "module":"commonjs",
      "moduleResolution":"node",
      "esModuleInterop": true, 
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true,  
    },
    "exclude": ["node_modules", "**/*.test.ts"]
  }
app.ts
- expport const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
+ const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {

    try {
        return {
            statusCode: 200,
            body: JSON.stringify({
                message: 'hello world',
            }),
        };
    } catch (err) {
        console.log(err);
        return {
            statusCode: 500,
            body: JSON.stringify({
                message: 'some error happened',
            }),
        };
    }
};

+ export = { lambdaHandler };

確認

curlで今回生成されたAPI Gatewayを叩いてみたところ無事表示されました。

サービス名はLambda関数の環境変数で設定したOTEL_SERVICE_NAMEの値です。
(app.LambdaHandelerは色々試してた時の残骸です)

splunk-lambda-apm

サービスマップもありますが単一コンポーネントな上にエラー等も出てないので流石にこれだけだと面白みはないですね...。

lambda-single-component-map

ログも見れるかな?と思ってconsole.debug()を埋め込んでみましたが、どうやらそちらはSplunk Cloud or Enterpriseのライセンスが必要そうで今回はみられませんでした。

o11y-splunk-cloud-log

終わりに

今回AWS Lambdaの関数にSplunk APM用のLambdaレイヤーを組み込んでSplunk Observability Cloud上で見れるようにセットアップしてみました。
nodejs個別の問題でハマる部分はありましたが、セットアップだけでいえば結構シンプル、むしろ以前New Relicで組み込んだ時は専用のCLIを入れる必要があった分こちらの方がお手軽ではあります(あくまでセットアップ手順だけ、周辺の機能等は別として)。

https://dev.classmethod.jp/articles/monitoring-aws-lambda-on-sam-used-new-relic/

今回はAPMを組み込んでみましたが、SplunkにはRUMもあるのでそちらも合わせて組み込んで見てみたいと思います。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.