1コマンドで作った。Dockerで開発環境を構築する方法 | SiTest (サイテスト) ブログ

メニューボタン閉じるボタン

1コマンドで作った。Dockerで開発環境を構築する方法

最終的にたった1コマンドで開発環境を作り上げてしまう事をゴールにプロジェクトを作成していきます。

前回の記事ではVagrantとDockerの違いに関して開設しましたが、実際にLAMPサーバーを1コマンドで立ち上げて行きたいと思います。

そもそもDockerは何を置き換えるものなのか

Vagrantは仮想マシン自体を管理します。
細かいアプリやシステム構成はAnsibleで整えて、全く同じマシンを用意することが目的でした。

対するDockerはアプリケーションの仮想化という概念で公開されています。
なので、置き換える対象はあくまでApacheMySQL等のアプリケーションやその依存ライブラリになります。
上がVagrant+Ansibleで、下がDockerを含めた構成図です。

1_vagrant-ansible

2_vagrant-docker

ちょっと待って、内容が増えただけで全然楽になってないじゃん!
そうなのです。
ローカルの開発環境に着目した場合、使うツールが増えた分環境を構築するコストは増えます。

しかし、無駄に複雑になったわけではありません。
Dockerは本番環境へのデプロイを見越して採用します。

スクリーンショット 2017-02-13 11.41.54

思わぬ良い効果もあります。
今まではシステム構築する上でのアプリや依存ライブラリは全てAnsibleで用意する必要がありましたが、全てDockerに逃がせます。
AnsibleDockerのインストールに集中するだけで良くなったのです。

もうプロジェクトの度にVagrantfileやPlaybookを切り貼りして似たようなファイルを大量に生成することはありません。

VagrantもAnsibleもDockerに置き換わりました

Vagrantに関する記事の執筆当初はVagrant+Ansible+Dockerを組み合わせて最強の開発環境を作る事をゴールにしていました。
既にDocker for WindowsDocker for Macが正式版になって既に半年が経過しています。

3_docker-for-mac

もうVagrant+Ansibleに残っているメリットはクラウドマシンの操作と複数環境を立ち上げるくらいしか残っていません。
特殊な用途でVagrantを活用するケース以外、Dockerのみで開発環境を構築していくスタイルが主流になることでしょう。

よって、次の章からはDocker for WindowsDocker for Macをベースにすすめていきます。

LAMP環境を立ち上げてみる

まずはDocker for Windows、もしくはDocker for Macを公式サイトからダウンロードしてインストールしましょう。
インストールが完了したら、ターミナルエミュレータを開き、dockerコマンドが利用出来ることを確認します。
確認にはdocker versionコマンドが利用できます。

$ docker version
Client:
 Version:      1.13.1
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   092cba3
 Built:        Wed Feb  8 08:47:51 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      1.13.1
 API version:  1.26 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   092cba3
 Built:        Wed Feb  8 08:47:51 2017
 OS/Arch:      linux/amd64
 Experimental: true

// Docker for xxxが立ち上がっていない場合
$ docker version
Client:
 Version:      1.13.1
 API version:  1.26
 Go version:   go1.7.5
 Git commit:   092cba3
 Built:        Wed Feb  8 08:47:51 2017
 OS/Arch:      darwin/amd64
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

それではHelloWorldを出力するPHPファイルを生成し、
Dockerで作ったWebサーバーで確認するという事をやってみましょう。

$ cat << EOF > index.php
<?php
echo "Hello", "World";
EOF

$ docker run -d -p "80:80" -v "$PWD:/var/www/html" php:apache
8e7374e208e40776fa8b14742e054a7074ee6d0e815af15590aaff968f1a6c6f

$ curl localhost
HelloWorld

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
8e7374e208e4        php:apache          "docker-php-entryp..."   31 seconds ago      Up 30 seconds       0.0.0.0:80->80/tcp   condescending_goldwasser

このdocker runコマンドが少々長くてややこしい以外はとても楽ですね。
本記事の目的である1コマンドにぐっと近づいたように思えます。

