概要
AWS Web Adapterというものがあり、ウェブAPIのコードをそのままにサーバーレス化することができる。
これいいじゃんということで、GoのGinで動くウェブAPIをサーバーレス化したのだが、以前のECS Fargate環境では正常に動作していたDatadogのトレース送信が動かなくなった。
ハマった末にやり方が分かったので、Lambda Web Adapter & Gin の構成でウェブAPIを動かす際にDatadogのトレースを正常に送る方法をお伝えする。
Web Adapterを使っていない場合はどうやってトレースを送るのか?
まずは、Web Adapterを使っていない通常のLambdaの場合だとどのようにDatadogトレースを送るのか紹介する。
Dockerfileに以下のコードを書き、datadog extensionをコンテナイメージに含める。
COPY --from=public.ecr.aws/datadog/lambda-extension:latest /opt/. /opt/
datadog-lambda-goのライブラリをインストールし、
go get github.com/DataDog/datadog-lambda-go
ddlambda.WrapFunction
を仕込む。
func main() { // Wrap your lambda handler lambda.Start(ddlambda.WrapFunction(myHandler, nil)) } func myHandler(ctx context.Context, event MyEvent) (string, error) { // lambdaの処理をここに書く }
このやり方は公式ドキュメントにも記載があるし、特に問題なく動かせると思う。(※ 2023年4月現在、英語でないとcontainer imageの場合のやり方が出ないので注意)
Web Adapterを使っている場合はどうなるか?
通常、コンテナイメージのLambdaを使う場合、上記のように lambda.Start
がエントリポイントとなる。
しかし今回は、Web Adapterを使っているので、アプリケーションのコードはginを使った純粋なweb APIのコードになり、 lambda.Start
をどこにも書いておらず、ddlambda.WrapFunction
を仕込むことができない。
ではどうやるのかというと、Datadogのトレーサーをスタートするときに、以下のようにオプションを入れると良い。
tracer.Start( tracer.WithEnv("環境名(stagingやproduction)を入れる"), tracer.WithService("Datadog上で扱われるサービス名を入れる"), tracer.WithLambdaMode(false), // trueにするとstdoutにtraceが出力され、datadog agentに送信されなくなる tracer.WithGlobalTag("_dd.origin", "lambda"), ) defer tracer.Stop()
ECSからLambdaにginのAPIを移した際、当初はtracer.WithLambdaMode(false)
と tracer.WithGlobalTag("_dd.origin", "lambda")
を入れておらず、トレースがDatadogに送信されてこなかった。
コードのコメントにも書いた通り、tracer.WithLambdaMode
がtrueだとトレースがstdoutに出力され、datadog agentに送信されなくなる。
実際にはLambdaで動かしているのに、WithLambdaModeをfalseにするのは奇妙だが、Datadogのライブラリの実装を追っていくと確かにそうしている。
まとめ
Web Adapterを使うことで簡単に既存のAPIをLambdaに載せれるし、ローカルでも簡単に動かしやすくなる。 Datadogとの連携がドキュメントにも書いておらず難しかったが、これで解決できたのでよかった。