MySQL サーバーの root パスワードがわからない!
もうパスワードを再設定するしかない。
パスワードを再設定するには MySQL サーバーを再起動する必要があります。
でも、PowerCMS でサイト内検索機能や会員サイト機能、DynamicMTML などの動的な機能を利用している場合は、MySQL サーバーを停止したり再起動したりすれば、同時にそれらの動的機能が機能しなくなり、障害となります。
今回は、一時的にメンテナンス表示に切り替えてから、MySQL サーバーを再起動して、できるだけ短時間でメンテナンス表示を解除する手順を考えます。
前提は次の通りで、これらの前提から外れる部分は手直しや工夫が必要です。
RHEL 7 や CentOS 7、Amazon Linux 2 などの Systemd で管理する Red Hat 系の Linux OS。
root ユーザーによるシェル操作。
MySQL 5.7.6 以降。
MySQL サーバー (mysqld) が同居している。
Web サーバーとして rewrite モジュールが有効な Apache HTTP サーバー 2.4 (httpd)。
PowerCMS で管理しているすべてのサイトのバーチャルホストは /var/www/example.jp/html
のように、/var/www
配下にまとまっている。
DynamicMTML を使用している。
PSGI や CGI (mt-*.cgi
) は非公開。
CMS 管理画面 (mt.cgi
) は関係者にメンテナンスを周知して利用を控えてもらうか、httpd 設定でアクセスを拒否しておく。
メンテナンス設定の準備
まず、メンテナンス表示用の HTML を用意します。
既存サイトのデザインを継承し、リッチで違和感のないメンテナンスページを用意してもよいですが、CMS の管理下にあるバーチャルホストの数が多いと、それぞれの HTML ファイルを作成して配置したり、事前確認などもなかなか大変です。
今回の再起動の所要時間は数秒から長くても数十秒ほどの想定なので、扱いやすい簡易な1行 HTML の、簡素なメンテナンス表示で済ませます。
httpd の ErrorDocument
ディレクティブを、次のような感じで用意します。
ErrorDocument
ディレクティブの下書き
ErrorDocument 503 "<!DOCTYPE html><meta charset=UTF-8><title>一時メンテナンス中</title><p>メンテナンスのため12時34分までの予定でご利用いただけません。ご迷惑をおかけいたしますが、ご理解の程よろしくお願い申し上げます。"
しかし、httpd 2.4 では ErrorDocument ディレクティブに HTML を記述した場合、文字エンコーディングが ISO-8859-1 に強制されますので、このように ISO-8859-1 の範囲外の日本語などを記述するとメンテナンスページが文字化けしてしまいます。
httpd の環境変数「suppress-error-charset
」 を設定するとこれを回避できますが、デメリットがあったり、httpd の再起動が必要だったりして面倒で、目標とする短時間メンテナンスを阻害するので、次のように ISO-8859-1 のまま文字参照でマルチバイト文字を記述します。
そして、rewrite モジュールで HTTP ステータス503のエラーページに書き換え、機械向けに Retry-After ヘッダーを追加します。
メンテナンス表示用に追加する httpd 設定
ErrorDocument 503 "<!DOCTYPE html><meta charset=ISO-8859-1><title>一時メンテナンス中</title><p>メンテナンスのため12時34分までの予定でご利用いただけません。ご迷惑をおかけいたしますが、ご理解の程よろしくお願い申し上げます。"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !\.(png|jpe?g|gif|css|js(on)?|xml|atom)$ [NC]
RewriteRule ^ - [R=503,L]
Header always set Retry-After "Thu, 15 Nov 2018 03:34:00 GMT"
実行
これを /var/www
配下にある分散設定ファイル (いわゆる .htaccess
) に対して一斉に追記してメンテナンス表示に切り替えます。
事前に分散設定ファイルをコピーしてバックアップしておき、設定を追記、MySQL サーバーを再起動して root パスワードを再設定、分散設定ファイルをバックアップから復旧して完了です。
この例では「New-Password-0123」というパスワードを再設定しているので、このあと落ち着いたら root ユーザーで接続して、必ずパスワードを更新し直してください。
Systemd の場合 (RHEL 7、Oracle Linux 7、CentOS 7、Amazon Linux 2)
[root]# _YMDHMS=$(date +%Y%m%d) &&
find /var/www -name .htaccess -type f -exec /bin/cp -av {}{,~$_YMDHMS} \; &&
find /var/www -name .htaccess -type f -exec sed -i '1s/^/ErrorDocument 503 "<!DOCTYPE html><meta charset=ISO-8859-1><title>\一\時\メ\ン\テ\ナ\ン\ス\中<\/title><p>\メ\ン\テ\ナ\ン\ス\の\た\め12\時34\分\ま\で\の\予\定\で\ご\利\用\い\た\だ\け\ま\せ\ん\。\ご\迷\惑\を\お\か\け\い\た\し\ま\す\が\、\ご\理\解\の\程\よ\ろ\し\く\お\願\い\申\し\上\げ\ま\す\。"\nRewriteEngine On\nRewriteCond %{REQUEST_FILENAME} !\\.(gif|png|jpe?g|css|js|xml)$ [NC]\nRewriteRule ^ - [R=503,L]\nHeader always set Retry-After "Thu, 15 Nov 2018 03:34:00 GMT"\n/' {} \; &&
_MYSQLD_INIT_FILE=/var/tmp/mysqld-init.sql &&
echo "ALTER USER 'root'@'localhost' IDENTIFIED BY 'New-Password-0123';" > $_MYSQLD_INIT_FILE &&
systemctl set-environment MYSQLD_OPTS="--init-file=$_MYSQLD_INIT_FILE" &&
systemctl restart mysqld.service &&
find /var/www -name .htaccess~$_YMDHMS -exec bash -c '/bin/mv -v "$1" "${1%\~'$_YMDHMS'}"' _ {} \; &&
systemctl unset-environment MYSQLD_OPTS &&
/bin/rm $_MYSQLD_INIT_FILE
起動管理が Upstart の場合 (RHEL 6、CentOS 6、Amazon Linux) は一時的に /etc/rc.d/init.d/mysqld
を書き換えてから systemctl
ではなく service
を使うなど、違いがあります。
また、MySQL サーバーのバージョン (5.1や5.5、5.6、5.7.5 未満) によっても、user テーブルのカラム構造や利用可能な関数など、再設定の方法にいくつかパターンがあります。
メンテナンススクリプト作成器
文字の変換や日時の RFC 1123 変換など、それなりに面倒なので簡易な作成器を用意しました。手直しして十分に予行と検証をしてご利用ください。
さいごに
MySQL サーバーで管理しているデータベースの規模や my.cnf
の設定、サーバーの性能などによりますが、おそらく再起動は数秒で終わるはずなので、一瞬のダウンがあっても許容できる場合は、メンテナンス表示せず再起動という判断もあるかと思います。
本当に瞬断も許されない場合は「$datadir/mysql/user.*
を差し替えて SIGHUP を送る」という荒業でも root を復旧できますが、いずれにしても失敗しないよう複製環境を用意して、十分に予行することをお勧めします。
さあ帰ろう。
コメントを投稿する