では、同様に初期状態のMySQLサーバーを立ち上げて接続してみましょう。
(mysqlコマンドのlocalhostは通信を行わずにプロセスを直接叩きに行きますので。接続できなければ127.0.0.1や0.0.0.0等を試してみてください。)

$ docker run -d -p "3306:3306" -e MYSQL_ROOT_PASSWORD=test mysql:latest
e5b3f76eb6f393185010326c9c324063abe4d61b3633d5555b097a77f3569118

$ mysql -h 127.0.0.1 -uroot -ptest -e "show databases;"
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
f6ecc6d0c10c        mysql:latest        "docker-entrypoint..."   24 minutes ago      Up 24 minutes       0.0.0.0:3306->3306/tcp   clever_euler
8e7374e208e4        php:apache          "docker-php-entryp..."   41 minutes ago      Up 41 minutes       0.0.0.0:80->80/tcp       condescending_goldwasser

こちらも1コマンドでMySQLサーバーが立ち上がりました。

これらのサーバーは何者?Docker Hubとの関係

少し脇道に逸れますが、コンテナの中身に迫っていきます。
1コマンドで動作するこれらのサーバーは一体何者なのでしょうか?

以降の流れはドットインストールさんの動画が参考になるので、イメージの湧かない箇所が出てきましたら、都度参考にしてみてください。
Docker入門 – ドットインストール

4_dockerfile

DockerはイメージというOSの雛形と、イメージを実体化したコンテナの2つで構成されています。
実体化したコンテナは、何時でもdocker commitコマンドを使ってスナップショットを取るかのようにイメージに戻す事が出来ます。
例えばシステムを構築するために必要な依存モジュールをインストールしたコンテナをdocker commitで再度イメージにして配布し、本番環境や同僚が利用できます。

DockerDocker Hubというホスティングサービスもセットです。
ユーザーはDocker Hub上に無料で公開リポジトリを作成し、新たにチューニングを施したイメージを預ける事ができます。

しかし、手作業でソフトをインストールしてから「docker commit」でイメージを作っていてはブラックボックスに入ったままという問題が残ります。
これの対応策として、Dockerfileが用意されています。
Dockerfileを指定してdocker buildコマンド発行すると、プロビジョニングツールで構築するかの如く新しいイメージを生成することが出来ます。

何者か分からないイメージを使うのは怖い、見分け方は?

Vagrantでは誰が作ったか分からない怪しいボックスが雑多に配置され、怖くて使えないというユーザーが続出しました。
(その後公式イメージをユーザーが見分けやすいように整備が進んでいきました)

Docker Hubでは、ユーザーがアカウントを開設してリポジトリを作成した場合、「gladcube/php-apahce」という風に「ユーザー名/イメージ名」という形式で登録されます。
公式のイメージはユーザー部分がありません。
「php」や「mysql」等のようにリポジトリ名に/がありませんので、ユーザーは公式のイメージを簡単にみつけられます。

PHPコンテナはこうして作られた

それではDocker HubにあるPHPのリポジトリを確認してみましょう。
php – Docker Hub

画面上の方に大きくOFFICIAL REPOSITORYと書いてありますね。
これが公式リポジトリの証です。

そして「Full Description」の項目には「Supported tags and respective Dockerfile links」と記載があり。
詳細説明の上部で、サポートしているタグとそれぞれのDockerfileを紹介している事が読み取れます。

各種リポジトリはイメージをどのような流れで作ったかを公開する目的で、利用したDockerfileGitHubのリポジトリを使って公開することが慣習となっています。
(ただし、これはあくまで自己宣告ですので公式以外のイメージを利用する際は注意してください)

Dockerfileの流れを追ってみよう

では、早速PHP(with Apache)のDockerfileの流れを追っていきましょう。
php/7.1/apache/Dockerfile – GitHub

プロジェクトの大型Dockerfileは慣れるまで読みづらいので、
私の方で抜粋しました。

