‘Docker’ タグのアーカイブ

murave

データベースコンテナでのバックアップの話(Docker)

先週、NISA(一般社団法人長崎県情報産業協会)の『Pythonと Docker ではじめる Web アプリ開発&運用入門』という研修に参加しました。

その中でコンテナ上のデータベースの論理バックアップの話があり、ホストPCにdumpツールを入れて普通にバックアップすればOKという流れだったのですが、自分はこの考え方で面倒な状況に陥ったことがありましたので

「稼働中のデータベースコンテナと同じイメージ上のdumpツールを別コンテナ起動してバックアップした方がバージョンミスマッチを回避できるので良いと思います」

というフィードバックをしました。

講師の方には伝わっていたと思いますが、痛い目の話、実際の例、あとちょっとしたハマりどころについて書いておこうと思います。

痛い目にあった話

今回の研修はMariadbでしたし、出力がSQLスクリプトのdumpをする場合はそもそもあまり問題ないと思います。
自分の場合はPostgreSQLを使うことが多く、pg_dumpのバージョンミスマッチでdumpできなくなるということを複数回経験しています。

今回のようにホストPCにツールを入れる場合ですが、コンテナとホストPCのOSが同じとは限らない(というか違うことが多い)わけで運用していくうちにコンテナで稼働しているデータベースのバージョンとホストPCのツールのバージョンを合わせるのが面倒な状況になることが結構あります。

