AWS Lambda Node.js runtime の EoL に疲れたので Go にしていっている話

SREチームの藤原です。Tech Kayac Advent Calendar Migration Track 19日目の記事です。いよいよ年も押し詰まってきましたね…!

AWS Lambda、使ってますか?最近はサーバーレスという文脈で取り上げられることも多い Lambda ですが、カヤックではそこまでサーバーレスにこだわることはせず、主にイベントドリブンな処理に適切なユースケースに使用しています。

Lambda のリリース当初に用意されていたランタイムは Node.js のみでした。カヤックで最近使うことが多い言語である Go, Ruby のランタイムがサポートされたのが比較的最近だったということもあり、Node.js の Lambda function が比較的多く存在している状況でした。

Node.js EoL (End of Life)

ところで、技術基盤チームのリポジトリで「Lambda Node EoL」で検索した結果がこちらです。

f:id:sfujiwara:20191218163448p:plain

最近2年に3回、Node.js Runtime EoL の issue が起票されています。今年は 6.10 の issue が対応を完了して閉じられたのが 2019-05-31、8.10 の issue が開かれたのが 2019-10-09 と、5ヶ月しか空いてないですね…

もっともこれは仕方のない面があって、Node.jsのリリースポリシー をみると、LTS のバージョンでも EoL が毎年やってくる想定になっています。現行の Lambda で利用できる 10.x, 12.x の EoL 予定はそれぞれ 2021-04, 2022-04 です (8.x は OpenSSL 1.0.2 の EoL に合わせて早まったので、特例的に 2019-12 でした)。AWS はそれにあわせて Lambda のランタイムを EoL にしているだけ、というわけです。

ともあれ、ランタイムが使用できなくなると Lambda 関数も止まってしまうため、古いランタイムを使用している関数はバージョンアップしていく必要があります。頻繁にデプロイするようなアプリケーションであればともかく、マネージドサービスからのイベントドリブン処理に使っているような関数は、一度安定して動き出すとそれほどいじる機会がないものも多いです。

僕はもう疲れ(ry

バージョンアップ作業は、言語やライブラリ、実行環境の互換性が保たれていれば単にランタイムのバージョンを変更するだけなのですが、Node.js 10 のランタイムからは実行環境がそれまでの AmazonLinux 1 から Amazon Linux 2 に変わったり、言語のバージョンにあわせて周辺ライブラリも適切にバージョンアップする必要があったりします。すんなり行けば御の字ですが、行かなかった場合は気が重い作業です。

要するに、メインのアプリケーションでもない雑多な処理のバージョンアップで削られたくない、というのが正直な気持ちでした。

Lambda Go runtime

ところで最近カヤックでは Go を採用しているプロジェクトが多く、プロダクションレベルで読み書きできる人材も豊富です。逆にサーバーサイドで Node.js を採用する例はそれほど多くないため、Lambda も Go にしていけばいいよね、という流れになります。

Go のランタイムはいまのところ go1.x 一種類のみです。Lambda の Go ではソースコードをアップロードして Lambda 側でビルドするような作りではなく、Linux 用のバイナリを各自でビルドして zip でアップロードする形のため、Go の細かいバージョンに依存しないためです。

つまり、Go 自体の言語の EoL は過ぎても、一度バイナリを配置して動き出した Lambda 関数については、実行環境のバイナリ互換があるうちは基本的に動く期待ができます。

また、aws/aws-lambda-go のソースコードを読むと分かるのですが、Lambda 実行環境と Go で書かれたアプリケーションのやりとりは、Go 本体の net/rpc パッケージを使用しています。

net/rpc はドキュメントに、フリーズされており新機能などの追加はない、と明記されていて、ある意味枯れきっているパッケージです。

The net/rpc package is frozen and is not accepting new features.

ここのインターフェースが変わることを心配する必要もまずないでしょう。

ということで、Lambda は Go で書いておけば、少なくとも Lambda のランタイム都合でのバージョンアップを頻繁に求められることはなさそうです。限られた人手である以上、ランタイムの要求によるバージョンアップの手間が少ないのは助かりますね。(もちろん、セキュリティfixであるとか、使用しているAPI/SDKに修正がある場合は個々に必要ですが、それは別の話です)

以前は、ほんのちょっとした処理なのに型のきっちりした Go で書くのはダルい印象がありましたが、今はカスタムレイヤーで bash が使えたりします。本当に aws-cli と jq だけで済むような簡単な処理は bash layer の shell script で、ちょっと複雑なものは Go で、というのがバランスがいいかなと思っています。