dshimizu/blog/alpha

とりとめのないITブログ

Stand Alone状態になった時のMySQL InnoDB Cluster復旧メモ

手元の環境でInnoDB Clusterに含まれるMySQLを全て手動で停止させたあとに、 mysqlsh経由でClusterを起動するため、メタデータストアからクラスタ情報を取得しようとすると以下のエラーが発生しました。

mysql-js> var cluster = dba.getCluster('ClusterDev')
Dba.getCluster: This function is not available through a session to a standalone instance (RuntimeError)

クラスタ内のどのサーバも切り離されてシングル構成となってしまい、mysqlsh経由からクラスタ情報を取得できなくなったようです。

詳しいことはよくわかっていませんが、mysqlshだけでは解決できず、各MySQLへのオペレーションも必要でしたので、復旧時にやったことをメモしておきます。 ただ、以下に記載するやり方が正しいのかはわかりません。

前提

元の構成は以下です(以前の記事の際に構成したものです)。

復旧のためにやったこと

まず各MySQLgtid_executedの値を確認します。 この中で gtid_executed の値が最も新しいものを確認します。

mysql1

mysql1 > show global variables like "gtid%";
+---------------------------------------------------+------------------------------------------------------------------------------------------------------+
| Variable_name                                     | Value                                                                                                |
+---------------------------------------------------+------------------------------------------------------------------------------------------------------+
| gtid_executed                                     | 05404f62-992b-11e7-aaae-525400ba38ee:1-11,
72b0d919-99d8-11e7-9dc9-525400ba38ee:1-39:1000011-1000017 |
| gtid_executed_compression_period                  | 1000                                                                                                 |
| gtid_mode                                         | ON                                                                                                   |
| gtid_owned                                        |                                                                                                      |
| gtid_purged                                       |                                                                                                      |
+---------------------------------------------------+------------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

mysql2

mysql2 > show global variables like "gtid%";
+---------------------------------------------------+----------------------------------------------------------------------------------------------------+
| Variable_name                                     | Value                                                                                              |
+---------------------------------------------------+----------------------------------------------------------------------------------------------------+
| gtid_executed                                     | 05404f62-992b-11e7-aaae-525400ba38ee:1-3,
72b0d919-99d8-11e7-9dc9-525400ba38ee:1-8:1000007-1000008 |
| gtid_executed_compression_period                  | 1000                                                                                               |
| gtid_mode                                         | ON                                                                                                 |
| gtid_owned                                        |                                                                                                    |
| gtid_purged                                       |                                                                                                    |
+---------------------------------------------------+----------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

mysql3

mysql3 > show global variables like "%gtid%";
+---------------------------------------------------+------------------------------------------------------------------------------------+
| Variable_name                                     | Value                                                                              |
+---------------------------------------------------+------------------------------------------------------------------------------------+
| gtid_executed                                     | 05404f62-992b-11e7-aaae-525400ba38ee:1-3,
72b0d919-99d8-11e7-9dc9-525400ba38ee:1-8 |
| gtid_executed_compression_period                  | 1000                                                                               |
| gtid_mode                                         | ON                                                                                 |
| gtid_owned                                        |                                                                                    |
| gtid_purged                                       |                                                                                    |
+---------------------------------------------------+------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

未実行のトランザクションが記録されたバイナリログが残っている場合

gtid_executed の値が最も新しいサーバのMySQL(この場合は mysql1 )に未実行のトランザクションが記録されたバイナリログが残っていれば、mysqlshからそのMySQLに接続して、rebootClusterFromCompleteOutage()を呼び出すことでクラスタを再構成して、その後にバイナリログを元にデータ同期が始まり、自動的に復旧できるはずです。 rebootClusterFromCompleteOutage()はこれはクラスタを復元するためのメソッドのようです。

mysql-js> shell.connect('icroot@192.168.10.1:24801')
Please provide the password for 'icroot@192.168.10.1:24801':
Creating a Session to 'icroot@192.168.10.1:24801'
Classic Session successfully established. No default schema selected.

[terminal] mysql-js> dba.rebootClusterFromCompleteOutage() Reconfiguring the default cluster from complete outage...

The cluster was successfully rebooted.

<cluster:cluster name=""> [/terminal]</cluster:cluster>

この時、以下のように The current session instance does not belong to the cluster: となってしまった場合は、一度MySQL Shellを終了させて、再度起動して接続させることでうまくいくと思います。 (もしダメな場合はMySQLを再起動するとうまくいくかもしれません。)

mysql-js> dba.rebootClusterFromCompleteOutage()
Reconfiguring the default cluster from complete outage...

The instance 'icroot@192.168.10.1:24801' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y|N]: y

Dba.rebootClusterFromCompleteOutage: Dba.rebootClusterFromCompleteOutage: The current session instance does not belong to the cluster: 'Cluster Name'. (RuntimeError)

これでクラスタメタデータにアクセスできます。

mysql-js> var cluster = dba.getCluster('ClusterDev')

