CentOS7でMySQL/MariaDBのプロセスが落ちる対策


CentOS7にしたらMySQL(MariaDB)が落ちる

古くなっていたWEBサーバーをCentOS7に載せ替えました。
WordPressのサイトなので、データベース(DB)が必要です。同じサーバーにWEB/DBが同居していました。
元のデータベースはMySQLだったのですが、バージョンが古いためバージョンアップが必要です。

CentOS7インストールでデフォルトでインストールされる、MariaDBを使用することにしました。MySQLとも互換性が高くパフォーマンスもそこそこだということです。

WordPressの移行も終わり、ページも正常に表示されるようになり運用が始まりました。

数日後、ページが正常に表示されず、ブラウザの画面上に下記のようなエラーが表示されました。

Error establishing a database connection

サーバーにシェルログインしてプロセスを確認すると、httpdのプロセスが大量に立ち上がり、MySQLのプロセスがありません。

まずはMariaDBを再起動。

# systemctl restart mariadb.service
# ps ax | grep mysql
22600 ?        Ss     0:00 /bin/sh /usr/bin/mysqld_safe --basedir=/usr
22798 ?        Sl     0:01 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
22850 pts/0    S+     0:00 grep --color=auto mysql
#

無事起動しました。ちなみにMariaDBですが、プロセス表示はmysqlです。この記事も含めゴチャゴチャに表記していますが、「MySQL≒MariaDB」として進めます。

MariaDBのバージョンも確認します。

# rpm -qa | grep mariadb
mariadb-libs-5.5.65-1.el7.x86_64
mariadb-5.5.65-1.el7.x86_64
mariadb-server-5.5.65-1.el7.x86_64
#

データベースが落ちては困る

データベースの再起動で復旧はしましたが、データベースは一度でも落ちるのは許容できません。
原因を探るべく、 /var/log/mariadb/mariadb.log を確認してみます。

201211 10:06:34 [ERROR] Plugin 'InnoDB' init function returned error.
201211 10:06:34 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
201211 10:06:35 [ERROR] mysqld: Out of memory (Needed 128917504 bytes)
201211 10:06:35 [ERROR] mysqld: Out of memory (Needed 96681984 bytes)
201211 10:06:38 [Note] Plugin 'FEEDBACK' is disabled.
201211 10:06:39 [ERROR] Unknown/unsupported storage engine: InnoDB
201211 10:06:39 [ERROR] Aborting
201211 10:06:40 [Note] /usr/libexec/mysqld: Shutdown complete

メモリが確保できず、シャットダウンしているようです。

ですが、MySQLってメモリ確保できないくらいでプロセスごと落ちるのかと考え、同時刻の/var/log/messagesを追ってみます。

Dec 11 10:06:41 sv2-33178 kernel: Out of memory: Kill process 23762 (mysqld) score 25 or sacrifice child
Dec 11 10:08:59 sv2-33178 kernel: httpd invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0

ああ、httpdが大量発生した結果、omm-killerが発動されてmysqldプロセスを殺してるのね。

MySQLの設定でメモリを節約したりできますが、httpdが発生し続けるといくらメモリがあっても足りないので、httpdのプロセスはいくつか落としても、MySQLのプロセスは落として欲しくないです。

MySQL/MariaDBのメモリ確保対策

MySQLが Out of memory にならないために、対策を考えました。

1.MySQLの設定変更

最大接続数からバッファを計算してメモリを確保する方法などがあります。
ただ、これも移行前サーバーで設定済みで、極端にメモリを消費しているわけではありません。

また、パフォーマンスが多少落ちる前提で下記の設定も追加済みです。

/etc/my.cnf
[mysqld]
performance_schema = off

2.メモリとスワップファイルのアップグレード

このサーバーのメモリは2GB、スワップは4GBです。
用途はコーポレイト(企業用)サーバーですが、通常はパフォーマンスは十分です。
スワップはパーテションとして設定されていますが、スワップファイルを確保しいくらでも設定することも可能です。

