ニコニコで12年運用した決済システムを移行する上で必要だったこと

### はじめに 
今日は、ニコニコのプレミアム会員サービスを支える「プレミアム課金システム」を動画システムのモノリスから切り出し、変更可能にしていった過程について書きます。プレミアム課金システムは金銭を扱うシステムですので、「（特に、失敗した）話を聞くのは面白いけど、自分で触りたくない」と思われる方も多いのではないでしょうか。
この記事では、決済にかかわるシステムでも一般的なシステム改善の方法が適用できることをお伝えしたいと思います。また、コストを抑えつつ着実なシステム改善を行う方法論としてもご理解していただけると嬉しく思います。

### 背景
#### プレミアム会員サービスについて
|![スクリーンショット 2020-12-02 18.01.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99980/73301012-6049-6be3-bcb8-5ad7ee3d1f81.png)|
|:--|
月額500円（税別）のプレミアム会員制度には159万人（2020年9月末現在）の方が加入してくださっており、ニコニコ事業を支える主要な有料サービスです。
ニコニコ動画は2006年にサービスを開始し、2007年にプレミアム会員サービスを開始しました。当時プレミアム課金を実現するシステムは、動画システムの内部に実装されました。当時のことを知っている人は少なく、私も残っているドキュメントとコードから想像することしかできませんが、売上に直接かかわるシステムとして、かなり気合いを入れて製作されたように思えます。

#### ビジネス要求の変化
プレミアム会員はニコニコ事業にとってもっとも重要なお客様です。しかしながら、プレミアム課金システムは複雑で、変更に時間がかかるという問題がありました。また、システムに問題が起きて、売上に影響が出ることへのプレッシャーから、手を入れるモチベーションの形成が難しいという状況にありました。

#### 新しい決済方法の広まり
キャッシュレス決済が急速に浸透するなかで、従来の決済手段の枠組みに当てはまらない決済手段が出現してきています。具体的には、QRコード決済や少額決済の後払いなどです。フィンテックと呼ばれる ICT を使った金融サービスを提供する会社の出現により、決済手段は今後も多様化していくものと思われます。プレミアム課金システムも、エンドユーザーが好みの決済手段を選択できるようにするという観点から、新しい決済手段への対応は引き続き取り組んでいく必要がありました。

#### コード全体を見直す必要性
動画からのシステム分離を計画した2019年時点で、カットオーバーから12年が経過しておりました。プレミアム課金システムのコードが改善され続けていたわけではなく、当時のまま残っているモジュールが多く存在する状態でした。いわゆるレガシー PHP です。システムに変更を加えるためには、コード全体も見直す必要があるような状況でした。

### 課題
#### 全面的な作り直しはしない
プレミアム課金システムは、業務要件が複雑です。Web/API の仕様だけを見るとコンパクトにも見えますが、実際は以下の2点で特殊性があります。

- 結合先の社外システムが多い
 - 決済を行う上で、決済代行会社や通信キャリアと結合しなければなりません
 - 各社のテスト環境で確認できる動作・ふるまいは限定的です
- 法令上の細かな要件がある
 - 消費者保護の観点から、画面上の要件に細かな要件があります
 - 特定の伝票を最低7年間は保存しなければならない、等の要件があります

特に社外システムとの結合が課題で、各決済代行会社、キャリアのドキュメントだけで軽く1,000ページは超えます。ドキュメントにない特別な API を使っている場所もあり、すべてを再実装できるレベルで把握するためには長い時間が必要なことは明白でした。
また、要求や要件のレイヤのドキュメントがない状態であり、このような状況での安易な作り直しは危険だと判断しました。

#### 安全に変更ができるシステムを目指す
システムへの要求が変化すると、当然ですがコードの変更が必要となります。当時の状況では、コードの変更が困難でした。直接的な原因を以下に挙げます。

- エンジニア個別の開発環境（いわゆるローカル開発環境）がない、作れない
- ユニットテスト、結合テストがない
- 要件・仕様がドキュメント化されていない部分がある
- システム・インフラ設計ドキュメントが不十分
- デプロイされているコードのバージョン管理を Subversion で行っている
 - GitHub Enterprise のコードレビュー機能だけを使っていました

### プロジェクト立案
#### ゴール設定
ITILなども参考にしながら、「変更管理をきちんとやる」「構成管理をきちんとやる」という2つの観点から課題を整理し、ゴール設定を行いました。

