Akerunバックエンドシステムの技術的負債に対する取り組み[後編]

この記事は Akerun Advent Calendar 2023 - Qiita の24日目の記事です。

こんにちは。@ps-tsh です。API Server などバックエンドシステムの開発を担当しています。前回に続き、当社(Photosynth)での技術的負債に対する取り組みについて紹介します。

前編はこちら:

akerun.hateblo.jp

2021年にスタートした技術的負債解消プロジェクトでは、メインの目的を「システムの安定稼働」と定め、安定稼働を実現するための取り組みとして「収集メトリクスの改善」「単一障害点の解消」「ソフトウェア脆弱性の解消」「運用作業の品質改善」の4方針でそれぞれ施策を実施していきました。

収集メトリクスの改善

最初に取り組んだのが「収集メトリクスの改善」です。正しい判断には正しい情報が不可欠だからです。前回触れたように、このあたりは外部サービスの導入やプラン変更で対応しました。2023年現在、アプリケーション性能監視(APM)ツールとして NewRelic、エラーの監視・トラッキングには Sentry、WebエンジニアとSREによるオンコール運用には PagerDuty などを利用しています。

障害対応が迅速に行えない原因の一つとして「機能のリリース時には問題に気づかず、数ヶ月後の状況変化で元からあった問題が顕在化する」というパターンがあります。APMツールの導入で、サービスが提供する機能のエラーやパフォーマンス遅延、処理全体におけるボトルネック箇所を早期に発見することができるようになりました。APMツールから得られる情報をもとに、パフォーマンス基準を下回るAPIレスポンスやSQLクエリのチューニングを実施しました。また、エラーの原因として「外部連携先のサービスがダウンした際の考慮不足」という傾向があることもわかり、こちらはエラーハンドリングを改良することで対処しました。

APMを使うとサービスの状況変化をリアルタイムで把握できるので、新機能のリリース時に性能問題があった際ただちに切り戻しの判断をすることができます。あわせて、エラー監視とオンコール運用の体制を整えることで、障害発生から対応完了までの期間を大幅に短縮することもできました。

単一障害点の解消

次は「単一障害点(SPOF: Single Point of Failure)の解消」です。Webサービスの運用においてはあらゆるトラブルを完全に予防することは困難であり、一部のエラーがシステム全体のダウンにつながらないような配慮が必要になります。当時のAkerunバックエンドシステムにも、以下のようなアーキテクチャ上の課題がありました。

リクエスト処理とバックグラウンドジョブを同じAPIサーバで処理していた

1つのAPIサーバにリクエスト処理とバックグラウンドジョブ処理の両方を担当させていたため、一方の負荷上昇がが双方に影響する構造になっていました。リソース逼迫の原因切り分けも困難であったため、専用の worker サーバを導入しリクエスト処理を行うサーバとバックグラウンドジョブを実行するサーバを分離することで解決しました。

重要度やアクセス特性の異なるリクエストを同じAPIサーバで処理していた

合鍵の権限更新やリモート施解錠など「常時安定して高可用性・低遅延が要求される重要機能」と、入退室履歴のダウンロードなど「利用頻度は高くないが負荷の高い機能」を同じAPIサーバで提供していました。

当時は入退室履歴のダウンロード機能が同期処理で実装されており、ユーザ数や利用ボリュームの増大によってAPIサーバにリクエスト処理プロセスが滞留し、同じAPIサーバで提供される他の機能もレスポンスが大幅に低下するといった問題がありました(現在は非同期処理への置き換えが完了し、データ生成はバックグラウンドで行っています)。APIサーバのクラスタと配置機能を見直し、リモート施解錠などの「重要機能」群とそれ以外の機能は別のクラスタで実行させるよう変更することで対応しました。

ソフトウェア脆弱性の解消

次は「ソフトウェア脆弱性の解消」です。すでにEOL(End of life)を迎えているもの、脆弱性が報告されているものが多数使用されていました。これは基本的にはライブラリのバージョンアップを地道に行なっていくことになります。EOLの確認には各種公式サイトや endoflife.date、ライブラリ脆弱性の確認には GithubDependabot alertsを使っていました。

2021年時点のAkerunバックエンドシステムは恥ずかしながら Ruby 2.2 + Rails 4.1 で 運用されていました。Ruby, Rails ともは2020年以前にEOLを迎えており、セキュリティサポートが受けられない状態でした。また、Ruby/Rails のバージョンが古いせいで、新規に導入したい rubygem があっても要求バージョンを満たせず、自前でコピー実装を用意するなどの非効率な対応を余儀なくされていました。

