sgykfjsm.github.com

AnsibleでMySQLをインストールする。

AWSなんかを使ってると、イチからMySQLなどの基本的なミドルウェアをセットアップすることは減ってくる。そんな中、AnsibleでMySQLをインストールする事になったのだけど、意外と忘れてたりAnsibleで作業するのと手作業で進めることの差異でちょっと詰まったりしたので、ここに備忘しておく。

大まかな手順

  1. 最新安定版(MySQL5.6)をインストールするためのリポジトリを設定する。
  2. リポジトリ軽油でインストールする。
  3. 初期セットアップを行なう。
    • rootのパスワード初期化
    • mysql_secure_installationによるMySQLの初期化
    • 諸々の設定

たったこれだけなんだけど、Ansibleでこれらの作業を実装するのはちょっと面倒。というのも、mysql_secure_installationはコマンドを実行すると対話的に処理を進めることになるが、これをAnsibleで実行するのがちょっと面倒だったりする。また、rootのパスワード初期化についても考慮が必要で、playbookに直書きするのはもちろん駄目だし、mysql_config_editorはこれまた対話式のコマンドなので、自動化が微妙に難しい。諸々の設定に関して、設定すべき内容は案件などによると思うが、予めいくつかのmy.cnfなどを用意しておいて、それをコピーして使うようすればいいだけなので、あまり問題にはならない。

ということで、rootのパスワード初期化および管理とmysql_secure_installationについて考える。

rootのパスワード初期化と管理

いくつかのブログなどにあるHowTo記事でrootの初期パスワードは.mysql_secretというファイルにある、と書かれていることがある。が、今回MySQL5.6をインストールしてみてもそのようなファイルは無かった。今までも見たことがないので、もしかしたらインストールの方法によってはこのファイルが生成されるのかもしれないが、とにかく無い。なので、パスワードの初期化は基本に忠実に以下のようにMySQLを起動させてからパスワードをSQLで更新した。

/bin/sh /usr/bin/mysqld_safe \
    --datadir=/var/lib/mysql \
    --socket=/var/lib/mysql/mysql.sock \
    --pid-file=/var/run/mysqld/mysqld.pid \
    --basedir=/usr \
    --user=mysql \
    --skip-grant-tables \
    --skip-networking

この起動コマンドについての詳細はResetting the Root Password: Generic Instructionsを参照。この方法がシンプルで覚えることが少なくて良いと思う。

これでMySQLサーバを起動した後、以下のSQLでパスワードを更新する。パスワードはAnsibleの中でcat /dev/urandom | LC_CTYPE=C tr -dc ‘[:alnum:]’ | head -c 16 ; echoとかやって適当な文字列を生成してあげれば良いと思う。パスワードをplaybookに直書きするよりかはマシだろう。

1
2
UPDATE mysql.user SET Password=PASSWORD('NewPassWord') WHERE User='root';
FLUSH PRIVILEGES;

ここまでは特段問題が無くて、問題はこのパスワードをどこに保管するか、ということ。結論から言えば、/etc/my.cnf.d配下に管理することに落ち着いた。以下の様なファイルを作っておいて、それを/etc/my.cnf.d配下に配置しておけば良いと思う。

1
2
3
4
[db-administrator]
user = root
password = { { mysql_root_password.stdout }}
host = localhost

{{ mysql_root_password.stdout }}の部分はAnsible内で設定されるプレースホルダー。また、通常は[client]となっている部分は別の文言に変えている。そうでないと、デフォルトの接続情報として使われるので、注意すること。また、/etc/my.cnf.d配下のファイルが読み込まれるように、/etc/my.cnfには!includedir /etc/my.cnf.dを追加しておくこと。

Ansibleでmysql_secure_installationを如何に行なうか。

ざっと思いついたのは以下の通り。

  1. 普通にmysql_secure_installationを実行して、対話的に処理を行う。
    • そもそもAnsibleで行なう必要が無いのと、rootのパスワードなどをどこかにメモすることになるので却下
  2. expectを使って、予め回答を入力しておいて、処理を自動化させる。
  3. mysql_secure_installationと同等の処理を実装し、それを実行する。
    • mysql_secure_installationがやっている処理自体はいくつかのSQLを実行しているだけで、実装自体は容易。
    • ただし、今後のバージョンアップなどでmysql_secure_installationの内容を常に気にしておく必要がある。
    • 1と2の案に比べて、必要な時間が圧倒的に少なく済むので、今回はこの案にした。

