CROOZ TECH BLOG

~読んだらわかるSHOPLISTの裏側~

CROOZ TECH BLOGとはクルーズ株式会社の開発チームが技術について共有するブログです
開発の中での発見や知識を広めてオモシロカッコイイ〇〇をツクリます。

SHOPLISTの脱レガシーシステム②(AWS のインフラアーキテクチャを見直した話)

こんにちは。クルーズ株式会社CTOの鈴木です。

今回はSHOPLISTにおけるAWS のインフラアーキテクチャを見直した話です。

以前、「SHOPLISTのシステムをモダンなアーキテクチャに変えようとしたら予想以上に闇が深かった話」でも記載のとおり、SHOPLISTのインフラ環境はオンプレミスの環境を2014にクラウド移行したものなのですが、当時は時間的な制約や、十分にAWSに関する知見なさなどにより、オンプレ環境をとりあえずクラウドに移行した状態で、その後ElastiCache やlambda、API GatewayといったEC2以外のサービスも徐々に導入しているものの、基本的には物理機器が「仮想サーバ」と「仮想ネットワーク」に置き換わっているような状況でした。

今回OS・PHPのバージョンアップに合わせて、インフラ設計の見直しを行っており具体的にどのような変更を行っていったかについて共有していこうと思います。

 

SHOPLISTをアーキテクチャの面から見た課題


サポート切れOSなどの話もありますが、全体のインフラアーキテクチャとして考えた場合以下の課題が存在していました。

1.Web/DBなどサービス稼働に必要な主要サーバがSingle-AZ構成

当然のこととしてWebサーバは冗長化され、DBもMaster-Slave構成にはなっているものの、Multi-AZ構成にはなっておらず、ゾーン障害に対する障害耐性が低い状態でした。

2.サーバの手動構築&追加が必要(投入までに時間がかかる)

当時は、例えばメガセール(SHOPLISTで開催している大規模セールイベント)が開催される1週間前に、最近作成したEC2のイメージから管理コンソール上で手動でインスタンスを作成し、BalancerのTarget Groupに追加する運用になっていました。

なのでメガセールのように計画されているセールについてはまだ頑張れば準備できるのですが、セール開始時に計画を上回るリクエストが来たなどといった場合、インスタンスの構築と追加に早くても約30分ほどかかっている状況で、柔軟にスケールするインフラ基盤ではない状況でした。

3.Webサーバ追加時にGIPの更新漏れの発生(オペミスを誘発しやすい設計)

SHOPLISTでは決済代行会社をはじめとする様々なシステム連携を行っており、Webサーバから各社のサーバに対しAPI通信を行っているのですが、通信元IPを制限している連携先サーバも当然あります。

通信経路としては当社Webサーバ⇒外部API(IP制限あり)となるのですが、正確には当社の保有するElasticIPに対するIP制限であるため、手動でインスタンスを追加した際に保有済みのElasticIPが枯渇して新規割り当てをした場合、そのIPでAPI通信ができない。またインスタンスの入れ替えを行った際にIPの紐づけミスがあり、API通信ができないといったようなオペミスを発生させやすいインフラ構成となっていました。

これはオンプレミスでサーバを運用していた時代の名残りで、当時Load BalancerがDSR形式で運用されており、戻りのパケットをBalancerを経由せず直接クライアントに返却するために各WebインスタンスはすべてGIPを持っていたため各種APIを呼び出す際の出口のIPが各WebサーバのGIPとなってしまっているだけです。

4.DBでかすぎ&スキーマを跨ぐ結合しすぎ問題

これはインフラというよりはアプリケーション実装の問題ではあるのですが、当時1インスタンスに7つのデータベース、データベース合計で約1,800のテーブル、そしてDB容量が約1.6TBという非常に大きな容量、そしてスキーマを跨ぐ結合処理が多く分散できない状況になっていました。

5.DBリクエストが多すぎてリクエスト課金のあるRDS for Auroraを採用するとインフラコストが一気に跳ね上がる問題

これはインフラというよりはアプリケーション設計の問題ではあるのですが、クエリ結果をRedisをはじめとするKVSにキャッシュしづらい要求仕様であったりキャッシュの実装が行われていなかったりなどの理由で、DBへのリクエスト数が多く、DBのデータサイズと相まってDBをマネージドサービスを採用しようとするとインフラコストが一気に増大するという問題に悩まされていました。

6.SSHでサーバに入らないと出来ないことが多い問題

各環境へのデプロイはgit コマンドがシェルスクリプト、ログ確認はtailfコマンド、ジョブスケジューリングはcrondで行っていたため、SSH接続を行わないと開発業務が行えない状態で、サーバにSSH接続できる人は特定小数ではあるものの、サーバ上で行えることが多いため、オペミスを減らす視点で見るとサーバにログインできるユーザをさらに狭めたいなと考えていました。

今回のレガシーシステム脱却のために設計として見直した点


具体的には以下の見直しを行っています。

1.EC2インスタンスのMulti-AZ化する

Multi-AZ構成であれば大丈夫なのかという点については議論はあるものの、サービスの可用性上げれる選択肢があるのに実現できていない点については課題であり、まずはMulti-AZ構成に変更する。

2.AWS Auto Scaling を使いサーバ増減の柔軟さを向上させる

手動でのインスタンス構築をせず、管理コンソール上の設定のみでインスタンス構築とサービス投入までを自動化し、急なインスタンス追加や縮退にも耐えうる設計とする。

3.WebサーバにGIPを持たせない

各WebサーバごとにそもそもGIPを持たせなければ、各種APIとの通信はGatewayのIPに集約されるため、各WebサーバにGIPを持たせることをやめる。

4.デプロイをGitlab CI化させる

各サーバにSSHログインさせないための施策。Gitlab Runner経由でデプロイさせることで、Gitコマンドや、シェルコマンドを各サーバで実行しなくても済むようにする。

5.Cloudwatch Logsを使ってログを集約する

各サーバにSSHログインさせないための施策。各サーバにログインしtailでログ収集しなくてもAWS 管理コンソール上でログの閲覧ができるようにする

6.DBの系統を分ける

エンジニアのリファクタリング作業に依存する部分は多いけど、サービス上での参照頻度に応じてDBの系統を分け独立したインスタンスにデータを格納することで各インスタンスの容量の削減とIO分散を行う。

7.ジョブ管理ツールを導入し、cronを外出しする

各サーバにSSHログインさせないための施策。cron設定を各インスタンス上で行わず、ジョブ管理ツールで一元管理する方式に変更する。

具体的な内容については各投稿記事に詳細を記載しておりますので、興味がありましたら見てみてください。

今回意図的に見送ったこと


RDSの導入については見直しの中で実施したかったのですが、見送りました。

理由としては、RDS導入の道のりとして、まずは数回のメンテナンスに分けて不要データのDeleteを行い、DBMSのバージョン更新時のデータ移行を定められたメンテナンス時間内に余裕をもって完了できる状況にし、新しく構築された新バージョンのDBMSにデータ移行を行い、さらにそこからDBへのリクエスト数を減らすためにプログラムが発行しているSQLを把握し、キャッシュ化できるものと出来ないものを切り分け、必要日応じてデータの持ち方を変えて初めて切り替えが可能となるというあまりにも壮大な話になってしまうためです。

なので今回はまずはデータ容量を減らして、DBMSの乗っているインスタンスの世代更新と、DBMSのバージョンアップまでをスコープとしました。

いつか必ずリベンジします。

-------------------------------------------------------------------------------------------------

※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした脆弱性リスクへの対処は完了しております。