NJSSの技術スタック大公開!〜なくてはならないサービスを目指して〜
こんにちは!
NJSS 事業本部のエンジニアの栗原と申します。
私は2018年夏にうるるに入社してから現在に至るまで NJSS のサーバーサイドエンジニアとして、 Web 開発とインフラ構築を中心に従事しており、現在は複数ある開発チームのいちリーダーを務めております。
最近はありがたいことに面接やカジュアル面談の場面が増えてきており、候補者の方からたくさんご質問をいただきます。
その中でもよくいただくのが「どんなフレームワークを使用しているのか?」「どんなインフラで組んでいるのか?」の2つです(参画するプロダクトの構成はやっぱり気になりますよね!)
そこで今回は2021年7月にリニューアルを実施した NJSS のインフラ構成をご紹介いたします!
NJSSとは?
NJSS(エヌジェス)は「入札情報速報サービス」の略で、あらゆる省庁・自治体が公示している入札情報の検索・閲覧とタスク管理がWebページ上で完結できるサービスです。
さまざまな機能を提供しておりますが、中でも NJSS が重要視しているポイントは以下の3点です。
- 各自治体・機関から上がってくる入札情報を早く、漏れなく登録できていること
- 累計約2,000万件(2022/8/3時点)の入札情報に対してユーザーがストレスなく検索できること
- 早朝に送る新着案件メール(速報メール)を遅延なく確実に配信できること
NJSS のサービス提供をする上ではこれらを実現できるアーキテクチャである必要があります。
3つのドメイン ~2つのサービスドメインと中間ドメイン~
それでは本題…と言いたいところですが、本題を語る前にまずは NJSS の全体の流れと3つのドメインを理解する必要があります。
NJSS は Web クローラーとクラウドワーカーを活用して案件情報を収集し、閲覧しやすい形に情報を整理した上でユーザーにお届けしております。
つまり、入札情報を「溜める為のサービス(ワーカードメイン)」と「見せる為のサービス(ユーザードメイン)」の2つが存在し、アプリケーションもそれぞれが独立して稼働しております。これに「検索機能を集約した中間ドメイン(検索ドメイン)」を加えた計3つのドメインによって構成されております。
※もともとは1つのモノリシックサービスだったのですが、リニューアルの際に分割を実施しました。なぜ分割したのかはこれだけで記事1本書けそうなのでそちらはまたの機会に!
アーキテクチャ全体
ドメイン構成を踏まえ、いよいよ本題です。まずは全体の構成からどうぞ。
ほぼすべてをAWSで構築しており、できる限りフルマネージドなインフラ構成になるように構築しています。これによりインフラ監視工数を最小化し、アプリケーションエンジニアが開発に集中できる環境を実現しております。
また、ローカル開発環境構築の容易性が高いのも特徴です。
基本的には docker-compose コマンド1発または yarn コマンドで必要な環境が立ち上がり半日もあれば環境構築が完了するので新規参画者もスムーズに開発に参画できます。
それでは各種詳細を見ていきましょう。
アプリケーションインフラ
Amazon ECS (Fargate)
API, Batch, QueueWorker などのサーバーサイドアプリケーションは一部を除き ECS Fargate で動作しております。これによりインフラ管理工数が最小限化できたことに加え、ローカルでも Docker を使用することで本番と同等の手順で開発・デバッグ可能なメリットを享受できております。
Amazon RDS (Aurora)
データベースの永続化機構としてRDS Auroraを採用しております。死活監視に加え、データベース容量の上限管理もしなくていいのが魅力的です。これにより日常的な監視はCPU、Memory使用率、スロークエリなどに限定することができております。
Amazon ElastiCache (Redis)
Cacheサーバーとして Redis を使用しております。Memcached との選択だったのですが、冗長構成が組める Redis を採用しました。(ただし色々あって現在は単一構成となっております。。。)
Amazon SQS
非同期処理を扱う処理については特に複雑な処理を扱うケースがなかったことから、癖が少なくAWS上で完結できるSQSを採用しました。リニューアル後から SQS 起因での大きなトラブルは起きておらず、非常に安定して運用できております。
Amazon CloudFront
ワーカードメイン限定ではありますが、フロントの機構にCloudFrontを採用しました。これによりフロントはコンテナ管理からも解放され、インフラ管理工数の圧縮に繋がりました。
ユーザードメインはSEOの観点からSSR(サーバーサイドレンダリング)を採用した背景からCloudFrontは採用せず、ECS上での動作となっております。
AWS WAF
悪意のある攻撃からアプリケーションを守るために、外界が繋がる部分においては WAF を使用しております。一度設定してしまえばルールに従いフルマネージドで守ってくれるので安心感があります。
Amazon OpenSearch Service
NJSSの検索機能における検索エンジンは OpenSearch を採用しております。
リニューアル前はSolr on EC2で運用しておりましたが、コンテナ運用が当たり前になってきた時代においてEC2サーバーの管理や死活監視に煩わしさを感じておりました。
そこでフルマネージドかつ辞書ファイル管理を両立できる Amazon ElasticSearch Service(※当時) を採用しました。
検索エンジンの具体的な検討を開始した2020年初夏に、ちょうど Amazon ElasticSearch Service が辞書ファイルのサポートを開始したことに運命を感じたのは今でも忘れることができません笑(※当時は検索エンジンと辞書ファイル管理を両立するフルマネージドサービスはほぼ存在しませんでした)
リニューアルリリース後も継続的にアップデートを実施し、ElasticSearch v7.4 から OpenSearch v1.0 にバージョンアップを行なっております。
SendGrid
メール送信サービスはSendGirdを使用しており、アプリケーションインフラ関連で唯一、AWS 外のサービスです。
AWS にも Amazon SES というメール送信サービスがありますが、バウンス管理を怠るとサービスの利用停止になるリスクがあり、これを回避するには常にバウンスレートを監視する監視工数が発生します。メール送信をサービスの主軸としている NJSS では上記リスクは無視できませんでした。
そのため自動でバウンス管理が出来る SendGird を採用することでバウンスレート監視工数の大幅な圧縮に成功し、エンジニアがメールの送信率を殆ど気にしなくてよい環境を作れています!
IaC関連
Terraform
AWS で構築しているインフラは Terraform で管理しております。VPC ピアリング接続など管理が難しいものなどは一部手動対応が残っている部分もありますが、ほとんどコード管理できており、ステージング・本番環境などの各種環境反映作業のミス防止やインフラの可視化につながっております。
CI/CD
CircleCI
CI/CD は CircleCI で組んでおります。テストの実行、API ドキュメントの生成、デプロイの実行など、自動化できそうなものは大体 CircleCI に任せています。一度構築できればこれらの作業がブランチワークだけで完結する為、エンジニアの開発工数削減につながっております。
監視関連
AWS Security Hub
AWS アカウントに構築されているリソースに対して、AWS がセキュリティリスクがないかをチェック&スコア化してくれるサービスです。IaC でインフラ構築している為、構築過程で事故るリスクは軽減できているものの、万が一リスクのある構築をしたとしてもすぐに気づけるような仕組みを導入しております。
Amazon GuardDuty
AWS アカウントに構築したリソースに対し、悪意あるアクティビティが発生していないかを検知してくれるサービスです。アプリケーションの安全性を高める為に導入をしております。
Datadog
基本的なログは Amazon CloudWatch で監視しているのですが、さまざまなメトリクスを俯瞰してみる際はインターフェースが優れている Datadog を使用しております。
また、ユーザー行動を追うことができる強力なツールが内蔵されている為、エラー報告を受けた際の事象確認にも使用しており、スピーディなバグ修正やアプリ改善に役立てております。
言語・フレームワーク
うるるは数年前まで PHP エンジニアが非常に多く、リニューアル前は PHP フレームワークを用いた MVC モデルで動作していたのですが、フロント・バックエンド分離型開発に切り替えました。
エンジニアの適性を見てバックエンドは PHP を継続採用したのですが、フロントは現在も主流である TypeScript を採用しました。
PHP + Lumen (ユーザードメイン)
ユーザードメインの API・バッチサーバは PHP7.4 と Lumen を採用しております。
ユーザードメインはより高速な API レスポンスを追求するために軽量版 Laravel とも言われる Lumen を採用しました。これにより Laravel とほとんど同じ開発感覚を維持しながらも API のスピードアップにも貢献しました。
現在は PHP7.4 で動いているのですが、2022年秋に EOL を迎える為、バージョンアップを計画中です。
PHP + Laravel (ワーカードメイン)
ワーカー向けサービスの API・バッチサーバは PHP8.1 と Laravel9 を採用しております。
リニューアルリリース時は PHP7.3+Laravel6 だったのですが、段階的にバージョンアップを重ね、2022年の夏に PHP8.1+Laravel9 にバージョンアップが完了しております。
今後は Laravel Octone への対応など、ユーザー体験をあげるために先進的な技術への挑戦を計画しております。
Vue.js + Nuxt.js + TypeScript (ワーカードメイン / ユーザードメイン)
フロントは当時主流になりつつあった Vue.js + Nuxt.js の組み合わせベースに TypeScript にて型の恩恵を受けられる形を採用しました。
ワーカードメインは UI の制約が比較的緩いことから UI フレームワークとして Vuetify を使用することで開発工数を圧縮していたり、ユーザードメインはちょうど世の中に知見が溜まってきた Vue Conposition API に挑戦しつつ SSR(サーバーサイドレンダリング)に対応するなど、同じフレームワークを採用している中でもドメイン特性に合わせた調整が入っております。
Golang + Echo (検索ドメイン)
検索ドメインはワーカー・ユーザードメイン双方からアクセスされることから永続化層の交換のリスクが非常に大きいことが課題でした。そこでマイクロサービスの考え方に則り、 API サーバを挟むことで永続化層の交換性を向上させた単独のドメインとして構築しております。
処理速度が求められたので PHP よりも高速に動作する言語を採択したかった背景があり、その中でも扱いやすかった Golang を採用しました。勇気のいる決断でしたが、開発難度・範囲が比較的小さいドメインだったことや、Golang に意欲的だったメンバーがいたことも今回の挑戦に踏み切れた大きな要因でした。
同じような実装をした際に従来の PHP + Laravel と比較して約2〜3倍程度早いレスポンスを実現できたので Golang を採用した価値は大いにあったと実感しております。
まとめ
いかがだったでしょうか。NJSS には複数のサービスドメインがあり、それぞれで開発の特色が異なることがご理解いただけたのではないでしょうか。
入札は年間約20兆円以上の大きなマーケットです。そこで公示されている案件数や機関数は、数多く存在しています。しかしながら、入札参加参入企業数はまだ少なく、入札マーケットを今後より活況させていくためにも、NJSSというプロダクトの成長は必須となりますので、今後もさらなるサービスの改善・技術研磨に努めていきます。
この記事では語れなかった Web クローラーのことや、別のマイクロサービスなど、まだまだお伝えしたいことはたくさんあるのですが、それはまた別の機会にお伝えできればと思います!そして現在もさまざまな施策が走っており、こちらもいつの日かお伝えできればと考えております。
うるるでは「なくてはならないサービス」を目指して一緒に挑戦いただけるエンジニアを絶賛募集中です!
もしも少しでもご興味を持っていただけましたら、是非カジュアル面談からお気軽にご連絡ください。ここでは記載できなかったマイクロサービスや今後の施策など、より具体的なお話が出来ると思います!
▼カジュアル面談やご応募はこちらから