データセンター単位の大規模障害に見舞われたとき、 mysqld で error log を見る前に確認していることなど

こんにちわ。せじまです。今回は、実際役に立つケースは限られていますが、備忘録として書いておきます。

はじめに

私は(たしか)2003年くらいから、しごとでMySQL使っていたので、もうかれこれ18年くらいは使ってるんですが、それだけ長いこと使っていると、大規模な障害というものにも何度か出くわしたりしました。

ざっくりとした規模感で言いますと、データセンターでラック一本まるごと異常が発生するケースとか、ラック一本にとどまらず、データセンター内のサーバの何割かが異常に見舞われる、といった規模で発生した障害です。物理的な故障で障害が発生したこともあれば、ネットワーク機器の不具合により、ラック一本まるごと切り離す必要性が発生したこともありました。

ラック丸ごと一本ならまだいいんですが、データセンター内のサーバの何割かがまとめて落ちるという規模になると、複数の mysqld が同時に落ちるとか、failover繰り返したりとか、確認しなければならない mysqld のインスタンス数や error log の数が、膨大なものになります。

「異常が発生したデータセンター、まるごと切り離せば良いのでは」というご意見もあるのでしょうが、すべてに於いてそのようにできるかはむつかしいところかと思いますので、

  • 一度に複数の mysqld が落ちる、ある程度大きな規模の障害が発生した場合、どのように切り分けるか

といった観点で、私がやっていることを書いてみようと思います。

普段使っているツール類は、大規模障害時には機能しないことを想定しておく

常日頃からMySQLの運用を最適化するために、ツール類を内製している方も少なからずいらっしゃるかと思います。ただ、データセンター内のサーバの何割かが影響を受ける規模の障害が発生している場合、ネットワーク機器等の基幹部分で問題が起きてることがあります。そういった状況下においては、 mysqld が稼働しているサーバやインスタンスの死活監視を、正常に行えるとは限りません。また、 replication の構成を可視化するためのツール類を内製していたとしても、ネットワーク周りが安定していないと、うまく機能しないこともあります。

ツールを内製して、 mysqld 本来の機能や mysql client の機能等が隠蔽されればされるほど、そういった状況下では思うように作業ができなかったりしますので、「MySQL本来の機能で最小限できること」を、普段から整理できてると良いのではないかと思います。

show global status like 'uptime';

私が最初に実施しているのは、トリアージです。そしてそのために、最初に切り離すべき mysqld 、継続して利用する mysqld を選別しています。

なにはともあれ、

Uptime を確認し、継続して利用する mysqld を選別します。

共有ディスクとDRBD使ってActive/Standby構成のHAクラスタを組んでいる場合はともかく、不自然に Uptime が短い場合、 mysqld が restart している可能性があります。

mysqld を起動する際、 mysqld_safesystemd管理下で mysqld が起動している可能性がありますが、みなさんは、すべての mysqld がどのように起動していて、自動で再起動されるようになっているかどうか、すべて把握できているでしょうか。また、すべての mysqld において、 crash safe な設定が施されており、 binlog も storage engine も、 crash safe であることが保証できる設定になっているでしょうか。

mysqld の数が増えたり、使っているMySQLやOSのバージョンが多岐にわたったりしていると、かなり難しいかもしれません。

そこで、大規模障害が発生した場合は、まずは mysqld 自身の Uptime を確認し、不自然に短いものについては「できればサービスから外せないか」と考えていくのが無難だと思います。 mysqld に限らず、データを永続化するためのコンポーネントの uptime が長いのは自然なことであり、明らかに uptime が短いのであれば、そのコンポーネントを使い続けることに、疑問を感じるのではないでしょうか。

私は、MySQLに関してはActive/Standby構成のHAクラスタはあまり使いたくないのですが、その理由の一つに、この方式だとActive/Standby切り替わったときに mysqld の Uptime が短くなってしまう、という理由が挙げられます。

show process list; で Binlog Dump Thread の有無を確認する

ネットワーク機器などで大規模障害が発生していると、まず fail over が発生しています。ある程度の台数のMySQLを使っているならば、OrchestratorのようなOSS、あるいは内製のツールで、自動で fail over して、 replication の構成を自動で変更できるようになっているかもしれません。

ただ、ネットワークが不安定で、かつ大量に fail over が発生していた場合、すべての mysqld で replication の構成を確認するのは、かなり骨が折れる対応になるかもしれません。

最終的には SHOW {REPLICA|SLAVE} STATUS から情報を取得することになると思いますが、 MySQL と向き合うエンジニアにとってまず知りたいのは

ということです。どの mysqld インスタンスが更新を受け付けていて、 binlog を吐いているのか、まずはそれが知りたいのです。

ネットワークが不安定な状態だと、すべての slave(さいきんのMySQL用語だと replica)にCHANGE MASTERが行き渡ってない可能性もあります。そうであるならば、「ぶら下がっている replica が存在するかどうか」を確認する方が確実といえましょう。

SHOW {REPLICA|SLAVE} STATUS の結果を記録する

Uptimeに問題がなさそうでも、最新のbinlogを受け取る前に master(ないしsource)が落ちてしまって、CHANGE MASTERできなかった slave が存在している可能性があります。replicationの状態を記録しておくと良いでしょう。

error log を確認する、 mysqld が zombie process になっていないか確認する

だいぶトリアージが進んできたら、 error log など確認していきましょう。 I/O error などが出ていないかとか、急に InnoDB: page_cleaner: 1000ms intended loop took みたいなログが増えていないか確認すると良いでしょう。例えば IaaS で一時的にディスクの性能が劣化した場合、 InnoDB: page_cleaner: 1000ms intended loop took ~のログは出やすくなります。

あと、ディスクから応答返ってこないような状況だと、 mysqld が落ちきれずに zombie process になってしまい、 SIGKILL でも落とせないことがあります。そういう場合は、諦めてOSまるごと落とすか、 iptables ないしそれに準ずる手段で、tcp:3306 に接続できなくしてやりましょう。アプリケーションサーバが 不安定な mysqld に接続し続ける状況が継続すると、被害が拡大していく恐れがあります。

おわりに

データセンターなどの規模で冗長化していたり、そもそもオンプレやIaaSでMySQLを自前運用していないのであれば、こういったことはあまり考えなくても良いのかもしれません。ただ、MySQLに限らず、大規模な障害が発生し、普段使っているツール群が使えなくなることは、稀によくあることかと思います。そういった状況を想定して備えておくことは、悪いことではないと私は考えています。