macOSでMySQL5.7をセットアップ

# 概要

macOS Sierraに、MacPortsを使ってのMySQL5.7インストールを行い、セットアップした際のメモです。
私は(なぜか)HomeBrewとは相性が悪いので、使っていません。

インストールはmacOSへのMacPortsを使用しての作業ですが、インストール部分はyumやapt-get、*BSD系のportsなどに読みかえれば、設定部分についてはCentOSやUbuntuなどでも同じかと思います。

パス名については、OS・パッケージごとに異なる場合があるので注意すればよいと思います。

## インストール

### rootになる

作業にはroot権限が必要です。コンソールにrootでログインをするか、wheelグループのユーザでログインしてからsuコマンドでrootになります(ここの作業をsudoで実行しても問題ないでしょう)。

```shell-session:rootになる
# sudo su - root
```

### MacPortsの更新

ここはMacPortsのみの作業となります。FreeBSDならportsnap fetch extractなどに相当します。

```shell-session:MacPortsの更新
# port selfupdate
# port upgrade outdated
```

### MySQL5.7のインストール

Mysql5.7のパッケージ名を指定してインストールします。
port list | grep mysqlなどでmysql関連のパッケージリストを見ることができるでしょう。

```shell-session:MySQL5.7のインストール
# port install mysql57
```

## 環境変数の設定

環境変数に、MySQL関連のパスを追加します。

### PATH環境変数に設定を追加

使っているシェルに依存しますが、PATH環境変数にMySQL5.7のパスを追加します。
.profileや.bashrc、.cshrcなど、環境変数の設定は各シェルの個人設定ファイルに依存するため、詳細は割愛します。
ここではbashを使っているという前提で.profileに環境変数PATHの設定追加を行います。

環境変数の設定部分のみ、以下にピックアップします。

```bash:.profile
# MySQL binディレクトリの設定
# CentOSやapt-getなどのLinux系OSなら/usr/binかも。要確認
# FreeBSD portsなどBSD系だと/usr/local/binだろうか。要確認
export MYSQL_BIN_PATH="/opt/local/lib/mysql57/bin"
export PATH="/opt/local/bin:/opt/local/sbin:$MYSQL_BIN_PATH:$PATH"
```

### 環境変数の反映

設定した環境変数を、現時点で実行中のシェルに反映させます。

```shell-session:bashの場合
# . ~/.profile
```
```shell-session:cshの場合
# source ~/.cshrc
```

## MySQLの設定

MySQLの設定ファイルmy.cnfの編集を行います。
設定ファイルのパスもシステムによって違いがあります。Linux系なら/etc/my.cnf、BSD系なら/usr/local/etc/my.cnfかと思います。
find /usr -name my.cnfやfind /etc -name my.cnfあたりで居場所を確認するのがよいかと思います。
MacPortsでMySQL5.7をインストールした場合は、以下のパスにmy.cnfが配置される。

```shell-session:my.cnfの編集
# vi /opt/local/etc/mysql57/my.cnf
```

以下のようにmy.cnfを編集しています。
主に設定している部分はパス名で、socketやdatadirなど、起動時や稼働中にMySQLが保存する状態保存ファイルをフルパスで指定する設定します。
パス自体はどこでも問題ありませんが、パーミッション(アクセス権限)が適切に設定されていないとmysqld(MySQLサーバ)が起動しませんので注意してください。
パーミッションの設定はこの後の設定を参考にしてください。

MacPortsで1点注意が必要なのが、最初の4行のコメント部分をよく見ていただくとわかりますが、!includeディレクティブで他の設定ファイルを読み込んでいる箇所です。このmy.cnfの例ではコメントアウトされていますが、port install直後のmy.cnfでは、これは有効になっています。

そしてこのincludeされるmacports-default.cnfの中には、skip-networkingという設定が記述されています。この設定は**TCP接続(ネットワーク接続)をしない状態で起動することをmysqldに指示しています**。そのため、このあと初期化や設定が終わってMySQLサーバを起動しても、まったく**ネットワーク経由でのサーバ接続ができない**という状況になります。MacPortsからのインストールの場合は案外ココがハマりポイントだったりすると思うので、よく注意してください。
他のパッケージシステムでも同様のデフォルト設定がされている可能性はありますので、どうしてもネットワーク経由での接続ができないと思ったら、skip-networkingが設定されていないかどうかチェックするとよいかもしれません。

