こんにちは。技術1課@大阪オフィスの柏尾(前厄)です。
年明けから下の子(1歳)が突発性発疹、長男→妻→自分の順番でインフルエンザにかかってしまい、年始早々大変でした。私はクラウドワークスタイル制度を利用し、ほぼ在宅勤務なこともあり、急な家族の看病もなんとか乗り切ることができました。
今年は、本厄に突入した先輩方(kubo/nagata/takada)に意見を聞きながら、慎重に行動したいと思います。
API GatewayとLambdaを使った際のステージ管理について
サーバーレスアーキテクチャといえば、API GatewayとLambdaを組み合わせて使うパターンが多いと思いますが、API GatewayとLambdaでは、
の機能を使うことで、ベースとなる設定を共有しつつ「開発・ステージング・本番」など複数のステージ管理を行うことができます。
図で示すと、下記のようになります。 例では、「開発:dev」「ステージング:stg」「本番:prod」でステージを分けることを想定してみます。
API GatewayとLambdaではそれぞれ下記の設定を行います。
■ API Gateway側の設定
ステージ変数の設定
統合リクエストの設定
■ Lambda側の設定
エイリアスとバージョンの紐付け
注意点としては、API Gatewayの統合リクエストで、呼び出すLambda関数を定義した際、下記のように「Lambda関数に権限を追加する」というモーダルウィンドウでAWS CLIコマンドが表示されるので、それを実行しておく必要があります。また、${stageVariables.ステージ変数}の部分は実際の設定値(dev/stg/prodなど)に書き換え、それぞれ実行する必要があります。
権限の追加
■ API GatewayのURLとステージ名の関係
呼び出すAPI GatewayのURLには下記のようにステージ名が含まれますので、クライアントからは呼び出すURLを切り替えることで、稼働確認をするステージの切り替えをすることができます。
prodステージのURL
devステージのURL
■ Lambdaのエイリアスとバージョンの紐付け
また、Lambda側のエイリアスとfunction バージョンの紐付けは後から変更することができます。
devエイリアスは「$LATEST」に紐付けておくことで、functionのデプロイによって変更を即時反映させることができます。
dev環境での稼働確認が終わったあとに、「publish new verison」で、$LATESTから新しいfunction バージョンを発行し、stgやprodエイリアスの紐付けを新しいバージョンに切り替えることで、安全にfunctionのバージョンを上げていくことが可能になります。
CloudWatch Logsでのログ出力について
この構成を前提とし、API GatewayやLambdaのログの出力単位について見てみます。
CloudWatch Logsコンソール
CloudWatch Logsでの出力単位
■ 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のログに対してキーワード検索」といったことが可能になります。
■ 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バージョンを分けることで、ログストリームを分けることができます。 例えば、
としておくと、それぞれ異なるログストリームにログが出力されますので、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のログ出力単位やその管理方法について書いてみました。
他にも、こんな方法があるよ、というのがあれば教えていただけると嬉しいです。