# L.7: Debianイメージを利用する
FROM debian:jessie

# L.19: 開発用モジュールをインストール
RUN apt-get update
RUN apt-get install -y autoconf file g++ gcc libc-dev make pkg-config re2c ca-certificates curl libedit2 libsqlite3-0 libxml2 xz-utils --no-install-recommends

# L.33: Apacheモジュールをインストール
RUN apt-get install -y apache2-bin apache2.2-common --no-install-recommends

# L.140: PHPのソースコードをコンテナ内に注入
COPY docker-php-source /usr/local/bin/

# L.158: PHPのソースコードをビルド
RUN ./configure
RUN make install

# L.192: コンテナ起動時、Apacheを起動して80番ポートを開ける
EXPOSE 80
CMD ["apache2-foreground"]

普段我々がPHPをインストールする時はパッケージ管理を利用するケースが多いのですが、
沢山のバージョンを作る為にあえてソースコードからコンパイルしているのですね。
他にもApacheにPHPを組み込むあたりの記述もありますので、興味があれば読んでみてください。

中でも注目は7行目のFROM debian:jessieです。
これはどのイメージを利用するかが記載されており、DockerHubのリポジトリ名:タグで表現されています。
DebianのjessieというコードネームはWikipediaの記事によると、「Debian 8.0(コードネーム: jessie)」と表記されており、
バージョン8.xの最新版である事が伺えます。

debian – Docker Hub
debianのリポジトリにも「8.7, 8, jessie, latest (jessie/Dockerfile)」と書いてあり、
jessieタグは8系の最新バージョンと同列であることがわかります。

FROM scratch
ADD rootfs.tar.xz /
CMD ["/bin/bash"]

因みにdebian:jessieのDockerfileはscratchというイメージを元に作られています。
これは何もファイルが入ってないブランクのイメージを指し、ルートディレクトリにOSの構成ファイルを丸ごと格納する作りになっているようです。

PHPイメージを調整してLAMP仕様に変更

さて、晴れてMySQLのコンテナとWebサーバのコンテナを立ち上げる事が出来ました。
早速PDOモジュールを利用してMySQLに接続していきましょう。

$ cat << EOF > initialize.php
<?php
$user = 'root';
$pass = 'test';
$dbh = new PDO('mysql:host=localhost;', $user, $pass);
$dbh->query("create database hogehoge");
$dbh->query("use hogehoge");
$dbh->query("create table users(id int, name text)");
EOF

$ curl localhost/initialize.php
<br />
<b>Fatal error</b>:  Uncaught PDOException: could not find driver in /var/www/html/initialize.php:4
Stack trace:
#0 /var/www/html/initialize.php(4): PDO-&gt;__construct('mysql:host=loca...', 'root', 'test')
#1 {main}
  thrown in <b>/var/www/html/initialize.php</b> on line <b>4</b><br />

残念、エラーが帰ってきました。
公式のPHPイメージは最小限のモジュールだけ組み込まれています。
PHPリポジトリの詳細説明」には「PHP Core Extensions」という項目が用意されており、iconvやmcrypt等のパッケージを盛り込む設定例が記載されています。
また、下記のような記事でも紹介されており、丁度DockerでLAMP環境を構築する為に設定が書かれていますので設定内容をお借りしました。
Docker Hubのオフィシャルイメージを使ったLAMP環境(Apache+PHP+MySQL)構築 – Qiita

$ cat << EOF > Dockerfile
FROM php:apache
RUN docker-php-ext-install pdo_mysql mysqli mbstring
EOF

$ docker build -t myphp .

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myphp               latest              3efc7867dc9f        2 minutes ago      391 MB
php                 apache              a05317fca519        6 days ago          389 MB
mysql               latest              7666f75adb6b        4 weeks ago         406 MB

$ docker run -d -p "80:80" -v "$PWD/public:/var/www/html" myphp
5a8971798f1adc01fda296c711b86b152a1af7f53e52766999601a4c20a34dc0

