がんばらないDBaaSの作り方


Loading...

はじめに

はじめまして、KCS部のmaruです。KCS部では、部長といくつかのサービスのオーナーをやっています。

KCS部は、KADOKAWAグループ向けプライベートクラウド(以下KCS)を提供しており、私がオーナーをしているサービスにはDataBase as a Service(以下DBaaS)があります。 主な利用者は株式会社ドワンゴがサービスを提供している『niconico』です。

今回はKCSが提供しているDBaaSについて、いかに頑張らないで運用できるようにしているかについて投稿します。

規模で見るKCS DBaaS

KCSが現在提供しているDBaaSは下記の3つです。

  • KCS RDB基盤 for MySQL
    • バージョン:MySQL 5.7系
    • MySQL数(概算):450
    • masterの総データ量(概算):3TB
  • KCS Cache基盤 for Redis*1
    • バージョン:Redis 3.2系
    • Redis Cluster数(概算):210 Clusters (Redis数:720)
    • masterの総データ量(概算):100GB
  • Elastic Stack as a Service
    • バージョン:Elasticsearch 7.4系
    • Elasticsearch Cluster数(概算):15 Clusters
    • 総データ量(概算):8TB

上記サービスを 私含む4名( DSREチーム*2 ) ですべて構築・運用しています 。

穏やかな気持ちでDBaaS運用をするためのアーキテクチャ

合言葉は、『シンプル』です。

1. 何も考えずに使える構成

(ESaaSを除き)DBaaSは、すべてVMware vSphereの仮想マシン上で稼働しています。
また、仮想マシン一つあたりに、MySQLやRedisは一つという、とにかくシンプルな構成*3です。

MySQL, Redisごとの仮想マシン
MySQL, Redisごとの仮想マシン

Redisでサーバーリソースの効率的な利用を目指す場合、通常は1つのサーバーに対し、複数のRedisをプロビジョニングするかと思います。
※Redis はシングルスレッドでの処理が中心で、バックグラウンドでファイル永続化が走る程度*4なため。 また、メモリも主にファイル永続化時にのみ最大2倍利用するため、オーバーコミットが可能です。

またオーバーコミットをより安全にするには、Redisをdockerコンテナに詰めて、コンテナ単位でリソース制御をすることになると思います。

しかし、私たちは下記の理由からRedisやMySQLと仮想マシンを1対1対応とし、オーバーコミットしないことにしました。

1-1. 仮想マシン単位のDisk I/O負荷を少なくする

障害による仮想マシン再起動時には、MySQL, Redisの数・規模に比例した大量のRead Disk I/O*5が発生します。
KCSが管理しているvSphereでは、1仮想マシンが利用するストレージは1つに絞っており、
仮想マシンの集約を効かせすぎてしまうと、障害時に単一のストレージに負荷が集中してしまい Noisy Neighboursによるカスケード障害に至ることもあります。

また一般に、突発負荷によるNoisy Neighboursは事前の回避が難しいです。 MySQL, Redisの数だけ仮想マシンが存在し、それがvSphere全体に分散配置されることで、Noisy Neighbours問題の緩和を意識しています。

仮にVM1つあたりにMySQLやRedisを複数載せていた場合、障害時の影響範囲
仮にVM1つあたりにMySQLやRedisを複数載せていた場合、障害時の影響範囲

1-2. 障害時に仮想マシン名のみで影響サービスがわかるようにする

HW障害時にKCSが管理するvSphereサービスから、速報で自動周知される情報は影響のあった仮想マシン名のみです。

仮想マシン名がそのままMySQL名やRedis名に1対1対応していることで、DBaaSチームが仮想マシン名からMySQL名・Redis名を名寄せせずとも 利用者(主にniconicoのエンジニア)は、自身のMySQLやRedisが障害に巻き込まれているかを瞬時に把握することが可能となっています。

ホスト障害時の自動発報例
ホスト障害時の自動発報例

