PHP Docker

## 追記
PHP, nginx, mysql, phpmyadminをDockerで構築する記事を新たに公開しました。
そちらでは`healthcheck`を用いて各コンテナの実行順序をコントロールしています。
合わせて確認していただけると幸いです。

https://qiita.com/kandalog/items/af8611479f3936064179


---

## はじめに
備忘録です。
PHPでのDocker構築には選択肢がいろいろありそうだったので一通り全て試してみました。


## PHPをWebサーバーで実行する方法の違い
WebサーバーでPHPを実行する方法は2つある
- モジュールモード PHPをWebサーバーに組み込み同一プロセスで実行
	(dockerimage: php-apatch)

- CGIモード PHPをWebサーバーの外(別プロセス)で実行する
  (PHPとWebサーバーでコンテナを分ける)

## Docker Imageの違い
Docker ImageにはCLIとFPMとphp-module版の3つがある
- Apache: PHPとApacheが含まれておりモジュールモードで実行できる
- CLI: ターミナルなどからPHPスクリプトを直接実行する
- FPM: WebサーバーがPHPエンジンを介してスクリプトを実行する(CGI)
    (FPMはWebサーバーとPHPエンジンの間で橋渡しをしている)



## PHP-CLI
PHPをCLIで利用する最小構成
*Dockerfile*
```Dockerfile
FROM php:8.1.19-cli-buster
WORKDIR /usr/src/myapp
COPY . /usr/src/myapp
```
*docker-compose.yml*
```yml
version: "3.8"
services:
  php:
    build: .
    tty: true
    volumes:
      - ./:/usr/src/myapp
    environment:
      - TZ=Asia/Tokyo
```

## PHP-apache
*PHPとApacheをPHPモジュールモードで利用する*

*構成*
```Docker
html
	css
	- index.php
php
	- .env
	- Dockerfile
	- php.ini

db

docker-compose.yml
```

*Dockerfile*
```Dockerfile
FROM php:8.1.19-apache-bullseye
WORKDIR /usr/src/myapp
COPY ./html /var/www/html
```

*docker-compose.yml*
```yml
version: "3.8"
services:
  php:
    build:
      context: .
      dockerfile: ./php/Dockerfile
    env_file: ./php/.env
    tty: true
    depends_on:
      - db
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./html:/var/www/html
      - /usr/src/myapp/db # DB配下をマウントしない設定
    ports:
      - "8080:80" # apacheは80を使用
    environment:
      - TZ=Asia/Tokyo

	db:
    image: mysql:8.0.31
    platform: linux/amd64
    env_file: ./db/.env
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/conf.d:/etc/mysql/conf.d
    ports:
      - 3306:3306
    # networks:
    #   - backend
    environment:
      TZ: Asia/Tokyo

	phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpMyAdmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=db
    depends_on:
      - db
    ports:
      - 8000:80
    # volumes:
    #   - ./phpmyadmin/sessions:/sessions
```

## PHP-apache (composer)
*Composerを利用する*

*構成*
```
src
	css
	- index.php
php
	- .env
	- Dockerfile
	- php.ini

vendor

composer.json
.gitignore
.dockerignore
docker-compose.yml
```

*Dockerfile*
```Dockerfile
FROM php:8.1.19-apache-bullseye

WORKDIR /usr/src/myapp

COPY ./ /usr/src/myapp

# hash of composer. this value will change so check the official site
# https://getcomposer.org/download/
ARG COMPOSER_HASH=55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae

# Change Apache Document Root
ENV APACHE_DOCUMENT_ROOT='/usr/src/myapp/src/'

# Change Document ROOT
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf

# Install Composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php -r "if (hash_file('sha384', 'composer-setup.php') === '$COMPOSER_HASH') \
     { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
    && php composer-setup.php \
    && php -r "unlink('composer-setup.php');" \
    && mv composer.phar /usr/local/bin/composer

# Composer install
RUN composer install
```
*docker-compose.yml*
DBとPHPMyadminはPHP-Apacheと一緒
```yml
version: "3.8"
services:
  app:
    build:
      context: .
      dockerfile: ./php/Dockerfile
    tty: true
    depends_on:
      - db
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./:/usr/src/myapp
    ports:
      - "8080:80"
    environment:
      - TZ=Asia/Tokyo
```

## PHP nginx
*最近だとnginxを使うことが一番多いらしい*
*Composerを導入する例を用いて実装する*


*構成*
```
src
	css
	- index.php
php
	- .env
	- Dockerfile
	- php.ini
	- default.conf

vendor

db
	- .env
	data
	conf.d
		- my.cnf

composer.json
.gitignore
.dockerignore
docker-compose.yml
```

*.dockerignore*
```
vendor
```

*Dockerfile*
```Dockerfile
FROM php:8.1.19-fpm-bullseye

WORKDIR /usr/src/myapp

COPY ./ /usr/src/myapp

# hash of composer. this value will change so check the official site
# https://getcomposer.org/download/
ARG COMPOSER_HASH=55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae

# Install Composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
    && php -r "if (hash_file('sha384', 'composer-setup.php') === '$COMPOSER_HASH') \
     { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
    && php composer-setup.php \
    && php -r "unlink('composer-setup.php');" \
    && mv composer.phar /usr/local/bin/composer

# Composer install
RUN composer install
```

*docker-compose.yml*
```yml
version: "3.8"
services:
  php:
    build:
      context: .
      dockerfile: ./php/Dockerfile
    env_file: ./php/.env
    tty: true
    depends_on:
      - db
    volumes:
      - ./php/php.ini:/usr/local/etc/php/php.ini
      - ./:/usr/src/myapp
      - /usr/src/myapp/db # DB配下をマウントしない設定
    environment:
      - TZ=Asia/Tokyo
  nginx:
    image: nginx:latest
    ports:
      - 8080:80
    volumes:
      - ./src:/usr/src/myapp/src
      - ./php/default.conf:/etc/nginx/conf.d/default.conf
  db:
    image: mysql:8.0.31
    platform: linux/amd64
    env_file: ./db/.env
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf
    ports:
      - 3306:3306
    environment:
      TZ: Asia/Tokyo

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpMyAdmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=db
    depends_on:
      - db
    ports:
      - 8000:80
    # volumes:
    #   - ./phpmyadmin/sessions:/sessions

```

*/etc/nginx/conf.d/default.conf*
```shell
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    # DocumentRoot
    root   /usr/src/myapp/src;
    # "/"の時に返却するファイルを指定
    index  index.html index.htm index.php;

    location  / {
        # 左から順に評価
        # 存在しなければ最後の指定に従ってindex.phpを返す
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        # php-fpmのservice名:9000
        fastcgi_pass  php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
        include       fastcgi_params;
    }
}
```

## おわりに
一番時間がかかったのはnginxの仕様や設定でした。 
Webサーバーの設定自分でやったことなかったので...
コピペで動いたとは思いますが、内容をきちっと理解するために時間がかかりました。

現在お世話になっている会社でPHPを利用しているので勉強のためDocker環境を自作してみました。
Webサーバーが何なのかフワッとした印象でしたが、今回の勉強を通して理解が深まりました。

## 参考

https://qiita.com/ritukiii/items/624eb475b85e28613a70

https://qiita.com/nemui_/items/f911be7ffa4f29293fd5

https://lazesoftware.com/ja/blog/230224/

https://qiita.com/morrr/items/7c97f0d2e46f7a8ec967

https://www.pandanoir.info/entry/2018/05/18/193000

https://zenn.dev/honda/scraps/4fba8235622704