docker-compose で apache2+PHP 開発環境 (silex, CLI, mysql, redis)
docker-compose でなるべく楽をして PHP 開発環境を作成する流れについて、一応使えそうになったのでまとめます。
ローカルに PHP を入れたりせず、Docker さえあればアプリケーションの CLI や composer を使って開発ができることを目的にします。
想定する PHP アプリ
- Silex
- MySQL、Redis
- apache2 + PHP7.1
作業環境
Windows, Mac どちらでも Docker が動けば問題ないはずです。いちおう下記で基本動作を確認。
開発環境
ディレクトリ構成
基本的には Silex Skeleton に従います。
├── bin │ └── console ├── composer.json ├── composer.lock ├── config ├── docker │ └── app │ ├── app.conf │ ├── Dockerfile │ ├── mpm_prefork.conf │ └── php.ini ├── docker-compose.yaml ├── etc │ ├── mysql.php │ └── redis.php ├── README.md ├── src ├── templates ├── tests ├── var └── web
アプリのルートに docker-compose.yaml を、docker/app 以下にアプリケーションコンテナ関連のファイルを置いています。
composer install
composer install します。composer 本体はDocker公式レポジトリのイメージ をバイナリのように使えます。
アプリのルートで以下のコマンドを実行します。
$ docker run --rm --interactive --tty --volume ${PWD}:/app composer install
無事 vendor ディレクトリが生成されるはずです。
msys2 など cygwin 互換環境の場合は --volume `cygpath $PWD -w`:/app とするとパスが解決できるかと思います。(以下 volume オプション使用時同様)
主題とは関係ないですが、日本で使う場合には composer.json にミラーの設定をしておくと速度が上がって多少苦しみが緩和されると思います。
...
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.jp"
}
},
...
アプリケーションコンテナ
apache2 + PHP が動くコンテナをビルドします。 手軽さを優先し、Docker公式レポジトリのPHPイメージ を使います。
docker/app/Dockerfile を書きます。今回は MySQL, Redis を使いたいので extension を追加します。
docker/app/Dockerfile
FROM php:7.1-apache RUN docker-php-ext-install -j$(nproc) mbstring opcache pdo pdo_mysql RUN apt-get update && apt-get install -y zlib1g-dev \ && pecl install redis-3.1.3 \ && docker-php-ext-enable redis \ && apt-get autoremove --purge -qq \ && apt-get clean \ && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* COPY php.ini /usr/local/etc/php/ COPY app.conf /etc/apache2/sites-available/app.conf RUN a2dissite '*default*' \ && a2ensite app \ && service apache2 restart
公式イメージのページにも十分な説明があるとは言えないのですが、docker-php-ext-install だけですんなり入るものとそうでないものがあります。phpredis はビルドの必要がありました。他にも、例えば CURL は docker-php-ext-install の候補に含まれているものの、事前に libcurl3-dev が必要だったりするので、目的の物によっては GitHub issues を探す必要はあるかもしれません。
ここでは、MySQL、Redis 関連の extension 追加の他に、php.ini と apache の設定をイメージに追加しています。
apache2 には後述の docker-compose で調整するマウントポイントが DocumentRoot になるように設定しておきます。この辺はお好みです。
docker/app/app.conf
<VirtualHost *:80>
DocumentRoot /app/web
<Directory /app/web>
Options all
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
php.ini には、開発用に
docker/app/php.ini
log_errors = On error_log = /dev/stderr
を記述しておくことで、PHP ログを docker 標準のログとしてとり扱うことができます。
MySQL、Redis コンテナ
今回は公式のイメージそのままで使います。docker-compose.yaml の記述のみですむので専用の Dockerfile は用意しません。
docker-compose で環境立ち上げ
docker-compose でアプリケーション、MySQL、Redis を立ち上げます。
docker-compose.yaml
version: '3'
services:
app:
build:
context: ./docker/app
volumes:
- .:/app
working_dir: /app
ports:
- "3000:80"
environment:
- SYMFONY_ENV=dev
- DB_MASTER_HOST=mysql
- DB_MASTER_USER=app
- DB_MASTER_PASSWORD=app
- DB_REPLICA_HOST=mysql
- DB_REPLICA_USER=app
- DB_REPLICA_PASSWORD=app
depends_on:
- mysql
- redis
mysql:
image: mysql:5.7
command: "mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
volumes:
- "mysql-myapp-data:/var/lib/mysql"
environment:
- "MYSQL_DATABASE=myapp_dev"
- "MYSQL_ROOT_PASSWORD=root"
- "MYSQL_USER=app"
- "MYSQL_PASSWORD=app"
ports:
- "3306:3306"
redis:
image: redis
volumes:
mysql-myapp-data:
app はアプリケーションを動かす apache2+PHP コンテナです。docker/app 以下をビルドし、カレント(アプリのルート)ディレクトリを /app にマウントしています。また、後々コマンドラインが使いやすいように working_dir を設定しています。環境変数では主に MySQL 関連の情報を渡しています。
以前の compose file v1 の頃からの差分になりますが、v3 では link を書かなくてもデフォルトで他のコンテナを名前解決できるようになっています。depends_on は起動順番を指定するものですが、MySQL コンテナは起動後準備できるまでにラグがあり、これを書いたからといって app 起動前に他の環境が全て ready というわけでもなくちょっと微妙です。
mysql では永続化ボリュームを設定しています。
ここまでアプリの動作環境がそろいました。
$ docker-compose up
で初回のアプリケーションコンテナのビルド、MySQL、Redis コンテナの Pull が行われるはずです。
立ち上がったら http://localhost:3000 でアプリケーションにアクセスできます。
CLI を使う
Silex Skeleton では bin/console で CLI が使えるようになっています。他のフレームワークでも Rails の bin/ のように CLI ツールを置くことがあるでしょう。
今回のようにローカルに実行言語を入れない場合でも、app コンテナを使って CLI を叩くことができます。
$ docker-compose exec app bin/console -h Usage: list [options] [--] [<namespace>] Arguments: namespace The namespace name Options: ...
今回はお試し CLI として、doctrine/migrations で DB のマイグレーションをやってみます。doctorin 自体の使い方はこちらの Silexだってdoctorin/migrations使う を参考にさせていただきました。
├── bin │ └── console ├── composer.json ├── composer.lock ├── config ├── db │ ├── migrations │ ├── migrations.yml │ └── migrations-db.php ... ├── src │ ├── console.php
db/ 以下に関連ファイルを置きました。
db/migrations.yml
name: Doctrine Migrations migrations_namespace: MyApp\Db\Migrations table_name: doctrine_migration_versions migrations_directory: ./db/migrations
db/migrations-db.php
<?php
return [
'driver' => 'pdo_mysql',
'host' => getenv('DB_MASTER_HOST'),
'dbname' => 'myapp_' . getenv('SYMFONY_ENV'),
'user' => getenv('DB_MASTER_USER'),
'password' => getenv('DB_MASTER_PASSWORD'),
'charset' => 'utf8mb4',
];
参考記事のように、console.php に migration コマンドを登録します。
use Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\LatestCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand;
$console = new Application('My Silex Application', 'n/a');
...
/** Doctrine\DBAL\Migrations */
$console->add(new DiffCommand());
$console->add(new ExecuteCommand());
$console->add(new GenerateCommand());
$console->add(new LatestCommand());
$console->add(new MigrateCommand());
$console->add(new StatusCommand());
$console->add(new VersionCommand());
準備できたら、migration 定義を生成、
$ docker-compose exec app bin/console migrations:generate --configuration db/migrations.yml --db-configuration db/migrations-db.php
db/migrations 以下に生成されたファイルを編集し、migration 実行します。
$ docker-compose exec app bin/console migrations:migrate --configuration db/migrations.yml --db-configuration db/migrations-db.php
array(5) {
["host"]=>
string(5) "mysql"
["dbname"]=>
string(9) "myapp_dev"
["user"]=>
string(3) "app"
["password"]=>
string(3) "app"
["charset"]=>
string(7) "utf8mb4"
}
Loading configuration from command option: db/migrations.yml
Doctrine Migrations
WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)y
Migrating up to 2017xxxxxxxxxx from 0
++ migrating 2017xxxxxxxxxx
-> CREATE TABLE example (col1 LONGTEXT NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB
++ migrated (0.33s)
------------------------
++ finished in 0.33s
++ 1 migrations executed
++ 1 sql queries
(migration の中身は適当です) 無事コマンド実行できました。
まとめ
docker-compose でローカル環境の影響を最小限に開発環境が整いました。
引き続き、lint ツールの統合やデプロイについてもまとめる予定です。