追記 (2018.12.30)
PHP5.6, PHP7.1 に加えて、PHP7.2, PHP7.3 にも対応しました! また、PHP から memcached につなぐサンプルを追加しました。
はじめに
こんにちは。小西です。開発環境の構築って面倒ですよねー。
今回、PHP, MySQL, PHP-FPM, nginx, memcached のローカル開発環境を、Docker を使ってコマンド一発で作られるようにしたところ、あまりに簡単で驚いたので、その方法をご紹介します。
ソースコードをgithubにおいておきます ので、すぐに起動できます!
開発環境構築のめんどくささ
僕はPHP+MySQL+nginx+PHP-FPMの環境をよく使うのですが、こういった構成をそれぞれのマシンで再現するのって結構面倒なんですよね。1プロジェクトならまだいいですが、大体プロジェクトによってそれぞれのバージョンや設定ファイルのパラメータなどが微妙に違うので、phpenvでの切り替えなきゃ・・・なんて話になってきて、なかなか面倒です。
やはりプロジェクトごとに環境作成手順を共有したほうが効率がよさそうです。
なぜ Docker ?
Docker を使うと、
- プロジェクトごとに開発環境を作ることができる
- イメージを共有するわけではなく、設計図だけの共有なのでリポジトリが重くならない
- Vagrant などと違い、ビルドのたびに VM が起動したりしないので軽い
Docker Toolbox ではなく Docker for Mac (Docker for Windows)を使う理由は、
- GUI でインストールが完結するので、導入が簡単
- Docker 社も今後はこちらを進めていく方針っぽい
2つの違いは以前こちらに書きました:これからは Docker Toolbox よりも Docker for Mac を使おう - koni blog
Docker、Docker Compose とは
今回作るものの概念をすごくざっくり説明すると、
- 動かしたいプロセスごとに
Docker イメージ
を作りますDocker イメージ
は、Dockerfile
というファイルをもとにビルドします- プロジェクトメンバーには、
イメージ
ではなく、Dockerfile
を共有します(Dockerfile
はテキストファイルなので軽い) - 基本的には、1イメージにつき1つの
デーモン
が動くように作ります- 今回の場合は、
nginx
,php-fpm
,mysqld
がデーモンになります
- 今回の場合は、
- イメージはゼロから作ることもできるし、他のイメージを元にして作ることもできます
- Docker Hub に
Docker イメージ
が多数共有されているので、それを活用することも可能
イメージ
からコンテナ
を起動しますコンテナ
は1つのプロセスで、1つのデーモンが動いています。このデーモン
が止まると、コンテナ
は終了します
docker-compose.yaml
という yaml のファイルを書いて、docker-compose
コマンドを実行することで、複数のコンテナ
をまとめて起動し、協調して動けるようにします
という感じです。
Docker 初めてだとよくわからないかもしれませんが、設定ファイル見て触って覚えたほうが早いですので、よくわからなくても次進みましょう。
ディレクトリ構成
以下のような構成にします。
├── application (ここにプロジェクトファイルが入る想定) │ └── initial.sql (データベースの初期のSQLが入っているファイル) ├── docker │ ├── docker-compose.yml (docker-compose の設定ファイル) │ ├── misc │ │ └── data (MySQLのデータフォルダが入るディレクトリ) │ ├── nginx │ │ ├── Dockerfile │ │ ├── fastcgi.conf │ │ ├── localhost.crt │ │ ├── localhost.csr │ │ ├── localhost.key │ │ ├── nginx.conf │ │ └── server.conf │ └── php-fpm │ ├── Dockerfile │ └── php.ini └── public (nginx のドキュメントルート) └── index.php (テスト用の PHP ファイル)
さっそく動かしてみよう
今回の記事で作成するファイルを github においておきますので、こちらをベースに説明していきます。
Docker for Mac / Docker for Windows のインストール
まずは 以下のページから Docker for Mac (または Docker for Windows )をインストールします。安定版とベータ版がありますが、安定版(stable)を選んでおきましょう。
Get started with Docker for Mac | Docker Documentation
指示に従ってインストールします。
これで、docker
コマンド、docker-compose
コマンドが使えるようになりました。
Mac の場合、右上で状況が確認できるようになります。クジラのアイコンがキュートですね。
ソースコードの展開
続いて、今回作成するファイルが置かれたリポジトリから、ファイルをダウンロードします。
$ git clone git@github.com:koni/docker-php-nginx-mysql-memcached.git
ビルド&起動
続いてビルド&起動します。初回は多少時間がかかりますが、2回目からは数秒で起動します。
$ cd docker-php-nginx-mysql-memcached/docker $ docker-compose up
無事に成功すると、ログが出始めます。
ここで MAMP や Apache, nginx など、TCP80, 443, 13306 ポートをLIESTENしているプロセスがいると、以下のようなエラーがでます。
他のプロセスを終了してから再度 docker-compose up
を実行しましょう。
ERROR: for nginx Cannot start service nginx: driver failed programming external connectivity on endpoint nginx (9ad3d10cef4673ee7a48fbd6c7c59fbe6f3043eeac8eb0e27797da03cfbfa60b): Bind for 0.0.0.0:443 failed: port is already allocated ERROR: Encountered errors while bringing up the project.
動作確認
ログが止まったら、ブラウザで http://localhost/ にアクセスします。 以下のような表示が出たら成功です。
nginx のドキュメントルートにあるテスト用の PHPファイル(public/index.php
)が実行されます。このファイルでは MySQL につないで、データを INSERT し、SELECT するので、リロードするたびに数字が増えていくはずです。
今回の動作の概念図
各ファイルの説明
では、各ファイルについて見ていきましょう。
docker-compose.yaml
このファイルがあるディレクトリで、docker-compose up
コマンドを実行すると、このファイルを使ってコンテナの起動を試みます。対応するイメージがまだない場合は、自動的にイメージが生成されます。必要なファイルをダウンロードしてイメージを作成するので、初回は時間がかかりますが、一度イメージが作られるとその作業はないので1,2秒で起動できるようになります。
docker-compose.yaml
には、起動したいコンテナと、それぞれが協調して動くための設定を記述します。
docker-compose の書き方
version
は '2' で固定です。
services
以下がそれぞれのコンテナの設定です。
ports
を使うと、コンテナ内のポートとローカルのポートにバインドできます。コロン(:)の左がローカルのポート、右がコンテナ内のポートです。MySQL コンテナでは、ローカルから直接コンテナの MySQL にアクセスできると便利なので、コンテナの 3306 をローカルの 13306 にバインドします。3306 でなく 13306 にしているのは、既に MySQL がローカルで動いているとバッティングしてしまうためです。この数字はなんでもかまいません。
volumes
と使うと、コンテナ内のディレクトリをローカルのディレクトリにバインドできます。コロン(:)の左がローカルのディレクトリ、右がコンテナのディレクトリです。
container_name
でコンテナに名前をつけることができます。これは必須ではないですが、docker-compose exec
でコンテナ内に入る時に、打つ文字数が減るので便利です。ただし、同じコンテナ名で複数のコンテナが起動できないので、1つに固定しておくと、同時に1つしか同じコンテナを起動できなくなります。
links
を使うと、その名前で他のコンテナにアクセスできるようになります。
これを使えばコンテナのIPを知らなくても他のコンテナにアクセスできます。
例えば、web
にlinks: mysql
とあるので、nginx コンテナ内からは、mysql
というホスト名でmysqlにアクセスできるようになります。この場合、アプリ側のDBの設定ファイルのホストの欄には mysql
と書けば接続できるようになります。こんな感じです。この例では`docker-compose.yaml から環境変数でホスト名を渡しています。
memcached
services: memcached: image: memcached:1.4
これは、memcached:1.4
イメージで起動しろ、という設定です。ローカルにイメージがない場合は、Docker Hub から自動的に取得されます。今回は、ここから取得されます。
mysql
data: image: busybox volumes: - ./misc/data:/var/lib/mysql mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: mysql_database MYSQL_USER: mysql_user MYSQL_PASSWORD: mysql_pw ports: - "13306:3306" volumes_from: - data volumes: - ../application/initial.sql:/docker-entrypoint-initdb.d/initial.sql container_name: mysql
こちらもイメージは Docker Hubのオフィシャルリポジトリから取得されます。
このイメージでは、MYSQL_ROOT_PASSWORD
, MYSQL_DATABASE
, MYSQL_USER
, MYSQL_PASSWORD
という環境変数を設定しておくと、自動的にROOTパスワードを設定し、ユーザーとデータベースを作成してくれる機能があるので、環境変数を設定しておきます。ちなみに、この処理はデータベースがないときだけ行われます。一回起動するとこの処理は行われません。misc/data
以下を消すと、再度ユーザーとデータベースが作成されます。
またこのイメージには、/docker-entrypoint-initdb.d/*.sql
というファイルを置いておくと、起動時にそれを実行してくれる機能があるので、volumes
を使って、そこにアプリケーションのDBスキーマをおいておきます。今回はテストなので、hoges
というテーブルを作成するCREATE TABLE
文だけおいておきます。実際にはここでアプリケーションのDBスキーマが書かれたファイルのパスを指定します。
コンテナは終了するとデータが消えてしまいます。コンテナを終了するたびデータが消えると開発上不便なので、busybox
を使ってMySQLのデータはコンテナ外においておくことにします。
ローカルの./misc/data
を、コンテナ内の /var/lib/mysql
にバインドします。これで、misc/data
にMySQLのデータがある状態になるので、コンテナを終了してもデータは消えません。データを消したいときは、misc/data
以下を削除すればOKです。
git で管理する場合は、このディレクトリは .gitignore
しておくことをおすすめします。
nginx
nginx: build: ./nginx ports: - "80:80" - "443:443" volumes: - ../:/var/www/html links: - web container_name: nginx
今度は image
ではなく、 build
があります。
イメージ内のnginxの設定ファイルに変更を加えたいので、別途 Dockerfile
を使ってイメージをビルドするようにします。
nginx/Dockerfile
このファイルで nginx イメージをビルドします。
FROM nginx ADD nginx.conf /etc/nginx/nginx.conf ADD fastcgi.conf /etc/nginx/fastcgi.conf ADD server.conf /etc/nginx/conf.d/default.conf ADD localhost.key /etc/ssl/private/localhost.key ADD localhost.crt /etc/ssl/certs/localhost.crt
FROM nginx
とあるので、Docker Hub のリポジトリからベースのイメージがダウンロードされます。
それから、設定ファイルをいくつかを変更します。
今回は nginxの設定ファイルである、nginx.conf
、ポートと処理の対応の設定であるserver.conf
、nginxとPHP-FPMとの連携の設定である fastcgi.conf
を変更します。
また、おまけとして、自己署名したSSL証明書もおいておきます。記事最後の手順を実行すると、https://localhostでもアクセスできるようになります。
web (PHP-FPM)
web: build: ./php-fpm volumes: - ../:/var/www/html links: - mysql environment: DATABASE_HOST: 'mysql' DATABASE_NAME: 'mysql_database' DATABASE_USER: 'mysql_user' DATABASE_PASSWORD: 'mysql_pw' container_name: web
volumes
で、プロジェクトルートと /var/www/html
をバインドしておきます。これで、コンテナ起動中にソースコードを修正して、すぐに確認することができます。
environment
を利用して、アプリの実行設定を環境変数で渡すことができます。今回はDBへの接続の設定を渡しました。
よく使うコマンド
イメージのビルド
$ docker-compose build
起動
$ docker-compose up
終了
$ docker-compose stop
起動中のコンテナを見る
$ docker ps
コンテナに入る
ログファイルや、設定ファイルの位置の確認などで、コンテナに入るときは以下を使います。
$ docker exec -it mysql bash root@a0d91988b7db:/# # コンテナに入れる
mysql
の部分には、コンテナ名 (docker-compose.yaml
の container_name
)か、コンテナID (docker ps
コマンドの出力の CONTAINER ID
)を入れます。
その他使い方メモ
コンテナのMySQLに接続したい
MySQL クライアントで以下のように設定すると、コンテナの MySQL に入れます。もちろん mysql コンテナが起動していないと入れません。
Host: 127.0.0.1:13306 User: mysql_user PW: mysql_pw Database: mysql_database
みんな大好きSequelProならこんな感じですね。
SSL で入れるようにしたい
今回、こちらで生成したSSL自己署名証明書が入っているので、この証明書を信頼する設定にすれば、SSLで接続できます。
この証明書は、自己署名なのでそのままだとブラウザでエラーが出てしまいます。 これを回避するために、localost.crt をキーチェーンアクセスの「システム」として証明書を登録します。
$ open docker/nginx/localhost.crt
上のコマンドを実行するとキーチェーンアクセスが起動するので、「システム」を選択し、証明書を追加する。 「キーチェーンアクセス」を起動し、「システム」を選択、今追加した証明書(名前が「localhost」になっている)をダブルクリックし、「この証明書を使用するとき」欄を「常に信頼」にし、画面を閉じる。画面を閉じるときに保存されるので、必ず画面を閉じる。
SSL 自己署名証明書の作成手順
リポジトリに含まれている証明書は、以下の手順で作成したものです。
$ openssl genrsa 2048 > localhost.key $ openssl req -new -key localhost.key > localhost.csr $ openssl x509 -req -days 3650 -signkey localhost.key < localhost.csr > localhost.crt
まとめ
というわけで、これで開発環境をコマンド一発で起動できるようになりました。
これを git などでメンバーに共有して、メンバーには「Docker for Mac 入れて、docker-compose up
実行して」と伝えるだけで開発環境の構築が済むようになりました。
<宣伝> 株式会社AutoScaleでは、この環境を使って、Twitterフォロワー管理・運用支援ツール Cheetah というサービスを開発しています! Twitter アカウントをお持ちの方は、ぜひお試しください!
参考
DevOps導入指南 Infrastructure as Codeでチーム開発・サービス運用を効率化する
この本は、2016年10月発売と新しいので、Docker for Mac についても書いてあり、開発の運用についてかなり具体的に書いてありおすすめです。
Compose ファイル・リファレンス — Docker-docs-ja 17.06.Beta ドキュメント
追記 (2017.9.28)
「モダン」と行っておきながら PHP 5.6 だったので、PHP 7.1 に対応させました。
PHP5.6 に対応させたい場合は、docker-compose.yml
を build: ./php-fpm56
にしてください。