他の例としては、昔、開発環境として Laradock (https://laradock.io/) を使っていたのですが、Laradock ではデータベースコンテナと作業用のコンテナが別になっています。作業用のコンテナにデータベースのクライアントツールを導入して扱うという形になるのですが、コンテナ間のバージョンを合わせるのが面倒で早々に破綻しました。

PostgreSQLでの実施例

そんなわけで、データベースコンテナ(のイメージ)には対応したバージョンのクライアントツールが入っているんだからそれを使うのがいいんじゃない?というわけです。

自分の場合、コンテナまわりの操作は docker compose、データベースはPostgreSQLのことが多いので、その組み合わせでの例とさせていただきます。

データベースコンテナのサービス名が postgres 、データベースがsampledb、データベースユーザーがsampleuser、パスワードがsamplepassとします。

docker compose exec -T postgres bash -c "PGPASSWORD=samplepass pg_dump -Fc --no-acl --no-owner -h postgres -U sampleuser sampledb" > sampledb.dump
PGPASSWORDをコマンドラインで書いちゃってるのはあまり良くないですね。

リストアも同じようにできると思いますが、自分はエラーメッセージなど確認したいので一旦コンテナにdumpファイルをコピーしてコンテナ上で実施しています。

# ホストPC上
$ docker compose cp sampledb.dump postgres:/tmp/
$ docker compose exec postgres bash

# コンテナ上
root@440414c73f7a:/# pg_restore -U sampleuser -d sampledb /tmp/sampledb.dump
root@440414c73f7a:/# rm /tmp/sampledb.dump
素のdockerとcomposeのexecの違いでハマる前述の exec でオプションに -T (–no-TTYでもOK)を指定しているのに注目してください。これは疑似 TTY 割り当てを無効化するものです。
TTY有効だとpg_dumpからバイナリで出力されたファイルが破損します。

ややこしいのがdocker compose execだとTTY有効がデフォルト、docker execだとTTY無効がデフォルトだということです。

同じ目にあった人がいたので解決は早かったですが、気づかんて。

Why is pg_restore segfaulting in Docker?
https://stackoverflow.com/questions/63934856/why-is-pg-restore-segfaulting-in-docker

リストア時にセグメンテーションフォールトを喰らって気づくという悲劇もありえます。リストアのテスト大事ですね。

murave

Docker Desktop for Mac さん、API無反応で起動に失敗しがち

Docker Desktop 使ってますか?

ボクの開発環境はもうどっぷりと依存してます。
大抵の開発で必須なので動かなくなると復旧するまでなにもできなくなります。

そんな、 Docker Desktop for Mac さんですが、このところかなり頻繁にAPIが反応しない的なメッセージをよこして起動に失敗します。
そしてメッセージについてくる対処用のボタンをクリックしても無反応で Docker Desktop に対して何もできなくなります。

最初は本当にビビりましたが、繰り返すうちに慣れ、なんとなく理由も想像できてきました。

よく発生するのはOSの更新直後の再起動後、Mac自体が異常終了したあとの起動後です。
どうもOSのバックエンド処理がガンガンに負荷を上げているために Docker のAPIが反応を返せず起動に失敗するようです。

まぁ、想像ですけど。

しばらく待ってバックエンド処理が落ち着いてから再起動をOSの再起動を行うと良いようです。
すぐに再起動してもバックエンド処理が落ち着いてなくてまた失敗したりしますので、まずは落ち着いてコーヒーでも淹れてきましょう。

たいした内容ではないのですが、無駄にドキドキさせられたので記事にしてみました。

murave

雰囲気でDocker Composeを使ってて落ちた穴

昔やった案件の開発環境を作る必要がでまして

「docker-compose.ymlとDockerfileをgitで管理しているオレに死角はないぜ」

とか思ったんですけど、死角に落とし穴がバンバンでひどい目に会いました。
まぁ、見えてる穴に落ちたってのもありますが。

名前かぶりに気をつけよう

先に書いておくと、ハマったのは大体がボリュームの名前かぶりでした。

とある日のSlack

「開発環境復活。docker-composeだとデフォルトだとディレクトリ名ベースで名付けが行われるので同じディレクトリ名におかれた別プロジェクトでボリューム名が被る。結果、変なボリュームができて想定しているボリュームが使用されずににっちもさっちも行かなくなってた。
ボリューム全部削除して、COMPOSE_PROJECT_NAME環境変数で名付けのベースを変更して作り直して復活。」

参考: docker-composeで作成されるものの名前を明示的に指定する方法 – Qiita

落ちた穴

昔のプロジェクトの環境を作ろうとして今の環境がぶっ壊れる

前のSlackのヤツですが、今やってるプロジェクトと前のプロジェクトのdocker-compose.ymlがあるディレクトリ名が同じ今のプロジェクトの開発環境が死にました。昔のプロジェクトのほうも死んでました。

泣きながらとりいそぎ今の環境の方をbuildし直すじゃないですか? うまくいかないんだな、これが。

残っているボリュームが影響してbuidしてもにっちもさっちもいかなくなりますので、前にも書いている通り、残っているボリュームを削除してからbuildする必要があります。

あと、COMPOSE_PROJECT_NAME環境変数で名付けのベースを変更できるので.envでプロジェクト名などをベースにかぶらないように設定を入れておくのが吉です。

COMPOSE_PROJECT_NAME=old_project_name
「とりあえずlatestでいいか」で後になって困る新規開発だと基本的には最新バージョンで開発環境を作るじゃないですか。

FROM postgres:latest
とかDockerfileに書いて。

いいんですけど、そのままにしておくと後で環境を作り直すときに当時と違うバージョンが入って困るわけです。
完全に見えてる落とし穴なんですけど、ズッポリですよ。

ちなみに2019年8月中旬現在で11が入りました。そして当時のバージョンで作り直す。

FROM postgres:9.6
「失敗失敗」とか軽く思いながらno-cacheでbuildし直すじゃないですか? うまくいかないんだな、これが。

残っているボリュームが(以下略)

番外編:パッケージが消えていって困る

PostGISのバージョンがバンバン上がって過去パッケージが消えていくのでbuildで結構な頻度でひどい目にあってたり。

以上です。

ちゃんと(ベースの)イメージファイルを取っておけよって話ではある。

murave

Laravel Dusk メモ

Laravel 5.5 & Laradock(https://github.com/laradock/laradock)環境です。
Laradockについては少し古いかもしれません(活発に更新されているのですぐに古くなる)。

Laravel Dusk はブラウザテストをお手軽に!というパッケージです。

https://readouble.com/laravel/5.5/ja/dusk.html

Laradockでのセットアップと使い始めてすぐのログインテストで何回かつまづいたのでメモを残しておきます。

Laradock問題

まず、Laradockのドキュメント通りにセットアップしても正常動作しませんでした(2017/12/13現在)

LaradockでLaravel Duskを動かすメモ(https://qiita.com/uenoryo/items/bdded2dc6f8e5f2075cc)

こちらを参考に動かすことができました。ただし、seleniumコンテナへフォントを入れなければ失敗時などのscreenshotで日本語が表示されないため laradock/selenium/Dockerfile を以下のように編集しました。

FROM selenium/standalone-chrome

MAINTAINER Edmund Luong <edmundvmluong@gmail.com>

RUN sudo apt-get update && sudo apt-get install fonts-ipafont -y

EXPOSE 4444

そしてビルド

$ docker-compose up -d --build selenium
これで豆腐たち(□□□□的な文字化け)とはおさらばです。

Session問題

午前中、Laravelへのブラウザでのログインテストが動かないと悩んでた原因はセッションドライバにarrayを設定してたというオチだったアカウントがこちら。単体テスト用の設定流用してたのが原因(調整漏れ)。
— murave (@murave) 2017年12月13日

.env.dusk.local でDusk用の設定が行えるのですが単体テスト用のものをコピーして修正していたためセッションにarrayドライバを設定してたのでした。

https://readouble.com/laravel/5.5/ja/session.html

array – セッションはPHPの配列として保存されるだけで、リクエスト間で継続しません。

ですよね〜。

他環境への影響を抑えるためにCookieドライバを使用することにしました。

SESSION_DRIVER=cookie
Transaction問題テスト開始時にデータベースクリアしてマイグレーションしてマスター用のSeederでマスター準備してFakerでユーザー作って、みたいにやるじゃないですか。

Laravel 5.5 にはRefreshDatabaseという便利トレイトがあります。

https://readouble.com/laravel/5.5/ja/database-testing.html

前のテストがその後のテストデータに影響しないように、各テストの後にデータベースをリセットできると便利です。インメモリデータベースを使っていても、トラディショナルなデータベースを使用していても、RefreshDatabaseトレイトにより、マイグレーションに最適なアプローチが取れます。テストクラスてこのトレイトを使うだけで、全てが処理されます。

そう、全てが処理されるのです。影響が残らないようにTransaction中に処理してRollbackまでしてくれる。便利。

だが思い出そう。ブラウザテストだということを。

ページ遷移したらデータ消えるわけで、うっかりRefreshDatabaseトレイトを使うと想定通りに動作しません。

そんなわけで、リセットしてマイグレーションまでだけしてくれるDatabaseMigrationsトレイトのほうを使いましょう。

例

<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use App\User;

class LoginTest extends DuskTestCase
{
use DatabaseMigrations; //実行時DB内容は消える

public function testLogin()
{
\Artisan::call('db:seed', ['--class' => 'MasterSeeder']);
$user = factory(User::class)->create();

$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'secret')
->press('ログイン')
->assertPathIs('/home');
});
}
}
この例ではDatabaseMigrationsで再構築されたまっさらな状態のデータベースにマスター系投入用のMasterSeederを実行した後にテスト用のユーザーを作成してログイン処理のテストを行っています。

okada

dockerからAmazon EC2への奮闘記

今までdockerを使ったことがなかった私、勉強がてらLaradockを使ってLaravelで開発を行っていました
そこからAmazon EC2へ移行するまでの過程で色々行き詰まり、
なんとかLaradockからEC2へ環境移行することができましたので、
今後のメモのためにログを残しておこうと思います

今回書く内容はLaradockの環境設定〜Amazon EC2へ設置するまでの簡単な流れで行き詰ったものを晒すだけのほぼ日記の様なものです(笑)

Docker for Mac 〜 Laradockを導入するまで

$ git clone --depth=1 -b v5.5.1 https://github.com/LaraDock/laradock.git
$ cd laradock
$ cp env-example .env
$ vi .env
…とこのようにまずはドキュメントに従って導入を行っていました
しかし

問題1:Laradockを導入したが起動しない

$ docker-compose up -d nginx mysql
ERROR: Couldn't connect to Docker daemon - you might need to run `docker-machine start default`.
$ docker-machine start default
Host does not exist: "default"
defaultを設定しないといけない?

$ docker-machine create --driver virtualbox default
をすればよい、とのことだったので実行してみたけれどエラー発生
そもそもDocker for Macはvirtualbox使わなくても良くなった、との記述を見かけた気が…

