「Dockerを使ってMySQLのソースコードを読む環境を作ってみる」の準備をしてみた

* @y-asabaさんの[Dockerを使ってMySQLのソースコードを読む環境を作ってみる - Qiita](https://qiita.com/y-asaba/items/34e043166af20cf3c465)の準備をしてみた。

## Dockerfile

```apache:Dockerfile
# サンプルは14.04ベースだったので16.04にしてみた
FROM ubuntu:16.04

# versionをあとで変更しやすいように
ENV MYSQLVER 5.7.20
ENV DEBIAN_FRONTEND=noninteractive

# 日本のミラーサイトのほうが早いのと、build-dep用にdeb-srcの有効化
RUN sed -i \
 -e "s/archive.ubuntu.com/jp.archive.ubuntu.com/g" \
 -e "s/# deb-src/deb-src/g" \
 /etc/apt/sources.list
RUN apt update
RUN apt build-dep -y mysql-server
RUN apt install -y wget curl gdbserver

# mysqlのソース取得
WORKDIR /usr/src
RUN wget "http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-boost-${MYSQLVER}.tar.gz"
RUN tar xzf mysql-boost-${MYSQLVER}.tar.gz

# mysqlのソースをdebug付きでビルド
WORKDIR /usr/src/mysql-${MYSQLVER}
RUN CFLAGS=-O0 cmake \
 -DWITH_PIC=1 \
 -DWITH_DEBUG=1 \
 -DWITH_INNODB_EXTRA_DEBUG=1 \
 -DCMAKE_INSTALL_PREFIX="/opt/mysql57" \
 -DDOWNLOAD_BOOST=1 \
 -DWITH_BOOST=./boost
RUN make -j$(getconf _NPROCESSORS_ONLN) install

# my.cnf何かあったほうが良いので、ひな型からコピーしたが、エラー回避用に追加
RUN cp ./packaging/rpm-docker/my.cnf /etc/my.cnf
## 面倒なのでrootで実行します！
RUN sed -i -e "s/user=mysql/user=root/" /etc/my.cnf
RUN echo "explicit_defaults_for_timestamp=true" >> /etc/my.cnf
RUN echo "skip-name-resolve" >> /etc/my.cnf
# mysqld起動用のディレクトリがいくつかないので作成
RUN mkdir /var/lib/mysql-files
RUN mkdir /var/run/mysqld

# mysqldの初期化
RUN /opt/mysql57/bin/mysqld --initialize

EXPOSE 3306
EXPOSE 2345
VOLUME /usr/src

# skip-grant-tablesは、mysql側のアカウント作成の手間を省きたかったため。
CMD gdbserver :2345 /opt/mysql57/bin/mysqld --skip-grant-tables
```

## dockerを動かすホスト

今回は ubuntu17.10 で動かした。

```sh:
apt install -y docker.io
# vi /etc/group にて現在のユーザーをdockerグループに追加。
# 一度ログアウト、ログインしてdockerグループに属していることを確認。

# mysqlコマンドが必要。もしなければ以下で入れておく。
apt install -y mysql-client
```

```sh:準備
wget "http://ftp.jaist.ac.jp/pub/mysql/Downloads/MySQL-5.7/mysql-5.7.20.tar.gz"
mkdir src
cd src
tar xzf mysql-5.7.20.tar.gz
```

```sh:コンテナ起動
docker run \
 --rm \
 -v $(pwd)/src/mysql-5.7.20:/usr/src/mysql-5.7.20 \
 -v /etc/localtime:/etc/localtime \
 -p 3306:3306 \
 -p 2345:2345 \
 -t --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
 mysql-debug-5.7 \
 sh -c "
cp /opt/mysql57/bin/mysqld /usr/src/mysql-5.7.20/;
gdbserver :2345 /opt/mysql57/bin/mysqld --skip-grant-tables
"
```

cp しているのは、ローカルにコピーしたほうが時間の短縮になるため。

```sh:
# --cap-add=SYS_PTRACE --security-opt seccomp=unconfined をつけなかった場合、以下エラーになる
(gdb) run
Starting program: /usr/local/src/mysql-4.0.30/sql/./gen_lex_hash 
warning: Error disabling address space randomization: Operation not permitted
Cannot create process: Operation not permitted
During startup program exited with code 127.
```



```zsh:gdb
$ gdb
...
(gdb) file ./src/mysql-5.7.20/mysqld

# リモートに接続。上で fileを実行していなければ /opt/mysql57/bin/mysqld が読み込まれます。
(gdb) target remote :2345
Remote debugging using :2345
Reading /opt/mysql57/bin/mysqld from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /opt/mysql57/bin/mysqld from remote target...
Reading symbols from target:/opt/mysql57/bin/mysqld...done.
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
Reading /lib64/ld-linux-x86-64.so.2 from remote target...
Reading symbols from target:/lib64/ld-linux-x86-64.so.2...Reading /lib64/ld-2.23.so from remote target...
Reading /lib64/.debug/ld-2.23.so from remote target...
(no debugging symbols found)...done.
0x00007ffff7dd7c30 in ?? () from target:/lib64/ld-linux-x86-64.so.2

# ローカルとリモートのPATHマッピング http://d.hatena.ne.jp/syasuda/20071208/1197116413
(gdb) set substitute-path /opt/mysql57/ ./src/mysql-5.7.20/

# ブレークポイントを指定
(gdb) b mysql_alter_table

# remoteでmysqldの起動
(gdb) c
```

* [デバッガ(gdb)の使い方 - マイクロデータベース管理システムの実装](http://wombat.cc.tsukuba.ac.jp/~furuse/jikken/text-07/text-07.html)

```sh:接続
/usr/bin/mysql -uroot -h 127.0.0.1
```

```sql:alter&nbsp;tableしてみる
create database test;
use test;
create table test(id int);

alter table test add column no int;
```

`alter table test add column no int;`を投入したところ、通常は以下出力となるが

```sql
mysql> alter table test add column no int;
Query OK, 0 rows affected (2 min 10.28 sec)
Records: 0  Duplicates: 0  Warnings: 0
```

以下で止まることを確認。
`(gdb) bt` でソースファイル名と行番号が出力される。
`(gdb) c` で停止された処理を続行。

![スクリーンショット_2017-10-31_22-47-30.png](https://qiita-image-store.s3.amazonaws.com/0/25728/8edb1dfa-3c8e-0d28-338d-083d5cb809a8.png)


## ハマリポイント

mysql5.6以降を触っていなかったためmysqlの設定自体でハマった。

### TIMESTAMP with implicit DEFAULT value is deprecated

> [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).

* [MySQL :: MySQL 5.6 リファレンスマニュアル :: 5.1.4 サーバーシステム変数](https://dev.mysql.com/doc/refman/5.6/ja/server-system-variables.html#sysvar_explicit_defaults_for_timestamp)
* [explicit_defaults_for_timestamp という WARNING | katz+](http://katzplus.com/centos-6/mysql/explicit_defaults_for_timestamp-%E3%81%A8%E3%81%84%E3%81%86-warning/)

>  これから作るTIMESTAMPは「明示的に NOT NULL 指定」をしない限り「NOT NULL」じゃなくなる。
よってTIMESTAMPについて何も指定していなかったSQLで新しいテーブルに書き込むと現在時刻は自動的に入らず、「NULL」が入ります！

```sh:/etc/my.cnf
[mysqld]
explicit_defaults_for_timestamp=true
```

### secure-file-priv設定のエラー

> mysqld: Error on realpath() on '/var/lib/mysql-files' (Error 2 - No such file or directory)
> [ERROR] Failed to access directory for --secure-file-priv. Please make sure that directory exists and is accessible by MySQL Server. Supplied value : /var/lib/mysql-files

* デフォルト値が mysql 5.7.6 で変更になった様子。 [MySQL :: MySQL 5.7 Reference Manual :: 5.1.5 Server System Variables](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_secure_file_priv)
    * [日本語マニュアル](https://dev.mysql.com/doc/refman/5.6/ja/server-system-variables.html#sysvar_secure_file_priv)は5.6止まりで、5.7.6の情報はなかった。

> ディレクトリの名前に設定すると、LOAD_FILE() 関数と、LOAD DATA および SELECT ... INTO OUTFILE ステートメントの効果を制限し、そのディレクトリ内のファイルにのみ機能します。

```shell-session
mkdir /var/lib/mysql-files
```

### --initialize

* [MySQL :: MySQL 5.7 Reference Manual :: 4.4.2 mysql_install_db — Initialize MySQL Data Directory](https://dev.mysql.com/doc/refman/5.7/en/mysql-install-db.html)
* mysql5.7.6から`mysql_install_db`ではなく `mysql --initialize`に変更になった。


### --skip-name-resolve つけないと大量のWARNING

![スクリーンショット_2017-10-31_22-02-42.png](https://qiita-image-store.s3.amazonaws.com/0/25728/4e390d12-3d91-0f1e-6e67-e9888990852c.png)

### /var/run/mysqld がない

```
mkdir /var/run/mysqld
```

### ./src/mysql-5.7.20/data/がない

```
mkdir ./src/mysql-5.7.20/data/
```

## 接続できない

```sh:
$ /usr/bin/mysql -uroot -h 127.0.0.1
ERROR 1130 (HY000): Host '172.17.0.1' is not allowed to connect to this MySQL server
```

`--skip-grant-tables`オプションが必要だった。