今年もたくさんクソコードを書きました。障害対応もたくさんしました。
その中でも特にしんどかった障害について回想したいと思います。
環境
- Amazon ECS
- node:alpine
嵐の前の静けさ
この環境で動いているのは、私が前任者から引き継いだサブシステムの1つでした。
プロジェクトの中でもほとんど改修されることもなければ、特に改善の話も出てこなかったこのシステムに、突如として新APIを追加するタスクが私に降ってきました。
好きに作ってくれて良いと言われたNode.js素人の私は、ソースコードにごm、、APIを水を得た魚のように追加していくタスクに明け暮れました。
Expressを使っていくうちに感じる開発のスピード感や、バリバリ使われているPromiseをawait/asyncにリファクタリングしていく爽快感。。
「なるほど、、完全に理解した」
初心者特有の勘違いをしたままの私は、その自信満々のソースコードを引っ提げてレビューを依頼するのでした。
そして散々に指摘されまくった私の改修したソースコードは晴れて日の目を見ることになります。
障害は突然に
それはリリースされて数ヶ月後の出来事でした。
お客さんから報告された内容としては、
「未登録データが多いのでサーバで何か問題が起きてるのではないか」
といったざっくりとした内容でした。
この時、一次調査をメンバーが担当していたので私はそんなことつゆ知らずといった感じで別タスクに精を出していました。
そして、一次調査を行ったメンバーから次のような報告を受けました。
メンバー「ログを追ってみたところ、〇〇(私)さんが以前担当していたサブシステムでメモリリークが起きてコンテが再起動してて、それが尾を引いて未登録データが増えてるみたいっす」
わたし「、、ほーん、、なるほどですね(わかってない)」
、、と、正気に戻って詳しく内容をみてみると、確かにここ数ヶ月でユーザー数が増えたことにより、当初想定していたコンテナ負荷を軽々と超えていたということが分かりました。
(「なぜアラート通知やモニタリングをしていないんだ?」という正論も飛んできそうですが、、)
最初の過ち
とりあえずエラーメッセージを手がかりに、具体的な対策を打つ方法を調べてみましたが、
- Node.jsが使用するメモリ容量(ヒープ)の上限値を上げる(デフォルトは1.4GB)
という情報しか出てきませんでした。
そのほかにもリファクタリングしたところがまずかったのではないかと、ソースコードを確認しましたが、リファクタリングした箇所でもメモリリークと関連つけて報告されてるような記事を見つけられませんでした。
とにかく最初に出てきた情報頼りで、メンバーに打診しようとした直前、
メンバー「とりあえずECSのスケールアップ(メモリ増やす)が先ですかね〜」
と回答が来ていて、「なるほど、まずはスケールアップが先決か、、」とすっかり思い込んだ私は、検証環境でスケールアップしたシステムのテストを行い、実際のアクセス数と同様の負荷に耐えられることを確認しました。
そして障害報告のあった翌日、本番環境のスケールアップを行い、この障害はこれで解決、、かのように見えました。
悪夢再び
それはスケールアップを行った翌日のことでした。
翌日は、私に代わってメンバーがスケールアップ後の本番環境のログを追っていました。
そして、当日昼にメンバーから以下のような報告を受けました。
メンバー「メモリリーク、起きちゃってました😂」
メモリリークってそんなに頻繁に起きるものなのか。。
上の空状態の私は、また同じトレースログを追って同じ記事を見つけ、
「よし、、もうこれしかない、、これしかないよな、、」
とNode.jsが使用するメモリ容量を上げることを前提にした報告を考えていました。
救いの手が差し伸べられる
ここで、別チームのエンジニアから1つの画像とリンクが送られてきました。
https://github.com/nodejs/node/issues/29038
別チームエンジニア「ここで報告されてる通り、対象のDockerイメージだとメモリリークするみたいですね。」
本番のメトリクスを確認すると、確かに同じようなグラフの形をしていました。
報告されているのはnodeのベースイメージで対象のalpine版だとメモリリークが起きるとのことで、まさにピンポイントのベースイメージを使用していました。
試しに、ベースイメージを現状のalpine版とalpineではないイメージとで比較して負荷テストを行った結果、確かにalpineではないイメージは上記のようなグラフにはならずにメモリが解放されて波打つようなグラフになりました。
そして、対応としてはalpineではないイメージをベースイメージとして使用するという結論に至りました。
(結局、私が打診しようとしていたNode.jsのメモリ容量を上げる対策だけを行ったところで延命措置にしかならなかったと気づくこともできました😂)
まとめ
経験が浅いからとかそういうのではなく、反省の多い障害対応(トラブルシューティング)だったと思います。。
(エラーメッセージに固執してしまったり、負荷テストが十分でなかったり、モニタリングしてなかったり、そもそもなんでメトリクスに気づけなかったんだとか思ったり、、)
と、思うことに留めずに反省をバネにして来年もトラブルシューティングしていきます😂