解決:boot2dockerとDocker for macが混在していた

Laradockの問題ではなく、dockerの問題でした
どうやらDocker for Macを入れる前にboot2dockerをhomebrewで入れていたようで、
PC内にdockerが2つある状態でした
ということでboot2dokerを削除

brew uninstall docker boot2docker docker-compose docker-machine
念のためDocker for Macもアンインストールして入れ直し、もう一度動かしてみた所無事にLaradockを動かすことができました!
まぁこんなことしでかすなんて自分くらいしかいないと思うので参考にはならないかもしれませんが、思い出の一つとして…

問題2:MySQLが動かない

次のようなエラーが発生してMySQLが起動しない
docker-compose psを実行してみた所、laradock_mysql_1のstateはExitでした

$ docker-compose up --build -d nginx mysql
$ docker-compose exec mysql bash
ERROR: No container found for mysql_1
ログを確認

$ docker logs laradock_mysql_1
2017-08-01T08:01:50.339823Z 0 [ERROR] [FATAL] InnoDB: Table flags are 0 in the data dictionary but the flags in file ./ibdata1 are 0x4800!
解決:MySQLのデータファイルを削除このエラーに関しては.envファイルを確認し、MySQLのデータファイルを削除した所、起動に成功しました

$ vi .env ⇐ laradock/.envを確認
10 ### Data Path:
11 # For all storage systems.
12
13 DATA_SAVE_PATH=~/.laradock/data
$ cd ~/.laradock/data
$ mv data data.old
Laradockが動かない…動かない…と入れ直しを繰り返した時にゴミファイルとして残ってしまっていた模様

ローカルからAmazon EC2へ

システムが出来上がり、次はシステムをローカルからEC2へ移行作業へ…
まずLaradock環境内のバージョンを確認
EC2側にもPHP7.1、MySQL5.7、nginxをインストール

$ docker-compose exec workspace bash
# php -v
PHP 7.1.4-1+deb.sury.org~xenial+1 (cli) (built: Apr 11 2017 22:12:32) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.1.4-1+deb.sury.org~xenial+1, Copyright (c) 1999-2017, by Zend Technologies
# php artisan --version
Laravel Framework 5.4.30
# exit
exit

$ docker-compose exec mysql bash
# mysql --version
mysql Ver 14.14 Distrib 5.7.19, for Linux (x86_64) using EditLine wrapper
# exit
exit
EC2側環境作成PHP、MySQL、nginxインストール後…
以下のファイルを編集

$ vi /etc/php-fpm.d/www.conf
24 user = nginx
25 ; RPM: Keep a group allowed to write in log dir.
26 group = nginx