で、その内容は以下の通り。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/bin/bash
# http://dev.mysql.com/doc/refman/5.0/en/resetting-permissions.html#resetting-permissions-unix

set -xeu

[ $(/usr/bin/whoami) = 'root' ] || {
    /bin/echo root only
    exit 1
}

init_file=/tmp/mysql-init

/bin/cat <<EOF > ${init_file}
-- Set the root password
UPDATE mysql.user SET Password=PASSWORD('${mysql_root_password}') WHERE User='root';
FLUSH PRIVILEGES;
-- Remove anonymous users
DELETE FROM mysql.user WHERE User='';
-- Disallow remote root login
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost',  '127.0.0.1',  '::1');
-- Remove test database
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
-- Reload privilege tables
FLUSH PRIVILEGES;
EOF
chmod 600 ${init_file}

nohup /bin/sh /usr/bin/mysqld_safe \
    --datadir=/var/lib/mysql \
    --socket=/var/lib/mysql/mysql.sock \
    --pid-file=/var/run/mysqld/mysqld.pid \
    --basedir=/usr \
    --user=mysql \
    --skip-grant-tables \
    --skip-networking &

while :
do
    [ -r /var/run/mysqld/mysqld.pid ] || continue

    if ps -ef | grep -q $(cat /var/run/mysqld/mysqld.pid) ; then
        break
    fi
    sleep 10
done

mysql -uroot < ${init_file} # 実際の処理では--login-pathを指定してrootでログイン、実行としている。

/etc/init.d/mysqld stop

rm -f ${init_file}

exit

実際に書いてみると、なんてことは無いコードではある。

あと、他に何か書こうかと思ったけど、忘れたので、とりあえずMySQL56をインストールするためのplaybookを載っけてお茶を濁すことにする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
---
- name: Install Official MySQL Repository
  yum: name={ { item | quote }} # 実際に使う時はスペースを削除すること
  with_items:
    - http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm
  sudo: yes
  tags:
    - yum
    - mysql

- name: Disable Official MySQL Repository
  replace: dest="/etc/yum.repos.d/{ { item }}.repo" regexp="^enabled *= *1$" backup=yes replace='enabled=0' # 実際に使う時はスペースを削除すること
  with_items:
    - mysql-community
    - mysql-community-source
  sudo: yes
  tags:
    - yum
    - mysql

- name: Install MySQL56
  yum: name={ { item | quote }} state=latest enablerepo=mysql56-community,remi # 実際に使う時はスペースを削除すること
  with_items:
    - php56-php-mysqlnd # remi
    - mysql-community-client
    - mysql-community-common
    - mysql-community-devel
    - mysql-community-libs
    - mysql-community-server
    - MySQL-python # for remote-host
  sudo: yes
  tags:
    - mysql

- name: Create root user password
  shell: cat /dev/urandom | LC_CTYPE=C tr -dc '[:alnum:]' | head -c 16
  register: mysql_root_password
  tags:
    - mysql

- name: Execute mysql_secure_installation
  script: secure_install.sh { { mysql_root_password.stdout | quote }} # 実際に使う時はスペースを削除すること
  sudo: yes
  register: result
  tags:
    - mysql

- name: Add Line for include append config
  lineinfile: >
    dest=/etc/my.cnf
    line="!includedir /etc/my.cnf.d"
    backup=yes
  sudo: yes
  register: result
  tags:
    - mysql


- name: Copy my.cnf To /etc/my.cnf.d/login.cnf
  template: >
    src=my.cnf.j2
    dest=/etc/my.cnf.d/login.cnf
    owner=root
    group=root
    mode=0600
  sudo: yes
  tags:
    - mysql

- name: Add mysqld to StartUps on boot And Be Running
  service: enabled=yes name=mysqld state=running
  sudo: yes
  tags:
    - mysql

あんまりかっこよくないけど、Jekyllyがcodeブロック内の波括弧を解釈しようとして変な風になるので、スペースを入れてごまかしている。