TECH PLAY

CROOZ.inc

CROOZ.inc の技術ブログ

55

こんにちは。クルーズ株式会社CTOの鈴木です。 今回は脱 レガシーシステム の話から少しそれるのですが、ActiveDirectory のログインユーザ情報を使って AWS Mamagement Console にSingle Sign Onできるようにした話をしようと思います。 経緯 以前より AWS Management Console のアカウントをどのように管理すべきかについては悩みの種で、従業員やパートナー企業ごとにIAMアカウントを発行する方法だと、退職時や業務委託期間終了時にアカウントを無効化する運用を行わないとならず、だからといって共通のIAMアカウントを複数名で使うにしても退職時や業務委託期間終了時にIAMアカウントのパスワードを変更する運用を行うにしても今度はパスワード変更を定期的に行わないとならず、すでに退職時にアカウントの停止を行う運用のある Active Directory とうまく連携して行う方法はないものかと考えて、EC2上すでに存在していた Active Directory サーバ(ADDS構築済み)にADFSを構築し、 AWS Mansgement Consoleと連携させる方法を検証していましたが、設定が複雑なこと、ADDS とADFSを同一 インスタンス で動作させるとそれなりのサーバリソースが必要となり、 インスタンス サイズを上げてないと運用が難しい状況になったため、もっと簡単に実現する方法を調べていたところ AWS SSOがあったので今回は AWS SSOにて認証を実現してみました。 ADFSを利用した構成についてはこちらの記事が参考になります。 dev.classmethod.jp   システム構成 前提として当社のActiveDirectory の構成を説明すると、 AWS 上のEC2 インスタンス として1台、残り2台が在籍する従業員数が比較的多い オフィスビル 内に分散されて配置されていて、各 オフィスビル と AWS 間で拠点間 VPN が構築されています。 当時 AWS SSOは最寄りは シンガポール リージョンしか存在しなかったため、まずは シンガポール リージョンの VPC と、EC2上のActiveDirectoryのある東京リージョン間を VPC Peeringで接続し、 シンガポール リージョンにAD Connectorを配置します。 AWS SSOのIDソースにAD Connectorを指定し、 AWS Management Consoleへのログインを AWS SSOのユーザポータルURLから行うことで、 AWS SSOがADに問合せにいってユーザとユーザが所属するADグループと AWS アカウントと AWS アカウントの表示権限の紐づけを行い、そのユーザの持つ権限で制限された状態で AWS Management Console にログインするというのが一連の仕組みです。 すごくざっくりとですが図で書くとこんな構成です。   追記:現在だと AWS SSOは東京リージョンでもサービスインしているためAD Connectorおよび AWS SSOは シンガポール リージョンに作る必要はないです。 Active Directory 上での設定 実際に AWS SSOでのアカウントの紐づけはADアカウントもしくはADグループのどちらでも可能なので、権限ロール単位でAD グループを作成し、そこにADアカウントを紐づけておいた方が運用が容易です。 AWS SSOの ディレクト リ設定 AD Connector構築後、 AWS SSOの初回設定時に ディレクト リの管理画面で Microsoft AD ディレクト リを選択してください。選択するとしたのプルダウンに構築したAD Connectorが出てくるので選択して設定を完了させます AWS SSOの AWS アカウント連携設定 ここでの設定は「アクセス権限セット」と「 AWS アカウントとADアカウントまたはADグループとアクセス権限セット関連付け」を行います。 アクセス権限セットはIAMポリシーのようなもので紐づく「ADアカウントまたはADグループ」で操作できる権限が設定できます。 アクセス権限セットは標準でいくつか用意されていますが自分で作成することも可能で、その場合は JSON でポリシーを記述していきます。 その後、 AWS アカウントを選択してADアカウントまたはADグループとアクセス権限セットを関連付ければ完了です。 運用コスト的な話 詳細には計算していないのですが従来構成とそこまで大きく変わらないです。ざっくりとした費用内訳で見ると約100USDが月あたり増えています。 ・AD Connector 費用 :約60USD/月(新規発生) ・ VPC Peering費用  :約30~40USD/月(新規発生) ただ、一方でEC2上のADサーバのCPU使用率が激減したことで インスタンス サイズを1サイズ落としていて、それの削減効果がだいたい100USDくらいあります。なのでトータルだとそこまで変わらなかったです。 ただ設定がはるかに楽なのですでにADがあって AWS Management Console をADで認証させたい場合はこの方法をお勧めします。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 今回はこの一連のOS/ ミドルウェア バージョンアップの中の残タスクとなっているWebおよびバッチ用の インスタンス の対応の話をしたいと思います。 以前の投稿「 脱レガシーシステム⑥(DBインスタンスのOS/MariaDBバージョンを最新安定版にあげた話) 」で記載のとおり、2020年の10月にDBまではOS/ ミドルウェア バージョンアップが完了して残るはPHP7.4周りのみとなっています。 前提 2020年の10月末からの約1週間がメガセールで、その期間はエンジニアもメガセールに関連する対応、運用監視などで手が空けれないため、メガセール完了後からの作業再開となりました。 とはいっても、「 脱レガシーシステム③(OS/ミドルウェアのバージョンアップを計画した話) 」のバージョンアップの進め方で記載のとおり、PHP7動作確認用のブランチへのコミットと動作確認はすでに進めていて、メインの作業は機能 デバッグ がメインでした。 デバッグ については進捗が悪く計画より2週間遅れている状況でした。 デバッグ の遅れの要因として大きかったのは、すべての画面やバッチについて存在しておらず、稀にしか使わない画面やバッチ、使われ方が多彩すぎる(機能が多すぎる)画面などがあって、 デバッグ を行った際に想定するシナリオがよくわからないことでした。 これは仕様書が管理されていないという別の問題で、まさに仕様書をかき集めて、不足しているものをリバース作成するプロジェクトを開発部門で実施しています。 ただ、現時点で現在進行中のプロジェクトの成果物を当てにしても意味がないので、ログインや決済、商品選択、検索などエンドユーザが使う主要機の未過去の デバッグ 項目書に従って デバッグ 、それ以外はフリー デバッグ を依頼し、表示崩れやデータがおかしい箇所を見つけたら開発者に報告してもらう。開発者は デバッグ 報告及びログ上に出るFatalエラーをマストでつぶすという形で進めました。 バッチについてはテスト実行してある程度不具合が出ないことさえ確認していれば、万一PHP7移行後に仮に処理が失敗してもそこでログ採取して修正してもリトライできるので楽観的といえば楽観的ですがある程度まで事前に潰せたら、PHP7移行後に修正という方針としました。 メンテナンス中に発生した問題 デバッグ 、切り替えの当日スケジュール、作業手順のレビューが終了したタイミングで2020年の11月の4週に4回目のメンテナンスを実施しました。 今回は作業手順としては簡単で、あらかじめ構築しておいた新環境用のEC2 インスタンス (AmazonLinux2/PHP7.4)を新環境用に用意したALBに紐づけて準備しておき、 DNS の設定として紐づけるALBを変更する、バッチサーバについても旧 インスタンス のcronを無効化して新 インスタンス のcronをゆうこうにするのみだったため、メンテナンスに入ってから切り替えて、メンテナンスを開ける前の最終動作確認までは極めて順調でした。 問題が発生したのは最終動作確認時で、以下の不具合が出ていました。 1.アプリでログインできない端末がある 原因が分からず3時間ほど様々な仮説を立てては切り分けを行うことを繰り返したのですが結局原因が分からず、ただなぜか時間がたつにつれて次第にこの問題は解消され、最終的には特定の従業員の端末1台でしか発生しない状況になっていました。 挙動から見て、なにかしらのキャッシュ影響のようだと考えられるのですが特定従業員の端末以外でこの問題が発生しておらず、明らかに一般ユーザとは異なる使い方をしている端末、会員アカウントなので、今回のメンテナンスとは別に端末やユーザアカウントの問題の可能性もあるため、今回のメンテナンスに起因した不具合ではないと結論付けてこの問題についてはCloseという判断を行いました。 実際にメンテナンスが明けてから同じ事象が一般ユーザで発生していないためおそらく デバッグ で使っていた端末、会員アカウントに起因する問題だったようです。これはこれで問題で、 デバッグ 端末や デバッグ アカウントは一定期間過ぎたらリセットして通常のユーザに近い状態を作成しておかないと正しく デバッグ できなくなるので次回バージョンアップメンテナンスを行う前までには準備作業としてやることします。 2.決済が行えない 決済手段 が2つあった 結論、両方とも今回のバージョンアップ起因ではなかったです。 片方の 決済手段 については決済システムの仕様として IPアドレス 制限が実際には存在したが、そのことが社内資料に記載がなかったため、決済システムの管理画面上で IPアドレス を変更するというタスクが計画時点で漏れていたことが原因。 もう一方の 決済手段 については、これも決済システムの仕様で、つなぎ先サイト画面手ナンス中は決済が行えないような仕様となっていることが原因。 原因が判明したのは計画上でメンテナンスを開けなければならない時間だったため、この2つの 決済手段 のみ選択不可の状態でメンテナンスを開け、開けた後に動作確認が行えた 決済手段 ごとに決済を選択可能とし、最終的にはメンテナンス明けから1時間後にはすべての 決済手段 が利用可能な状態となり、一連の作業は終了しました。 メンテナンス終了後に発覚した問題 懸念していたとおり、 バッチ処理 周りで問題が発生しました。 具体的には CRM ツールでデータ連携できない問題が発生していました。 原因としては2点で、1つ目は リポジトリ で管理されていない野生のコードと設定ファイルが存在していて、それらのファイルが移行対象から漏れたこと、2つ目はバッチが生成する一時ファイルを格納するフォルダが移行後の インスタンス に存在していなかったことがそれぞれ原因でした。 前者は該当する旧 インスタンス から新たに リポジトリ 内に格納したうえで、移行後の インスタンス に移し、後者については作業 ディレクト リと パーミッション を設定し正常動作するようになりました。 上記以外で厄介な問題として、PHP7.4にバージョンアップ後に SQL を発行した際に意図したインデックスが使われない問題というのが発生していた増したが、これはPHP7.2以降でPDOでINT型をバインドする際の内部動作が変更になったことが原因と後日分かり修正を実施し解消しました。 blog.tokumaru.org また今回のバージョンアップは社内でもそれなりに大ごとだったため、各営業部門の従業員がメンテナンスが明けてから本番環境で変なところがないかを本当に細かく見てくれたおかげで、PHP7.4へのバージョンアップとは無関係で元々の環境から不具合を起こしていた個所を2か所ほど見つけてくれ、そこも併せて修正対応を実施しました。 まとめ 今回のWeb/バッチ インスタンス のバージョンアップについては、バージョンアップに伴う技術的な話よりも、仕様の文書化。ナレッジ化ができていないことによる問題のほうが多かったと感じています。 リポジトリ 管理外のコードやPDOの内部動作の仕様変更に関わる部分は今回のタイミングを機に直しましたでよいと思うのですが、仕様が把握できていなかった問題、特に決済システムや CRM ツールなどの外部システム連携周りの仕様が把握できていないことは致命的で、今回のメンテナンス中に発覚した仕様漏れについては社内の文書に更新し、今後の再発防止につなげていきたいと考えています。 ただ、全体通してはユーザ影響を最小限に抑えた形でOS/ ミドルウェア のバージョンアップができたのではないかと思っています。 PHP7.4へのバージョンアップ対応については本格的に動き出したのが9月下旬で、対応完了が11月下旬なので約2カ月間で PHP のメジャーバージョンを跨ぐバージョンアップを行ったことになります。 正直今回かなり強引な進め方をしましたが、この規模のサイトでこの期間で無事にバージョンアップ対応を行えたのは以前のDBのバージョンアップ対応の際にも触れたとおり当社のエンジニアの技術的なスキルの高さと圧倒的な問題解決能力に本当に助けられてのことだと思っています。 今回のこのWeb インスタンス 及びバッチ インスタンス のOS/ ミドルウェア のバージョンアップ完了をもって、今回のスコープで行おうとしていたインフラ的な レガシーシステム 脱却はほぼ完了しました。 まだ投稿記事としては作成していないのですが、「インフラ的な話として AWS Auto Scalingを利用した インスタンス 管理に切り替えた話」があり、次回投稿したいと思います。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 以前の投稿「 脱レガシーシステム①(何からどう進めるか問題)   」のどうすすめるか?の部分でも少し触れたのですが、当社ではシステム品質向上のため週に4時間を リファクタリング 作業に充てれる時間を設けています。 リファクタリング 作業に対する支援として、 PHP ログ、アプリケーションログを一箇所で見れるようにしたというのが今回の話です。 いままでの運用 基本的に、必要な時に必要なサーバに SSH で接続して Linux コマンドでごにょごにょしてみる。さすがにしんどいので一部のログについては、一か所のサーバに集約されてはいますが基本的には各サーバに SSH 接続してみるというやり方でした。 上記とは別に特定のキーワード、例えば当社独自で定義しているエラーコードなどがログに記録された場合、監視ツールがアプリケーションログ監視としてアラートをSlackに発報する仕組みがあり、重大なアプリケーションエラーについては通知が受けれるという仕組みでした。 なので、まず大前提として本番環境については調査の依頼や不具合がない限り、開発者は能動的にログファイルを見ない(重要なものについてはアラート通知で気づけるようにしていて、それ以外は見ていない)。ログ件数の増減などについてもモニタリングができている状態になっていませんでした。 もう一つのログに関する問題 ログにまつわる問題としてはもう一つ大きな問題があって、 PHP エラーログの設定でWarningを出さない設定がバッチなどの一部本番環境にされていたことです。 おそらくなのですが、 リファクタリング 作業を組織立って行う前の ソースコード 状態で1リク エス ト中にWarning/Deprecated/Noticeを含めると多い場合300件ほどのログが出ていたので、これが全部本番のログとして蓄積されるとログファイルも肥大化するし見る側も雑音になるので php .iniの設定で出さなくしていたんじゃないかなと思います。 ただ、本来の考え方でいうとNoticeを含むエラーログについては0が望ましと考えていて、今回は雑音になるかもしれないけど出力してCloudWatchLogsに保管してみるときにフィルタリングするという形にしました。 Cloud Watch Logs 導入の効果 いちいち SSH で各サーバに接続せず、集約されたデータをCloudWatchLogsで見れることは言うまでもないですが、Deprecatedがどこで何件起こっているのかが把握できたことです。開発環境、検証環境は社内 デバッグ 用なのですべてのユーザ 動線 のログが取得できるわけではなく、次回の PHP バージョンアップまでにここを明確に解決しないとまずい!というのが分かるようになったことは大きかったです。 また「今、件数どのくらいあるの?」や「急にこの ディレクト リでWarning増えてるんだけど、何かおかしいかもしれないからソースチェックしてみて」といった会話のように、エラーログ件数をモニタリングすることで、普段気づけないことを見つけ、 ソースコード の品質向上につながるのではないかと期待をしています。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 SHOPLISTの 脱レガシ―システム の話も10回目となりました。 今回はSHOPLISTのデプロイ方法を変更した話です。 今までのデプロイ方法 今までのデプロイ方法はかなり古典的で以下のやり方をしていました。 検証環境へのデプロイ 検証環境に SSH ログインし、gitのコマンド操作でリリース対象のブランチやタグを指定し、GitLab上からcheckout 本番環境へのデプロイ 検証環境に SSH ログインし、対象の ソースコード を本番に rsync 課題 今回デプロイ方法の変更を行うきっかけになる課題がいくつかありました。 ⓵リリースの ロールバック が手間であること リリース直後に不具合が出て切り戻そうとした時に、まず、検証環境に SSH ログインをし、前回リリース時点のタグを指定して ソースコード をcheckout後、本番環境に rsync を行う。実際には rsync のオプションで除外するための ソースコード を指定するファイルを指定して実行しているため、その除外ファイルのリストの確認などがリリースたびに発生するので、切り戻しの意思決定をしてから ロールバック を実行するまでの時間が長い状態になっている。 ② リポジトリ 上の特定リリースタグで指定されるコードの状態と実際に本番環境で動いているコードの状態が違うケースがある。 今までのデプロイ方法は rsync を実行する際に rsync 対象に含めないコードをまとめたファイルを指定して実行していたため、そのファイルに含まれるパスの ソースコード については本番サーバに rsync が実行されない。結果として リポジトリ 上のリリースタグで指定される状態と、本番のコード状態に差異が生まれてしまうケースがあった。 ③EC2 インスタンス の追加・縮退の都度 rsync 先の IPアドレス のメンテナンスを行う必要があり管理が煩雑 各種セールでEC2 インスタンス の台数が変更される都度 IPアドレス のメンテナンスを行う必要がある。 GitLab CI/CDで自動デプロイを実現する設計 Git上のブランチとして検証環境の ソースコード に対応するブランチ、本番環境の ソースコード に対応するブランチを作成しておき、それぞれのブランチに対してマージがされたタイミングでGitLabRunnerが起動し、Pipelineの各stageに定義されているジョブを実行します。 各ジョブ(dryrun、deploy、post_deploy)にはデプロイにおける必要な定義が記載されていて、各処理の実行を待って ソースコード のデプロイが完了する形です。 実際の開発はGitLab-Flowに従って実施されているため、 検証環境へのデプロイの場合は、featureブランチから検証用ブランチにMerge Requestが申請され、承認されたタイミングでマージが実行され、検証環境デプロイ用のパイプラインが実行。 本番環境へのデプロイの場合はfeatureブランチもしくはHotFixブランチから本番用ブランチにMerge Requestが申請され、承認されたタイミングでマージが実行され、本番環境デプロイ用のパイプラインが実行。 切り戻す際は切り戻す時点のコミット ハッシュ値 からHotFixブランチを作成し、あとは上記に記載する本番環境へのデプロイの流れと同じです。 デプロイ対象のEC2 インスタンス の特定は、ansibleがEC2 インスタンス 構築時に、用途に応じてタグをつける仕様となっており、そのタグに紐づくリストを AWS の API を利用して取得しています。 デプロイ方法を変更してみて 変更直後については、デプロイの仕方が大きく変わったため技術統括部への問い合わせが若干発生していましたが、その後問題なく運用できています。 また実際にリリース作業を行う現場のサーバサイドエンジニア数名に ヒアリ ングしたところ、概ね上々だったのでデプロイ方法変更としてはうまくいったと思っています。 当初懸念として、 rsync から比べるとデプロイ開始から完了までのトータル時間がかかるので、デプロイ時間が延びることで何か良くないこと(リリース中に不具合が発生した場合に中止できないとか、デプロイ中に、デプロイが完了している インスタンス と未完了の インスタンス ができ、両方にALBが振り分けてくるので大丈夫なのかとか)が起こるんじゃないかという漠然とした不安があったのですが、特段今のところは問題が出ていません。 この件については漠然とした不安レベルなので、具体的に問題が発生したら回避策を考えていきたいと思います。 ちなみに切り替えて約5カ月が経過していますが今のところ問題の発生はないです。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 今回は「脱レガシ―システム」シリーズとは少しずれてしまう話ですが、 ここ数カ月、SHOPLISTのOS/ ミドルウェア のバージョンアップを複数のメンテナンスに分けて行っていて その過程でメンテナンス画面の表示機能改修をプチ改修したという話をしようと思います。 改修を行うきっかけ 深夜メンテナンスの計画作業を行っていたと頃に、社内会議で SEO 担当者から「メンテナンス時の ステータスコード がいま200になっている。もしクロールされると正常ページとしてキャッシュされちゃうから503にしないとまずいです。」と初めて聞くことを言われました。 え?503ってService Temporarily Unavailable なんだけど…。ほんとに? サーバエンジニアだと、Webサーバが標準で返すあのエラー画面の印象が強すぎて俄かに信じがたいのですが調べた結果どうやらそうらしいです。 ステータスコード 503が適切な理由 複数のネット文献や SEO 会社の方が言っていることをまとめて要約すると ・503はクローラ的には一時的にサイトが利用できないので後でまたクロールしてくださいという意味になるらしい ・200だとクローラは正常ページとして認識してしまうためキャッシュ対象として扱ってしまう。 ・404だとページがないと判断になるのでページがなくなってモノとして認識してしまう。 だから 503が適切 ということらしいです。 要するにサーバエンジニアの視点でいうと5xxはサーバエラーという認識だからそんなものお客さんに返しちゃだめだしすぐサーバプログラム直さなきゃという発想なのですが、クローラのの視点だと503は一時的にサイトが利用できないという解釈になるらしいです。 なるほど!と思いながら当該箇所のソースの修正を行いました。 その時見つけた小さな問題 修正箇所を特定して直そうした ソースコード が以下なのですが header('HTTP/1.0 200 OK'); となってました。 さらっとHTTP プロトコル が1.0になってたんで修正しました。 歴史を感じます… こういうの含め直してかないとですね。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 今回も前回に引き続き リファクタリング ネタです。 前回の投稿「 リファクタリングの第一歩として不要ソースを削除して論理LOCを15%削減した話   」の背景の部分でも触れましたが、SHOPLISTで使用している フレームワーク はSHOPLISTがクルーズ株式会社から分社化する以前より共通で使用していたVenusという自社開発の独自 フレームワーク 上で実装されています。 なんで今このご時世に独自 フレームワーク なんて使っているんだ!!という指摘はごもっともなのですが、だからと言って今から独自 フレームワーク 上で動作しているすべての処理をすべて書き換えるというのはあまりにも非現実的な話なので、まずはSHOPLISTの運営に不要な共通共通処理の排除をできる限りの保守性の向上を今回の レガシーシステム 脱却の一連の活動の中で実施しました。 実際の作業は不要コードの削除と合わせて進めているので行っている内容に一部重複もあります。 実際に行ったこと SHOPLISTで使用していないコードの削除 これは前回の投稿「 リファクタリングの第一歩として不要ソースを削除して論理LOCを15%削減した話 」で触れているので割愛します。 公式コンテンツ固有の処理コードの削除 元々公式コンテンツを作成するのに使っていた フレームワーク だったため、共通処理の中に公式コンテンツ固有のものが入っています。 分かりやすい例でいうと「uid=NULLDOCOMO」で docomo の公式IDを取得する。 ガラケー の場合、キャリアに応じてContent-Typeを変更するヘッダーを送るなどです。 そもそもキャリア決済は行っていても、公式コンテンツではないので完全に無駄な処理でかつ無駄にデータを格納してしまっていたので共通処理から削除しました。 旧計測システム用の独自 アクセスログ 出力処理の削除 当時のCROOZには Compass という独自の簡易的な集計システムがあり、そのシステムに渡すためのログを生成してファイルに吐き出す処理やログファイル上でユーザを一位に特定するためのユニークキーを生成する処理などが残っていたため、共通処理から一式削除しました。 重複メソッドの親クラスへの移動 もうこれは、当たり前の話なので割愛します。 クラスの各メソッド、メンバ変数の公開レベルの見直し これももう当たり前なので割愛します。 プライベートメソッド、メンバ変数の名称の修正 明らかにやっている処理がメソッド名称から判断できないものがあり、このようなものについては今回一式直してしまいした。影響範囲がそのクラス内のみで、呼び出し元に迷惑をかけないからです。 フレームワーク の ディレクト リ構成を定義するdefineの見直し そもそもそれ定数定義する必要あるの?みたいなもの、例えば フレームワーク が指定する何かのライブラリ ディレクト リが定数で定義され、その定数は3つの定数と ディレクト リセパレータで構築されている。そして今回参照された3つの定数はほかのどこでも参照されていないようなものがいくつかありました。もうそういったものは 参照元 の3つの定数を削除して直接パスを文字列で指定するようにしました。 ソース中のToDo, WIPコメントのある個所は全部確認して対応する このようなコメントの周りは変数定義されて代入されているんだけどその変数がどこにも使われていないなど意味のない処理があり、内容を精査し不要なものはToDo、WIPというコメント含め削除、使っているものはコメントを書き直して保存するように修正しました。 メンテナンス画面のHTTPレスポンスコードとHTTPバージョンの修正 「 メンテナンス中に表示するページのレスポンスヘッダを修正した話 」として次回投稿します。 Frame、Controller内で呼び出される共通関数の粒度の統一 適切な表現が浮かばないのであいまいな言い方にはなってしまうのですが、処理の中で呼ばれる関数の粒度が異なり、処理の流れが非常に読みづらくなっていたのでメソッドアウトすべきものについては出して可読性がよくなるようソースの改修を行いました。 その他、細かい改修はいくつか行ったのですが大きな部分でいうと以上が行った内容です。 まずはできることから 今回は自分のできる部分として、自分が昔開発にかかわっていた自社独自 フレームワーク の リファクタリング 作業をおこないましたが、今後はサービス側、管理画面側も含め、直せるものを直していこうと思います。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 今回はSHOPLISTの リファクタリング 作業第一歩として、不要ソースを削除して論理LOCを15%削減した話をします。 論理LOCってなに? ・LOC = Lines of Codeのことで、平たく言えば行数です。 昭和の SIer っぽいですね(笑)。この時代だとコード行数で会話することはほぼ無いのですが、不要ソースの削除の話なので今回はわかりやすい指標としてLOCを使って話したいと思います。 ちなみに、 ・物理LOC = 単純な ソースコード の行数 ・論理LOC = コメントや空白行など 機械的 に省けるソース以外を省いた行数 という定義です。 詳細は Wikipeadia のLOC の説明を見てください。あくまでも ソースコード の規模を見る単位としてしか今回使わないので説明は割愛します。 不要な ソースコード を削除する目的 まず リファクタリング の第一歩として不要な ソースコード の削除を行うかについてですが大きく分けて2つの目的があります。 ①狭義でのデッドコードを排除する前処理として、雑音となりそうなファイルやまったく使っていない ソースコード の一部を先に消すことで、今後行う リファクタリング 作業の効率を上げること(可読性を下げる要因を少しでも排除してこと)。 ②未使用ライブラリであっても自動ロードの対象となっている ディレクト リ内のものは フレームワーク が実行時に読み込んでしまい、Webサーバのメモリを無駄に消費してしまうため、無駄なメモリ確保をやめてWebサーバの スループット を上げること。 ②はどちらかというとおまけで今回は主に①がメインの目的です。 今回の作業を行う背景として、現在SHOPLISTで使用している自社で開発した独自 フレームワーク がSHOPLISTの前にCROOZ株式会社で運営していた公式コンテンツ、ブログや SNS でも使われていたものを機能拡張したもので、当時のコンテンツ運営に必要ではあるもののSHOPLISTのシステム運用では不要なライブラリなどを含んで自動ロードしてしまっていること。そしてSHOPLISTの ソースコード においても、過去に運営していた姉妹サイトや、すでに終了した機能の ソースコード が リポジトリ のリリース対象ブランチ内に入っていることがあります。 今後の リファクタリング 作業では、定数、変数、関数レベルで未使用、重複を分析していくことになり、その作業の雑音となる不要な ソースコード の排除については一番初めに実施すべき内容なので今回行いました。 今回の不要ソースの削除にあたり、独自 フレームワーク の ソースコード 部分はCROOZ入社後携わっていた部分でありナレッジがあったため私が担当し、SHOPLISTの ソースコード 部分については、開発部の数名に リファクタリング 時間の中で対応を依頼しました。 実施結果 以下、実施前後のファイル数と論理LOCの比較です。 1.実施前 13,274ファイル、1,998,657論理LOC 2.実施後ファイル数 12,475ファイル、1,699,309論理LOC 3.前後比較 ・ファイル数:800ファイル、約6%分の削減 ・論理LOC :30万行、約15%分の削減 まとめ 実施してみて、 リファクタリング 作業が非常に行いやすくなったというのが所感です。 一方で、クラスファイルのメソッドの動的呼び出し箇所を不要なソースと誤って削除してしまい、その後の デバッグ で気づくなど、 機械的 に未使用な処理をさがして消すということが難しいケースも体験し、なかなか難しいと感じた部分もあります。 ただ、 リファクタリング で行う内容にもよるのですが、未使用の定数の検索や重複するクラスの把握する際に、今までは明確に使ってないソースであっても機械抽出で上がってきてしまうため雑音になっていたものがなくなったので本質的な処理の見直しに使える時間が増えるのは大きかったです。 今後は内部処理の見直し、特にDB呼び出しまわりに積極的に手を入れていこうと思います。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 DB インスタンス のOS/ MariaDB のバージョンアップの前段作業として5回目では「 DB容量を1.6TB⇒1.1TBに減らした話 」をしました。 ちなみにですがDB容量削減という意味ではまだまだ削減の余地はあるものの、プログラム面の改修も大幅に入れないといけないため上記のデータ削減が終わった段階でDB インスタンス のバージョンアップを先行させました。 またRDSの利用についても、まずはDB インスタンス へのリク エス トを減らすことが先なので今回のスコープでは見送りDB インスタンス のバージョンアップ後、Cahceの利用などによりDBへのリク エス トを減らしたのちの実施としました。 DB切り替えの大枠の流れ 以下の流れで切り替えを検討しました。 事前作業 ⓵リプレイス用の インスタンス (AmazonLinux2/MariaDB10.5.5)を切り替え後必要な台数分用意しておく ②既存 インスタンス の特定時点のDBバックアップ(dump)をリプレイス用 インスタンス にRestoreする。 ③既存 インスタンス のSlaveのうち、サービスから参照されていない インスタンス を レプリケーション 元として、リプレイス用の インスタンス のうちMasterとして使用する インスタンス に対し レプリケーション する。 ④リプレイス用の インスタンス のうちMasterとして使用する インスタンス から、残りのリプレイス用のDB インスタンス に対し レプリケーション を行う 整理すると、 既存(Slave)⇒リプレイス用(Slave兼Master)⇒リプレイス用(Slave) という状況を事前に設定しておき、 レプリケーション が追い付くのを待つ メンテナンス時の作業 ⑤既存 インスタンス のSlaveのうち、サービスから参照されていない インスタンス を レプリケーション 元として、リプレイス用の インスタンス のうちMasterとして使用する インスタンス に対して設定されている レプリケーション を止める。 ⑥既存Web、バッチ インスタンス のHAProxyに設定されているDBの向き先を既存DB インスタンス のMaster/Slaveから、リプレイス用の インスタンス のMaster/Slaveに切り替える。 挙動に問題がないことを確認後メンテナンスを終了して切り替え完了。 進める中で発生した問題 実際に進めている中でいくつか想定していない問題が発生しました。 DBの設定パラメータが、既存 インスタンス のSlave間で値が異なる これは想定外でした。当社だとSlaveとなっているDBに大きく2つあり、サービスから参照されているSlaveと、管理画面やバッチサーバから参照されているSlaveサーバ(社内ではAdminDBと呼んでいるもの)で設定パラメータが異なっていました。 確かに発行されるQueryの特性はサービスで使うSlaveと管理画面やバッチサーバから発行されるものとは全く異なるので設定パラメータが異なること自体は適切なのですが、それが分かる形でナレッジ化されていなかったためこのような問題が発生したのだと思います。 DB インスタンス 切り替え後に発生した問題 2020年10月の2週目に第3回目となるメンテナンスを行い、DB インスタンス を切り替えました。切り替え自体は非常にうまくいったのですが、その後いくつか問題が発生していていました。 旧DB インスタンス をプログラムが参照している箇所があった。 Web インスタンス からDB インスタンス への接続経路は Web インスタンス ( PHP )⇒Web インスタンス (HAProxy)⇒DB インスタンス という構成となっていてWeb インスタンス ( PHP )上だとMasterDBへの接続の場合は localhost :xxxxx、SlaveDBへの接続の場合は localhost :yyyyy(生きている複数のSlaveDB インスタンス に対して ラウンドロビン )という状況になっていて、直接DB インスタンス の IPアドレス :3306で接続する想定はなかったが、一部バッチサーバ内の PHP プログラムが直接DB インスタンス の IPアドレス を指定して移行前の旧DBのSlave インスタンス を参照していた。しかも バッチ処理 内でDBの接続文字列が上書きされていた。 実はこの問題が発覚したのは、移行後2週間以上経過したタイミングで、移行前の旧DB インスタンス はサービス上で問題が発生した際に即時切り戻せるように MariaDB が起動している状況だったためバッチとしては処理が正常に動作しているものの正しく動作していないという状況になっていて、問題が発覚したのは旧DB インスタンス を停止した際にバッチが失敗したためでした。 なので今後システムリプレイスなどでDB移行作業を計画されている方は手間でも移行前に現環境のDB インスタンス の IPアドレス をリスト化しソース上 grep かけたほうが良いです。見つからなければ取り越し苦労、見つけたらラッキーです。 バッチの実行速度が最大で2倍遅くなるものがあった。 これは理由がすぐに分かり、DBのSlave インスタンス がMulti-AZ配置になったためバッチ インスタンス が置かれているZoneと参照しているDB インスタンス が異なる場合に今までよりもレイテンシが下がるケースがあるためでした。 対策としてはバッチ インスタンス が参照するDBについてはHAProxy上でゾーン毎に接続定義を分け、バッチからは同じZone内のDBSlave インスタンス を参照するように変更しました。 バッチの処理中でクエリ処理が詰まる問題 調べたところ バッチ処理 内で CREATE TEMPORARY TABLEを実行してるケースでGAP Lockが発生していで、呼び出し方がCREATE TEMPORARY TABLE XXX(SELECT …);の形で、ネットで文献を調べたとこあまりよろしくない実装っぽく、そのあたりが関係しているのではないか思っています。 バッチプログラムが発行している SQL をCREATEとINSERT INTO SELECTに書き直した方がよいということは文献よりわかったもののそれが原因と断定できず追加で調査したところ、 MariaDB のナレッジベースに以下の記載がありました。 mariadb.com   自社の設定を含め、要点を要約すると ・ MariaDb のデフォルトの トランザクション の分離レベルは「REPEATABLE READ」である。 ・MariaDB10.4までは「 innodb _locks_unsafe_for_binlog」という変数があり、この変数がONの場合、GAPロックの発生を防げる。 ・移行前のDB インスタンス 上の MariaDB のバージョンは10.0で「 innodb _locks_unsafe_for_binlog」の設定はONなのでGAPロックは発生していない。 ・MariaDB10.4以降では「 innodb _locks_unsafe_for_binlog」という変数がなくなってしまっているため、GAPロックの回避には分離レベルを「READ COMMITTED」にする必要がある。 そのことでした。 実際に検証環境で トランザクション の分離レベルを下げて動作確認したところGAPロックの発生はなく、かつ処理的にも分離レベルを下げても問題がないことの確認が取れたため、バッチサーバが参照するDB インスタンス の MariaDB については トランザクション の分離レベルを「READ COMMITTED」に変更してこの問題については回避しました。 バッチが参照する MariaDB がメモリ不足で再起動する問題 これは MariaDB バージョン差異の影響やEC2 インスタンス を第4世代から第5世代にあげたことによってDBが受け付けられるコネクション量が増えるなど、処理性能が変わったことが影響していると考えられ、「 innodb _buffer_pool_size」を設定値より下げ、その後経過観察をしています。 バッチが参照する MariaDB の接続が タイムアウト する処理がある問題 移行前の旧DBを確認したところ、設定ファイル上の タイムアウト のパラメータ値とSHOW VARIABLES コマンドで直接取得した タイムアウト の値が異なっており、過去に誰かが直接書き換えてそのままになっていたため、設定ファイル値に タイムアウト の時間が更新されておらず、その設定ファイルに基づいて新環境の MariaDB のパラメータが設定されていることが原因でした。 まあ、これは緊急対応だったら私も普通にやると思いますが、変更を設定ファイルに書き戻し、どこかの再起動のタイミングで設定ファイルの値を正とした状態に戻すことをしないとこの問題が発生し続けるので構成管理上まずいなと感じました。なので今後は定期メンテのタイミングなど、定期的に今後設定ファイルとコマンドで取得した値の比較を行って、違ったら設定ファイルを更新してDB再起動する運用とします。 取り急ぎ、バッチ インスタンス の参照する MariaDB の タイムアウト 値を変更しこの問題は回避しています。 まとめ 今回DB インスタンス のOS/ MariaDB 自体のバージョンアップ作業についてはそこまでトラブルはなかったものの、DBを呼び出すプログラム側の問題(DB接続情報の上書きや非推奨なインサート処理)や、主にバッチから発行される SQL に起因するDBの挙動に関する切り替え後の問題が多く、移行を行う際にバッチから実行されるクエリのパフォーマンスについて、事前にもう少し調査する必要があるというのが反省点です。 バージョンアップ影響で インスタンス やDB自体のパフォーマンス変わることはある程度やむをえないもので、今回のようにいかに早くチューニングを行い正常化させる進め方で間違ってはいないと考えています。 最後に、今回のDB インスタンス のOS/ MariaDB のバージョンアップに際し、事前のデータ消込作業から新 インスタンス 切替のトラブルシュートまでを2020年の9月・10月の2カ月間、約4.5時間しか時間の取れない深夜メンテナンス×3回で行い、バージョンアップ後に様々な問題も出ましたが、大きなユーザ影響なく短期間で収束できたのは結構すごいことだと思います。 一応予備日として10月4週にもう一日メンテ日程を用意していたのですが、予備日を使うことなくかつ10月30日から開始のMEGA SALEのスケジュールにも影響を出さず無事に完了できました。 今回これだけの仕事をこの短期間で実現できたのは、当社の技術統括部やSHOPLISTの各開発部のエンジニアのスキルの高さ、特に問題にぶち当たった際のトラブルシュート力あってこそで本当に助かりました。 次のアクションとしてはWeb/バッチサーバのOS/ インスタンス のバージョンアップ作業で、引き続き実施していきます。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 今回は「 SHOPLISTの脱レガシ―システム 」の5回目「 脱レガシーシステム⑤(DB容量を1.6TB⇒1.1TBに減らした話) 」で話した InnoDB のテーブル圧縮の検証についての話です。 検証を思い立ったきっかけ SHOPLISTのインフラ的な問題の1つで、本番のデータベース容量が1.6TBある問題の解決案を考えていて、サービス上参照しないけど、だからと言って SQL でデータ抽出できるDBにもっときたいデータがあるという話が分かりました。 じゃあ昔からやってみたかったネタでテーブル圧縮やってみようと思い立ったのがきっかけです。 実際にはこのテーブル圧縮のほかにもストレージタイプの変更であるとか インスタンス サイズについても変更するのですが、今回の検証としてはテーブル圧縮を進めました。 すごくざっくりなぺージ圧縮のしくみの理解 斜め読みなので正確に理解しているわけではないのですが、ほかの RDBMS 同様にデータ格納時に圧縮ページを作成してストレージ保存する仕組みのようです。 なので期待する挙動としてはデータサイズは減り、書き込みが遅くなるはずで、実際どのくらいの効果があるかを把握するため、当社のメンバーに協力してもらい実際のデータで検証を行いました。 検証の概要 検証方法としては実際のテーブルをdumpし、同じ構造のテーブルをCREATE する際に DDL に「ROW_FORMAT=Compressed KEY_BLOCK_SIZE=8」を追加した別テーブルdumpしたデータをRestoreして、非圧縮テーブルと圧縮テーブルを作りテーブルの容量とRestore速度を比較しました。 前提としては DBMS はMariaDB10.2です。 検証結果⓵ 圧縮率比較 結論:約5割ほどの圧縮率となる。 ※データによって差があるが、Text型が多いテーブルは効果があるように見受けられる 圧縮無し [root@ecSubDb01 compression_test]# ll -htr | grep "product_m.ibd" -rw-rw---- 1 mysql mysql 11G Jul 15 15:32 product_m.ibd 圧縮あり [root@ecSubDb01 compression_test]# ll -htr | grep "product_m.ibd" -rw-rw---- 1 mysql mysql 5.1G Jul 15 15:40 product_m_compression.ibd 検証結果② Insert速度(Restore速度で検証) 結論:約2倍遅くなる。 圧縮無し [root@ecSubDb01 mysql]# time mysql compression_test < session_t_local.dump real 28m23.795s user 1m8.439s sys 0m4.598s 圧縮あり [root@ecSubDb01 mysql]# time mysql compression_test < session_t_local.dump real 60m42.655s user 1m7.089s sys 0m4.394s 結論 期待通りといえば期待通り。ただ、ずば抜けて1/10に圧縮されます!とかではなかったです。 KEY_BLOCK_SIZEを変えていけばなどもっと検証すればいろいろ新しい発見があったかもしれないのですが、期待した通りの結果だったためここで検証としては終了としました。 ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 「 SHOPLISTの脱レガシーシステム 」の記事も5回目となりました。  今回はDB インスタンス のリプレイスの障壁となりそうなDBのテーブル容量を減らす話です。 今までメンテナンスを定期的に実施できなかった弊害 「 SHOPLIST.com のシステムをモダンなアーキテクチャに変えようとしたら予想以上に闇が深かった話 」でも触れた通り、今までほとんどメンテナンスが行われていなかったため、メンテナンスを行わないとサービスに影響が出るような作業が未着手となっていました。 その結果、DBの容量も日々膨張し、1.6TB、テーブル数も全 スキーマ を合算すると1,800を超える状況となってしまっていました。 ・DB容量⇒BackUp &Restoreや今回のようなリプレイス時の作業時間 ・テーブル件数⇒可読性 にそれぞれ影響が出る可能性ものなので、仕様上許容される範囲で少ないこしたことは無く、まずはDB容量を減らす算段を立てました。 容量削減対象のテーブルと容量の洗い出し 容量削減に際し、まず以下の3つの視点でテーブルを洗い出しました。 ⓵ 昨年度(2019-04-01)以降にデータ更新が行われていないテーブル  ⇒要するに、「機能終了時の消し忘れ?いらなくね?」というテーブル。 ② 論理削除フラグが立っているデータが全レコードの50%以上のテーブル  ⇒「いらなくない?そのデータ。」があるかもしれないテーブル。 ③ エンジニアへの ヒアリ ングでデータを使っていない可能性のあるテーブル  ⇒データ更新はされてるけどいらないかもしれないテーブル。 この3つの視点で各テーブルのデータを調査し、最終的に15テーブル、データ容量にして合計で515GB(元のDB容量の32%分)のデータについて本番のDB インスタンス から削除可能であることが分かりました。 本番DB インスタンス からのデータ退避を計画する 本番DB インスタンス からのデータ退避については以下3方針としました。 ⓵サービス上も業務上も不要なものはTruncateもしくはDelete。 ②サービス上で不要だけど調査や分析で必要なものはSubDBに退避。 ③削除フラグが50%以上のものは、テーブル名を別名に変更し、新規作成テーブルに削除フラグが立っていないデータのみインサート、元テーブルは⓵または②で対処。 SubDBというのは今回のインフラ構成変更のタイミングで用意したDB インスタンス で、データ保管コストを安くするために準備した退避専用のDB インスタンス です。サービスに投入しているDB インスタンス との違いについては、 インスタンス サイズが小さく割り当てられているシステムリソースが本番DB インスタンス の半分以下であること、本番DB インスタンス のSlave台数が6~10台(セール状況により変動)なのに対しSlave1台の合計2台構成であることと、 InnoDB テーブルの圧縮設定がデフォルトでしていることです。 InnoDB のテーブル圧縮機能についての検証結果については「 MariaDB のテーブル圧縮の検証を検証した話」に投稿してあるので興味のある方は確認してみてください。 今回データ削除を行う15テーブルについて、Backup環境で事前にデータ退避を行う際と同じオペレーションを事前に実施し、実際にかかる時間を算出したところトータルで5時間、余裕を見て6時間ほどの作業時間が必要であることが分かり、メンテで作業可能な時間が4.5時間しか取れない状況だったため、実際にはデータ退避は2回のメンテナンスをに分けて実施することにしました。 第1回目の計画メンテナンスを行う 第1回目の計画メンテナンスは2020年の9月2週に実施しました。ただこの際はすべてのメンテナンス時間がデータ退避以外にも、EC2 インスタンス をMulti-AZ化するための VPC 上ネットワーク構成の変更、その他メンテ中でないと実施できないテーブル構成の変更、インデックス付与などの作業があり、メンテナンス時間フルフルを今回のデータ退避作業に使えない状況だったので、15テーブルのうちの4テーブル、容量にして約300GBについてデータ退避を行う作業計画を立て、当日のメンテナンスに臨みました。 メンテ開始時直後、メンテ表示の不具合調査で作業時間が30分なくなる うそでしょwと思うかもしれないのですが、メンテナンスに入った直後にメンテナンス画面の表示(メンテナンス明け日時)に誤りがあることが発覚して、原因箇所の特定で30分ほど意図しない作業が発生してしまいました。定期的にメンテナンスを行っていないと、メンテナンス時のナレッジも陳腐化してしまいこのような問題も発生してしまうようです。 調べて分かったことなのですが、メンテナンスの案内のテキストなのですがどうやら複数箇所で設定しているらしく、今回は片方のテキスト設定が漏れたため表示誤りが発生していたようです。 データ退避作業を開始する その後DB作業ができるようになったため、ほかの作業と並行しながらdumpでバックアップを取得してその後データ退避を行っていったのですが、なぜかテーブルのdumpの実行速度が遅く、結局4テーブル中3テーブル、サイズにして約265GBまでしか実行できず、残り1テーブルについては2度目の計画メンテナンスに回しました。 第2回目の計画メンテナンスを行う 第2回目の計画メンテナンスは第1回目の計画メンテナンスの2週間後に実施しました。 このメンテナンスのゴールは前回データ退避のできなかった1テーブルを含む12テーブル、データサイズにして残りの約265GBを本番DB インスタンス から退避することです。 前回の反省点として、本番 インスタンス のdump取得にかかる時間が事前検証を元に算出した時間を超えてしまっていたため、今回は作業時間の削減のために複数テーブルを並行して実施しました。 メンテ開始時直後、メンテ表示の ステータスコード でトラブる 実は今回のメンテナンスに合わせてメンテナンスアナウンス時にブラウザに返すHTTPの ステータスコード の変更をする修正を入れたのですがそれが正常に動作しておらずブラウザ表示としては正常なのですが、返している ステータスコード が異なるという問題が起こり、原因調査のためDB作業開始が少し遅れました。 データ退避作業、無事完了する その後、複数テーブル並行してdumpとデータ退避を行い何とか今回行いたかった合計15テーブル、データ量にして約515GBのデータ退避が完了しました。 但し、第1回目のメンテナンス時に既存テーブルに対するインデックスの追加作業があったためDB容量は約80GB増えてしまい最終的には約1.2TBのDB容量となりました。 まとめ 今回の教訓としては ⓵計画メンテナンスが本当に機会損失で絶対にメンテナンスを入れたくない、もしくは回数をできる限り最小にしたいのであれば、何でもかんでもDBに突っ込むべきじゃない。 なぜならデータ削除の際にスレーブ遅延を起こすのでサービス稼働中に消込ができないから。業務にしか使用しないのであればテキストで吐いてバッチで別DBに突っ込むとかやり方はあるため。 ②論理削除を前提とする設計を極力しない。 なぜならdeleteではidbファイルのサイズが減るわけじゃないから。 ③とはいっても計画メンテナンスを行わないは論外。 目標として目指すはいいけど、メンテ中でないと出来ないことは事実としてあるし、先送りし続けると今回みたいなことになるし、データ保管コストも発生する。 以上です。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社の鈴木です。 「 SHOPLISTの脱レガシーシステム 」の記事も4回目となりました。 私の所属する技術統括部の担当業務の一つに各開発部に所属するエンジニアに対する技術支援があり、その中でもローカル開発環境は昔から問い合わせが多く悩みの種でした。 今回は、インフラ構築のコード化の恩恵として、ローカル開発環境の構築ができたという話を共有していこうと思います。 当社のローカル開発環境の変遷 ~2011年頃 無し。 漢らしく開発環境、場合によっては直接本番環境を直接修正していた時代でした。 ~2014年頃 XAMPP利用時代 クライアントPCのOS上にXAMPPを入れて開発に必要な ミドルウェア を動かしていた時代でした。 OSもですが ミドルウェア バージョン、 ディレクト リセパレータなど、いろんな部分で本番環境とは微妙に異なり、結局開発環境以降の環境と挙動が違う、また Mac ユーザに対して、オフィシャルでサポート無く、各開発者は MAMP か自分でミドルウエアを入れていた。 ~今まで  Oracle VirtualBox 利用時代 ローカル上に仮想環境を用意して、その上にゲストOSとしてサービスで利用しているものと同じOS/ ミドルウェア を動かしています。 うる覚え ですが当時 VMware Fusion と Oracle VirtualBox の2製品両方を検証して、ホストOSから コマンドライン 上で操作できるものが多いという理由で Oracle VirtualBox を選定した記憶があります。   ローカル開発環境における問題点 技術的な話というよりは、新規に参画する開発者が一番初めに環境構築を行う際に作業時間がかかる、構築中問題が発生した際にトラブルシュートに時間がかかるという問題がありました。 これは VirtualBox の仮想ネットワーク設定、ゲストOS起動後にゲストOS上で行うWebサーバ上のアプリケーション部分の開発環境の構築など、手作業部分がそれなりの作業量があるのと、とはいってもこの分野は全くインフラを触ってきてない開発者から見るとトラブルが発生した際に自分自身で トライアンドエラー を行い正常に動作するまでもっていくのにそれなりに時間がかかってしまい、運が悪いと1日から2日ローカル開発環境の構築にかっててしまう状況でした。   vagrant とansibleで構築を コマンドライン 化する インフラのコード化を現在進めており、仮想環境固有の部分を vagrant で構築することで、開発環境構築簡素化できるんじゃないかという話が部内で出て以降、このやり方で行っています。   導入してみて 個人的には「構築すげえ楽になったじゃん!」という感想です。 これまで、忙しい時に限ってPCが壊れて環境作り直すなどしていたため、コマンド2回実行でOKなこと、1時間弱待つだけで環境構築が完了できるのはものすごく魅力に感じています。 一方、当たり前といえば当たり前なのですが、ホストOS側に入れる vagrant などのソフトウェアのバージョン管理やインストールや設定漏れがあると vagrant コマンドが正常に動作しない、 クラウド 時代に慣れすぎて1時間ですら長く感じるときがあるなど課題もあり、前者は PowerShell などでホストOS側でのインストール作業の自動化、後者はゲストOSの構築方法の変更や、初回にCloneしてくる ソースコード および画像リソースの最適化を今後していき改善していきたいと考えています。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。   以前の「 SHOPLISTのシステムをモダンなアーキテクチャに変えようとしたら予想以上に闇が深かった話   」でも書いたのですが、現行のSHOPLISTにおけるOSや ミドルウェア がとにかく古い状態でした。 今回はOS ミドルウェア をバージョンアップした話について共有していこうと思います。 OS/ ミドルウェア のバージョンアップはインフラ的な山場の一つです。 CentOS の6.4は2020年の11月でサポート終了、PHP5.4は2015年に終了しておりセキュリティ面、生産性面で大きな課題がある状況でした。セキュリティ面は当然のこととして、 AWS SDK for PHP や google - api - php -clientなど主要ライブラリが PHP のバージョンが古すぎて使えないこと、Web API を呼び出して使うにしてもlibcurlの問題で SSL connect errorが発生して呼び出せない、その他文献がない、調べようにもネット上の文献少なくなっている、しまいには新規に参画したメンバから「SHOPLISTのこのシステム、化石ですね」と言われてしまう、笑うに笑えない状況でした。 今回どこまでバージョンアップするか? 計画時点のStable 最新版を選定しました。 ■OS  CnetOS 6.4 ⇒  Amazon Linux release 2 (Karoo) ■Web  Apache2.2  ⇒  Apache 2.4.43 ■ PHP   PHP5.4  ⇒ PHP7.4 ■DB  MariaDB10.0.3 ⇒ MariaDb10.5.5 ■redis redis2.8.3-1 ⇒ redis4.0.10 構築を行う際に議論となったこと ソフトウェアパッケージを管理する リポジトリ について 今回OSがAmazonLinuxで、 amazon - linux -extrasライブラリを正としようとすべきなのですが、以下2点の問題があり、以下の方針としました。 1. amazon - linux -extrasの php のバージョンタグにphp7.4のバージョンタグがstableとなっていてリビジョンがない問題 ⇒stableで代用。なのでリビジョン単位で見ると環境差異が生まれる可能性はあるが受容する。 2.必要なすべてのライブラリが amazon - linux -extras内に存在しない問題 ⇒やむを得ないのでそのようなものは pecl 、 pear を使うけど、 amazon - linux -extrasにあるものはそこからインストールする。 今後のバージョンアップの頻度、その時のバージョンの選定ルールについて 議論の発端としては各 ミドルウェア ごとにEOLが異なることです。 例えば2年おきに最新安定版にアップデートを行うという決めごとにした場合、 PHP の アクティブサポート は2011年の11月に終了となり、サポート切れ状態となりますが、EOLの長いもの、例えば MariaDB などについてはサポート範囲内なので今のままでも特段追加したい機能や、今使っているバージョンで問題が無ければ無理にバージョン上げる必要ないのではないかという話です。 この件については結論があいまいな部分はあるのですが以下の方針でよいのではないかと考えています。 1. PHP および PHP ライブラリは2年ごとに最新安定版に 2年おきにバージョンを安定最新版にあげる。理由としては PHP の アクティブサポート が2年、セキュリティサポートが3年なので、2年おきにやる計画を立てておけば仮にトラブって多少足が出たとしてもセキュリティサポート終了までにはアップデートが完了できる。またバージョンアップに伴い非推奨関数の書き直しなど、ソース上の回収箇所も一定数あるものなので、定期的にバージョンアップを計画し、それに向けて通常業務においても非推奨関数の書き換えを行う運用を作るなど、定常業務化した方がバージョンアップ前に苦労することが減るので、 機械的 に2年で最新安定版にあげることを計画しそのために日々準備しておく方が適切と判断。 2.それ以外は必要な場合は2年おきに 追加機能要望のあるもの、もしくはサポートが切れることが見込まれるものについては2年おきの PHP バージョンアップの際に同時に行い、それ以外においてはバージョンアップは行わない。 3.重大な 脆弱性 が見つかり、システム全体で見ても リスクヘッジ の手段がないものは即時。 CVN、NVDは日次確認しており、その中でその 脆弱性 が ミドルウェア 単体の設定変更や、WAFやALBなどその他サービスの組み合わせでも防げないものについては即時バージョンアップ。 バージョンアップの進め方 実施のスケジュール一気に進めてしまうと問題が発生した際に原因の切り分けできなくなるので以下の段取りで進めました。 1.ローカル環境及び開発環境のOS/ PHP を含む ミドルウェア をバージョンアップしてEC2 インスタンス をそれぞれ作成する。 2.Git上でPHP7動作確認用のブランチを定義しておき、そのブランチに ソースコード がデプロイされて場合に、新バージョンに対応したEC2 インスタンス にデプロイし、PHP7による影響範囲の洗い出しとプログラムの回収を個なっていく。releaseブランチからPHP7動作確認用のブランチには定期的にpullして機能差分を埋めていく。 3.まず先に本番環境のDB インスタンス のバージョンアップから先に行い、その後にWebとバッチ インスタンス サーバのバージョンアップを行う。ただその前に 不要なテーブル・データで削除できるものは消す 。(事前にバックアップDB インスタンス でテーブルおよびデータ削除にかかる時間を計測したところ数時間規模だったので、データ削除のために計画メンテナンスを一度行う。 4.計画メンテナンスを行い本 番環境のDB インスタンス のバージョンアップを先に行う 。この時点でWeb インスタンス がCentOS6.4/PHP5.4環境、DB インスタンス がAmazonLinux2/MariaDB10.5.5環境に置き換わっている。 5.PHP7対応のソースとreleaseブランチのマージのため、新規機能リリースを一定期間停止し、releaseブランチ = php7.4対応の状態に整理していく。 6.計画メンテナンスを行い、Web とバッチ インスタンス をPHP7.4に対応した インスタンス に切り替えていく。この時点でWebとバッチ インスタンス もAmazonLinux2/PHP7.4環境に置き換わっている。 7.ここで何も問題が発生しなければ旧バージョンの インスタンス をTerminateして終了。   ちなみに上記の2までは比較的順調に進みました。3以降は何かしらの問題が発生し難儀しました。   結構長くなってしまいました。 次回以降の記事で、発生した問題と解決方法を書いていきたいと思います。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社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サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 前回の投稿「 SHOPLISTのシステムをモダンなアーキテクチャに変えようとしたら予想以上に闇が深かった話 」の続きです。 レガシーシステム 脱却のため、まずは リファクタリング でできる限りのことをする。という話なのですが、これは方針を決めたにすぎません。 今回は、SHOPLISTにおいて レガシーシステム 脱却のためにどのような計画を立てて、何からどのように進めていたかについて共有していこうと思います。   まずは 兵站 (サポートとして投下できるヒト・モノ・カネ)を確保することから 私が一番初めに考えたのは 兵站 でした。 レガシー脱却を行うためには、私1人や私の所属する技術統括部の人員だけでは到底進めきれません。 また、既存システム上でサービスを動かしながら改修をしていくことを考えると長期戦になることが予想されるため、①一連の活動に割けるヒト・モノ・カネがどの程度あるか ②計画メンテナンスのようにレガシー脱却の トレードオフ としてビジネスサイドのスタッフがどの程度協力が得られるか(例えば一定期間は新規開発を止めるこことが受容できるか、定期メンテナンスを行えるか) ③ PHP バージョンアップを行うに際しても、サーバへ移行稼働期間がかかるので、サーバ費用としてオーバーラップできる予算がどのくらい必要か、④ リファクタリング を効果的に行う上で必要な支援は何か、をまず考えました。 どんなに素晴らしい戦略、作戦をたたたところで 兵站 を蔑ろにすると絵に描いた餅にしかならないからです。 少し余談になりますが、日本人は 兵站 を疎かにする節があると私は思っています。 第二次世界大戦 において日本軍は奇襲をかけ、艦艇や航空機を数多く破壊することには成功ているものの一方で、ドッグの破壊は行わず、先頭 不能 状態であった空母ヨークタウンがそのドッグで修理されミッドウエイ海戦に参戦しているという事実があります。本来は日本は4隻の空母で2隻の空母を戦うところ、 兵站 を軽視した結果としてヨークタウンが参戦することを許す形となり、もし 真珠湾攻撃 でドッグを破壊していれば戦局は変わっていたかもしれないのです。 話を戻します。今回進め方としては 1.必要となる 兵站 の見積     ↓↓ 2.問題に対する緊急度、優先度付け     ↓↓ 3.解決策の進め方のステークホルダ合意 という形で進めています。 レガシーシステム の解決策としてまず行ったこと 解決策は大きく分けて長期的施策と短期的施策があるのですが、まず一番初めに行ったことは、以下2点について会社代表および各部の部長と合意形成を取ることでした。 ①深夜メンテナンスを今後は3カ月に行えるようにする。また当面の間は2週間おきに深夜メンテナンスを行えるようにする。 ②エンジニア職種ついては週に4時間 リファクタリング をはじめとするシステム品質の維持改善に充てられる時間を確保する。 1.計画メンテナンス時間の確保 SHOPLISTは実は今まで計画メンテナンスの時間をほとんどとっていませんでした。多くても年に1~2回で、その理由についてもElastiCacheをはじめとする AWS のPaaSのメンテナンスに伴う事前停止でした。 その理由について昔からSOHPLISTにいるエンジニアに ヒアリ ングしたところ、「メンテンナンスを行うとその間の売上が損失するのと、ブランド様への調整が大変なので実施したいけどできない」という回答が返ってきました。 要するに、メンテナンスを行うを行うと売上が減少するので、自社から見た場合にアンコントローラブルな AWS のメンテナンス以外の理由のメンテナンスがビジネスサイドから許可されていない状況が長年続いていたという状況だったということです。 そのため、本来当然行われるべきOSや ミドルウェア アップデートも行えず、データメンテナンスや大規模なインデックス付加も行えない状況となり、結果として、サポート切れOS ミドルウェア の問題や、肥大化したDBが生まれることとなっていたわけです。 また、実際にどの程度売上が逸失するかについて十分な議論がなされていない現状も見えてきました。メンテナンスを行う時間帯を考慮すればせいぜい1回あたりの 逸失利益 ってせいぜい数十万円いかないです。かつ、メンテナンス中に購入できなかったユーザーが別のECプラットフォームで購入する前提であり、メンテを開けて当社サイトで購入する割合が0%であるという前提で考えての場合です。 本当に冷静に考えるとメンテナンスの 逸失利益 とEBSのコスト削減効果って同じかもしれないし、それ以上あるかもしれない。ただ、売上が減ることが困るというだけでメンテナンスが入れれない状況に陥っていたのです。 なので、「まず3か月おきにメンテをやる。やるというよりやる予定で計画する。他社はそんなにやってないとか思うかもしれないけど、うちはうち、よそはよそ、全く アーキテクチャ が違うシステムを横並びで比較しても意味がないのでメンテをやる前提でスケジュールを組ませてください。で、もしメンテやらなくても済めば売り上げ失わなくてラッキーでしょ」という話と、「現状的負債として、サポート切れOS・ ミドルウェア 問題とDB肥大化問題があるんで、当面のうちは2週間おきに深夜メンテやらせてください。」という2点について社内でコンセンサスを取りに行きました。 2. リファクタリング をはじめとするシステム品質改善時間の確保 これは、3Mの「15%カルチャー」、 Google やAtlassianで「20%ルール」のシステム品質版のを行おうという取り組みです。 SHOPLISTのように事業成長のため、売り上げに直結する開発以外が後回しになってしまうのであれば、直接的に売り上げに貢献しないけど、重要度の高いソフトウェア品質の維持のための活動を行う時間を取ることを経営者と明確に握ってしまいおうと考えました。 もちろん本家のようにイノベーティブな仕事に一定時間を当てようという話ではなく、技術的負債の返済に一定時間充てようという話であり厳密には違います。 ただ、ここで時間を設ける意図としては「売上に直結しないけど、システム品質を上げる活動をエンジニアが行える時間と、システム品質を維持向上させることを文化として根付かせること」と、「システム品質を維持向上させるための一連の活動を通じて、狂った状態になっている 正常性バイアス を取っ払うこと」の2点でした。 なので社内で リファクタリング 枠と言われている週4時間の時間確保をまず確保しました。 ちなみに週4時間に明確な理由はないです。それは実際に4時間でどこまでシステムの リファクタリング が行えるかどうかということよりも、文化形成と 正常性バイアス を取り除くことを目的としていたためです。 どう進めるか? 計画メンテナンス時間の確保と、 リファクタリング 時間を確保し、十分に レガシーシステム 脱却に充てれる時間、リソースを確保したところで、具体的に行うべき事柄を以下の5点で整理をしました。 1.システム品質維持向上のために永続して行う内容(長期的施策) ・3カ月に一度の定期メンテナンス ・日々のリリースの中で バックログ として積んだ リファクタリング 作業。 2.2020年中にマストで完了させるべき内容 ・インフラ アーキテクチャ の見直し ・インフラ構築のコード化(ansibleによる環境構築の自動化) ・ローカル開発環境の構築コストの削減( vagrant +ansibleによる構築自動化) ・OS/ PHP / MariaDB のバージョンアップ ・デプロイ方法の変更(GitLab CIによるデプロイ) ・Git リポジトリ 上の不要なブランチ削除 ・DB上のサービス上不要なデータの削除 ・開発ルール、 ガイドライン の明文化 3.2020年中にできる限りで完了させるべき内容 ・ログ上に存在するNotice/Warning潰し ・デッドコードの削除 ・リソースファイル管理引退するGit LFS の導入 4.2020年中にやれる余力があれ対応するべき内容 ・ログ上に存在するNotice/Warning潰し ・デッドコードの削除 ・リソースファイル管理引退するGit LFS の導入 ・ジョブ管理ツールの導入(cronの外だし) 5.2020年中にやらない内容(課題として持ち超す内容) ・ iOS のネイティブ実装をSwiftにの統一すること ・ Android のネイティブKotlinに統一すること ・サーバの独自 フレームワーク を入れ替えること ・DBをEC2 インスタンス からRDSに入れ替えること   各項目で具体的に何を行っていったかについては今後公開される記事を参照下さい。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。
アバター
こんにちは。クルーズ株式会社CTOの鈴木です。 2020年の7月よりCROOZ SHOPLIST株式会社の技術統括部長を兼務しており、日々システムと開発組織の業務改善に現場のエンジニアとともに取り組んでおります。 今回当社が運営しているファッション ECサイト 『SHOPLIST.com by CROOZ』にて絶賛格闘中のシステム品質改善の話を数回に分けてお話ししたいと思います。   SHOPLISTのシステムを改善しようとなったきっかけ 「今のSHOPLISTのシステムってゼロからリニューアルするといくらくらいかかるの?」 SHOPLISTの業務を兼務するようになって、一番初めに社長に聞かれたことです。何をいきなり出だすのかと詳細を聞いていくと、「開発エンジニア数名にヒヤリングしたところ、システムがレガシー過ぎて開発が超しづらい」「もうリニューアルでゼロベースで作り直すしかない」という意見があった。まず現状どんな課題があるのか調べてほしいとのこと。 SHOPLISTについては、CROOZ株式会社から分社化した会社で、もともとのシステム アーキテクチャ は把握してましたし、また定期的にエンジニアの定例を行っていたので全く内情を把握していないというわけではなかったのですが、改めてシステム上何が問題なのかについて現状の ソースコード 、インフラ構成、開発体制などあらゆる面で約1カ月程度かけて調査してみました。 そして以下、具体的に見えてきた問題です。(問題の粒度はバラバラです)  SHOPLISTの問題その①:仕様の複雑さ+仕様書の無さ+デッドコードの組み合わせで、調査及び実装が昔からいたベテランエンジニアしか効率的に行えない問題。 一番はここだと個人的には思っています。とにかく仕様把握に時間がかかる。入口のControllerから順に読んでいくとDBから何かの値とってその値に応じた分岐が複数あり、分岐を一個一個追っていくと、結局DBの検索結果がnullで、すべての条件分岐を抜け、結局やってることってTOPページにリダイレクトしてるだけじゃん…。それでその話を古くからいたエンジニアに聞いたら、あの人知ってるかもと別のエンジニアに聞くように言われ、そのエンジニアに聞くと実は2年前に終了した機能で、結局Controllerの中の100行の分岐制御でいらないでしょw みたいなことが実際にありました。 他にもDBの CRUD で見るとデータは生成されてるんだけど、参照されてなくない?みたいなものや、このテーブル何?みたいなものがあってとにかくおそらくデッドコードなんだけど、何の目的でその処理があるのかが仕様書としてもソースコメントとしても残ってないのでよくわからない。とはいっても現状だとテストコードなどもなくCICD環境も無いので、処理を消した結果で不具合が起こるリスクに対する リスクヘッジ の方法がなく能動的に回収しづらいという話も聞きました。 決して ソースコード が汚いわけではないです。CROOZでは過去に SNS や ソーシャルゲーム を運営しておりましたが、よっぽどそっちの方がソースは汚かったです。SHOPLIST固有の問題としては仕様の複雑さ+仕様書の無さ+デッドコードの組み合わせだと考えています。   SHOPLISTの問題その②:OS・ ミドルウェア が古い(サポート切れ状態)問題 SHOPLISTのインフラは、2014年にオンプレミスから AWS 上に移行したのですが、その際OS・ ミドルウェア をそのままのバージョンで クラウド に移行しています。なのでEC2を使ってはいるものの、本当にオンプレミスのサーバが仮想サーバに置き換わっただけで、 クラウド を意識したプログラム実装にはなっていない部分が多い状況になっていました。 それはそれで問題なのですが、もっと問題なのはOS・ ミドルウェア バージョンが古いこと。一例ですがOSは CentOS 6.4系、 PHP に至っては5.4系でした。ちなみにPHP5.4系のサポートは2015年に終了しておりセキュリティ面、生産性面で大きな課題がある状況でした。もっともセキュリティの話だけであれば Apache の設定や AWS WAFなど、複数のサービスや ミドルウェア の組み合わせにより リスクヘッジ はまだできる部分があるのですが、当然すべてをセキュリティリスクをゼロにはできないですし、生産性の話でいうと当たり前ですが最近ライブラリや SDK はサポート切れの PHP に対応しているわけがないので恩恵が受けれず開発に苦労することになります。 一番の問題での一番の課題は、 エンジニアの 心理的 な負荷が大きいこと 。まあcomposerで何か入れようとしてら PHP のバージョンが古くてインストールできない、セキュリティサポートも終わっていて、重大な 脆弱性 が見つかっても直せない可能性がある状態だと、 心理的 な負荷は大きいです。 実際にサポート切れバージョンを使い続けることに耐えかねず、やめてしまったエンジニアもいたようです。 その他SHOPLISTが抱えている問題  SHOPLISTのシステムにはこの他にも数々の問題がありますが、主だったものだけ列挙すると、 ・デプロイを rsync で行っていてリリース直後に不具合が出た際に切り戻しに苦労する問題。 ・ローカル開発環境の構築に人によっては1日潰れる問題。 ・Gitが重い問題。 ・ PHP のWarning / Notice件数が1リク エス トあたり300を超えている問題。 ・ PHP の定数多すぎて管理できなくなっているんじゃないか疑惑(問題というよりは疑惑)。 ・超属人化していて、 ブラックボックス になっている問題。 ・CI/CD環境が存在しない&CI/CDを実践できない問題。 ・ iOS の場合SwiftとObject-C、 Android の場合 Java とKotlinが機能毎に混在している問題。 などなど、いろいろありました。 SHOPLISTの根底にある問題はなにか? 問題をひととおり出したところで、正直な感想を言うとよくある話だなと感じました。 よくある レガシーシステム の話です。 事業拡大、売上を上げるために継ぎ足し継ぎ足しで開発し続けた結果、ドキュメント作成や終了した機能のプログラムやデータの削除、 リポジトリ 上からの不要ブランチの削除・ ミドルウェア のバージョンアップなど、売上に直結しないことを後回しにせざるを得ない状況となり、そして後回しにしたものが永遠に着手できなくなって技術的負債として蓄積される。 なのでこれだけだったら急成長したサービスではよく聞く話なのですが、やばいなと感じたのは この問題が前から発生していたのにも関わらず長い間放置されていたため、現場のエンジニアやプランナー(WEBディレクター)や役員の中で 正常性バイアス が働いて問題に対して楽観的な見通しになっていたこと です。 具体的な話でいうと ・ ソースコード レベルの課題や 工数 、移行計画を十分に検討しない段階でリニューアルするしかない、リニューアルすれば レガシーシステム から脱却できる。 ・DBサーバの容量が大きくなってもプロビジョニングに対応したEBSだから容量の問題はいったん大丈夫。 など、もちろんそれはそうだけどちょっと冷静に考えたときに非現実的だったり、すべての問題がクリアできなかったりいろいろまずくないか?と思うところがあり、はじめのきっかけの話に戻ると社長から質問のあったリニューアルするといくらくらいかかるかの質問については、「超概算でこれくらいかかるけど、 現状のドキュメントがなく超属人化している状況下で、そのほかの機能開発を行いながら進めるのは極めて非現実的で、まずは リファクタリング ベースでできる部分までレガシーな部分直していきましょう。」 という話を提案しました。   今回はここまで。 以降、数回に分けてSHOPLISTにおける レガシーシステム 脱却に向けたシステム改善の話を投稿していきますが、この記事を読んでいて今まさに成長しているサービスの開発に携わっているエンジニアがいらっしゃいましたら、本当に今のうちに リファクタリング すべきです。 また既に同じような状況になっている企業の方がおりましたら、(あくまでも一例ですが)当社の レガシーシステム 脱却に向け実践したことが参考になれば幸いです。 ------------------------------------------------------------------------------------------------- ※2020年の内容を記事にしており、2020年11月以降PHP7サポート切れをはじめとした 脆弱性 リスクへの対処は完了しております。  
アバター