でこてっくろぐ ねお

でこてっくろぐ(http://dekokun.github.io/)から進化しました。でこらいふろぐ(http://dekolife.hatenablog.com/)の姉妹版。デコテックログ(deko tech log)である

はてなに入社して経験したMySQL4系のオンラインでのmaster切り替え

このエントリははてなエンジニアアドベントカレンダー2015の12日目です。

developer.hatenastaff.com

昨日は id:daiksy によるこちらのエントリでした。

daiksy.hatenablog.jp

現在、私は東京のオフィスで働いているのですが、京都のエンジニアと共に仕事をする場面が多いです。しかし、上記エントリに記載されている様々な取り組みのおかげで遠隔地でもかなり働きやすい環境だなというのを感じています。これからもより皆が働きやすい環境になるように私もいろいろと工夫をしていけたらなと思います。

このエントリでは、今年の8月にはてなに入社した私が最近経験したMySQL4系のmaster切り替え手法について説明します。

背景・概要

はてなの運用するWebサービスではデータストアとしてMySQLが使われる場面が多いです。それにより、入社してから私もMySQLのmasterサーバを切り替える必要があるようなメンテナンスを何度か行いました。

masterサーバを切り替えるようなメンテナンスを行う理由としては様々なものが考えられます。例えば

  • サーバのディスク使用量が増えてきておりこのままでは近日ディスク使用量が100%になってしまいサービスが停止してしまう(からディスク容量の多い別のサーバに切り替えよう)
  • サービスの成長に伴いサーバのCPU使用率が高まってきており放っておくとそのうちシステムが高負荷に陥ってサービスに影響が出てしまう(から性能の良い別のサーバに切り替えよう)

などがパッと思いつくのではないでしょうか

もちろん、上に挙げた問題への対策はサーバの切り替え以外にもいろいろ考えられます。ディスク使用量が多ければ、不要なデータを消す、データを圧縮するなどで対処することが可能な場合がありますし、CPU使用率が上がっている場合はインデックスやクエリ、キャッシュ戦略を見直すなどにより対処することが可能な場合もあります。 しかしそれらの手を打ったとしても、どうしてもサーバを切り替える必要が出てくる場合もあります。また、話は変わりますが、サーバを切り替えるためにはサービスを止めてDBへのアクセスがない状態にして切り替えるのが最も安全ではありますが、可能であればサービスを止めない状態のまま切り替えたいですよね。

このエントリでは、MySQLにおいて、サービスを止めずにmasterサーバを手動で別のサーバへ切り替える作業について説明します。

特に、今回ははてなで行われている「MySQL4」系のmaster切り替え手法、その中でもMySQL4系特有の「masterサーバを切り替える際にどのように旧masterのslaveサーバ群を新masterのslaveにするか」に重点を置いて説明していきます。

MySQL5.0が世に出てからもう10年になります。今ではMySQLのバージョンに関する世間の耳目は5.6や5.7、もしくは、MySQLではないですが互換性のあるDBとしてAmazon AuroraやMariaDBに集まっていますね。そんな中では、MySQL4系を運用している人はかなり少ないのではないかと思います。 はてなでも古いバージョンのMySQLは新しいバージョンのMySQLに切り替えていっているところです。しかし、まだ一部でMySQL4系も使われています。先日私ははてなのサービス運用を行っている中で稀にあるMySQL4系のmasterサーバの切り替えを、人生において初めて行いました。そこで行った作業内容が比較的面白い感じでしたので、その内容をお伝えしたいと思いこの記事を書いています。

具体的にどのように面白いと感じたかと言いますと、以下のようなものが挙げられます。

  • 昔の人はこんなことをやったりしたのか、という歴史を体験するような面白さ
  • MySQLの管理関連のツールの充実していないMySQL4系においてツールを使わずに作業することによって、改めてMHAやmk-slave-moveなどのツールが内部で何を行っているのかについて実感することができた
  • こんな職人芸っぽい作業をしたぞ、という自己満足感
    • これは本来はエンジニアとしてはよくない感情ですね。もっと如何に楽するか、職人芸を撲滅するかを考えなくては。とは思うのですが、一方で、「こんな変なことやってやったぜ」のような喜びはやはりあるなぁと思っています

ですので、読む皆様も上記のような気持ちを感じながら読むと、「俺はMySQL4系なんか触ったこともないし触る予定もない」という人も楽しく読むことができるのではないかと思います。

MySQL5系のmasterサーバの切り替えに関しては、最後におまけとしてほんの少しだけ(本当にほんの少しだけですが)載せてありますのでそちらもご参照ください。

前提知識としてのはてなでのMySQLの冗長構成と、本ブログ内での用語

はてなではMySQLのmasterサーバは以下の図にありますように相互レプリケーションによるactive-standby構成で運用されています。以下図では、太い矢印の向きがレプリケーションにおけるbinlogの流れを表しています。また、以下では「active master」が、現在サービスに使用されているmasterサーバを表し、「standby master」が、active masterに障害が起きた際/もしくは手動でサーバを切り替える際の切り替え役として待機しているサーバを表すこととします。

このブログ内で「master切り替え」といった場合は、active masterとstandby masterを切り替える(旧active masterをstandby masterとしてサービスから外し、旧standby masterをactive masterとしてサービスから外す)ことを表します。

f:id:dekokun:20151210232746p:plain

master切り替えのステップ

さて、手動でmaster切り替えを行うためには、以下2つのステップを実行する必要があります。

  1. 旧active masterにぶら下がっているslaveを新active masterにぶら下げる f:id:dekokun:20151211012501p:plain
  2. Webサーバからの接続を旧active masterから新active masterに向ける f:id:dekokun:20151211012629p:plain

"1."がこのエントリの最大のテーマとなっています。 MySQL5系ですと、MHAという便利なツールがあり、そちらに任せてしまえば、設定さえちゃんとしていれば上記"1.", "2."を機械がよしなに実施してくれるので大変ありがたいのですが、MHAはMySQL5.0以上しか対応していません。

"2." については、一般的にはなんらかの形でDB接続に使用されているIPアドレスを旧active masterサーバから新active masterサーバに付け替える、もしくはDB接続の際にDNSを使用して接続先IPを特定している場合はDNSレコードの書き換えによって接続先のIPを変更する、などの方法でWebサーバからのアクセス先を切り替えている場合が多いかと思います。 はてなでもkeepalivedによるIP付与及びgarpの送信や、(AWSであれば)ENIの付け替えによって、Webサーバからの接続に使用しているIPを新active masterに付与することでWebサーバからの接続を新active masterに向けるようにしています。切り替えサーバが別のネットワークに所属している場合はまた別の方法で…などもありますが、こちらにつきましては、MySQL5でも4でも特に行っていることは変わらないです。詳細は割愛させていただきます。

それでは、実際にMySQL4系において"1."で行うべきであることを知るべく、以下で"1."を更に細かいステップに分けていきましょう。

旧active masterにぶら下がっているslaveを新active masterにぶら下げるステップ

MySQLにおいて、MySQL5.6に搭載されるGTIDを使えない場合、slaveを別のmasterに付け替える際は、slaveの現在のbinlogのポジションが新masterではどのポジションに対応するかを把握してそのポジションに対してCHANGE MASTER TOを打つ必要があります。 実際に行う必要があるのは以下です。

  1. active masterの更新をロックする
  2. 切り替え対象の全slave、及び新active masterの、レプリケーションが最後に実行したクエリのactive master上のbinlogのポジション(説明が難しいですね…Exec_Master_Log_Posのことです)がactive masterの現在のbinlogのポジションに到達するのを待つ
  3. 新active masterの現在のbinlogのポジションを把握する
  4. 切り替え対象の全slaveにて、レプリケーションを止めた後に"3."で把握したポジションへCHANGE MASTER TOを使って切り替え、レプリケーションを再開する
  5. active masterの更新ロックを解除する

うーん複雑ですね。ただ複雑であるだけならいいのですが、active masterの更新をロックしているがために、上記を高速で実施する必要があるというのがまた難しいところです。

これらを自動的に行ってくれているMHAのありがたさが身にしみます。

旧active masterにぶら下がっているslaveを新active masterにぶら下げるステップ(更新ロックを行わないver)

私が作業を行った際は上記の手順で行ったのですが、後から更新ロックが不要な手順が存在すると発覚したのでした。。。

まだ私はこの更新ロックを行わない手順ではslaveの切り替えを行ったことはないのですが、参考までにステップを書いておきます。

イデアとしては、上記更新ロックを行う方法では「切り替え対象のslaveの現在のポジションが新active masterのどのポジションに対応するかが分からないために、active masterの更新を止めることで新active masterと切り替え対象のslaveの更新を止めて、現在のslaveのポジションに対しての新active masterのポジションを把握する」という考えだったのですが、更新ロックを行わない方法は、「切り替え対象のslaveのレプリケーションを止め、mysqlbinlogコマンドなどを使用して最後のクエリをbinlogから特定した後に、新active masterのbinlogからそのクエリに対応するクエリのポジションを把握する」という考えです。

  1. 切り替え対象のslaveのレプリケーションを止める
  2. mysqlbinlogコマンドで最後のクエリを特定する
  3. 新active masterにて、上記切り替え対象のslaveの最後のクエリに対応するポジションをmysqlbinlogコマンドから特定する
  4. 上記ポジションに対してCHANGE MASTER TOを使って切り替え、Slaveを再開する

この手順ですと、基本的にslaveは1台ずつ操作していく形になり、更に確実に操作対象のslaveが遅延するために対象のslaveをサービスから外しながら実施する必要がありその分時間はかかりますが、masterサーバの更新を止めずに済むというのは非常に大きいため、なるべくこちらの手順で行ったほうが良さそうでしたね…次回から気をつけます…

手順

では、(更新ロックを行う方の)コマンド等を書いた手順のお披露目です。職人芸が必要となるっぽい感じがしますね。私が実際に行った手順ははてなお手製スクリプトによってもう少しだけ自動化されてはいるのですが、ほぼ以下のとおりです。

前準備

tmux-cssh などを使用し、複数のサーバに同時にコマンドを投げられるような環境を作っておく

active masterの更新をロック

mysql> FLUSH TABLES WITH READ LOCK;

切り替え対象のslaveのExec_Master_Log_Posがactive masterの現在のbinlogのポジションに到達するのを待つ

切り替え対象の全slave、及び新active masterにて以下を打ち、Exec_Master_Log_Posを確認

mysql> SHOW SLAVE STATUS\G

active masterにて以下を打ってPositionを確認

mysql> SHOW MASTER STATUS\G

両者が一致するまで待つ

新active masterの現在のポジションを把握する

新active masterにて

mysql> SHOW MASTER STATUS\G

ここで、FileとPositionからCHANGE MASTER TO構文を構築する

CHANGE MASTER TO master_host='xx.xx.xx.xx', master_user='xxx', master_log_file='xxxxxxxx-bin.xx', master_log_pos=x;

切り替え対象の全slaveにて、レプリケーションを止めた後に"3."で把握したポジションへCHANGE MATER TOを使って切り替え、レプリケーションを再開する

切り替え対象の全slaveにて、以下を実施

mysql> STOP SLAVE;
mysql> CHANGE MASTER TO master_host='xx.xx.xx.xx', master_user='xxx', master_log_file='xxxxxxxx-bin.xx', master_log_pos=x;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G     // slaveがちゃんと動いているか確認

active masterの更新を解除

mysql> UNLOCK TABLES;

重要なのは、上記を出来る限り素早く(可能であれば5秒以内くらいで)行うことです。

はい、大変ですね!ちなみに、上記手順以外でも細かいことをいうと新active masterでFLUSH LOGSを打つことによってCHANGE MASTER TOを事前に用意しておいたりできるようにしたりという小技もあるのですが、まぁだいたい上記のような感じのことを先日行ったという話でした。

練習なども行った後に実際に作業を行ってみると結構さくっとできるようになったのですが、もうあまり行いたくない類の作業ですね。

感想など

「背景・概要」のところにも記載してありますが、このエントリは、「先日行ったMySQL4系のmaster切り替えの手順が面白かったから共有しよう」という思いの元に書かれたもので、実際に皆様のMySQLの運用にはほぼ立たないかと思いますが、楽しんでいただけましたでしょうか。もし他にもMySQL4系の運用を行っている方がいらっしゃいましたらお友達になりたいなぁと思います。

このエントリを書きながら「この作業は自動化しなくてはあかんやつや」という思いを受けもしましたが、そもそもはてなにおいて現在MySQL4系はだいぶ減ってきているため、自動化も大切ですが、とにかくMySQL4系を撲滅することに力を割かなくてはいけないなというのを強く感じました。 また、このような作業を行うとMHAなどの自動化ツールの重要性が改めてよく分かるなというのは感じました。

本題とは関係ない話なのですが、この記事を書くことによって、これまでなんとなくしか分かっていなかったことが明確になり、非常に勉強になったなというのも感じます。私は前職でアプリケーションエンジニアだったのですがはてなでインフラエンジニアになり、知識が足りないところが非常に多いのですが、そんな私こそブログにいろいろ書いていくことで学んでいかなくてはならないということもまた強く思いました。

おまけ MySQL5系におけるmaster切り替え手法

slaveの付け替えにつきましては、上記でも少し単語を出していますがMHA (MySQL5以上対応)を使うのが最も良いのではないでしょうか。

また、MySQL5系のslaveの付け替えについては私はmk-slave-moveというコマンドをいつも使っており皆さんも使うといいと思います。mk-slave-moveはIt was retired because it didn't work consistently.ということでPercona Toolkitに含まれていないのが非常に残念です。なお、このブログを書くまで私は「mk-slave-moveは、内部でSTART SLAVE UNTIL という構文を使用しており、MySQL5以上でないと使えない」と思っていたのですが、START SLAVE UNTIL構文はMySQL4.1.1から対応されているようでして、もしかしたらMySQL4.1.1以降でしたらmk-slave-moveを使うことができるかもしれません。そちらはまだ検証を行ってはいないため、どなたか使ったことのある方などいらっしゃいましたら教えていただけたらと思います。

他にも、上記に少し書きましたようにMySQL 5.6の場合はGTIDを使用しても楽にslaveの付け替えを行うことができるという話は聞いていますが、私はGTIDについては詳しくないので今後の研究課題としておきます。

最後に

はてなでは、様々なversionのミドルウェアを触ることが大好き(だけどなるべく新しいバージョンにしていきたいという思いを持つ)メンバーを募集しています。

hatenacorp.jp

アドベントカレンダーですが、明日はid:hatz48 です!どんな内容になるのか楽しみです!皆様もお楽しみに!!!!