一応ここではネットワーク接続を有効にすることを前提にしているため、!includeディレクティブそのものをコメントアウトしています。

```mysql:my.cnf
# Use default MacPorts settings
# >>> 以下の行を生かしておくと、macports-default.cnfに記載されているskip-networkingによってTCP接続ができない状態で起動されるため注意。
# >>> (MacPorts独自のインストール時設定)
#!include /opt/local/etc/mysql57/macports-default.cnf
 
[client]
port=3306
socket=/var/run/mysql/mysql.sock
 
[mysqld]
port=3306
socket=/var/run/mysql/mysql.sock
datadir=/var/db/mysqld
pid-file=/var/run/mysql/mysqld.pid
secure-file-priv=/var/db/mysqld/files
character-set-server=utf8
key_buffer_size=16M
max_allowed_packet=16M
 
[mysqldump]
quick
 
[mysqld_safe]
log-error=/var/log/mysqld.log
```

## ディレクトリの作成

MySQLが起動時、稼働中に使用する状態保存などのファイルを配置するディレクトリを作成し、適切なパーミッションに設定します。

### pidファイル保存場所の作成

pidファイルを保存するディレクトリを作成します。
これらはmy.cnfへの設定も含め、システムのパッケージシステムによっては事前にお膳立てしてくれるものもあるので、必要に応じて適宜作成します。

先にも述べましたが、ディレクトリも「絶対にこの位置でなければならない」というわけではないです。パス名(ディレクトリ名)を変更する場合は、同時にmy.cnfの設定も同一に設定する必要がある点に注意してください。

パーミッションに設定するユーザとグループもまた、OSやパッケージシステムなどで既定のものがあると思いますので、以下のユーザ・グループ名はそれらに従ってください。
MacPortsの場合はユーザ名が_mysql、グループ名も_mysqlとなっていますので、これらでパーミッションを設定します。パーミッションは775で設定しています。

```shell-session:関連ディレクトリの作成とパーミッションの設定
# mkdir /var/run/mysql
# chmod 775 /var/run/mysql
# chown _mysql:_mysql /var/run/mysql
```

### データディレクトリの作成

MySQLの実データを保存するディレクトリを作成します。別ディスクなどをマウントして使う場合などは、マウントポイントとなるディレクトリを指定することになります。
こちらもMacPortsの場合は_mysql:_mysqlで設定し、パーミッションは770で設定しています。データディレクトリはmysqldだけがアクセスできれば問題ないので、othersのパーミッションはすべて落としています。

```shell-session:データ用ディレクトリの作成とパーミッションの設定
# mkdir /var/db/mysqld
# chown -R _mysql:_mysql /var/db/mysqld
# chmod 770 /var/db/mysqld
```

## MySQLの初期化

MySQLを初期化します。この作業で、さきほど作成したデータ用ディレクトリなどに初期化された一連のファイルが生成されます。

### 初期化の実行

初期化にはmysqldコマンドに--initializeオプションを指定して実行しますが、実行時jに/var/db/mysqldディレクトリ内が空になっていなければ、起動しません。

また、**この起動メッセージには、MySQLのrootユーザの初期パスワードが出力されます**。これは一時的なパスワードなので、後の作業で変更することになりますが、その際に**必要になるため、メモをとるなりして、必ず覚えておいてください**。

```shell-session:初期化の実行
# mysqld --initialize --user=_mysql
2016-09-18T01:10:37.413222Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2016-09-18T01:10:37.415091Z 0 [Warning] Setting lower_case_table_names=2 because file system for /opt/local/var/db/mysql57/ is case insensitive
2016-09-18T01:10:39.012024Z 0 [Warning] InnoDB: New log files created, LSN=45790
2016-09-18T01:10:39.236154Z 0 [Warning] InnoDB: Creating foreign key constraint system tables.
2016-09-18T01:10:39.314635Z 0 [Warning] No existing UUID has been found, so we assume that this is the first time that this server has been started. Generating a new UUID: b667d394-7d3c-11e6-987f-d5f44681b507.
2016-09-18T01:10:39.425260Z 0 [Warning] Gtid table is not ready to be used. Table 'mysql.gtid_executed' cannot be opened.
2016-09-18T01:10:39.425931Z 1 [Note] A temporary password is generated for root@localhost: rootの初期パスワード
```

