秘技!無停止ドメイン移行芸

この記事はCyberAgent Developers Advent Calendar 2016の5日目の記事です。

CyberAgent Developers Advent Calendar 2016 - Adventar

4日目はyodareさんの産まれたてのAbemaTVは、ユーザにどう受け入れられていたか?:Twitterデータを用いた分析でした。

ちなみに弊社のAdvent Calendarは3年連続の参戦。過去2回の記事はこちら。

ドメイン変更とは

ドメイン変更・・・。それはWeb上で事業を生業とする者にとっては、時にはどうあがいても抗うことの出来ない哀しみの案件である。

FRESH!の悲哀

  • 1/21に**AmebaFRESH!**としてローンチ
  • 4/1にCyberAgentからAbemaTVに事業譲渡。サービス名を**AbemaTV FRESH!**に変更し、**一度目のドメイン変更**
  • 6/1に**AbemaTV FRESH!**から**FRESH! by AbemaTV**にサービス名を変更
  • 12/1にAbemaTVからCyberAgentに再度事業譲渡。サービス名を**FRESH!**に変更し、**二度目のドメイン変更**

基本的な移行スケジュール

Web/APIだけを提供しているサービスであればそれほど細かいことを気にしなくていい。しかし、iOS/Androidアプリを提供しているため新ドメインに向いてある新アプリへのアップデートを促しつつシームレスに移行する必要がある。

一度目のドメイン移行は本当に時間がなかった。3月末くらいに通達があり、二週間くらいしかなかった。ドメインと証明書を待たないと作業できないものもあるので、実作業は一週間くらいだった気がする。

