こんにちは、SREの千明です。2020年1月に入社して、気づけば7年目に突入しました。
最近SREでは、イベント参加やブログ執筆が活発になってきているので、私もそれに触発されて久しぶりにブログを執筆することにしました。前回ブログに投稿したのが2020年6月だったので、なんと約6年ぶりの投稿になります。
WordPressをShifterへ移行した話 - ANDPAD Tech Blog
今回は最近導入した「Lambdaランタイムの更新をRenovateで検知できるようにする仕組み」について紹介しようと思います。
こんな方におすすめの内容です
今回の内容は、以下のような方向けになっています。ぜひ参考にしていただけると幸いです。
- TerraformでLambda関数の管理をしていて、ランタイム更新を自動検知したい方
- Renovateをすでに導入済みでパッケージ自動検知、更新に利用されている方(Renovateの導入方法は説明しません)
- 複数のLambdaランタイムが混在しているリポジトリを管理されている方
何を解決したかったのか
この仕組みを導入する以前は、以下の手順で定期的にLambdaランタイムの更新を実施していました。
- Lambda runtimes(英語版)のページを定期的に確認して、EOLが間近に迫ったLambdaランタイムがないか確認する。
- EOLが間近に迫ったLambdaランタイムがあった場合、該当のLambdaランタイムを使用しているLambda関数をリストアップする。
- リストアップしたLambda関数を管理している開発チームへLambdaランタイムの更新を依頼する。
- 各開発チームが開発環境でLambdaランタイム更新の動作検証を行い、問題なければ本番環境でもLambdaランタイムの更新を実施する。
しかし、上記の方法では誰かが能動的にEOLの確認をしなければいけません。さらに、EOLを基準として動き始めるため、更新期間(移行のための準備期間)が短くなってしまいます。Lambda関数を多数管理している開発チームの場合は、期限日(Deprecation date)までに更新が間に合わないこともあります。
この課題を解決するために、新しいLambdaランタイムがリリースされたタイミングを自動検知して、更新を始められるようにしたいと考えました。まず、更新タイミングを自動検知することによって、能動的に確認をする必要がなくなります。そして、更新を始めるタイミングをEOLからリリースにすることで、更新期間を長く取ることができて、余裕を持ってLambdaランタイムを更新できます。
どうやって解決したのか
上記の課題を解決するために、Terraformで管理しているLambdaランタイムのバージョンをRenovateで検知して、新しいバージョンがリリースされたタイミングでPRを作成するようにしました。以下でその方法を解説します。
リポジトリ構成
今回サンプルとして用意したリポジトリは以下のような構成になっていて、lambda_function_rubyとlambda_function_nodejsの2つのLambda関数を管理しています。(ソースコードは別で管理されていて、今回の説明では割愛します)
. ├── lambda_function_ruby │ └── main.tf ├── lambda_function_nodejs │ └── main.tf ├── modules │ └── lambda_function │ └── main.tf └── renovate.json
いくつかのファイルについても、簡単に説明します。まず、modules/lambda_functionは、Lambda関数に必要なリソースを定義するモジュールで、runtimeの値を変数で受け取るようになっています。例えば、lambda_function_rubyでは、以下のようにモジュールを呼び出して、ランタイムの値を指定しています。
module "lambda" { source = "../../../modules/lambda_function" runtime = "ruby3.3" }
また、すでにRenovateが導入されていて、その内容はrenovate.jsonによって設定されています。今回は、Terraformを更新するだけのシンプルな設定になっています。
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "packageRules": [ { "matchManagers": ["terraform", "terraform-version"], "labels": ["dependencies", "terraform"] }, { "matchPackageNames": ["hashicorp/terraform"], "additionalBranchPrefix": "{{packageFileDir}}-", "groupName": "terraform-version", "versioning": "hashicorp" }, { "matchDatasources": ["terraform-provider"], "additionalBranchPrefix": "{{packageFileDir}}-", "groupName": "terraform-providers" } ] }
説明は以上にして、次からはlambda_function_rubyを使って、Lambdaランタイムの更新をRenovateで検知できるようにするための設定方法を解説していきます。
Terraform側の設定
まずは、Terraform側のLambda関数のリソース定義にあるruntimeへ、以下のようなRenovate用のコメントを追加します。このコメントを元にRenovateが更新対象を抽出します。
module "lambda" { source = "../modules/lambda_function" # renovate: datasource=endoflife-date depName=aws-lambda versioning=loose runtime = "ruby3.3" }
Renovate側の設定
次に、renovate.jsonにcustomManagersを追加します。この設定によって、先ほど追加したコメントとLambdaランタイムの設定を抽出します。
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "packageRules": ["...Terraformの設定(省略)..."], "customManagers": [ { "customType": "regex", "description": "Update AWS Lambda runtime in Terraform files", "managerFilePatterns": ["/.+\\.tf$/"], "matchStrings": [ "#\\s*renovate:\\s*datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s*runtime\\s*=\\s*\"(?<packageName>[a-z]+)(?<currentValue>[0-9.]+)(\\.x)?\"" ], "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}" } ] }
このとき、正規表現の名前付きキャプチャーグループで名前を付けた値が、後述するpackageRulesの設定で利用できます。 そして、ここが今回のミソなのですが、packageNameにrubyが入るようになっていて、この値もpackageRulesの中で利用できます。
次に、先ほどのcustomManagersで抽出した部分を修正するために、packageRulesに2つのルールを追加します。このリポジトリでは、RubyとNode.js、2つのLambdaランタイムを管理しているので、それぞれのLambdaランタイムに対してルールを追加しています。
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "packageRules": [ "...Terraformの設定(省略)...", { "matchDatasources": ["endoflife-date"], "matchDepNames": ["aws-lambda"], "matchPackageNames": ["ruby"], "overridePackageName": "aws-lambda", "extractVersion": "^ruby(?<version>.+)$", "additionalBranchPrefix": "{{packageFile}}-", "labels": ["Lambda runtimes", "ruby"] }, { "matchDatasources": ["endoflife-date"], "matchDepNames": ["aws-lambda"], "matchPackageNames": ["nodejs"], "overridePackageName": "aws-lambda", "extractVersion": "^nodejs(?<version>.+)\\.x$", "additionalBranchPrefix": "{{packageFile}}-", "labels": ["Lambda runtimes", "nodejs"] } ], "customManagers": [ { "customType": "regex", "description": "Update AWS Lambda runtime in Terraform files", "managerFilePatterns": ["/.+\\.tf$/"], "matchStrings": [ "#\\s*renovate:\\s*datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s*runtime\\s*=\\s*\"(?<packageName>[a-z]+)(?<currentValue>[0-9.]+)(\\.x)?\"" ], "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}" } ] }
この設定を追加することで、先ほどのcustomManagersで抽出されたruntimeの値を元に、新しいバージョンのLambdaランタイムがリリースされているか検索します。リリースされていればPRを作成します。 今回の場合は、packageNameがrubyになるので、1つ目のルールが適用されます。そして、extractVersionの設定に従い、RubyのLambdaランタイムで新しいバージョンがないか検索して、更新があればPRを作成します。
ただし、packageNameがrubyのままだと、endoflife-dateのデータソースから更新情報を取得できません。そのため、overridePackageNameによってデフォルト値のaws-lambdaに戻しています。なぜこのようなことをしているかというと、(今回はRubyだけでなく)Node.jsのLambdaランタイムも管理しているためです。packageNameで一時的にLambdaランタイムの種類を保持することで、packageRulesのルールをLambdaランタイムの種類別に分けられます。
以上の設定で、Lambdaランタイムの更新をRenovateで検知してPRを作成できるようになります。この例ですと、lambda_function_rubyに対して、ruby3.3からruby3.4以降のバージョンへの更新PRが作成されます。
まとめ
今回は、Lambdaランタイムの更新をRenovateで検知できるようにする方法について紹介しました。LambdaランタイムのバージョンをRenovateで検知してPRを作成することで、リリースを早期に把握できます。さらに、Lambdaランタイムのリリースを起点にすることで、更新期間を長く取ることができ、余裕を持ってLambdaランタイムを更新できます。
また上記以外にも、Lambdaランタイムを更新するコストを下げる取り組みも行っています。具体的には、マネージドサービスへの置き換えによるLambda関数の削減やGo言語での書き直し、Amazon Linux 2023ランタイム移行によるEOL期間の延長などです。アンドパッドでは、次々と新しいプロダクトをリリースしていて、それに伴って管理リソースも増えています。そのため、このような運用コストを下げる取り組みも積極的に実施しています。
We are hiring!
アンドパッドでは、「幸せを築く人を、幸せに。」というミッションの実現のため、一緒に働く仲間を大募集しています。アンドパッドのSREチームにご興味がありましたら、以下のページからご応募ください。カジュアル面談も実施しています。