48 listen.owner = nginx
49 listen.group = nginx
50 listen.mode = 0660
$ vi /etc/nginx/nginx.conf
40 server {

44 root /var/www/html/public;

73 location ~ \.php$ {
74 root /var/www/html/public;
75 fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
76 fastcgi_index index.php;
77 fastcgi_param SCRIPT_FILENAME /var/www/html/public$fastcgi_script_name;
78 include fastcgi_params;
79 }
それぞれのサービスを起動 & 自動起動するように設定

$ sudo service nginx start ⇐ サービス起動
$ sudo service php-fpm start
$ sudo service mysqld start
$ sudo chkconfig nginx on ⇐ 自動起動
$ sudo chkconfig php-fpm on
$ sudo chkconfig mysqld on
MySQL5.7ではrootユーザには初期パスワードなるものが存在し、
最初はそのパスワードでログインする

[ec2-user@ip-172-31-46-167 ~]$ sudo cat /var/log/mysqld.log
2017-08-03T06:59:34.488345Z 1 [Note] A temporary password is generated for root@localhost: Jt?eJUpbO6kl ⇐コレ
rootのパスワードは変更しておくべし

$ mysql -u root -p
> ALTER USER 'root'@'localhost' IDENTIFIED BY 'パスワード';
Laravelのドキュメントに従いComposerをインストール後、Laravel5.4を導入

$ composer global require "laravel/installer"
$ cd /var/www/
$ laravel new html
ブラウザでLaravelのTOP画面が表示されることを確認

Laradockからお引っ越し

ローカルのLaravelより、gitを使ってアーカイブファイル作成!
Laradockには既に.gitignoreファイルが準備されており、logファイル等など必要ないファイルは省かれる様になっていました。便利!

git archive HEAD --output=~/WorkSpace/project/release/archive.zip
このファイルを/var/www/htmlに上書き(.envの取扱い注意!)し、
ファイルの権限を変更 chmod o+w -R storage
そして以下のコードを実行

$ php artisan config:cache
$ php artisan route:cache
.env.exampleを.envに書き換え、EC2上の環境に合わせた設定に書き換えます
そしてブラウザで確認!

The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.

こんなエラーが表示されましたよ…
以下を実行

$ php artisan key:generate
$ php artisan config:clear
.envファイルの APP_KEY= にキーがセットされるわけですね
無事TOPが表示されました!

でもページが遷移しませんよ?

/loginへ遷移できません。404エラーです。
原因はnginxの設定。5.4のドキュメント通りにnginx.confを編集。

$ vi /etc/nginx/nginx.conf
49 location / {
50 try_files $uri /index.php?$query_string;
51 }
これで他のページへも遷移できるようになりました!
以上がEC2への引っ越し奮闘記になります。

murave

Dockerのデータボリュームをバックアップ・リストア

現状試しているのはローカルストレージドライバでのデータボリュームの場合ですが、コンテナでバインドして扱うのが常道な感じです。

データ・ボリュームのバックアップ・修復・移行
http://docs.docker.jp/engine/userguide/dockervolumes.html#id9

に書いてあるやり方ですね。

jenkins-masterというコンテナの /var/jenkins_home をバックアップ・リストアする例をあげます。

バックアップ

$ docker run --rm -u root --volumes-from jenkins-master -v $(pwd):/backup alpine tar czvf /backup/alp.tar.gz -C /var jenkins_home
リストア

$ docker run --rm -u root --volumes-from jenkins-master -v $(pwd):/backup alpine sh -c "rm -rf /var/jenkins_home/* && cd /var && tar xzvf /backup/alp.tar.gz"
念のためユーザーにrootを指定し、Alpine Linuxで実行しています。バックアップ時、相対ディレクトリ指定のためtarの-Cオプションを使用しています。展開時はゴミファイルが残るのが嫌だったのでリストア対象ディレクトリ内を全て削除してから展開していますが、削除はしないほうが安全かもしれません。

以上です。

データボリュームコンテナについての蛇足
データ永続化のために別途データボリュームコンテナを作成している場合でもローカルストレージなデータボリュームを使用していれば同じこと、docker save や docker export でバックアップというわけにはいかないようです。残念。

murave

DockerのJenkinsコンテナでアクセスログについて悩んでいた

muraveです。

今年の4月にリリースされたJenkins 2系をDocker公式イメージ

OFFICIAL REPOSITORY jenkins

で試していたのですが、Webフックからキックしてのビルドを試していて大変ハマリました。Webフックからのアクセス状況とかどうなってんだ?などなどtcpdumpとか使って調べてたんですけど、アクセスログもあったほうがいいよね、と思ったのでした。

公式イメージのJenkinsさんってそのまま立ち上げたらアクセスログ出してくれないんですよね。

調べまして、JENKINS_OPTSに

--accessLoggerClassName=winstone.accesslog.SimpleAccessLogger --simpleAccessLogger.format=combined --simpleAccessLogger.file=ログファイルのパス
てな感じで設定すれば良いことはわかりました。分かりましたが、コンテナ内やボリュームにログファイルを出すのは違う気がします。docker logs で見ることができるログはどういう扱いなんでしょう?

デフォルト設定のロギング・ドライバのログ(JSON形式)はホストの

/var/lib/docker/containers/(コンテナID)/(コンテナID)-json.log
にあって、これが docker logs の表示元になっています。

問題はどうやったらここに出力されるのか?ですが、標準出力、標準エラー出力へ出力すれば良いそうです。例えば、Nginx公式イメージのDockerfileでは

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
と、/dev/stdout と /dev/stderr へのシンボリックリンクを作成することで対応しているようです。

Jenkinsさんの場合は先ほどのJENKINS_OPTSでログファイルのパス指定が可能でしたので

--accessLoggerClassName=winstone.accesslog.SimpleAccessLogger --simpleAccessLogger.format=combined --simpleAccessLogger.file=/dev/stdout
と設定すると標準出力に出力されるようになり、コンテナのログにJenkinsさんへのアクセスログが記録されるようになりました。

これでDockerのロギング・ドライバでアクセスログも統一的にあつかえますね。
例えばFluentdロギング・ドライバに切り替えてログ集約するようにしたりする場合にも安心です。

murave

Docker for Mac の使用されていないデータボリュームを削除

murave@ファイルの権限絡みで -v オプションの事を調べていたら今日という日が終わりそう、です。

やりたいことは別にあったんですが、せっかく調べたのでメモを残しておきます。今回、結論は最後に書くので経緯とか読みたくない方は最後まで飛ばして下さい。

データボリュームっていうのは例えば、

$ docker run -v コンテナ上のディレクトリ名 busybox
$ docker volume create --name test-volume
などとすると、

$ docker volume ls
DRIVER VOLUME NAME
local 17e11ebb9fc56e18d99b175d77b0c37b90da561bff07d601279ab7bfc0b0f414
local test-volume
と言った感じでデータボリュームがホスト上に出来ます。
ランダム文字列な名前のほうは前記docker runのように名前未指定な場合によろしくやってくれてるわけです。問題なのが、コンテナを削除しても基本的にこれが残るってこと。

「開発用RDBをゲットだぜ」シリーズの調査中

$ docker rm -f $(docker ps -aq)
でバンバン動作しているコンテナを含めて全部削除しまくっていたのですが、datastoreのデータボリュームが残りまくっているってわけです。

消さねばならぬ。

Macの何処につくられているのだろう?と調べていたのですが、Docker for MacもVMベースで動作しているわけでして、

~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2
の中にDockerイメージ共々はいってるんだと思います。こいつ、どんどん膨らみます。

VM上の状況は

$ docker run --rm -it -v /:/vm-root alpine ls -l /vm-root/var/lib/docker/volumes
total 220
drwxr-xr-x 3 root root 4096 Aug 15 08:16 0f2fee8ea1b621774905898b5fb22b79478251f0e6d3901dde5698e9430cfc0f
drwxr-xr-x 3 root root 4096 Aug 16 07:58 15bc83ca3604af4ca66713e3388dd879722ceca30ffcdc3fc05763d75b3dd077
drwxr-xr-x 3 root root 4096 Aug 26 07:23 17e11ebb9fc56e18d99b175d77b0c37b90da561bff07d601279ab7bfc0b0f414
drwxr-xr-x 3 root root 4096 Aug 23 02:08 18e959a82fa5e40f1d02facba9924d6f06f18aa2c2da2c6e07dd3a1c7e429f3a
drwxr-xr-x 3 root root 4096 Aug 16 08:38 261353597aeb63143e38b51306cba073835251795614fa7d3a43ee49008d5756
drwxr-xr-x 3 root root 4096 Aug 16 08:01 3326a3b193cc8e6bce214fd3c33fcc4f10c1317e92959e70c3752d3df83eb771
drwxr-xr-x 3 root root 4096 Aug 16 06:58 33844fb90eb4326f978bc35caf2cb244d5a66d211b4164fcb5130be2a7453c63
drwxr-xr-x 3 root root 4096 Aug 15 08:31 3adc5ccfbd90b059ce8a9fe55034411d0b49d97d18cd00147a11dcc49d7cef3b
drwxr-xr-x 3 root root 4096 Aug 16 05:20 42754f3ef91f7460cb31295de09d203a1c25097051b70451f356fde334efea0a
drwxr-xr-x 3 root root 4096 Aug 16 08:42 4b90ee03c48f0a328ba261cb9174dd62f9f167d544b73576db5171270d17b262
drwxr-xr-x 3 root root 4096 Aug 16 02:23 4e755fcf6804123343e9b40a681dbe5fc9de280cd526497d0afcf89cec963534
drwxr-xr-x 3 root root 4096 Aug 22 08:46 5ce13286a385bd714468174491c9f65bbb9e05e8bdfc4628d89a85b437344351
drwxr-xr-x 3 root root 4096 Aug 16 06:23 63a9fca8324a2e462901a6a92ebbdf08a82fbe50268771353c0017a9ef799f45
drwxr-xr-x 3 root root 4096 Aug 16 06:57 76e7e31984beea2b91d340e91fa20011c69b4922e0c2f22be1c290d75e45b82d
drwxr-xr-x 3 root root 4096 Aug 16 07:46 834badee4c6ae6290f6879779e33885772b962131f85496aaabd43809d6cec62
drwxr-xr-x 3 root root 4096 Aug 9 07:32 8805aaef35fe82403bfb0939cfbb3b66100f051b3bd5f52412e14a28bcb95342
drwxr-xr-x 3 root root 4096 Aug 16 09:02 96b0b5ca5c624d03c310db3e9952d1211cc7123e72aa193eb3d4515286b83c64
drwxr-xr-x 3 root root 4096 Aug 15 09:08 9c09d5123d36a349273a8b3213273317c75b7ba205316e0d6bb1b4596f5a5f01
drwxr-xr-x 3 root root 4096 Aug 16 08:28 9dc0d5b59060261c19a6db265010f975ec90597cf8a80a7a02d629917b7aa143
drwxr-xr-x 3 root root 4096 Aug 9 07:32 a00d1be583a3f80f156f5a77b98cad6687dce8772e345c7b5065e895ba46f110
drwxr-xr-x 3 root root 4096 Aug 16 07:36 a013c0992864236cbf872e33edd7063394111d7d64ce261a0bd49f3533f93612
drwxr-xr-x 3 root root 4096 Aug 15 04:40 a68f07ad252ceb73f842767ed3f2fd1a9628e0ec4b64516d8fd5371a63e1b22e
drwxr-xr-x 3 root root 4096 Aug 10 06:45 ab798f01923ecd36eaf5f92d627a16391c77f036cd9ff45ddad2f7fde6534b03
drwxr-xr-x 3 root root 4096 Aug 16 07:22 ad28856ce3de53103b2c20c7c9f32c616f1e806913c5f37d0c45984443bde92d
drwxr-xr-x 3 root root 4096 Aug 16 07:08 b381a395adf5d6237ba57ca70c8b1c7edd8834b113c204472be25e15ac44d84f
drwxr-xr-x 3 root root 4096 Aug 16 06:20 b5c2eaa2958e6679d9efbcbf915e6018314aeb90641c50368b2728c41efffc4b
drwxr-xr-x 3 root root 4096 Aug 16 09:26 ba6da465e33ee52ff58187a3104624693a5485d1d6084b6a0972a29a92a1210b
drwxr-xr-x 3 root root 4096 Aug 16 07:27 ce72509bdb23ac0791c71b4736c1e92a0d0175e5cc443616639ee8691bfe6613
drwxr-xr-x 3 root root 4096 Aug 23 02:08 d5237bdcc608de3ca3c8a513df47676330844043503dc436e8e6a8a05bb31873
drwxr-xr-x 3 root root 4096 Aug 16 08:55 d548a17a651bef16453793d79c2ff06b20d650039a68627b6736e27002bbaf0d
drwxr-xr-x 3 root root 4096 Aug 15 08:58 d56545b413bb10139f76ecee271a2477be0fe1ca92e1657ec7f57a73e6ae8806
drwxr-xr-x 3 root root 4096 Aug 16 07:14 d6230f991ce389726121d7e03fdf25b8f9bf72764cce7c93998cabca213551d9
drwxr-xr-x 3 root root 4096 Aug 15 09:09 dcd3700e965b5a6b9707e68584106820cbab895d7da99c046af9e6d36ebfd81b
drwxr-xr-x 3 root root 4096 Aug 16 07:30 e0130a6762542f69b670112cf2033f2816546293b4d5ed3189d7b89fb78ff676
drwxr-xr-x 3 root root 4096 Aug 16 08:54 e042396da246d4700a0db47a511d4f0e3214f646e2df2c034bf7056a1fc20f5c
drwxr-xr-x 3 root root 4096 Aug 10 06:52 e316debbe91c31402271ec50af58c975d6af1e345177426708c95ece630da53c
drwxr-xr-x 3 root root 4096 Aug 22 08:49 e8ed864afc8da5110fb3041182c9c26b0bb497293b0df666783d9ad1ba292854
drwxr-xr-x 3 root root 4096 Aug 15 04:54 f5d083892023106f0e17fae041da3d133c2b2ce7f334db24e285f41b1033f425
drwxr-xr-x 3 root root 4096 Aug 16 07:34 f703c284137660273a40e94bf6f2eb3f4ae0305113d8e513774be4da7a857862
drwxr-xr-x 3 root root 4096 Aug 15 08:58 initdb
-rw------- 1 root root 65536 Aug 26 07:23 metadata.db
てな感じで確認できますが、こんなことを調べても動作理解以上の意味はないのでした。

なぜなら

$ docker volume ls
で必要な情報は取得できるからです。

さらに docker volume ls の -q オプションでボリューム名のみ、-f オプションのdangling=true指定でコンテナから参照されていないもの(danglingは宙ぶらりんなという意味らしい)を取得できるので大変便利。

ということで以下のコマンドを実行すると使用されていないデータボリュームが削除されます。

$ docker volume rm $(docker volume ls -q -f dangling=true)
参考
No space left on device error – Docker for Mac – Docker Forums
volume ls — Docker-docs-ja 1.12.RC ドキュメント

murave

Docker公式イメージで開発用RDBをゲットだぜ(MariaDB、Percona Server編)

まえがき

Docker公式イメージで開発用RDBをゲットだぜ(PostgreSQL編)
Docker公式イメージで開発用RDBをゲットだぜ(MySQL編)

に続き予定どおりMariaDB編をお送りします。一言で済むので結論を書くと

「公式イメージのページを読み比べたらMySQLと同じでした。ありがとうございます」

という内容です。ついでに

Docker Hub で Official縛りでMySQLを検索したらPerconaも出てきたので思い出したりした。定期的にPercona良さそうだなぁと思って名前を忘れるを繰り返す存在。
— murave (@murave) 2016年8月16日

というわけでPercona Serverの公式イメージのページも見てみたらこれもMySQLの場合と同じ扱いでしたので含めてみましたよ。

Docker for Macを使用しております。

MySQL派生RDBMS達の公式イメージ

まえがきに引用したTwitterのつぶやきにあるようにDocker HubでOfficial縛りでMySQLを検索しました。

Docker HubでOfficial縛りでMySQLを検索

この様に3件引っかかります。

MariaDBとPercona ServerはMySQL派生のRDBMSです。概要をつかみやすいかと思うページへのリンクをあげておきますね。

MariaDB – Wikipedia
MySQL 互換のDB、Percona Server を使う理由 – Qiita

個人的に、これからはMySQLよりMariaDBを使うことが多くなりそうに感じておりますし、以前からPercona Serverは良さそうだと思っていました(そして忘れる)。

これらの公式イメージのページを読み比べた所、設定用の環境変数の名称も含めてMySQLと同じで、扱いかたはほぼ同じと考えて良いのではないかと思います。

というわけで、調査はすっとばしてMySQL用に作成したファイルをベースにイメージの日本語化とDocker Composeでの起動をしてみようと思います。

基本的に変更点だけ書いていきますのでベースについては前回MySQLの記事をご参照ください。

MariaDB

OFFICIAL REPOSITORY mariadb

2016年8月中旬現在の対応バージョン。

10.1(10.1.16), 10.0 (10.0.26), 5.5(5.5.51)

デフォルトで選択されるlatestは10.1系。今回は10.1系で進めます。

日本語化されたイメージの作成

default localeを設定するDockerfileを作ります。

Dockerfile

FROM mariadb:10.1

MAINTAINER murave

RUN apt-get update && apt-get install locales -y && localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
日本語化されたイメージ、mariadb_jp:10.1を作ります。

$ docker build -t mariadb_jp:10.1 ./
確認

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mariadb_jp 10.1 3fa6fd548f5b 11 seconds ago 420.8 MB
mariadb 10.1 9a0138c02438 3 weeks ago 391.6 MB
参考に元の公式イメージも載せましたが、サイズが結構でかいですね。

Docker Compose で起動する

docker-compose.ymlを調整。ポートフォワードは23306に設定しました。

docker-compose.yml

datastore:
image: busybox
volumes:
- /var/lib/mysql
container_name: mariadb-datastore

mariadb:
image: mariadb_jp:10.1
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_USER: foo
MYSQL_PASSWORD: foopassword
MYSQL_DATABASE: foodb
volumes:
- ./conf.d/:/etc/mysql/conf.d
- ./initdb/:/docker-entrypoint-initdb.d
ports:
- "23306:3306"
volumes_from:
- datastore
container_name: mariadb-db
起動します。

$ docker-compose up -d
Creating mariadb-datastore
Creating mariadb-db
起動状態の確認。

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad95de15a67a mariadb_jp:10.1 "docker-entrypoint.sh" About a minute ago Up About a minute 0.0.0.0:23306->3306/tcp mariadb-db
3515029a71b5 busybox "sh" About a minute ago Exited (0) About a minute ago mariadb-datastore
Sequel Pro を使いポートフォワードした23306にユーザーfooでfoodbデータベースに接続して初期化スクリプトが動作していることを確認できました。

クライアントコンテナでの接続用に作成したclientスクリプトを調整。

client

docker run -it --link mariadb-db:mysql --rm mariadb_jp:10.1 sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
接続して動作確認します。

$ ./client
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.1.16-MariaDB-1~jessie mariadb.org binary distribution

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

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

MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| foodb |
| information_schema |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

MariaDB [(none)]> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | foo |
| % | root |
+------+------+
2 rows in set (0.00 sec)

MariaDB [(none)]> USE foodb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [foodb]> SHOW TABLES;
+-----------------+
| Tables_in_foodb |
+-----------------+
| users |
+-----------------+
1 row in set (0.00 sec)

MariaDB [foodb]> SELECT * from users;
+------+--------+
| id | name |
+------+--------+
| 1 | first |
| 1 | second |
+------+--------+
2 rows in set (0.00 sec)

MariaDB [foodb]> exit
Bye
Percona ServerOFFICIAL REPOSITORY percona

2016年8月中旬現在の対応バージョン。

5.7(5.7.13), 5.6 (5.6.32), 5.5 (5.5.51)

MariaDBと違ってMySQL準拠ですね。latestは5.7系。今回は5.7系で進めます。

日本語化されたイメージの作成

default localeを設定するDockerfileを作ります。

Dockerfile

FROM percona:5.7

MAINTAINER murave

RUN apt-get update && apt-get install locales -y && localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
日本語化されたイメージ、percona_jp:5.7を作ります。

$ docker build -t percona_jp:5.7 ./
確認

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
percona_jp 5.7 22e3746fb304 8 minutes ago 367.2 MB
percona 5.7 ed3fc6a159be 3 weeks ago 338.2 MB
こちらも参考に元の公式イメージも載せました。

Docker Compose で起動する

docker-compose.ymlを調整。ポートフォワードは33306に設定しました。

docker-compose.yml

datastore:
image: busybox
volumes:
- /var/lib/mysql
container_name: percona-datastore

percona:
image: percona_jp:5.7
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_USER: foo
MYSQL_PASSWORD: foopassword
MYSQL_DATABASE: foodb
volumes:
- ./conf.d/:/etc/mysql/conf.d
- ./initdb/:/docker-entrypoint-initdb.d
ports:
- "33306:3306"
volumes_from:
- datastore
container_name: percona-db
起動します。

$ docker-compose up -d
Creating percona-datastore
Creating percona-db
起動状態の確認。

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b39630e2fd38 percona_jp:5.7 "docker-entrypoint.sh" 35 seconds ago Up 33 seconds 0.0.0.0:33306->3306/tcp percona-db
ea4c9a0a21b5 busybox "sh" 35 seconds ago Exited (0) 34 seconds ago percona-datastore
Sequel Pro を使いポートフォワードした33306にユーザーfooでfoodbデータベースに接続して初期化スクリプトが動作していることを確認できました。

クライアントコンテナでの接続用に作成したclientスクリプトを調整。

client

docker run -it --link percona-db:mysql --rm percona_jp:5.7 sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
接続して動作確認します。

$ ./client
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 6
Server version: 5.7.13-6 Percona Server (GPL), Release '6', Revision 'e3d58bb'

Copyright (c) 2009-2016 Percona LLC and/or its affiliates
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> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| foodb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)

