6715.jp

2018-07-10

Ubuntuでmysql-serverをmariadb-serverで置き換えるとsystemd経由でmariadbが起動できない

sekaisekai
LinuxMariaDBMySQLsystemdUbuntuインフラ

Ubuntu(18.04 LTS)でmysql-serverをmariadb-serverで置き換えるとsystemd経由でmariadbが起動できない。

追記

この記事には一部誤りがあります。 以下の記事も合わせてご覧ください。

トラブル

タイトルの通りです。 Ubuntu 18.04で発生したトラブルですが、他のバージョンでも起こり得そうな予感がします。おそらく。

$ sudo apt install mysql-server
$ sudo apt purge mysql-server
$ sudo apt install mariadb-server

とすると、最後のapt installが妙に遅い事に気が付きます。 コレは、aptがインストール後におせっかいでmariadbを起動してくれるのですが、何らかの原因でしばらく経ってもmariadbが起動しないためです。

その後、systemctl start mariadbを試しても、しばらくした後に起動失敗します。

$ sudo systemctl start mariadb
Job for mariadb.service failed because a timeout was exceeded.
See "systemctl status mariadb.service" and "journalctl -xe" for details.

調査

systemdのログ

$ sudo systemctl status mariadb
● mariadb.service - MariaDB database server
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
   Active: failed (Result: timeout) since Tue 2018-07-10 01:20:10 JST; 36s ago
 Main PID: 2404 (code=exited, status=0/SUCCESS)

Jul 10 01:18:38 localhost systemd[1]: Starting MariaDB database server...
Jul 10 01:18:38 localhost mysqld[2404]: 2018-07-10  1:18:38 139689982721152 [Note] /usr/sbin/mysqld (mysqld 10.1.29-MariaDB-6) starting as process 2404 ...
Jul 10 01:20:08 localhost systemd[1]: mariadb.service: Start operation timed out. Terminating.
Jul 10 01:20:10 localhost systemd[1]: mariadb.service: Failed with result 'timeout'.
Jul 10 01:20:10 localhost systemd[1]: Failed to start MariaDB database server.

timeoutしているようです。 しかしながら、mysqld[2404]: ...から始まる行を見ると、どうやら起動できているようにも見えますが……?

接続してみる

systemctl start mariadbを実行後、シェルが待機中に別のシェルからMariaDBに接続してみると、普通に接続できます。

$ mysql -u root
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 10.1.29-MariaDB-6 Ubuntu 18.04

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

やはり、DBサーバーの起動自体はできているようですが、systemdが起動に成功したことを検知できていないようです。

systemd unit定義の確認

systemdでプロセスを起動するための設定ファイルを確認してみます。 /lib/systemd/system/mariadb.serviceにあります。

注目すべきは、以下の設定です。

[Service]
Type=notify

これは、sd_notifyを使ってプロセスの起動完了をsystemdへ通知する設定であることを表しています。 mariadbは起動できているのに、systemdがそれを認識していない、ということはどうやらこのsd_notifyが正しく送信されていないのではないか?と疑われます。

auditログ

sd_notifyはUNIXドメインソケット(/run/systemd/notify)を介してsystemdに通知を送信しますが、パーミッションも特に問題なく、アクセスできそうですが……? 次に疑うのはSELinuxやAppArmorといった強制アクセス制御機能です。UbuntuはデフォルトでAppArmorが有効なので、怪しいです。

auditログを確認します

$ journalctl -n 1 _TRANSPORT=audit
Jul 10 01:42:59 localhost audit[3057]: AVC apparmor="DENIED" operation="sendmsg" info="Failed name lookup - disconnected path" error=-13 profile="/usr/sbin/mysqld" name="run/systemd/notify" pid=3057 comm="mysqld" requested_mask="w" denied_mask="w" fsuid=112 ouid=0

AppArmorによって、sd_notifyの送信が拒否されているのが発見できました。 ようやく尻尾を掴みましたね……

原因

mysql-serverをインストールすると、AppArmorプロファイルが同時にインストールされ、有効化されます。 このプロファイルは/etc/apparmor.d/usr.sbin.mysqldに設置されます。

このプロファイルでは、限られたディレクトリへのアクセスのみを許可しており、sd_notifyで使うソケットへはアクセスできません。 しかしながら、mysql-serverでは、プロセス起動検知にType=simpleを使用しているため、これは問題になりません。

mysql-serverをアンインストールし、mariadb-serverをインストールすると、/etc/apparmor.d/usr.sbin.mysqldは空のファイルで上書きされますが、AppArmorがすでに読み込んでいるプロファイルは削除されません。 また、systemctl reload apparmorしても、OSを再起動しても、一度読み込まれたプロファイルが勝手に削除されることはありません。

……なので、プロセス起動検知にType=notifyを使うmariadb-serverにもこのプロファイルが適用されてしまい、sd_notifyが失敗してsystemdがタイムアウトする、というオチでした。

解決法

Ubuntuでは、削除されたプロファイルをアンロードするコマンドaa-remove-unknownが用意されています。 mariadb-serverをインストールした後、これを実行すれば良いです。

$ sudo aa-remove-unknown
Removing '/usr/sbin/mysqld'

コレで正常に起動できるようになります。

$ sudo systemctl start mariadb
$ sudo systemctl status mariadb
● mariadb.service - MariaDB database server
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2018-07-10 01:55:56 JST; 15s ago
 Main PID: 4753 (mysqld)
   Status: "Taking your SQL requests now..."
    Tasks: 27 (limit: 1112)
   CGroup: /system.slice/mariadb.service
           └─4753 /usr/sbin/mysqld

おしまい

traP部内ISUCONをしていて、とりあえずmysqlを入れている人が多かったので、なんとなく**「mariadbに替えた方がいいよ!」**と言ったら起動できなくなる人が続出して阿鼻叫喚でした。

ICTSCみたいですね。

おわり。