DBaaSから上記のような影響のあったMySQLやRedisを自動報告しないのか?と疑問に思われるかもしれません。
DBaaSにおけるMySQL, Redisの障害発生時のOnCallは、すべてDSREチームが受け、対応するようにしています。
一方で、すべてのMetricsを監視のSaaSであるDatadogへ送信しているため、クリティカルなサービスは自身に必要なサービスレベルで、適した通知手段を使い、任意の通知先に送るよう、Datadog上でカスタマイズ可能にしています。

つまりDBaaSにおいて、MySQL・Redisレイヤーの障害は、クリティカルなサービスを除き利用者は関知すらせずに済むため自動報告もしていません。
しかし、vSphereチームからの自動報告でDBaaSが障害範囲に入っていると、DBaaS利用者の心境として正常性を確認したくなるものです。
※AWSを利用している時、TwitterでAWSの障害情報が流れてきたら、アラートが出ていなくてもついつい確認してしまいますよね?

vSphereチームからの自動報告でそもそも巻き込まれてないことがひと目でわかることは、精神衛生上非常に重要なことです。

2. 意識の低い自動化によるプロビジョニング

続いて運用の話です。運用には定常と突発の二種類があります。

f:id:kdx_writer:20200327135606p:plain
定常的な運用・突発的な運用

定常的な運用は、特定の人・チームがボトルネックにならないような意識が重要だと考えています。
特に定常的な運用で多いのは、利用者からの新規MySQLやRedis作成依頼です。
究極系はAWSなどのPublic Cloudで、スマートなWeb Consoleから利用者自身でプロビジョニング出来ますね。

一方で、KADOKAWAグループ内などの限りなく身内向けのPrivate Cloudですと、Web Consoleはある程度意識が低いものでも許されます。
RDB基盤 for MySQL では、SpreadSheetに必要なスペックを埋めると生成されるURLから、パラメータ入力済みのJenkinsに飛べるようになっています。
SpreadSheetを利用者チーム内で確認し合い、問題なければJenkins JobをKickするだけで、MySQLが利用出来るようになっています。
※Jenkinsの裏側ではansibleがkickされ、「VM作成->OS構築->MySQLインストール&設定->監視開始」までが一気通貫で完了します

スプレッドシート + Jenkinsによる、意識の低い払い出し用Webコンソール
スプレッドシート + Jenkinsによる、意識の低い払い出し用Webコンソール

また2019年末にリリースしたElastic Stack as a Serviceでは完全セルフサービスとなっており、 利用者がLDAP認証でログインしたJenkinsのJobをKickすると、Slack DMにてパスワードなどの情報が送られ、ElasticsearchやKibanaがすぐに利用出来るようになっています。

f:id:kdx_writer:20200327160203p:plain
ESaaSの払い出し時のSlack DM例

3. 楽するための可用性設計

突発的な運用といえば、障害対応です。
この項目では、RDB基盤 for MySQLのみに焦点を絞ってお話します。

3-1. HW障害時

vSphereにはvSphere HAという機能があり、HW障害時に別HWにて仮想マシンが自動で再起動してきます。 そのため、自動再起動に耐えられる設計にし、何か問題が発生したら再起動をしてしまえばいい、という割り切りを行いました。

RDB基盤 for MySQLはすべてのMySQLが下記のような構成になっています。

  1. クラッシュセーフにする
    • InnoDBを強く強く強く推奨する
    • masterでは
      • innodb_flush_log_at_trx_commit=1
      • sync_binlog=1
    • slaveでは
      • relay_log_info_repository=TABLE
      • relay_log_recovery=ON
  2. どんな小規模でもBackup Role*6のSlaveを用意する
  3. 準同期レプリケーション*7にする
    • マスターでデータロストレベルのクラッシュしてもBackup RoleのSlaveにデータ同期がほぼ終わっている状態を維持

3-2. シングルスレッドのDisk I/Oレイテンシが問題にならないように

また、ネットワーク接続ストレージを利用していると、シングルスレッドのDisk I/O Latencyがどうしても遅くなってしまうケースがあります。
MySQLでは、ほぼ全てのDisk I/Oがマルチスレッドで処理出来るのですが、唯一レプリケーションの処理がデフォルトではシングルスレッドです。

※レプリケーションについては、yoku0825さんの資料が非常に参考になります。いつもお世話になっています。
その行がマスターにINSERTされてからスレーブでSELECTされるまで - Speaker Deck