### ファイルロード用ディレクトリを作成

初期化作業後に、もう1つだけ必要なディレクトリの作成と設定を行います。secure-file-privで設定されているディレクトリの作成に相当します。
このディレクトリは、LOAD_FILE()関数やLOAD DATA構文、SELECT ... INTO OUTFILE構文を使用する際に、指定されたファイルが特定のディレクトリ内のファイルに限定されることを意図しています。

```shell-session:ファイルロード用ディレクトリの作成とパーミッションの設定
# mkdir /var/db/mysqld/files
# chown -R _mysql:_mysql /var/db/mysqld/files
# chmod 770 /var/db/mysqld/files
```

secure-file-privの設定がない、あるいは設定されているパスが存在しない、パーミッションが不適切でアクセスができないなどだと、mysqld_safe起動時にエラーとなり起動しません。

```shell-session:secure-file-privまわりの設定が不適切な場合のエラーメッセージ
[ERROR] Failed to access directory for --secure-file-priv. Please make sure that directory exists and is accessible by MySQL Server. Supplied value : /opt/local/var/db/mysql57-files
```

secure-file-priv=/var/db/mysqld/filesで設定はされているけれど、パーミッションがroot:wheelだったり、755だったりなどすると、mysqld_safe起動時に警告を出します。

```shell-session:パーミッションに問題がある場合の警告メッセージ
[Warning] Insecure configuration for --secure-file-priv: Location is accessible to all OS users. Consider choosing a different directory.
```

my.cnfを設定する前にmysqld –initializeとか実行したり試行錯誤していると、こんなエラーでmysqld_safeが起動しない事があります。この場合は、一旦/var/db/mysqld配下をすべて消し(cd /var/db/mysql && rm -rf *)、mysqld –initializeをやり直したあとに改めて/var/db/mysqld/filesを作り直す必要があります。

```shell-session:設定順序が不適切だったりするなど不具合があった場合のエラーメッセージ
[ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.
```

## 初期設定

一通りの初期化が終わったので、MySQLサーバを起動させて初期設定に入ります。

### MySQLサーバの起動

MySQLサーバ、つまりmysqldを起動します。mysqldそのものを起動させるのではなく、ラッパーであるmysqld_safeを使って起動します。

```shell-session:MySQLサーバの起動
# mysqld_safe &
```

### 起動状況の確認

起動したかどうか、状況を確認します。
mysqld_safeコマンドを起動すると、/var/log/mysqld.logなどのログファイルに、起動状況のログが出力されます。このログを見て、正常起動しているかどうかを確認します。

最初に確認するのが、ログ出力の最後の行がAbortとかERRORであるかどうか。この出力があった場合は、MySQLサーバの起動は失敗しています。
設定ファイルやディレクトリパーミッションに不備があるなどが主な原因だと思いますので、あらためてmy.cnfに書かれているパス名・ディレクトリ名と、実際のパス・ディレクトリを確認してください。

「port: 0」になっていたら、ネットワーク接続ができない状態を意味しています。my.cnfの設定のところで注意書きをした「skip-networking」の設定の可能性を疑ってみるなど、設定を改めて見直して見てください。

Macの場合は考えにくいかとは思いますが、Linuxなど他のOSにおいてFirewall設定などがデフォルトで(あるいは自分で)設定している影響で、my.cnfで指定したポート番号での通信が確立できないという可能性もあるかもしれません。これらの可能性については、ここでは割愛します。

