MySQL
docker

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

Dockerfile

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 で動かした。

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

# mysqlコマンドが必要。もしなければ以下で入れておく。
apt install -y mysql-client
準備
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
コンテナ起動
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 しているのは、ローカルにコピーしたほうが時間の短縮になるため。

# --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.
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
接続
/usr/bin/mysql -uroot -h 127.0.0.1
alter 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;を投入したところ、通常は以下出力となるが

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

ハマリポイント

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).

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

/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

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

mkdir /var/lib/mysql-files

--initialize

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

スクリーンショット_2017-10-31_22-02-42.png

/var/run/mysqld がない

mkdir /var/run/mysqld

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

mkdir ./src/mysql-5.7.20/data/

接続できない

$ /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オプションが必要だった。