Laravel9 + php8 JIT + nginx unit をDockerで構成する

# メモ
* Laravel9でアプリケーションを作成する際に、事前にどのインフラ構成がレスポンスが速いのかを検証するうえで候補にしたインフラ構成
* php-fpmを利用する例は記事が多くあるが、unitの構成はphp-fpmに比べると情報量が少ない、且つ導入時に時間も少しかかったので備忘録としてメモを残す。

# ミドルウェア構成
* Debian 11
* PHP 8.1
* Nginx 1.2 + Nginx Unit
※RDBMS、Redisなどのミドルウェアは省いている

# 参考
[NginxUnitとは](https://www.nginx.co.jp/products/nginx-unit/)

# ディレクトリ構成
```
./
├── docker-compose.yml
├── infra
│   ├── nginx
│   │   ├── Dockerfile
│   │   └── default.conf
│   └── php
│       ├── Dockerfile
│       ├── config.json
│       └── php.ini
└── src
```

# docker-compose.yml
``` yml
version: "3.9"
services:
  app:
    build: ./infra/php
    ports:
      - 8081:8081
    volumes:
      - ./src:/data
  web:
    build: ./infra/nginx
    volumes:
      - ./src:/data
      - ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf
    working_dir: /data
```

# Nginx
* Dockerfile
``` Dockerfile
FROM nginx:1.20-alpine
```
* default.conf
``` conf
server {
    listen 80;
    server_name example.com;
    root /data/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    charset utf-8;

    location / {
        root /data/public;
        index index.php;
    }

    error_page 404 /index.php;

    location ~ \.php$ {
        proxy_pass http://xxxx; # proxy_passにはdocker-compose ps した時のNameを指定した
        proxy_set_header Host $host;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
```

# PHP
* Dockerfile
``` Dockerfile
FROM nginx/unit:1.27.0-php8.1

ENV COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install --no-install-recommends git unzip libzip-dev libicu-dev libonig-dev && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  docker-php-ext-install intl pdo_mysql zip bcmath

COPY ./php.ini /usr/local/etc/php/php.ini
COPY ./config.json /tmp/config.json

WORKDIR /data
```
* php.ini
``` ini
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8

[Date]
date.timezone = Asia/Tokyo

[mysqlnd]
mysqlnd.collect_memory_statistics = on

[Assertion]
zend.assertions = 1

[mbstring]
mbstring.language = Japanese

[opcache]
zend_extension = opcache
opcache.enable = 1
opcache.enable_cli = 1
opcache.jit = tracing
opcache.jit_buffer_size = 128M

```
* config.json
``` json
{
    "listeners": {
        "*:8081": {
            "pass": "routes"
        }
    },

    "routes": [
        {
            "match": {
                "uri": "!/index.php"
            },
            "action": {
                "share": "/data/public$uri",
                "fallback": {
                    "pass": "applications/laravel"
                }
            }
        }
    ],

    "applications": {
        "laravel": {
            "type": "php",
            "processes": 20,
            "root": "/data/public/",
            "script": "index.php"
        }
    }
}
```
# 手順
* 用意したDocker環境を立ち上げる
``` shell
$ docker compose build --no-cache
$ docker compose up -d
```
* laravelをインストール
``` shell
$ docker compose exec app bash
$ composer create-project laravel/laravel .
$ curl http://localhost:8081
```
curlでリクエストしてunit内でLaravelが立ち上がっていることを確認

* Http経由でNginxを通しunit内のアプリケーションにアクセスできるようにする
``` shell
# 引き続きappのコンテナ内で実行
$ cd /tmp
$ curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config/
```
ブラウザから
http://localhost:8081/
へアクセスするとLaravelのWelcomeページが表示できる