API Gateway+Lambdaでのステージ管理やCloudWatch Logsのログ運用のはなし

AWS運用自動化サービス「Cloud Automator」
この記事は1年以上前に書かれたものです。
内容が古い可能性がありますのでご注意ください。

こんにちは。技術1課@大阪オフィスの柏尾(前厄)です。

年明けから下の子(1歳)が突発性発疹、長男→妻→自分の順番でインフルエンザにかかってしまい、年始早々大変でした。私はクラウドワークスタイル制度を利用し、ほぼ在宅勤務なこともあり、急な家族の看病もなんとか乗り切ることができました。

今年は、本厄に突入した先輩方(kubo/nagata/takada)に意見を聞きながら、慎重に行動したいと思います。

API GatewayとLambdaを使った際のステージ管理について

サーバーレスアーキテクチャといえば、API GatewayとLambdaを組み合わせて使うパターンが多いと思いますが、API GatewayとLambdaでは、

  • API Gatewayの「ステージ」
  • Lambdaの「エイリアス」「function バージョン」
  • の機能を使うことで、ベースとなる設定を共有しつつ「開発・ステージング・本番」など複数のステージ管理を行うことができます。

    図で示すと、下記のようになります。
    例では、「開発:dev」「ステージング:stg」「本番:prod」でステージを分けることを想定してみます。

    APIGW_Lambda_CloudWatchLogs_01

    API GatewayとLambdaではそれぞれ下記の設定を行います。

    ■ API Gateway側の設定

  • API Gateway側で「ステージ」を定義
  • API Gateway側の各ステージで「ステージ変数」を定義(各ステージのステージ変数で、呼び出すLambda functionのエイリアス名を設定する。変数名は何でも良い。)
  • API Gatewayの統合リクエストで呼び出すLambda functionを、function名:${stageVariables.ステージ変数名} の形式で指定
  • ステージ変数の設定

    APIGW_Lambda_CloudWatchLogs_02

    統合リクエストの設定

    APIGW_Lambda_CloudWatchLogs_03

    ■ Lambda側の設定

  • Lambda側で「エイリアス」を定義
  • Lambdaで定義した「エイリアス」と、それに実際に紐づくLambdaの「function バージョン」を紐付け
  • エイリアスとバージョンの紐付け

    APIGW_Lambda_CloudWatchLogs_04

    注意点としては、API Gatewayの統合リクエストで、呼び出すLambda関数を定義した際、下記のように「Lambda関数に権限を追加する」というモーダルウィンドウでAWS CLIコマンドが表示されるので、それを実行しておく必要があります。また、${stageVariables.ステージ変数}の部分は実際の設定値(dev/stg/prodなど)に書き換え、それぞれ実行する必要があります。

    権限の追加

    APIGW_Lambda_CloudWatchLogs_05

    ■ API GatewayのURLとステージ名の関係

    呼び出すAPI GatewayのURLには下記のようにステージ名が含まれますので、クライアントからは呼び出すURLを切り替えることで、稼働確認をするステージの切り替えをすることができます。

    prodステージのURL
  • https://<10桁のID>.execute-api.ap-northeast-1.amazonaws.com/prod/hogehoge
  • devステージのURL
  • https://<10桁のID>.execute-api.ap-northeast-1.amazonaws.com/dev/hogehoge
  • ■ Lambdaのエイリアスとバージョンの紐付け

    また、Lambda側のエイリアスとfunction バージョンの紐付けは後から変更することができます。

    devエイリアスは「$LATEST」に紐付けておくことで、functionのデプロイによって変更を即時反映させることができます。

    dev環境での稼働確認が終わったあとに、「publish new verison」で、$LATESTから新しいfunction バージョンを発行し、stgやprodエイリアスの紐付けを新しいバージョンに切り替えることで、安全にfunctionのバージョンを上げていくことが可能になります。

    CloudWatch Logsでのログ出力について

    この構成を前提とし、API GatewayやLambdaのログの出力単位について見てみます。

    CloudWatch Logsコンソール

    APIGW_Lambda_CloudWatchLogs_06

    CloudWatch Logsでの出力単位

    APIGW_Lambda_CloudWatchLogs_07

    ■ API Gatewayにおけるログ出力

    API Gatewayでログ出力を有効にすると、CloudWatch Logsに、下記のようなネーミングルールで、「ステージ単位」に「ロググループ」が作成され、その配下のログストリームにAPI Gatewayのログが出力されるようになります。

    API Gatewayのロググループ名

    API-Gateway-Execution-Logs_<10桁のID>/<ステージ名>

    ※ API Gatewayでのログ出力等の設定は下記のURLを参考にしてください。

    【参考】ステージの設定

    http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-stage-settings.html

    ロググループに対する検索

    CloudWatch Logsの管理コンソールでは、「ロググループ単位」でキーワード検索することができるようになっていますので、「本番ステージのAPIのログに対してキーワード検索」といったことが可能になります。

    APIGW_Lambda_CloudWatchLogs_08

    ■ Lambdaにおけるログ出力

    一方、Lambdaの方は「Lambda functionごと」にロググループが分かれています。ロググループ名は次のようになります。

    Lambda functionのロググループ名

    /aws/lambda/<Lambda function名>

    さらに、その配下のログストリームは、下記のようにログストリーム名に日付と、functionバージョンが含まれる形でログが出力されています。

    Lambda functionのログストリーム名

    yyyy/MM/dd/[<functionバージョン>]34bb6d9de7334a099d59cae827193ba9

    例えば、

    バージョンが「$LATEST」のfunctionのログですと、

    2017/01/30/[$LATEST]7dbc786edbf04ae0ae3e8b086a6ff6a2

    バージョンが「5」のfunctionのログは

    2017/01/30/[5]d9e74698de4a45bfb3e4c9f29034f31d

    のようなログストリームにログが出力されます。

    このように、Lambdaログの場合、エイリアスごとにログが出るわけではないので、そのままの状態では、「prodエイリアスに対応するLambda functionのログ」という指定でログを検索することはできません。

    ■ CloudWatch Logsにおけるログの管理方法

    これらを踏まえた上で、以下のような方法を組み合わせることで、Lambdaログの管理や検索を簡易化することが出来るかと思います。

    ロググループの分割

    APIメソッドごとにLambda functionを分ける

    API Gateway側で複数のAPIメソッドを定義する場合、Lambda側もそれに対応するようにLambda functionを作成する方法です。
    functionが別になると、ロググループも別々になりますので、functionごとのログ検索がやりやすくなります。

    ログストリームの分割

    それぞれのエイリアスを異なるfunction バージョンに紐付ける

    Lambda側で定義する各エイリアスと、それに紐付けるfunctionバージョンを分けることで、ログストリームを分けることができます。
    例えば、

  • 「dev」エイリアス → 「$LATEST」
  • 「stg」エイリアス → 「ver 6」
  • 「prod」エイリアス→「ver 5」
  • としておくと、それぞれ異なるログストリームにログが出力されますので、CloudWatch Logsコンソール上でのログ検索が多少やりやすくなるかと思います。

    エイリアス名をログに出力

    Lambdaのログ出力時に、API Gatewayから渡されるステージ変数から「エイリアス名」を取得し、ログに含めるようにする

    Lambda処理内で、API Gatewayのステージ変数を取得することができますので、ログ内にステージ変数から取得した「エイリアス名」を含めて出力します。

    こうしておくと、ログが混ざった状況でも、どのエイリアスのLambda functionが呼び出されたかが分かるようになり、検索条件などにも含めることができるようになります。

    特定のログを他サービスに連携する

    CloudWatch Logsのサブスクリプション機能を使う

    CloudWatch Logsにはサブスクリプションという機能があり、ロググループに対して、フィルタパターンに一致するログを、Lambdaに処理(通知など)させたり、Elastic Searchに登録し、後から検索したりすることが出来るようになります。

    フィルタパターンでは、「特定のキーワード」で、対象のログを絞り込むことが可能です。「エイリアス名」をログに出力しておけば、特定のエイリアス名+キーワードの組み合わせでログをフィルタしたりすることができるかと思います。

    【参考】サブスクリプションを使用したログデータのリアルタイム処理

    http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/Subscriptions.html

    【参考】CloudWatch Logs データを Amazon Elasticsearch Service にストリーミング

    http://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/CWL_ES_Stream.html

    まとめ

    前半はAPI GatewayとLambdaのステージ管理を行う方法、後半はその構成における、CloudWatch Logsのログ出力単位やその管理方法について書いてみました。

    他にも、こんな方法があるよ、というのがあれば教えていただけると嬉しいです。

    AWS運用自動化サービス「Cloud Automator」