他の機能開発も並行しながらの対応となったため最速・一括のアップデートというわけにはいきませんでしたが、何度かの段階的リリースを経て、2023年11月までに最新の Ruby 3.2 + Rails 6.1 にアップデートしました。一部サーバでは YJIT も有効にしてパフォーマンスアップの恩恵も享受できています。現在は Rails 7.0 へのバージョンアップ対応準備を進めています。

バージョンアップ対応を進める過程で、全APIリグレッションテストを効率よく実施するサイクルを確立することができました。当初は手動テストの比率が高かったのですが、QAエンジニアによるE2Eテストの自動化なども進み、効率よく互換性を確認できるようになりました。

さらに、本番環境へのリリース前には毎回1週間程度のドッグフーディング(社内での試験運用によるテスト)も実施し、実運用に問題ないことを確認してから本番環境に適用しています。

運用作業の品質改善

最後は「運用作業の品質改善」です。「ユーザーに提供するための新機能開発を優先するあまり運用系の機能整備を後回しにしがち」というのはスタートアップによくある話だと思いますが、運用系の機能不足は大量のトイルを生み出します。これも一種の技術的負債であるといえるでしょう。

トイルとは、プロダクションサービスを動作させることに関係する作業で、手作業で繰り返し行われ、自動化することが可能であり、戦術的で長期的な価値を持たず、作業量がサービスの成長に比例するといった傾向を持つものです。 (SRE サイトリライアビリティエンジニアリング)

トイルの問題は「単調で退屈である」といったことにとどまりません。各担当者は一生懸命やっているにもかかわらず、経営層をはじめとするビジネスサイドからは全く成果が出ていないように見えるので、相互尊重の雰囲気が失われやすいという問題もあります。

運用作業の品質改善としては主に「手動オペレーションの自動化」「リリース関連のルール整備・作業記録の徹底」を行いました。

手動オペレーションの自動化

2021年時点でバックエンドアプリケーションのデプロイは既に自動化されていましたが、不定期に発生する「ファームウェア更新」「設定ファイル更新」などの運用業務の多くは手動で行われていました。dotfile に定義されたバージョン情報やファームウェアのバイナリなど、更新に必要なファイルをサーバに配置する必要があるのですが、手動作業の結果「複数台のサーバに同じ設定が反映されていない」「アップロードされたファイルの中身が違っている」など、たびたび作業ミスが発生していました。これらをすべてリポジトリで管理し、更新ファイルのハッシュ値チェックやサーバへのデプロイをCD/CDに組み込むことでオペレーションミスが発生しにくい形に置き換えました。

リリース関連のルール整備・作業記録の徹底

これまでも大きな機能開発についてはQAプロセスを経てリリースしていたのですが、リリース後の不具合対応など、細かいものについては担当者が対応して都度リリースしてよいという方針でした。その結果、「いつ」「誰が」「何を」リリースしたのかわからない状態で運用されていました。

また、データ更新をはじめとしたメンテナンス対応についても手順書や作業記録が残っておらず、担当者ごとの作業品質に大きなばらつきがありました。商用サービスとして成熟する過程においては頻繁な機能リリースの利便性よりも安定稼働が重視されるタイミングと判断し、思い切ってリリースを定期イベントにしました。いくつかのプラクティスを紹介します。

  • ブランチ運用: git-flowをベースにしたブランチ運用ルールを導入した
  • リリース内容共有会: リリースを原則週1回の定期イベントとし、その週のリリース内容を開発チーム全員に事前共有する会議体を設けた
  • リリース手順書: リリース手順書のフォーマットを用意し、リリース内容、タイムライン、チェック項目などを事前にまとめる
    • イレギュラー発生時の切り戻し手順もあらかじめ明確化しておく
  • ペアオペレーション: 本番環境へのリリースやメンテナンス作業は単独作業を禁止、必ず2名以上で画面共有しながら行う
  • 作業記録: リリース作業やメンテナンス実施時に出力されたログやスクリーンショットなどを収集し、ドキュメントとして残しておく
    • トラブルシュートを行なった場合の対応内容も記述する

リリースプロセスとしてはやや重めの内容になりましたが、新規メンバー向けのトレーニング効果もあったと思います。また、作業記録は蓄積されることで信頼できるアーカイブとして機能します。現時点で3年弱の履歴が残っていますが、過去に実績のある作業の再現やトラブルシュートに役立つことも増えました。

おわりに

技術的負債は一度解消して終わり、といった性質のものではありません。また新しい課題や取り組みが出てきたら、このブログで紹介したいと思います。


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 photosynth.co.jp

Akerunにご興味のある方はこちらから akerun.com