保育士人材バンクの技術基盤改善 2024

この記事は株式会社エス・エム・エス Advent Calendar 2024の12月4日の記事です。

qiita.com

保育士人材バンク のサービスを担当しておりますエンジニアの田実と申します。 保育士人材バンクは保育業界で働く方と事業所のための就職・転職マッチングサービスです。

本記事では2024年の保育人材バンクの技術基盤改善の取り組みを振り返っていきます!

コンテナ化

今年一番大きかった基盤改善は何と言ってもコンテナ化です!コンテナ化前はEC2上にアプリケーションがホスティングされており、デプロイはrsync、ログはSSHでサーバーに入って直接ログファイルを確認、といった運用を行っていました。

EC2でホスティングされているためミドルウェアのアップグレードが気軽にできなかったり、デプロイ方法もアトミックな変更ではないためデプロイ中のアクセスがエラーになることもありました。ログの確認はログファイルを直接awk、sort、uniqコマンドを使って走査していく方法になるため手軽に確認できない課題がありました。開発環境に関しても1人1環境といった形で運用していたため、複数の施策を並行して開発する場合、対応内容がバッティングする問題もありました。

これらの課題を解決するためアプリケーションをコンテナでホスティングするよう改善を行いました。コンテナ化により、

  • ミドルウェアの変更が簡単に行えるようになった
  • デプロイがコンテナベースになりアトミックな変更になった
  • ログがCloudWatch Logsに連携され、簡単に確認・集計できるようになった
  • プルリクエストごとの環境(Edge環境)が作成できるようになった

など開発体験が大幅に向上しました。取り組みの詳細はSREチーム伊藤のPHPカンファレンス小田原2024での登壇資料にも書かれておりますので是非ご参照ください!

speakerdeck.com

PHPとLaravelのアップグレード

保育士人材バンクのサイトはPHP・Laravelで実装されており、これらのアップグレードを行いました。アップグレードガイドの確認をしつつ、手動テスト・自動テスト・PHPStanによる静的解析・Rectorによる自動リファクタリングを行い、不具合を検知したりアプリケーションコードを修正しました。社内でも同バージョンへのアップグレードを行ったアプリケーションがあったため、知見を聞いて回ったりしました。

前日に急いで聞き回っている図

Cookieの暗号化・復号化のロジックが変わる変更で一部影響がありましたが、特に大きな不具合なくリリースができました。アップグレードにより、PHPやLaravelの新しい機能を利用できるようになり開発体験が向上しました。特にmatch式、Enum、Constructor Property Promotion、readonly の機能は積極的に活用しており、より保守性の高いコードを書けるようになりました。

GitHub ActionsによるCI/CD導入

コンテナ化に伴い、GitHub Actionsで ecspresso を使って自動デプロイを行っています。 また、GitHub ActionsによるCIも導入し、プッシュ時に以下の処理を動かしています。

  • Laravel Pint, Biome を使ったフォーマットチェック
  • PHPStanによる静的解析
  • PHPUnitによる自動テスト・カバレッジ集計
  • technote-space/assign-author を使ったプルリクエストの自動アサイン

以前は手動テストやレビューで品質保証することが多かったのですが、CI導入によりデグレ防止やコード整形など一定の品質水準を保つことができています。

Laravel Pint導入に伴い既存ファイルを一括変更した図

Datadog APMの導入

以前はアプリケーションのパフォーマンス計測ツールを導入していなかったため、ボトルネックがどこにあるのかがわからず、パフォーマンス改善戦略を立てることができませんでした。また、パフォーマンスチューニング後の効果測定も難しい状況でした。これらの課題を解決するため、Datadog APMを導入しました。

Datadog APMによりN+1クエリやindexが効いていないクエリなどのボトルネックがわかっただけではなく、アクセス数が多いエンドポイントや、メルマガなどの施策によりスパイクしやすいエンドポイントなどアクセス傾向を把握することができました。 リリース前後のレイテンシ変動が可視化されたため、効果測定も容易になりました。

N+1対策の例。7.5s => 180msと40倍の改善!

自動テスト拡充

PHP・Laravelのアップグレードやリファクタリングを安全に行うため、自動テストを拡充しました。4〜5ファイルほどしか無かったテストコードが、今では100ファイル以上、カバレッジも70%を越えました。PHP・Laravelアップグレードなど大規模な変更を行うときも自動テストにより不具合を検知するなど、自動テストの効果を実感しています。

octocovでカバレッジ集計しています。Code To Test Ratioはこれから頑張る!

PHPStanの導入

前述の通り、静的解析ツールとしてPHPStanを導入しています。未定義変数の参照、use漏れ、クラス名・メソッド名間違い、引数の過不足・型間違いなどの不具合を検知してくれるため、堅牢なコードを書くのにとても便利です。