mysql> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | foo |
| % | root |
+------+------+
2 rows in set (0.00 sec)
せっかくのPercona Serverなのでレスポンスタイム分布の取得をやってみましょう。

Response Time Distribution

を参考にPluginを導入して有効化します。

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME_AUDIT SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.03 sec)

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.03 sec)

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME_READ SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.03 sec)

mysql> INSTALL PLUGIN QUERY_RESPONSE_TIME_WRITE SONAME 'query_response_time.so';
Query OK, 0 rows affected (0.02 sec)

mysql> SHOW PLUGINS;
+-----------------------------+----------+--------------------+------------------------+---------+
| Name | Status | Type | Library | License |
+-----------------------------+----------+--------------------+------------------------+---------+
(長いので省略しました)
| QUERY_RESPONSE_TIME_AUDIT | ACTIVE | AUDIT | query_response_time.so | GPL |
| QUERY_RESPONSE_TIME | ACTIVE | INFORMATION SCHEMA | query_response_time.so | GPL |
| QUERY_RESPONSE_TIME_READ | ACTIVE | INFORMATION SCHEMA | query_response_time.so | GPL |
| QUERY_RESPONSE_TIME_WRITE | ACTIVE | INFORMATION SCHEMA | query_response_time.so | GPL |
+-----------------------------+----------+--------------------+------------------------+---------+
52 rows in set (0.00 sec)