##### リポジトリの共有を解消する（変更管理）
リポジトリを動画システムと共有している状況を解消し、プレミアム課金システム単体でリポジトリを管理・運用できる状態をゴールとします。また、設定ファイルについても同様に分離し、動画システムとは独立した設定ファイルとします。

##### Subversionから完全に脱却する（変更管理）
ドワンゴ内部でSubversionを主体的に使っているチームはおらず、また技術的にもGitの方が多くの面で優れているため、Subversionから完全に脱却することをゴールとしました。

##### 実際の環境と異なる構成管理ツール（構成管理）
構成管理ツールとしてはAnsibleを利用していましたが、冪等性の担保が甘く、本番環境に再度適用することができない状態でした。システム移行にともない、この状況を解消することで、確実な構成管理ができる状態をゴールとしました。

#### システム移行の合意形成
技術的な内容からかなり離れるので簡単に書きますが、システム移行することそれ自体の合意形成は案外簡単でした。私の事前の想定と違って、ビジネスを成長させないという理由で反対されることはありませんでした。目的や課題よりも、安全に移行できないリスクにどう対処するのか、についてきちんと考えることを要求されました。

### 移行設計
#### アプリケーションの構成要素
アプリケーションの構成を通信要件ごとに分析すると、以下のようになりました。

- エンドユーザーが利用するWebページ、API
- ニコニコのシステム間API
- 決済代行会社などとの間で利用するシステム間API

これらをそれぞれ「ロール」と定義し、各ロールごとにコード・設定、データ、通信要件、デプロイの各コンポーネントが移行対象かどうか、といった観点で検討を行いました。なお、バッチについては要件が不明な部分が多く、今回の移行対象外としました。

#### アプリケーションコードの移行
アプリケーションについては、動画システムのリポジトリからプレミアム課金のものを抜き出し、社内で運用されている GitHub Enterprise にリポジトリを作成しました。また、設定ファイルについても同様に動画システムからコピーしてきました。
#### データの移行
データは、MySQL（マスタ） と memcached（キャッシュ）を利用していました。MySQLについては、プレミアム課金システム専用となっていたため、これをそのまま利用しました。memcachedについては、Redisへ移行することとしました。こちらのデータは一時的なキャッシュで、消えてしまっても問題がないデータが入っている状況でしたので、データの移行は行いませんでした。
また、ログの長期保存のために共有ストレージ（NFS）を利用していますが、こちらも使い方に変更は加えないようにしました。

#### ドメインの移行
従来のプレミアム課金システムは、 secure.nicovideo.jp ドメインで運用されていました。このドメインは、他のシステムでも利用されており、プレミアム課金システム独自のものではありませんでした。
システムの独立性を高めるため、今回の移行で新ドメイン premium.nicovideo.jp を用意しました。
また、システムの切り替えは旧ドメインのサーバーでリバースプロキシを行い、新旧どちらのドメインでも動作することで、ドメイン変更を徐々に行う方式を採用しました。この方法を採用した理由は、以下の通りです。

- 切り戻しが簡単なこと
- アクセス元となる決済代行会社、キャリアの設定を同時に変更する必要がない
- アクセス変更元が新しいURLに即座に変更しなくても良い

もちろんドメインに対して固有で紐付くもの（Cookieなど）については考慮が必要ですが、今回のケースではサブドメインのみが異なるので、技術的には対応可能でした。

#### 通信要件の整理
IP アドレスの許可リスト設定はありましたが、どのIPがどの通信相手なのか分からない状況でした。既存の通信相手を netstat で調査し、要件を逆算していく方法が採られることもありますが、今回のケースでは各決済代行会社・キャリアの要件をもとに ACL を再設計し、実装しました。通信相手が分かっているが設定値が分からない場合は、このような方法の方が安全かつ低コストだと思います。
ACL のすべての行を見直し、正しく設定しました。

#### デプロイシステムの移行
従来のデプロイシステムは Subversion に依存していたため、デプロイの仕組みは全面的に見直しました。詳細は省きますが、Jenkins上でライブラリの依存関係を解決し、設定ファイルを含めて1つのアーティファクト（zipファイル）にまとめ、各サーバーに配布する方式を採用しました。

### システム移行の実施設計
#### チーム体制
今回のシステム移行作業は深夜に実施するため、翌営業日（移行完了から数時間後）の業務開始にともなって問題が発覚することが指摘されていました。このため、5名のチームを移行作業をするグループとその後の問題に対処するグループの2つに分け、体制を組みました。