上記検討しましたが、結局 http のプロセスが逼迫してくれば、MySQLに影響を与えかねないのでやめました。
じゃあ、http 側の設定を変更して制限すべきですが、すでに同時接続制限はしており、正確に WEB/DB のメモリ配分の切り分けができるわけではないと思ったからです。

動作が遅くなってもいいので、MySQLに落ちないでもらいたいのです。

OOM Killerの対象から外す

元にもどり考えると、MySQLのプロセスを OOM(Out Of Memory) Killer の対象から外せば、プロセス自体が落ちることはありません。

OOM Killerは各プロセスの設定変数 oom_score_ad の優先値を参照しているとのこと。
早速MySQLのプロセスの設定値を確認します。

# cat /proc/<プロセスID>/oom_score_ad
0
#

ここを-1000にすると、OOM Killerから見逃してもらえるとのこと。
ちなみにsshdも-1000に設定されています。sshdのプロセスが落ちたら外からログインできないですからね。

早速設定します。

# echo -1000 > /proc/<プロセスID>/oom_score_ad
# cat /proc/<プロセスID>/oom_score_ad
-1000
#

プロセスIDは、ps コマンドで探してください。
mysqld_safe ではなく、 mysqld です。両方設定してもいいかも。
これでOOM Killerに目をつけられてプロセスが落ちることは無くなったはずです。

MySQL起動時に oom_score_ad の設定

前記までで、プロセスが落ちることが無くなった(多分)わけですが、MySQL/MariaDBの再起動時やサーバーの再起動時ににも再設定しなくてはなりません。
CentOS6であれば、/etc/init.d に入っているスクリプトに追加すればよかったのですが、CentOS7では起動スクリプトの記載形式が変更されています。

起動設定のファイルがどこにあるのかわかりにくいので検索してみます。

# find /etc -name "mariadb.*" 
/etc/systemd/system/multi-user.target.wants/mariadb.service
/etc/systemd/system/mariadb.service.d
#

かなり奥の階層に mariadb.service ファイルがあります。

mariadb.serviceファイルに追加記述すればいいと思うのですが、ファイルの先頭に

It's not recommended to modify this file in-place

と、このファイルに追加するなと警告してあります。

# systemctl edit mariadb.service

のエディタで設定ファイルを編集し、下記の行を追加します。

/etc/systemd/system/mariadb.service.d/override.conf
[Service]
OOMScoreAdjust=-1000

自動的に追加用の override.conf ファイルが生成されます。

データベースを再起動します。

# systemctl restart mariadb.service

oom_score_adj の値を確認してみます。

#  for dir in /proc/[0-9]*; do   
>    if [ "`cat $dir/oom_score_adj`" != 0 ]; then   
>      echo "`cat $dir/comm` : `cat $dir/oom_score_adj`"   
>    fi   
>  done 
systemd-udevd : -1000
mysqld_safe : -1000
mysqld : -1000
auditd : -1000
dbus-daemon : -900
sshd : -1000

おお、sshdと同じ値に設定されたのを確認できました。

これで OOM Killer でプロセスが落とされることがなくなりました。

完全解決ではありませんが、Webサイトが長時間落ちたままになる最悪の事態は避けられると思われます。

原因は httpd のプロセスが大量に滞留することが原因です。

一時的なアクセス集中か、攻撃か、WordPressのバグか、システムの問題かはっきりしません。
リソースのグラフを見ても、極端なトラフィックの増加は見当たらず、CPU使用率が急激に上がるのみです。

WordPressも怪しいのですが、バージョンアップなどコンテンツ運用は別の担当で、さらに複数サイトが同居して、WordPressのバージョンもバラバラなので。。。

別の記事で、CPU使用率が上がったら httpd を再起動するCRONスクリプトを投稿しますので併用します。

ここまで MySQL/MariaDB がごちゃまぜで記載されて読みにくいと思いますが、適宜読み替えてください!