Nginxとphp-fpmを用いてLaravalを表示する

どうも、若松です。

前回はLaravelをDockerで起動し、イメージを軽量化するところまで行いました。
https://qiita.com/t_wkm2/items/245288e42083ac7e4057

今回は、 `php artisan` でのサーバ起動ではなく、Nginx+php-fpmでLaravelを表示するところまでを行います。

# 設定
## ディレクトリ構造
```
docker/
├─ docker-compose.yml
├─ nginx/
|  ├─ Dockerfile
|  └─ default.conf
└─ laravel/
   └─  Dockerfile
```

## docker-compose.yml
```yml
version: '2'
services:
  nginx:
    image: nginx
    ports:
      - "80:80"
  laravel:
    image: laravel
```

## Dockerfile(nginx)
```dockerfile
FROM nginx:1.17-alpine

# ローカルから設定ファイルをコピー
COPY  default.conf /etc/nginx/conf.d/default.conf
```
## default.conf(nginx)
```nginx
server {
    listen       80;
    server_name  localhost;
   
    location / {
        # ドキュメントルート設定
        root   /var/www/laravel/public;
    
        fastcgi_split_path_info  ^(.+\.(?:php|phar))(/.*)$;   
        fastcgi_intercept_errors on;

        fastcgi_index  index.php;
        include        fastcgi_params;
        # FastCGIの向き先をLaravelコンテナに設定
        fastcgi_pass   laravel:9000;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED $document_root$fastcgi_path_info;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

### Dockerfile(laravel)
```dockerfile
FROM amazonlinux:2 as vender

# PHPインストール
RUN amazon-linux-extras install -y php7.3
RUN yum install -y php-pecl-zip php-mbstring php-dom

# Composerインストール
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

# 環境変数設定
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME "/opt/composer"
ENV PATH "$PATH:/opt/composer/vendor/bin"

# Laravelインストール
RUN composer global require "laravel/installer"

# Laravelプロジェクト作成
WORKDIR /var/www
RUN composer create-project laravel/laravel laravel

FROM php:7.3-fpm-alpine

# ビルド用コンテナから必要なコンテンツをコピー
COPY --from=vender --chown=www-data:www-data /var/www/ /var/www/
```

# 操作
コマンドは全て最上位ディレクトリの `docker` から行う想定です。
## Nginxコンテナビルド
```bash
docker build nginx/. -t nginx --squash
```

## Laravelコンテナビルド
```bash
docker build laravel/. -t laravel --squash
```

## docker-composeで起動
```bash
docker-compose up
```

# ブラウザで表示を確認
http://localhost:8000 にアクセスすることで以下のサンプルを表示します。
<img width="1280" alt="スクリーンショット 2019-07-06 22.57.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94816/b64122b2-c679-f6cc-4269-9595ccafe853.png">


# 解説
## Nginxコンテナ
### ベースイメージ
```dockerfile
FROM nginx:1.17-alpine
```
ベースイメージにはNginx公式リポジトリにある`nginx:1.17-alpine`を使用しました。
2019/7/14現在のNginxの最新が1.17であり、軽量化を目的にAlpineLinux版を使いたかったためです。
80番ポートの開放やNginxの起動についてはベースイメージ内で既に設定されているため、今回のDokcerfileには記述していません。

### default.conf
```dockerfile
COPY  default.conf /etc/nginx/conf.d/default.conf
```
設定はローカルに用意した`default.conf`をイメージにコピーして配置します。
FastCGI設定のほとんどは一般的な設定のため、特徴的なものだけ解説します。

#### root
```nginx
root   /var/www/laravel/public;
```
ドキュメントルートはLaravelコンテナのアプリケーションが配置されているディレクトリを指定します。

#### fastcgi_pass
```nginx
fastcgi_pass   laravel:9000;
```
UnixソケットかTCPを指定できますが、Unixソケットではコンテナを越えられないため、TCPで設定します。
アドレスの指定にはDockerのNamespaceを利用します。

## Laravelコンテナ
前回のDockerfileからの差異のみ解説します。
### ベースイメージ
```dockerfile
FROM php:7.3-fpm-alpine
```
実行用イメージのベースを `php:7.3-alpine` から `php:7.3-fpm-alpine` に変更しました。
これによってデフォルトでphp-fpmがインストールされた状態から設定を行えばよくなります。
9000番ポートの開放やphp-fpmの起動についてはベースイメージ内で既に設定されているため、今回のDokcerfileには記述していません。

### Laravelコンテンツのオーナー変更
```dockerfile
COPY --from=vender --chown=www-data:www-data /var/www/ /var/www/
```
`php:7.3-fpm-alpine`のphp-fpm初期設定では、php-fpmのワーカ起動ユーザは`www-data`になっています。
COPYコマンドで配置したLaravelコンテンツはrootがオーナーになってしまうため、そのままだと権限エラーとなります。
そこで、`--chown`オプションを使用し、オーナーを`www-data`へ変更しています。
`--chown`オプションはBashでいうところの`chown -R`となるため、ディレクトリがあっても再帰的に処理してくれます。

## docker-compose
今回からコンテナが2つになったため、操作簡略のために`docker-compose`を導入しました。
`docker-compose` には起動時のオプション設定や、複数コンテナのビルド、依存関係制御など様々な機能がありますが、ここではコンテナ起動/停止とポートオプションのみ使用しています。
### 複数コンテナのビルドを使用しない理由
本当であれば使用したかったのが本音です。
しかしながら2019/7/14現在、BuildKitやsquashオプションに対応していないため、あえてdockerコマンドでビルドを行っています。

# Tips
## コンテナイメージ内にあるファイルをローカルにコピーする
設定ファイルを作成する際に、デフォルトの設定をローカルにコピーし、それを改変して作成していくことはよくあると思います。
コンテナではSCPが使えないため、代わりに`docker cp`コマンドを使用します。
今回のdefault.confの場合は、以下のようにしてコピーしました。

```bash
docker run -d --name nginx nginx:1.17-alpine
docker cp $(docker ps --filter name=nginx -aq):/etc/nginx/conf.d/default.conf .
```
## コンテナイメージの履歴を確認する
FROMで使用するベースイメージには予めポートの開放やデーモンの起動が設定されている場合があります。
今回でいうところのNginxやphp-fpmですね。
それを確認するには`docker history`コマンドを使用します。
例としてphp-fpmのhistoryを確認してみます。

```bash
docker history --format {{.CreatedBy}} php:7.3-fpm-alpine
/bin/sh -c #(nop)  CMD ["php-fpm"]
/bin/sh -c #(nop)  EXPOSE 9000
/bin/sh -c #(nop)  STOPSIGNAL SIGQUIT
/bin/sh -c set -eux;  cd /usr/local/etc;  if…
/bin/sh -c #(nop) WORKDIR /var/www/html
/bin/sh -c #(nop)  ENTRYPOINT ["docker-php-e…
/bin/sh -c docker-php-ext-enable sodium
...
```
このようにズラズラとコマンドが表示されるかと思います。
これは実行日次のtimestampが新しい順で上から並んでいます。
これを見るとベースイメージの最後に、9000番ポートの開放とphp-fpmの実行が行われているため、今回のDockerfileではポートの開放とデーモンの起動が不要なことがわかります。

# まとめ
Nginx+php-fpmに加えて、docker-composeも導入してみました。
そんなに特殊な設定を行ったわけではありませんが、Dockerfileの書き方やコンテナ特有の設定等はお伝えできたかと思います。