mysql> SET GLOBAL query_response_time_stats = on;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * from INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
+----------------+-------+----------------+
| TIME | COUNT | TOTAL |
+----------------+-------+----------------+
| 0.000001 | 0 | 0.000000 |
| 0.000010 | 0 | 0.000000 |
| 0.000100 | 0 | 0.000000 |
| 0.001000 | 1 | 0.000271 |
| 0.010000 | 0 | 0.000000 |
| 0.100000 | 0 | 0.000000 |
| 1.000000 | 0 | 0.000000 |
| 10.000000 | 0 | 0.000000 |
| 100.000000 | 0 | 0.000000 |
| 1000.000000 | 0 | 0.000000 |
| 10000.000000 | 0 | 0.000000 |
| 100000.000000 | 0 | 0.000000 |
| 1000000.000000 | 0 | 0.000000 |
| TOO LONG | 0 | TOO LONG |
+----------------+-------+----------------+
14 rows in set (0.01 sec)
おー。取れた取れた。

初期投入データのサンプルについてsecondのidを1にしているという恥ずかしい状況に気づいたのでSequel Proで編集、ついでに3:thirdを追加してから再度レスポンスタイム分布を取得してみます。

mysql> SELECT * from INFORMATION_SCHEMA.QUERY_RESPONSE_TIME;
+----------------+-------+----------------+
| TIME | COUNT | TOTAL |
+----------------+-------+----------------+
| 0.000001 | 0 | 0.000000 |
| 0.000010 | 8 | 0.000057 |
| 0.000100 | 3 | 0.000173 |
| 0.001000 | 9 | 0.001866 |
| 0.010000 | 0 | 0.000000 |
| 0.100000 | 2 | 0.058591 |
| 1.000000 | 0 | 0.000000 |
| 10.000000 | 0 | 0.000000 |
| 100.000000 | 0 | 0.000000 |
| 1000.000000 | 0 | 0.000000 |
| 10000.000000 | 0 | 0.000000 |
| 100000.000000 | 0 | 0.000000 |
| 1000000.000000 | 0 | 0.000000 |
| TOO LONG | 0 | TOO LONG |
+----------------+-------+----------------+
14 rows in set (0.00 sec)
発行されたSequel Proから発行されたクエリ文増えました。スロークエリはないですね(あたりまえ)。