生放送サービスの悲哀なのだが、あまりにも急転直下すぎたため既に移行のターゲットの日に多くの番組の配信予約設定がされている('A`)ということにある。そこそこ人気の番組があると痛いので、腹をくくって完全無停止でドメイン移行することにした(当たり前だけどサービス止めたほうが圧倒的に楽です)。

二度目に関しては一度目の倍くらいの時間の猶予があったのと、一度目の経験があったのでわりかし余裕だった。 

iOSアプリの申請

スケジュール的な最初のチェックポイントはiOSアプリの申請タイミングにある。ドメイン移行は最終的なターゲットの日に終わればいいというわけではなくて、iOSの新アプリの申請時点では新ドメインでAPIが利用できるようにしておかなければならない。アプリ対応が大変だが、サーバサイドは本当に時間がない。

ストアに出す。そして強制アップデート

アプリの申請が通ったら実際にアプリをストアから市場に出すわけだが、全てのユーザーが律儀にアップデートしてくれるわけではない。

そこで強権的ではあるが強制アップデートの出番となる。サーバはもちろん強制アップデートをチェックするAPIを用意しておき、アプリは最初のバージョンから強制アップデートを発動させる仕組みを備えさせておかなくてはならない。そうでなければこのようなドメイン移行をせざるを得なくなった場合、多くのユーザーをロストすることになる。

強制アップデートの大義名分は、全てのアプリに新ドメインのAPIを利用させるため。

どうやるの

ここからは具体的な方法論の話。 

Replicated Service Pattern

移行スケジュールでも述べたように、基本的に新旧ドメインが共存する期間が発生します。手っ取り早くこの構成を作るには、既にある既存サービスのレプリカセットを作成しELB+ドメインだけすげ替えるというもの。

FRESH!がFull DockerでAmazon ECSで稼働していることはAWS Summitやこのブログでも紹介してますが、このようなコンテナベースの開発をしているとこの構成はあっさり作ることが可能。コンテナベースで作ることのメリットはその環境の再現や複製のしやすさであり、また環境に依存するようなパラメータがある場合は環境変数を変更するだけで済むことにあります。

Microservices構成でやっているので、Publicなサービスは基本的にこれをサービスの数だけ行えばいい。

Single Service + Multi LB Pattern

Replicated Service Patternは手堅いが、リリースがある場合はデプロイを双方にしないといけなかったり、移行期間中にDBマイグレーションが必要になった際のケアが難しくなるという欠点もある。二度目のドメイン移行の際は、以下の構成での移行を試みた。

Serviceは1つのままで、新旧のELBを同じサービスにつけるという方式。この方式であればリソースも効率活用でき、デプロイ面のケアを考えることもなくなる。一度目の移行の際にReplicated Service Patternを採用した理由として、Nginxとアプリケーションが完全に新旧どちらのドメインにも依存しないように作りきれていなかったためであり、二度目の際はその問題がクリアされていたためこの方式を採用。

強制アップデートとアプリ側のケア

アプリに強制アップデートを投入すれば、旧アプリも新アプリにアップデートしないと利用できないためアプリが旧APIを参照することは無くなる。もちろんリダイレクト投入後も、旧アプリは少なからず存在しており、旧アプリは旧ドメインの強制アップデート確認APIを実行するため、少なくともGET APIに関してはアプリ側ではリダイレクトを解釈し、その先のAPIを実行できるようにしておくことが重要になります。

リダイレクト(Nginx方式)

強制アップデート投入後に新アプリの浸透率を見つつ、いよいよ旧ドメインから新ドメインへのリダイレクトを実施します。一度目のドメイン変更では、リダイレクト用のELBとNginxを大量に立てるという方式を採用。

リダイレクト用の汎用的なNginxコンテナを用意するというのがこの方式のミソ。ドメイン毎にNginxのserverディレクティブを記述するという方式では、万が一変更がある場合に全ての旧ドメインが影響を受けることになるし、この方式だと環境変数でいい感じに解決できるようになる。

Nginxのconfファイルはこんな感じになります。entrykitを使って環境変数に受けるドメインとリダイレクト先のドメインを設定できるようにしておく。

server {
    listen 80;
    server_name localhost {{ var "SERVER_NAME" }};
    charset utf-8;

    location / {
        rewrite ^(.*) {{ var "TARGET_SERVER_HOST" }}$request_uri? permanent;
    }
}

Nginxのモジュールを使って環境変数対応するにはperlかluaモジュールを使う方法があるが、perlをDockerイメージにインストールするとイメージがデブるという問題があるので、entrykitでdocker run時に変数を埋め込む手法の方が好まれるかと思われます。

リダイレクト(S3方式)

上記はNginxを立てるという方式ですが、AWSであればS3とCloudFrontを使えばサーバを立てることなく旧ドメインから新ドメインへのリダイレクトが可能です。S3のRedirection Rulesというやつです。

ウェブページリダイレクトの設定 - Amazon Simple Storage Service

一度目の移行の際は、リダイレクト投入後に万が一問題がおきてトリッキーな事象に対応できなくなることを恐れたので100%コントロールできるNginx方式を選択しましたが、特に何も問題なかったので二度目の移行ではRedirection Rules方式を採用。HTTPSなのでCloudFrontも必要。ELBもEC2もいらないのでコスト的にも当然こちらの方に優位性がある。

先行で移行できるものはしれっとする

基本的に移行ものはWEB/APIといったサービスが本丸なので、本丸を実施する前に移行できるものはひっそりとどんどん移行していった。チャット(Socket.IO) や動画のPlaylist、配信サーバのドメイン等がそれにあたる。配信サーバに関してはそれぞれEIPを振っているので、新旧ドメインで同じEIPを向くようにしておけば時間が経てば自然に完全移行が済むし、チャットに関してはCORS制約にさえ気をつければデプロイのタイミングでシュッと移行ができる。

ドメイン移行の副産物

ドメイン移行を無理やりポジティブに捉えれば、実はまったく悪いことばかりではなかったりします。二度目の移行の際はドサクサに紛れてELBからALBへの移行を変えたりすることができ、コスト削減に寄与したり、HTTP2化される等の副産物があった。

急転直下のドメイン移行を支えた技術

AWSといったクラウドの利用や、インフラのコード化(Terraform)、Dockerといったコンテナ技術があったからこそであり、普段からこの技術をプロジェクトに定着させていたからこそ短期間で実現できたかと思われる。

弊社は非常に組織変更が多いわけですが、このようにしれっとやってしまうと「何だドメイン変更って簡単にできるんだ」とビジネスサイドの人間に要らぬ誤解を与えてしまうかもしれないので、決してそうではなく、積み上げてきたことを一掃しかねないということをちゃんと認識させないといけないw

そして何よりも開発者のメンタルを破壊しかねないので、変えなくてもいいドメインを選択せねばならないのです(これは自戒でもある)。最初の通達の瞬間のツイートがこちら。

二回目も一瞬折れかけたけど、サービス的にはようやくあるべき方向に戻れたので個人的にはかなりよかったと思ってる。いやー、一度目の絶望感ったらなかったねw 例えるならばわたしの戦闘力は53万ですくらいの衝撃だったので。

まとめ

Immutable Service Name, Immutable Domain Nameマジ重要。

というわけで明日の6日目は、yutoriさんです。