dockerでPHP7.3+Laravel環境を15分で作る~2019年版

昨年の最後にLaravelのプロジェクトの雛形を作る必要があったので、メモとして書きました。
この記事では、dockerコマンドだけで、以下のことができるようになります。

1. Laravelプロジェクト雛形の作成
2. PHP v7.3 コンテナの作成 - xdebug, opcacheも含む
3. nginx v1.15 コンテナの作成
4. ローカルマシンで動かす

プロジェクトは、githubに公開しています。
https://github.com/tomoyamachi/docker-laravel-fpm-7.3

なお、この記事は、以下の記事の知識をベースに進んでいきます。
[本番運用に備えたPHP7.3.0のコンテナを作る](https://qiita.com/tomoyamachi/items/ff5ac4a96063a12409cd)

## プロジェクト作成

composerコンテナ経由でカレントディレクトリにLaravelのプロジェクトを作成します。

```bash
$ docker run --rm --interactive --tty -v $(pwd):/app \
  composer bash -c \
  "composer create-project --prefer-dist laravel/laravel ."
```

カレントでカレントディレクトリを確認

```bash
$ docker-laravel-php-fpm-7.3.0# ls
app            composer.json  database       public         routes         tests
artisan        composer.lock  package.json   readme.md      server.php     vendor
bootstrap      config         phpunit.xml    resources      storage        webpack.mix.js
```

OK.

## PHP-FPMコンテナを作成

PHP-FPMコンテナのDockerfile/.dockerignoreファイルを作成します。

PHPのDockerfileの作成については、長くなるので別の記事にまとめました。
[こちら](https://qiita.com/tomoyamachi/items/ff5ac4a96063a12409cd)をご参照ください。

**2019/01/26 追記**
@hokutoasari さんの[記事](https://qiita.com/hokutoasari/items/4db320e519d87186974f)を参考に `echo 'opcache.optimization_level=0x7FFFBBFF'` を追加しました。

```docker
# Dockerfile
FROM php:7.3.0-fpm-alpine3.8

RUN apk upgrade --update && apk --no-cache add \
    # iconv, intl
    icu-dev \
    # xdebug
    autoconf make g++ gcc

RUN docker-php-ext-install  -j$(nproc) iconv intl mbstring pdo_mysql opcache && \
  pecl install xdebug-2.7.0beta1 && \
  docker-php-ext-enable xdebug

RUN { \
  echo 'upload_max_filesize = 100M'; \
  echo 'post_max_size = 108M'; \
  echo 'short_open_tag = On'; \
  echo 'fastcgi.logging = 1'; \
  echo 'opcache.enable=1'; \
  echo 'opcache.optimization_level=0x7FFFBBFF'; \
  echo 'opcache.revalidate_freq=0'; \
  echo 'opcache.validate_timestamps=1'; \
  echo 'opcache.memory_consumption=128'; \
  echo 'opcache.interned_strings_buffer=8'; \
  echo 'opcache.max_accelerated_files=4000'; \
  echo 'opcache.revalidate_freq=60'; \
  echo 'opcache.fast_shutdown=1'; \
  echo 'xdebug.remote_enable=1'; \
} > /usr/local/etc/php/conf.d/overrides.ini

COPY . /var/www/html
RUN chown -R www-data:www-data /var/www
```

そして `docker build` 。とりあえずlaravelという名前で起動できるようにしてコンテナ起動。

```
$ docker build -t laravel . 
$ docker run laravel

[30-Dec-2018 17:24:55] NOTICE: fpm is running, pid 1
[30-Dec-2018 17:24:55] NOTICE: ready to handle connections
```

動いてそうというところまで来ました。

## NGINXコンテナを作成

ちゃんと動いていることを確認しましょう。
nginxのコンテナを作成します。


```dockerfile
FROM nginx:1.15.6-alpine

RUN { \
echo 'server {';\
    echo 'listen 80;';\
    echo 'root /var/www/html/public;'; \
    echo 'index index.php index.html;'; \
    echo 'error_log  /var/log/nginx/error.log;'; \
    echo 'access_log /var/log/nginx/access.log;';\
    echo 'location ~ \.php$ {';\
        echo 'try_files $uri =404;';\
        echo 'fastcgi_split_path_info ^(.+\.php)(/.+)$;';\
        echo 'fastcgi_pass laravel:9000;';\
        echo 'fastcgi_index index.php;';\
        echo 'include fastcgi_params;';\
        echo 'fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;';\
        echo 'fastcgi_param PATH_INFO $fastcgi_path_info;';\
    echo '}';\
    echo 'location / {';\
        echo 'try_files $uri $uri/ /index.php?$query_string;';\
        echo 'gzip_static on;';\
    echo '}';\
echo '}';\
} > /etc/nginx/conf.d/default.conf

COPY public /var/www/html/public
```

上記のDockerfileをビルドしていきます。
`Dockerfile.nginx` などと名付けて、 `docker build -f Dockerfile.prod -t laravel-nginx .` でビルドしましょう。

やっていることは、phpへのアクセスが来たら `laravel:9000` にプロキシするよ、という感じになってます。
次は、laravelという名前でphp-fpmコンテナにつなげるようにしましょう。

## nginx経由でphp-fpmに接続

nginxからphp-fpmコンテナに接続したいので、以下の条件を満たしていきます。

1. 同じネットワーク上にコンテナを配置する
2. nginxからphp-fpmコンテナに `laravel` というhost名でアクセスできるようにする

では、やっていきます。

### 1. 同じネットワーク上にコンテナを配置する

dockerは通常のプロセスと切り離されて存在し、各自にhosts情報を持ちます。
コンテナ同士で通信するためには、同じネットワーク上にコンテナを作成する必要があります。

この際、他のコンテナに影響を与えないように、新たなネットワークを作成します。
名前は `docker network ls` をしてかぶってないものを選びましょう。
今回は laravel-test として作成します。

```shell
$ docker network create laravel-test
```

### 2. nginxからphp-fpmコンテナに `laravel` というhost名でアクセスできるようにする
docker runするときにnameを指定します。
さきほど作成したlaravelというコンテナを、先程つくったlaravel-testというネットワーク上に、 laravelという名前で配置します。

```shell
$ docker run --network laravel-test --name laravel laravel
```

そして、別のターミナルで、同じネットワーク上に先程つくったnginxコンテナを設置します。その際、コンテナの80ポートをマシンの80ポートにexposeします。

```
docker run --network laravel-test -p 80:80 laravel-nginx
```

## ブラウザでチェック

![laravel.png](https://qiita-image-store.s3.amazonaws.com/0/14275/e5bb7af4-cfb1-513f-cf24-b226e4edb17e.png)

見えました!!

なお、上記のコマンドだと、ターミナルにログが表示されているはずです。

 
PHP-FPM側

```
172.25.0.3 -  30/Dec/2018:17:51:57 +0000 "GET /index.php" 200
172.25.0.3 -  30/Dec/2018:17:51:59 +0000 "GET /index.php" 200
```

nginx側

```
172.25.0.1 - - [30/Dec/2018:17:51:57 +0000] "GET / HTTP/1.1" 200 2333 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
172.25.0.1 - - [30/Dec/2018:17:51:59 +0000] "GET / HTTP/1.1" 200 2333 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
```

これは、docker run時に `-d` オプションを渡すと、バックグラウンドで動いてくれます。
あるいは一旦プロセスを停止させて、 `docker container start` 経由でコンテナを再始動させてもバックグラウンドで動きます。

```
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                NAMES
39a0c410b1cc        laravel-nginx       "nginx -g 'daemon of…"   10 minutes ago      Exited (0) 6 seconds ago                        boring_rosalind
2b46c41b09db        laravel             "docker-php-entrypoi…"   22 minutes ago      Exited (0) 4 minutes ago                         laravel

$ docker container start 2b46c41b09db
2b46c41b09db

$ docker container start 39a0c410b1cc
39a0c410b1cc
```

# まとめ

ちゃんと手順をまとめたりすると結構時間がかかりましたが、コンテナだけでプロジェクトを作成するだけなら15分くらいでできます。