気軽に試せて良いですね。
以上、「Docker公式イメージで開発用RDBをゲットだぜ」でした。

murave

Docker公式イメージで開発用RDBをゲットだぜ(MySQL編)

まえがき

Docker公式イメージで開発用RDBをゲットだぜ(PostgreSQL編)

に続きMySQL編です。Docker for Macを使用しております。はじめるよ〜。

MySQL

Dockerの公式MySQLイメージの使い方を徹底的に解説するよ

という素敵なページがありましたのでこちらも参考にさせていただこうと思います。

公式イメージを調べる

OFFICIAL REPOSITORY mysql

2016年8月中旬現在、以下のバージョンがあるようです。

5.7(5.7.14), 5.6(5.6.32), 5.5(5.5.51)

少ない気もしますが覚悟を決めて5.5以上で行きましょう。「古いMySQL死すべし」という意思を感じます。

起動する

latestは5.7系のようですので、あえて5.5系ですすめてみます。

$ docker run -d --name mysql55 -e MYSQL_ROOT_PASSWORD=mysecretpassword -p 13306:3306 mysql:5.5
公式の例を参考にコンテナの3306を13306にポートフォワードしています。

接続する

mysqlコマンドで接続します。公式の例はクライアントコンテナでenvの情報を利用して一気につなぐ方法ですね。

$ docker run -it --link mysql55:mysql --rm mysql:5.5 sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.51 MySQL Community Server (GPL)

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> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

mysql> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | root |
+------+------+
1 row in set (0.00 sec)

mysql> exit
Bye

便利だけど長い〜。シェルスクリプトにでもしておくのがよいでしょうか。clientというファイルに書いて実行権限を付けておきます。

$ ./client
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.5.51 MySQL Community Server (GPL)

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>
おけおけ。PostgreSQLの方にも作っておこうかな。

次に Sequel Pro 先生で繋いでみます。13306にポートフォワードしてますので

Sequelで接続(接続情報)

サクッと繋がりました。

Sequelで操作

mysqlデータベースの中など覗いてみたり。楽ちん。

設定ファイルなど

コンテナのシェルを起動して探検してみます。

$ docker exec -it mysql55 bash
とりあえずmy.cnfを眺めてみる。

root@688eab40f1a8:/# more /etc/mysql/my.cnf
[mysqld]
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
!includedir /etc/mysql/conf.d/
/etc/mysql/conf.d/ のcnfファイルを読み込むようになっていますね。

拡張する

