コンテナ型仮想化ツールとして知られるDocker。
Dockerconなんてものも開催されているようで、最近目に触れる機会がますます増えています。
自分も、「プログラマだからインフラとかよくわかんないし…」「まだプログラマ2年目だからそのうち覚えればいいだろうし…」などと目を背けていたのですが、この盛り上がりをみるとそんなことも言っていられないような気になり始めました。
「そろそろDockerを始めないとやばい…」
ということで、使って覚える派の自分はとりあえずローカル開発環境でDockerを試してみることにしました。
前提
- 本記事では、ローカルのVagrant仮想環境でのDockerの導入をチュートリアル形式で解説します。
- Dockerの機能を余すところなく紹介していると記事があまりにも冗長になってしまうので、主要な機能をかいつまんで紹介しています。適宜参考記事を掲載しているので、詳細についてはそちらを確認してください。
今回の環境
- Vagrant 1.8.1
- VirtualBox 5.0.20
- CentOS 7.2.1511
- Docker 1.11.2
Dockerで開発環境を構築する
まずはVagrant上にDockerで開発環境を構築してみましょう。
Vagrantの仮想環境の中にDockerでコンテナ仮想環境を作ることで、Vagrantのboxひとつで複数の仮想環境をやりくりすることができます。Virtualboxのboxひとつでも相当容量を食うので、日常的に複数の仮想環境を使っている人はそれだけでかなりのメリットですね。
今回はひとまず、Docker上にLAMP環境を構築してみようと思います。
ディレクトリ構成
ローカルのVagrantfileがあるディレクトリに、こんな構成の作業ディレクトリを作成してみます。
Vagrant仮想環境の構築
Vagrant上でDockerを使用するにあたり、Docker公式が管理しているパッケージを使用するにはCentOS7系が必要なので、CentOS7を導入しておいてください。
Dockerのインストール
それではDockerのインストールです。
ちなみにDockerの操作にはroot権限が必要なので、ここからの作業はすべてsudo
あるいはroot権限での操作を前提とします。
- まずは既存のyumパッケージを更新します。
# yum update
- Docker公式のyumリポジトリを追加します。
# tee /etc/yum.repos.d/docker.repo <<-'EOF' [dockerrepo] name=Docker Repository baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/ enabled=1 gpgcheck=1 gpgkey=https://yum.dockerproject.org/gpg EOF
- Dockerパッケージをインストールします。
# yum install docker-engine
- Dockerデーモンを開始します。
# systemctl restart docker.service
- ついでにサーバー起動と同時にDockerデーモンを起動するように設定します。
# chkconfig docker on
これでDockerがインストールできました。
参考:
http://docs.docker.jp/engine/installation/linux/centos.html
Dockerコンテナを起動してみる
Dockerイメージの取得
さっそくDockerコンテナを起動してみましょう。まずはMySQLのコンテナを作成してみます。
とはいえ、なにもないところからコンテナは作れません。Dockerではコンテナをつくるために「 イメージ 」というものが必要になります。
コンテナはイメージを元に作られ、同じイメージを元にすればどこでも全く同じコンテナを作成することができます。
Docker Hub(会員登録が必要)で公式イメージが配布されているので、それをもとにコンテナを作成しましょう。
# docker pull mysql:latest
docker pull
でDocker Hubからイメージを取得することができます。ちなみに mysql:latest
の部分は<イメージ名>:<タグ>の形式で指定します。イメージ名とタグもDocker Hubで調べることができます。
イメージの取得が終わったら、docker images
コマンドで取得済みのイメージの中にmysql:latestが含まれているか確認しましょう。
Dockerコンテナの起動
さて、今度こそDockerコンテナの起動です。
# docker run --name app-db mysql:latest
docker run [オプション] <イメージ名>:<タグ>
で、指定したイメージを元にしたコンテナが起動します。それから、 --name
オプションでコンテナに名前を付けることができます。名前を付けておくといろいろ便利なので習慣的につけるようにしましょう。
さて、これでDockerコンテナは起動できたでしょうか!
error: database is uninitialized and password option is not specified You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD
あれ…。
環境変数の設定
Dockerコンテナは起動時に環境変数を設定することができます。
そしてDockerでは、特定のコンテナで特定の環境変数を設定しておくとコンテナ内の様々な設定を行うことができます。
一例としてMySQLコンテナで使用できる環境変数をみてみましょう。
環境変数名 | 概要 |
---|---|
MYSQL_ROOT_PASSWORD | rootのパスワードを設定します |
MYSQL_DATABASE | 指定した名前のデータベースが作成されます |
MYSQL_USER | 指定した名前のユーザーが作成されます |
MYSQL_PASSWORD | MYSQL_USERで作成したユーザーのパスワードを設定します |
MYSQL_ALLOW_EMPTY_PASSWORD | yes を設定すると、rootで空のパスワードを許可します |
なるほど、先ほどのエラーはrootのパスワードを設定するか、空のパスワードでもいいのか、ランダムパスワードでいいのかを指定してくれ、というものだったようです。
# docker run --name app-db -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=app mysql:latest
ということで環境変数を設定してコンテナを起動します。環境変数の設定は-e
オプションです。
ついでにDB作成用の環境変数も設定してみました。
Error response from daemon: Conflict. The name "app-db" is already in use by container bd44a80999fb. You have to delete (or rename) that container to be able to reuse that name.
またエラーです…。
コンテナの削除
# docker ps -a
docker ps
コマンドで、現在稼働中のコンテナ一覧を確認することができます。また、-a
オプションで稼働していないものも含めたすべてのコンテナを確認できます。
STATUSの項目をみると、Exited
となっているコンテナがいくつかあるかと思います。
これは稼働が停止したコンテナです。コンテナは稼働を停止しても、明示的に削除しないかぎり残り続けるのです。気付かずに放っておくと停止したコンテナだらけになってしまうので注意してください。
先ほどのエラーは、起動に失敗したapp-dbコンテナが残っていたので、同じ名前のコンテナを新しく作成できない、というエラーでした。
# docker rm app-db
docker rm
コマンドでコンテナを削除することができます。不要なコンテナはしっかり削除しましょう。
さて、今度こそ。先ほどのコマンドを実行してみます。
# docker run --name app-db -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=app mysql:latest
コマンドを実行すると、いい感じでログが吐かれたのち、
…なぜか動かなくなります
同じ状況になった方は泣く泣くSSH再接続して次に進みましょう。
コンテナの挙動を理解する
Dockerコンテナは基本的に起動時にコマンドを渡され、そのコマンドが完了するとその役目を終えて動作を停止します。
また、コマンドが渡されなかった場合はイメージで設定されたデフォルトのコマンドが渡されます。
# docker run --name app-db -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=app mysql:latest ping -c 10 192.168.33.33
試しにこのコマンドを実行してみましょう。
イメージの後に引数を指定すると、コンテナ起動時に実行するコマンドを指定できます。
コンテナはpingコマンドを10回実行し、それが終わると勝手に終了します。
(ちなみに、先ほどSSH再接続した方はMySQLコンテナがまだ生きているので、docker rm -f app-db
でコンテナを削除しましょう。-f
オプションを付けると稼働中のコンテナも削除できます。)
MySQLのコンテナはデフォルトでdocker-entrypoint.sh
というコマンドが実行されます。
このコマンドではMySQLの基本的な初期設定を行っているようです。そのため、MySQLコンテナを起動する際は基本的にコマンドを渡すことはありません。
また、docker-entrypoint.shはその中でmysqldを呼び出しているようです。
そのため、普通にコンテナを起動しようとするとコマンドがいつまで経っても完了せず、先ほどのように如何様にもできなくなってしまった、というわけです。
MySQLコンテナを作成するときは、以下のようにしましょう。
# docker run --name app-db -d -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=app mysql:latest
-d
オプションで、コンテナをバックグラウンドで実行できます。
docker ps -a
でapp-dbコンテナのステータスを確認し、Exitedになっていなければ成功です。
Dockerイメージを作成してみる
続いてはPHPコンテナを作成してみましょう。
php:5.6.23-apache
イメージはPHPとApacheが同梱されているので、これと先ほどのMySQLコンテナを使ってLAMP環境にしていきます。
ブラウザからコンテナを見られるようにする
さて、ここからPHPコンテナを作っていきますが、まずはコンテナに外部からアクセスする方法について触れておきましょう。
# docker run --name app-web -d -p 8080:80 php:5.6.23-apache
docker run
時に、-p
オプションを指定すると外部からコンテナにアクセスできるポートを指定することができます。指定方法は-p <ホスト側ポート番号>:<コンテナ側ポート番号>
です。
これでコンテナを起動し、ブラウザからホスト側のIPアドレスにポート番号8080番でアクセスし、403エラー(Forbidden You don’t have permission to access / on this server.)が表示されていれば成功です。
ローカルとコンテナを同期させる
次に、ローカルとコンテナを同期させてみましょう。
仮想環境で作業する際に、ローカルのファイルを更新するとコンテナも更新されるようになれば非常に便利です。
ということで、ローカルのhtmlディレクトリと、コンテナのドキュメントルートを同期させてみましょう。
# docker run --name app-web -d -p 8080:80 -v /vagrant/app/web/html:/var/www/html php:5.6.23-apache
docker run
時に-v
オプションを付けると、ホスト側とコンテナ側で同期させるディレクトリをそれぞれ指定することができます。指定方法は、-v <ホスト側のパス>:<コンテナ側のパス>
です。
Vagrantで作成した仮想環境上では、ローカルのVagrantfileがあるディレクトリと仮想環境上の/vagrantが同期されるというのを利用し、ローカル→Vagrant→コンテナという流れでローカルとコンテナを同期させることができます。
ためしにローカルのhtmlディレクトリにindex.phpを作成し、ブラウザでアクセスしてみましょう。
Dockerfileでイメージを作成する
さて、これで無事にPHPコンテナが起動できたのですが、このコンテナをそのまま使うとなると色々問題が生じます。
例えば先ほどのindex.phpにecho date(‘Y-m-d’);
などとしてブラウザでアクセスしてみましょう。以下の様なエラーが表示されているはずです。
Warning: date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in /var/www/html/index.php on line 2
これはPHPの設定ファイルであるphp.iniでデフォルトタイムゾーンの設定をしていないことにより発生しているエラーです。
このエラーはPHPコンテナでphp.iniの設定を行えば表示されなくなるのですが、新しくコンテナを起動するたびにいちいちphp.iniを書き換えるのはあまりスマートではありません。
ということで、php.iniの設定がすでにされてあるイメージを作成してみましょう。
イメージの作成にはDockerfileを使用します。
先ほどのディレクトリ構成にあるように、webディレクトリ直下、htmlディレクトリと同じところにDockerfileを作成し、以下のように記述しましょう。
FROM php:5.6.23-apache ADD php.ini /usr/local/etc/php/ RUN apt-get update RUN docker-php-ext-install pdo_mysql
Dockerfileについて詳しいことはDockerfileリファレンスなどを見るとよいですが、ここでも簡単に解説します。
FROM
は新しく作成するイメージの元となるイメージを指定します。今回はphp:5.6.23-apache
イメージを元に作成します。
ADD
はホスト側にあるファイルをコンテナに送ります。ホスト側のphp.iniをコンテナの適切な場所に置くことで、コンテナ内PHPの設定を変更することが出来ます。
後半の二行では、コンテナ内のPHPに後ほど使用するpdo_mysql拡張を追加しています。
RUN
はイメージの中であらゆるコマンドを実行し、その結果を新しいイメージとして書き足していく命令です。
ちなみに、docker-php-ext-install
というのはPHPコンテナ内で使用できるコマンドで、引数で渡したPHP拡張を追加することができます。
さて、これでDockerfileが完成しました。
続いてホスト側からコンテナに送るphp.iniも作成しましょう。
[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"
こんな感じです。デフォルトタイムゾーンの設定の他に、mbstringの設定もしておきました。
これをDockerfileと同じ場所に置いておきます。すると、先ほど書いたDockerfileのADDでコンテナの指定した場所にphp.iniが作成されます。
では、いよいよイメージを作成してみましょう。
# docker build -t test/php:5.6.23-apache ./web
docker build [Dockerfileのあるディレクトリ]
でDockerfileからイメージを構築することができます。
-t
オプションでイメージ名を指定できます。<Docker Hubのアカウント名>/<イメージ名>:<タグ名>
とするのが一般的なようです。
コンテナをリンクさせる
MySQLコンテナ、PHPコンテナが構築できるようになったところで、これら二つのコンテナをリンクさせてLAMP環境を作成してみましょう。
先ほど作成したMySQLコンテナとPHPコンテナをリンクさせてみます。
# docker run --name app-web -d -p 8080:80 --link app-db:mysql -v /vagrant/app/web/html:/var/www/html test/php:5.6.23-apache
--link
オプションで、コンテナ間のリンクを実現することができます。設定方法は、--link <コンテナ名>:<エイリアス>
です。
PHPコンテナが起動したら、MySQLコンテナとちゃんとリンクされているかを確かめるため、ためしにindex.phpを以下のようにして、ブラウザで確認してみましょう。
<?php $dsn = 'mysql:dbname=app;host='.$_ENV['MYSQL_PORT_3306_TCP_ADDR']; $user = 'root'; $password = $_ENV['MYSQL_ENV_MYSQL_ROOT_PASSWORD']; $dbh = new PDO($dsn, $user, $password); $sql = 'SHOW DATABASES;'; foreach ($dbh->query($sql, PDO::FETCH_ASSOC) as $row) { echo '<p>'.$row['Database'].'</p>'; }
MySQLコンテナとPHPコンテナがリンクされていれば、DB名が表示されるはずです。
コンテナ間リンクについて簡単に説明しておきましょう。
PHPコンテナからMySQLコンテナにアクセスする際、本来ならばPHPコンテナもMySQLコンテナの外部からのアクセスとなるため、docker run
時に-p
オプションを使用してポートを開いておかなければなりません。
しかし、--link
オプションを使用すればポートを開かずとも別コンテナからアクセスすることができます。
--link
オプション付きで起動したコンテナはリンクしたコンテナの設定を格納した環境変数が渡されます。先ほどのスクリプトでは、MYSQL_PORT_3306_TCP_ADDR
, MYSQL_CONTAINER_ENV_MYSQL_ROOT_PASSWORD
という二つの環境変数を呼び出していました。
MYSQL_CONTAINER_ENV_MYSQL_ROOT_PASSWORD
については、MySQLコンテナ起動時に-e
オプションで指定したMYSQL_ROOT_PASSWORD
の値が格納されているだけですが、MYSQL_PORT_3306_TCP_ADDR
に格納されているのはMySQLコンテナの3306番ポートにアクセスするためのプライベートIPアドレスです。
これを使用することで、わざわざポートを開かずともPHPコンテナからMySQLコンテナにアクセスすることができるというわけです。
ちなみに接頭のMYSQL_
の部分はdocker run
時の--link
オプションで設定したエイリアスにより変わるので注意してください。
なにはともあれ、これでLAMP環境のできあがりです!
Dockerをいい感じで使っていくために
ここまで、Dockerを使用してLAMP環境を構築する方法を一通りさらってきましたが、ここからは実際にDockerを使用していくにあたり、「いい感じで」使っていくためのノウハウを紹介していきます。
Dockerの起動を簡略化する
先ほどのLAMP環境の例でやったように、Dockerは同時に複数のコンテナを運用する事が多いのですが、それをいちいちひとつずつ起動していくのは非常に面倒です。
出来ることならばコマンド一つで、使用するコンテナを一度に起動できるようにしたいものです。
そこで今回は、Dockerのオーケストレーションツール、Docker Composeを使用してみましょう。1
docker-composeのインストール
まずはdocker-composeのインストールです。
Dockerのホストに以下の様な手順でdocker-composeをインストールしましょう。
# curl -L https://github.com/docker/compose/releases/download/1.6.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose # chmod +x /usr/local/bin/docker-compose
上記ではusr/local/bin
にインストールしていますが、パスの通っているところならどこでも良いかと思います。インストールが完了したら、docker-compose --version
などとして、正しくインストールされているかチェックしておきましょう。
今回は、docker-compose version 1.6.2がインストールされました。
docker-compose.ymlの作成
docker-composeの起動時の設定はdocker-compose.ymlで定義します。
プロジェクト用のディレクトリ(ここではapp
)直下にdocker-compose.ymlという名前のファイルを作成しましょう。
docker-compose.ymlの書き方についてはここでは詳しく紹介しません。
以下の参考などに目を通してみてください。
今回は、このようなymlファイルを作成しました。
db: image: mysql:latest environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: app web: image: test/php:5.6.23-apache volumes: - /vagrant/app/web/html:/var/www/html links: - db:mysql ports: - 8080:80
docker-composeでは各コンテナをサービスとよび、サービスごとに設定を定義します。
今回はdb
, web
という二つのサービスを定義しており、設定されたとおりdb
→web
という順序でコンテナが起動します。(web
はdb
とリンクさせているので、db
から先に起動させるようにします)
コマンドでの実行・停止
docker-compose.ymlができたらdocker-composeコマンドを使ってプロジェクトの実行・停止をしてみましょう。
# docker-compose up -d
docker-compose up
で、docker-compose.ymlで定義したコンテナの構築、作成、起動、アタッチを行えます。-d
オプションを付けるとバックグランドでコンテナを起動してくれます。
docker ps
でコンテナの状況を確認してみると、
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8a56c6b16801 test/php:5.6.23-apache "apache2-foreground" 4 seconds ago Up 3 seconds 0.0.0.0:8080->80/tcp app_web_1 5f51fa97dba9 mysql:latest "docker-entrypoint.sh" 5 seconds ago Up 4 seconds 3306/tcp app_db_1
ちゃんと二つのコンテナが起動しているのがわかります。
ちなみにdocker-composeで起動するコンテナ名は、プロジェクト名とdocker-compose.ymlで定義したサービス名を元に自動で命名されています。
# docker-compose down
プロジェクトの停止はdocker-compose down
です。
実行すると、コンテナの停止、そして停止したコンテナの削除まで自動でやってくれます。
これで、わずらわしいコンテナの起動や停止をコマンド一つで実行できるようになりました!
参考:
コマンドライン・リファレンス
起動中のコンテナにターミナルでアクセスする
# docker exec -it app_web_1 /bin/bash
docker exec
コマンドで、起動中のコンテナに対してコマンドを実行することができます。
また、-i
, -t
オプションを付けるとターミナルでアクセスします。(これらのオプションはdocker run
でも使用可能です。)
Dockerコンテナは基本的にコンテナ内でexit
を実行するとコンテナ自体が停止してしまうのですが、docker exec
でアクセスした場合はexit
でコンテナの状態はそのままにコンテナから抜けることができます。
データベースのデータを永続化する
Dockerの特徴のひとつに、環境の破棄や再生成が容易という点があります。
これは環境の作成に失敗しても簡単に作り直せるという点で大きなメリットになるのですが、その反面いくつか弊害も生まれます。
例えば、先ほど作ったMySQLコンテナは一旦停止してしまえば、次回再び起動したときにはデータが綺麗さっぱりなくなってしまいます。
これでは、起動するたびにデータベースにデータを入れなければならず、非常に面倒です。
そこで、MySQLコンテナのデータをホスト側にマウントさせることで、データベースのデータを永続化させてみましょう。
まずはプロジェクト用ディレクトリ直下にdb
というディレクトリを作成し、その中にdatadir
というマウ
ント用のディレクトリを作成しましょう。
できたらMySQLコンテナにオプションを追加して起動しなおします。
# docker run --name app-db -v /vagrant/app/db/datadir:/var/lib/mysql -d -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=app mysql:latest
-v
オプションを追加しました。作成したマウント用ディレクトリと、MySQLコンテナ内のデータが格納されている/var/lib/mysql
ディレクトリをマウントさせました。
先程のdocker-composeを使用する場合は以下のように設定します。
db: image: mysql:latest environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: app volumes: - /vagrant/app/db/datadir:/var/lib/mysql web: image: test/php:5.6.23-apache volumes: - /vagrant/app/web/html:/var/www/html links: - db:mysql ports: - 8080:80
db
サービスにvolumes
の設定を追加しました。
これでMySQLコンテナのデータが永続化されているはずです。
おわりに
お疲れ様です。
これでDockerの基礎はある程度つかめたでしょうか。
今回紹介したのは基本的な部分ばかりでしたが、Dockerにはまだまだ様々な機能や使い方があります。
また、先日発表されたDocker Engine 1.12 RC版でもさらなる機能が追加されているようです。
これからますます進化していきそうなDocker、今後の動向に注目です。
参考記事:
Docker 入門ハンズオン資料
- 2016年7月現在RC版が発表されているDocker Engine1.12ではオーケストレーション機能が組み込まれる予定です ↩