$ curl localhost/initialize.php
<br />
<b>Fatal error</b>:  Uncaught PDOException: SQLSTATE[HY000] [2002] No such file or directory in /var/www/html/initialize.php:4
Stack trace:
#0 /var/www/html/initialize.php(4): PDO-&gt;__construct('mysql:host=loca...', 'root', 'test')
#1 {main}
  thrown in <b>/var/www/html/initialize.php</b> on line <b>4</b><br />

まだエラーが出ます。
これはPHPの乗っているApacheサーバーからMySQLのサーバーが見えていない事が原因です。
次の章ではそれぞれのサーバーを紐付ける設定を紹介していきます。

MySQLサーバーとPHPサーバーを接続する

立ち上げたコンテナ同士は異なる仮想マシンという扱いになり、
各々の仮想マシンには異なるIPアドレスが割り振られます。
(IPアドレスの確認コマンドはDockerのネットワークの基礎 – SOTAを参考にさせて頂きました)

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
e5b3f76eb6f3        mysql:latest        "docker-entrypoint..."   2 days ago          Up 2 days           0.0.0.0:3306->3306/tcp   ecstatic_boyd
5a8971798f1a        myphp               "docker-php-entryp..."   2 days ago          Up 2 days           0.0.0.0:80->80/tcp       condescending_goldwasser

$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' e5b3f76eb6f3
172.17.0.3

$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' 5a8971798f1a
172.17.0.2

docker runにはlinkというオプションが存在します。
linkオプションを指定して立ち上げると、その対象のマシンのIPアドレスが/etc/hostsファイルに書き込まれます。
こうして他のコンテナにリンク名をすることで接続できます。
早速接続してみましょう。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
e5b3f76eb6f3        mysql:latest        "docker-entrypoint..."   2 days ago          Up 2 days           0.0.0.0:3306->3306/tcp   ecstatic_boyd
5a8971798f1a        myphp               "docker-php-entryp..."   2 days ago          Up 2 days           0.0.0.0:80->80/tcp       condescending_goldwasser

// 一度phpコンテナは削除
$ docker rm -f 5a8971798f1a
5a8971798f1a

$ docker run -d -p "80:80" -v "$PWD/public:/var/www/html" --link ecstatic_boyd:mysql myphp
b79bd2724e7f186fd9a57e5757bdf9285787b8bdf6ee673ade0fec98cee9df4c

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
b79bd2724e7f        myphp               "docker-php-entryp..."   19 minutes ago      Up 19 minutes       0.0.0.0:80->80/tcp       wizardly_goldwasser
e5b3f76eb6f3        mysql:latest        "docker-entrypoint..."   3 days ago          Up 3 days           0.0.0.0:3306->3306/tcp   ecstatic_boyd

$ docker exec b79bd2724e7f cat /etc/hosts | grep mysql
172.17.0.3    mysql e5b3f76eb6f3 ecstatic_boyd

$ cat << EOF > initialize.php
<?php
$user = 'root';
$pass = 'test';
$dbh = new PDO('mysql:host=mysql;', $user, $pass);
$dbh->query("create database hogehoge");
$dbh->query("use hogehoge");
$dbh->query("create table users(id int, name text)");

$result = $dbh->query("show create table users;");
var_dump($result);
EOF

$ curl localhost/initialize.php
object(PDOStatement)#2 (1) {
  ["queryString"]=>
  string(24) "show create table users;"
}

おお、遂にPHPとMySQLが繋がりましたね!
概念は複雑ですが、慣れてしまえば殆どコードもコマンドも最小限にサーバーの定義が書けてしまいます。

Docker Composeを活用して1コマンド運用を実現する

1コマンドで簡単にサーバーが立ち上がるとはいえrunコマンド長すぎですね。
Shell Scriptにでもまとめて……というのはちょっと待って下さい。
公式がDocker Composeというオーケストレーションツールを用意してくださっています。

そのインストール方法も公開されています。

