DockerでApache + Laravel の環境を構築する時にNot Foundで泣いた話

# この記事の対象者
- DockerでApache + Laravelの環境を作りたい**初心者**(←重要)

- Laravelで開発してるけどアプリケーションの仕組みを実はよくわかっていない**初心者**

- DockerもLaravelも長年使っていて未熟者のワシにアドバイスくれる先輩

ソースコードはここに載せてます
https://github.com/maip0902/DockerApachePHP

# 環境
MacOS
Docker 19.03.2

# なんで泣いたのか
Laravel + nginx の構成でしか開発を行ったことがなく、初めてApacheで構築しようとしたら

<img width="469" alt="スクリーンショット 2020-05-23 1.23.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454650/d5c6516d-cb26-a46c-28c6-c43268aec1bd.png">

ドキュメントルート違ってるやーん ><
ということでこれを直します
せっかくなのでどういう仕組みでアプリケーションの画面が表示されるかにも少し触れながら環境構築を行います〜

# ディレクトリ構成
apache-laravel-app という名前のプロジェクトを作成するものとして、最終的に以下のようなディレクトリ構造になります。

```
  ├ apache-laravel-app   // Laravelで作られるファイル
  |
  ├ ── php 
  |     └ php.ini　// なくてもいい  
  |
  ├ Dockerfile
  |
  ├ docker-compose.yml

```

# Dockerfile
今回使うイメージは`php:7.2-apache`です。

```docker:Dockerfile
# ①
FROM php:7.2-apache

# ②ドキュメントルートをデフォルトから修正
ENV APACHE_DOCUMENT_ROOT /var/www/html/apache-laravel-app/public
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

# php.iniを独自で設定するときだけ
COPY ./phhp/php.ini /etc

# ③モジュールのinstall
RUN apt-get update \
  && apt-get install -y zlib1g-dev libpq-dev unzip git vim curl\
  && docker-php-ext-install pdo_mysql zip

# ④Composer入れる
# マルチステージビルドを採用cpm
COPY --from=composer /usr/bin/composer /usr/bin/composer
```

①　公式からイメージを持ってくる

```
FROM php:7.2-apache
```
php:(version)-apacheを使うとapacheにphpのモジュールが入っている状態を持ってこれます。

②　ドキュメントルートの修正

```Docker
ENV APACHE_DOCUMENT_ROOT /var/www/html/apache-laravel-app/public
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
```

今回のポイント。
apacheの設定ファイルは `/etc/apache2` 配下にあるのですが、その中でもドキュメントルートを指定しているファイルがあって、その部分のみ環境変数で置き換えるということをしました。Apche起動後にドキュメントルート書き換えたり、独自の.confを使って

```Docker
RUN rm /etc/apache2/sites-available/000-default.conf
COPY ./000-default.conf etc/apache2/sites-available
```
ともできるんですけど(最初自分もこれ考えましたが)環境変数にぶっこんだ方がスマートですね。
まあ公式に書いてあったんですけどねw ⬇️
https://hub.docker.com/_/php

