Apache、PHP-FPMのオフィシャルDockerイメージを使ってPHPの環境を作る

# What's？

ApacheおよびPHP（PHP-FPM）のオフィシャルDockerイメージを使って、PHPの環境を作ってみようということで。

- [Apache（httpd）](https://hub.docker.com/_/httpd)
- [PHP（php）](https://hub.docker.com/_/php)

# 環境と前提

今回の環境。

Docker。

```shell
$ docker version
Client: Docker Engine - Community
 Version:           20.10.11
 API version:       1.41
 Go version:        go1.16.9
 Git commit:        dea9396
 Built:             Thu Nov 18 00:37:06 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.11
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.9
  Git commit:       847da18
  Built:            Thu Nov 18 00:35:15 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

ApacheとPHPのコンテナは、Docker Composeで立てることにします。

```shell
$ docker-compose version
Docker Compose version v2.1.1
```

利用するDockerイメージですが、それぞれ以下とします。

- `httpd:2.4.51`
- `php:7.4.26-fpm`

単にPHPを動かしたいだけなら、`php:<version>-apache`を使えば済むようにも見えますが、今回はFPMを使うということでやってみます。

ApacheとPHP-FPM、2つのコンテナをDocker Composeで起動するという構成を目指します。

# Apacheの設定ファイルを作成する

できる限り、オフィシャルのイメージそのままでいきたいのですが、Apacheに関しては設定ファイルを作成する必要があります。

こんなファイルを作成。

```apache:apache2/conf/extra/proxy-php.conf
LoadModule proxy_module modules/mod_proxy.so
LoadModule authnz_fcgi_module modules/mod_authnz_fcgi.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

AddType text/html .php
DirectoryIndex index.php

SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1

<LocationMatch \.(php|phar)$>
    ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "/var/www/html%{reqenv:REQUEST_URI}"
    SetHandler "proxy:fcgi://php-fpm:9000"
</LocationMatch>
```

`mod_proxy`、`mod_proxy_fcgi`は少なくともロードしておく必要があります。

ApacheからPHP-FPMへの転送は、別コンテナなのでTCPで行うようにしました。

```apache
<LocationMatch \.(php|phar)$>
    ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "/var/www/html%{reqenv:SCRIPT_NAME}"
    SetHandler "proxy:fcgi://php-fpm:9000"
</LocationMatch>
```

UNIXドメインソケットで構成してもよかったのですが、PHP-FPM側がTCPポート9000でリッスンしている状態で構成されているようだったので、こちらに合わせました。

`php-fpm`というのは、転送先のコンテナ名です。

また、以下の部分もポイントで


```apache
    ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "/var/www/html%{reqenv:SCRIPT_NAME}"
```

これがないと、以下のエラーにずっと悩まされることになります。

```shell
[proxy_fcgi:error] [pid 10:tid 140642638350080] [client 172.17.0.1:35954] AH01071: Got error 'Primary script unknown'
```

これは、ApacheのオフィシャルイメージとPHP-FPMのオフィシャルイメージでドキュメントルートに差異があり、これを合わせるためのものです。

- Apache - `/usr/local/apache2/htdocs`
- PHP-FPM - `/var/www/html`

# Docker Composeを設定を作成する

PHP-FPM側には特に設定は要らないので、あとは`docker-compose.yml`を作成します。

最終的にできあがった`docker-compose.yml`は、こちら。

```yaml:docker-compose.yml
version: "3.9"
services:
  apache:
    image: httpd:2.4.51
    ports:
      - "80:80"
    depends_on:
      - php-fpm
    volumes:
      - ./apache2/conf/extra/proxy-php.conf:/usr/local/apache2/conf/extra/proxy-php.conf
    networks:
      php_network:
        aliases:
          - apache
    entrypoint: >-
      bash -c "
        grep 'Include conf/extra/proxy-php.conf' /usr/local/apache2/conf/httpd.conf || echo 'Include conf/extra/proxy-php.conf' >> /usr/local/apache2/conf/httpd.conf
        httpd-foreground
      "
  php-fpm:
    image: php:7.4.26-fpm
    volumes:
      - ./app:/var/www/html
    networks:
      php_network:
        aliases:
          - php-fpm
networks:
  php_network:
    driver: bridge
```

Apache側のポイントですが

```yaml
  apache:
    image: httpd:2.4.51
    ports:
      - "80:80"
    depends_on:
      - php-fpm
    volumes:
      - ./apache2/conf/extra/proxy-php.conf:/usr/local/apache2/conf/extra/proxy-php.conf
    networks:
      php_network:
        aliases:
          - apache
    entrypoint: >-
      bash -c "
        grep 'Include conf/extra/proxy-php.conf' /usr/local/apache2/conf/httpd.conf || echo 'Include conf/extra/proxy-php.conf' >> /usr/local/apache2/conf/httpd.conf
        httpd-foreground
      "
```

先ほど作成した設定ファイルをvolumeとしてマウントしていることと

```yaml
    volumes:
      - ./apache2/conf/extra/proxy-php.conf:/usr/local/apache2/conf/extra/proxy-php.conf
```

起動時に設定ファイルの`Include`を追加していることですね。

```yaml
    entrypoint: >-
      bash -c "
        grep 'Include conf/extra/proxy-php.conf' /usr/local/apache2/conf/httpd.conf || echo 'Include conf/extra/proxy-php.conf' >> /usr/local/apache2/conf/httpd.conf
        httpd-foreground
      "
```

`/usr/local/apache2/conf/extra`にファイルを置いておけば読んでくれるかと思ったのですが、そうはいきませんでした…。

PHP-FPM側は、ソースコードを`/var/www/html`にvolumeとしてマウントします。

```yaml
  php-fpm:
    image: php:7.4.26-fpm
    volumes:
      - ./app:/var/www/html
    networks:
      php_network:
        aliases:
          - php-fpm
```

# 動作確認する

あとは、動作確認用のPHPファイルを作成して

```php:app/index.php
<?php
phpinfo();
```

起動。

```shell
$ docker-compose up
```

`http://localhost/index.php`にアクセスすると、PHPが動作していることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309669/33950a8e-66e4-b4a6-289a-4310322f8633.png)