現在はPHPStanをレベル6で運用しています。レベル6は引数や戻り値の型付けが必須なため ignoreErrors で対応しているのが現状なのですが、一部Rectorを使った自動型付けを行い型エラーを解消していたりします。

OPcacheの有効化

OPcacheが無効だったため有効にし、全体で200〜300ms程度のレスポンス改善を行いました。

16:00ごろに適用して全ページでレスポンス改善した図

パフォーマンス向上だけではなく、CPU・メモリ負荷も下がりインフラコストも抑えられています。ちなみに、PHPのバージョンによってはJITを有効化するとセグメンテーションフォルトが発生する事象があったため、安全のためJITは無効化しています。

アプリケーションログの改善

コンテナ化に伴い、ログをCloudWatch Logsに連携するようになったので検索性を向上するために構造化ログ(JSONL)にしました。具体的には以下のようにMonoLogのフォーマッタ・プロセッサーを設定しています。

<?php
class CustomSettingApply
{
    public function __invoke(Logger $logger): void
    {
        $introspectionProcessor = new IntrospectionProcessor(Level::Debug, ['Illuminate\\']);
        $webProcessor = new WebProcessor();

        $logger->pushProcessor(new UidProcessor());
        $logger->pushProcessor($introspectionProcessor);
        $logger->pushProcessor($webProcessor);
        $logger->pushProcessor(function (LogRecord $record): LogRecord {
            $record['extra']['platform'] = 'laravel';

            return $record;
        });

        foreach ($logger->getHandlers() as $handler) {
            if ($handler instanceof StreamHandler) {
                $handler->setFormatter(new JsonFormatter());
            }
        }
    }
}

JSONLのフォーマット以外の処理だと以下のような情報も入れ、検索性やオブザーバビリティを上げる試みも行っています。

  • UidProcessorでリクエストIDを払い出し
  • IntrospectionProcessorでログが吐かれたファイル名・クラス名・行番号などを記録
  • WebProcessorでwebリクエスト関連の情報を付与

Viteを導入

以前はSCSSのビルドを各自ローカルで実行し、ビルド物であるCSSをgitにコミットしてrsyncでリリースしていました。この方法だと、元のSCSSだけでなくビルド物をコミットする必要があるため、ビルド物のコンフリクトが多発していました。そのため、ビルドツールである Vite を導入し、開発時のHotReloadやデプロイ時の自動ビルドができるようにしました。これにより手動ビルドやコミット、コンフリクト解消の手間がなくなりました。

また、SCSSだけではなく一部のJavaScriptもViteでビルドしています。ビルドツールを入れることでライブラリのバージョン管理やキャッシュバスティングもやりやすくなりました。

不要コード・パッケージの削除

保守性を上げるために不要なコードは随時削除していきました。結果、40万行のコードを削除しました。

4400ファイル、30万行の大削除の図

CSS、JS、IMGの不要ファイルがほとんどで、grepしたりアクセスログを見て泥臭く削除していきました。PHPに関してはPHPStan、Rectorなどのツールを使って削除対象を特定したり、明らかに使って無さそうなwebエンドポイント・コマンドを逐次ヒアリングして削除していきました。PHPの削除に関しては、前述の静的解析や自動テスト拡充により誤って削除することもほとんど無かったです。

Renovateによる定期アップグレード

依存ライブラリのアップグレードを定期的に行うと、ライブラリの変更量が減り影響範囲が読みやすくなったり、最新機能を利用できるようになるなど開発体験を上げることができます。そのため、アップグレードを定期的に行う仕組みとして Renovate を導入しました。Renovateはアップグレードのプルリクエストを1つにまとめてくれるなど小回りの効く設定を入れられるのが運用上のメリットです。今は週次の当番制にしてRenovateが作ったPRを担当者がリリースするようにしています。

振り返り(スプリントレビュー)の実施

2週に1回、振り返り会を実施するようにしました。エンジニアだけではなくマーケティングチームの方にも参加いただき

  • よかったこと・続けたいこと
  • Thank you!!感謝!!
  • やってわかったこと・気付き
  • これどうする?これは変えていきたい

の4つに分けて付箋を貼っていき発表しています。

個人的には「Thank you!!感謝!!」のところが良いと思っていて、エンジニア間だけではなくマーケティングチーム・エンジニア間で改めて感謝を伝える場として、とても良い振り返り会になっていると感じています。

まとめ

保育士人材バンクでの技術基盤改善の取り組みについて紹介しました。本記事では技術基盤の改善を中心に紹介しましたが、攻めの施策開発もそれ以上にたくさん行っています。これまで攻めの施策をしっかりやってきた、やっているからこそ守りの施策の意義が出ると思っています。改めて保育士人材バンクに携わっている、携わってきたすべての方々に感謝しつつ、今後もこれまで以上にエンジニアリングで事業を加速させていき、より社会貢献ができていければと考えています。