いろいろな方法が用意されているようです。

まず、先ほど調べたようにコンテナの /etc/mysql/conf.d/ にcnfファイルを置くと読み込まれます。

PostgreSQLの公式イメージと同じくコンテナの /docker-entrypoint-initdb.d に拡張子がsh、sql、sql.gzのファイルを置くとコンテナ起動時に実行してくれます。mysql/5.5/docker-entrypoint.shを眺めてみたところ、

for f in /docker-entrypoint-initdb.d/*; do
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
done
このように仕組みも同じ感じでした。

他、PostgreSQLの時と同じように日本語化されたイメージを作ろうと思います。

日本語化されたイメージの作成

PostgreSQLでのやり方を参考にdefault localeを設定するDockerfileを作ります。

Dockerfile

FROM mysql:5.5

MAINTAINER murave

RUN apt-get update && apt-get install locales -y && localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.utf8
公式イメージにはlocalesが入っていなかったのでlocaledefの前にapt-getで導入するようにしました。

日本語化されたイメージ、mysql_jp:5.5を作ります。

$ docker build -t mysql_jp:5.5 ./
確認

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql_jp 5.5 42465a1c94d5 22 minutes ago 284.5 MB
設定ファイルの調整/etc/mysql/conf.d/ にバインドするディレクトリconf.dを作成して追加の設定を書いたcnfファイルを置きます。

conf.d/custom.cnf

[mysqld]
character-set-server=utf8
Mac上のcond.dディレクトリを/etc/mysql/conf.dにバインドするようにオプションを付けて起動します。

$ docker run -d --name mysql55 -e MYSQL_ROOT_PASSWORD=mysecretpassword -p 13306:3306 -v $(pwd)/conf.d/:/etc/mysql/conf.d mysql_jp:5.5
execでコンテナ上の/etc/mysql/conf.dのls結果を見てバインドされているか確認。

$ docker exec -it mysql55 ls /etc/mysql/conf.d
custom.cnf
おけおけ。

データベースの初期化

アカウント、データベース作成は設定用に準備された環境変数(MYSQL_USER、MYSQL_PASSWORD、MYSQL_DATABASE)を利用し、/docker-entrypoint-initdb.d ディレクトリのスクリプト実行を利用してテーブル作成などをしてみたいと思います。

ユーザーをfoo、データベースをfoodbとする前提で初期化用のsqlファイルを作ります。
initdb/01createtable.sql

CREATE TABLE foodb.users (id int , name varchar(255));
GRANT ALL ON foodb.users TO foo;
INSERT INTO foodb.users (id, name) VALUES (1, 'first'), (1, 'second')
foodbデータベースにusersテーブルを作成しユーザーfooに権限を与え、usersテーブルに初期値をINSERT文で投入というシナリオです。

/docker-entrypoint-initdb.d をバインドして起動。

$ docker run -d --name mysql55 -e MYSQL_DATABASE=foodb -e MYSQL_USER=foo -e MYSQL_PASSWORD=foopassword -e MYSQL_ROOT_PASSWORD=mysecretpassword -p 13306:3306 -v $(pwd)/conf.d/:/etc/mysql/conf.d -v $(pwd)/initdb/:/docker-entrypoint-initdb.d mysql_jp:5.5
確認します。

$ ./client
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.51 MySQL Community Server (GPL)

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> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| foodb |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

mysql> select Host, User from mysql.user;
+------+------+
| Host | User |
+------+------+
| % | foo |
| % | root |
+------+------+
2 rows in set (0.00 sec)

mysql> USE foodb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> SHOW TABLES;
+-----------------+
| Tables_in_foodb |
+-----------------+
| users |
+-----------------+
1 row in set (0.01 sec)

mysql> SELECT * from users;
+------+--------+
| id | name |
+------+--------+
| 1 | first |
| 1 | second |
+------+--------+
2 rows in set (0.00 sec)

mysql> exit
Bye
想定通りに初期化されているようです。

スクリプト実行についての悲しい補足情報

はじめはsqlファイルではなくshファイル(シェルスクリプト)内でmysqlコマンドを呼び出して初期化しようとしていました。実際に使用するユーザーでテーブル作成やデータ投入したいじゃないですか。気分的に。

initdb/01createtable.sh

!/bin/bash
set -e

mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" $MYSQL_DATABASE <<-EOSQL
CREATE TABLE users (id int , name varchar(255));
EOSQL
このようなスクリプトでテーブル作成は行われるのですがshスクリプト実行後にコンテナがExitしてしまうという症状が発生。解決出来ずにハマりまくり、sqlファイルでの初期化に方針変更しました。

その後、落ち着いて公式イメージのページを見なおしていると

No connections until MySQL init completes

という項目があるのに気付きました。mysqlコマンドで接続しているのが原因っぽいですね。

Docker Compose で開発用の MySQL を起動する

日本語化したイメージを使用しconf.dのcnfファイルで設定調整、initdbのスクリプトで初期化、データの永続化はbusyboxを使用する方針でdocker-compose.ymlを作成します。

docker-composeでmysql & postgreSQL をサクッと起動

を参考にさせていただきました。

docker-compose.yml

datastore:
image: busybox
volumes:
- /var/lib/mysql
container_name: mysql-datastore

mysql:
image: mysql_jp:5.5
environment:
MYSQL_ROOT_PASSWORD: mysecretpassword
MYSQL_USER: foo
MYSQL_PASSWORD: foopassword
MYSQL_DATABASE: foodb
volumes:
- ./conf.d/:/etc/mysql/conf.d
- ./initdb/:/docker-entrypoint-initdb.d
ports:
- "13306:3306"
volumes_from:
- datastore
container_name: mysql-db
起動します。

$ docker-compose up -d
Creating mysql-datastore
Creating mysql-db
以降、使用するあいだはstop・startで停止・起動。

mysqlコマンドでの接続用のclientスクリプトのコンテナ名をmysql-dbに修正して接続して動作を確認。
Sequel Pro を使いポートフォワードした13306にユーザーfooでfoodbデータベースに接続しての操作もできました。

ディレクトリファイル構成は以下のようになりました。

.
├── Dockerfile
├── client
├── conf.d
│   └── custom.cnf
├── docker-compose.yml
└── initdb
    └── 01createtable.sql