sedコマンドに関しては以下参照。
[sedでこういう時はどう書く?](https://qiita.com/hirohiro77/items/7fe2f68781c41777e507)

また、なぜドキュメントルートが`/var/www/html/apache-laravel-app/public`なのかという話ですが、これはLaravelのプロジェクトを作るとプロジェクト内のpublic配下にindex.phpが作られるためです。また、Apacheの設定で一番最初に見に行くファイルがindex.phpになっています。
実際にindex.phpと書かれているファイルを検索して見てみます。

```
root@be20b535a937:/var/www/html# grep index.php -rl /etc/apache2
/etc/apache2/mods-available/dir.conf # 利用可能なモジュールのディレクトリに関するファイル
/etc/apache2/conf-available/docker-php.conf # phpが読み込まれた時の処理に関するファイル
```
ということでphpの処理がApacheでどう行われているかを以下のコマンドで見ます。

```
root@be20b535a937:/var/www/html# less /etc/apache2/conf-available/docker-php.conf

<FilesMatch \.php$>
        SetHandler application/x-httpd-php
</FilesMatch>

DirectoryIndex disabled
DirectoryIndex index.php index.html

<Directory ${APACHE_DOCUMENT_ROOT}>
        Options -Indexes
        AllowOverride All
</Directory>
```

DirectryIndexは最初に見に行くファイルで、それにindex.phpが指定されていることがわかりました。Laravelはindex.phpから始まるので、このファイルが置いてある `/var/www/html/プロジェクト名/public`をドキュメントルートに指定する必要があります。

③　モジュールのインストール

```
# ③モジュールのinstall
RUN apt-get update \
  && apt-get install -y zlib1g-dev libpq-dev unzip git vim curl\
  && docker-php-ext-install pdo_mysql zip
```

`RUN apt-get update`はインストール可能なパッケージの**一覧の更新**だけが行われます。upgradeにするとパッケージ自体の更新がされます。
`apt-get install -y`も必要なもののインストールです。-y は何か請求があったとき全部YESと答えるという意味です。
`docker-php-ext-install`の方はphp の拡張機能をインストールしているのですが、このイメージだと`mysqli`とか入れようとするとエラーになってしまったんですよね、、ここの原因はまだ不明です、、、、

④　Composer入れる
Composer入れます。
こちら参考。
[商用環境でも使っている Laravel 用 php-fpm イメージの Dockerfile レシピ](https://qiita.com/mpyw/items/731e710342faf4513776)

```
# ④Composer入れる
# マルチステージビルドを採用
COPY --from=composer /usr/bin/composer /usr/bin/composer
```

# docker-compose.yml 

```docker-compose.yml
version: "3.7"
services:
  app:
    build: .
    container_name: php-apache-app
    ports:
      - 80:80
    volumes:
      - ./apache-laravel-app:/var/www/html

  db:
    image: mysql:5.7
    container_name: php-apache-database
    ports:
      - 3306:3306
    environment:
      - MYSQL_DATABASE=apache_laravel_db
      - MYSQL_ROOT_PASSWORD=root
```

# コンテナ立ち上げる

```
$ docker-compose up -d
```
Dockerfileで指定すると拡張モジュールがうまく入らないものがあるので手動で入れる。

```
$ docker exec -it php-apache-app bash
root@be20b535a937:/var/www/html# docker-php-ext-install mysqli
~ 処理が走る
root@be20b535a937:/var/www/html# php -m // モジュール一覧表示 mysqliがあることを確認
```

なんで手動だとできるのかもまだ謎、、

プロジェクトを作成します。

```
root@be20b535a937:/var/www/html# composer create-project laravel/laravel apache-laravel-app --prefer-dist "6.*
```

# Laravelの画面を開く
`http://localhost` にアクセスしてlaravelのページが開ければオッケーです！
でも！以前[nginx + Laravel でホーム以外のページの404エラー解決](https://qiita.com/maip0902/items/2fae4944e0115b40dcdc)に投稿したみたいにApacheでも同じこと起こるんじゃないかと思って、Auth入れた後に /login にアクセスしてみた結果、、、

<img width="469" alt="スクリーンショット 2020-05-23 1.23.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454650/e463f03b-83ae-7a45-051a-49a6f80142e0.png">

ビンゴ！(泣)
ドキュメントルートの問題じゃないのになぜなの。。。

# ホーム以外が見れない原因
ApacheでLaravelを動かす時には`.htaccess`を読ませないといけないのですが、

```apache:.htaccess

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>
```
のようになっていて、`mod_rewrite`というモジュールが有効でないと動かないんです。そこで、コンテナ内で

```
root@be20b535a937:/etc# a2enmod rewrite
```
を実行します。
もしこれでも動かない場合は`.htaccess`自体が読み込まれない設定になっている可能性があるのでドキュメントルートで`AllowOverride`が`All`になっているか確かめてください！

# 最後に
本来であればDocker関連のファイルはプロジェクトと同じ階層にあると思うのでドキュメントルートはよしなに変えてください。

# 参考にさせていただいた記事など
[ubuntuでapache2のDocumentRootを変更するまで](https://qiita.com/shita_fontaine/items/40a086265f0cf07d10e0)
[【find・grep】特定の文字列を含むファイルのリストを取得する方法。](https://qiita.com/pokari_dz/items/0f14a21e3ca3df025d21)
https://gist.github.com/chronon/95911d21928cff786e306c23e7d1d3f3