Dockerで作るNginx + PHP7 + Xdebug環境


PHP での開発や PHP の使い方を練習する時、ローカルにすぐ立ち上がる開発環境があると便利です。PHP といえば Wordpress!開発環境は XAMPP!と良く紹介されてきましたが、2019 年の開発環境としては docker をオススメします。

今回は Nginx + PHP7-fpm のローカル環境を docker で作成しました。もちろん、データベースも追加できます。

docker を使うメリット

docker は仮想環境の立ち上がりが高速で、多くの仮想環境を一度に立ち上げておくことも容易です。dockerfile や docker-compose.yaml といった設定ファイルを元に環境が構築されるため、一度設定を作れば同じ環境を何度でも何個でも作成できます。開発環境を壊すことを恐れなくて良いのは大きなメリットです。

実際のサーバーだと怖くて中々できないことが docker なら簡単に試すことができます。また、設定ファイルがテキストファイルなので git 管理しやすく、チーム開発時に環境を共有しやすいです。仮に自身で docker の設定が書けなくても、 誰かが用意した設定ファイルがあれば開発環境をすぐにローカルに作れてしまいます。便利!

ローカルかつ作り直しが容易な環境は精神的に良く開発の幅を広げます。例えば使ったことがないが興味はある機能を入れてみる、壊すくらいの気持ちで linux コマンドを試す、問題のある SQL を発行してみて経過をみてみるなど実験がしやすいです。繰り返しですがとても便利で、一度使うと手放せません。

docker の設定ファイル

※docker の使用には無料の会員登録が必要ですので、使用する場合は公式サイトから登録してください。

この記事で作成する PHP 環境の全ファイルと使い方は下記のレポジトリを参考にしてください。

php-nginx-docker

上記をローカルにコピーした後、docker-compose.yaml がある階層で下記のコマンドを打てば PHP 環境が立ち上がります。

docker-compose up -d

初期設定だと localhost の 3900(docker-compose.yaml の 3900 を別のポート番号にすればその番号に変更できます)でアクセスが可能です。Xdebug のポートは 9012 です。初回は立ち上がりには少し時間がかかります。terminal にたくさんの文字ば流れますが正常な動作です。

ここからは作り方の解説になります。

コンテナ

docker で立ち上げ る仮想環境は docker コンテナと呼ばれます。この後で出てくる、コンテナというキーワードは全て docker コンテナのことです。

Apache と Nginx

PHP で作成したアプリケーションにブラウザでアクセスするには、Web サーバー(Nginx か Apache が一般的)と PHP ファイルを動かせる環境が必要です。今回は Nginx + PHP-FPM の構成にしていますが dockerhub 公式の php image の場合、Apache を使う方が PHP 環境を作るのは簡単です。Apache だと module として PHP が組み込まれているので、1 つの dockerimage で WEB サーバーと PHP の両方が動くからです。

Nginx だと、Web サーバーと PHP-FPM で 2 つの docker コンテナを立ち上げることになります。Nginx+PHP 構成はつまづく所も多いので、今は docker 自体の勉強よりとにかく PHP の開発環境!という場合は Apache も試すことをお勧めします。この記事では紹介しませんが。

PHP の image か、OS の image か

docker の PHP 環境の出発点としては大きく2通りの選択があります。

  • CentOS など公式 OS イメージから作成し、yum や apt-get で PHP を入れる
  • すでに PHP が入った公式の php image からスタートする

開発後のアプリケーションを置くサーバーと docker 開発環境を極力揃えたい時は CentOS など OS image からスタートします。例えば、CentOS7 で xx や oo が入っていて PHP やデータベースは…など通常のサーバー構築時と同じような手順で作成していきます。レガシーな OS だと docker 公式が image を提供していないケースも多く、構築難度は高くなります。docker ではなく vagrant や実際のサーバーで用意した方が簡単かもしれません。

開発後のアプリケーションを docker コンテナで公開する、環境の再現よりも開発効率や練習用として使いたい場合は後者をお勧めします。今回はこちらの方法で PHP 環境を作ります。dockerhub で公開されている PHP image は下記から確認できます。

php dockerhub

alpine linux

docker では alpine linux という軽量な linux を良く使います。例えば、php の公式イメージで比較すると

  • php:7.3.6-apache : 378MB
  • php:7.3-fpm-alpine3.8: 78MB