// パスの通っているフォルダにダウンロードします。
$ curl -L "https://github.com/docker/compose/releases/download/1.11.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

// 実行権限を付与
$ chmod +x /usr/local/bin/docker-compose

$ docker-compose --version
docker-compose version 1.11.1, build 7c5d5e4

Docker ComposeDockerコマンドをラッピングしているだけですので、
Docker for xxxからでも、DockerをインストールしたLinuxマシンでも同様のインストール方法で導入できます。

Docker Composeコマンドはカレントディレクトリにdocker-compose.ymlファイルが存在すると使用できます。

// 前章の2つのコンテナは削除
$ docker rm -f b79bd2724e7f e5b3f76eb6f3
b79bd2724e7f
e5b3f76eb6f3

$ cat << EOF > docker-compose.yml
version: "2"
services:
  web:
    build: .
    container_name: web
    image: myphp
    ports:
      - "80:80"
    links:
      - mysql
    volumes:
      - .:/var/www/html
    restart: always
  mysql:
    container_name: mysql
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: test
    ports:
      - "3306:3306"
    restart: always
EOF

$ docker-compose up -d
Creating mysql
Creating web

$ docker-compose ps
Name               Command               State           Ports
-----------------------------------------------------------------------
mysql   docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp
web     docker-php-entrypoint apac ...   Up      0.0.0.0:80->80/tcp

docker-compose.ymlの書き方は最初はとっつきにくいですが、
docker runコマンドのオプションの定義が殆ど使えますので簡単に記述できます。

コマンドをシンプルにまとめる

docker-composeコマンドに慣れてしまえば上記で1コマンド達成と言っても良いかもしれません。
しかし、プロジェクトにはDockerに詳しくないメンバーが居る可能性もありますので、
頻出のコマンドをテキストファイルで残す事にしました。

$ mkdir bin

$ cat << EOF > bin/start
#!/bin/bash
script_dir="$(cd "$(dirname "${BASH_SOURCE:-${(%):-%N}}")"; pwd)"
cd "$script_dir/../"

docker-compose up -d
EOF

$ chmod +x bin/*

$ bin/start
Creating mysql
Creating web

これで新しく参入してきたメンバーにも簡単に1コマンドでサーバーを起動することが出来るようになります。
新しくジョインしたメンバーには、bin配下のシェルスクリプト読むように伝えればすぐに理解出来るかと思います。

今回はaddコマンドのみ作成しましたが、
幾つかの頻出コマンドをまとめてプロジェクトとして公開しました。
(またプロジェクトとして機能し易いようにディレクトリ分けも行いました)

簡単なコードばかりですので是非一度読んでみて下さい。

まとめ

いかがでしたでしょうか?

ついに目標であった1コマンドで環境構築を実現することが出来ました。
もちろんこれで全てが解決した訳ではありません。
デプロイするフローを考えたり、プロジェクトやソースコードの管理していくには様々な技術との組み合わせが必要になっていくことと思います。

その上で、開発環境構築という1分野に於いて少しでも参考になれば幸いです。
もし良ければこの方法にあなたのアイデアを盛り込んでいただき、
さらにクールな開発環境を構築してみてください。

以上、「1コマンドで作った。Dockerで開発環境を構築する方法」でした。
最後まで読んでいただきありがとうございました。

今すぐお気軽に
ご相談ください。

0120-315-465

(平日 10:00~19:00)

今すぐお気軽に
ご相談ください。

0120-315-465

(平日 10:00~19:00)

グラッドキューブは
「ISMS認証」を取得しています。

認証範囲:
インターネットマーケティング支援事業、インターネットASPサービスの提供、コンテンツメディア事業

「ISMS認証」とは、財団法人・日本情報処理開発協会が定めた企業の情報情報セキュリティマネジメントシステムの評価制度です。

いますぐ無料で
お試しください。

SiTest の革新的な機能を
お試しいただけます。
利用規約

お名前【必須】
メールアドレス【必須】
電話番号【必須】