デフォルトではシングルスレッドですが、MySQL5.6からマルチスレッドスレーブ(以下MTS)が利用出来るようになり、5.7系からはさらに改良されました。
そのためRDB基盤 for MySQLでは、対応バージョンを5.7系にし、全MySQLでMTSを有効にしています。

3-3. カーネルパニック時

管理している仮想マシン数が多いと、どうしてもカーネルパニックに遭遇します。
基本的に綺麗に死んで再起動してくれた方がありがたいので、カーネルパニック時は自動再起動するような設定を入れています。

  1. /etc/sysctl.confkernel.panic = 10 を設定します
  2. sysctl -p で設定を反映させます
  3. sysctl kernel.panic で確認します

3-4. Out Of Memory時

OOMにならないように監視することは前提ですが、 万が一OOM Killerが実行され、特定プロセスのみkillされると少し面倒くさいです。
そのため、OOM発動時はカーネルパニックになるようにしており、前述の設定の通り仮想マシンが再起動されるようにしています。

  1. /etc/sysctl.confvm.panic_on_oom = 2 を設定します
  2. sysctl -p で設定を反映させます
  3. sysctl vm.panic_on_oom で確認します

3-5. やっていないこと

  1. MySQL slaveのフェイルオーバー
    • サービス復旧は早くなりますが、vSphere HAに甘んじています

まとめ

最後まで読んでいただき、ありがとうございました。
本投稿では比較的少人数かつ低コストで運用するためのDBaaS全体を簡単にご説明しました。

今回紹介したDBaaSを構築・運用しているチームとして、DataStore Reliability Engineer(以下DSRE)のチームが組織されています。 なおDSREチームの4名は、今回紹介したDBaaS以外にもそれぞれ3-4つほどのサービスの開発・運用を兼務している状況ですが、 それが出来ているのも、今回紹介したような楽をするための設計を重視しているためだと考えています。

当然クラッシュセーフのためにMySQLで sync_binlog=1 にしていれば、Disk I/O負荷は大きくなり、結果としてvSphereで利用しているブロックストレージにそのコストを転嫁することになっています。 一方でそのブロックストレージもDSREの管理物です。

つまり、DBaaSだけを見るのではなく、Private Cloud全体でどこに負担をさせ、どこで手を抜くかを設計することが非常に重要だと考えています。
これを横断的に考えられるように、DSREを組織しました。

今後、順不同・時期未定ですが、一つ一つのトピックをより深堀りした記事も投稿予定です。

今後、本ブログで記事化予定のもの

下記のようなネタを考えています。
まだまだ走り出したばかりの組織ですが、意見交換やお話されたい方などいらっしゃいましたら、@maru までお気軽にお声がけください。

  1. Disk I/Oによる不安定さ回避のためにRedisのDisklessアーキテクチャで運用している話
  2. RDB基盤 for MySQLの監視アーキテクチャをコスパとSLA両立のために変更した話
  3. DSREを組織した話を詳しく

執筆済みDBaaS関連記事

engineering.kdx.co.jp

engineering.kdx.co.jp

engineering.kdx.co.jp

さいごに、一緒に働ける仲間も募集しています。
興味のある方は採用サイトをご確認の上、もし希望するポジションがない場合でも、オープンポジションからご応募お待ちしています。

*1: RedisはRedis Labs Ltd.の商標です。その権利はすべてRedis Labs Ltd.に留保されています。株式会社KADOKAWA Connectedでの使用は、参照目的のみを目的としたものであり、Redisと株式会社KADOKAWA Connectedとの間のスポンサーシップ、推奨、提携を示すものではありません。

*2:DSRE = DataStore Reliability Engineerの略。このチームでブロックストレージやファイルストレージも見ているため、DBREではなくDSREという名称。

*3:監視用agentなどは同居しています

*4:Redis 3.2系現在です。Redis6系以降ではマルチスレッド対応していくロードマップになっています。

*5:MySQLではクラッシュリカバリ、Redisではaofやrdbからの再生が該当

*6:開発者が重いクエリを投げたりするのにも使ってます

*7:今はまだ準同期レプリケーションに助けられたことはありません