300MB ほどの差がありました。サイズが小さい方がディスク容量を圧迫しないのはもちろん、docker image のダウンロードや 各種 docker registory への登録時間が短縮できます。OS に縛りがないなら、とりあえず alpine-linux で初めて問題が出たら他を検討するのが良いと思います。

dockerfile の作成

docker image を選んだら dockerfile を作ります。dockerfile は docker image をスタート地点として、必要なパッケージや設定を追加することができます。

FROM php:7.3-fpm-alpine3.8
RUN apk --update add curl
RUN set -ex \
  && apk --no-cache add \
  postgresql-dev php-pgsql autoconf build-base
RUN pecl install xdebug
RUN docker-php-ext-install pdo_pgsql pgsql
RUN docker-php-ext-enable xdebug
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer

apk は alpine linux のパッケージ管理コマンドです。CentOS でいう yum、ubuntu の apt-get です。alpine-linux に PHP7.3 があり、postgres や xdebug,composer 用の module を追加しています。docker 固有のインストールもあり、こういう設定は概ね image の提供元のドキュメントに使い方が書いてあります。

dockerfile での RUN は実際に linux 上でコマンドを打つのと同じです。docker コンテナを image で立ち上げた後そのコンテナに入り、コンテナ内でコマンドを打つのと同じ結果になります。毎回それらコマンドを打つのは大変なので、dockerfile にコマンドを書いて次回の立ち上げ時にはコマンドを打たなくて済むようにする工程です。

コンテナに入るには exec コマンドを使います。後述する doker-compose でコンテナを立ち上げるなら

docker-compose up -d
docker-compose exec php /bin/ash
/var/www/html # コンテナ内

このようになります。今は深入りせずに進み、一度 PHP 環境を立ち上げた後、必要ならここに戻って自身の設定に変更するのがお勧めです。dockerfile は最初から完璧にする必要はなく、実際に使いながら設定を追加するものです。

docker-compose のメリット

dockerfile を使い、docker run すれば docker コンテナが起動できます。ただ、docker run はコマンドが長くなりがちで、環境ごとにコマンドを覚えておくのはとても面倒です。docker には設定ファイルを元にして docker コンテナを起動してくれる docker-compose という機能があります。その設定ファイルが docker-compose.yaml です。

今回は web サーバーに Nginx のコンテナ、データベースに postgres のコンテナを後々追加します。docker-compose.yaml なら複数のコンテナを連動して立ち上げることも可能です。docker run だとコンテナ毎に色々と必要な引数を指定してコンテナを立ち上げる必要がありますが docker-compose なら

docker-compose up -d

この短いコマンドで設定ファイル通りに立ち上がります。-d はバックグラウンドで起動させるコマンドです。docker-compose は複数のコンテナの使用で良く紹介されますが、1 つのコンテナだけでも十分に便利です、docker-compose.yaml はとりあえず作っておきましょう。後々他のコンテナを追加したくなった時にも対応しやすいです。

docker-compose.yaml の作成

まず PHP だけのパターンです。web サーバーがないのでブラウザからはつながりません。docker-compose.yaml と同じ階層に php フォルダを作成してください。このフォルダ内で開発を行います。フォルダ名が気に入らない場合は別名に変えても大丈夫ですが他の設定の方も連動して変えてください。

version: "3"
services:
  php:
    build: .
    volumes:
      - ./php:/var/www/html
      - ./php.ini:/usr/local/etc/php/php.ini

version は 1.x ~ 3.x の間で docker の必要バージョン次第で決めます。3 で問題ありませんが、実装されたばかりの機能などバージョンを細かく指定する必要がある時は下記のドキュメントを参考にします。

Compose file version 3 reference

次に service の下に起動するコンテナの情報を書きます。php というのは docker-compose で立ち上げる時のコンテナ名、自由です。build は使用する dockerfile のパスを書くところです。.は docker-compose.yaml と同じ階層にある dockerfile を使うという意味です。他の場所にあるならそのパスを指定します。

dockerfile ではなく docker image をそのまま使う時は build ではなく image を使います。

services:
  php:
    image: php:7.3-fpm-alpine3.8
    #省略...

volumes では docker コンテナ内と自分の作業フォルダを同期しています。こうすることで、docker コンテナを再起動しなくても自動的にファイルが更新されます。:の左側がローカルのパス、右側がコンテナ内のパスです。

