【Laravel+Docker】 0から！PHP(Laravel) + Docker+nginx+mysqlの環境構築方法(完全版)

# 記事の目的
MAMPを使わない、初期設定状態からPHP(Laravel)の環境構築を終えるために必要な手順を備忘録の意味も兼ねて記述します（macのCatarina向けです）

#手順
##homebrewをインストール

```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```

##homebrewを使い必要なソフトウェアをインストール

```
brew install automake autoconf curl pcre bison re2c mhash libtool icu4c gettext jpeg openssl libxml2 gmp libevent
```

##VScodeをホームページ（`https://azure.microsoft.com/ja-jp/products/visual-studio-code/`）からダウンロード

vscodeのおすすめ拡張機能
Japanese Language Pack for Visual Studio Code
HTML Snippets
PHP Intelephense
zenkaku

VSCodeのサイドバーより、「管理（下部の歯車マーク）」→ 「設定」の順に選択
Editor: Tab Sizeを２に設定
Editor: Render Whitespaceで「all」を選択

VSCodeのサイドバーより、「管理（下部の歯車マーク）」→ 「設定」の順に選択（直前の作業に続けて行う方は、この操作の必要はありません）
オートセーブの設定で「onFocusChange」を選択

##sequelpro（データベース管理アプリ）をダウンロード（MAMPを使う場合は類似のものが初期機能として備わっています）
https://sequelpro.com/download#auto-start

##プロジェクト作成
ホームディレクトリを確認→Users/user
Users/user/projects(好きな名前のフォルダ)を作成
Users/user/projects/phpForEveryone（好きな名前のフォルダ）を作成

##docker-compose.ymlの作成

Users/user/projects/phpForEveryone/docker-compose.ymlファイルを作成(設定用ファイルなので同じ名前にしてください)。これはvscodeでphpForEveryone右クリック→ファイル作成でも、ターミナルでプロジェクトディレクトリで`vi docker-compose.yml`からの作成でも構いません。
その後下記の内容を記述してください（コピペ可）

```docker-compose.yml
version: '3.7' 

services:
  php:　　　　　 
    build:　　 　
      context: .  
      dockerfile: Dockerfile　
    container_name: php　　　 
    volumes:　　　　　　　　　　
      - ./src:/var/www/html
    ports:　　　　　　　　　　　
      - "8000:8000"　　　　　　
```

※上記docker-compose.ymlの詳しい解説は下記の通りとなっています。最初は読むだけで疲れてしまうので基本飛ばしで大丈夫です。どうしても理解したいという人だけ見ていただければと思います

```docker-compose.yml
version: '3.7'  #Compose ファイルの形式のバージョン指定

#以下はインデントで親子構造が決められています(ymlファイルの書き方になってます。ちなみにyml=yamlで拡張子はどちらでも大丈夫になってます)
services:　　　　#バージョン２以降必須となった文言です。この文言の下の階層にオプション(buildやvolumesなどの初期設定)を記述することで、そのオプションを立ち上げ時に自動で設定してくれるようになります
  php:　　　　　 #ここは自分で分かりやすいように名前をつけている部分です。htmlのclass名をつけるのと同じような使い方です。各機能ごとに初期設定のまとまりを作りたいから作っています。この記事の後半でmysqlとnginxのまとまりを作るのでオプションを区別をできるように指定しています。
    build:　　 　#installコマンドの詳細に関する記述を子要素で指定できるようになるオプションです。
      context: .  #buildの下の階層でかけるオプションです。docker-composeファイルからみた相対的なdockerfileの位置を記載しています。"."は同じディレクトリ（フォルダ内）という意味です。
      dockerfile: Dockerfile　#こちらもbuildの下の階層でかけるオプションです。Dockerfileというファイル名にしたくない時に使います。（今回は必要ない記述ですね。）
    container_name: php　　　 #読んで字のごとくですが、このオプションのまとまり（コンテナ）の名前を設定しています。最初のphpはdocker-compose.ymlがビルド（各種インストール）のために使う名前で、今回指定したphpがデータベース,nginxとの接続の時に使う名前になってます。
    volumes:　　　　　　　　　　#"dockerに丸々コピーしたいディレクトリ:docker内のコピー先のディレクトリ" となってます。今回はdocker-compose.ymlがあるディレクトリから一つ下にあるsrcディレクトリ以下全てを、docker内の/var/www/htmlのディレクトリにコピーするという書き方になっています。
      - ./src:/var/www/html
    ports:　　　　　　　　　　　#ホスト側（データを操作ができる接続側。普通はパスワード設定で作成者以外接続できないようにします）とコンテナ側（他人に見せるようで、操作にできない設定を施したりする側。普通はパスワードなどを設定せずipアドレスとport番号だけで接続可とするものです）の両方のポートを指定（ホスト側:コンテナ側）しています。通常"ipアドレス:port番号" でコンテナへ接続ができるようになっており、設定が必要な項目となります。（ipアドレスは自動で生成されますがport番号は生成されません）
      - "8000:8000"　　　　　　#今回はどちらも同じポートにしていてかつパスワードも設定していないため、ipアドレスとポート番号際知っていれば誰でもホスト側で接続できてしまう設定になっています。しかしこれには理由があって、このコンテナの中身自体がそもそも外から操作する内容がないため問題ないという事情からこのように設定しています。
```