1. システム移行作業を実施するグループ（3名）
1. システム移行の実作業は担当せず、およそ15時までの問題に対処するグループ（2名）

#### 動作確認手順
システム移行にあたって、動作確認手順を作成する必要があります。まず、ブラックボックステストとして、Web/API フロントエンドの各機能について、動作確認手順を作成しました。

- 正常系については、全ての決済手段で確認する
- 異常系については、一部のよく使われる手段だけ確認する（全ての異常系を確認すると時間がかかりすぎるため）

また、ホワイトボックステストとして、ロールごとにアクセスログやエラーログを確認し、おかしなログが出力されていないことを確認するようにしました。

#### リハーサル
移行リハーサルは開発環境とステージング環境で行いました。開発環境でのリハーサルはコンポーネント単位で行い、主要な処理が問題なく行えることを確認しました。ステージング環境でのリハーサルでは、本番と同じ設定ファイルを使うことで、より本番環境に近い状態での移行リハーサルを行いました。
ただし、決済代行会社からの通信は稼働中の本番環境に到達します。このため、十分な試験ができたとは言えない状況でした。

### システム移行作業
#### 作業計画
このため、実際の移行作業をエンドユーザーおよび売上の影響が小さい深夜（早朝）行うことでリスクヘッジを行いました。また、移行作業自体を連携する各システムに事前告知し、何か問題があった場合はすぐに連絡するようにお願いしておきました。

#### 切り替え失敗
1回目の移行の確認中、とあるキャリアの動作確認で正しく動作しないことが判明しました。この他にも小さな問題が複数見つかり、ロールバックを行いました。このキャリアで正しく動作しなかったのは、通信相手となる決済代行会社（通信キャリア）1社のファイアウォール設定の変更依頼を行っていなかったためでした。

#### 2回目の移行作業
ロールバックの原因となったファイアウォール設定の変更依頼を決済代行会社に依頼して2日後、設定が変更されていることを確認しました。また、他の決済手段についてもファイアウォール設定の再点検を行い、問題がないことを確認しました。
2回目の移行作業では、1回目で実際にかかった時間をもとにタイムラインの時刻・順序を修正しました。
そして当日。深夜の作業となりましたが、無事完了することができました。

### 今後の展望
#### フレームワークの導入
これからは、システムに手を入れないことで壊さないように気をつけるのではなく、適切な技術を選択・導入することで、壊れない（壊れにくい）システムへ変化させることが大切だと考えています。

#### リモデリングとマイクロサービス化
プレミアム課金システムは動画システムのモノリスから分離されましたが、ひとことでプレミアム課金システムといっても内部には様々な機能があります。いわば、プレミアム課金システムというモノリスです。
契約を管理する機能、プレミアム会員状態を管理する機能、債権を管理する機能、入金の消し込み・誤差の検出、処理を行う機能は、それぞれ必要としている人（部署）が異なりますし、可用性要件、性能要件、セキュリティの要件も異なります。
エンドユーザーの期待に応えつつ、会社から求められる経理処理、監査といった業務にも適切に対応するためには、マイクロサービス化していくことが最良の方法だと考えています。

#### エンジニアが成長できる環境づくり
プレミアム課金システムを開発・運用しているエンジニアは私を含めて5名おり、そのうち2名はドワンゴ入社後1年未満です。決済に関わるシステムはエンドユーザーからの問い合わせも多いですが、運用の効率を上げることで開発と運用のバランスを取っていくことが必要です。
運用を軽視してはいませんが、日々の仕事が定型運用ばかりになってしまうとエンジニアとして成長しづらいことは事実です。エンジニアとして成長できる環境を作るためには、運用の品質とボリュームを良い形でコントロールすることが必要だと思います。

### おわりに
最後になりますが、本システム移行を一緒にやってきたメンバー、障害が発生するリスクがありながらも後押ししてくれたマネージャ、サービス改善の基礎となる作業だとして理解を示して下さった部長のみなさまに、この場にて御礼申し上げます。
現在まで残存している共有部分の解消を引き続き行いつつ、時代にあった形でプレミアム会員サービスを提供できるよう、今後も尽力してまいります。
本記事が、インターネットを長年支えてきた（もしかしたら、時代遅れだと言われがちな）サービスのさらなる発展のお役に立てば何よりです。