docker-compose で立ち上げてもすぐにコンテナが止まってしまうなら tty: true を指定するとコンテナは起動し続けます。

Nginx

docker コンテナを立ち上げた際、ブラウザからのアクセスは Nginx コンテナが受け、そのアクセスを上記の PHP コンテナに渡す想定です。

Nginx の設定ファイル、site.conf を次のように設定。

server {
    listen       80;
    index index.php index.html;
    server_name  localhost;
    root /var/www/html;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

docker-compose.yaml で php とコンテナに名前をつけました。そのコンテナの IP は php と指定することで docker が解決してくれます。

 fastcgi_pass php:9000;

ここの php は docker コンテナ名です。Nginx を docker-compose.yaml に追加して docker-compose up で Nginx と PHP のコンテナ両方が立ち上がるようにします。docker-compose.yaml と同じ階層に nginx フォルダを作り、上記の site.conf ファイルを入れます。

version: "3"
services:
  nginx:
    image: nginx:1.15.7-alpine
    ports:
      - 4200:80
    volumes:
      - ./nginx/site.conf:/etc/nginx/conf.d/default.conf
      - ./php:/var/www/html
    links:
      - php
  php:
    build: .
    volumes:
      - ./php:/var/www/html
      - ./php.ini:/usr/local/etc/php/php.ini

これで Nginx と php のコンテナが連携して起動します。また、Nginx の ports に 4200 を指定しているので docker-compose で立ち上げた後は localhost:4200 で繋がります。空いているポートなら自由です、80 を指定すれば概ね port なしの localhost で繋がりますが、80 は空いていないことも多いです。

ここまでの設定できたら

docker-compose up -d

このコマンドで立ち上がります。php フォルダ内に index.php を作り、http://localhost:4200でアクセスできれば成功です。

コンテナを停止する時は

docker-compose down

で docker-compose.yaml で設定したコンテナ全てが止まります。また、dockerfile 更新した場合は

docker-compose down
docker-compose build --no-cache
docker-compose up -d

キャッシュなしでビルドしてからコンテナを立ち上げることができます。

Xdebug の設定

ブラウザからアクセス出来るようになったら次は Xdebug です。php.ini に xdebug 用の設定を追加します。

[xdebug]
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_host = "host.docker.internal"
xdebug.remote_port=9012
xdebug.remote_log = /var/log/xdebug.log

xdebug の remote_host は docker 用の設定を使います。remote_port は IP で衝突しないように 9000 以外にしています。エディタの Xdebug 設定と合わせる数値です。docker-compose の php の volume で xdebug のログファイルを同期しておきます。また、新たに log フォルダを作成。

#省略
volumes:
  - ./php:/var/www/html
  - ./php.ini:/usr/local/etc/php/php.ini
  - ./log:/var/log

Vscode の設定

Vscode で Xdebug を使うための設定です。Vscode のデバッグ->構成を開く、で launch.json に追加します。すでに type php がある時は新たに設定を作ってもそちらの設定が優先されることがあるので注意が必要です。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for XDebug",
      "type": "php",
      "request": "launch",
      "port": 9012,
      "pathMappings": {
        "/var/www/html": "${workspaceFolder}/php"
      }
    }
  ]

port に php.ini で指定した 9012 を指定して。pathMappings に php ファイルがある場所を選択します。workspaceFolder/php が自身のプロジェクトの PHP ファイルがある場所です。/var/www/html は docker コンテナ内のパスです。

workspaceFolderは Vscode の変数なのでそのまま書いてください。Xdebug がうまく動かない時は、Vscode 側の設定に問題があるケースが多いです。上でも書きましたが、既に launch.json に type: “php”の設定があれば注意です。

Xdebug を使う

index.php の適当なところに breakpoint を設定し、Vscode のデバッガを ON にします。再度ブラウザから localhost:4200 にアクセスし、上手く動いてれば breakpoint で止まるはずです。

Xdebug が動かない時は下記を試します。

  • index.php で phpinfo して xdebug の設定を確認する(xdebug.remote_port が 9000 ではなく 9012 になっているかなど)
  • log フォルダに xdebug.log があるかを確認する
  • log 内に breakpoint の情報があるかを確認する

データベースの追加

追記するか別記事に。設定は github の REAEME に書いています

Todo

php-nginx-docker

  • database 連携方法を追記
  • 初期設定もう少しする?(composer や phpunit など)