同様にUsers/user/projects/phpForEveryone/Dockerfileファイルを作成(設定用ファイルなので同じ名前にしてください)。これもvscodeでphpForEveryone右クリック→ファイル作成でも、ターミナルでプロジェクトディレクトリで`vi docker-compose.yml`からの作成でも構いません。
その後下記の内容を記述してください（コピペ可）。
※筆者はここに`WORKDIR /var/www/html/laravel`という記述をしてしまった結果のちのcssが当たらないエラーになってしまったので注意しましょう。



## Dockerfileの作成
同様にUsers/user/projects/phpForEveryone/Dockerfileファイルを作成(設定用ファイルなので同じ名前にしてください)。これもvscodeでphpForEveryone右クリック→ファイル作成でも、ターミナルでプロジェクトディレクトリで`vi docker-compose.yml`からの作成でも構いません。
その後下記の内容を記述してください（コピペ可）。
※筆者はここに`WORKDIR /var/www/html/laravel`という記述をしてしまった結果のちのcssが当たらないエラーになってしまったので注意しましょう。

```
FROM php:7.4-fpm-alpine

RUN docker-php-ext-install pdo pdo_mysql
```

##docker-composeコマンドを利用してコンテナの作成(初回は少し時間がかかります)

```
docker-compose up -d
```

コンテナのステータスを確認

```
docker-compose ps -a
```


##Laravelのインストール
Users/user/projects/phpForEveryone/appフォルダを作成。（ローカルフォルダで作ったものがある場合は入れますが、今回は無しなので空でいきます。本番環境のsrc、ローカル環境のappとなります。）これがないと次のコマンドが紐づかなくて失敗します。

composerコンテナを利用して./srcディレクトリにLaravelのインストールを行います。(全て一行で行います)

```
% docker run --rm -v /Users/user/projects/phpForEveryone/src:/app composer create-project --prefer-dist laravel/laravel .
```

vscodeかターミナルで`ls`を使いUsers/user/projects/phpForEveryone/srcの中にフォルダがあることを確認してください。

開発サーバの起動ができるようになったので下記のコマンドからLaravelの画面が出るか確認してください。

```
% docker exec -it php php artisan serve --host=0.0.0.0 --port=8000
```

このコマンド実行後、ブラウザで127.0.0.1:8000(or 0.0.0.0:8000)にアクセスするとトップ画面が表示されます。

##サインアップ、ログイン機能を下記のコマンドによりつけます。

```
% docker run --rm -v /Users/user/projects/phpForEveryone/src:/app composer require laravel/ui
```

これはそれなりにデータ通信量が多いみたいで通信環境が悪いと筆者のようにエラーを出すことがあるので通信環境を変えて何度か同じコマンドをうちましょう。（上書きになるだけなのでそこまで影響は出ません）

筆者はnpmとnodeが入っていないせいでエラーが出たので
https://stackoverflow.com/questions/57857489/command-ui-is-not-defined-in-laravel-6-0
https://qiita.com/mame_daifuku/items/373daf5f49ee585ea498
を参照してnpmとnodeをインストール

```
% docker exec -it php php artisan ui vue --auth 
```

```
% docker run --rm -v /Users/user/projects/phpForEveryone/src:/usr/src/app -w /usr/src/app node npm install && npm run dev
```

##MySQLコンテナの作成
###docker-compose.ymlファイルへの追加

