AWS SAM CLIでローカル実行時のみNew Relicの拡張機能が適用されないようにしてみました
初めに
しばらく前の話ですがSAMテンプレート上の全ての関数に一律でNew RelicのLambdaレイヤーを適用するテンプレートを書いてみました。
手元のいくつかのツールはこれを利用しているのですが、久々にメンテナンスを行ったらいつの間にかこのレイヤー起因で名前解決周辺でエラーが発生するようになっており、ローカルでは動かないがリモートでは動くという状態になっていました(雑にやってたので実は元からだった可能性も)。
[NR_EXT] New Relic Lambda Extension starting up
[NR_EXT] Initializing version 2.3.11 of the New Relic Lambda Extension...
[NR_EXT] Failed to start logs HTTP serverlisten tcp: lookup sandbox.localdomain on 192.168.5.3:53: read udp 172.17.0.2:38149->192.168.5.3:53: i/o timeout
panic: Failed to start logs HTTP serverlisten tcp: lookup sandbox.localdomain on 192.168.5.3:53: read udp 172.17.0.2:38149->192.168.5.3:53: i/o timeout
(根本的には別原因で解決しないとという問題ではありますが、)これまで個人で使ってる環境なのであまり有無効のコントロールは気にしていなかったのの冷静に考えるとローカルで開発用に動かしている部分のログがリモート側の環境に乗ってしまうのも少し違うんではないか?というところもあったのでよしなにデプロイ〜実際の環境での実行時のみ拡張機能を利用できるようにし、ローカルでは無効化するようにしておきました。
想定ケース1:Fn::Ifによるレイヤーレベルでの無効化
当初はSAM側のGlobalsセクション側でLambdaレイヤー自体をまるっと乗らないようにできないかと考えておりました。これをやることでローカルの実行時にLambda Layerの取得自体がなくなりローカルの起動オーバーヘッドをなくせるメリットがあります。
Globals:
Function:
Handler: !If [NewRelicEnable, "newrelic_lambda_wrapper.handler", "app.handler"
Environment:
Fn:If:
- NewRelicEnable
- Variables:
NEW_RELIC_LAMBDA_HANDLER: app.lambda_handler
NEW_RELIC_ACCOUNT_ID: !Ref NewRelicAccountId
NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS: true
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED: true
- !Ref AWS::NoValue
- !Ref AWS::NoValue
Layers:
- !If [NewRelicEnable, !Ref NewRelicLayerArn, !Ref AWS::NoValue]
一方で複数箇所にFn::Ifを書き込む+Condtionsセクション自体の生成も必要でローカルのために必要以上にテンプレートが肥大化するため視覚的・メンテナンス的にはあまり好ましくはなさそうです。
1つのFn::If
で分岐できれば良いのですがGlobalsセクションのリソース種別指定(例としてFunction)直下ではyamlにおけるキーの部分には指定プロパティ以外のものを指定できず実現ができないものとなります。
Error: [InvalidGlobalsSectionException('Globals', "'Fn::If' is not a supported property of 'Function'. Must be one of the following values -...
例えばこれEnvironmentの階層を挟んでいるのでOKですが
Globals:
Function:
Handler: newrelic_lambda_wrapper.handler
Environment:
Fn:If:
- NewRelicEnable
- Variables:
NEW_RELIC_LAMBDA_HANDLER: app.lambda_handler
NEW_RELIC_ACCOUNT_ID: !Ref NewRelicAccountId
NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS: true
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED: true
- !Ref AWS::NoValue
Layers:
- !Ref NewRelicLayerArn
この場合はFunction直下になるのでNGです。
Globals:
Function:
Handler: "newrelic_lambda_wrapper.handler"
Fn:If:
- NewRelicEnable
- Environment:
Variables:
NEW_RELIC_LAMBDA_HANDLER: app.lambda_handler
NEW_RELIC_ACCOUNT_ID: !Ref NewRelicAccountId
NEW_RELIC_EXTENSION_SEND_FUNCTION_LOGS: true
NEW_RELIC_DISTRIBUTED_TRACING_ENABLED: true
- !Ref AWS::NoValue
Layers:
- !Ref NewRelicLayerArn
必要であればこういった条件分岐を使うこともやぶさかではないのですが、次に示す方法の方がシンプルとなりそうなのでそちらを採用することにしました。
想定ケース2:環境変数による無効化
そもそもNew Relicの機能として何かないかと探していたところNEW_RELIC_LAMBDA_EXTENSION_ENABLED
という環境変数が用意されておりこちらを使うことで拡張機能全体のON/OFFを切り替えられることができるようです。
こちらの場合は環境変数を一つ追加しParametersからの値の引き渡し一つで実現できる関係でテンプレートとしてもシンプルさを保つことができます。
Globals:
Handler: newrelic_lambda_wrapper.handler
Environment:
Variables:
#...
NEW_RELIC_LAMBDA_EXTENSION_ENABLED: !Ref NewRelicEnable
一方で完全にNew Relicによる拡張機能が無効化されるわけではなくレイヤーの取得〜初期化処理までは走る形とはなります。
% sam local invoke
Invoking newrelic_lambda_wrapper.handler (python3.12)
arn:aws:lambda:ap-northeast-1:451483290750:layer:NewRelicPython312ARM64:6 is already cached. Skipping download
Using local image: samcli/lambda-python:3.12-arm64-d25adf066a44b291c42ac3367.
Mounting /Users/xxxxx/.aws-sam/build/XXXXXFunction as /var/task:ro,delegated, inside runtime container
START RequestId: 417db7a0-b864-4833-b39f-fca342b22439 Version: $LATEST
[NR_EXT] New Relic Lambda Extension starting up
[NR_EXT] Initializing version 2.3.11 of the New Relic Lambda Extension...
[NR_EXT] Extension telemetry processing disabled
[NR_EXT] Starting no-op mode, no telemetry will be sent
とは言いつつごく微量である点、また今回の大元の名前解決問題は解消したため管理性をメインにおきこちらを採用しております。
デプロイ・ローカル実行で自動的に値を切り替える
さていずれの方法にしても実行・デプロイ時によしなに指定させる必要があります。
デプロイの仕組みが綺麗に整っていれば良いのですがそうではなく逐一手動でコマンドを動かす場合、環境によってはコピペミスなどちょっとしたオペレーションミスで意図しない方のフラグを読み込ませてしまう可能性があります。
こういったパラメーターに関してはsamconfig.toml(yaml)
に記載を行い各コマンド実行時に自動で指定した値がパラメーターとして渡るようにしておきましょう。
[default.deploy.parameters]
parameter_overrides="NewRelicEnable=True"
[default.local.parameters]
parameter_overrides="NewRelicEnable=False"
今回の環境は実質本番・ローカルしかないためdefault
の指定となっていますが、よりいろんな環境で細々調整したいのであればこ環境ごとに記載して使い分けるようにするのが良いでしょう。
終わりに
ちょっとした備忘録になりますがNew Relic側、SAMテンプレート側の機能それぞれ必要に応じて自動的に適用有無を調整できるようにしてみました。
本環境だと当たり前のように考慮しないといけないような部分かとは思いますが個人でちょっと触っている分には忘れがちな部分ではあります。
こういった有無効の方式はツールにより千差万別は思う一方、とはいえ切り替える用にしておきましょうの話は当たり前すぎてか(もしくは自分の調べ方の問題か)以外とでてこない気もするので実際の運用だとどんな感じのパターンが多いのかな?というのは個人的には少し気になるところではあります。