こんにちは。 急に秋の気配がやってきましたね。 研究が進んでおらず冷や汗をかいております。
今回はこれまでVPS上で運用していたTweet generatorをAWSに移行したので、そのことについて書いていきます。
Tweet generatorはこれまで約50万ユーザの方に支えられ、小さいプログラムながら大きなサービスへと成長することができました。
その過程で、可能な限り運用費をケチったシンプルなVPSでの運用に限界を感じたことは多々ありました。
ストレージ逼迫による約37万ユーザ分の学習データの破棄、アクセスが多かった時期には読み込みが非常に遅いなどの問題がありました。
しかしながら、Tweet generatorも収益化を行ってから1年以上が経ち、得られた収益により様々な恩恵を受けることがありました。
収益化を行っているからには、私にはこのサービスをユーザの皆様に快適に利用していただけるよう努める義務があると思っております。
今回はそんな気持ちを抱きながら、より安定した長期稼働の為に行ったAWSへの移行を、検討から実際の移行についてまとめました。 またTweet generatorの話かよ、と思われるかもしれませんが、まぁ結構大変だったので聞いてくださるとありがたいです。
検討
まずは移行の検討を行った背景について説明します。
まず、これまでTweet generatorを運用していたサーバは、私の公開しているほとんどのサービスの運用を一手に担うConoHaのVPSサーバでした。 ConoHaは学生にやさしく、10%割引で各種サービスを利用することができます。 このサーバ上では常時5つ程度のWebサービス、Discord botなどが動いており、CPUリソースもメモリリソースも常時それなりに逼迫しているという状況でした。
この段階ですでにやばいのですが、2020年2月頃、急にTweet generatorへのトラフィックが増加し始め、一番ひどいときは通常時500ミリ秒以内程度のテキスト生成に数十秒かかるほどにサーバを圧迫していました。
そしてそれなりに重めの学習済みマルコフ連鎖モデルは50GBしかなかったVPSのストレージを即座に食い尽くし、何度かの学習済みマルコフ連鎖モデルの破棄とストレージの追加契約とVPSのアップグレードを余儀なくされました。 この時点ではまだTweet generatorは収益化していなかったため、アクセスが来れば来るほど私が損するという状況にありました。
そして2020年5月頃、友人が広告とか置けばいいんじゃないかと言っていたのをふと思い出し、収益化を行いました。 結果として、すぐにサーバ代をペイするくらいの収益が得られるようになりました。
この時点ではまだサーバ代をケチる精神が健在だったため、この1台にできるだけサービスを詰め込む運用でしばらく運用を続けていました。 Tweet generator自体の設計のやばさもあったため、2020年12月頃には学習済みマルコフ連鎖モデルの管理をファイルベースからPostgreSQLでの管理に切り替え、また学習済みマルコフ連鎖モデルの破棄を行いました。 こうして、つい先日までのVPSでの運用が続きました。
しかしながら、私自身業務でAWSに触る機会が増え、AWSでの堅牢で可用性の高いサービス運用について知見を深めていくと共に、Tweet generatorの運用がやばいということを強く感じるようになりました。 また、個人的に仕事などが増えたことから金銭的な余裕が生まれ、AWSをポケットマネーで自由に触ってみようと思い、趣味でもAWSを触ることにしました。
こうしてAWSでの運用の知見が蓄積されていき、AWSでの運用を検討するに至りました。
AWSでの運用を検討した際に、まずうれしかったのは、またアクセスが集中することがあっても、オートスケールする設計が簡単に組めることでした。 また、もしまたアクセスが集中した場合、その場合はストレージの逼迫が考えられたので、ストレージ容量も簡単にスケールするようなDBが求められました。
AWSはこれらの点をすべて満たしてくれています。
また、他社サービス、具体的にはGCPなども検討しましたが、AWSで求められる機能は実現でき、かつ業務などでAWSを使うことが多いのにわざわざGCPを使うこともないだろうと思い、AWSで運用することを決めました。
要件
今回の移行で求められた要件を書いていきます。
ダウンタイムは最小に
まず今回の移行にあたっての目標は、ダウンタイムを最小にして移行させることでした。
Tweet generatorにはおおよそ毎秒リクエストが飛んできています。 また、ダウンタイムが長引けば、広告収入減につながります。
以上の事情から、ダウンタイムは最小にすることが求められました。
データ損失なく
これは完全に趣味として、データ損失なく移行を進めたいという気持ちがありました。 技術的にも金銭的にも、かかる費用を考えればこれまでのデータを破棄して、まっさらなDBでサービスを継続するのが楽でした。
しかし、実際の企業のサービスなどではそんなことは言ってられないでしょう。 折角の機会なんだからデータ損失なく移行させられたら面白いじゃん?と思い、自分の技術力の範囲でできるだけデータ損失なく移行させようと思いました。
移行
ここからは、実際の移行にあたって発生した問題などについて書いていきたいと思います。
コンピューティングリソースの移行
これは普通にEC2上にdocker-composeでコンテナ群を立ち上げる方式で、EC2インスタンスをオートスケーリンググループで立ち上げてやることにしました。 負荷分散は普通にALBでやってあげている感じです。
コンピューティングリソースの移行はそんなに苦労しなかったのですが、唯一苦労した点と言えば、Graviton、つまりaarch64なAWSインスタンスへのdocker-composeのインストールでした。 docker-composeの公式はaarch64向けのバイナリを配布していません。 結論から言うとこちらの回答を参考にしたらすんなりと行きました。
何故苦労してまでaarch64を使いたかったのかというと、価格とパフォーマンスが良かったからです。 詳しくはこちらの記事がわかりやすくてよいでしょう。 Tweet generatorの場合だと、t3.microを時間単価0.0104USDで使うところを、t4g.microを時間単価0.0084USDで使うことができます。 もちろん、性能の比較は難しいですが、単純にvCPU数とRAM容量を見た限りでは、この二つに差異はありません。 また適当な機会にt3.microも導入して、ALBレスポンスタイムやロードアベレージがどうなるかを調査するなどして、性能については詳しく追っていきたいと思っています。
DBの移行
DBはVPS上でDockerを使い立ち上げていたPostgreSQLからRDSのPostgreSQLに移行しました。
結論から言うと、DBの移行には普通にSQLダンプを使って移行した後、自前で作ったマイグレーションツールを使って細かい差分を吸収してやりました。
DMSとの闘い
当初はAWSのサービスであるDatabase Migration Service、DMSを用いてDBの移行を行う予定でした。
しかし、DMSでは学習済みマルコフ連鎖モデルを保持しているテーブルの移行に何故かエラーも吐かずに終了してしまいました。 これは全くの原因不明で、この調査の為に数日を溶かしたのですが、結局何もわからず、DMSでのDB移行を諦めました。
ダンプファイルとの闘い
結局、普通にダンプを利用することにしました。 まずは移行元データベースのダンプを取ってきて、ダンプしたSQLを利用してRDSにデータを突っ込んでやります。 このダンプファイルが294GBありました。 294GBも何を保持しているんですかね……
ダンプファイルを今度はRDS上のPostgreSQLに突っ込みます。 この作業に162時間かかりました。 皆さんがSQLを話し終わるまで162時間かかった校長先生かな?
さらにこの手続きが終わった後、ダンプからの移行中に発生した移行元DBの変更を移行先DBに反映してやるために、お手製ツールで移行元DBと移行先DBの差分を取ってきて反映、みたいなことをしました。
これだけの時間、これだけのリソースを消費して移行したデータなので、十分に活用していきたいですね1……
DNSの移行
最後に行ったのはDNSの移行です。
移行に伴いTweet generatorのドメイン名を変更することも考えたのですが、前回ドメイン名を変更したときに、既にTwitter上には古いドメイン名でのリンクが大量に転がっていたため、古いドメイン名からのリダイレクト処理をずっと走らせる必要がありました。 今回もそれをやるのは避けたかったこともあり、同じドメイン名でサーバの移行のみをすることにしました。
基本的に行ったことは、これまでVPSに向いていたAレコードをALBのドメイン名のCNAMEに置き換えてあげることだけでした。
しかし、DNSは基本的にTTLの間はキャッシュがきくので、移行直後は移行元サーバと移行先サーバの両方にリクエストが来ます。
この際、特に対策を取らないと移行元でのDBへの変更は移行先DBへ反映されません。 そのため、事前にAWSのセキュリティグループでVPSのIPアドレスからRDSへのアクセスを許可するように設定しておき、移行のタイミングで移行元サーバのアクセス先DBをRDSに変更することで、移行のタイミングで移行元サーバと移行先サーバ共にRDSにアクセスするように変更しました。
こうすることで、DNSの移行期間中も、データを失うことなくサービスを継続させることができました。
結果
結果として、ダウンタイム2時間以内で、ユーザテーブルとマルコフ連鎖モデルテーブルに関しては2データ損失なく移行を終えることができました。 いや結構ダウンしとるな?
ダウンタイムは、停止後に最終のデータ移行を行ったのと、移行後のデータベースに異常がないかの確認、特にsequenceテーブルが壊れてしまっていたのでそれの修正と、移行元サーバのアクセス先DBの変更の為にどうしても必要になりました。
最後に
長くなりましたが、Tweet generatorのAWSへの移行はこんな感じで行いました。
サーバ移行を真面目にやったのは初めてだったのですが、なかなか苦労しました。 今後はもうあまりやりたくないですね……
もしこの記事が今後AWSに触れる方などの参考になることがあれば幸いです。
最後になりましたが、これからもどうぞよろしくお願いいたします。