CloudFormationでLambda関数を作るときはロググループも同時に作った方が良いけど依存関係も正しくした方が良いという話
CloudFormation(あるいはSAM)でLambda関数を作成するときは、ログの保持期間を設定したりCloudFormationスタックと同時にロググループを削除したりできるように、CloudWatch Logsロググループも同時に作成する方が良いです。
参考:【小ネタ】AWS SAMでLambda関数を作成する場合はCloudWatch LogsのLog Groupも同時に作った方がいいという話 | DevelopersIO
さらに、場合によっては依存関係も正しく記載した方が良いという話をします。
CloudWatch Logsロググループを作成するには
Lambda関数と同時にロググループを作成するには、前述の参考サイトにも記載のとおり、以下のようにCloudFormationテンプレートを記述します。
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
FunctionName: MyFunction
...省略...
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${Function}
...省略...
Lambda関数はデフォルトで/aws/lambda/{関数名}
という名前のロググループにログを出力するため、その名前でロググループを作成しておけばよいというわけです。
些細な問題?
しかし、よく考えるとこれは依存関係が逆であるということに気づきます。上の例ではLogGroup
がFunction
に依存しているためFunction
が先に作成されますが、本来であればログを出力する関数よりもログの出力先であるロググループの方が先に作成されるべきです。
この順序が問題になることはほとんどありませんが、例えば関数が作成された直後(ロググループが作成される前)に関数が実行される場合は問題になります。関数の実行により自動的に/aws/lambda/{関数名}
というロググループが作成された後、同じ名前を持つLogGroup
が作成されようとして失敗するためです。
それでも、そんな状況が発生することはまずないだろう、と思っていたのですが、先日その状況が発生する事例に遭遇しました。
問題が顕在化する事例:カスタムリソース作成時
Lambda関数を使用するカスタムリソースを関数と同じスタックで作成する場合、前述の誤った依存関係(作成順序)にしてしまうと問題が発生することがあります。
私の場合、スタックの作成オペレーションが失敗したときは以下の順番でイベントが進んでいました。
-
Function
の作成が開始 -
Function
の作成が完了 -
LogGroup
の作成が開始 -
Function
を使用するカスタムリソースの作成が開始(※この時、カスタムリソースの裏側のFunction
が実行され、自動的に/aws/lambda/{関数名}
という名前のロググループが作成されたと推測される) -
Function
を使用するカスタムリソースの作成が完了 -
LogGroup
の作成が失敗(※「すでにその名前のロググループは存在する」という理由で失敗)
なお、必ずこのように失敗するというわけではなく、偶然失敗しないときもありました。
ちなみに、こうしたスタックの削除オペレーションを実行したときは、ロググループ削除→カスタムリソース削除(=関数実行=ロググループ自動作成)の順番で進む場合があるため、意図に反してロググループが残ってしまうことがありました。
改善策
というわけで、Lambda関数と同時にロググループを作成するには、以下のようにCloudFormationテンプレートを記述しましょう。
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
FunctionName: MyFunction
LoggingConfig:
LogGroup: !Ref LogGroup
...省略...
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/MyFunction
...省略...
これにより、LogGroup
→Function
の順番で作成されるようになるため、前述の問題を防ぐことができます。
Discussion