```
version: '3.7'
services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: php
    volumes:
      - ./src:/var/www/html
    ports:
      - "8000:8000"
  mysql:
    image: mysql:5.7
    container_name: mysql
    ports:
      -  "3306:3306"
    volumes:
      -  ./db:/var/lib/mysql
      -  ./my_conf:/etc/mysql/conf.d
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: laravel
```

###Volumesの作成

```
% pwd        
/Users/user/projects/phpForEveryone/src
 % mkdir db
 % mkdir my_conf
```

my_confの下には設定ファイルmysql5.7.cnfファイルを作成し、日本語対応できるように文字コードの設定を行います。

```
[mysqld]
character-set-server=utf8mb4

[client]
default-character-set=utf8mb4
```

###MySQLコンテナの作成

MySQLコンテナを作成する前に一度Laravelコンテナの削除をしておきます。docker-compose downコマンドを実行するとコンテナの停止と削除を同時に行います。

```
% docker-compose down
```

LaravelコンテナとMySQLコンテナを作成します。docker-compose.ymlにコンテナ情報が記述されているのでdocker-compose up -dコマンドで2つのコンテナが作成されます。

```
% docker-compose up -d
```

下記コマンドでup状態になっているか確認しておきましょう

```
% docker-compose ps -a
```

##Laravelからの接続

srcディレクトリ内にある.envファイルを開いてデータベースの接続に関する設定を下記のように更新してください。ほとんど初期設定と同じなのですがDB_HOSTが変わっているので注意しましょう。

```
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=password
```

.envファイルの設定がしたら、php artisan migrateコマンドを実行してデータベースにテーブルが作成されるか確認しましょう。接続が正常に行われた場合は下記のようなメッセージが表示されます。

```
 % docker-compose exec php php artisan migrate 
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.09 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.05 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.03 seconds)
```

##Nginxコンテナの作成
###docker-compose.ymlファイルへの追加

```

version: '3.7'
services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: php
    volumes:
      - ./src:/var/www/html
    ports:
      - "9000:9000"
  mysql:
    image: mysql:5.7
    container_name: mysql
    ports:
      -  "3306:3306"
    volumes:
      -  ./db:/var/lib/mysql
      -  ./my_conf:/etc/mysql/conf.d
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: laravel
  nginx:
    image: nginx:stable-alpine
    container_name: nginx
    ports:
      -  "8080:80"
    volumes:
      -  ./src:/var/www/html
      -  ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      -  php
      -  mysql
```

下記コマンドをうち、ポートはローカルから8080でアクセスできることを確認しdocker-compose.ymlが更新されていることを確認しましょう。またphpのポートを先ほどまで8000:8000に設定していましたが、9000:9000に変更を行ってください。Nginxと通信する際にポート9000を利用するためです。


```
docker exec -it php php artisan serve --host=0.0.0.0 --port=8000
```

###nginxの設定ファイルの作成
ローカル上に設定ファイルを保存するnginxフォルダを作成します。（Users/user/projects/phpForEveryone/nginx)

```
server {
    listen 80;
    index index.php index.html;
    server_name localhost;
    error_log /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html/public;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        try_files $uri = 404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
```

コンテナの作成

```
 % docker-compose down 
 % docker-compose up -d  
```

確認しましょう。具体的には下記コマンドでコンテナがあるのを確認したあと`127.0.0.1:8080`(webサーバ）にアクセスしましょう。(nginxの正常稼働確認を確認しましょう)

```
 % docker-compose ps -a
```

以上で環境構築が出来上がっているはずです。
ここからは各自作りたいサーバーサイドとフロントサイドを作っていきましょう！

#参考にした記事
Dockerの公式リファレンス(docker-compose.ymlの書き方部分)
https://docs.docker.jp/compose/compose-file.html
↑めちゃくちゃわかりやすいのに検索になぜかヒットしにくいので、Dockerを使うならこのタイミングで一度目を通されるのを強くお勧めします。

Dockerの公式リファレンス(最初の立ち上げの説明部分)
https://docs.docker.jp/compose/index.html

今回はこの方の記事の再現という形で実装できました
https://reffect.co.jp/laravel/finally-understand-laravel-on-docker

筆者の出会ったエラーとその解決法
https://qiita.com/KONTA2019/items/d3f026961088afef4393
https://qiita.com/KONTA2019/items/0f41346c2ead56e7f52c
https://qiita.com/KONTA2019/items/bddc8eae021b8396dada