```shell-session:ログファイルによる起動確認
# cat /var/log/mysqld.log
2016-09-18T03:09:21.6NZ mysqld_safe Starting mysqld daemon with databases from /var/db/mysqld
2016-09-18T03:09:21.411563Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2016-09-18T03:09:21.413085Z 0 [Note] /opt/local/lib/mysql57/bin/mysqld (mysqld 5.7.15) starting as process 12795 ...
2016-09-18T03:09:21.414474Z 0 [Warning] Setting lower_case_table_names=2 because file system for /var/db/mysqld/ is case insensitive
2016-09-18T03:09:21.417107Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2016-09-18T03:09:21.417119Z 0 [Note] InnoDB: Uses event mutexes
2016-09-18T03:09:21.417123Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2016-09-18T03:09:21.417128Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.8
2016-09-18T03:09:21.417383Z 0 [Note] InnoDB: Number of pools: 1
2016-09-18T03:09:21.417477Z 0 [Note] InnoDB: Using CPU crc32 instructions
2016-09-18T03:09:21.418525Z 0 [Note] InnoDB: Initializing buffer pool, total size = 128M, instances = 1, chunk size = 128M
2016-09-18T03:09:21.428345Z 0 [Note] InnoDB: Completed initialization of buffer pool
2016-09-18T03:09:21.442972Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
2016-09-18T03:09:21.447976Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2016-09-18T03:09:21.448129Z 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2016-09-18T03:09:21.578287Z 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
2016-09-18T03:09:21.578989Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
2016-09-18T03:09:21.578998Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
2016-09-18T03:09:21.579238Z 0 [Note] InnoDB: Waiting for purge to start
2016-09-18T03:09:21.634491Z 0 [Note] InnoDB: 5.7.15 started; log sequence number 2532293
2016-09-18T03:09:21.634635Z 0 [Note] InnoDB: Loading buffer pool(s) from /private/var/db/mysqld/ib_buffer_pool
2016-09-18T03:09:21.634740Z 0 [Note] Plugin 'FEDERATED' is disabled.
2016-09-18T03:09:21.636158Z 0 [Note] InnoDB: Buffer pool(s) load completed at 160918 12:09:21
2016-09-18T03:09:21.638696Z 0 [Warning] Failed to set up SSL because of the following SSL library error: SSL context is not usable without certificate and private key
2016-09-18T03:09:21.638712Z 0 [Note] Server hostname (bind-address): '*'; port: 3306
2016-09-18T03:09:21.638785Z 0 [Note] IPv6 is available.
2016-09-18T03:09:21.638795Z 0 [Note] - '::' resolves to '::';
2016-09-18T03:09:21.638856Z 0 [Note] Server socket created on IP: '::'.
2016-09-18T03:09:21.645699Z 0 [Note] Event Scheduler: Loaded 0 events
2016-09-18T03:09:21.645832Z 0 [Note] /opt/local/lib/mysql57/bin/mysqld: ready for connections.
Version: '5.7.15' socket: '/var/run/mysql/mysql.sock' port: 3306 Source distribution
```

### 初期設定処理の実施

初期設定を実施します。
なお、以下のシチュエーションは、入力内容次第で順序や表示内容が変わる場合がありますので、この例における進行例と思ってください。実際の作業とここでの表記が多少異なっても、問題なく進められる場合もあります。

なお、ここで起動する初期設定コマンドにおいて、**初期化の実行の項目でメモを推奨した一時パスワードを使用する**ことになります。パスワードを入力しないと、先に進むことができないので注意してください。

```shell-session:初期設定用コマンドの実行
# mysql_secure_installation
```

### rootの新規パスワード設定

一時パスワードではない、任意の新規rootパスワードを設定します。

```shell-session:新規パスワードの設定
The existing password for the user account root has expired. Please set a new password.
New password: 自分の新しいパスワード
Re-enter new password: 自分の新しいパスワード(再入力)
```

### パスワード検証プラグインの使用可否

パスワード検証プラグインを使って、mysqlで設定・使用するユーザのパスワードを検証するかどうかを決定します。このプラグインは、CREATE USERやGRANT、SET PASSWORD構文で入力されるパスワードを評価します。 
プラグインを使用してパスワード検証をしたいならY、ゆるい(安全でない)パスワードも通したいならNを入力します。ここではYを選択しています。

```shell-session:パスワード評価
VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?
Press y|Y for Yes, any other key for No: Y
```

### パスワード検証基準の設定

パスワード検証プラグインの検証基準を設定します。強弱の検証ポリシーが表示されるので、このうちのどれにするかを番号で選択します。

| 強度 | 番号 | 基準 |
|:--|:--|:--|
|LOW|0|8文字以上のパスワード|
|MEDIUM|1|8文字以上で数字、アルファベットの大小文字、記号の混在|
|STRONG|2|8文字以上で数字、アルファベットの大小文字、記号、辞書ファイルの混在|

ここでは2(STRONG)を選択しています。初期パスワードのような比較的ランダムな状態の強固なパスワードを評価基準としてプラグインが動作します。

```shell-session:
There are three levels of password validation policy:
LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
```

### rootパスワードの検証