クラスタのステータスを確認します。

mysql-js> cluster.status();
{
    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.10.1:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 2 members are not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
        }
    }
}

mysql2 192.168.10.1:24802を追加します。

mysql-js> cluster.addInstance('icroot@192.168.10.2:24802')
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.

Please provide the password for 'icroot@192.168.10.2:24802':
Adding instance to the cluster ...

The instance 'icroot@192.168.10.2:24802' was successfully added to the cluster.

ステータスが RECOVERING となり、データ同期が始まります。

mysql-js> cluster.status()
{

    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.10.1:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 2 members are not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.2:24802": {
                "address": "192.168.10.2:24802",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "RECOVERING"
            }
        }
    }
}

mysql2の error.log に以下のようなログが出力されていればデータ同期が正常に開始されています。

2017-05-261T16:40:06.552022+09:00 136485 [Note] Plugin group_replication reported: 'This server is working as secondary member with primary member address 192.168.10.1:24801.'
2017-05-26T16:40:06.552101+09:00 0 [ERROR] Plugin group_replication reported: 'Group contains 3 members which is greater than group_replication_auto_increment_increment value of 1. This can lead to an higher rate of transactional aborts.'
2017-05-26T16:40:06.552610+09:00 136493 [Note] Plugin group_replication reported: 'Establishing group recovery connection with a possible donor. Attempt 1/10'
2017-05-26T16:40:06.552819+09:00 0 [Note] Plugin group_replication reported: 'Group membership changed to 192.168.10.3:24803, 192.168.10.1:24801, 192.168.10.2:24802 on view 15283536049318228:3.'
2017-05-26T16:40:06.556755+09:00 136493 [Note] 'CHANGE MASTER TO FOR CHANNEL 'group_replication_recovery' executed'. Previous state master_host='', master_port= 0, master_log_file='', master_log_pos= 4, master_bind=''. New state master_host='192.168.10.1', master_port= 24801, master_log_file='', master_log_pos= 4, master_bind=''.
2017-05-26T16:40:06.561195+09:00 136493 [Note] Plugin group_replication reported: 'Establishing connection to a group replication recovery donor ********-****-****-****-******** at 192.168.10.1 port: 24801.'
2017-05-26T16:40:06.561475+09:00 136495 [Warning] Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
2017-05-26T16:40:06.562177+09:00 136496 [Note] Slave SQL thread for channel 'group_replication_recovery' initialized, starting replication in log 'FIRST' at position 0, relay log './mysql3-relay-bin-group_replication_recovery.000001' position: 4
2017-05-26T16:40:06.672847+09:00 136495 [Note] Slave I/O thread for channel 'group_replication_recovery': connected to master 'mysql_innodb_cluster_r**********@192.168.10.1:24801',replication started in log 'FIRST' at position 4

リトライが繰り返されるようだとどこかに問題があると思われます。

mysql3 192.168.10.1:24803も追加します。 2台同時にデータ同期のためにバイナリログのデータが流れるので、ネットワーク帯域に余裕がない場合は1台ずつタイミングをずらして行います。

mysql-js> cluster.addInstance('icroot@192.168.10.3:24803')
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.

Please provide the password for 'icroot@192.168.10.3:24803':
Adding instance to the cluster ...

The instance 'icroot@192.168.10.3:24803' was successfully added to the cluster.

ステータスが RECOVERING となり、データ同期が始まります。

mysql-js> cluster.status()
{

    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.10.1:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 2 members are not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.2:24802": {
                "address": "192.168.10.2:24802",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "RECOVERING"
            },
            "192.168.10.3:24803": {
                "address": "192.168.10.2:24803",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "RECOVERING"
            }
        }
    }
}

しばらく待って全ノードのステータスがONLINEになれば完了です。

mysql-js> cluster.status();
{
    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.28.251:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 1 member is not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24802": {
                "address": "192.168.10.1:24802",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24803": {
                "address": "192.168.10.1:24803",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            }
        }
    }
}

未実行のトランザクションが記録されたバイナリログが残っていない場合

クラスタが切り離されて以降のすべてのバイナリログが残っていない場合、実行すべきトランザクションそのものの情報がなくデータ同期が行えないので、最新のデータを保持しているMySQL(この場合は mysql1 )から mysqldump等でバックアップを取得して、それを他のサーバにインポートする必要があります。 (たぶん壊れ方によっては mysql_innodb_cluster_metadata のデータがおかしくなってそうなので、その時に全データダンプ取得して他のサーバにインポートするとさらにおかしくなりそうなので、その時は必要なデータを個別にバックアップするか metadata も消して一から構築し直すことになります)

この場合はバックアップを取得します。

※5.7.18では、Group Replication中だとSavepoint がサポートされていないため失敗します。

$ mysqldump --all-databases --lock-all-tables -uroot --triggers --routines --events -p > backup.sql

このダンプデータをほかのMySQL(この場合は mysql2, mysql3 )にインポートします。 reset masterを実行してバイナリログやGTIDもリセットします。