この検証ポリシーに照らし合わせた上で、最初に入力したrootパスワードを検証プラグインで評価し、その上で再入力するかどうかを決定します。
「Estimated strength of the password」欄に表示される数値が100に近いほど検証ポリシーに合致しているものと見積もられていることになります。
ここではパスワードを変更することにしたのでYを選択しています。

```shell-session:rootパスワードの検証
Using existing password for root.
Estimated strength of the password: 100
Change the password for root ? ((Press y|Y for Yes, any other key for No) : Y
```

### 強いパスワードの入力

STRONGで説明されているような強いパスワードで、rootパスワードを再設定します。

```shell-session:rootパスワード再設定
New password: 強いパスワード
Re-enter new password: 強いパスワード(再入力)
```

### rootパスワードの検証結果

プラグインによって検証され、強度の見積もり値が出力されます。100に近いほどポリシーに合致するパスワードとみなされます。
いま設定したrootパスワードで作業を続けるならYを選択します。ここではYを選択して、初期設定の先に進みます。

```shell-session:rootパスワード検証結果
Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : Y
```

### テストアカウントの削除

MySQLは、デフォルトで匿名ユーザを持っています。この匿名アカウントを使うことで、ユーザアカウントを作成しなくてもMySQLにログインすることができます。ただしこれはテスト用のアカウントなので、プロダクション環境(要するに本番運用)移行前に削除することが望まれています。
そもそも使わないため今この場で消すのであれば、Yを選択します。ここではYを選択して、匿名アカウントを削除しています。

```shell-session:匿名アカウントの取り扱いの決定
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.
Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y
```

削除が成功したら、以下のようにメッセージが表示されて先に進みます。

```shell-session:削除成功
Success.
```

### rootのリモート接続可否

MySQLは通常、rootアカウントはローカルホストからの接続のみ許可されるように設定されています。これは、不特定のユーザに対してネットワーク経由でrootパスワードのブルートフォースや辞書攻撃などといった形での類推を回避するためのものです。たとえばサーバへのアクセスがファイアウォールなどを通じて限定された環境にて運用されるため、リモートからのrootログインを許可したいという場合もあるでしょう。ただし、MySQLとしては禁止を推奨しています。
rootのリモートログインを禁止するならYを選択します。許可するならNを選択します。ここではYを選択しました。

```shell-session:rootアカウントのリモートログイン可否設定
Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y
```

設定が成功したら、以下のようにメッセージが表示されて先に進みます。

```shell-session:設定成功
Success.
```

### テスト用データベースの削除

MySQLは、デフォルトで誰でもアクセスできるtestデータベースを持っています。これはあくまでもテストのためのものであり、プロダクション環境(本番環境)に移行する前に削除する必要があるものです。しかし、もともとtestデータベースを使わないのであれば、今から削除しておくほうが望ましいでしょう。
testデータベースと、そのアクセス権限を削除するならYを選択します。ここではYを選択しました。

```shell-session:testデータベースとアクセス権限の削除の決定
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y
```

削除に成功したら、以下のようにメッセージが表示されます。
ちなみにtestデータベースが削除(DROP)されればアクセス権限(privileges)を消すまでもない(どうせアクセスできないのだから)と思いますが、testデータベースは後日改めて作成することが可能で、その際にtestデータベースへのアクセス権限が残っていたら、それが生き残ってしまうため、権限も同時に削除します(と私は認識しています)。

```shell-session:削除成功
- Dropping test database...
Success.

- Removing privileges on test database...
Success.
```

### 設定の反映

アクセス権限テーブルを再読み込みすることで、ここまで行ってきたすべての変更を即時反映することになります。
再読み込みをして即時反映するならYを選択します。ここではYを選択しました。

```shell-session:設定の即時反映
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y
```

即時反映に成功したら、初期設定の完了です。

```shell-session:即時反映に成功
Success.
All done!
```

## サーバへの接続確認

mysqlコマンドを使って、MySQLサーバへの接続確認を行います。rootでログインできれば、接続成功となります。
さきほど入力(再設定)したrootパスワードを使用して、rootでログインします。コマンドプロンプトが出たら接続は成功です。

```bash:サーバ接続確認
# mysql -hlocalhost -uroot -p'rootパスワード'
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 5.7.15 Source distribution
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
```

さらに「CREATE DATABASE temp_db;」などとデータベースを作成し、「use temp_db;」と入力してtemp_dbデータベースに移行し、SQLやDDLなどを操作するなど、通常通りの使用を確認できれば、インストールと設定は完了です。