mysql2> reset master;

mysql3> source /tmp/backup.sql
mysql3> reset master;

mysql3> source /tmp/backup.sql

MySQL ShellからrebootClusterFromCompleteOutageメソッドを呼び出します。 この時、クラスターに参加していたMySQLインスタンスの一部を再度クラスターに参加するか問われますので、yes(y)を選択します。

mysql-js> shell.connect('icroot@192.168.10.1:24801')
Please provide the password for 'icroot@192.168.10.1:24801':
Creating a Session to 'icroot@192.168.10.1:24801'
Classic Session successfully established. No default schema selected.
mysql-js> dba.rebootClusterFromCompleteOutage()
Reconfiguring the default cluster from complete outage...

The instance '192.168.10.1:24802' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y|N]: y

The instance '192.168.10.1:24803' was part of the cluster configuration.
Would you like to rejoin it to the cluster? [y|N]: y


The cluster was successfully rebooted.


これでメタデータストアからクラスタメタデータを取得できます。

mysql-js> var cluster = dba.getCluster('ClusterDev')

クラスタのステータスを確認します。 Read-Onlyの192.168.10.1:24802, 192.168.10.1:24803インスタンスMISSINGとなっていますのでこれを復旧させる必要があります。

mysql-js> cluster.status();
{
    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.10.1:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 2 members are not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24802": {
                "address": "192.168.10.1:24802",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "(MISSING)"
            },
            "192.168.10.1:24803": {
                "address": "192.168.10.1:24803",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "(MISSING)"
            }
        }
    }
}

クラスタをrejoinしようとしましたが以下のエラーが発生して失敗しました。

mysql-js> cluster.rejoinInstance('icroot@192.168.10.1:24802');
Rejoining the instance to the InnoDB cluster. Depending on the original
problem that made the instance unavailable, the rejoin operation might not be
successful and further manual steps will be needed to fix the underlying
problem.

Please monitor the output of the rejoin operation and take necessary action if
the instance cannot rejoin.

Please provide the password for 'icroot@192.168.10.1:24802':
Rejoining instance to the cluster ...

Cluster.rejoinInstance: Access denied for user 'root'@'192.168.10.1' (using password: YES) (MySQL Error 1045)

rejoinInstance()はrootアカウントを使用してクラスタが作成されたことを前提としていた実装になっていたため、ユーザーが 'root'以外のアカウントを使用した場合、コマンドは失敗するようです。 すでにパッチが作成されているようですが、ここでは別な方法を取ることにします。

一旦、対象インスタンスに対してremoveInstanceを発行してクラスタから切り離します。

mysql-js> cluster.removeInstance('icroot@192.168.10.1:24802');
The instance will be removed from the InnoDB cluster. Depending on the
instance being the Seed or not, the Metadata session might become invalid.
If so, please start a new session to the Metadata Storage R/W instance.

Cluster.removeInstance: The instance 'icroot@192.168.10.1:24802' does not belong to the ReplicaSet: 'default'. (RuntimeError)

再度対象インスタンスクラスタに追加します。

mysql-js> cluster.addInstance('icroot@192.168.10.1:24802');
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.

Please provide the password for 'icroot@localhost:24802':
Adding instance to the cluster ...

The instance 'icroot@192.168.10.1:24802' was successfully added to the cluster.

クラスタのステータスを確認します。 192.168.10.1:24802がONLINEになっていることが確認できます。

mysql-js> cluster.status();
{
    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.28.251:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 1 member is not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24802": {
                "address": "192.168.10.1:24802",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24803": {
                "address": "192.168.10.1:24803",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "(MISSING)"
            }
        }
    }
}

同様に192.168.10.1:24803に対してもremoveInstance, addInstanceを発行します。

The instance will be removed from the InnoDB cluster. Depending on the
instance being the Seed or not, the Metadata session might become invalid.
If so, please start a new session to the Metadata Storage R/W instance.

The instance 'icroot@192.168.28.251:24803' was successfully removed from the cluster.
mysql-js> cluster.addInstance('icroot@192.168.28.251:24803');
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.

Please provide the password for 'icroot@192.168.28.251:24803':
Adding instance to the cluster ...

The instance 'icroot@192.168.28.251:24803' was successfully added to the cluster.

クラスタ内の全てのインスタンスがオンラインになっていることを確認します。

mysql-js> cluster.status();
{
    "clusterName": "ClusterDev",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "192.168.28.251:24801",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures. 1 member is not active",
        "topology": {
            "192.168.10.1:24801": {
                "address": "192.168.10.1:24801",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24802": {
                "address": "192.168.10.1:24802",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "192.168.10.1:24803": {
                "address": "192.168.10.1:24803",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            }
        }
    }
}

まとめ

クラスタそのものが止まった時に復旧させた際の手順を書きました。 今思えば mysql_innodb_cluster_metadata の中の情報も取得しておけばよかったなぁと思うので、次同じことが起こったら確認してみようと思います。

参考